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

CoAP-based TDD API #551

Open
JKRhb opened this issue Feb 13, 2025 · 0 comments
Open

CoAP-based TDD API #551

JKRhb opened this issue Feb 13, 2025 · 0 comments

Comments

@JKRhb
Copy link
Member

JKRhb commented Feb 13, 2025

One of the workitems for the current WG charter period is the specification of a CoAP-based API for TDDs. I tried to deal with this aspect a little bit and for now came up with an (partially) incomplete first draft of a corresponding Thing Model which can serve as a basis for future discussions. As we go forward, I would gradually update the TM shown below to reflect the latest state of discussion and apply bug fixes.

Aspects that I found interesting while dealing with this potential "alternative" API:

  1. I think there is a question of how to allow for "modularity", e.g., how to best provide reusable definitions that allow implementations to implement either of the two APIs (HTTP and CoAP) or both of them. In this context, we might need to take a closer look at our extension and import mechanisms, and how to best utilize them for a scenario like this one.
  2. Ideally, supporting CoAP should also go along with supporting CBOR to achieve data transmissions that are as efficient as possible. To fully support CBOR (or rather at the same level as we support) JSON, there are currently a couple of standardization gaps that would need to be filled, the most important one being CBOR-LD. However, I also noticed that, for instance, application/merge-patch+cbor is currently not referencable in a normative way. Including a different content type also raises the question of "modularity" or partially optional elements, as we cannot put mulitple alternative forms in the same Thing Model without forcing implementations to support all of them (also see Allow for more TM fields to be optional wot-thing-description#2042), if I am not mistaken.
  3. Using the CoAP vocabulary for this API also relates to Specifying CoAP Response Codes wot-binding-templates#254, where @ektrah raised the point that CoAP response codes in a TD are mostly irrelevant to a consumer as it receives them as part of a server's response anyway and will know how to handle them without any prior knowledge (or otherwise reject/ignore them). Still, we probably need this kind of vocabulary to be able to specify the API for implementors of the service, that should then omit the status codes (or the response-related information in general) in the TDs they expose.

I think some of these aspects are related to potential user cases/user stories which I will try to distill and submit to the use cases repository.

CoAP TDD API Thing Model
{
  "@context": [
    "https://www.w3.org/2022/wot/td/v1.1",
    "https://www.w3.org/2022/wot/discovery",
    {
      "cov": "https://www.example.org/coap-binding#"
    }
  ],
  "@type": ["tm:ThingModel", "ThingDirectory"],
  "title": "Thing Description Directory (TDD) Thing Model for CoAP",
  "version": {
    "model": "1.0.0"
  },
  "base": "{{DIRECTORY_BASE_URL}}",
  "tm:optional": [
    "/actions/createThing",
    "/actions/createAnonymousThing",
    "/actions/retrieveThing",
    "/actions/updateThing",
    "/actions/partiallyUpdateThing",
    "/actions/deleteThing",
    "/actions/searchJSONPath",
    "/actions/searchXPath",
    "/actions/searchSPARQL",
    "/events/thingCreated",
    "/events/thingUpdated",
    "/events/thingDeleted"
  ],
  "properties": {
    "things": {
      "description": "Retrieve all Thing Descriptions",
      "uriVariables": {
        "offset": {
          "title": "Number of TDs to skip before the page",
          "type": "number",
          "default": 0
        },
        "limit": {
          "title": "Number of TDs in a page",
          "type": "number"
        }
      },
      "forms": [
        {
          "href": "/things{?offset,limit}",
          "cov:method": "GET",
          "response": {
            "description": "Success response",
            "cov:responseCode": "2.05",
            "contentType": "application/ld+json",
            "cov:contentFormat": 9001
          },
          "additionalResponses": [
            {
              "description": "Invalid query arguments",
              // Note: There is no Content Format for JSON-based problem details
              "contentType": "application/concise-problem-details+cbor",
              "cov:contentFormat": 257,
              "cov:responseCode": "4.00"
            }
          ]
        }
      ]
    }
  },
  "actions": {
    "createThing": {
      "description": "Create a Thing Description",
      "uriVariables": {
        "id": {
          "@type": "ThingID",
          "title": "Thing Description ID",
          "type": "string",
          "format": "iri-reference"
        }
      },
      "input": {
        "description": "The schema is implied by the content type",
        "type": "object"
      },
      "forms": [
        {
          "href": "/things/{id}",
          "cov:method": "PUT",
          "contentType": "application/td+json",
          "cov:contentFormat": 432,
          "response": {
            "description": "Success response",
            "cov:responseCode": "2.01"
          },
          "additionalResponses": [
            {
              "description": "Invalid serialization or TD",
              "contentType": "application/concise-problem-details+cbor",
              "cov:contentFormat": 257,
              "cov:responseCode": "4.00"
            }
          ]
        }
      ]
    },
    "createAnonymousThing": {
      "description": "Create an anonymous Thing Description",
      "input": {
        "description": "The schema is implied by the content type",
        "type": "object"
      },
      "forms": [
        {
          "href": "/things",
          "cov:method": "POST",
          "contentType": "application/td+json",
          "response": {
            "description": "Success response including the system-generated URI",
            "htv:headers": [
              {
                "description": "System-generated URI",
                "htv:fieldName": "Location"
              }
            ],
            "cov:responseCode": "2.01"
          },
          "additionalResponses": [
            {
              "description": "Invalid serialization or TD",
              "contentType": "application/concise-problem-details+cbor",
              "cov:contentFormat": 257,
              "cov:responseCode": "4.00"
            }
          ]
        }
      ]
    },
    "retrieveThing": {
      "description": "Retrieve a Thing Description",
      "uriVariables": {
        "id": {
          "@type": "ThingID",
          "title": "Thing Description ID",
          "type": "string",
          "format": "iri-reference"
        }
      },
      "output": {
        "description": "The schema is implied by the content type",
        "type": "object"
      },
      "safe": true,
      "idempotent": true,
      "forms": [
        {
          "href": "/things/{id}",
          "cov:method": "GET",
          "response": {
            "description": "Success response",
            "cov:responseCode": "2.05",
            "contentType": "application/td+json"
          },
          "additionalResponses": [
            {
              "description": "TD with the given id not found",
              "contentType": "application/concise-problem-details+cbor",
              "cov:contentFormat": 257,
              "cov:responseCode": "4.04"
            }
          ]
        }
      ]
    },
    "updateThing": {
      "description": "Update a Thing Description",
      "uriVariables": {
        "id": {
          "@type": "ThingID",
          "title": "Thing Description ID",
          "type": "string",
          "format": "iri-reference"
        }
      },
      "input": {
        "description": "The schema is implied by the content type",
        "type": "object"
      },
      "forms": [
        {
          "href": "/things/{id}",
          "cov:method": "PUT",
          "contentType": "application/td+json",
          "response": {
            "description": "Success response",
            "cov:responseCode": "2.04"
          },
          "additionalResponses": [
            {
              "description": "Invalid serialization or TD",
              "contentType": "application/concise-problem-details+cbor",
              "cov:contentFormat": 257,
              "cov:responseCode": "4.00"
            }
          ]
        }
      ]
    },
    "partiallyUpdateThing": {
      "description": "Partially update a Thing Description",
      "uriVariables": {
        "id": {
          "@type": "ThingID",
          "title": "Thing Description ID",
          "type": "string",
          "format": "iri-reference"
        }
      },
      "input": {
        "description": "The schema is implied by the content type",
        "type": "object"
      },
      "forms": [
        {
          "href": "/things/{id}",
          "cov:method": "PATCH",
          "contentType": "application/merge-patch+json",
          "cov:contentFormat": 52,
          "response": {
            "description": "Success response",
            "cov:responseCode": "2.04"
          },
          "additionalResponses": [
            {
              "description": "Invalid serialization or TD",
              "contentType": "application/concise-problem-details+cbor",
              "cov:contentFormat": 257,
              "cov:responseCode": "4.00"
            },
            {
              "description": "TD with the given id not found",
              "contentType": "application/concise-problem-details+cbor",
              "cov:contentFormat": 257,
              "cov:responseCode": "4.04"
            }
          ]
        }
      ]
    },
    "deleteThing": {
      "description": "Delete a Thing Description",
      "uriVariables": {
        "id": {
          "@type": "ThingID",
          "title": "Thing Description ID",
          "type": "string",
          "format": "iri-reference"
        }
      },
      "forms": [
        {
          "href": "/things/{id}",
          "cov:method": "DELETE",
          "response": {
            "description": "Success response",
            "cov:responseCode": "2.04"
          },
          "additionalResponses": [
            {
              "description": "TD with the given id not found",
              "contentType": "application/concise-problem-details+cbor",
              "cov:contentFormat": 257,
              "cov:responseCode": "4.04"
            }
          ]
        }
      ]
    },
    "searchJSONPath": {
      "description": "JSONPath syntactic search.  This affordance is not normative and is provided for information only.",
      "uriVariables": {
        "query": {
          "title": "A valid JSONPath expression",
          "type": "string"
        }
      },
      "output": {
        "description": "The schema depends on the given query",
        "type": "object"
      },
      "safe": true,
      "idempotent": true,
      "forms": [
        {
          "href": "/search/jsonpath?query={query}",
          "cov:method": "GET",
          "response": {
            "description": "Success response",
            "contentType": "application/json",
            "cov:contentFormat": 50,
            "cov:responseCode": "2.05"
          },
          "additionalResponses": [
            {
              "description": "JSONPath expression not provided or contains syntax errors",
              "contentType": "application/concise-problem-details+cbor",
              "cov:contentFormat": 257,
              "cov:responseCode": "4.00"
            }
          ]
        }
      ]
    },
    "searchXPath": {
      "description": "XPath syntactic search.  This affordance is not normative and is provided for information only.",
      "uriVariables": {
        "query": {
          "title": "A valid XPath expression",
          "type": "string"
        }
      },
      "output": {
        "description": "The schema depends on the given query",
        "type": "object"
      },
      "safe": true,
      "idempotent": true,
      "forms": [
        {
          "href": "/search/xpath?query={query}",
          "cov:method": "GET",
          "response": {
            "description": "Success response",
            "contentType": "application/json",
            "cov:contentFormat": 50,
            "cov:responseCode": "2.05"
          },
          "additionalResponses": [
            {
              "description": "XPath expression not provided or contains syntax errors",
              "contentType": "application/concise-problem-details+cbor",
              "cov:contentFormat": 257,
              "cov:responseCode": "4.00"
            }
          ]
        }
      ]
    },
    "searchSPARQL": {
      "description": "SPARQL semantic search",
      "uriVariables": {
        "query": {
          "title": "A valid SPARQL 1.1. query",
          "type": "string"
        }
      },
      "output": {
        "description": "The schema depends on the given query",
        "type": "object"
      },
      "safe": true,
      "idempotent": true,
      "forms": [
        {
          "href": "/search/sparql?query={query}",
          "cov:method": "GET",
          "response": {
            "description": "Success response",
            "contentType": "application/json",
            "cov:contentFormat": 50,
            "cov:responseCode": "2.05"
          },
          "additionalResponses": [
            {
              "description": "SPARQL query not provided or contains syntax errors",
              "contentType": "application/concise-problem-details+cbor",
              "cov:contentFormat": 257,
              "cov:responseCode": "4.00"
            }
          ]
        },
        {
          "href": "/search/sparql",
          "cov:method": "POST",
          "response": {
            "description": "Success response",
            "contentType": "application/json",
            "cov:contentFormat": 50,
            "cov:responseCode": "2.05"
          },
          "additionalResponses": [
            {
              "description": "SPARQL query not provided or contains syntax errors",
              "contentType": "application/concise-problem-details+cbor",
              "cov:contentFormat": 257,
              "cov:responseCode": "4.00"
            }
          ]
        }
      ]
    }
  },
  "events": {
    "thingCreated": {
      "description": "Registration of Thing Descriptions inside the directory",
      "uriVariables": {
        "diff": {
          "description": "Receive the full created TD as event data",
          "type": "boolean"
        }
      },
      "data": {
        "title": "Partial/Full TD",
        "type": "object"
      },
      "forms": [
        {
          "op": "subscribeevent",
          "href": "/events/thing_created{?diff}",
          "response": {
            "contentType": "application/json",
            "cov:contentFormat": 50
          }
        }
      ]
    },
    "thingUpdated": {
      "description": "Updates to Thing Descriptions within the directory",
      "uriVariables": {
        "diff": {
          "description": "Include TD changes inside event data",
          "type": "boolean"
        },
        "lastEventId": {
          "description": "ID of the last event for reconnection",
          "type": "integer"
        }
      },
      "data": {
        "title": "Partial TD",
        "type": "object",
        "contentMediaType": "application/merge-patch+json",
        "cov:contentFormat": 52
      },
      "forms": [
        {
          "op": "subscribeevent",
          "href": "/events/thing_updated{?diff}",
          "response": {
            "contentType": "application/json",
            "cov:contentFormat": 50
          }
        }
      ]
    },
    "thingDeleted": {
      "description": "Deletion of Thing Descriptions from the directory",
      "data": {
        "title": "Partial TD",
        "type": "object"
      },
      "uriVariables": {
        "lastEventId": {
          "description": "ID of the last event for reconnection",
          "type": "integer"
        }
      },
      "forms": [
        {
          "op": "subscribeevent",
          "href": "/events/thing_deleted{?lastEventId}",
          "response": {
            "contentType": "application/json",
            "cov:contentFormat": 50
          }
        }
      ]
    }
  }
}
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

No branches or pull requests

1 participant