Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

v4.7.0 Release merge to main #3168

Merged
merged 38 commits into from
Nov 18, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
38 commits
Select commit Hold shift + click to select a range
9f9987b
Initial Pass at Lambda
bcdmi Aug 19, 2023
4c43d58
🔨 - OData filter inital prototype
Tanddant Nov 20, 2023
fbf1f56
🎉 - Initial work on filter queryable with lambda expressions
Tanddant Dec 11, 2023
430a3c0
🔨 - Move away from the "class based" model towards just a string array
Tanddant Dec 12, 2023
181de6e
🔒 - Updated package-lock (version: 4.0.0)
Tanddant Dec 12, 2023
c955e88
🔨 - SPText filters working - not auto detecting types
Tanddant Dec 18, 2023
ed46762
🔨 - switch to f => f.Text('x').Equals('y')
Tanddant Dec 18, 2023
fc6fd40
🔨 - Remove the SP-prefixed options
Tanddant Dec 18, 2023
ca0b26f
Revert "🔨 - Remove the SP-prefixed options"
Tanddant Dec 19, 2023
7d0462b
🔨 - Inital work on rewrite inspired by Beau
Tanddant Dec 19, 2023
8efc415
🔨 - Added In operator
Tanddant Dec 19, 2023
22360ba
🔨 - Lookup field support
Tanddant Dec 19, 2023
44b0795
🔨 - a bit of lint cleanup
Tanddant Dec 19, 2023
0df4ec6
🔨 - Support for quick inline field edits
Tanddant Dec 19, 2023
e2e12b0
Merge branch 'version-4' of https://github.com/pnp/pnpjs into version-4
patrick-rodgers Oct 15, 2024
17daee5
WIP graph pages, graph site open extenions and DebugHeaders
patrick-rodgers Oct 15, 2024
9019579
🔨 - Move to arrow function syntax in filters
Tanddant Oct 18, 2024
5582271
🔨 - Linting | fixed Dates being suggested as internal name in lookup …
Tanddant Oct 18, 2024
5d7ea44
🔨 Slightly more linting
Tanddant Oct 18, 2024
f92c6ba
🔨 - Allowed for mixed queries in .some and .all combiners
Tanddant Oct 18, 2024
5be7693
🔨 - Move around the code to allow .All and .Some, not only on "top le…
Tanddant Oct 19, 2024
f1c3663
🔨 - Change from taking an array of queries to using spread operator
Tanddant Oct 19, 2024
4d3bbea
Merge remote-tracking branch 'PnP/version-4' into v4-ODataFilterQuery
Tanddant Oct 20, 2024
c777844
🔨 - Changed based on feedback in initial PR
Tanddant Oct 20, 2024
76dca87
🔨- First draft of docs
Tanddant Oct 23, 2024
5b2565d
added missing exports to graph/presets/all
tavikukko Oct 26, 2024
ac62455
Merge pull request #3157 from tavikukko/graph-presets-all-added-missi…
juliemturner Oct 27, 2024
759bbcf
🎨 - rename to equals notEquals greaterThanOrEquals lessThanOrEquals
Tanddant Oct 28, 2024
a6611e0
Merge pull request #3155 from Tanddant/v4-ODataFilterQuery
bcameron1231 Nov 4, 2024
32ba4c7
wip
patrick-rodgers Nov 11, 2024
0982b9d
Merge branch 'version-4' of https://github.com/pnp/pnpjs into version-4
patrick-rodgers Nov 11, 2024
90ba34b
initial pages api support, fix for #3136
patrick-rodgers Nov 11, 2024
2066a0e
fixing linting errors
patrick-rodgers Nov 11, 2024
bda482e
fixing linting/build errors
patrick-rodgers Nov 18, 2024
5e12843
update tsconfig.json
patrick-rodgers Nov 18, 2024
d4bb740
Merge pull request #3153 from patrick-rodgers/version-4
patrick-rodgers Nov 18, 2024
636e5a8
4.7.0 updates for release
patrick-rodgers Nov 18, 2024
54bc410
Merge pull request #3167 from patrick-rodgers/4.7.0-release
patrick-rodgers Nov 18, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 14 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,20 @@ 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).

## 4.7.0 - 2024-Nov-18

- sp
- Introduces new filter lamda patterns as beta

- graph
- Renamed OneNote Pages to OneNotePages
- Basic Pages API support as beta
- Site Open Extensions as beta
- Fixed #3136 for improving paging support for query params

- queryable
- Introduced DebugHeaders behavior

## 4.6.0 - 2024-Oct-14

- Only documentation and package updates
Expand Down
4 changes: 2 additions & 2 deletions debug/launch/main.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,15 +19,15 @@ import { Example } from "./sp.js";
// create a settings file using settings.example.js as a template
import(findup("settings.js")).then((settings: { settings: ITestingSettings }) => {

Logger.activeLogLevel = LogLevel.Info;
Logger.activeLogLevel = LogLevel.Info;

// // setup console logger
Logger.subscribe(ConsoleListener("Debug", {
color: "skyblue",
error: "red",
verbose: "lightslategray",
warning: "yellow",
}));
}));

Example(settings.settings);

Expand Down
30 changes: 17 additions & 13 deletions debug/launch/setup.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,17 +3,19 @@ import { SPDefault, GraphDefault } from "@pnp/nodejs";
import { spfi, SPFI } from "@pnp/sp";
import { GraphFI, graphfi } from "@pnp/graph";
import { LogLevel, PnPLogging } from "@pnp/logging";
import { Queryable } from "@pnp/queryable";
import { Queryable, DebugHeaders } from "@pnp/queryable";

export function spSetup(settings: ITestingSettings): SPFI {

const sp = spfi(settings.testing.sp.url).using(SPDefault({
msal: {
config: settings.testing.sp.msal.init,
scopes: settings.testing.sp.msal.scopes,
},
})).using(
const sp = spfi(settings.testing.sp.url).using(
SPDefault({
msal: {
config: settings.testing.sp.msal.init,
scopes: settings.testing.sp.msal.scopes,
},
}),
PnPLogging(LogLevel.Verbose),
DebugHeaders(),
function (instance: Queryable) {

instance.on.pre(async (url, init, result) => {
Expand All @@ -29,13 +31,15 @@ export function spSetup(settings: ITestingSettings): SPFI {

export function graphSetup(settings: ITestingSettings): GraphFI {

const graph = graphfi().using(GraphDefault({
msal: {
config: settings.testing.graph.msal.init,
scopes: settings.testing.graph.msal.scopes,
},
})).using(
const graph = graphfi().using(
GraphDefault({
msal: {
config: settings.testing.graph.msal.init,
scopes: settings.testing.graph.msal.scopes,
},
}),
PnPLogging(LogLevel.Verbose),
DebugHeaders(),
function (instance: Queryable) {

instance.on.pre(async (url, init, result) => {
Expand Down
5 changes: 2 additions & 3 deletions debug/launch/sp.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,21 +3,20 @@ import { Logger, LogLevel } from "@pnp/logging";
import { spSetup } from "./setup.js";
import "@pnp/sp/webs";
import "@pnp/sp/lists";
import "@pnp/sp/items";

declare var process: { exit(code?: number): void };

export async function Example(settings: ITestingSettings) {

const sp = spSetup(settings);

const w = await sp.web();
const w = await sp.web.lists();

Logger.log({
data: w,
level: LogLevel.Info,
message: "Web Data",
});

process.exit(0);
}
3 changes: 3 additions & 0 deletions docs/graph/site-openextensions.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
# Site Open Extensions

// TODO
37 changes: 37 additions & 0 deletions docs/queryable/behaviors.md
Original file line number Diff line number Diff line change
Expand Up @@ -491,3 +491,40 @@ setTimeout(() => {
// this is awaiting the results of the request
await p;
```

### DebugHeaders

Adds logging for the request id and timestamp of the request, helpful when contacting Microsoft Support. It works for both Graph and SP libraries.

```TypeScript
import { DebugHeaders } from "@pnp/queryable";
import { spfi } from "@pnp/sp";

const sp = spfi().using(DebugHeaders());

sp.some_action();

// output to log:
// Server Request Id: {guid}
// Server Date: {date}
```

You can also supply additional headers to log from the response:


```TypeScript
import { DebugHeaders } from "@pnp/queryable";
import { spfi } from "@pnp/sp";

const sp = spfi().using(DebugHeaders(["X-MyHeader", "client-request-id"]));

sp.some_action();

// output to log:
// Server Request Id: {guid}
// Server Date: {date}
// X-MyHeader: {value}
// client-request-id: {guid}
```


97 changes: 97 additions & 0 deletions docs/sp/items.md
Original file line number Diff line number Diff line change
Expand Up @@ -113,6 +113,89 @@ const r = await sp.web.lists.getByTitle("TaxonomyList").getItemsByCAMLQuery({
});
```

### Filter using fluent filter

>Note: This feature is currently in preview and may not work as expected.

PnPjs supports a fluent filter for all OData endpoints, including the items endpoint. this allows you to write a strongly fluent filter that will be parsed into an OData filter.

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

const sp = spfi(...);

const r = await sp.web.lists.filter(l => l.number("ItemCount").greaterThan(5000))();
```

The following field types are supported in the fluent filter:

- Text
- Choice
- MultiChoice
- Number
- Date
- Boolean
- Lookup
- LookupId

The following operations are supported in the fluent filter:

| Field Type | Operators/Values |
| -------------------- | -------------------------------------------------------------------------------------------- |
| All field types | `equals`, `notEquals`, `in`, `notIn` |
| Text & choice fields | `startsWith`, `contains` |
| Numeric fields | `greaterThan`, `greaterThanOrEquals`, `lessThan`, `lessThanOrEquals` |
| Date fields | `greaterThan`, `greaterThanOrEquals`, `lessThan`, `lessThanOrEquals`, `isBetween`, `isToday` |
| Boolean fields | `isTrue`, `isFalse`, `isFalseOrNull` |
| Lookup | `id`, Text and Number field types |

#### Complex Filter

For all the regular endpoints, the fluent filter will infer the type automatically, but for the list items filter, you'll need to provide your own types to make the parser work.

You can use the `and` and `or` operators to create complex filters that nest different grouping.

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

const sp = spfi(...);

interface ListItem extends IListItem {
FirstName: string;
LastName: string;
Age: number;
Manager: IListItem;
StartDate: Date;
}


// Get all employees named John
const r = await sp.web.lists.getByTitle("ListName").items.filter<ListItem>(f => f.text("FirstName").equal("John"))();

// Get all employees not named John who are over 30
const r1 = await sp.web.lists.getByTitle("ListName").items.filter<ListItem>(f => f.text("FirstName").notEquals("John").and().number("Age").greaterThan(30))();

// Get all employees that are named John Doe or Jane Doe
const r2 = await sp.web.lists.getByTitle("ListName").items.filter<ListItem>(f => f.or(
f.and(
f.text("FirstName").equals("John"),
f.text("LastName").equals("Doe")
),
f.and(
f.text("FirstName").equals("Jane"),
f.text("LastName").equals("Doe")
)
))();

// Get all employees who are managed by John and start today
const r3 = await sp.web.lists.getByTitle("ListName").items.filter<ListItem>(f => f.lookup("Manager").text("FirstName").equals("John").and().date("StartDate").isToday())();
```

### Retrieving PublishingPageImage

The PublishingPageImage and some other publishing-related fields aren't stored in normal fields, rather in the MetaInfo field. To get these values you need to use the technique shown below, and originally outlined in [this thread](https://github.com/SharePoint/PnP-JS-Core/issues/178). Note that a lot of information can be stored in this field so will pull back potentially a significant amount of data, so limit the rows as possible to aid performance.
Expand Down Expand Up @@ -326,6 +409,8 @@ const sp = spfi(...);

// you are getting back a collection here
const items: any[] = await sp.web.lists.getByTitle("MyList").items.top(1).filter("Title eq 'A Title'")();
// Using fluent filter
const items1: any[] = await sp.web.lists.getByTitle("MyList").items.top(1).filter(f => f.text("Title").equals("A Title"))();

// see if we got something
if (items.length > 0) {
Expand Down Expand Up @@ -425,6 +510,9 @@ const sp = spfi(...);
// first we need to get the hidden field's internal name.
// The Title of that hidden field is, in my case and in the linked article just the visible field name with "_0" appended.
const fields = await sp.web.lists.getByTitle("TestList").fields.filter("Title eq 'MultiMetaData_0'").select("Title", "InternalName")();
// Using fluent filter
const fields1 = await sp.web.lists.getByTitle("TestList").fields.filter(f => f.text("Title").equals("MultiMetaData_0")).select("Title", "InternalName")();

// get an item to update, here we just create one for testing
const newItem = await sp.web.lists.getByTitle("TestList").items.add({
Title: "Testing",
Expand Down Expand Up @@ -593,6 +681,15 @@ const response =
.filter(`Hidden eq false and Title eq '[Field's_Display_Name]'`)
();

// Using fluent filter
const response1 =
await sp.web.lists
.getByTitle('[Lists_Title]')
.fields
.select('Title, EntityPropertyName')
.filter(l => l.boolean("Hidden").isFalse().and().text("Title").equals("[Field's_Display_Name]"))
();

console.log(response.map(field => {
return {
Title: field.Title,
Expand Down
15 changes: 12 additions & 3 deletions docs/sp/webs.md
Original file line number Diff line number Diff line change
Expand Up @@ -254,12 +254,15 @@ const infos2 = await web.webinfos.select("Title", "Description")();

// or filter
const infos3 = await web.webinfos.filter("Title eq 'MyWebTitle'")();
// Using fluent filter
const infos4 = await web.webinfos.filter(w => w.text("Title").equals('MyWebTitle'))();


// or both
const infos4 = await web.webinfos.select("Title", "Description").filter("Title eq 'MyWebTitle'")();
const infos5 = await web.webinfos.select("Title", "Description").filter(w => w.text("Title").equals('MyWebTitle'))();

// get the top 4 ordered by Title
const infos5 = await web.webinfos.top(4).orderBy("Title")();
const infos6 = await web.webinfos.top(4).orderBy("Title")();
```

> Note: webinfos returns [IWebInfosData](#IWebInfosData) which is a subset of all the available fields on IWebInfo.
Expand Down Expand Up @@ -537,9 +540,12 @@ const folders = await sp.web.folders();

// you can also filter and select as with any collection
const folders2 = await sp.web.folders.select("ServerRelativeUrl", "TimeLastModified").filter("ItemCount gt 0")();
// Using fluent filter
const folders3 = await sp.web.folders.select("ServerRelativeUrl", "TimeLastModified").filter(f => f.number("ItemCount").greaterThan(0))();


// or get the most recently modified folder
const folders2 = await sp.web.folders.orderBy("TimeLastModified").top(1)();
const folders4 = await sp.web.folders.orderBy("TimeLastModified").top(1)();
```

### rootFolder
Expand Down Expand Up @@ -856,6 +862,9 @@ const users = await sp.web.siteUsers();
const users2 = await sp.web.siteUsers.top(5)();

const users3 = await sp.web.siteUsers.filter(`startswith(LoginName, '${encodeURIComponent("i:0#.f|m")}')`)();
// Using fluent filter
const user4 = await sp.web.siteUsers.filter(u => u.text("LoginName").startsWith(encodeURIComponent("i:0#.f|m")))();

```

### currentUser
Expand Down
1 change: 1 addition & 0 deletions mkdocs.yml
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,7 @@ nav:
- search: 'graph/search.md'
- shares: 'graph/shares.md'
- sites: 'graph/sites.md'
- 'site openextensions': 'graph/site-openextensions.md'
- subscriptions: 'graph/subscriptions.md'
- taxonomy: 'graph/taxonomy.md'
- teams: 'graph/teams.md'
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
"name": "@pnp/monorepo",
"private": true,
"type": "module",
"version": "4.6.0",
"version": "4.7.0",
"description": "A JavaScript library for SharePoint & Graph development.",
"devDependencies": {
"@azure/identity": "4.4.1",
Expand Down
3 changes: 1 addition & 2 deletions packages/core/util.ts
Original file line number Diff line number Diff line change
Expand Up @@ -34,8 +34,7 @@ export function combine(...paths: (string | null | undefined)[]): string {

return paths
.filter(path => !stringIsNullOrEmpty(path))
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
.map(path => path!.replace(/^[\\|/]/, "").replace(/[\\|/]$/, ""))
.map(path => path.replace(/^[\\|/]/, "").replace(/[\\|/]$/, ""))
.join("/")
.replace(/\\/g, "/");
}
Expand Down
4 changes: 2 additions & 2 deletions packages/graph/decorators.ts
Original file line number Diff line number Diff line change
Expand Up @@ -147,8 +147,8 @@ export function getById<R>(factory: (...args: any[]) => R) {
return function <T extends { new(...args: any[]): {} }>(target: T) {

return class extends target {
public getById(this: IGraphQueryable, id: string): R {
return factory(this, id);
public getById(this: IGraphQueryable, id: any): R {
return factory(this, `${id}`);
}
};
};
Expand Down
Loading
Loading