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

add Sharing NG endpoints #112

Merged
merged 4 commits into from Oct 13, 2023
Merged

add Sharing NG endpoints #112

merged 4 commits into from Oct 13, 2023

Conversation

butonic
Copy link
Member

@butonic butonic commented Aug 9, 2023

libregraph API spec for the sharing ADR in owncloud/ocis#6995

When following a share lifecycle, the following OCS requests can be replaced with these libregraph counterparts:

List shares on a resource (includes sharing options)

OCS: web calls two endpoints:

  • /ocs/v1.php/apps/files_sharing/api/v1/shares?path={space-relative-path}&space_ref={space-id}&reshares=true
  • /ocs/v1.php/apps/files_sharing/api/v1/shares?path={space-relative-path}&space_ref={space-id}&shared_with_me=true

ms graph: /drives/{drive-id}/items/{drive-item-id}/permissions

{
    "value": [
        {
            "id": "collaborative-share-id",
            "roles": [
                "write"
            ],
            "grantedToV2": {
                "user": {
                    "displayName": "Albert Einstein",
                    "id": "4c510ada-c86b-4815-8820-42cdf82c3d51"
                }
            },
        },
        {
            "id": "public-link-share-id",
            "roles": [
                "read"
            ],
            "link": {
                "webUrl": "https://cloud.example.com/s/CMXOrzoFODpHKsS"
            }
        }
    ]
}

libre graph: for now we stick to two kinds of permissions:

  • grantedToV2 for internal user or group shares
  • link for public link shares
    jfd: roles are currently hardcoded to read, write and owner. We can make them customizable but I'll show that at the end of this description
{
    "value": [
        {
            "id": "collaborative-share-id",
            "roles": [
                "write"
            ],
            "grantedToV2": {
                "user": {
                    "displayName": "Albert Einstein",
                    "id": "4c510ada-c86b-4815-8820-42cdf82c3d51"
                }
            },
        },
        {
            "id": "public-link-share-id",
            "roles": [
                "read"
            ],
            "link": {
                "webUrl": "https://cloud.example.com/s/CMXOrzoFODpHKsS"
            }
        }
    ]
}

Search for recipient (not part of this PR)

OCS: /ocs/v2.php/apps/files_sharing/api/v1/sharees?search=einst&itemType=(folder|file)&page=1&perPage=200&format=json
ms graph: /me/people?$search="einst" is used to interact with users that are relevant or in a working-with relationship. The returned list of person entities has a personType property to differentiate types of groups and users.
libre graph: /me/people?$search="einst" is the only endpoint we need, I think. For now, a $search="einst" parameter can be used instead of the OCS sharee call.

Note: There is a difference between $search="foo bar" and $search=foo bar. To mimic OCS the request has to quote the typed in string.*

Create a share with user or group

OCS:

POST ocs/v1.php/apps/files_sharing/api/v1/shares

shareType=0
shareWith=marie
path=/Neuer Ordner
space_ref=storage-users-1$some-admin-user-id-0000-000000000000!71beebf5-0057-4104-a814-bb49712eaab9
permissions=31
role=editor

ms graph: sharing wit a user is done by posting an invite

POST /drives/{drive-id}/items/{drive-item-id}/invite

{
    "requireSignIn": true,
    "recipients": [
        {
            "email": "einstein@example.org"
        }
    ],
    "roles": [
        "read"
    ]
}

response:

{
    "@odata.context": "https://graph.microsoft.com/v1.0/$metadata#Collection(permission)",
    "value": [
        {
            "@odata.type": "#microsoft.graph.permission",
            "id": "r_mJa3kBqBtkotIl8tWt9nVA2L1",
            "roles": [
                "read"
            ],
            "shareId": "s!BFB3CkuhRCbBgmcZvWieFTBrBGQ",
            "expirationDateTime": "0001-01-01T00:00:00Z",
            "hasPassword": false,
            "grantedToV2": {
                "user": {
                    "id": "einstein@example.org"
                }
            },
            "invitation": {
                "email": "einstein@example.org",
                "signInRequired": true
            },
            "link": {
                "webUrl": "https://1drv.ms/f/s!BFB3CkuhRCbBgmcZvWieFTBrBGQ"
            }
        }
    ]
}

It seems the response does not use grantedToV2. And when requireSignIn is left out or false it will happily create a link webUrl that allows browsing the file.

libre graph: sharing wit a user is done by posting an invite. While ms graph uses email to identify a recipient libre graph assumes internal shares are created using the objectId (the users id)

POST /drives/{drive-id}/items/{drive-item-id}/invite

{
    "requireSignIn": true,
    "recipients": [
        {
            "objectId": "4c510ada-c86b-4815-8820-42cdf82c3d51"
        }
    ],
    "roles": [
        "read"
    ]
}

response:

{
    "value": [
        {
            "id": "8c5ed185-1fcb-4c5a-8569-b9ed04293204",
            "roles": [
                "read"
            ],
            "grantedToV2": {
                "user": {
                    "id": "4c510ada-c86b-4815-8820-42cdf82c3d51"
                }
            },
        }
    ]
}

jfd: multiple recipients can be sent in the same request. Each will receive a dedicated permissions object with their own id. WChen an error occurs a 207 multistatus response will be returned, similar to https://learn.microsoft.com/en-us/graph/api/site-follow?view=graph-rest-1.0&tabs=http#response-1 which shows an examplo where one of the entries contains an error
request:

POST /drives/{drive-id}/items/{drive-item-id}/invite

{
    "recipients": [
        {
          "objectId": "4c510ada-c86b-4815-8820-42cdf82c3d51"
        },
        {
          "objectId": "f7fbf8c8-139b-4376-b307-cf0a8c2d0d9c"
        }
    ],
    "roles": [
        "read"
    ]
}

response:

{
  "value": [
    {
      "id": "81d5bad3-3eff-410a-a2ea-eda2d14d4474",
      "roles": [
        "write"
      ],
      "grantedToV2": [
        {
          "user": {
            "id": "4c510ada-c86b-4815-8820-42cdf82c3d51",
            "displayName": "Albert Einstein"
          }
        }
      ]
    },
    {
      "id": "b470677e-a7f5-4304-8ef5-f5056a21fff1",
      "error": {
        "@odata.type": "#odata.error.main",
        "code": "invalidRequest",
        "message": "The user id that is provided in the request is incorrect",
        "innerError": {
            "code": "itemNotFound",
            "errorType": "expected",
            "message": "Unknown user id b470677e-a7f5-4304-8ef5-f5056a21fff1 ",
        }
      }
    }
  ]
}

Shared by me

List shares created by the currently logged in user
OCS: is called Shared With Others, see below
ms graph: odata query not implemented
onedrive web: can list shares by me in the web ui. maybe something is coming to ms graph as well?
libre graph: /me/drive/sharedByMe (only includes shares created by the current user)

Shared with others (not part of this PR)

Includes shares in project spaces that can be managed by the current user because he is a manager of the space. He does not need to be the creator of the share.
OCS: /ocs/v1.php/apps/files_sharing/api/v1/shares?reshares=true&include_tags=false&share_types=0,1,4,6
ms graph: odata query not implemented
libre graph: does not exist, but we could add a new /me/drive/sharedWithOthers endpoint

Note: the OCS share_types are 0 = User, 1 = Group, 3 = public link, 4 = guest, 6 = federated share, 7 = federated group / space member user (OH great we have a clash here), 8 = space member group.

Hint: There is a difference in the concept of shares. In OC10 shares are tied to a user. In OCIS they are tied to a space. The Owner/All managers of a space can collaboratively manage all shares in a personal/project space.

Shared with me

As the recipient you want to get a list of all driveItems that have been shared with you.
OCS: /ocs/v1.php/apps/files_sharing/api/v1/shares?include_tags=false&state=all&shared_with_me=true
ms graph: /me/drive/sharedWithMe
libre graph: /me/drive/sharedWithMe
The endpoint returns a collection(driveItem) that contains both: mounted and unmounted driveItems. shared driveItems are wrapped in another driveItem representing the mountpoint as on the ms graph API. But there are two deviations from the ms graph API:

  1. Shared driveItems that have not been mounted are not wrapped by another drive item. The ms graph api always wraps the shared driveItem. We do not want to do that on the fly as the driveItem representing the mountpoint would not have a parent id as it is unmounted. We will only wrap the shared driveItem with a mount point if the share has been created.
  2. libre graph for now does not use the shared property but expands the permissions relation as it contains all necessary information. This might change if we think the shared property is enough to show the necessary indicators. It would be faster than listing all details of the permission.
  3. libre graph uses a @UI.hidden annotation on driveItems to indicate
{
  "value": [
    {
      "id": "u-u-id-of-mountpoint",
      "name": "November-December Ad Proposals.pptx",
      "size": 100984,
      "parentReference": {
        "driveType": "personal",
        "driveId": "u-u-id-of-drive-containing-the-pointpoint",
        "id": "parent-u-u-id"
      },
      "remoteItem": {
        "id": "u-u-id-of-shared-driveItem",
        "name": "November-December Ad Proposals.pptx",
        "size": 100984,
        "parentReference": {
          "driveType": "personal",
          "driveId": "u-u-id-of-drive-containing-the-item"
          // no "id" because recipient is not allowed to see parent
        },
        "permissions": [
          {
            "@UI.hidden": false,
            "id": "92f276ac-827a-40c1-9d9c-bb41a628ce71",
            "roles": [
              "write"
            ],
            "grantedToV2": {
              "user": {
                "displayName": "Jörn Dreyer",
                "id": "c12644a14b0a7750"
              }
            }
          }
        ]
      }
    },
    {
      "id": "7fd82e03-09af-4b38-8d36-c7f7ec83cd99",
      "name": "Marketing Term Successes International.xlsx",
      "size": 17776,
      "parentReference": {
        "driveType": "personal",
        "driveId": "u-u-id-of-drive-containing-the-item"
        // no "id" because recipient is not allowed to see parent
      },
      "permissions": [
        {
          "@UI.hidden": false,
          "id": "477731b4-56a6-4f58-9530-2e08bdf52df5",
          "roles": [
            "write"
          ],
          "grantedToV2": {
            "user": {
              "displayName": "Jörn Dreyer",
              "id": "c12644a14b0a7750"
            }
          }
        }
      ]
    },
    {
      "id": "a7d033f9-6093-43bb-91a7-bc82265e6a7f",
      "name": "Irrelevant Marketing Term Successes International.xlsx",
      "size": 176536587,
      "parentReference": {
        "driveType": "personal",
        "driveId": "u-u-id-of-drive-containing-the-item"
        // no "id" because recipient is not allowed to see parent
      },
      "permissions": [
        {
          "@UI.hidden": true,
          "id": "5277be2e-db85-4d11-9a6c-28853142f279",
          "roles": [
            "write"
          ],
          "grantedToV2": {
            "user": {
              "displayName": "Jörn Dreyer",
              "id": "c12644a14b0a7750"
            }
          }
        }
      ]
    }
  ]
}

jfd: we cannot put the @UI.hidden property on the drive item directly, because it would mean the driveItem was hidden. So we put it on the permissions property. That hidden flag can be toggled.

Accept/Reject a share / an invite

OCS:

  • Accept POST ocs/v2.php/apps/files_sharing/api/v1/shares/pending/{share-id}
  • Deny DELETE ocs/v2.php/apps/files_sharing/api/v1/shares/pending/{share-id}
    ms graph: /sharedWithMe will list all shares, but
  • I cannot see a property that shows the mountpoint ... the parentReference only contains
"parentReference": {
    "driveId": "c12644a14b0a7750",
    "driveType": "personal"
},

libre graph: mount shares in the share jail by creating a driveItem using a POST /drives/{sharejailid}/items request:

{
  "name": "Einsteins project share",
  "remoteItem": {
    "id": "{drive-item-id}"
  }
}

This request can check if the current user has access to the given drive item and will 'mount'/ accept it with the name "Einsteins project share". In theory we could add a remoteItem anywhere in a drive like in OC10, but it is a product decision to collect them in the dedicated virtual share jail drive.

The mountpoint can be deleted/rejected by sending a DELETE /drives/{drive-item-id} where {drive-item-id} is the id of the drive item representing the mount point, not the remote item.

Create a public link

ms graph

POST /drives/{drive-id}/items/{drive-item-id}/createLink

{
    "type": "view",
    "scope": "anonymous"
}

response:

{
    "id": "{permission-id}",
    "link": {
        "type": "view",
        "webUrl": "https://1drv.ms/f/s!AlB3CkuhRCbBgmde472f8-qxYdg",
        "application": {
            "id": "4c1ad100"
        }
    }
}

Update shares / links

ms graph:
jfd: same as libregraph but I tried changing the role ... we'll just not go there for now
The role does affect the ui. It now shows upload elements even for non logged in users. The link type is still view. When trying to upload a file the web ui will try to log you in before making the request. The link type cannot be changed: in the UI there is a hint "This setting can't be changed. Create a new link if you need different permissions.". I guess this is to prevent changing permissions on existing links?

libre graph:

PATCH /drives/{drive-id}/items/{drive-item-id}/permissions/{permission-id}

{
    "link": {
        "type": "edit"
    }
}

response

{
    "id": "{permission-id}",
    "link": {
        "type": "edit",
        "webUrl": "https://1drv.ms/f/s!AlB3CkuhRCbBgmde472f8-qxYdg",
        "application": {
            "id": "4c1ad100"
        }
    }
}

For links we ignore the role and instead set the link type. ms graph has a lot predefined: https://learn.microsoft.com/en-us/graph/api/listitem-createlink?view=graph-rest-beta&tabs=http#link-types

Type value Description
view Creates a read-only link to the item.
review Creates a review link to the item. This option is only available for files in OneDrive for Business and SharePoint.
edit Creates an read-write link to the item.
embed Creates an embeddable link to the item.
blocksDownload Creates a read-only link that blocks download to the item. This option is only available for files in OneDrive for Business and SharePoint.
createOnly Creates an upload-only link to the item. This option is only available for folders in OneDrive for Business and SharePoint.
addressBar Creates the default link that is shown in the browser address bars for newly created files. Only available in OneDrive for Business and SharePoint. The organization admin configures whether this link type is supported, and what features are supported by this link type.
adminDefault Creates the default link to the item as determined by the administrator of the organization. Only available in OneDrive for Business and SharePoint. The policy is enforced for the organization by the admin

Delete shares / links

OCS DELETE ocs/v1.php/apps/files_sharing/api/v1/shares/{share-id}
libre graph DELETE /drives/{drive-id}/items/{drive-item-id}/permissions/{permission-id}

older notes

  • /drives/{drive-id}/items/{item-id}/invite to create shares
  • /drives/{drive-id}/items/{item-id}/createLink to create links
  • /me/drive/sharedWithMe to list shares with the current user
  • /me/drive/sharedByMe to list shares by the current user. This only includes shares created by the current user, not all shares he can manage, eg. in project spaces.
  • extend /drives to allow /drives?$expand=items&$filter=items/any(property:property/shared+ne+null), the listing of shares with others we went with a libregraph only /me/drive/sharedByMe endpoint instead
    • Q: should this work for all spaces or only on a per space/drive basis? in project spaces, does a user want to see all shares in the space or only the ones he created? This request lists any driveItem that is shared aka that has a shared facet
  • /drives/{drive-id}/items/{item-id}/permissions to list permissions
    • get permission
    • update permission
    • delete permission
  • How do we make the list of preconfigured roles configurable?
    • sharing roles as app roles / appRoleAssignment for a sharing app???
    • a sharing relationship on the roleManagement resource seems more fitting. appRoles are supposed to show up in access token scopes ... so ... nah
    • clients want to be able discover the sharing roles that can be used for a specific driveItem
      • uses a collectiong annotation in the list permissions response at /drives/{drive-id}/items/{item-id}/permissions:
        {
           "@libre.graph.permissions.roles.allowedValues": [
             {
               "name": "read",
               "displayname": "Viewer"
             },
             {
               "name": "write",
               "displayname": "Editor"
             },
             {
               "name": "owner",
               "displayname": "Manager"
             }
           ],
           "value": [
              ...
           ]
        }
        
    • /roleManagement/permissions/roleDefinitions
  • ms graph has /drives/{drive-id}/items/{item-id}/invite and /drives/{drive-id}/items/{item-id}/createLink. They both add permissions to drive items and the permission object as a result can carry a link or a grantedToV2 facet/property. This needs more investigation: permissioens have a role property but link objects themselves have a type ... and the existing link types in ms graph pretty much cover all we need. That being said we want to have them customizeable ... so they should be discoverable, similar to the sharing roles at /roleManagement/permissions/roleDefinitions

Further reading

It follows the MS Graph API, documented here:

Listing shares incoming / outgoing shares:

  • list shared with me https://learn.microsoft.com/en-us/graph/api/drive-sharedwithme
  • list shared with others has no official andpoint, but the semantically matching request might be https://graph.microsoft.com/v1.0/me/drive/items?$filter=shared ne null. It works by matching all driveItems that have no sharing facet. However, it is not implemented in ms graph. Maybe because it is not clear that /v1.0/me/drive/items works on ALL drives. Semantically, it should only list the shared drive items on the users personal drive. To match drive items on all drives the user has access to https://graph.microsoft.com/v1.0/drives?$expand=items&$filter=items/any(property:property/shared+ne+null) could be used to get a list of all drives that have shared drive items and expand the items. This is not allowed on the ms graph api.
    What does work is https://graph.microsoft.com/v1.0/me/drive/root/search(q='*')?filter=shared+ne+null but it is slow as it relies on the search() function. I'd go with the semantically correct version.

interesting sidenote

@butonic butonic changed the title add sharedWithMe [WIP] add sharedWithMe Aug 9, 2023
@butonic butonic changed the title [WIP] add sharedWithMe [WIP] add Sharing NG enpoints Aug 9, 2023
@butonic
Copy link
Member Author

butonic commented Aug 16, 2023

The web ui currently needs to PROPFIND all path segments to receive sharing information and look up the fileid for path segments... Some thoughts:

how do we get all driveItems that between the root and a child /a/b/c/d.txt?

  • GET all driveItems whose path is a prefix of a given path
  • GET all driveItems whose path is a prefix of eg /a/b/c/d.txt
  • GET /drive/items/?$filter=startswith('/a/b/c/d.txt',path)
  • GET /drive/items/?$filter=startswith('/drive/root:/a/b/c/d.txt',path)

for that the path in the parentReference needs to have a determinable path:

            "parentReference": {
                "driveId": "c12644a14b0a7750",
                "driveType": "personal",
                "id": "C12644A14B0A7750!6227",
                "name": "linux-5.19-rc6",
                "path": "/drive/root:/linux/linux-5.19-rc6"
            },   

https://graph.microsoft.com/v1.0/drives/C12644A14B0A7750/items

better to always return the /drives endpoint with the drive id:

This should always work: GET /drives/{uuid}/items/?$filter=startswith('/drives/{uuid}/a/b/c/d.txt',path)

ah no it lacks the parent reference

GET /drives/{uuid}/items/?$filter=startswith('/drives/{uuid}/a/b/c/d.txt',parentReference.path)

@dschmidt dschmidt mentioned this pull request Aug 16, 2023
39 tasks
@butonic
Copy link
Member Author

butonic commented Aug 23, 2023

I think sharing roles are best sent as an odata instance annotation of the collection returned when listing permissions:

{
  "@libre.graph.permissions.roles.allowedValues": { "read", "write", "owner", "editor" },
  "value": [
    {
      "id": "67445fde-a647-4dd4-b015-fc5dafd2821d",
      "roles": [
        "read"
      ],
      "shareId": "TODO the share id?",
      "link": {
        "type": "view",
        "webUrl": "https://cloud.example.org/TODO-URL-to-use-in-the-browser-bar"
      }
    },
    {
      "id": "81d5bad3-3eff-410a-a2ea-eda2d14d4474",
      "roles": [
        "write"
      ],
      "shareId": "TODO the share id?",
      "expirationDateTime": "2018-07-15T14:00:00.000Z",
      "hasPassword": "false,",
      "grantedTo": [
        {
          "user": {
            "id": "4c510ada-c86b-4815-8820-42cdf82c3d51",
            "displayName": "Albert Einstein"
          }
        }
      ],
      "invitation": {
        "email": "einstein@example.org",
        "signInRequired": true
      },
      "link": {
        "webUrl": "https://cloud.example.org/TODO-URL-to-use-in-the-browser-bar"
      }
    }
  ]
}

The allowed values can be configured in a way, similar to other role and permission relationships are managed in microsoft graph: the roleManagement resource.

The @libre.graph.permissions.roles.allowedValues property is an odata annotation, or in ms graph terms an instance attribute. It follows the odata naming scheme for a annotation identifier: it starts with @ because it annotates the collection, the namespace is libre.graph, I arbitrarily appended the permissions.roles to identify the term and used allowedValues to clarify what the annotation is about. We could use all lowercase?

Annotations can have a qualifier separated by #, eg. @libre.graph.permissions.roles#AllowedValues but I do not think that is what a qualifier is meant to represent if you look at the instance annotation example 53:

{
  "@context": "http://host/service/$metadata#Customers",
  "@com.example.customer.setkind": "VIPs",
  "value": [
    {
      "@com.example.display.highlight": true,
      "ID": "ALFKI",
      "CompanyName@com.example.display.style": { "title": true, "order": 1 },
      "CompanyName": "Alfreds Futterkiste",
      "Orders@com.example.display.style#simple": { "order": 2 }
    }
  ]
}

It feels more like qualifying a CSS class. So, I think the annotation identifier is ok.

Now for the value. I chose readable identiffiers, because I think that makes the api better understandable. We could use uuids then clients would have to look up the corresponding sharing roles at new the /rolemanagement/sharing endpoint. I think I prefer objects, because these annotations are only meaningful to build a ui when trying to present users with sharing roles objects can carry an id, a name, a translated displayname, a description, a hint ... whatever we may need ... even the full set of permissions if necessary.

When inviting a user or updating a permission I would prefer to use the name so the api stays compatible with microsoft graph as much as possible.

{
  "@libre.graph.permissions.roles.allowedValues": {
    { "name":"read", "displayname":"Viewer" },
    { "name":"write", "displayname":"Editor" },
    { "name":"owner", "displayname":"Manager" },
    { "name":"foo", "displayname":"Fooer", "description":"Can foo things, but not bar them." }
  },
  "value": [
    {
      "id": "67445fde-a647-4dd4-b015-fc5dafd2821d",
      "roles": [
        "read"
      ],
...

@butonic butonic changed the title [WIP] add Sharing NG enpoints [WIP] add Sharing NG endpoints Aug 24, 2023
api/openapi-spec/v1.0.yaml Outdated Show resolved Hide resolved
Copy link
Contributor

@rhafer rhafer left a comment

Choose a reason for hiding this comment

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

In general I think we need some good examples that fit the use-cases for ocis.

It's still somewhat unclear to me how the /invite this is supposed to work.
Is it only supposed to be used for sharing with other users and groups? Are "public" shares supposed to be created via /createLink

If that is the case we might want to remove the requireSignIn and password properties from the properites of the invite body.

api/openapi-spec/v1.0.yaml Outdated Show resolved Hide resolved
api/openapi-spec/v1.0.yaml Outdated Show resolved Hide resolved
api/openapi-spec/v1.0.yaml Outdated Show resolved Hide resolved
api/openapi-spec/v1.0.yaml Outdated Show resolved Hide resolved
api/openapi-spec/v1.0.yaml Outdated Show resolved Hide resolved
@butonic
Copy link
Member Author

butonic commented Aug 30, 2023

@jkoberg @micbar @rhafer I updated the examples and added the sharedByMe endpoint. please rereview

@butonic butonic requested review from micbar and rhafer August 30, 2023 15:50
rhafer
rhafer previously requested changes Aug 30, 2023
properties:
type:
type: string
description: Optional. The type of sharing link to create.
Copy link
Contributor

Choose a reason for hiding this comment

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

If this is optional, what kind of link is created, when no type is specified? If only a certain set of types is allowed I think we should specify those values here via enum

Copy link
Member Author

Choose a reason for hiding this comment

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

AFAICT the link type is redundant to sharing roles. I made the latter discoverable at the /roleManagement/permissions/roleDefinitions endpoint. I think we can probably leave out link types and reuse roles for links as well ...

api/openapi-spec/v1.0.yaml Outdated Show resolved Hide resolved
api/openapi-spec/v1.0.yaml Outdated Show resolved Hide resolved
api/openapi-spec/v1.0.yaml Outdated Show resolved Hide resolved
api/openapi-spec/v1.0.yaml Outdated Show resolved Hide resolved
@butonic butonic requested a review from rhafer August 30, 2023 17:13
api/openapi-spec/v1.0.yaml Outdated Show resolved Hide resolved
@butonic
Copy link
Member Author

butonic commented Sep 22, 2023

We need to fix the https://{{HOST}}/graph/v1.0/drives?$filter=driveType eq 'grant' requests to contain the permission relation and the share id so that ocis web can corralate it with the list of shares from ocs ... or get rid of ocs it completely ;-)

{
  "value": [
    {
      "driveType": "grant",
      "id": "storage-users-1$some-admin-user-id-0000-000000000000!c37cfa1b-f93c-407d-a2e1-ca9d102cc7df",
      "name": "",
      "owner": {
        "user": {
          "displayName": "",
          "id": "some-admin-user-id-0000-000000000000"
        }
      },
      "quota": {
        "remaining": 335284809728,
        "state": "normal",
        "total": 0,
        "used": 8
      },
      "root": {
        "id": "storage-users-1$some-admin-user-id-0000-000000000000!c37cfa1b-f93c-407d-a2e1-ca9d102cc7df",
        "webDavUrl": "https://cloud.ocis.test/dav/spaces/storage-users-1$some-admin-user-id-0000-000000000000%21c37cfa1b-f93c-407d-a2e1-ca9d102cc7df",
        "permissions": [
          {
            "id": "81d5bad3-3eff-410a-a2ea-eda2d14d4474",
            "roles": [
              "write"
            ],
            "shareId": "TODO the share id?",
            "expirationDateTime": "2018-07-15T14:00:00.000Z",
            "hasPassword": "false,",
            "grantedTo": [
              {
                "user": {
                  "id": "4c510ada-c86b-4815-8820-42cdf82c3d51",
                  "displayName": "Albert Einstein"
                }
              }
            ],
            "invitation": {
              "email": "einstein@example.org",
              "signInRequired": true
            },
            "link": {
              "webUrl": "https://cloud.example.org/TODO-URL-to-use-in-the-browser-bar"
            }
          }
        ]
      },
      "webUrl": "https://cloud.ocis.test/f/storage-users-1$some-admin-user-id-0000-000000000000%21c37cfa1b-f93c-407d-a2e1-ca9d102cc7df"
    }
  ]
}

onedrive allows listing all received shares regardless of their mount status by creating a driveItem ... maybe on the fly ... that is wrapped around the remoteItem in the /me/sharedWithMe list ... sneaky ...

but that still does not help us because we cannot find a difference in that mountpoint driveItem before and after quicklinking it.

We could leave out the parentReference to indicate that a driveItem has not been mounted ... because it has no parent 🤷‍♂️

On the graph api All driveItems in the sharedWithMe result set have a parentReference:

    "parentReference": {
        "driveId": "c12644a14b0a7750",
        "driveType": "personal"
    },

It is the same for all items, but I guess only because quicklinks will alll land in my personal space ... 🤔 They do not contain a drive item id to indicate exaclty which driveItem is the parent ... so we could use that as well to indicate the mount status ...

this all does not yet deal with how to 'hide' shares ... 😞

@butonic
Copy link
Member Author

butonic commented Sep 22, 2023

interesting:
grafik

Removing' a folder only seems to wipe it from the lists that are queried by the web ui ...

{
    "HttpStatusCode": 200,
    "RetryAfterHeader": null,
    "error": {
        "code": 3000,
        "debugMessage": "Item not found or access denied.",
        "isExpected": true,
        "message": "This item might have been deleted, expired, or you might not have permission to access it. Contact the owner of this item for more information.",
        "stackTrace": "3d3c5b6e1ebb7d041:R2FYwkV\/COoO7cMpIE1+bV4corvUIUSf\/DbVywcRQpMrXngNTWls8LjMpQTL4nxxbZWZmjy1Lnk0kDwUrchkw1VTuFxlJe3nLauY8X0S0t1i93UTr4vhEM3SWfpDKVISVEaQ9p\/TDHFoSs\/GCqWmGmmUNHNy73fGfoVGxwCCCfZV9QPj5AkzD1+sBlCWjw5UgbXJsAs6l8MXX9QbuJSm04VCFL8VWgB\/9UmIfbTiDaxI0Y9p4bdc2iAjnF\/gBdb0KdG8BY7D+AzYKnuCIycP6YeyiyU73T6GE0rgjkxnp1iAZM+9HpQ6wraecMwoaJpLLINcuJnu9ZGsO7\/bPCM1mwrz44e2yIPRy8PwdyP5LIsaKWeltI4kNfI5Yhy9WVRdNDE3FX3xhsjiEpzgoeWifHJa\/y1KNip+TY\/\/AW00HUAehHnJFTttpbm+nceUGRj01D9AtKdlz1NSZ6KWn59NaFOEIh0Y0PMUeaVhQtZWH3XqgeLSnGuWmQiVMBJ\/IQU9Wq9EIHlxKtQWX36l49ljrdckdXJ4KuTsAqj9VAyLUbYxnAFak2ePJyQISWWP546247AB0SWOut4fYl1PkcuCJynnRI8MYRRDcIDvSqh6KoFUdnrL2hm1MzZU+FQmn5mhB9PMADWdNGmtNEdeRL62kOT7f4OgPFk+Pw0fDzgIVUbz\/sJbssZ5E\/2Rh1iCvi5ZYzu0PN7\/\/7o\/yzhdO8m0d8tFXx0tXuOwvUe4I+nYxszQwGeHosJGtct4GgEd8lxY4eN0tuN4IclxzI+vw40TRAZ5sR4x9m6q1kqvuI5K2+K1tKeY9AJWv2xsmRhFr3ZMrxZ6iWx9T0SsDZJ63BKdtCh0Vyo0e\/EU87LYWkSZ+d7YGlcYz0fEX6Ft\/fE5VYw9nvisAt+J8FKlmmKBpJFvSAXwlmXiTfXdDeSkUo6hXgAcM1LaW38WQ4wUaaF14x1vSonvWLleD90WivDVHxgn8qndmMiEl3zQ0ZOZNSmfzw5zD9lxDA7j\/wrEw1kOF6dLCqMKf9umdRjSmBFKGpCXryTcCfr22xLYB4pxfjyYjKOhp90e+AJL2QfGiovn1UaqsqV8Rmk2KxPFHXiai5pjm9Nkzg\/WrOLTnWevSkGIro3dEGC6tlphDyMUAu1yjLPToSGxpMwBWqimnWtdBXme7t26cUfRalVSzmseYDg1gdYZAJ\/hS7krwAqWeZ4T0J+RtStNZ\/M9FeN+vEP737mPFdyLDhEjxSEUtNNzwTgDQ9hFrPMaS7+smb+tYxr\/LyJuRzKPRMDCtZpw0DhDX7jow240GtQlFW3b9l5\/AtqGg9g2vQ2Klrm8MVcVZR7XCMJ\/3LiKHh8PH0WNfTKn8D+e8wPqsaV3GBTUfcqPMiYk74fOY+iQHVu2yZn+zu+Bd+U3oAXY6NGzUMIYhsXzN72rTLCI\/1zuETVA7ASJGYO2aMs5un67G9Wnz+LrEcM+RMmBGcXK0oZwxPe1h0Nl9GmX\/T4uoqJoMmt3gI1NsX9JLHKORRVsraAJHha9ITg8MMCMWfC9R+e7a3XE07ooqwNbuinEI2ydtFwQPzvRMMgNMRqQUgSUaexaqOdzEO7V3tKSLjNtOeFy6vCPR8d4AqH95knVI9fTv+RIsLjGtb6VjAEZ6CD8hgMvNT7+rpzzQmtYVIyCWrN0CgRlkoSED8Dr18dl+csi05vTRSBAgzcz\/o9k5SYR9lbD81pO3NC+\/8Z\/EUtW1gOUwSHauvIHY4s1ruyRRj4AIZXuCQ12ZgzLVAgUy7J4bGrWIRnys5\/HnwKcSywtZQNmiGpHiMmmV7nETvPdJtfwGkXC0xb9OZ9nNOy5GBqDC9bjAon\/5bEP13EoeoT20pQNHASzCie1zsCdXHIZmJJz8mHz28kHhpd4iO0Wdi4NAbEM+ygxEhVJYx+yPN9gYYg+ndv40majhLPeok8vJEIAMC6d7xL6+6Tmu3Sv+xoHQECBgsZh1thOX+L83EHKkw3\/3WM3HAjndgcpoCYSFHLHYzSZPDOGnxZ0o1ObGj5C9OwILfI+9z\/rJp9zwV5saL07zeVbmNen9C+mkDRFmbgSeaK8OR3ypki2IOnXDTDgVHKQ3qNejcGsoldjCdL1zpy5455g2nPdy8\/J7EEUUNxZ3j8QZbtb5JjItnR+L49MN0iEP5d1mZHf09YKlUz9yjY54kjWBw6myHoIcopOW4hrPCs5o\/+Loo7KY\/czH3jRO5BUXJ+L\/uZr6sCMb5vO5qNCDHkbVLHL+\/bwQaff4IxyJ4Llv4fcJuumx2VXFM2Hs3\/tlyykgv479Ow9sKntR+sxwJunj4ZzGyVFmTPOAJk9D75v4Prda1JALACggnTJokgh8y2WcIRe6NUFZnhD7PdoHIoCal7haD0rcLhpE+IMNrprf5ny+DVUIpqficeAsoCBGDkzehlx6UsYp5WJTn5U+sml5xHncCa\/bEPSJS1AGw0nm\/px1CapBlLpdtZrEY+5PG9pultTjldjgZOc5E9A2iibPRpz\/vspMryzJa5AINCisPHbkVaBtqI5GU\/OjgJdnTVM5KyFCMACsf7sOY1tHkd2Ff7TVYZN\/9CSsqdk125\/wkUqdjuJ8PCq8gfhdPZ5LS\/SRiig5EBha1YpSJmFYFa1tbQjUdOrJXFsOen8fmXTPVd5RoJLDPN6yzc+BfTvrNasOB74MAt0yw4MYwS5TDUrPpV59BkQBLKDvA1APVOzYCxrEDZ4lSEq0Qk1Y13RIqwbWiA33jeXF72Rkj3T\/+phg+paYO0dlesyMJMNKEeH1iu2csjiL5WvEqZA+QYE\/Z5SFW41sp79MjKcmVOap5KEh5YzgEZgShDteMAPm4fNUkIntd6TseX5PVhGhG+aAER7wuO77cQxy6aB5Hv3Bca5zt+wQSgbz+AD38Y23e86hUKmDj+glF46TpX\/7\/OMvjHIlpeNHjpyqKRGI3qZpnhy\/DNUIx1W6x2CrD2d\/JYtuOUhemeg7hzGyf18GbT7yivvPmGBJbUutWD51sbF4e7UK4a3o1BvZNsMeIKACh5gdYnXrmJIgkho1VPQMlrMqCIJZKuNOC4BooQxd5GjrJU9WyW8BC8dlSGK9Ma9bdB+AxdUAUtYXlTBFqNw+jjqovEsdDQjgPMrep6s2mqWO8uNo4ATfVwDfciawcPz5UE5vTGX7l\/ihXaiZ+Jk\/LzAZkpsluSfyEQVg9Po9kTfhQEwO+2ppr5gxwQlTyVTN\/w9hCzIdkZFXsZOKon4mZTNWMUvI9PoLm0ZIBSkAozj\/2JoHMaetEwe2A=="
    },
    "transactionId": "BiV2RVnJ3kmtB9Zj5eGQLQ.0"
}

@butonic
Copy link
Member Author

butonic commented Sep 28, 2023

@dschmidt we were talking about the wish of the web client to list all shares in one request because the internal data model prefers that, or did I misunderstand that? Listing /sharedWithMe returns a list of driveItems. On Ms graph it only returns all shares, regardless of their mount status ... because it does not have a mount status. The Ms graph API represents mount points as driveItems with a remoteItem that wraps the shared driveItem. This way of wrapping the share in the mount point prevents listing shares without a mount point .... hmmm ... /sharedWithMe is a list of driveItems, so we could list unmounted shares as drive items without wrapping them in a drive Item that represents the mountpoint. While the graph API does not do that I think it semantically fits the concept. I'll add an example tomorrow.

Now about hiding shares... we could use a @libregraph.hidden annotation.

@dschmidt
Copy link
Member

@dschmidt we were talking about the wish of the web client to list all shares in one request because the internal data model prefers that, or did I misunderstand that?

Yeah, from Web's POV "sharedness" is just a property of a Drive(Item). So for us it would be nice to retrieve all shares accepted or not from the same endpoint.
Especially do we need the same data available, e.g. the shareId... and anything else we might need to access it/match urls

@butonic
Copy link
Member Author

butonic commented Sep 29, 2023

@dschmidt yeah ... so .... let me clarify what the /me/sharedWithMe endpoint could return:

{
  "@odata.context": "https://graph.microsoft.com/v1.0/$metadata#Collection(driveItem)",
  "value": [
    {
      "id": "u-u-id-of-mountpoint",
      "name": "November-December Ad Proposals.pptx",
      "size": 100984,
      "parentReference": {
        "driveType": "personal",
        "driveId": "u-u-id-of-drive-containing-the-pointpoint",
        "id": "parent-u-u-id"
      },
      "createdBy": { ... },
      "lastModifiedBy": { ... },
      "remoteItem": {
        "id": "u-u-id-of-shared-driveItem",
        "name": "November-December Ad Proposals.pptx",
        "size": 100984,
        "parentReference": {
          "driveType": "personal",
          "driveId": "u-u-id-of-drive-containing-the-item"
          // no "id" because recipient is not allowed to see parent
        },
        "shared": {
          "scope": "users",
          "sharedBy": {
            "user": { ... }
          }
        }
      }
    },
    {
      "id": "7fd82e03-09af-4b38-8d36-c7f7ec83cd99",
      "name": "Marketing Term Successes International.xlsx",
      "size": 17776,
      "parentReference": {
        "driveType": "personal",
        "driveId": "u-u-id-of-drive-containing-the-item"
        // no "id" because recipient is not allowed to see parent
      },
      "shared": {
        "scope": "users",
        "sharedBy": {
          "user": { ... }
        }
      }
    }
  ]
}

as you can see ... a mount point is not just a property ... it wraps a remoteItem ... but I think that might work, as to the end user the relevant properties are name and size. So when a driveItem in the list has a remoteItem it is a mountpoint. if it has no remoteItem it is a grant. Does that make sense?

ms graph only returns a superficial share object with few properties, but maybe sufficient to build a list in the ui. when selecting a share you would then fetch the share details ...

@butonic
Copy link
Member Author

butonic commented Sep 29, 2023

with regards to a @libre.graph.hidden annotation: we can follow the odata spec and include libregraph specific properties as annotations.

{
  "@odata.context": "https://graph.microsoft.com/v1.0/$metadata#Collection(driveItem)",
  "value": [
    {
      "@libre.graph.hidden": true,
      "id": "408082ff-98ac-4b48-aa47-3ae99c4a4bb0",
      ...
    },
    {
      "@libre.graph.hidden": true,
      "id": "7fd82e03-09af-4b38-8d36-c7f7ec83cd99",
      ...
    }
  ]
}

and changing the annotation would be a PATCH request:

PATCH /drives/{driveid}/items/{itemid}
{
  "@libre.graph.hidden": true
}

@dschmidt
Copy link
Member

dschmidt commented Oct 4, 2023

as you can see ... a mount point is not just a property ... it wraps a remoteItem ... but I think that might work, as to the end user the relevant properties are name and size. So when a driveItem in the list has a remoteItem it is a mountpoint. if it has no remoteItem it is a grant. Does that make sense?

The remoteItem points to the origin of the share (?) - why would it be missing for a grant? I don't get that logic.
Why can't we have a @libregraph.accepted annotation?

Ftr: The remoteItems we currently receive contain a path (yes the path in the original space), we want to get rid of this dependency, but for now we still depend on that for the full share owner paths feature.

Another reminder: we need access to the shareId, otherwise we cannot efficiently match urls to shares if we don't have access to full paths (from url and at some point neither from api) and if we may not walk up dirs to find the share, if we just have a fileId

@butonic
Copy link
Member Author

butonic commented Oct 12, 2023

When navigating into a random driveItem how can we find out the path up to the driveItem the user has been granted access to?

Every drilveItem has a parentReference. One of the propertios is path which we can use to return the whole path, relative to the drive root. See https://learn.microsoft.com/en-us/graph/api/driveitem-get?view=graph-rest-1.0&tabs=http

GET /graph/v1.0/drives/{driveId}/items/{driveItemId}

{
      "id": "7fd82e03-09af-4b38-8d36-c7f7ec83cd99",
      "name": "Marketing Term Successes International.xlsx",
      "parentReference": {
        "driveType": "project",
        "driveId": "marketing-space-id",
        "path": "/drives/{driveId}/root:/International/Foo/Bar"
      }
}

This should also works for shares by using a path that 'starts' at the shared resource:

GET /graph/v1.0/drives/{driveId}/items/{driveItemId}

{
      "id": "7fd82e03-09af-4b38-8d36-c7f7ec83cd99",
      "name": "Marketing Term Successes International.xlsx",
      "parentReference": {
        "driveType": "project",
        "driveId": "marketing-space-id",
        "path": "/drives/{driveId}/items/{driveItemId}:/International"
      }
}

I checked this request: https://graph.microsoft.com/beta/drives/36BDDD3D93D9831F/items/36BDDD3D93D9831F!220:/.DS_Store This works. The : switches from id based navigation to path based navigation and back. See https://learn.microsoft.com/en-us/graph/onedrive-addressing-driveitems#path-based-addressing and https://learn.microsoft.com/en-us/graph/api/resources/onedrive?view=graph-rest-1.0#commonly-accessed-resources which contains the example /me/drive/items/{item-id}:/path/to/folder:/children

A request that fetches all driveItems with name and id could be expressed as:

GET /drives/{driveId}/items/?$filter=startswith('/drives/{driveId}/root:/a/b/c/d.txt',path)

concrete example: We want to get all driveItems for /a/b/c/d/e/f/myfile.txt

GET /drives/{driveId}/items/?$filter=startswith('/drives/{driveId}/root:/a/b/c/d/e/f/myfile.txt', path)

path here is the path property of the driveItems. startsWith now selects all drive items whose path is a prefix of the files path.

@butonic
Copy link
Member Author

butonic commented Oct 12, 2023

@micbar I think I have now added all needed properties. odata uses vocabularies to extend entities and properties. I am using the SAP UI vocabulary to add the @UI.Hidden flag users can toggle to hide unwanted received permissions. I also tried to be spec conforamnt and used @libre.graph as the namespace for all the other custom annotations, introducing the libre graph odata vocabulary. Now ... I have no idea what any code generators will make of this, but this is how the spec says we should extend an existing odata api.

@micbar
Copy link
Contributor

micbar commented Oct 12, 2023

@dschmidt @TheOneRing @dragotin

something fails in the validate-cpp step. Can you take a look?

@butonic
Copy link
Member Author

butonic commented Oct 13, 2023

The @libre.graph... and @UI.Hidden annotations look fine:
c++

// ...

     QList<QString> libre_graph_permissions_actions;
     bool libre_graph_permissions_actions_isSet;
     bool libre_graph_permissions_actions_isValid;

     bool ui_hidden;
     bool ui_hidden_isSet;
     bool ui_hidden_isValid;

// ...

        d->libre_graph_permissions_actions_isSet = false;
        d->libre_graph_permissions_actions_isValid = false;

        d->ui_hidden_isSet = false;
        d->ui_hidden_isValid = false;

// ...

    d->libre_graph_permissions_actions_isValid = ::OpenAPI::fromJsonValue(d->libre_graph_permissions_actions, json[QString("@libre.graph.permissions.actions")]);
    d->libre_graph_permissions_actions_isSet = !json[QString("@libre.graph.permissions.actions")].isNull() && d->libre_graph_permissions_actions_isValid;

    d->ui_hidden_isValid = ::OpenAPI::fromJsonValue(d->ui_hidden, json[QString("@UI.Hidden")]);
    d->ui_hidden_isSet = !json[QString("@UI.Hidden")].isNull() && d->ui_hidden_isValid;

// ...

    if (d->libre_graph_permissions_actions.size() > 0) {
        obj.insert(QString("@libre.graph.permissions.actions"), ::OpenAPI::toJsonValue(d->libre_graph_permissions_actions));
    }
    if (d->ui_hidden_isSet) {
        obj.insert(QString("@UI.Hidden"), ::OpenAPI::toJsonValue(d->ui_hidden));
    }

// ...


QList<QString> OAIPermission::getLibreGraphPermissionsActions() const {
    Q_D(const OAIPermission);
    if(!d){
        return {};
    }
    return d->libre_graph_permissions_actions;
}
void OAIPermission::setLibreGraphPermissionsActions(const QList<QString> &libre_graph_permissions_actions) {
    Q_D(OAIPermission);
    Q_ASSERT(d);

    d->libre_graph_permissions_actions = libre_graph_permissions_actions;
    d->libre_graph_permissions_actions_isSet = true;
}

bool OAIPermission::is_libre_graph_permissions_actions_Set() const{
    Q_D(const OAIPermission);
    if(!d){
        return false;
    }

    return d->libre_graph_permissions_actions_isSet;
}

bool OAIPermission::is_libre_graph_permissions_actions_Valid() const{
    Q_D(const OAIPermission);
    if(!d){
        return false;
    }
    return d->libre_graph_permissions_actions_isValid;
}

bool OAIPermission::isUiHidden() const {
    Q_D(const OAIPermission);
    if(!d){
        return {};
    }
    return d->ui_hidden;
}
void OAIPermission::setUiHidden(const bool &ui_hidden) {
    Q_D(OAIPermission);
    Q_ASSERT(d);

    d->ui_hidden = ui_hidden;
    d->ui_hidden_isSet = true;
}

bool OAIPermission::is_ui_hidden_Set() const{
    Q_D(const OAIPermission);
    if(!d){
        return false;
    }

    return d->ui_hidden_isSet;
}

bool OAIPermission::is_ui_hidden_Valid() const{
    Q_D(const OAIPermission);
    if(!d){
        return false;
    }
    return d->ui_hidden_isValid;
}

// ...

go

// ...

	LibreGraphPermissionsActions []string `json:"@libre.graph.permissions.actions,omitempty"`
	// Properties or facets (see UI.Facet) annotated with this term will not be rendered if the annotation evaluates to true. Users can set this to hide permissons.
	UIHidden *bool `json:"@UI.Hidden,omitempty"`

// ...


	if !IsNil(o.LibreGraphPermissionsActions) {
		toSerialize["@libre.graph.permissions.actions"] = o.LibreGraphPermissionsActions
	}
	if !IsNil(o.UIHidden) {
		toSerialize["@UI.Hidden"] = o.UIHidden
	}

// ...

// GetLibreGraphPermissionsActions returns the LibreGraphPermissionsActions field value if set, zero value otherwise.
func (o *Permission) GetLibreGraphPermissionsActions() []string {
	if o == nil || IsNil(o.LibreGraphPermissionsActions) {
		var ret []string
		return ret
	}
	return o.LibreGraphPermissionsActions
}

// GetLibreGraphPermissionsActionsOk returns a tuple with the LibreGraphPermissionsActions field value if set, nil otherwise
// and a boolean to check if the value has been set.
func (o *Permission) GetLibreGraphPermissionsActionsOk() ([]string, bool) {
	if o == nil || IsNil(o.LibreGraphPermissionsActions) {
		return nil, false
	}
	return o.LibreGraphPermissionsActions, true
}

// HasLibreGraphPermissionsActions returns a boolean if a field has been set.
func (o *Permission) HasLibreGraphPermissionsActions() bool {
	if o != nil && !IsNil(o.LibreGraphPermissionsActions) {
		return true
	}

	return false
}

// SetLibreGraphPermissionsActions gets a reference to the given []string and assigns it to the LibreGraphPermissionsActions field.
func (o *Permission) SetLibreGraphPermissionsActions(v []string) {
	o.LibreGraphPermissionsActions = v
}

// GetUIHidden returns the UIHidden field value if set, zero value otherwise.
func (o *Permission) GetUIHidden() bool {
	if o == nil || IsNil(o.UIHidden) {
		var ret bool
		return ret
	}
	return *o.UIHidden
}

// GetUIHiddenOk returns a tuple with the UIHidden field value if set, nil otherwise
// and a boolean to check if the value has been set.
func (o *Permission) GetUIHiddenOk() (*bool, bool) {
	if o == nil || IsNil(o.UIHidden) {
		return nil, false
	}
	return o.UIHidden, true
}

// HasUIHidden returns a boolean if a field has been set.
func (o *Permission) HasUIHidden() bool {
	if o != nil && !IsNil(o.UIHidden) {
		return true
	}

	return false
}

// SetUIHidden gets a reference to the given bool and assigns it to the UIHidden field.
func (o *Permission) SetUIHidden(v bool) {
	o.UIHidden = &v
}

// ...

typescript

// ...

    /**
     * Use this to create a permission with custom actions.
     * @type {Array<string>}
     * @memberof Permission
     */
    '@libre.graph.permissions.actions'?: Array<string>;
    /**
     * Properties or facets (see UI.Facet) annotated with this term will not be rendered if the annotation evaluates to true. Users can set this to hide permissons.
     * @type {boolean}
     * @memberof Permission
     */
    '@UI.Hidden'?: boolean;

// ...

PHP

// ...

    /**
      * Array of property to type mappings. Used for (de)serialization
      *
      * @var string[]
      */
    protected static $openAPITypes = [
        //...
        'at_libre_graph_permissions_actions' => 'string[]',
        'at_ui_hidden' => 'bool'
    ];

// ...

    /**
      * Array of property to format mappings. Used for (de)serialization
      *
      * @var string[]
      * @phpstan-var array<string, string|null>
      * @psalm-var array<string, string|null>
      */
    protected static $openAPIFormats = [
        //...
        'at_libre_graph_permissions_actions' => null,
        'at_ui_hidden' => null
    ];

// ...

    /**
     * Array of attributes where the key is the local name,
     * and the value is the original name
     *
     * @var string[]
     */
    protected static $attributeMap = [
        //...
        'at_libre_graph_permissions_actions' => '@libre.graph.permissions.actions',
        'at_ui_hidden' => '@UI.Hidden'
    ];

// ...

    /**
     * Array of attributes to setter functions (for deserialization of responses)
     *
     * @var string[]
     */
    protected static $setters = [
        //...
        'at_libre_graph_permissions_actions' => 'setAtLibreGraphPermissionsActions',
        'at_ui_hidden' => 'setAtUiHidden'
    ];

// ...

    /**
     * Array of attributes to getter functions (for serialization of requests)
     *
     * @var string[]
     */
    protected static $getters = [
        //...
        'at_libre_graph_permissions_actions' => 'getAtLibreGraphPermissionsActions',
        'at_ui_hidden' => 'getAtUiHidden'
    ];

// ...

    /**
     * Constructor
     *
     * @param mixed[] $data Associated array of property values
     *                      initializing the model
     */
    public function __construct(array $data = null)
    {
        //...
        $this->setIfExists('at_libre_graph_permissions_actions', $data ?? [], null);
        $this->setIfExists('at_ui_hidden', $data ?? [], null);
    }

// ...

    /**
     * Gets at_libre_graph_permissions_actions
     *
     * @return string[]|null
     */
    public function getAtLibreGraphPermissionsActions()
    {
        return $this->container['at_libre_graph_permissions_actions'];
    }

    /**
     * Sets at_libre_graph_permissions_actions
     *
     * @param string[]|null $at_libre_graph_permissions_actions Use this to create a permission with custom actions.
     *
     * @return self
     */
    public function setAtLibreGraphPermissionsActions($at_libre_graph_permissions_actions)
    {
        if (is_null($at_libre_graph_permissions_actions)) {
            throw new \InvalidArgumentException('non-nullable at_libre_graph_permissions_actions cannot be null');
        }
        $this->container['at_libre_graph_permissions_actions'] = $at_libre_graph_permissions_actions;

        return $this;
    }

    /**
     * Gets at_ui_hidden
     *
     * @return bool|null
     */
    public function getAtUiHidden()
    {
        return $this->container['at_ui_hidden'];
    }

    /**
     * Sets at_ui_hidden
     *
     * @param bool|null $at_ui_hidden Properties or facets (see UI.Facet) annotated with this term will not be rendered if the annotation evaluates to true. Users can set this to hide permissons.
     *
     * @return self
     */
    public function setAtUiHidden($at_ui_hidden)
    {
        if (is_null($at_ui_hidden)) {
            throw new \InvalidArgumentException('non-nullable at_ui_hidden cannot be null');
        }
        $this->container['at_ui_hidden'] = $at_ui_hidden;

        return $this;
    }

I strongly suggest we use those annotations for our deviations from the ms graph api

@butonic
Copy link
Member Author

butonic commented Oct 13, 2023

Note that I dropped the shareID from the itemReference as it is AFAICT unused: https://github.com/owncloud/libre-graph-api/pull/112/files#diff-ef34f08de0f08ec7094fc1391308521fae6824a2ad2e5b279673c98da8a60e33L2488-R3276

@butonic
Copy link
Member Author

butonic commented Oct 13, 2023

regarding the c++ build failure: I added #/components/schemas/sharingLinkType which is a string with an enum and a long description:

    sharingLinkType:      
        type: string
        enum: [view, upload, edit, createOnly, blocksDownload]
        description: |
          The type of the link created.

          | Value          | Display name      | Description                                                     |
          | -------------- | ----------------- | --------------------------------------------------------------- |
          | view           | View              | Creates a read-only link to the driveItem.                      |
          | upload         | Upload            | Creates a read-write link to the folder driveItem.              |
          | edit           | Edit              | Creates a read-write link to the driveItem.                     |
          | createOnly     | File Drop         | Creates an upload-only link to the folder driveItem.            |
          | blocksDownload | Secure View       | Creates a read-only link that blocks download to the driveItem. |

My guess is that maybe the code generation does not yet handle the case that $ref: '#/components/schemas/sharingLinkType' is just a type: string ... not an object? all other schemas are objects ...

@TheOneRing
Copy link
Member

I guess we need to patch our fork of the mustache files there are 3 to 4 errors in OAISharingLinkType.cpp

@butonic
Copy link
Member Author

butonic commented Oct 13, 2023

rebased on main which now has the c++ fixes, @micbar ready to merge IMO

Signed-off-by: Jörn Friedrich Dreyer <jfd@butonic.de>

add invite endpoint

Signed-off-by: Jörn Friedrich Dreyer <jfd@butonic.de>

update invite, add driveRecipient, extend permissions

Signed-off-by: Jörn Friedrich Dreyer <jfd@butonic.de>

fix quotes

Signed-off-by: Jörn Friedrich Dreyer <jfd@butonic.de>

add list permissions endpoint

Signed-off-by: Jörn Friedrich Dreyer <jfd@butonic.de>

turn listing permissions schema into a collection

Signed-off-by: Jörn Friedrich Dreyer <jfd@butonic.de>

add get permission, minor fixes

Signed-off-by: Jörn Friedrich Dreyer <jfd@butonic.de>

update and delete permissions, allowedValues, fix parameters

Signed-off-by: Jörn Friedrich Dreyer <jfd@butonic.de>

add roleDefinitions

Signed-off-by: Jörn Friedrich Dreyer <jfd@butonic.de>

clarify

Signed-off-by: Jörn Friedrich Dreyer <jfd@butonic.de>

incorporate feedback

Signed-off-by: Jörn Friedrich Dreyer <jfd@butonic.de>

add creatLink

Signed-off-by: Jörn Friedrich Dreyer <jfd@butonic.de>

add sharedByMe endpoint, drop unused paremeters in sharing examples

Signed-off-by: Jörn Friedrich Dreyer <jfd@butonic.de>

fix examples, better description for group shares

Signed-off-by: Jörn Friedrich Dreyer <jfd@butonic.de>

fix indentaton of shared property

Signed-off-by: Jörn Friedrich Dreyer <jfd@butonic.de>

add error case

Signed-off-by: Jörn Friedrich Dreyer <jfd@butonic.de>

work on links and permissions

Signed-off-by: Jörn Friedrich Dreyer <jfd@butonic.de>
Signed-off-by: Jörn Friedrich Dreyer <jfd@butonic.de>
Signed-off-by: Jörn Friedrich Dreyer <jfd@butonic.de>
Signed-off-by: Jörn Friedrich Dreyer <jfd@butonic.de>
@dragotin
Copy link
Contributor

Well, maybe the type just has to be changed... In XML, an enum is also not a string.

@dschmidt
Copy link
Member

on request from @butonic : @ in property names is no issue in typescript

@butonic
Copy link
Member Author

butonic commented Oct 13, 2023

@dragotin the codegen was already fixed in #118

@butonic butonic dismissed rhafer’s stale review October 13, 2023 09:41

top PR comment contains sharing walkthrough

Copy link
Contributor

@micbar micbar left a comment

Choose a reason for hiding this comment

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

looking forward to use it 😄

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
Status: Done
Development

Successfully merging this pull request may close these issues.

None yet

6 participants