0

I am trying to read the entries from a people-picker field in a list and add those user accounts to a SharePoint group. To avoid throttling, I am batching my requests. (getSP makes the call to spfi using SPBrowser.)

import { useState } from "react";
import { DefaultButton, TextField } from "@fluentui/react";
import { chunk } from "lodash";
import "@pnp/sp/batching";
import "@pnp/sp/items";
import "@pnp/sp/items/get-all";
import "@pnp/sp/lists";
import "@pnp/sp/webs";
import "@pnp/sp/site-groups";
import { getSP } from "./helpers/sp";
import "./App.css";

function App() 
{
  const [groupName, setGroupName] = useState("");

  const onChange_groupName = (_, valueNew) => (void setGroupName(valueNew));
  const onClick_populateGroup = async () =>
  {
    const onGroup_fail = () => (sp.web.siteGroups.add({ Title: groupName }));
    const populateGroup = ({ group, users }) =>
    {
      const chainPromises = (funcs) =>
      {
        const chainPromise = (result, func) =>
          ((typeof result.then === "function") ? result.then(func) : result().then(func));
        return funcs.reduce(chainPromise);
      };
      const populateChunk = (usersChunk) => () =>
      {
        const addUser = (spGroup) => (user) => (spGroup.users.add(user));
        const [web, execute] = sp.web.batched();
        const promises = usersChunk.map(addUser(web.siteGroups.getById(group.Id ?? group.ID)));
        return execute().then(() => (Promise.all(promises))).then(console.log).catch(console.error);
      };
      return chainPromises(chunk(users, 20).map(populateChunk));
    };
    const readList = async () =>
    {
      const filterNotNull = (value) => (value != null);
      const mapEmployee = ({ Email }) => (Email?.Name);
      return (await sp.web.lists.getByTitle("Source").items.expand("Email").select("Email/Name").getAll())
        .map(mapEmployee)
        .filter(filterNotNull);
    };
    let sp = getSP();
    populateGroup({
      group: await sp.web.siteGroups.getByName(groupName)().catch(onGroup_fail),
      users: await readList()
    });
  };

  return (
    <>
      <h1>Create Group from Source list</h1>
      <div className="card">
        <label>
          <span>Group name:</span>
          <TextField onChange={onChange_groupName} />
        </label>
        <div><DefaultButton onClick={onClick_populateGroup}>Populate Group</DefaultButton></div>
      </div>
    </>
  );
}

export default App;

What happens? SharePoint consistently fails to resolve seven of the users in each batch of 20 before executing the batch. Thus, 13 users of each batch are added, seven are not, and each batch request returns with seven copies of the error message reading, "The user does not exist or is not unique." I had originally tried batches of 50; 23 would resolve and 27 would not.

While I found an undocumented work-around (see below), I'd like to find a way to get this process working as documented.

P.S. Here is the workaround: Import @pnp/sp/context-info, then replace populateChunk and the subsequent line of code above with the following.

const populateChunk = ({ FormDigestValue, WebFullUrl }) => (usersChunk) => async () =>
{
  const mapUser = (username) => ({ Key: username });
  const body = {
    peoplePickerInput: JSON.stringify(usersChunk.map(mapUser)),
    roleValue: `group:${group.Id ?? group.ID}`,
    url: WebFullUrl
  };
  return fetch(
    `${WebFullUrl}/_api/SP.Web.ShareObject`,
    {
      body: JSON.stringify(body),
      headers: {
        Accept: "application/json;odata=verbose",
        "Content-Type": "application/json;odata=verbose",
        "X-RequestDigest": FormDigestValue
      },
      method: "POST"
    }
  );
};
return chainPromises(chunk(users, 20).map(populateChunk(await sp.web.getContextInfo())));

0

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Browse other questions tagged or ask your own question.