msgraph-sdk-java icon indicating copy to clipboard operation
msgraph-sdk-java copied to clipboard

How to get special app folder with the new SDK?

Open NLLAPPS opened this issue 1 year ago • 6 comments

I am pulling my hair out trying to upload to special app folder https://docs.microsoft.com/en-us/onedrive/developer/rest-api/api/drive_get_specialfolder?view=odsp-graph-online

On 5 series, I was simply doing

val specialAppFolder = "approot" val uploadSession = graphServiceClient .me() .drive() .special(specialAppFolder) .itemWithPath(uploadDestination.removePrefix("/")) // removePrefix because OrganiserConverter.fromConfig will return /path/.. .createUploadSession(.....)

On 6+ I cannot figure-out how to get hold of specialAppFolder in order to createUploadSession().

Could someone with a better understanding point me to the right direction please?

NLLAPPS avatar May 02 '24 13:05 NLLAPPS

@NLLAPPS thanks for reaching out. This should be possible via:

Drive drive = graphServiceClient.me().drive();
DriveItem specialFolder = graphServiceClient.drives().byDriveId(drive.getId()).special().byDriveItemId("{special folder name}").get();
UploadSession uploadSession = graphServiceClient.drives().byDriveId(drive.getId()).items().byDriveItemId(specialFolder.getId()).createUploadSession().post(uploadSessionRequest);

Relevant docs links: https://learn.microsoft.com/en-us/graph/api/drive-get-specialfolder?view=graph-rest-1.0&tabs=java https://learn.microsoft.com/en-us/graph/api/driveitem-createuploadsession?view=graph-rest-1.0#create-an-upload-session

Ndiritu avatar May 03 '24 03:05 Ndiritu

Thank you. I am not usually this thick but getting my head around 6 series proven to be very hard. This is what I used to to on 5 series

val uploadSession = graphServiceClient
            .me()
            .drive()
            .special("approot")
            .itemWithPath("some/sub/folder/and/filename.txt")
            .createUploadSession(DriveItemCreateUploadSessionParameterSet.newBuilder().withItem(DriveItemUploadableProperties()).build())
            .buildRequest(listOf<Option>(QueryOption("@name.conflictBehavior", "rename")))

Where I was able to upload "filename.txt" to ""some/sub/folder/and/" in "approot" special folder

For 6 series, I am following the guide at https://learn.microsoft.com/en-us/graph/sdks/large-file-upload?tabs=java Uploading to root with below example works fine.

val myDriveId: String = graphServiceClient.me().drive().get().id
val uploadSession = graphServiceClient.drives()
             .byDriveId(myDriveId)
             .items()
             .byDriveItemId("root:/some/sub/folder/and/filename.txt:")
             .createUploadSession()
             .post(uploadSessionPostRequestBody)

But, I want to upload the file to the special "approot" folder and configure it like this:

val driveId = graphServiceClient.me().drive().get().id
        val specialFolder = graphServiceClient.drives().byDriveId(driveId).special().byDriveItemId("approot").get()
        val driveItem = graphServiceClient.drives()
            .byDriveId(specialFolder.id)
            .items()
            .byDriveItemId("/some/sub/folder/and/filename.txt")
            .createUploadSession()
            .post(uploadSessionPostRequestBody)

Above example fails with com.microsoft.graph.models.odataerrors.ODataError: Invalid request

Could you tell me how I am supposed to configure DriveItem name and path when uploading to special "approot" folder?

Here is the full log ` --> GET https://graph.microsoft.com/v1.0/users/TokenToReplace/drive accept: application/json authorization: Bearer xxxx --> END GET <-- 200 https://graph.microsoft.com/v1.0/users/TokenToReplace/drive (433ms) cache-control: no-store content-type: application/json;odata.metadata=minimal;odata.streaming=true;IEEE754Compatible=false;charset=utf-8 vary: Accept-Encoding strict-transport-security: max-age=31536000 request-id: xxxxxxxx client-request-id: xxxxxxxx odata-version: 4.0 date: Fri, 03 May 2024 09:26:44 GMT --omitted data--- <-- END HTTP (437ms, 346-byte body) --> GET https://graph.microsoft.com/v1.0/drives/xxxxxx/special/approot accept: application/json authorization: Bearer xxxx --> END GET <-- 200 https://graph.microsoft.com/v1.0/drives/xxxxxx/special/approot (137ms) --omitted data--- <-- END HTTP (145ms, 1059-byte body) --> POST https://graph.microsoft.com/v1.0/drives/xxxxxx%2111432/items/2024_05_03_09_56_59.mp3/createUploadSession Content-Length: 55 authorization: Bearer xxxx accept: application/json content-type: application/json

 {"item":{"@microsoft.graph.conflictBehavior":"rename"}}
 --> END POST (0-byte body)
 <-- 400      https://graph.microsoft.com/v1.0/drives/xxxxxx%2111432/items/2024_05_03_09_56_59.mp3/createUploadSession (108ms)
 cache-control: no-store
 content-type: application/json
 vary: Accept-Encoding
 strict-transport-security: max-age=31536000
 request-id: 3426f604xxxx
 client-request-id: 3426f604xxxx

 date: Fri, 03 May 2024 09:26:44 GMT

  {"error":{"code":"invalidRequest","message":"Invalid request","innerError":{"date":"2024-05-     03T09:26:44","request-id":"3426f604xxxx","client-request-id":"3426f604xxxx"}}}
  <-- END HTTP (114ms, 218-byte body)`

NLLAPPS avatar May 03 '24 09:05 NLLAPPS

I have also dug through traffic of 5 series and noticed that graphServiceClient .me() .drive() .special("approot").itemWithPath(uploadDestination)

Produces: https://graph.microsoft.com/v1.0/me/drive/special/approot:/filename.txt:

But val driveId = graphServiceClient.me().drive().get().id val specialFolder = graphServiceClient.drives().byDriveId(driveId).special().byDriveItemId("approot").get() val driveItem = graphServiceClient.drives() .byDriveId(specialFolder.id) .items() .byDriveItemId(uploadDestination)

Produces below after multple requests: https://graph.microsoft.com/v1.0/drives/XXXXX%21XXXXX/items/filename.txt/

Isn't there any way to build https://graph.microsoft.com/v1.0/me/drive/special/approot:/filename.txt: Saving round trips to the API?

NLLAPPS avatar May 03 '24 11:05 NLLAPPS

OK, finally fixed it! See below. I must say it was excruciatingly hard. I hope MS will consider providing upgrade path on significant changes like this in the future. Issue I had created on Feb 17 2024 at Android Samples at https://github.com/microsoftgraph/msgraph-sample-android/issues not even acknowledged yet.

val driveId = graphServiceClient.me().drive().get().id
        val specialFolderId = graphServiceClient.drives().byDriveId(driveId).special().byDriveItemId("approot").get().id
        val driveItemId = "${specialFolderId}:/fileName.txt:"
        val uploadSession = graphServiceClient.drives().byDriveId(driveId)
            .items()
            .byDriveItemId(driveItemId)
            .createUploadSession()
            .post(uploadSessionPostRequestBody)

Above seems to create unnecessary requests. Each get() is a request to API. This was not the case in 5 series. Isn't there a way to construct Special Folder name without request to get IDs? Some sort of hand crafted DriveItemItemRequestBuilder with URL https://graph.microsoft.com/v1.0/me/drive/special/approot:/?

NLLAPPS avatar May 03 '24 14:05 NLLAPPS

Are there any plans to improve the API to avoid the extra fetches? This still seems to be an issue with the latest version of the SDK (and is not an issue with version 5).

Digipom avatar Sep 11 '25 13:09 Digipom

@Digipom I don't think MS accepts feedback on their SDK. This one is to watch https://github.com/openmobilehub/android-omh-auth/pull/113

NLLAPPS avatar Sep 11 '25 14:09 NLLAPPS