Skip to content

Commit

Permalink
Merge branch 'version-3' into main
Browse files Browse the repository at this point in the history
  • Loading branch information
juliemturner committed Jul 24, 2023
2 parents 7848c74 + cb17b39 commit e9cb8cc
Show file tree
Hide file tree
Showing 21 changed files with 719 additions and 404 deletions.
1 change: 1 addition & 0 deletions .eslintignore
Original file line number Diff line number Diff line change
Expand Up @@ -13,3 +13,4 @@ coverage
.eslintrc.cjs
debug
buildsystem-config.ts
settings.js
12 changes: 12 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,18 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/)
and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.html).

## 3.17.0 - 2023-Jul-24

- sp/graph
- Addresses #2716 - Fix for batching adding incorrect headers in certain circumstances

- graph
- Addresses #2567 - Fixes issue with channel messages in teams.
- Addresses #2677 - Removed analytics endpoint on drive root as it's not longer supported. V4 updates will include new analytics modules that will add more features.

- logging
- Addresses #2731 - Fixed issues with browser console levels.

## 3.16.0 - 2023-Jun-9

### Fixed
Expand Down
52 changes: 33 additions & 19 deletions docs/concepts/auth-nodejs.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,7 @@ We support MSAL for both browser and nodejs and Azure Identity for nodejs by pro

Depending on which package you want to use you will need to install an additional package from the library because of the large dependencies.

For the NodeJS MSAL package:

`npm install @pnp/msaljsclient --save`

We support MSAL through the [msal-node](https://github.com/AzureAD/microsoft-authentication-library-for-js/blob/dev/lib/msal-node/README.md) library.
We support MSAL through the [msal-node](https://github.com/AzureAD/microsoft-authentication-library-for-js/blob/dev/lib/msal-node/README.md) library which is included by the @pnp/nodejs package.

For the Azure Identity package:

Expand All @@ -18,15 +14,14 @@ We support Azure Identity through the [@azure/identity](https://github.com/Azure

## MSAL + NodeJS

The SPDefault and GraphDefault exported by the nodejs library include MSAL and takes the parameters directly. Please consider that ability deprecated and instead use the method shown below to chain the MSAL auth behavior and configure it independently.
The SPDefault and GraphDefault exported by the nodejs library include MSAL and takes the parameters directly.

The following samples reference a MSAL configuration that utilizes an Azure AD App Registration, these are samples that show the typings for those objects:

```TypeScript
import { SPDefault, GraphDefault } from "@pnp/nodejs";
import { spfi } from "@pnp/sp";
import { graphfi } from "@pnp/graph";
import { MSAL } from "@pnp/msaljsclient";
import { Configuration, AuthenticationParameters } from "msal";
import "@pnp/graph/users";
import "@pnp/sp/webs";
Expand All @@ -38,24 +33,43 @@ const configuration: Configuration = {
}
};

const authParams: AuthenticationParameters = {
scopes: ["https://graph.microsoft.com/.default"]
};
const sp = spfi("{site url}").using(SPDefault({
msal: {
config: configuration,
scopes: ["https://{tenant}.sharepoint.com/.default"],
},
}));

const sp = spfi("https://{tenant}.sharepoint.com/sites/dev").using(
SPDefault(),
MSAL(configuration, authParams)
);

const graph = graphfi().using(
GraphDefault(),
MSAL(configuration, authParams)
);
const graph = graphfi().using(GraphDefault({
msal: {
config: configuration,
scopes: ["https://graph.microsoft.com/.default"],
},
}));

const webData = await sp.web();
const meData = await graph.me();
```

## Use Nodejs MSAL behavior directly

It is also possible to use the MSAL behavior directly if you are composing your own strategies.

```TypeScript
import { SPDefault, GraphDefault, MSAL } from "@pnp/nodejs";

const sp = spfi("{site url}").using(SPDefault(), MSAL({
config: configuration,
scopes: ["https://{tenant}.sharepoint.com/.default"],
}));

const graph = graphfi().using(GraphDefault(), MSAL({
config: configuration,
scopes: ["https://graph.microsoft.com/.default"],
}));

```

## Azure Identity + NodeJS

The following sample shows how to pass the credential object to the AzureIdentity behavior including scopes.
Expand Down
212 changes: 212 additions & 0 deletions docs/concepts/calling-other-endpoints.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,212 @@
# Calling other endpoints not currently implemented in PnPjs library

If you find that there are endpoints that have not yet been implemented, or have changed in such a way that there are issues using the implemented endpoint, you can still make those calls and take advantage of the plumbing provided by the library.

## SharePoint

To issue calls against the SharePoint REST endpoints you would use one of the existing [operations](https://github.com/pnp/pnpjs/blob/version-3/packages/sp/operations.ts):

- spGet
- spPost
- spDelete
- spPatch
and the extended post methods with additional headers.
- spPostMerge
- spPostDelete
- spPostDeleteETag

To construct a call you will need to pass, to the operation call an SPQueryable and optionally a RequestInit object which will be merged with any existing registered init object. To learn more about queryable and the options for constructing one, check out the [documentation](../queryable/queryable.md).

Below are a couple of examples to get you started.

### Example spGet

Let's pretend that the getById method didn't exist on a lists items. The example below shows two methods for constructing our SPQueryable method.

The first is the easiest to use because, as the queryable documentation tells us, this will maintain all the registered observers on the original queryable instance. We would start with the queryable object closest to the endpoint we want to use, in this case `list`. We do this because we need to construct the full URL that will be called. Using `list` in this instance gives us the first part of the URL (e.g. `https://contoso.sharepoint.com/sites/testsite/_api/web/lists/getByTitle('My List')`) and then we can construct the remainder of the call by passing in a string.

The second method essentially starts from scratch where the user constructs the entire url and then registers observers on the SPQuerable instance. Then uses spGet to execute the call. There are many other variations to arrive at the same outcome, all are dependent on your requirements.

```TypeScript
import { spfi } from "@pnp/sp";
import "@pnp/sp/webs";
import "@pnp/sp/lists";
import { spGet, SPQueryable, SPFx, AssignFrom } from "@pnp/sp";

// Establish SPFI instance passing in the appropriate behavior to register the initial observers.
const sp = spfi(...);

// create an instance of the items queryable

const list = sp.web.lists.getByTitle("My List");

// get the item with an id of 1, easiest method
const item: any = await spGet(SPQueryable(list, "items(1)"));

// get the item with an id of 1, constructing a new queryable and registering behaviors
const spQueryable = SPQueryable("https://contoso.sharepoint.com/sites/testsite/_api/web/lists/getByTitle('My List')/items(1)").using(SPFx(this.context));

// ***or***

// For v3 the full url is require for SPQuerable when providing just a string
const spQueryable = SPQueryable("https://contoso.sharepoint.com/sites/testsite/_api/web/lists/getByTitle('My List')/items(1)").using(AssignFrom(sp));

// and then use spQueryable to make the request
const item: any = await spGet(spQueryable);
```

The resulting call will be to the endpoint:
`https://contoso.sharepoint.com/sites/testsite/_api/web/lists/getByTitle('My List')/items(1)`

### Example spPost

Let's now pretend that we need to get the changes on a list and want to call the `getchanges` method off list.

```TypeScript
import { spfi } from "@pnp/sp";
import "@pnp/sp/webs";
import "@pnp/sp/lists";
import { IChangeQuery, spPost, SPQueryable } from "@pnp/sp";
import { body } from "@pnp/queryable";

// Establish SPFI instance passing in the appropriate behavior to register the initial observers.
const sp = spfi(...);


// build the changeQuery object, here we look att changes regarding Add, DeleteObject and Restore
const query: IChangeQuery = {
Add: true,
ChangeTokenEnd: null,
ChangeTokenStart: null,
DeleteObject: true,
Rename: true,
Restore: true,
};

// create an instance of the items queryable
const list = sp.web.lists.getByTitle("My List");

// get the item with an id of 1
const changes: any = await spPost(SPQueryable(list, "getchanges"), body({query}));

```

The resulting call will be to the endpoint:
`https://contoso.sharepoint.com/sites/testsite/_api/web/lists/getByTitle('My List')/getchanges`

## Microsoft Graph

To issue calls against the Microsoft Graph REST endpoints you would use one of the existing [operations](https://github.com/pnp/pnpjs/blob/version-3/packages/graph/operations.ts):

- graphGet
- graphPost
- graphDelete
- graphPatch
- graphPut

To construct a call you will need to pass, to the operation call an GraphQueryable and optionally a RequestInit object which will be merged with any existing registered init object. To learn more about queryable and the options for constructing one, check out the [documentation](../queryable/queryable.md).

Below are a couple of examples to get you started.

### Example graphGet

Here's an example for getting the chats for a particular user. This uses the simplest method for constructing the graphQueryable which is to start with a instance of a queryable that is close to the endpoint we want to call, in this case `user` and then adding the additional path as a string. For a more advanced example see `spGet` above.

```TypeScript
import { graphfi } from "@pnp/graph";
import "@pnp/graph/users";
import { GraphQueryable, graphGet } from "@pnp/graph";

// Establish GRAPHFI instance passing in the appropriate behavior to register the initial observers.
const graph = graphfi(...);

// create an instance of the user queryable
const user = graph.users.getById('[email protected]');

// get the chats for the user
const chat: any = await graphGet(GraphQueryable(user, "chats"));
```

The results call will be to the endpoint:
`https://graph.microsoft.com/v1.0/users/[email protected]/chats`

### Example graphPost

This is an example of adding an event to a calendar.

```TypeScript
import { graphfi } from "@pnp/graph";
import "@pnp/graph/users";
import "@pnp/graph/calendars";
import { GraphQueryable, graphPost } from "@pnp/graph";
import { body, InjectHeaders } from "@pnp/queryable";

// Establish GRAPHFI instance passing in the appropriate behavior to register the initial observers.
const graph = graphfi(...);

// create an instance of the user queryable
const calendar = graph.users.getById('[email protected]').calendar;

const props = {
"subject": "Let's go for lunch",
"body": {
"contentType": "HTML",
"content": "Does noon work for you?"
},
"start": {
"dateTime": "2017-04-15T12:00:00",
"timeZone": "Pacific Standard Time"
},
"end": {
"dateTime": "2017-04-15T14:00:00",
"timeZone": "Pacific Standard Time"
},
"location":{
"displayName":"Harry's Bar"
},
"attendees": [
{
"emailAddress": {
"address":"[email protected]",
"name": "Samantha Booth"
},
"type": "required"
}
],
"allowNewTimeProposals": true,
"transactionId":"7E163156-7762-4BEB-A1C6-729EA81755A7"
};

// custom request init to add timezone header.
const graphQueryable = GraphQueryable(calendar, "events").using(InjectHeaders({
"Prefer": 'outlook.timezone="Pacific Standard Time"',
}));

// adds a new event to the user's calendar
const event: any = await graphPost(graphQueryable, body(props));
```

The results call will be to the endpoint:
`https://graph.microsoft.com/v1.0/users/[email protected]/calendar/events`

## Advanced Scenario

If you find you need to create an instance of Queryable (for either graph or SharePoint) that would hang off the root of the url you can use the `AssignFrom` or `CopyFrom` [behaviors](../core/behaviors.md).

```TypeScript
import { graphfi } from "@pnp/graph";
import "@pnp/graph/users";
import { GraphQueryable, graphPost } from "@pnp/graph";
import { body, InjectHeaders } from "@pnp/queryable";
import { AssignFrom } from "@pnp/core";

// Establish GRAPHFI instance passing in the appropriate behavior to register the initial observers.
const graph = graphfi(...);

const chatsQueryable = GraphQueryable("chats").using(AssignFrom(graph.me));

const chat: any = await graphPost(chatsQueryable, body(chatBody));
```

The results call will be to the endpoint:
`https://graph.microsoft.com/v1.0/chats`
2 changes: 1 addition & 1 deletion docs/getting-started.md
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ We've created two Getting Started samples. The first uses the more traditional R

[Getting started with PnPjs 3.0: 5-part series](https://youtube.com/playlist?list=PLR9nK3mnD-OWvmtj9TKE6tM7ZrUosV_vB)

In addition, [Beau Cameron](https://github.com/bcameron1231) gave the team an assist by converting the sample project from React Component to React Hooks. His version can be found in [react-pnp-js-hooks](https://github.com/pnp/sp-dev-fx-webparts/tree/main/samples/react-pnp-js-hooks). This sammple will help those struggling to establish context correctly while using the hooks conventions.
In addition, we have converted the sample project from React Component to React Hooks. This version can be found in [react-pnp-js-hooks](https://github.com/pnp/sp-dev-fx-webparts/tree/main/samples/react-pnp-js-hooks). This sample will help those struggling to establish context correctly while using the hooks conventions.

The SharePoint Framework supports different versions of TypeScript natively and as of 1.14 release still doesn't natively support TypeScript 4.x. Sadly, this means that to use Version 3 of PnPjs you will need to take a few additional configuration steps to get them to work together.

Expand Down
14 changes: 12 additions & 2 deletions docs/graph/teams.md
Original file line number Diff line number Diff line change
Expand Up @@ -183,6 +183,17 @@ const newChannel = await graph.teams.getById('3531f3fb-f9ee-4f43-982a-6c90d82265

```

## List Messages

```TypeScript
import { graphfi } from "@pnp/graph";
import "@pnp/graph/teams";

const graph = graphfi(...);

const chatMessages = await graph.teams.getById('3531fzfb-f9ee-4f43-982a-6c90d8226528').channels.getById('19:[email protected]').messages();
```

## Add chat message to Channel

```TypeScript
Expand All @@ -197,8 +208,7 @@ const message = {
"content": "Hello World"
}
}
const chatMessage: ChatMessage = await graph.teams.getById('3531f3fb-f9ee-4f43-982a-6c90d8226528').channels.getById('19:[email protected]').messages(message);

const chatMessage: ChatMessage = await graph.teams.getById('3531fzfb-f9ee-4f43-982a-6c90d8226528').channels.getById('19:[email protected]').messages.add(message);
```

## Get installed Apps
Expand Down
33 changes: 33 additions & 0 deletions docs/sp/items.md
Original file line number Diff line number Diff line change
Expand Up @@ -418,6 +418,39 @@ await execute();

console.log("Done");
```
### Update Taxonomy field

Note: Updating Taxonomy field for a File item should be handled differently. Instead of using update(), use validateUpdateListItem(). Please see below

List Item
```TypeScript
import { spfi } from "@pnp/sp";
import "@pnp/sp/webs";
import "@pnp/sp/lists";
import "@pnp/sp/items";

const sp = spfi(...);

await sp.web.lists.getByTitle("Demo").items.getById(1).update({
MetaDataColumn: { Label: "Demo", TermGuid: '883e4c81-e8f9-4f19-b90b-6ab805c9f626', WssId: '-1' }
});

```
File List Item
```TypeScript
import { spfi } from "@pnp/sp";
import "@pnp/sp/webs";
import "@pnp/sp/lists";
import "@pnp/sp/items";
import "@pnp/sp/files";

const sp = spfi(...);

await (await sp.web.getFileByServerRelativePath("/sites/demo/DemoLibrary/File.txt").getItem()).validateUpdateListItem([{
FieldName: "MetaDataColumn",
FieldValue:"Demo|883e4c81-e8f9-4f19-b90b-6ab805c9f626", //Label|TermGuid
}]);
```

### Update Multi-value Taxonomy field

Expand Down
Loading

0 comments on commit e9cb8cc

Please sign in to comment.