msgraph-sdk-java
msgraph-sdk-java copied to clipboard
Can't get/patch fields for SharePoint drive item by path
Describe the bug
I am trying to get and patch fields for SharePoint drive item by path.
In v5 of SDK I was able to do something like:
graphClient.sites(siteId).lists(libraryId).drive().root().itemWithPath(path)
.listItem().fields().buildRequest().patch(sourceFieldValueSet);
which leads to:
https://graph.microsoft.com/v1.0/sites/<siteId>/lists/<libraryId>/drive/root:/<filename>:/listItem/fields
In v6 of SDK this endpoint is not accessible. I found 2 way to get fields:
graphClient.sites().bySiteId(siteId).lists().byListId(libraryId).items().byListItemId(path).fields().get();
graphClient.drives().byDriveId(driveId).list().items().byListItemId(path).fields().get();
As I've read in https://github.com/microsoftgraph/msgraph-sdk-java/issues/1961#issuecomment-2082588357 it should be easy to use root:/folder/file: instead of item-id, but it is not working.
Expected behavior
Easy way to call https://graph.microsoft.com/v1.0/sites/<siteId>/lists/<libraryId>/drive/root:/<filename>:/listItem/fields and other similar endpoints
How to reproduce
I've written some example code:
public void fileTest() {
try {
final String siteId = "<removed>";
final String libraryId = "<removed>";
final String filename = "doc.txt";
final List<String> paths = List.of( //
filename, //
"/" + filename, //
"root:/" + filename, //
"root:/" + filename + ":" //
);
System.out.println("by sites");
for (final String path : paths) {
try {
graphServiceClient.sites().bySiteId(siteId).lists().byListId(libraryId).items().byListItemId(path)
.fields().get();
System.out.println("OK for " + path);
} catch (final ODataError e) {
System.out.println("Error for " + path + " " + e.getMessage());
}
}
System.out.println("\nby drives");
final Drive drive = graphServiceClient.sites().bySiteId(siteId).lists().byListId(libraryId).drive().get();
for (final String path : paths) {
try {
graphServiceClient.drives().byDriveId(drive.getId()).list().items().byListItemId(path).fields()
.get();
System.out.println("OK for " + path);
} catch (final ODataError e) {
System.out.println("Error for " + path + " " + e.getMessage());
}
}
System.out.println("\nProof it's there:");
final var driveItem = graphServiceClient.drives().byDriveId(drive.getId()).items()
.byDriveItemId("root:/" + filename + ":").get();
System.out.println(driveItem.getId() + " " + driveItem.getName());
} catch (final Exception e) {
System.out.println("Other Exception:");
e.printStackTrace();
}
}
which leads to output:
by sites
Error for doc.txt Item not found
Error for /doc.txt Item not found
Error for root:/doc.txt Resource not found for the segment 'getByPath'.
Error for root:/doc.txt: Resource not found for the segment 'getByPath'.
by drives
Error for doc.txt Item not found
Error for /doc.txt Item not found
Error for root:/doc.txt Item not found
Error for root:/doc.txt: Item not found
Proof it's there:
014ICIUEH23MWGBJOMZRF3RFLCCU7KXWAI doc.txt
SDK Version
6.13.0
Latest version known to work for scenario above?
5.73.0 / 5.80.0
Known Workarounds
No response
Debug output
Click to expand log
```</details>
### Configuration
- OS: Linux
- architecture x64
### Other information
_No response_
I would appreciate it if someone could help me. I also tried to use the ID I got from driveItem.getId() in my "proof" section (014ICIUEH23MWGBJOMZRF3RFLCCU7KXWAI) to make the request instead of the path:
String driveItemId = "014ICIUEH23MWGBJOMZRF3RFLCCU7KXWAI";
graphServiceClient.sites().bySiteId(siteId).lists().byListId(libraryId).items().byListItemId(driveItemId).fields().get();
graphServiceClient.drives().byDriveId(drive.getId()).list().items().byListItemId(driveItemId).fields().get();
But in both cases I get this error: Provided list item identifer is not in an allowed format
Hi @poschi3.
Sorry for the poor experience here.
Please try the withUrl method as a workaround as I investigate this further:
client.sites().bySiteId("").lists().byListId("").items().byListItemId("").fields().withUrl(
"https://graph.microsoft.com/v1.0/sites/<siteId>/lists/<libraryId>/drive/root:/<filename>:/listItem/fields"
).get();
withUrl overrides all the values passed into the fluent request builder methods. Trick is to call it on an object who's HTTP method deserializes the response to an object you expect.
Please let me know if this helps.
Hi @Ndiritu
thank you for your reply. In the meantime I've done something like this:
private FieldValueSet sendListItemFieldsPatch(final String siteId, final String listId,
final String unescapedPath, final FieldValueSet body) {
final HashMap<String, Object> pathParameters = new HashMap<>();
pathParameters.put("site%2Did", siteId);
pathParameters.put("list%2Did", listId);
pathParameters.put("path", "root:/" + unescapedPath + ":");
final RequestInformation requestInfo = new RequestInformation();
requestInfo.httpMethod = HttpMethod.PATCH;
requestInfo.urlTemplate = "{+baseurl}/sites/{site%2Did}/lists/{list%2Did}/drive/{path}/listItem/fields";
requestInfo.pathParameters = pathParameters;
requestInfo.headers.tryAdd("Accept", "application/json");
requestInfo.setContentFromParsable(getGraphClient().getRequestAdapter(), "application/json", body);
final HashMap<String, ParsableFactory<? extends Parsable>> errorMapping = new HashMap<>();
errorMapping.put("XXX", ODataError::createFromDiscriminatorValue);
return getGraphClient().getRequestAdapter().send(requestInfo, errorMapping,
FieldValueSet::createFromDiscriminatorValue);
}
But now I'm rewriting it to use .withUrl() but still using RequestInformation with .getUri().toURL().toString() to get urlTemplate with escaping of parameter values.
Please also reconsider to not remove non canonical request path. With the SDK v6 I have to double the number of requests because I have to retrieve the drive to get the drive ID to put it into the next request which was before just one request total.
Now I'm doing something like this. It's may be helpful for someone.
private String urlGenerator(final String urlTemplate, final Map<String, Object> pathParameters,
final Map<String, Object> queryParameters) {
final RequestInformation requestInfo = new RequestInformation();
requestInfo.urlTemplate = urlTemplate;
requestInfo.pathParameters = new HashMap<>(pathParameters);
final String baseUrl = getGraphClient().getRequestAdapter().getBaseUrl();
requestInfo.pathParameters.put("baseurl", baseUrl);
if (queryParameters != null) {
for (final Entry<String, Object> parameter : queryParameters.entrySet()) {
requestInfo.addQueryParameter(parameter.getKey(), parameter.getValue());
}
}
try {
return requestInfo.getUri().toURL().toString();
} catch (final URISyntaxException | MalformedURLException | IllegalArgumentException
| IllegalStateException e) {
throw new RuntimeException("Error building url", e);
}
}
private FieldValueSet sendListItemFieldsPatch(final String siteId, final String listId, final String unescapedPath,
final FieldValueSet body) {
final String urlTemplate = "{+baseurl}/sites/{site%2Did}/lists/{list%2Did}/drive/{path}/listItem/fields";
final Map<String, Object> pathParameters = Map.of( //
"site%2Did", siteId, //
"list%2Did", listId, //
"path", "root:/" + unescapedPath + ":" // root:/doc.txt:
);
final String rawUrl = urlGenerator(urlTemplate, pathParameters, Collections.emptyMap());
return getGraphClient().sites().bySiteId("").lists().byListId("").items().byListItemId("").fields()
.withUrl(rawUrl).patch(body);
}
@baywet Which feedback from me is needed? The current state is waiting for Microsoft.
@poschi3 sorry about that, it was a mistake from a script I ran to clear older issues. As you can see the label was added and removed almost immediately.
Reading this issue though, I'm not entirely sure what is the ask here? Expand all paths in the SDK? If so, this is something we're unlikely to change anytime soon. We've spent months tweaking the conversion library and fixing the metadata to ensure the right number of paths (>5000) was generated.
We had a similar question in go lately here is some more context
Let us know if you have any additional comments or questions.
@baywet No problem.
The question is: How do I get the field information for a file when I have the path to the file?
There is no way to get the information with normal usage of the SDK. There are two requests where I can put a path but both lead to an error, regardless how I encode the path to the file (https://github.com/microsoftgraph/msgraph-sdk-java/issues/2126#issue-2475641063). The endpoints just don't support "by path".
There is a way to get the ID for a file by path (graphServiceClient.drives().byDriveId(drive.getId()).items().byDriveItemId("root:/" + filename + ":").get();) but with this ID I'm not able to retrieve the fields (https://github.com/microsoftgraph/msgraph-sdk-java/issues/2126#issuecomment-2315153772).
For all of this there is a specific Graph endpoint (/v1.0/sites/<siteId>/lists/<libraryId>/drive/root:/<filename>:/listItem/fields) which is just not added to the SDK. I think accessing a file based storage by filename is a straight forward use case.
Beside of GET we are also PATCH FieldValues and retrieve Versions by filename (/sites/{site%2Did}/lists/{list%2Did}/drive/{path}/listItem/versions) and other requests.
@poschi3 Thank you for the additional information.
Have you tried without the extra ":" after the file name? Can you share the result of "toGetRequestInformation()" instead of get, and the .Url of the returned object?
@baywet I adopted my example code from https://github.com/microsoftgraph/msgraph-sdk-java/issues/2126#issue-2475641063 and added logging of .toGetRequestInformation().getUri(). I changed company name in uri. I tested with doc.txt, /doc.txt, root:/doc.txt and root:/doc.txt:. SDK version 6.27.0
by sites
path: doc.txt
uri: https://graph.microsoft.com/v1.0/sites/company.sharepoint.com%2C929413da-d359-4fdf-9a9f-4ce9c080f0d5%2C756690bf-4fee-43a7-a1df-4eaafda8482f/lists/acf21aeb-2bfa-491d-80ae-816023169169/items/doc.txt/fields
Error: Item not found
path: /doc.txt
uri: https://graph.microsoft.com/v1.0/sites/company.sharepoint.com%2C929413da-d359-4fdf-9a9f-4ce9c080f0d5%2C756690bf-4fee-43a7-a1df-4eaafda8482f/lists/acf21aeb-2bfa-491d-80ae-816023169169/items/%2Fdoc.txt/fields
Error: Item not found
path: root:/doc.txt
uri: https://graph.microsoft.com/v1.0/sites/company.sharepoint.com%2C929413da-d359-4fdf-9a9f-4ce9c080f0d5%2C756690bf-4fee-43a7-a1df-4eaafda8482f/lists/acf21aeb-2bfa-491d-80ae-816023169169/items/root%3A%2Fdoc.txt/fields
Error: Resource not found for the segment 'getByPath'.
path: root:/doc.txt:
uri: https://graph.microsoft.com/v1.0/sites/company.sharepoint.com%2C929413da-d359-4fdf-9a9f-4ce9c080f0d5%2C756690bf-4fee-43a7-a1df-4eaafda8482f/lists/acf21aeb-2bfa-491d-80ae-816023169169/items/root%3A%2Fdoc.txt%3A/fields
Error: Resource not found for the segment 'getByPath'.
by drives
path: doc.txt
uri: https://graph.microsoft.com/v1.0/drives/b%212hOUklnT30-an0zpwIDw1b-QZnXuT6dDod9Oqv2oSC_rGvKs-isdSYCugWAjFpFp/list/items/doc.txt/fields
Error: Item not found
path: /doc.txt
uri: https://graph.microsoft.com/v1.0/drives/b%212hOUklnT30-an0zpwIDw1b-QZnXuT6dDod9Oqv2oSC_rGvKs-isdSYCugWAjFpFp/list/items/%2Fdoc.txt/fields
Error: Item not found
path: root:/doc.txt
uri: https://graph.microsoft.com/v1.0/drives/b%212hOUklnT30-an0zpwIDw1b-QZnXuT6dDod9Oqv2oSC_rGvKs-isdSYCugWAjFpFp/list/items/root%3A%2Fdoc.txt/fields
Error: Item not found
path: root:/doc.txt:
uri: https://graph.microsoft.com/v1.0/drives/b%212hOUklnT30-an0zpwIDw1b-QZnXuT6dDod9Oqv2oSC_rGvKs-isdSYCugWAjFpFp/list/items/root%3A%2Fdoc.txt%3A/fields
Error: Item not found
Proof it's there:
014ICIUEH23MWGBJOMZRF3RFLCCU7KXWAI doc.txt
Thank you for the additional information.
First off, I don't think the "directory syntax" works on lists operations (any url with a /lists or /list in it)
I was able to eventually get it working with this syntax
GET https://graph.microsoft.com/v1.0/drives/{drive-id}/items/root:{file-path-starting-with-slash}:/listItem/fields
(the second colons after the file path is indeed required)
which should be accessible through something like this
client.drives().byDriveId("doesntmatter").list().items().byItemId("doesntmatter").fields().withUrl(URL).get();
Now, if you want to use request information to build the URI, you NEED TO specific a + in the variable name. see RFC 6570 reserved expansion
Let us know if you have any additional comments or questions.
@baywet Thanks.
The request to GET https://graph.microsoft.com/v1.0/drives/{drive-id}/items/root:{file-path-starting-with-slash}:/listItem/fields seems to work but if I still have to do the manual withUrl() approach, I'll keep the old url I used for years now (https://graph.microsoft.com/v1.0/sites/<siteId>/lists/<libraryId>/drive/root:/<filename>:/listItem/fields). There I know it works reliably and I save extra requests (finding out drive-id).
Your sample code client.drives().byDriveId("doesntmatter").list().items().byItemId("doesntmatter").fields().withUrl(URL).get(); looks easy to use in the first place, but the trick is to get the correct URL, doing escaping and queryParameters like expand and so on. At the end I think the code will be as complex as in https://github.com/microsoftgraph/msgraph-sdk-java/issues/2126#issuecomment-2326297763.
To be honest: The version 6 of the SDK is so frustrating. Endpoints like this one got lost, workaround is complex. There is still no JavaDoc or proper sources.jar making development so hard! Exception handling is a huge problem. If you open an issue you are expected to fix basic Java contracts for Microsoft. My newest problem is that I get a not annotated but checked Exception (IOException) from okhttp3. How is this even possible? I'm not going to open an issue for this, I just catch global Exception every time I use the SDK.
@poschi3 thanks for the additional information.
url
Oh yes, you can use any URL that matches the signature, mine was just an example. We know building URLs properly is difficult, which is why the SDK provides a fluent API in the first place. Beyond adding the missing operation to the fluent API, which comes with its own sets of challenges, what could we offer to make this easier?
doc
I'll reach out to the engineering manager to understand why this is still not fixed.
exception
I understand the frustration caused by the other elements. It'll be difficult to help you with that without the details though.
Thanks!
This issue has been automatically marked as stale because it has been marked as requiring author feedback but has not had any activity for 4 days. It will be closed if no further activity occurs within 3 days of this comment.