Osy#betaRun it on Osyrin: osyrin.com

Reference / Workflow

Workflow.InRun (can this code wait?)

Workflow.InRun → bool

`Workflow.InRun` is true in a workflow's body and in everything that body calls — where `await Workflow.Run(…)` may wait for days — and false everywhere else: a page's action, a function called over the API, a test. A function that may need to wait for somebody reads it to choose: wait in place when the caller can, start the wait and answer "still waiting" when it cannot.

preview1 example compiled by CIworkflowauthoringstdlib

Summary#

Only a workflow can wait. await Workflow.Run("sign", signature) in a workflow's body parks the run until the signature is in; the same line reached from a page's action has nothing to park, and fails. A function that is called from both — a kit's "ask a person", "get this approved" — needs to know which it is in:

if (!Workflow.InRun) { Workflow.Run(signature); return "asked"; }   // start it, and say it is not in yet
await Workflow.Run("sign", signature);                              // wait for it, however long
return "signed";

Signature#

Workflow.InRun -> bool   // true in a workflow's body and in anything it calls

Description#

Where is it true?#

In a workflow's enter, exit, on, Start and reminder bodies, and in every function, method and lambda those call, to any depth. It is the question "would an awaited Workflow.Run here have a run to park?", so it is true in a function exactly when that function was reached from a workflow's body.

It is false in a page's action, in a function called by a page, over the API or by a tool, in an [On] handler that a plain function's change set off, and in a test's own body.

What is it for?#

One function with one name for both callers. Without it a kit offers two — "ask and wait" for workflows, "ask and come back later" for pages — and every caller has to know which it is. With it, the function waits when its caller can and otherwise hands back something that says the answer is not in yet; the caller that could not wait hears the answer as an event, or reads it when it looks again.

It is read, never kept#

The answer is a fact about the call, not about the function: the same function is in a run for one caller and not for the next. Read it where you branch.

Examples#

using Osysharp.Workflow;

enum SignStatus { Asked, Signed }
enum LetterStatus { Drafting, Sent, Held }

entity Signature {
  SignStatus Status = SignStatus.Asked;
  security { allow read, create, update when IsAuthenticated || IsAnonymous; }
}

workflow SignFlow {
  Tracks = Signature.Status; Autostart = false; Initial = Asked;
  event Sign();
  state Asked { subscribe Sign(); on Sign { goto Signed; } }
  terminal success Signed { }
}

entity Letter {
  LetterStatus Status;
  [MaxLength(80)] string Note = "";
  security { allow read, create, update when IsAuthenticated || IsAnonymous; }
}

public class Desk {
  /// Get a signature: waits for it where the caller can wait, and says "asked" where it cannot.
  public static async Task<string> GetSigned(Signature s) {
    if (!Workflow.InRun) {
      Workflow.Run(s);                 // start it; whoever signs will find it
      return "asked";
    }
    await Workflow.Run("sign", s);     // the run parks here until it is signed
    return "signed";
  }
}

/// What a page's action calls.
public async Task<string> AskFromAPage(Signature s) { return await Desk.GetSigned(s); }

workflow LetterFlow {
  Tracks = Letter.Status; Autostart = true; Initial = Drafting;
  state Drafting {
    enter {
      var s = Workflow.Once("signature", () => new Signature { });
      this.Item.Note = await Desk.GetSigned(s);
      goto Sent;
    }
  }
  terminal success Sent { }
  terminal cancel  Held { }
}

[Test]
void called_from_a_page_it_cannot_wait_and_says_so() {
  var s = new Signature { };
  Assert.Equal("asked", await AskFromAPage(s));
  Assert.Equal(SignStatus.Asked, s.Status);
}

[Test]
void called_from_a_workflow_it_waits_for_the_signature() {
  var letter = new Letter { };
  Workflow.Settle(letter);
  Assert.Equal(LetterStatus.Drafting, letter.Status);     // parked: nobody has signed

  SignFlow.RaiseSign(Signature.Single());
  Workflow.Settle(letter);
  Assert.Equal(LetterStatus.Sent, letter.Status);
  Assert.Equal("signed", letter.Note);
}

Notes#

See also#

Related

Workflow.Run (start a workflow)

Start the workflow bound to an entity's type, on that entity. Bare — `Workflow.Run(order)` — is fire-and-forget: start…

Tasks that wait (Task.WhenAll across days)

In a workflow body, a call started as a task may wait — on a child workflow run that takes days, a person's answer —…

Automatic durability (steps you do not have to write)

Any call that leaves the platform — an outbound client call, an external service — is made a durable step by the…

async / await and Task

C#'s async and await, with C#'s meaning. A call to a Task-returning method that you do not await where you make it…