Conversation
Added endpoints for managing open extensions on DriveItems, including listing, retrieving, creating, updating, and deleting extensions.
There was a problem hiding this comment.
Pull request overview
Adds Libre Graph v1beta1 OpenAPI documentation for DriveItem open extensions, allowing clients to attach arbitrary key/value metadata to files and folders and (optionally) retrieve it via $expand=extensions.
Changes:
- Adds
GET/PUT/DELETEendpoints under/v1beta1/drives/{drive-id}/items/{item-id}/extensionsfor list/get/upsert/delete. - Extends the
driveItemschema with anextensionscollection (documented as returned only on$expand). - Introduces
openTypeExtensionandopenTypeExtensionUpdateschemas to model extension read/update payloads.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 1 out of 1 changed files in this pull request and generated 3 comments.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 1 out of 1 changed files in this pull request and generated no new comments.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
|
I'm not sure about indexing the extension data - maybe we should have a rough idea how we want to do that (or not) even if we dont implement it straight away |
Summary
Add API endpoints for open extensions on DriveItems, enabling applications to attach arbitrary custom metadata to files and folders via the Libre Graph API.
Motivation
Currently, there is no way to attach application-specific metadata to DriveItems through the Libre Graph API. While OpenCloud's WebDAV layer supports arbitrary properties via PROPPATCH/PROPFIND, this functionality is not exposed through the Graph API. This means applications that use the Graph API cannot store and retrieve custom metadata on files.
Open extensions close this gap. They provide a simple, schema-less mechanism for any application to store key-value data on a DriveItem, identified by a unique extension name.
Use case
Avoid small extension services having to introduce their own shadow metadata storage that needs to be kept in sync with the storage/index in OpenCloud.
I wouldn't do it in the first iteration, but in the long run the data could potentially be indexed by the search service as well. Needs more thought and planning.
API design
Endpoints
Four new endpoints under
v1beta1:GET.../items/{item-id}/extensionsGET.../items/{item-id}/extensions/{extensionName}PUT.../items/{item-id}/extensions/{extensionName}DELETE.../items/{item-id}/extensions/{extensionName}DriveItem schema extension
The
driveItemschema gains anextensionsproperty (array ofopenTypeExtension), returned only when the client requests$expand=extensions.Schemas
openTypeExtension: Object with a read-onlyextensionNameandadditionalProperties: truefor the free-form data.openTypeExtensionUpdate: Object withadditionalProperties: { nullable: true }to allownullvalues for property deletion.Example flow
Create/set properties:
201 CreatedUpdate a single property (merge):
’
200 OK.assigneeandpriorityremain unchanged.Remove a property:
200 OKpriorityis removed, other properties remain.Read:
{ "extensionName": "com.example.project", "status": "approved", "assignee": "alice" }Delete entire extension:
→
204 No ContentDesign considerations
PUT with upsert instead of POST + PATCH
Microsoft Graph uses
POSTto create andPATCHto update extensions, requiring the client to know whether the extension already exists. This separation makes sense when the server generates the resource identifier, but extension names are client-chosen (reverse DNS convention). Since the client determines the target URI,PUTis the semantically correct HTTP method.This is consistent with existing Libre Graph API patterns: profile photos use
PUTwith upsert semantics (UpsertProfilePhoto), and tags usePUTfor assignment (AssignTags). Neither uses the POST-to-create / PATCH-to-update split.Merge semantics on PUT
For DriveItems specifically, Microsoft Graph's beta API uses merge semantics on PATCH: properties not included in the request body remain unchanged, and properties set to
nullare removed. We adopt the same behavior on PUT:nullremoved from the extensionDELETEon the extension removes the extension and all its propertiesNote: Microsoft's documentation for open extensions is internally contradictory on this point - for directory objects it describes replace semantics, for other resources (including DriveItems) it describes merge semantics. We follow the DriveItem-specific behavior.
Naming convention
Extension names should use reverse DNS notation (e.g.
com.example.myApp) to avoid collisions between applications. This matches the Microsoft Graph convention forextensionName.Relation to Microsoft Graph API
Microsoft Graph supports open extensions on DriveItems only in its beta API it is not available in v1.0. In practice, developers report that the DriveItem support is unreliable, and Microsoft recommends using SharePoint listItem fields as a workaround instead.
This means there is no stable MS Graph API to be compatible with. We are free to design the cleanest API for this use case. The key differences from MS Graph beta:
POST .../extensionsPUT .../extensions/{name}(upsert)PATCH .../extensions/{name}PUT .../extensions/{name}(upsert)nullnullPlanned implementation
Storage mapping
The implementation can build directly on OpenCloud's existing
ArbitraryMetadatainfrastructure in the CS3/reva layer no storage-layer changes are required.Each extension maps to a set of
ArbitraryMetadatakeys using a fixed namespace prefix:The Graph service handler translates between the JSON representation and the flat key-value pairs:
SetArbitraryMetadatafor all non-null properties,UnsetArbitraryMetadatafor null propertiesGetMDwith the extension's key prefix, strips the prefix, groups by extension name, returns as JSONUnsetArbitraryMetadatafor all keys matching the extension's prefix$expand=extensionsrequests all keys with thehttp://opencloud.eu/ns/extensions/prefix, groups them into extension objectsCross-protocol access via WebDAV
Because the extension data is stored as standard
ArbitraryMetadatawith a well-defined key format, it is automatically accessible via WebDAV without any additional implementation:A WebDAV PROPFIND requesting properties in this namespace will return the extension data. A PROPPATCH setting properties in this namespace will update it. This means applications can read and write the same metadata through both protocols interchangeably.
Scope of implementation
The implementation requires:
services/graph/pkg/service/v0/service.go)api_driveitem_extensions.go)driveitems.goto populate theextensionsfield when$expand=extensionsis requestedFuture work (out of scope)
Of course I'm willing to implement this.