Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Extended return type of invokeAction() #561

Draft
wants to merge 4 commits into
base: main
Choose a base branch
from

Conversation

danielpeintner
Copy link
Contributor

@danielpeintner danielpeintner commented Aug 26, 2024

I started to work on a PR to show a possible solution how the extended return type ActionInteractionOutput for invokeAction() may look like.

resolves #555


Preview | Diff

index.html Outdated Show resolved Hide resolved
<p>
The <dfn>error</dfn> property represents a possible error, initially `null`.
</p>
<p class="ednote" title="Should state be a function or an attribute only?">
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I started with a function but I start thinking an attribute should be sufficient..

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

An action is supposed to be always polled? Or could a script subscribe just for changes in status?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think observing state changes would be nice but I am not sure whether that is actually necessary...

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Mhh, I tend to think now that a a function for reporting the status would be more powerful and useful. What do people think? Other ideas?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Mhh, I tend to think now that a a function for reporting the status would be more powerful and useful. What do people think? Other ideas?

Hmm, if I see it correctly, we could also treat the attribute as a getter and add more logic to that if needed, right?

Or could a script subscribe just for changes in status?

If we wanted to that, what would be the best way to model that? Adding a way for registering a callback function?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Mhh, maybe we should decide what we need/want from the status

  • is it likely that requesting the status may take a while? (or put differently do we need a Promise)
  • do we need a way to observe a status change (would be nice but adds complexity)

My take is that having a promise function for status() is fine while I am not 100% sure we should allow for listening status changes...

Are there opinions/proposals?

@danielpeintner danielpeintner marked this pull request as draft August 26, 2024 14:20
index.html Outdated
Comment on lines 1295 to 1296
"success", /* Profile uses completed? */
"error" /* Profile uses failed? */
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should we coordinate in some way with the Profile TF to align the terms that are being used? Or is it no problem if they differ, since they are operating on a different level anyway?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good point. Once we feel comfortable with our choices we should definitely reach out. Most of the status terms are 1:1 matches. What seems different is pending.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

FYI: Profile has 4 terms

  • pending --> we don't have a corresponding term!
  • running -> same ✅
  • completed -> we use success instead
  • failed -> we use error instead

What can we do with aligning the terms? Use the ones from profile or talk to them? Any opinion?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hmm, their terminology actually sounds quite good to me, so from my side we could also adopt what they are using.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I am fine withe the Profile terms also. The question remains if it is fine dropping "pending" then... (same same but different 🙃)
Other opinions?

@danielpeintner
Copy link
Contributor Author

We have some inline discussions which need some more feedback. Please chime in with your opinion!

index.html Outdated Show resolved Hide resolved
<p>
The <dfn>error</dfn> property represents a possible error, initially `null`.
</p>
<p class="ednote" title="Should state be a function or an attribute only?">
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Mhh, I tend to think now that a a function for reporting the status would be more powerful and useful. What do people think? Other ideas?

Hmm, if I see it correctly, we could also treat the attribute as a getter and add more logic to that if needed, right?

Or could a script subscribe just for changes in status?

If we wanted to that, what would be the best way to model that? Adding a way for registering a callback function?

index.html Outdated
Comment on lines 1295 to 1296
"success", /* Profile uses completed? */
"error" /* Profile uses failed? */
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hmm, their terminology actually sounds quite good to me, so from my side we could also adopt what they are using.

Co-authored-by: Jan Romann <[email protected]>
@danielpeintner
Copy link
Contributor Author

@JKRhb seems to be fine with using the terms used by the profile.
@zolkis you were proposing the current terms. Any opinions?

@danielpeintner
Copy link
Contributor Author

Scripting Call 2024/12/11
Note: I try to summarize the decision which were made in the call yesterday.

  • we use the terms defined by the WoT profile ("pending", "running", "completed", "failed" )
  • we need a 5th parameter to indicate that an action has been cancelled
    • --> "pending", "running", "completed", "failed", "cancelled"
    • Question: shall we use cancelled or canceled (the difference seems to be British English vs American English)
  • No need for error attribute (the status provides already a mean to indicate an error)
  • we use EventEmitter for each event
    • Q: How to model EventEmitters in WebIDL? @zolkis may be able to help (I found this)
    • there was the alternative mentioned that we could have one event like change that reports the actual type of the change
    • what I envision is that the new interface looks somehow like this (I think that should work in TypeScript)
   interface ActionInteractionOutput extends InteractionOutput, EventEmitter {
        // cancel a pending/running action
        cancel(): Promise<void>
        // possible events
        'pending': () => void;
        'running': () => void;
        'completed': () => void;
        'failed': (error: Error) => void;
        'cancelled': () => void;
        // Note: value() function will only work after completed state has been reached
    }

Please comment!
If there are no further comments/changes, I can try to implement it in this PR.

@zolkis
Copy link
Contributor

zolkis commented Dec 12, 2024

Nowadays the use of Promises is encouraged instead of events wherever possible (which tend to be misused). Promise are also easier to use from client code. I liked more the original design with status query returning a Promise. The latest status terms look good.

@zolkis
Copy link
Contributor

zolkis commented Dec 12, 2024

Cancelling operations should be done using AbortSignal, but in our case a dedicated function is fine, since it's scope is strictly the referred action.

@danielpeintner
Copy link
Contributor Author

@zolkis Thanks for your feedback!

Nowadays the use of Promises is encouraged instead of events wherever possible (which tend to be misused). Promise are also easier to use from client code. I liked more the original design with status query returning a Promise.

I may be wrong in my thinking, but I think from a developer point of use, events seem simpler.
I can do something like the following

action.on('completed', () => console.log("Action completed, inform user"));
action.on('failed', (err) => console.log("Error happened: " + err));

Note: The only thing I am not sure about is whether we can miss events. Why am I saying that.

let action = await thing.invokeAction("doFoo"));
// register handlers (could it be that action is done before?)
action.on('completed', ...);

I think it might be possible that the emit() call comes before Í registered the handlers?

Anyhow, in the case of a status() promise I need to pull regularly, don't I?

do {
  let status = await action.status();
  if (status === "failed") {
     console.log("Action failed"));
  } else if (status === "completed" ) {
     console.log("Action completed"));
  } else {
     console.log("Action not yet done..."));
  }
} while(...);

I don't think there is a easy way to register for an event?
Maybe I am just not seeing the solution 🙈

@relu91 @JKRhb any opinion?

@zolkis
Copy link
Contributor

zolkis commented Dec 13, 2024

We won't miss events, JS is single threaded, the event loop will check for events and dispatch.

In my latest experience, devs preferred Promises to events, no setup is needed, and most interactions can be modeled with one-off transactions.

What are the main dev use cases with this API?

  • canceling action is one - needs a function
  • display completion %: in this use case the client wants to control the loop and update frequency. That is easier than reacting to (and skipping some of the) events, and the loop is not spared, either. Events also need setting up. Impl already set up everything when the action started, the spec should just expose that in the control object returned by invoking the action. So I think polling the status for % is more efficient (doesn't even need Promise, just the last know status as an attribute). See also https://www.w3.org/TR/design-principles/#synchronous
  • know when the action has completed (with success or error): since it's a one-off, a Promise covers it.
    But we can experiment with implementing both, and see how they fulfill the dev use cases.

Belongs to the <a>WoT Consumer</a> conformance class.
An {{ActionInteractionOutput}} object is always created by a consumer implementation
and exposes functionality to interact with long running (asynchronous) actions.<br />
Note: a synchronous action MAY not offer any <emph>additional</emph> sensible interaction beyond the ones from {{InteractionOutput}}.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

  • This **MAY** not offer is likely to be confused with **MAY NOT** offer, despite formatting, casing, etc.
  • Further, I'm not sure what sensible is contributing here.
  • I'm also left wondering whether there should be some indication of what other interactions (sensible or not) might be offered (if the action does offer more than those in {{InteractionOutput}})?

Perhaps this rephrasing is acceptable, for the first two points above? Something additional might be needed for the last.

Suggested change
Note: a synchronous action MAY not offer any <emph>additional</emph> sensible interaction beyond the ones from {{InteractionOutput}}.
Note: A synchronous action MAY offer only the interactions found in {{InteractionOutput}} and no others.

Copy link
Member

@JKRhb JKRhb Dec 18, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've tried to cover the third aspect that you've mentioned by rephrasing the sentence as follows – hope that it is a bit clearer than it was before:

Suggested change
Note: a synchronous action MAY not offer any <emph>additional</emph> sensible interaction beyond the ones from {{InteractionOutput}}.
Note: The output of a synchronous action MAY be limited to the functionality of a regular {{InteractionOutput}} object, e.g., invoking the `cancel()` method might not have an effect.

Should we allow pause/resume also? TD has no notion of it.
</p>
<section><h3>The <dfn>status()</dfn> function</h3>
Reports the status of an <a>Action</a> (one of <code>running</code>, <code>success</code> or <code>error</code>) or rejects on error. The method MUST run the following steps:
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
Reports the status of an <a>Action</a> (one of <code>running</code>, <code>success</code> or <code>error</code>) or rejects on error. The method MUST run the following steps:
Reports the status of an <a>Action</a> (one of <code>running</code>, <code>success</code>, or <code>error</code>), or rejects on error. The method MUST run the following steps:

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Extended return type of invokeAction()
4 participants