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鈥檒l occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add support for schema-based message definitions #846

Merged
merged 14 commits into from
Apr 8, 2024
Merged

Conversation

shakyShane
Copy link
Collaborator

@shakyShane shakyShane commented Nov 21, 2023

Feature entry pointbefore

Features can opt-in when ready. Each feature would contain 3 top-level keys

  • notifications
  • requests
  • subscriptions

These are designed to match the messaging API directly, and will provide automatic documentation for
native teams as they integrate.

{
  "$schema": "http://json-schema.org/draft-07/schema#",
  "type": "object",
  "title": "WebCompatMessages",
  "description": "Requests, Notifications and Subscriptions from the Web Compat feature",
  "additionalProperties": false,
  "properties": {
    "notifications": {},
    "requests": {
      "oneOf": [
        {
          "$ref": "./web-compat/web-share.json"
        }
      ]
    },
    "subscriptions": {}
  },
  "required": [
    "requests",
    "notifications",
    "subscriptions"
  ]
}
Feature entry point NEW

Place a folder inside src/messages with at least 1 notification, request or subscription

馃搧 src/messages
  - 馃搧 duck-player
     - setUserValues.notify.json

When you have multiple messages, will look more like

馃搧 src/messages
  - 馃搧 duck-player
     - setUserValues.notify.json
     - getUserValues.request.json
     - getUserValues.response.json
     - onUserValuesUpdated.subscribe.json

src/messages

Individual Message

The important parts of a message are:

  • method this is what native teams will map to their handlers
  • params (optional) this is the payload we'll send in the outgoing message
  • result (optional) this is the payload the native side will return in a response to a request.
{
  "$schema": "http://json-schema.org/draft-07/schema#",
  "type": "object",
  "title": "WebShare",
  "additionalProperties": false,
  "description": "todo: add description for `webShare` message",
  "required": ["method", "params", "result"],
  "properties": {
    "method": {
      "const": "webShare"
    },
    "params": {
      "title": "WebShareParams",
      "type": "object",
      "additionalProperties": false,
      "properties": {
        "title": {
          "description": "todo: add description for 'title' field",
          "type": "string"
        },
        "url": {
          "description": "todo: add description for 'url' field",
          "type": "string"
        },
        "text": {
          "description": "todo: add description for 'text' field",
          "type": "string"
        }
      }
    },
    "result": {
      "description": "todo: add return type here"
    }
  }
}
Individual Message NEW

Notifications

Notifications have 2 elements - method and params.

  • method: this is represented in the filename, everything before the first . will be used
  • params: in your json schema file, add the object type and properties as seen below

notification without params

{ "$schema": "http://json-schema.org/draft-07/schema#" }

Given the filename debug.notity.json, this will generate

export interface DebugNotification {
  method: "debug";
}

notification with params

{
  "$schema": "http://json-schema.org/draft-07/schema#",
  "type": "object",
  "additionalProperties": false,
  "required": ["a"],
  "properties": {
    "a": {
      "type": "string"
    }
  }
}

Given the filename debug.notity.json, this will generate

export interface SetValueNotification {
  method: "setValue";
  params: SetValueParams;
}
export interface SetValueParams {
  a: string;
}

requests & responses

Requests work the same, except they use the file format debug.request.json.

  • method: as above, it's inferred from the filename
  • params: as above

Requests differ from Notifications in that they have an optional return type.
To create a return type, create a new file debug.response.json.

As above, the response can contain an object with properties.

For example, it might be common to not have any params, but to expect a return types (for example, when fetching data).

To create that situation, create the following 2 file

  • fetchData.request.json
  • fetchData.response.json
{ "$schema": "http://json-schema.org/draft-07/schema#" }
{
  "$schema": "http://json-schema.org/draft-07/schema#",
  "title": "MyData",
  "type": "object",
  "properties": {
    "a": {
      "type": "string"
    }
  }
}

These 2 files would create the following:

export interface FetchDataRequest {
  method: "setValue";
  result: MyData;
}
export interface MyData {
  a: string
}

subscriptions

Subscriptions use the file format onChange.subscribe.json.

  • subscriptionEvent: onChange from the filename
  • params: as above, anything specified under the top level type will be used
Generated Types

From those JSON files, we get these types that can be imported and used in each feature

/**
 * Requests, Notifications and Subscriptions from the Web Compat feature
 */
export interface WebCompatMessages {
  requests: WebShare;
}
/**
 * todo: add description for `webShare` message
 */
export interface WebShare {
  method: "webShare";
  params: WebShareParams;
  /**
   * todo: add return type here
   */
  result: {
    [k: string]: unknown;
  };
}
export interface WebShareParams {
  /**
   * todo: add description for 'title' field
   */
  title?: string;
  /**
   * todo: add description for 'url' field
   */
  url?: string;
  /**
   * todo: add description for 'text' field
   */
  text?: string;
}
Javascript Usage

Once we have the types, we can proxy calls to notify, request and subscribe and provide
type-checking.

For example, from the types above we know that this feature can only make a request for webShare.

export class WebCompat extends ContentFeature {
  init() {
    this.request('webShare', dataToSend)
      .then(result => console.log(result))
      .catch(console.error)
  }
}
image

@shakyShane
Copy link
Collaborator Author

shakyShane commented Nov 21, 2023

This stack of pull requests is managed by Graphite. Learn more about stacking.

Join @shakyShane and the rest of your teammates on Graphite Graphite

@shakyShane shakyShane changed the title schema-based message definitions Proposal: schema-based message definitions Nov 21, 2023
@shakyShane shakyShane force-pushed the shane/schema branch 2 times, most recently from cf001bb to 92e2387 Compare March 13, 2024 09:35
@shakyShane
Copy link
Collaborator Author

What's changed in the update:

  • Removed the need to specify any types in the feature file 馃帀
    • The original had a requirement where the feature author had to add the following JSDOC comment within each feature: @extends {ContentFeature<import('../types/web-compat.js').WebCompatMessages>}
    • The new version completely removes this - instead we import and augment the module inside a types files that the feature author never touches

@shakyShane shakyShane marked this pull request as ready for review March 13, 2024 10:15
@shakyShane shakyShane changed the title Proposal: schema-based message definitions Add support for schema-based message definitions Mar 13, 2024
@shakyShane
Copy link
Collaborator Author

@jonathanKingston I need to add a couple of unit tests for the code-gen part, but this is ready to re-review now :)

@shakyShane
Copy link
Collaborator Author

CC: @muodov

scripts/build-types.mjs Outdated Show resolved Hide resolved
src/content-feature.js Outdated Show resolved Hide resolved
src/globals.d.ts Outdated Show resolved Hide resolved
src/types/web-compat.ts Outdated Show resolved Hide resolved
@shakyShane
Copy link
Collaborator Author

@jonathanKingston I updated the description with the new simpler model.

@shakyShane
Copy link
Collaborator Author

@jonathanKingston can you take another look through this 馃檹馃徎

Copy link
Collaborator

@jonathanKingston jonathanKingston left a comment

Choose a reason for hiding this comment

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

Thanks for getting this over the line! Really great work!

@shakyShane shakyShane merged commit f20b378 into main Apr 8, 2024
4 checks passed
@shakyShane shakyShane deleted the shane/schema branch April 8, 2024 11:29
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.

None yet

3 participants