Can't get/patch fields for SharePoint drive item by path
Opened this issue · 4 comments
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 #1961 (comment) 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);
}