Ein Fall aus der Praxis: Warum PnP JS an seine Grenzen stößt
Im Rahmen eines Kundenprojekts entwickelte ich ein client-seitiges Provisioning-Tool auf Basis von SPFx. Dieses Tool sollte SharePoint-Sites inklusive zugehöriger Gruppen automatisiert anlegen, konfigurieren und mit Berechtigungen versehen. Teil der Aufgabenstellung war es, den Gruppen auch automatisch eine passende Besitzergruppe zuzuweisen.
Die Herausforderung: Die PnP JS-Bibliothek erlaubt mit folgendem Aufruf:
tsCopyEditsp.web.siteGroups.getById(<groupId>).setUserAsOwner(<userId>)
…nur das Setzen eines einzelnen Benutzers als Gruppenbesitzer. Eine native Möglichkeit, eine andere SharePoint-Gruppe als Besitzer zu definieren, gibt es nicht – doch genau das war in unserem Fall notwendig.
Die Lösung fanden wir über einen wenig dokumentierten Mechanismus: ProcessQuery.
Die Lösung im Detail: POST-Request an Client.svc/ProcessQuery
Die klassische SharePoint REST API bietet hierfür keine geeignete Option. Über den CSOM-Endpunkt/_vti_bin/client.svc/ProcessQuery
lässt sich jedoch eine Gruppe als Besitzer einer anderen setzen – mittels eines XML-basierten Requests.
Beispielhafter Aufruf:
tsCopyEditspHttpClient.post(`${webUrl}/_vti_bin/client.svc/ProcessQuery`, ...)
Was ist ProcessQuery?
ProcessQuery ist Teil des Client Side Object Model (CSOM) von SharePoint. Diese Schnittstelle verarbeitet XML-basierte Clientanfragen und liefert JSON-Antworten zurück. Sie wird hauptsächlich intern genutzt und ist nicht breit dokumentiert.
Ein Request beschreibt:
- Welche Objekte betroffen sind (z. B. Gruppen, Listen)
- Welche Aktionen ausgeführt werden sollen (z. B.
SetProperty,Update)
Der XML-Aufbau: Actions und ObjectPaths
Ein typischer Payload sieht so aus:
xmlCopyEdit<Request ...>
<Actions>
<SetProperty Id="1" ObjectPathId="2" Name="Owner">
<Parameter ObjectPathId="3" />
</SetProperty>
<Method Name="Update" Id="4" ObjectPathId="2" />
</Actions>
<ObjectPaths>
<Identity Id="2" Name="GUID:site:SITEID:g:GROUPID" />
<Identity Id="3" Name="GUID:site:SITEID:g:OWNERGROUPID" />
</ObjectPaths>
</Request>
Die Stolperfalle: Das „Name“-Attribut im Identity-Tag
Besonders verwirrend war für uns der Aufbau des Name-Attributs:
xmlCopyEditName="740c6a0b-85e2-48a0-a494-e0f1759d4aa7:site:<siteId>:g:<groupId>"
Dabei gilt:
740c6a0b-85e2-48a0-a494-e0f1759d4aa7ist die Standard-ClientContext-GUID von SharePoint Online.- Sie ist nicht mandantenspezifisch, sondern in SharePoint Online universell gültig.
Codebeispiel: TypeScript-Methode zur Besitzübertragung
Hier die Methode, die wir erfolgreich eingesetzt haben:
tsCopyEditpublic async setOwnerOfGroup( siteId: string, newGroupId: number, ownerGroupId: number, spHttpClient: SPHttpClient, targetWebInfo: IWebInfo, digest: string ): Promise<void> { const contextGuid = "740c6a0b-85e2-48a0-a494-e0f1759d4aa7";const setOwnerPayload =
<Request xmlns="http://schemas.microsoft.com/sharepoint/clientquery/2009" SchemaVersion="15.0.0.0" LibraryVersion="16.0.0.0" ApplicationName="SPFx Custom"> <Actions> <SetProperty Id="1" ObjectPathId="2" Name="Owner"> <Parameter ObjectPathId="3" /> </SetProperty> <Method Name="Update" Id="4" ObjectPathId="2" /> </Actions> <ObjectPaths> <Identity Id="2" Name="${contextGuid}:site:${siteId}:g:${newGroupId}" /> <Identity Id="3" Name="${contextGuid}:site:${siteId}:g:${ownerGroupId}" /> </ObjectPaths> </Request>;const response = await spHttpClient.post(
${targetWebInfo.Url}/_vti_bin/client.svc/ProcessQuery, SPHttpClient.configurations.v1, { headers: { “Content-Type”: “text/xml”, Accept: “application/json”, “X-RequestDigest”: digest, }, body: setOwnerPayload.trim(), } );const responseText = await response.text(); const responseJson = JSON.parse(responseText); const hasError = responseJson.some((item: any) => item.ErrorInfo);
if (!response.ok || hasError) { throw new Error(“Setting SharePoint group owner failed.”); } }
Fazit: ProcessQuery – nicht offiziell, aber wirkungsvoll
Auch wenn ProcessQuery nicht zu den offiziell unterstützten Methoden zählt, bietet es in der Praxis eine effiziente Möglichkeit, bestimmte SharePoint-Konfigurationen vorzunehmen – insbesondere dann, wenn REST oder PnP JS an Grenzen stoßen.
Wir planen, diese Methode künftig gezielt in SPFx-Projekten und Power Platform-Lösungen zu nutzen.
Tipp: Sie stehen vor einer ähnlichen Herausforderung? Wir beraten Sie gern – schreiben Sie uns!