Skip to content

Commit d89a0da

Browse files
authored
Merge pull request #3287 from pnp/version-4
Merge Release 4.15.0 to Main
2 parents 2cb65b1 + 9f8d1ed commit d89a0da

File tree

11 files changed

+205
-172
lines changed

11 files changed

+205
-172
lines changed

CHANGELOG.md

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,14 @@ All notable changes to this project will be documented in this file.
55
The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/)
66
and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.html).
77

8+
## 4.15.0 - 2025-Jul-14
9+
10+
- sp
11+
- Small fix for addChunked for small files.
12+
13+
- graph
14+
- Fix issues with addCopyFromContentTypeHub returning queryable and header for polling api
15+
816
## 4.14.0 - 2025-Jun-9
917

1018
- sp

docs/sp/sp-utilities-utility.md

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,9 @@ Through the REST api you are able to call a subset of the SP.Utilities.Utility m
44

55
## sendEmail
66

7+
> ⚠️ **Deprecated**: This method is deprecated in favor of the [Graph module's `sendMessage`](https://pnp.github.io/pnpjs/graph/mail-messages/#send-message).
8+
> Please migrate to the Graph module for sending emails. This functionality will be removed in future versions.
9+
710
This methods allows you to send an email based on the supplied arguments. The method takes a single argument, a plain object defined by the EmailProperties interface (shown below).
811

912
### EmailProperties
@@ -63,7 +66,8 @@ const sp = spfi(...);
6366

6467
let addressString: string = await sp.utility.getCurrentUserEmailAddresses();
6568

66-
// and use it with sendEmail
69+
// and use it with sendEmail
70+
// note: sendEmail has been deprecated in favor of Graph module's sendMessage functionality
6771
await sp.utility.sendEmail({
6872
To: [addressString],
6973
Subject: "This email is about...",

package-lock.json

Lines changed: 132 additions & 130 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,10 @@
22
"name": "@pnp/monorepo",
33
"private": true,
44
"type": "module",
5-
"version": "4.14.0",
5+
"version": "4.15.0",
66
"description": "A JavaScript library for SharePoint & Graph development.",
77
"devDependencies": {
8-
"@azure/identity": "4.10.0",
8+
"@azure/identity": "4.10.2",
99
"@azure/msal-browser": "3.28.1",
1010
"@azure/msal-node": "2.16.2",
1111
"@microsoft/microsoft-graph-types": "2.40.0",

packages/azidjsclient/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
"dependencies": {
88
"@pnp/core": "0.0.0-PLACEHOLDER",
99
"@pnp/queryable": "0.0.0-PLACEHOLDER",
10-
"@azure/identity": "4.9.1",
10+
"@azure/identity": "4.10.2",
1111
"tslib": "2.8.1"
1212
}
1313
}

packages/graph/content-types/types.ts

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -81,11 +81,11 @@ export class _ContentTypes extends _GraphCollection<IContentTypeEntity[]>{
8181
*/
8282
public async addCopyFromContentTypeHub(contentTypeId: string): Promise<IContentTypeAddResult> {
8383
const creator = ContentType(this, "addCopyFromContentTypeHub").using(JSONHeaderParse());
84-
const data = await graphPost(creator, body({ contentTypeId }));
85-
const pendingLocation = data?.headers?.location || null;
84+
const result = await graphPost(creator, body({ contentTypeId }));
85+
const pendingLocation = result?.headers?.get("location") || null;
8686
return {
87-
data: data.data,
88-
contentType: (<any>this).getById(data.id),
87+
data: result.data,
88+
contentType: (<any>this).getById(result.data.id),
8989
pendingLocation,
9090
};
9191
}

packages/queryable/behaviors/parsers.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -48,14 +48,14 @@ export function JSONHeaderParse(): TimelinePipe {
4848
return parseBinderWithErrorCheck(async (response) => {
4949

5050
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
51-
if ((response.headers.has("Content-Length") && parseFloat(response.headers.get("Content-Length")!) === 0) || response.status === 204) {
51+
if (response.status === 204) {
5252
return {};
5353
}
5454

5555
// patch to handle cases of 200 response with no or whitespace only bodies (#487 & #545)
5656
const txt = await response.text();
5757
const json = txt.replace(/\s/ig, "").length > 0 ? JSON.parse(txt) : {};
58-
return { data: { ...parseODataJSON(json) }, headers: { ...response.headers } };
58+
return { data: { ...parseODataJSON(json) }, headers: response.headers };
5959
});
6060
}
6161

packages/sp/files/types.ts

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -422,25 +422,30 @@ export class _File extends ReadableFile<IFileInfo> {
422422
buffer = newBuffer;
423423
}
424424

425-
while (buffer.length >= chunkSize || (chunk.done && buffer.length > 0)) {
425+
while (buffer.length >= chunkSize) {
426426
const chunkToUpload = buffer.slice(0, chunkSize);
427427
buffer = buffer.slice(chunkSize);
428428

429429
if (first) {
430430
progress({ offset, stage: "starting", uploadId });
431431
offset = await spPost(File(fileRef, `startUpload(uploadId=guid'${uploadId}')`), { body: chunkToUpload });
432432
first = false;
433-
} else if (chunk.done && buffer.length === 0) {
434-
progress({ offset, stage: "finishing", uploadId });
435-
return spPost(File(fileRef, `finishUpload(uploadId=guid'${uploadId}',fileOffset=${offset})`), { body: chunkToUpload });
436433
} else {
437434
progress({ offset, stage: "continue", uploadId });
438435
offset = await spPost(File(fileRef, `continueUpload(uploadId=guid'${uploadId}',fileOffset=${offset})`), { body: chunkToUpload });
439436
}
440437
}
441438

442439
if (chunk.done) {
443-
break;
440+
if (first) {
441+
// Small file: not enough data to trigger a chunk upload
442+
progress({ offset, stage: "starting", uploadId });
443+
offset = await spPost(File(fileRef, `startUpload(uploadId=guid'${uploadId}')`), { body: buffer });
444+
first = false;
445+
buffer = new Uint8Array(); // reset buffer on small file upload, so we don't duplicate the buffer on finishUpload. Issue #3278
446+
}
447+
progress({ offset, stage: "finishing", uploadId });
448+
return spPost(File(fileRef, `finishUpload(uploadId=guid'${uploadId}',fileOffset=${offset})`), { body: buffer.length ? buffer : "" });
444449
}
445450
}
446451
}

packages/sp/sputilities/types.ts

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -89,8 +89,14 @@ export class _Utilities extends _SPQueryable implements IUtilities {
8989
export interface IUtilities {
9090

9191
/**
92-
* This methods will send an e-mail based on the incoming properties of the IEmailProperties parameter.
93-
* @param props IEmailProperties object
92+
* Sends an email based on the provided {@link IEmailProperties}.
93+
*
94+
* @param props - The email configuration object.
95+
*
96+
* @deprecated This method is deprecated in favor of the Graph module's email functionality.
97+
* Use {@link https://pnp.github.io/pnpjs/graph/mail-messages/#send-message | Graph: sendMessage} instead.
98+
*
99+
* @see {@link https://pnp.github.io/pnpjs/graph/mail-messages/#send-message}
94100
*/
95101
sendEmail(props: IEmailProperties): Promise<void>;
96102

test/graph/admin.ts

Lines changed: 31 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ describe("Admin", function () {
88
// let propertyId = "";
99

1010
// Ensure we have the data to test against
11-
before(async function () {
11+
before(async function () {
1212

1313
if (!this.pnp.settings.enableWebTests || stringIsNullOrEmpty(this.pnp.settings.testUser)) {
1414
this.skip();
@@ -36,36 +36,36 @@ describe("Admin", function () {
3636
});
3737

3838
describe("SharePoint", function () {
39-
it("Get SharePoint Settings", pnpTest("923c1bd6-8621-41d2-9ea9-004a4a735c9f", async function () {
39+
it("Get SharePoint Settings", pnpTest("923c1bd6-8621-41d2-9ea9-004a4a735c9f", async function () {
4040
const sharePointSettings = await this.pnp.graph.admin.sharepoint.settings();
4141
return expect(sharePointSettings.availableManagedPathsForSiteCreation.length > 0).is.true;
4242
}));
4343

44-
it("Update SharePoint Settings", pnpTest("bbf52535-3a7e-452b-b0eb-9940832163aa", async function () {
44+
it("Update SharePoint Settings", pnpTest("bbf52535-3a7e-452b-b0eb-9940832163aa", async function () {
4545
const sharePointSettings = await this.pnp.graph.admin.sharepoint.settings.update({ deletedUserPersonalSiteRetentionPeriodInDays: 30 });
4646
return expect(sharePointSettings.deletedUserPersonalSiteRetentionPeriodInDays === 30).is.true;
4747
}));
4848
});
4949

5050
describe("People", function () {
51-
it("Get People Settings", pnpTest("9bd5a022-65d3-4a34-b8c4-c74381b98551", async function () {
51+
it("Get People Settings", pnpTest("9bd5a022-65d3-4a34-b8c4-c74381b98551", async function () {
5252
const settings = await this.pnp.graph.admin.people();
5353
return expect(settings.profileCardProperties).is.not.null;
5454
}));
5555

56-
it("Get Pronoun Settings", pnpTest("bbc0e5af-3620-4164-9120-556ac534db39", async function () {
56+
it("Get Pronoun Settings", pnpTest("bbc0e5af-3620-4164-9120-556ac534db39", async function () {
5757
const settings = await this.pnp.graph.admin.people.pronounSettings();
5858
return expect(settings.isEnabledInOrganization).to.be.an("boolean");
5959
}));
6060

61-
it.skip("Update Pronoun Settings", pnpTest("830c2b41-5642-40d6-8585-3e26207e3f13", async function () {
61+
it.skip("Update Pronoun Settings", pnpTest("830c2b41-5642-40d6-8585-3e26207e3f13", async function () {
6262
const settings = await this.pnp.graph.admin.people.pronounSettings.update({
6363
isEnabledInOrganization: true,
6464
});
6565
return expect(settings.isEnabledInOrganization).is.true;
6666
}));
6767

68-
it.skip("Add Profile Card Property", pnpTest("49b98899-0af3-4b8b-8f66-3748410420b7", async function () {
68+
it.skip("Add Profile Card Property", pnpTest("49b98899-0af3-4b8b-8f66-3748410420b7", async function () {
6969
const property = await this.pnp.graph.admin.people.profileCardProperties.add({
7070
directoryPropertyName: "CustomAttribute2",
7171
annotations: [{
@@ -81,12 +81,12 @@ describe("Admin", function () {
8181
return expect(property.id).is.not.null;
8282
}));
8383

84-
it.skip("Get Profile Card Property", pnpTest("05d8f50a-1b47-4631-9576-2aa3c5efcf75", async function () {
84+
it.skip("Get Profile Card Property", pnpTest("05d8f50a-1b47-4631-9576-2aa3c5efcf75", async function () {
8585
const property = await this.pnp.graph.admin.people.profileCardProperties.getById(customUserProperty)();
8686
return expect(property.id).is.not.null;
8787
}));
8888

89-
it.skip("Update Profile Card Property", pnpTest("04fb914e-41c6-4b8e-a326-63c41e6672a4", async function () {
89+
it.skip("Update Profile Card Property", pnpTest("04fb914e-41c6-4b8e-a326-63c41e6672a4", async function () {
9090
const displayName = getRandomString(5) + "Cost Center";
9191
const property = await this.pnp.graph.admin.people.profileCardProperties.getById(customUserProperty).update({
9292
directoryPropertyName: this.customUserProperty,
@@ -103,7 +103,7 @@ describe("Admin", function () {
103103
return expect(property.annotations[0]?.displayName).equals(displayName);
104104
}));
105105

106-
it.skip("Delete Profile Card Property", pnpTest("fbfae956-d776-4bd7-8ad2-3db384ec02c3", async function () {
106+
it.skip("Delete Profile Card Property", pnpTest("fbfae956-d776-4bd7-8ad2-3db384ec02c3", async function () {
107107
const property = await this.pnp.graph.admin.people.profileCardProperties.add({
108108
directoryPropertyName: getRandomString(5) + "CustomAttribute2",
109109
annotations: [{
@@ -122,55 +122,62 @@ describe("Admin", function () {
122122
});
123123

124124
describe("Service Health", function () {
125-
it("Get Health Overviews", pnpTest("79f7392b-053d-44a0-87f6-a1c2332d6841", async function () {
125+
it("Get Health Overviews", pnpTest("79f7392b-053d-44a0-87f6-a1c2332d6841", async function () {
126126
const healthOverviews = await this.pnp.graph.admin.serviceAnnouncements.healthOverviews();
127127
return expect(healthOverviews).to.be.an("array");
128128
}));
129129

130-
it("Get Health By Service Name", pnpTest("f06cd76b-3a61-4728-ba5e-f97bb6e718a8", async function () {
130+
it("Get Health By Service Name", pnpTest("f06cd76b-3a61-4728-ba5e-f97bb6e718a8", async function () {
131131
const serviceHealth = await this.pnp.graph.admin.serviceAnnouncements.healthOverviews.getByName("Microsoft 365 suite")();
132132
return expect(serviceHealth).has.property("id");
133133
}));
134134

135-
it("Get Health Issues", pnpTest("6b04e99e-dcbb-48ee-87c2-4d17b1fad12d", async function () {
135+
it("Get Health Issues", pnpTest("6b04e99e-dcbb-48ee-87c2-4d17b1fad12d", async function () {
136136
const issues = await this.pnp.graph.admin.serviceAnnouncements.issues();
137137
return expect(issues).to.be.an("array");
138138
}));
139139

140-
it("Get Health Messages", pnpTest("d06cd76b-3a61-4728-ba5e-f97bb6e718a8", async function () {
140+
it("Get Health Messages", pnpTest("d06cd76b-3a61-4728-ba5e-f97bb6e718a8", async function () {
141141
const messages = await this.pnp.graph.admin.serviceAnnouncements.messages();
142142
return expect(messages).to.be.an("array");
143143
}));
144144

145-
it("Get Health Message by ID", pnpTest("2cc3edd5-b7af-4967-b8b4-840d161f1b61", async function () {
145+
it("Get Health Message by ID", pnpTest("2cc3edd5-b7af-4967-b8b4-840d161f1b61", async function () {
146146
const messages = await this.pnp.graph.admin.serviceAnnouncements.messages();
147-
if(messages.length > 0){
147+
if (messages.length > 0) {
148148
const messageById = await this.pnp.graph.admin.serviceAnnouncements.messages.getById(messages[0]?.id)();
149149
return expect(messageById).is.not.null;
150-
}else{
150+
} else {
151151
console.log("No messages to test");
152152
return true;
153153
}
154154
}));
155155

156-
it("Get Health Message Attachments", pnpTest("2e26b2a1-5ce8-4cf9-a0dc-4decddba5641", async function () {
156+
it("Get Health Message Attachments", pnpTest("2e26b2a1-5ce8-4cf9-a0dc-4decddba5641", async function () {
157157
const messages = await this.pnp.graph.admin.serviceAnnouncements.messages();
158158

159159
const attachments = await this.pnp.graph.admin.serviceAnnouncements.messages.getById(messages[0]?.id).attachments();
160160
return expect(attachments).to.be.an("array");
161161
}));
162162

163-
it("Get Health Message Attachments by Id", pnpTest("2cef2a70-31c9-4180-91bf-f0bab86e3501", async function () {
163+
it("Get Health Message Attachments by Id", pnpTest("2cef2a70-31c9-4180-91bf-f0bab86e3501", async function () {
164164
const messages = await this.pnp.graph.admin.serviceAnnouncements.messages();
165-
const attachments = await this.pnp.graph.admin.serviceAnnouncements.messages.getById(messages[0]?.id).attachments();
166-
const attachmentById = await this.pnp.graph.admin.serviceAnnouncements.messages.getById(attachments[0]?.id)();
167-
168-
return expect(attachmentById).is.ok;
165+
if (messages.length > 0) {
166+
const attachments = await this.pnp.graph.admin.serviceAnnouncements.messages.getById(messages[0]?.id).attachments();
167+
if (attachments.length > 0) {
168+
const attachmentById = await this.pnp.graph.admin.serviceAnnouncements.messages.getById(attachments[0]?.id)();
169+
return expect(attachmentById).is.ok;
170+
} else {
171+
return true;
172+
}
173+
} else {
174+
return true;
175+
}
169176
}));
170177
});
171178

172179

173-
after(async function () {
180+
after(async function () {
174181

175182
// Only needed when profile card properties can be created with application permissions
176183
// if (!stringIsNullOrEmpty(propertyId)) {

0 commit comments

Comments
 (0)