diff --git a/docs/sources/microsoft-365/msft-onedrive/example-api-responses/original/list_groups_drives.json b/docs/sources/microsoft-365/msft-onedrive/example-api-responses/original/list_groups_drives.json new file mode 100644 index 000000000..2aa08337e --- /dev/null +++ b/docs/sources/microsoft-365/msft-onedrive/example-api-responses/original/list_groups_drives.json @@ -0,0 +1,46 @@ +{ + "@odata.nextLink": "https://graph.microsoft.com/v1.0/groups/6257b47d-9e87-418b-9ac2-031f09397de7/drives?$top=1&$skiptoken=aWQ9OUZCRjU2OUEtN0UwOC00RDFELTlFREMtMkY3ODg3MTUzNjY3", + "value": [ + { + "id": "b!-RIj2DuyvEyV1T4NlOaMHk8XkS_I8MdFlUCq1BlcjgmhRfAj3-Z8RY2VpuvV_tpd", + "driveType": "documentLibrary", + "createdDateTime": "2017-07-27T02:41:36Z", + "createdBy": { + "user": { + "id": "CE251278-EF9E-4FE5-833C-1D89EEAE68E0", + "displayName": "Ryan Gregg", + "email": "ryan@contoso.com" + } + }, + "lastModifiedDateTime": "2018-03-27T07:34:38Z", + "lastModifiedBy": { + "user": { + "id": "9a8b7c6d-5e4f-3a2b-1c0d-9e8f7a6b5c4d", + "displayName": "Dana Lee", + "email": "dana@contoso.com" + } + } + }, + { + "id": "b!kjtJnMGCeEeNkZsY18PtOiNjCV0Z9s9Fqyly8jKvPEB2kal9T5pOT5y7TXClirlE", + "driveType": "documentLibrary", + "system": {}, + "createdDateTime": "2017-07-27T02:41:36Z", + "createdBy": { + "user": { + "id": "CE251278-EF9E-4FE5-833C-1D89EEAE68E0", + "displayName": "Ryan Gregg", + "email": "ryan@contoso.com" + } + }, + "lastModifiedDateTime": "2017-07-27T02:41:36Z", + "lastModifiedBy": { + "user": { + "id": "CE251278-EF9E-4FE5-833C-1D89EEAE68E0", + "displayName": "Ryan Gregg", + "email": "ryan@contoso.com" + } + } + } + ] +} diff --git a/docs/sources/microsoft-365/msft-onedrive/example-api-responses/original/list_drives.json b/docs/sources/microsoft-365/msft-onedrive/example-api-responses/original/list_user_drives.json similarity index 87% rename from docs/sources/microsoft-365/msft-onedrive/example-api-responses/original/list_drives.json rename to docs/sources/microsoft-365/msft-onedrive/example-api-responses/original/list_user_drives.json index 85b9dc59a..d8d35b4f6 100644 --- a/docs/sources/microsoft-365/msft-onedrive/example-api-responses/original/list_drives.json +++ b/docs/sources/microsoft-365/msft-onedrive/example-api-responses/original/list_user_drives.json @@ -1,4 +1,5 @@ { + "@odata.nextLink": "https://graph.microsoft.com/v1.0/users/6257b47d-9e87-418b-9ac2-031f09397de7/drives?$top=1&$skiptoken=aWQ9OUZCRjU2OUEtN0UwOC00RDFELTlFREMtMkY3ODg3MTUzNjY3", "value": [ { "id": "b!-RIj2DuyvEyV1T4NlOaMHk8XkS_I8MdFlUCq1BlcjgmhRfAj3-Z8RY2VpuvV_tpd", diff --git a/docs/sources/microsoft-365/msft-onedrive/example-api-responses/sanitized/list_groups_drives.json b/docs/sources/microsoft-365/msft-onedrive/example-api-responses/sanitized/list_groups_drives.json new file mode 100644 index 000000000..2b2df3969 --- /dev/null +++ b/docs/sources/microsoft-365/msft-onedrive/example-api-responses/sanitized/list_groups_drives.json @@ -0,0 +1,42 @@ +{ + "@odata.nextLink":"https://graph.microsoft.com/v1.0/groups/6257b47d-9e87-418b-9ac2-031f09397de7/drives?$top=1&$skiptoken=aWQ9OUZCRjU2OUEtN0UwOC00RDFELTlFREMtMkY3ODg3MTUzNjY3", + "value":[ + { + "id":"b!-RIj2DuyvEyV1T4NlOaMHk8XkS_I8MdFlUCq1BlcjgmhRfAj3-Z8RY2VpuvV_tpd", + "driveType":"documentLibrary", + "createdDateTime":"2017-07-27T02:41:36Z", + "createdBy":{ + "user":{ + "id":"CE251278-EF9E-4FE5-833C-1D89EEAE68E0", + "email":"t~rhHXsIpQCoOYdgzj6URY4x8oUcvnLMLR5d71_wRuVAA@contoso.com" + } + }, + "lastModifiedDateTime":"2018-03-27T07:34:38Z", + "lastModifiedBy":{ + "user":{ + "id":"9a8b7c6d-5e4f-3a2b-1c0d-9e8f7a6b5c4d", + "email":"t~1ECTeY42RlWDU6D4DtrXKyFmVrUHbEEpdYzI34l0y9Q@contoso.com" + } + } + }, + { + "id":"b!kjtJnMGCeEeNkZsY18PtOiNjCV0Z9s9Fqyly8jKvPEB2kal9T5pOT5y7TXClirlE", + "driveType":"documentLibrary", + "system":{ }, + "createdDateTime":"2017-07-27T02:41:36Z", + "createdBy":{ + "user":{ + "id":"CE251278-EF9E-4FE5-833C-1D89EEAE68E0", + "email":"t~rhHXsIpQCoOYdgzj6URY4x8oUcvnLMLR5d71_wRuVAA@contoso.com" + } + }, + "lastModifiedDateTime":"2017-07-27T02:41:36Z", + "lastModifiedBy":{ + "user":{ + "id":"CE251278-EF9E-4FE5-833C-1D89EEAE68E0", + "email":"t~rhHXsIpQCoOYdgzj6URY4x8oUcvnLMLR5d71_wRuVAA@contoso.com" + } + } + } + ] +} diff --git a/docs/sources/microsoft-365/msft-onedrive/example-api-responses/sanitized/list_drives.json b/docs/sources/microsoft-365/msft-onedrive/example-api-responses/sanitized/list_user_drives.json similarity index 87% rename from docs/sources/microsoft-365/msft-onedrive/example-api-responses/sanitized/list_drives.json rename to docs/sources/microsoft-365/msft-onedrive/example-api-responses/sanitized/list_user_drives.json index 91fc55041..8a1733702 100644 --- a/docs/sources/microsoft-365/msft-onedrive/example-api-responses/sanitized/list_drives.json +++ b/docs/sources/microsoft-365/msft-onedrive/example-api-responses/sanitized/list_user_drives.json @@ -1,4 +1,5 @@ { + "@odata.nextLink":"https://graph.microsoft.com/v1.0/users/6257b47d-9e87-418b-9ac2-031f09397de7/drives?$top=1&$skiptoken=aWQ9OUZCRjU2OUEtN0UwOC00RDFELTlFREMtMkY3ODg3MTUzNjY3", "value":[ { "id":"b!-RIj2DuyvEyV1T4NlOaMHk8XkS_I8MdFlUCq1BlcjgmhRfAj3-Z8RY2VpuvV_tpd", diff --git a/docs/sources/microsoft-365/msft-onedrive/example-api-responses/sanitized_no-app-ids/list_drives.json b/docs/sources/microsoft-365/msft-onedrive/example-api-responses/sanitized_no-app-ids/list_groups_drives.json similarity index 87% rename from docs/sources/microsoft-365/msft-onedrive/example-api-responses/sanitized_no-app-ids/list_drives.json rename to docs/sources/microsoft-365/msft-onedrive/example-api-responses/sanitized_no-app-ids/list_groups_drives.json index d133c46d6..b060eaa7d 100644 --- a/docs/sources/microsoft-365/msft-onedrive/example-api-responses/sanitized_no-app-ids/list_drives.json +++ b/docs/sources/microsoft-365/msft-onedrive/example-api-responses/sanitized_no-app-ids/list_groups_drives.json @@ -1,4 +1,5 @@ { + "@odata.nextLink":"https://graph.microsoft.com/v1.0/groups/6257b47d-9e87-418b-9ac2-031f09397de7/drives?$top=1&$skiptoken=aWQ9OUZCRjU2OUEtN0UwOC00RDFELTlFREMtMkY3ODg3MTUzNjY3", "value":[ { "id":"b!-RIj2DuyvEyV1T4NlOaMHk8XkS_I8MdFlUCq1BlcjgmhRfAj3-Z8RY2VpuvV_tpd", diff --git a/docs/sources/microsoft-365/msft-onedrive/example-api-responses/sanitized_no-app-ids/list_user_drives.json b/docs/sources/microsoft-365/msft-onedrive/example-api-responses/sanitized_no-app-ids/list_user_drives.json new file mode 100644 index 000000000..cea365cea --- /dev/null +++ b/docs/sources/microsoft-365/msft-onedrive/example-api-responses/sanitized_no-app-ids/list_user_drives.json @@ -0,0 +1,42 @@ +{ + "@odata.nextLink":"https://graph.microsoft.com/v1.0/users/p~G-O6qnggmabXX7oVLICcae1h1B_h3yud3zcNymQoGVqSOg0H9jqiKau9wvs7oToujvTK_Woudb8vCT95nqMWc89UQSbrUBZGukrZoydhHl8/drives?$top=1&$skiptoken=aWQ9OUZCRjU2OUEtN0UwOC00RDFELTlFREMtMkY3ODg3MTUzNjY3", + "value":[ + { + "id":"b!-RIj2DuyvEyV1T4NlOaMHk8XkS_I8MdFlUCq1BlcjgmhRfAj3-Z8RY2VpuvV_tpd", + "driveType":"documentLibrary", + "createdDateTime":"2017-07-27T02:41:36Z", + "createdBy":{ + "user":{ + "id":"t~jL-PZwZ7AqMLlWzY4F0nkDdSpci8cF1GzIzZC51Y9Rg", + "email":"t~rhHXsIpQCoOYdgzj6URY4x8oUcvnLMLR5d71_wRuVAA@contoso.com" + } + }, + "lastModifiedDateTime":"2018-03-27T07:34:38Z", + "lastModifiedBy":{ + "user":{ + "id":"t~DTbvYdTNRIQXAF1SvLNyAIENog0-Dy0dxTEB2Yoo5Ys", + "email":"t~1ECTeY42RlWDU6D4DtrXKyFmVrUHbEEpdYzI34l0y9Q@contoso.com" + } + } + }, + { + "id":"b!kjtJnMGCeEeNkZsY18PtOiNjCV0Z9s9Fqyly8jKvPEB2kal9T5pOT5y7TXClirlE", + "driveType":"documentLibrary", + "system":{ }, + "createdDateTime":"2017-07-27T02:41:36Z", + "createdBy":{ + "user":{ + "id":"t~jL-PZwZ7AqMLlWzY4F0nkDdSpci8cF1GzIzZC51Y9Rg", + "email":"t~rhHXsIpQCoOYdgzj6URY4x8oUcvnLMLR5d71_wRuVAA@contoso.com" + } + }, + "lastModifiedDateTime":"2017-07-27T02:41:36Z", + "lastModifiedBy":{ + "user":{ + "id":"t~jL-PZwZ7AqMLlWzY4F0nkDdSpci8cF1GzIzZC51Y9Rg", + "email":"t~rhHXsIpQCoOYdgzj6URY4x8oUcvnLMLR5d71_wRuVAA@contoso.com" + } + } + } + ] +} diff --git a/docs/sources/microsoft-365/msft-onedrive/msft-onedrive.yaml b/docs/sources/microsoft-365/msft-onedrive/msft-onedrive.yaml index aa123d831..dd89f1caa 100644 --- a/docs/sources/microsoft-365/msft-onedrive/msft-onedrive.yaml +++ b/docs/sources/microsoft-365/msft-onedrive/msft-onedrive.yaml @@ -68,6 +68,34 @@ endpoints: type: "array" items: $ref: "#/definitions/DriveItem" +# OData function-call continuation shape for /root/delta, eg "/root/delta(token=abc123)" +- pathTemplate: "/v1.0/drives/{driveId}/root/{deltaExpression}" + allowedQueryParams: + - "token" + - "$select" + - "$expand" + - "$top" + transforms: + - ! + jsonPaths: + - "$..user.email" + encoding: "URL_SAFE_TOKEN" + pathParameterSchemas: + deltaExpression: + pattern: "^delta\\(token=[^)]+\\)$" + responseSchema: + type: "object" + properties: + '@microsoft.graph.hasMoreData': + type: "boolean" + '@odata.deltaLink': + type: "string" + '@odata.nextLink': + type: "string" + value: + type: "array" + items: + $ref: "#/definitions/DriveItem" - pathTemplate: "/v1.0/groups" allowedQueryParams: - "$top" diff --git a/docs/sources/microsoft-365/msft-onedrive/msft-onedrive_no-app-ids.yaml b/docs/sources/microsoft-365/msft-onedrive/msft-onedrive_no-app-ids.yaml index 2416cfb39..5225886a7 100644 --- a/docs/sources/microsoft-365/msft-onedrive/msft-onedrive_no-app-ids.yaml +++ b/docs/sources/microsoft-365/msft-onedrive/msft-onedrive_no-app-ids.yaml @@ -71,6 +71,35 @@ endpoints: type: "array" items: $ref: "#/definitions/DriveItem" + # OData function-call continuation shape for /root/delta, eg "/root/delta(token=abc123)" + - pathTemplate: "/v1.0/drives/{driveId}/root/{deltaExpression}" + allowedQueryParams: + - "token" + - "$select" + - "$expand" + - "$top" + transforms: + - ! + jsonPaths: + - "$..user.id" + - "$..user.email" + encoding: "URL_SAFE_TOKEN" + pathParameterSchemas: + deltaExpression: + pattern: "^delta\\(token=[^)]+\\)$" + responseSchema: + type: "object" + properties: + '@microsoft.graph.hasMoreData': + type: "boolean" + '@odata.deltaLink': + type: "string" + '@odata.nextLink': + type: "string" + value: + type: "array" + items: + $ref: "#/definitions/DriveItem" - pathTemplate: "/v1.0/groups" allowedQueryParams: - "$top" @@ -176,6 +205,10 @@ endpoints: - "$..user.id" - "$..user.email" encoding: "URL_SAFE_TOKEN" + - ! + jsonPaths: + - "$.['@odata.nextLink']" + regex: "^https://graph.microsoft.com/v1.0/users/([a-zA-Z0-9_-]+)/.*$" pathParameterSchemas: userId: anyOf: diff --git a/java/core/src/test/java/co/worklytics/psoxy/rules/msft/OneDriveTests.java b/java/core/src/test/java/co/worklytics/psoxy/rules/msft/OneDriveTests.java index 130bc550b..c29bb98ea 100644 --- a/java/core/src/test/java/co/worklytics/psoxy/rules/msft/OneDriveTests.java +++ b/java/core/src/test/java/co/worklytics/psoxy/rules/msft/OneDriveTests.java @@ -39,14 +39,21 @@ public Stream getExamples() { InvocationExample.of(baseEndpoint + "/groups", "groups.json"), InvocationExample.of(baseEndpoint + "/groups?$top=999&$select=id,mail&$skiptoken=abcXYZ123&$orderby=id&$count=true", "groups.json"), // /v1.0/users/{userId}/drives - no query params, and with all allowed query params - InvocationExample.of(baseEndpoint + "/users/" + userId + "/drives", "list_drives.json"), - InvocationExample.of(baseEndpoint + "/users/" + userId + "/drives?$select=id,driveType,system&$skiptoken=abcXYZ123&$top=999&$orderby=id&$expand=root", "list_drives.json"), + // (with app ids visible, fixture's @odata.nextLink is passed through unmodified -- no + // pathParameterSchemas constraint on userId in this rule variant) + InvocationExample.of(baseEndpoint + "/users/" + userId + "/drives", "list_user_drives.json"), + InvocationExample.of(baseEndpoint + "/users/" + userId + "/drives?$select=id,driveType,system&$skiptoken=abcXYZ123&$top=999&$orderby=id&$expand=root", "list_user_drives.json"), // /v1.0/groups/{groupId}/drives - no query params, and with all allowed query params - InvocationExample.of(baseEndpoint + "/groups/" + groupId + "/drives", "list_drives.json"), - InvocationExample.of(baseEndpoint + "/groups/" + groupId + "/drives?$select=id,driveType,system&$skiptoken=abcXYZ123&$top=999&$orderby=id&$expand=root", "list_drives.json"), + InvocationExample.of(baseEndpoint + "/groups/" + groupId + "/drives", "list_groups_drives.json"), + InvocationExample.of(baseEndpoint + "/groups/" + groupId + "/drives?$select=id,driveType,system&$skiptoken=abcXYZ123&$top=999&$orderby=id&$expand=root", "list_groups_drives.json"), // /v1.0/drives/{driveId}/root/delta - no query params, and with all allowed query params InvocationExample.of(baseEndpoint + "/drives/" + driveId + "/root/delta", "get_drive_delta.json"), + // real pagination continuation shape confirmed via live testing against the Graph API + // (a "?token=..." query param, matching MSFT's own docs example) InvocationExample.of(baseEndpoint + "/drives/" + driveId + "/root/delta?token=abcXYZ123&", "get_drive_delta.json"), + // the OTHER real continuation shape, from this fixture's own captured @odata.nextLink + // (an OData function-call path segment -- rules must accept both) + InvocationExample.of(baseEndpoint + "/drives/" + driveId + "/root/delta(token=1230919asd190410jlka)", "get_drive_delta.json"), // /v1.0/drives/{driveId}/items/{itemId}/activities - no query params allowed by rules InvocationExample.of(baseEndpoint + "/drives/" + driveId + "/items/" + itemId + "/activities", "list_itemActivity.json"), InvocationExample.of(baseEndpoint + "/drives/" + driveId + "/items/" + itemId + "/activities?$expand=driveItem", "list_itemActivity.json"), diff --git a/java/core/src/test/java/co/worklytics/psoxy/rules/msft/OneDrive_NoAppIds_Tests.java b/java/core/src/test/java/co/worklytics/psoxy/rules/msft/OneDrive_NoAppIds_Tests.java index 2f40c9f4d..930a9a1e7 100644 --- a/java/core/src/test/java/co/worklytics/psoxy/rules/msft/OneDrive_NoAppIds_Tests.java +++ b/java/core/src/test/java/co/worklytics/psoxy/rules/msft/OneDrive_NoAppIds_Tests.java @@ -41,14 +41,22 @@ public Stream getExamples() { InvocationExample.of(baseEndpoint + "/groups", "groups.json"), InvocationExample.of(baseEndpoint + "/groups?$top=999&$select=id,mail&$skiptoken=abcXYZ123&$orderby=id&$count=true", "groups.json"), // /v1.0/users/{userId}/drives - no query params, and with all allowed query params - InvocationExample.of(baseEndpoint + "/users/" + userId + "/drives", "list_drives.json"), - InvocationExample.of(baseEndpoint + "/users/" + userId + "/drives?$select=id,driveType,system&$skiptoken=abcXYZ123&$top=999&$orderby=id&$expand=root", "list_drives.json"), + // (fixture's @odata.nextLink embeds the raw, un-pseudonymized AAD user id that Graph + // resolves the path to; it must be re-tokenized so a client following the link isn't + // blocked by pathParameterSchemas requiring a reversible-pseudonym userId) + InvocationExample.of(baseEndpoint + "/users/" + userId + "/drives", "list_user_drives.json"), + InvocationExample.of(baseEndpoint + "/users/" + userId + "/drives?$select=id,driveType,system&$skiptoken=abcXYZ123&$top=999&$orderby=id&$expand=root", "list_user_drives.json"), // /v1.0/groups/{groupId}/drives - no query params, and with all allowed query params - InvocationExample.of(baseEndpoint + "/groups/" + groupId + "/drives", "list_drives.json"), - InvocationExample.of(baseEndpoint + "/groups/" + groupId + "/drives?$select=id,driveType,system&$skiptoken=abcXYZ123&$top=999&$orderby=id&$expand=root", "list_drives.json"), + InvocationExample.of(baseEndpoint + "/groups/" + groupId + "/drives", "list_groups_drives.json"), + InvocationExample.of(baseEndpoint + "/groups/" + groupId + "/drives?$select=id,driveType,system&$skiptoken=abcXYZ123&$top=999&$orderby=id&$expand=root", "list_groups_drives.json"), // /v1.0/drives/{driveId}/root/delta - no query params, and with all allowed query params InvocationExample.of(baseEndpoint + "/drives/" + driveId + "/root/delta", "get_drive_delta.json"), + // real pagination continuation shape confirmed via live testing against the Graph API + // (a "?token=..." query param, matching MSFT's own docs example) InvocationExample.of(baseEndpoint + "/drives/" + driveId + "/root/delta?token=abcXYZ123&", "get_drive_delta.json"), + // the OTHER real continuation shape, from this fixture's own captured @odata.nextLink + // (an OData function-call path segment -- rules must accept both) + InvocationExample.of(baseEndpoint + "/drives/" + driveId + "/root/delta(token=1230919asd190410jlka)", "get_drive_delta.json"), // /v1.0/drives/{driveId}/items/{itemId}/activities - no query params allowed by rules InvocationExample.of(baseEndpoint + "/drives/" + driveId + "/items/" + itemId + "/activities", "list_itemActivity.json"), InvocationExample.of(baseEndpoint + "/drives/" + driveId + "/items/" + itemId + "/activities?$expand=driveItem", "list_itemActivity.json"),