-
Notifications
You must be signed in to change notification settings - Fork 12
Introducing support for Containers in the LWS protocol #81
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
Changes from 8 commits
2531274
e8dba98
b6bed3d
ed3b22e
3b04b7c
310e576
650531a
fb0627a
3b88cd5
b7769aa
33b9f19
2ae6457
0fd4ead
d523005
41f0f39
cb2c892
6905521
5a60a4b
b1db70e
e2ab194
96e5016
0e34292
e7f047f
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -21,11 +21,15 @@ The **create resource** operation adds a new [served resource](#dfn-served-resou | |
| * **Conflict:** A resource with the generated identifier already exists, or there is another state conflict. | ||
| * **Unknown Error:** An unexpected internal error occurred. | ||
|
|
||
| New resources are created using POST to a target container URI, with the server assigning the final identifier. Clients MAY suggest a name via the Slug header. Clients MAY provide initial user-managed metadata for the new resource by including one or more Link headers in the POST request, following the syntax of Web Linking in [[RFC8288]]. Server-managed metadata MUST be generated automatically by the server upon creation and MUST NOT be overridden by client-provided links. | ||
| On success, return the 201 status code with the new URI in the Location header. The server MUST include Link headers for key server-managed metadata, such as a link to the parent container (rel="partOf"), a link to the ACL resource (rel="acl"), and a link to its dedicated linkset resource (rel="linkset"; type="application/linkset+json"). Additional links SHOULD include rel="type" (indicating Container or DataResource) and rel="mediaType" if applicable. The body MAY be empty or include a minimal representation of the resource. All metadata creation and linking MUST be atomic with the resource creation to maintain consistency. | ||
| New resources are created using POST to a target container URI, with the server assigning the final identifier. Clients MAY suggest a name via the `Slug` header. Clients MAY provide initial user-managed metadata for the new resource by including one or more `Link` headers in the POST request, following the syntax of Web Linking in [[RFC8288]]. Server-managed metadata MUST be generated automatically by the server upon creation and MUST NOT be overridden by client-provided links. | ||
|
|
||
| On success, return the 201 status code with the new URI in the `Location` header. The server MUST include `Link` headers for key server-managed metadata, such as a link to the parent container (`rel="up"`), a link to the ACL resource (`rel="acl"`), and a link to its dedicated linkset resource (`rel="linkset"; type="application/linkset+json"`). Additional links SHOULD include `rel="type"` (indicating `Container` or `DataResource`). The body MAY be empty or include a minimal representation of the resource. All metadata creation and linking MUST be atomic with the resource creation to maintain consistency. | ||
|
laurensdeb marked this conversation as resolved.
Outdated
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. ACL wording is probably old copy-paste things There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I suggest to not state " |
||
|
|
||
| **POST (to a container URI)** – *Create with server-assigned name:* | ||
| Use POST to add a new resource inside an existing container. The server assigns an identifier to the resource, optionally suggested via the Slug header. The server MAY honor the Slug header if it does not conflict with naming rules or existing resources. Clients MUST include a Content-Type header in the request to indicate the media type of the new resource, enabling the server to distinguish between resource types. Specifically, servers MUST interpret the Content-Type as follows: if it matches the LWS-defined media type for containers (application/lws+json), the server creates a Container; otherwise, it creates a DataResource with the specified media type. | ||
| Use POST to add a new resource inside an existing container. The server assigns an identifier to the resource, optionally suggested via the `Slug` header. The server MAY honor the Slug header if it does not conflict with naming rules or existing resources. Clients indicate the type of resource to create as follows: | ||
|
|
||
| - To create a **Container**, the client MUST include a `Link` header with `rel="type"` pointing to the Container type: `Link: <https://www.w3.org/ns/lws#Container>; rel="type"`. | ||
| - To create a **DataResource**, the client includes the resource content in the request body with the appropriate `Content-Type` header. | ||
This comment was marked as resolved.
Sorry, something went wrong.
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This is one of those things that the user will want to do in one step ("{put|copy|mov} this folder and all its contents there"), which the software may need to do in several (create a container named xyz at location bcd, and put each of these documents inside it; and (for a move) after all that has succeeded, delete the originals). Several pieces of ACID consideration come into play. We should NOT try to reinvent specifications for all these processes and activities, but learn from the wheels that already exist, and re-implement the things that have been previously specified. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Agree. How do you suggest we proceed? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. (I just saw next comment about #87 , we can continue there 👌 ) |
||
|
|
||
| **Example (POST to create a new data resource):** | ||
| ``` | ||
|
|
@@ -35,46 +39,58 @@ Authorization: Bearer <token> | |
| Content-Type: text/plain | ||
| Content-Length: 47 | ||
| Slug: shoppinglist.txt | ||
|
|
||
| milk | ||
| eggs | ||
| bread | ||
| butter | ||
| apples | ||
| orange juice | ||
| ``` | ||
| In this example, the client is posting to the container `/alice/notes/`. It provides `text/plain` content (a grocery list) and suggests the name `shoppinglist.txt` for the new resource. If `/alice/notes/` exists and the client is authorized, the server will create a new DataResource (based on the Content-Type), generate associated metadata, and link it via the linkset. | ||
| In this example, the client is posting to the container `/alice/notes/`. It provides `text/plain` content (a grocery list) and suggests the name `shoppinglist.txt` for the new resource. If `/alice/notes/` exists and the client is authorized, the server will create a new DataResource and add it to the container's membership. | ||
|
|
||
| **Example (Response to POST):** | ||
| **Example (Response to POST — data resource):** | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I suggest to consistently use the same term and capitalization (for easier searching in the spec doc). So maybe consistently use "Data Resource" (two words, title caps), so we can also differentiate our search with the rel-type URI? |
||
| ``` | ||
| HTTP/1.1 201 Created | ||
| Location: /alice/notes/shoppinglist.txt | ||
| Content-Type: text/plain; charset=UTF-8 | ||
| ETag: "def789012" | ||
| Link: </alice/notes/shoppinglist.txt.meta>; rel="linkset"; type="application/linkset+json" | ||
| Link: </alice/notes/>; rel="partOf" | ||
| Link: </alice/notes/>; rel="up" | ||
| Link: </alice/notes/shoppinglist.txt.acl>; rel="acl" | ||
| Link: <https://www.w3.org/ns/lws#DataResource>; rel="type" | ||
| Content-Length: 0 | ||
| ``` | ||
| On success, return 201 Created with the new URI in the Location header. The body may be empty or a minimal representation. Include relevant headers such as Content-Type matching the created resource; Content-Length: 0 indicates no body. Server responses MUST use entity tags for responses that contain resource representations or successful responses to HEAD requests, enabling concurrency control in subsequent operations. | ||
| On success, return 201 Created with the new URI in the `Location` header. The body may be empty or a minimal representation. Server responses MUST use entity tags for responses that contain resource representations or successful responses to HEAD requests, enabling concurrency control in subsequent operations. | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Should we consistently write return statements as MUSTs for the server?
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Note that ETags should not be part of the successful response to a creation request. See #62 |
||
| If the target container `/alice/notes/` does not exist, the server MUST return a 404 error status unless another status code is more appropriate. | ||
This comment was marked as resolved.
Sorry, something went wrong. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. (auto-creation of target containers is probably also an optional capability, cfr the discussion of multi-parent containment of today)
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Given that this is not a section of the spec which was modified in this PR I would propose to split this off into a separate issue.
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. See #87 |
||
|
|
||
| **Creating Containers:** To create a new container via the REST API, a client uses POST to an existing parent container, with the Content-Type header set to the LWS-defined media type `application/lws+json` to indicate container creation. The body MAY be empty. Servers MUST support creation of containers using this media type. For example: | ||
| **Creating Containers:** To create a new container, a client uses POST to an existing parent container with a `Link` header indicating the Container type. For example: | ||
| ``` | ||
| POST /alice/ HTTP/1.1 | ||
| Host: example.com | ||
| Authorization: Bearer <token> | ||
| Content-Type: application/lws+json | ||
| Content-Length: 0 | ||
| Slug: notes | ||
| Link: <https://www.w3.org/ns/lws#Container>; rel="type" | ||
| ``` | ||
|
|
||
| **Example (Response to POST — container):** | ||
| ``` | ||
| HTTP/1.1 201 Created | ||
| Location: /alice/notes/ | ||
| ETag: "container-new-123" | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. An ETag is for representations. A response with no content should not have an etag header. It will mess with caches |
||
| Link: </alice/notes/.meta>; rel="linkset"; type="application/linkset+json" | ||
| Link: </alice/>; rel="up" | ||
| Link: </alice/notes/.acl>; rel="acl" | ||
| Link: <https://www.w3.org/ns/lws#Container>; rel="type" | ||
| Content-Length: 0 | ||
| ``` | ||
| This would create a new container at `/alice/notes/`, with server-generated metadata including rel="type" as https://www.w3.org/ns/lws#Container. | ||
| This creates a new container at `/alice/notes/`, with server-generated metadata including `rel="type"` as `https://www.w3.org/ns/lws#Container`. | ||
|
|
||
| **Additional notes on Create (HTTP binding):** | ||
| * POST is not idempotent. Repeating it may create duplicates; clients SHOULD avoid unintentional retries or use unique identifiers/checks to prevent this. | ||
This comment was marked as resolved.
Sorry, something went wrong.
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Humans rarely discuss recursive create or recursive delete, except in context of UNIX-like environments, and even there, recursive delete is only spoken of in context of specific implementations. Humans want to "delete this folder and everything in it", or "(copy or move) this folder and all its contents to that other location". I do not think that recursive create and/or recursive delete should be tied together, because they do not really reflect the same capability, especially once they get described with full algorithmic steps. I may be proven wrong when such expression exists, but until that point I strongly believe that each activity should be explored only as itself, as I think it very likely that some implementation(s) will not support both of these conceptual features.
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @bjdmeest Could you maybe raise a separate issue clarifying what you mean by "recursive creation"? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. |
||
| * Servers MUST distinguish between DataResource and Container types in metadata upon creation, based on the Content-Type header in the request. | ||
| * Metadata updates are atomic; servers MUST ensure the linkset resource is created and populated with mandatory server-managed fields before returning success. | ||
| * For discoverability, servers SHOULD include a Link header with rel="storageDescription" on 401 responses to guide clients without hardcoded URIs. | ||
| * For discoverability, servers SHOULD include a `Link` header with `rel="storageDescription"` on 401 responses to guide clients without hardcoded URIs. | ||
|
|
||
| **Managing and Retrieving Metadata (Related to Creation):** | ||
| While metadata is primarily retrieved via read operations, it is generated during creation. Clients can immediately retrieve it post-creation using GET or HEAD on the new resource URI. Clients can use the Prefer header to request inclusion of specific metadata links (via relation types) and attributes. | ||
| While metadata is primarily retrieved via read operations, it is generated during creation. Clients can immediately retrieve it post-creation using GET or HEAD on the new resource URI. Clients can use the `Prefer` header to request inclusion of specific metadata links (via relation types) and attributes. | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -8,13 +8,16 @@ Permanently removes a resource and its associated metadata. | |
|
|
||
| The delete resource operation is implemented using the HTTP DELETE method, as defined in the abstract operation above. This section specifies the HTTP bindings for inputs, behaviors, and responses. | ||
|
|
||
| The DELETE request targets the URI of the resource or container to remove. Clients MAY include an If-Match header with an ETag for concurrency checks, as described in the abstract operation. | ||
| The DELETE request targets the URI of the resource or container to remove. Clients MAY include an `If-Match` header with an ETag for concurrency checks, as described in the abstract operation. | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think mentions of 'abstract operation' is no longer relevant? |
||
|
|
||
| For non-container resources, the server processes the deletion as specified in the abstract behavior. | ||
| **Deletion and Containment:** | ||
| When a resource is deleted, the server MUST atomically remove it from its parent container's `items` list. The parent container's `totalItems` count and ETag MUST be updated accordingly. | ||
|
|
||
| For container resources, the server defaults to non-recursive deletion. If recursion is desired and supported, clients MUST use the Depth: infinity header, as defined in [[RFC4918]]. Servers that do not support recursion MUST reject such requests with 501 Not Implemented. | ||
| For non-container resources, the server removes the resource content, its associated metadata (linkset), and the containment reference in the parent container. | ||
|
|
||
| On success, the server MUST respond with 204 No Content. Servers SHOULD support concurrency checks via If-Match with ETags; mismatches MUST yield 412 Precondition Failed. | ||
| For container resources, the server defaults to non-recursive deletion. If the container is not empty and recursion is not requested, the server MUST reject the request with 409 Conflict. If recursion is desired and supported, clients MUST use the `Depth: infinity` header, as defined in [[RFC4918]]. Servers that do not support recursion MUST reject such requests with 501 Not Implemented. | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 501 gives the impression that the server is not spec-compliant (but spec-aware). Why not either 500 or 405 and assume 'support for recursion' is an additional capability?
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. In general, if a client supplies a header that is not supported by a server, the server simply ignores it. I would suggest aligning this behavior with general practice. |
||
|
|
||
| On success, the server MUST respond with 204 No Content. Servers SHOULD support concurrency checks via `If-Match` with ETags; mismatches MUST yield 412 Precondition Failed. | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. For concurrency checks, there is more than just If-Match. I would suggest being more generic here and just refer to conditional requests. See also #62 |
||
|
|
||
| If the client lacks authorization, the server MUST return 403 Forbidden (if the client's identity is known but permissions are insufficient) or 401 Unauthorized (if no valid authentication is provided). In cases where revealing resource existence poses a security risk, the server MAY return 404 Not Found instead. | ||
|
|
||
|
|
@@ -24,7 +27,7 @@ DELETE /alice/notes/shoppinglist.txt HTTP/1.1 | |
| Authorization: Bearer <token> | ||
| If-Match: "abc123456" | ||
| ``` | ||
| Assuming the ETag matches and the client is authorized, the server deletes the resource, its metadata, and updates the containing container `/alice/notes/` atomically: | ||
| Assuming the ETag matches and the client is authorized, the server deletes the resource, its metadata, and removes it from the parent container `/alice/notes/` atomically: | ||
| ``` | ||
| HTTP/1.1 204 No Content | ||
| ``` | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,86 @@ | ||
| #### Managing Containment | ||
|
|
||
| When multiple containment is enabled, resources MAY belong to more than one container. This section defines the operations for adding existing resources to containers and removing resources from containers without deleting the resource itself. | ||
|
|
||
| ##### Adding a Resource to a Container | ||
|
|
||
| To add an existing resource to a container, a client sends a PATCH request to the target container's linkset resource using the `application/linkset-patch+json` media type. The patch body specifies the containment link to add. | ||
|
|
||
| The server MUST verify that: | ||
| - The target container exists and the client is authorized to modify it. | ||
| - The resource to be added exists. | ||
| - Adding the resource would not create a cycle in the containment graph (no self-containment, no circular ancestry). | ||
|
|
||
| On success, the server responds with 204 No Content. The container's `items` list and `totalItems` count are updated atomically. | ||
|
|
||
| **Example (Add a resource to a container):** | ||
| ``` | ||
| PATCH /alice/shared/.meta HTTP/1.1 | ||
| Authorization: Bearer <token> | ||
| Content-Type: application/linkset-patch+json | ||
| If-Match: "meta-v3" | ||
|
|
||
| { | ||
| "add": { | ||
| "linkset": [ | ||
| { | ||
| "anchor": "/alice/shared/", | ||
| "item": [ | ||
| { "href": "/alice/notes/shoppinglist.txt" } | ||
| ] | ||
| } | ||
| ] | ||
| } | ||
| } | ||
| ``` | ||
|
|
||
| Response: | ||
| ``` | ||
| HTTP/1.1 204 No Content | ||
| ETag: "meta-v4" | ||
| ``` | ||
|
|
||
| After this operation, `/alice/notes/shoppinglist.txt` appears in the `items` listing of both `/alice/notes/` and `/alice/shared/`. The resource now has two `rel="up"` links. | ||
|
|
||
| ##### Removing a Resource from a Container | ||
|
|
||
| To remove a resource from a container without deleting the resource, a client sends a PATCH request to the container's linkset resource specifying the containment link to remove. | ||
|
|
||
| The server MUST verify that: | ||
| - The target container exists and the client is authorized to modify it. | ||
| - The resource is currently a member of the target container. | ||
|
|
||
| **Orphan handling:** If removing the resource from this container would leave it with no parent containers, the server MUST reject the request with 409 Conflict, unless the server supports orphan handling policies (e.g., automatic deletion of orphaned resources or moving them to a designated container). Servers that support automatic orphan handling MUST document their policy. | ||
|
|
||
| On success, the server responds with 204 No Content. | ||
|
|
||
| **Example (Remove a resource from a container):** | ||
| ``` | ||
| PATCH /alice/shared/.meta HTTP/1.1 | ||
| Authorization: Bearer <token> | ||
| Content-Type: application/linkset-patch+json | ||
| If-Match: "meta-v4" | ||
|
|
||
| { | ||
| "remove": { | ||
| "linkset": [ | ||
| { | ||
| "anchor": "/alice/shared/", | ||
| "item": [ | ||
| { "href": "/alice/notes/shoppinglist.txt" } | ||
| ] | ||
| } | ||
| ] | ||
| } | ||
| } | ||
| ``` | ||
|
|
||
| Response: | ||
| ``` | ||
| HTTP/1.1 204 No Content | ||
| ETag: "meta-v5" | ||
| ``` | ||
|
|
||
| ##### Concurrency Control | ||
|
|
||
| All containment management operations MUST use optimistic concurrency control. Clients MUST include an `If-Match` header with the current ETag of the container's linkset resource. If the ETag does not match, the server MUST respond with 412 Precondition Failed. If the `If-Match` header is missing, the server MUST respond with 428 Precondition Required. |
This comment was marked as resolved.
Sorry, something went wrong.
Uh oh!
There was an error while loading. Please reload this page.
This comment was marked as resolved.
Sorry, something went wrong.
Uh oh!
There was an error while loading. Please reload this page.