Skip to content

Adaptor Writing Best Practice

josephjclark edited this page Jun 28, 2024 · 2 revisions

This is a living document for best practice guidelines for how to create adaptors

It is a rough working draft right now: take with a pinch of salt.

  • Use the common.http helper functions for any http stuff. No third party http libraries.
  • The first step in most adaptors is to provide a get/post wrapper, from which you can have general access to the backend API
  • Then you want to provide a user-friendly wrappers on the most common operations (or just the stuff you want to do in your job!)
  • Avoid polluting the state object where possible (ie, write clients to closure variables,rather than state).
  • If you DO write a client to state, always tidy it up at the end of the job.
  • Adaptors using internal state or client objects should a) override the execute function to wrap setup and teardown functions at the start and end of the pipeline, and b) save the client object to a closure variable (not to state).
  • Wrap (almost) all parameters in expandReferences() calls (this ensures that arguments can be lazily evaluated against state) Eg:

get(path, options)

export function get(path, options) {
  return (state) => {
    path = expandReferences(path)(state);
    options = expandReferences(options)(state);

    return state
  };
}
  • The result of any operation should be written to state.data
  • Well almost. Very occasionally an operation may want to write data to a special state property (like response). This is OK but should only be done with good reason. Maybe many operations use the same key. And you should probably tidy up at the end.
  • Provide callbacks so that users can "promote" stuff in state.data up to the top of the state object
  • Use composeNextState(state, nextData) when writing to state.data (this will ensure the references array gets updated)
  • Using async/await in adaptor code is... totally fine I think? We should take advantage of the improved syntax it offers
  • When writing an adaptor function, try to start by drafting some job code which USES that function. Compose it with get or each.
  • Try to minimise the number of parameters in your function. Required arguments should be a parameter, everything else basically goes into a config object
  • It is probably good practice for every Operation to log what it is doing. It's probably also good to just log the result, rather than eg console.log("starting query..."); ... console.log("Query completed with ${result.length} records!".

error handling

I want to say: on the event of an error, you should throw an object with details about the error. This will stop the workflow and inform the user. The runtime will handle the logging of that error.

Should we throw an Error() with a message? If so it's kinda hard to add details, like it's not very elegant. An Error class provided by common or the runtime like Error(message, details), where details is an object full of debugging information

I don't think you should console.error and THEN throw. Just throw.

Clone this wiki locally