Skip to content

MAP Uniform API

Steve Melville edited this page May 7, 2024 · 26 revisions

Uniform API

The MAP Uniform API is the means by which all MAP behavior may be invoked. The following diagram provides a high-level view of the major components making up the Uniform API.

image

  • The Dances Coordinator Zome -- provides the uniform data definitions and behavior for the Uniform API. This includes a Dancer that accepts calls from the Conductor on the dance interface function and dispatches them to adapter functions that implement the DanceFunction interface. The Dances Zome also provides definitions for DanceRequest and DanceResponse objects, standard ResponseStatusCodes, a canonical DanceFunction interface and a StagingArea used to pass staged holons back and forth between the guest and the client pending commit of the staged changes.
  • Native Coordinator Zomes -- are any holochain zomes that provide their own functionality. There can be an arbitrarily large number of native coordinator zomes offered by a variety of development groups.
  • Adaptor Coordinator Zomes -- these zomes make the functionality provided by Native Coordinator Zomes available within the MAP ecosystem. They provide adapter functions that map dispatch requests on the canonical DanceFunction interface into native function calls and maps their results to Result<ResponseBody, HolonError>. They also provide helper functions that can be used to build standard DanceRequest objects that bundle the native parameters into standard RequestBody structs. This is primarily intended for use by sweetest fixtures to build DanceRequest objects.

*NOTE: The HolonsAdapter is currently bundled into the Dances Zome. This will be re-factored into its own zome in the future.

Dances Coordinator Zome

The Dances Coordinator Zome consists of the following modules:

  • dancer-- responsible for implementing shared dance behavior.

    • defines and implements the single extern function: dance(request:DanceRequest)->ExternResult<DanceResponse> that can be invoked via the Conductor's call zome function. The Dancer transforms calls on this function into dispatch calls on Adapter functions that implement the canonical DanceFunction interface.
    • defines a canonical DanceFunction: fn(context: HolonsContext, request:DanceRequest) -> Result<DanceResponse, HolonError>; that specifies the standard interface for all dispatchable functions. Adaptors expose dispatchable functions that wrap native functions.
    • internally, the Dancer contains a dispatch_table with entries for each of the dances a given type offers. The dispatch_table maps function names onto pointers to functions that implement the DanceFunction interface. In the current design, this table is statically populated during Dancer construction. In the future, this design will be enhanced to support dynamic dispatch.
    • the Dancer implements a single dispatch() method that looks up a provided function name in the dispatch table and then dispatches that function:
      fn dispatch(&self, context: HolonsContext, name: &String, request:DanceRequest) -> Result<DanceResponse,HolonError>
  • dance_request -- defines the structures, enums and status codes associated with dance requests

  • dance_response -- defines the structures, enums and status codes associated with dance responses

  • StagingArea -- used to pass staged holons back and forth between the client and guest on each API request. The StagingArea is used to initialize the CommitManager on inbound requests and then convert the state of the CommitManager into a StagingArea immediately before returning responses.

Dance Invocations

The Uniform API exposes a single function: dance(request:DanceRequest)->ExternResult<DanceResponse>.

image

When the Conductor calls this function:

  1. the dance function initializes the Context for this call. This includes:
    • constructing a CommitManager from the staging_area passed in the DanceRequest.
    • constructing an (empty) HolonsCacheManager
    • constructing the dispatch_table. Currently this is done statically within the Dancer::new() function. This means that adding dispatchable functions requires a rebuild of the Dances zome.
  2. Uses the dispatch table to dispatch the DanceFunction corresponding to the dance_name in the API request.
  3. The Adapter offering the dispatched function extracts parameters from the DanceRequest and passes them to its corresponding native function. It then turns the result of that function, into a Result containing a ResponseBody or HolonError.
  4. The dancer:
  • evaluates the response and builds the DanceResponse.
  • TODO: Asks a WorkflowManager to build the adjacent_possible requests to include in the response.
  • rebuilds the staging_area from the current state of the CommitManager
  • Returns the ExternResult.

Dance Descriptors (not yet implemented)

  • Dances are offered by Holons via their HolonDescriptors. In this sense, they are like methods within an object-oriented paradigm.
  • The run-time representation of a HolonDescriptor includes a Dancer that contains a dispatch_table with entries for each of the dances that type offers.
  • The Dancer struct offers a single dispatch() method
  • Each HolonDescriptor's DanceDescriptorMap describes the dances it offers.
  • A DanceDescriptor describes the Request that can be submitted and the possible Responses to each such request.
  • Each response includes a list of Dances (i.e., a set of Request objects) that are currently available given the current state of the system.
  • The API layer allows Requests to be submitted (synchronously) and Results returned.
  • (Eventually) asynchronous requests and callbacks will be supported.
  • Modeled after RESTful patterns.

Open Questions

Question: Dances can be classified as Query (read-only) or Command (affects system state). The intention is for Commands to be Undoable. If we attempt to support undo/redo, the log will need to be part of the state shipped back and forth (like the staging_area) until we can use the ephemeral store for that purpose.

Question: Should the DispatchTables be partitioned by HolonType (i.e., each HolonDescriptor maintains a mapping table for the dances it offers)? Or, should there just be one DispatchTable per HolonSpace that maps DanceDescriptor Id's to functions?

Specifically, the Dispatcher:

  • Constructs a CommitManager and initializes it from the StagingArea
  • TODO: Consider CommandLogging here
  • Looks up the DanceRequest's descriptor in its dispatch_table and invokes that function, passing the request's body to the function
  • Handles the response from the dispatched function
    • dispatched functions are expected to return a ResponseObject that includes adjacent_possible dances for the offered_by holon.
    • marshal the staging_area from the the current state of the CommitManager
    • map errors to status codes (if not done by the dispatched function)
    • return from the zome call.

Queries

Fetch next page of query results, given a QuerySpec

HolonBuilder Workflow

  • Create HolonBuilder
  • Clone HolonBuilder from existing Holon
  • Derive HolonBuilder from existing Holon
  • AddProperty
  • RemoveProperty
  • AddToRelationship
  • RemoveFromRelationship
  • Commit -- creates a new (version of) a Holon
  • Delete Holon