microsoft-graph 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (151) hide show
  1. package/.editorconfig +18 -0
  2. package/.gitattributes +46 -0
  3. package/.vscode/extensions.json +3 -0
  4. package/.vscode/launch.json +15 -0
  5. package/.vscode/settings.json +15 -0
  6. package/CONTRIBUTING.md +2 -0
  7. package/LICENCE.md +360 -0
  8. package/README.md +52 -0
  9. package/biome.jsonc +83 -0
  10. package/dist/errors/BadTemplateError.js +6 -0
  11. package/dist/errors/EnvironmentVariableMissingError.js +6 -0
  12. package/dist/errors/InvalidArgumentError.js +21 -0
  13. package/dist/errors/ProtocolError.js +11 -0
  14. package/dist/errors/RequestFailedError.js +21 -0
  15. package/dist/graphApi.js +116 -0
  16. package/dist/index.js +2 -0
  17. package/dist/models/AccessToken.js +1 -0
  18. package/dist/models/DriveId.js +1 -0
  19. package/dist/models/DriveItemId.js +1 -0
  20. package/dist/models/DriveItemPath.js +1 -0
  21. package/dist/models/DriveItemRef.js +1 -0
  22. package/dist/models/DriveRef.js +1 -0
  23. package/dist/models/GraphOperation.js +1 -0
  24. package/dist/models/GraphOperationDefinition.js +1 -0
  25. package/dist/models/HostName.js +1 -0
  26. package/dist/models/Scope.js +1 -0
  27. package/dist/models/SiteId.js +1 -0
  28. package/dist/models/SiteName.js +1 -0
  29. package/dist/models/SiteRef.js +1 -0
  30. package/dist/models/WorkbookNamedRangeRef.js +1 -0
  31. package/dist/models/WorkbookRangeAddress.js +1 -0
  32. package/dist/models/WorkbookRangeName.js +1 -0
  33. package/dist/models/WorkbookRangeRef.js +1 -0
  34. package/dist/models/WorkbookRef.js +1 -0
  35. package/dist/models/WorkbookSessionId.js +1 -0
  36. package/dist/models/WorkbookTableId.js +1 -0
  37. package/dist/models/WorkbookTableRef.js +1 -0
  38. package/dist/models/WorkbookWorksheetId.js +1 -0
  39. package/dist/models/WorkbookWorksheetName.js +1 -0
  40. package/dist/models/WorkbookWorksheetRef.js +1 -0
  41. package/dist/operations/drive/createFolder.js +27 -0
  42. package/dist/operations/drive/createFolder.test.js +31 -0
  43. package/dist/operations/drive/listDrives.js +23 -0
  44. package/dist/operations/drive/listDrives.test.js +9 -0
  45. package/dist/operations/driveItem/copyDriveItem.js +21 -0
  46. package/dist/operations/driveItem/copyDriveItem.test.js +28 -0
  47. package/dist/operations/driveItem/deleteDriveItem.js +12 -0
  48. package/dist/operations/driveItem/deleteDriveItem.test.js +22 -0
  49. package/dist/operations/driveItem/getDriveItem.js +18 -0
  50. package/dist/operations/driveItem/getDriveItem.test.js +24 -0
  51. package/dist/operations/driveItem/getDriveItemByPath.js +20 -0
  52. package/dist/operations/driveItem/getDriveItemByPath.test.js +22 -0
  53. package/dist/operations/driveItem/getDriveItemContent.js +22 -0
  54. package/dist/operations/driveItem/getDriveItemContent.test.js +37 -0
  55. package/dist/operations/driveItem/listDriveItems.js +25 -0
  56. package/dist/operations/driveItem/listDriveItems.test.js +24 -0
  57. package/dist/operations/site/getSite.js +18 -0
  58. package/dist/operations/site/getSite.test.js +15 -0
  59. package/dist/operations/site/getSiteByName.js +20 -0
  60. package/dist/operations/site/getSiteByName.test.js +20 -0
  61. package/dist/operations/site/listSites.js +23 -0
  62. package/dist/operations/site/listSites.test.js +10 -0
  63. package/dist/operations/site/searchSites.js +23 -0
  64. package/dist/operations/site/searchSites.test.js +21 -0
  65. package/dist/operations/workbook/calculateWorkbook.js +20 -0
  66. package/dist/operations/workbook/calculateWorkbook.test.js +46 -0
  67. package/dist/operations/workbook/createWorkbook.js +26 -0
  68. package/dist/operations/workbook/createWorkbook.test.js +15 -0
  69. package/dist/operations/workbook/deleteWorkbook.js +5 -0
  70. package/dist/operations/workbookRange/clearWorkbookRange.js +17 -0
  71. package/dist/operations/workbookRange/clearWorkbookRange.test.js +56 -0
  72. package/dist/operations/workbookRange/deleteWorkbookRange.js +17 -0
  73. package/dist/operations/workbookRange/deleteWorkbookRange.test.js +56 -0
  74. package/dist/operations/workbookRange/getWorkbookNamedRange.js +20 -0
  75. package/dist/operations/workbookRange/getWorkbookUsedRange.js +22 -0
  76. package/dist/operations/workbookRange/getWorkbookUsedRange.test.js +54 -0
  77. package/dist/operations/workbookRange/getWorkbookVisibleRange.js +20 -0
  78. package/dist/operations/workbookRange/getWorkbookVisibleRange.test.js +109 -0
  79. package/dist/operations/workbookRange/insertWorkbookCells.js +25 -0
  80. package/dist/operations/workbookRange/insertWorkbookCells.test.js +41 -0
  81. package/dist/operations/workbookRange/updateWorkbookNamedRange.js +15 -0
  82. package/dist/operations/workbookRange/updateWorkbookRange.js +21 -0
  83. package/dist/operations/workbookRange/updateWorkbookRange.test.js +54 -0
  84. package/dist/operations/workbookSession/closeWorkbookSession.js +18 -0
  85. package/dist/operations/workbookSession/createWorkbookSession.js +22 -0
  86. package/dist/operations/workbookSession/refreshWorkbookSession.js +19 -0
  87. package/dist/operations/workbookTable/createWorkbookTable.js +26 -0
  88. package/dist/operations/workbookTable/createWorkbookTable.test.js +25 -0
  89. package/dist/operations/workbookTable/getWorkbookTable.js +20 -0
  90. package/dist/operations/workbookTable/getWorkbookTable.test.js +30 -0
  91. package/dist/operations/workbookTable/getWorkbookTableBodyRange.js +22 -0
  92. package/dist/operations/workbookTable/getWorkbookTableBodyRange.test.js +45 -0
  93. package/dist/operations/workbookTable/getWorkbookTableHeaderRange.js +22 -0
  94. package/dist/operations/workbookTable/getWorkbookTableHeaderRange.test.js +41 -0
  95. package/dist/operations/workbookTable/listWorkbookTableColumns.js +17 -0
  96. package/dist/operations/workbookTable/listWorkbookTableColumns.test.js +29 -0
  97. package/dist/operations/workbookTable/listWorkbookTableRows.js +17 -0
  98. package/dist/operations/workbookTable/listWorkbookTableRows.test.js +29 -0
  99. package/dist/operations/workbookTable/listWorkbookTables.js +25 -0
  100. package/dist/operations/workbookTable/listWorkbookTables.test.js +30 -0
  101. package/dist/operations/workbookWorksheet/createWorkbookWorksheet.js +25 -0
  102. package/dist/operations/workbookWorksheet/createWorkbookWorksheet.test.js +36 -0
  103. package/dist/operations/workbookWorksheet/deleteWorkbookWorksheet.js +14 -0
  104. package/dist/operations/workbookWorksheet/deleteWorkbookWorksheet.test.js +28 -0
  105. package/dist/operations/workbookWorksheet/getWorkbookWorksheetRange.js +20 -0
  106. package/dist/operations/workbookWorksheet/getWorkbookWorksheetRange.test.js +30 -0
  107. package/dist/operations/workbookWorksheet/listWorkbookWorksheets.js +28 -0
  108. package/dist/operations/workbookWorksheet/listWorkbookWorksheets.test.js +45 -0
  109. package/dist/operations/workbookWorksheet/updateWorkbookWorksheet.js +21 -0
  110. package/dist/operations/workbookWorksheet/updateWorkbookWorksheet.test.js +37 -0
  111. package/dist/services/accessToken.js +15 -0
  112. package/dist/services/address.js +3 -0
  113. package/dist/services/configuration.js +20 -0
  114. package/dist/services/drive.js +17 -0
  115. package/dist/services/driveItem.js +48 -0
  116. package/dist/services/driveItem.test.js +17 -0
  117. package/dist/services/httpAgent.js +9 -0
  118. package/dist/services/httpStatus.js +3 -0
  119. package/dist/services/operationId.js +6 -0
  120. package/dist/services/sharepointUrl.js +24 -0
  121. package/dist/services/sharepointUrl.test.js +32 -0
  122. package/dist/services/site.js +15 -0
  123. package/dist/services/sleep.js +3 -0
  124. package/dist/services/stringCaseConversion.js +5 -0
  125. package/dist/services/stringCaseConversion.test.js +16 -0
  126. package/dist/services/templatedPaths.js +20 -0
  127. package/dist/services/templatedPaths.test.js +49 -0
  128. package/dist/services/temporaryFiles.js +11 -0
  129. package/dist/services/workbookRange.js +10 -0
  130. package/dist/services/workbookRangeAddress.js +8 -0
  131. package/dist/services/workbookTable.js +13 -0
  132. package/dist/services/workbookWorksheet.js +13 -0
  133. package/dist/tasks/createWorkbookAndStartSession.js +10 -0
  134. package/dist/tasks/deleteDriveItemWithRetry.js +16 -0
  135. package/dist/tasks/downloadDriveItemContent.js +7 -0
  136. package/dist/tasks/endSessionAndDeleteWorkbook.js +6 -0
  137. package/dist/tasks/getRangeLastUsedCell.js +38 -0
  138. package/dist/tasks/getRangeLastUsedCell.test.js +89 -0
  139. package/dist/tasks/getWorkbookTableVisibleBody.js +24 -0
  140. package/dist/tasks/getWorkbookTableVisibleBody.test.js +104 -0
  141. package/dist/tasks/getWorkbookWorksheetRefByName.js +10 -0
  142. package/dist/tasks/setColumnHidden.js +7 -0
  143. package/dist/tasks/setColumnHidden.test.js +43 -0
  144. package/dist/tasks/setRowHidden.js +7 -0
  145. package/dist/tasks/setRowHidden.test.js +42 -0
  146. package/docs/approach.md +7 -0
  147. package/docs/calculateWorkbook.md +20 -0
  148. package/docs/concepts.md +4 -0
  149. package/docs/envs.md +11 -0
  150. package/docs/performance.md +24 -0
  151. package/package.json +426 -0
@@ -0,0 +1,13 @@
1
+ export function workbookTableRef(worksheetRef, tableId) {
2
+ if (!tableId) {
3
+ throw new Error("TableId is missing");
4
+ }
5
+ return {
6
+ siteId: worksheetRef.siteId,
7
+ driveId: worksheetRef.driveId,
8
+ itemId: worksheetRef.itemId,
9
+ worksheetId: worksheetRef.worksheetId,
10
+ sessionId: worksheetRef.sessionId,
11
+ tableId: tableId
12
+ };
13
+ }
@@ -0,0 +1,13 @@
1
+ export const defaultWorkbookWorksheetId = "{00000000-0001-0000-0000-000000000000}"; // Program Manager in Microsoft Office Extensibility team says this ID is used for the first sheet of all workbooks by design, but not documented. https://github.com/OfficeDev/office-js/issues/552#issuecomment-800841930
2
+ export function workbookWorksheetRef(workbookRef, worksheetId) {
3
+ if (!worksheetId) {
4
+ throw new Error("WorksheetID is missing");
5
+ }
6
+ return {
7
+ siteId: workbookRef.siteId,
8
+ driveId: workbookRef.driveId,
9
+ itemId: workbookRef.itemId,
10
+ sessionId: workbookRef.sessionId,
11
+ worksheetId
12
+ };
13
+ }
@@ -0,0 +1,10 @@
1
+ import createWorkbook from "../operations/workbook/createWorkbook.js";
2
+ import createWorkbookSession from "../operations/workbookSession/createWorkbookSession.js";
3
+ export default async function createWorkbookAndStartSession(driveRef, itemPath) {
4
+ const workbook = await createWorkbook(driveRef, itemPath);
5
+ const workbookRef = await createWorkbookSession(workbook);
6
+ return {
7
+ ...workbook,
8
+ ...workbookRef
9
+ };
10
+ }
@@ -0,0 +1,16 @@
1
+ import deleteDriveItem from "../operations/driveItem/deleteDriveItem.js";
2
+ import { sleep } from "../services/sleep.js";
3
+ export default async function deleteDriveItemWithRetry(driveItemRef) {
4
+ const retryDelays = [1000, 2000, 4000];
5
+ for (const delay of retryDelays) {
6
+ try {
7
+ await deleteDriveItem(driveItemRef);
8
+ return;
9
+ }
10
+ catch (_) {
11
+ await sleep(delay);
12
+ }
13
+ }
14
+ // Final attempt without delay
15
+ await deleteDriveItem(driveItemRef);
16
+ }
@@ -0,0 +1,7 @@
1
+ import { writeFile } from "node:fs/promises";
2
+ import getDriveItemContent from "../operations/driveItem/getDriveItemContent.js";
3
+ export default async function downloadDriveItemContent(itemRef, localFilePath) {
4
+ const arrayBuffer = await getDriveItemContent(itemRef);
5
+ const buffer = Buffer.from(arrayBuffer);
6
+ await writeFile(localFilePath, buffer);
7
+ }
@@ -0,0 +1,6 @@
1
+ import closeWorkbookSession from "../operations/workbookSession/closeWorkbookSession.js";
2
+ import deleteDriveItemWithRetry from "./deleteDriveItemWithRetry.js";
3
+ export default async function endSessionAndDeleteWorkbook(workbookRef) {
4
+ await closeWorkbookSession(workbookRef);
5
+ await deleteDriveItemWithRetry(workbookRef); // Close session takes a moment to release the file lock
6
+ }
@@ -0,0 +1,38 @@
1
+ import ProtocolError from "../errors/ProtocolError.js";
2
+ import getWorkbookWorksheetRange from "../operations/workbookWorksheet/getWorkbookWorksheetRange.js";
3
+ import { indexesToAddress as cellIndexesToAddress } from "../services/address.js";
4
+ export default async function getRangeLastUsedCell(rangeRef) {
5
+ // TODO: Consider adding chunking if the range is too large
6
+ // TODO: Reduce cells returned by using "used range"?
7
+ const range = await getWorkbookWorksheetRange(rangeRef);
8
+ const { rowCount, columnCount } = range;
9
+ if (rowCount === undefined || columnCount === undefined) {
10
+ throw new ProtocolError("Counts missing");
11
+ }
12
+ const values = range.values;
13
+ if (values === undefined) {
14
+ throw new ProtocolError("Range values missing");
15
+ }
16
+ for (let rowIndex = rowCount - 1; rowIndex >= 0; rowIndex--) {
17
+ const row = values[rowIndex];
18
+ if (row === undefined) {
19
+ throw new ProtocolError("Row missing");
20
+ }
21
+ for (let columnIndex = columnCount - 1; columnIndex >= 0; columnIndex--) {
22
+ const cell = row[columnIndex];
23
+ if (cell === undefined) {
24
+ throw new ProtocolError("Cell missing");
25
+ }
26
+ if (cell !== null && cell !== "") {
27
+ const address = cellIndexesToAddress(rowIndex, columnIndex);
28
+ return {
29
+ value: cell,
30
+ address,
31
+ rowIndex: rowIndex,
32
+ columnIndex: columnIndex
33
+ };
34
+ }
35
+ }
36
+ }
37
+ return null;
38
+ }
@@ -0,0 +1,89 @@
1
+ import { describe, expect, it } from "vitest";
2
+ import { sequential } from "../graphApi.js";
3
+ import calculateWorkbook from "../operations/workbook/calculateWorkbook.js";
4
+ import createWorkbook from "../operations/workbook/createWorkbook.js";
5
+ import updateWorkbookRange from "../operations/workbookRange/updateWorkbookRange.js";
6
+ import { getDefaultDriveRef } from "../services/drive.js";
7
+ import { driveItemPath } from "../services/driveItem.js";
8
+ import { generateTempFileName } from "../services/temporaryFiles.js";
9
+ import { workbookRangeRef } from "../services/workbookRange.js";
10
+ import { defaultWorkbookWorksheetId, workbookWorksheetRef } from "../services/workbookWorksheet.js";
11
+ import deleteDriveItemWithRetry from "./deleteDriveItemWithRetry.js";
12
+ import getRangeLastUsedCell from "./getRangeLastUsedCell.js";
13
+ describe("getRangeLastUsedCell", () => {
14
+ it("should return the last used cell value", { timeout: 10000 }, async () => {
15
+ const workbookName = generateTempFileName("xlsx");
16
+ const workbookPath = driveItemPath(workbookName);
17
+ const driveRef = getDefaultDriveRef();
18
+ const workbook = await createWorkbook(driveRef, workbookPath);
19
+ const worksheetRef = workbookWorksheetRef(workbook, defaultWorkbookWorksheetId);
20
+ const rangeRef = workbookRangeRef(worksheetRef, "A1:B2");
21
+ try {
22
+ await sequential(updateWorkbookRange(rangeRef, {
23
+ values: [
24
+ [1, 2],
25
+ [3, 4]
26
+ ]
27
+ }), calculateWorkbook(workbook));
28
+ const result = await getRangeLastUsedCell(rangeRef);
29
+ if (result === null) {
30
+ throw new Error("Expected a result");
31
+ }
32
+ expect(result.value).toBe(4);
33
+ expect(result.address).toBe("B2");
34
+ expect(result.rowIndex).toBe(1);
35
+ expect(result.columnIndex).toBe(1);
36
+ }
37
+ finally {
38
+ await deleteDriveItemWithRetry(workbook);
39
+ }
40
+ });
41
+ it("should return the last non-empty cell value when the last cell is empty", { timeout: 10000 }, async () => {
42
+ const workbookName = generateTempFileName("xlsx");
43
+ const workbookPath = driveItemPath(workbookName);
44
+ const driveRef = getDefaultDriveRef();
45
+ const workbook = await createWorkbook(driveRef, workbookPath);
46
+ const worksheetRef = workbookWorksheetRef(workbook, defaultWorkbookWorksheetId);
47
+ const rangeRef = workbookRangeRef(worksheetRef, "A1:B2");
48
+ try {
49
+ await sequential(updateWorkbookRange(rangeRef, {
50
+ values: [
51
+ [1, 2],
52
+ [3, null]
53
+ ]
54
+ }), calculateWorkbook(workbook));
55
+ const result = await getRangeLastUsedCell(rangeRef);
56
+ if (result === null) {
57
+ throw new Error("Expected a result");
58
+ }
59
+ expect(result.value).toBe(3);
60
+ expect(result.address).toBe("A2");
61
+ expect(result.rowIndex).toBe(1);
62
+ expect(result.columnIndex).toBe(0);
63
+ }
64
+ finally {
65
+ await deleteDriveItemWithRetry(workbook);
66
+ }
67
+ });
68
+ it("should return null when no cells are used", { timeout: 10000 }, async () => {
69
+ const workbookName = generateTempFileName("xlsx");
70
+ const workbookPath = driveItemPath(workbookName);
71
+ const driveRef = getDefaultDriveRef();
72
+ const workbook = await createWorkbook(driveRef, workbookPath);
73
+ const worksheetRef = workbookWorksheetRef(workbook, defaultWorkbookWorksheetId);
74
+ const rangeRef = workbookRangeRef(worksheetRef, "A1:B2");
75
+ try {
76
+ await sequential(updateWorkbookRange(rangeRef, {
77
+ values: [
78
+ [null, null],
79
+ [null, null]
80
+ ]
81
+ }), calculateWorkbook(workbook));
82
+ const result = await getRangeLastUsedCell(rangeRef);
83
+ expect(result).toBeNull();
84
+ }
85
+ finally {
86
+ await deleteDriveItemWithRetry(workbook);
87
+ }
88
+ });
89
+ });
@@ -0,0 +1,24 @@
1
+ import getWorkbookVisibleRange from "../operations/workbookRange/getWorkbookVisibleRange.js";
2
+ import getWorkbookTableBodyRange from "../operations/workbookTable/getWorkbookTableBodyRange.js";
3
+ export async function getWorkbookTableVisibleBody(tableRef) {
4
+ const range = await getWorkbookTableBodyRange(tableRef);
5
+ const visibleRange = await getWorkbookVisibleRange(range);
6
+ return visibleRange;
7
+ // TODO: Alternate approach. More performant, but doesn't handle hidden columns yet. Pondering!
8
+ // const rows = await listWorkbookTableRows(tableRef);
9
+ // const visibleRows: WorkbookTableRow[] = [];
10
+ // for (const row of rows) {
11
+ // const index = row.index;
12
+ // if (index === undefined) {
13
+ // throw new ProtocolError("Row index is undefined");
14
+ // }
15
+ // const rowRange = await getWorkbookWorksheetRange({
16
+ // ...tableRef,
17
+ // address: workbookRangeAddress(`${index + 1}:${index + 1}`)
18
+ // });
19
+ // if (!rowRange.rowHidden) {
20
+ // visibleRows.push(row);
21
+ // }
22
+ // }
23
+ // return visibleRows;
24
+ }
@@ -0,0 +1,104 @@
1
+ import { describe, expect, it } from "vitest";
2
+ import calculateWorkbook from "../operations/workbook/calculateWorkbook.js";
3
+ import createWorkbook from "../operations/workbook/createWorkbook.js";
4
+ import updateWorkbookRange from "../operations/workbookRange/updateWorkbookRange.js";
5
+ import createWorkbookTable from "../operations/workbookTable/createWorkbookTable.js";
6
+ import createWorkbookWorksheet from "../operations/workbookWorksheet/createWorkbookWorksheet.js";
7
+ import { getDefaultDriveRef } from "../services/drive.js";
8
+ import { driveItemPath } from "../services/driveItem.js";
9
+ import { generateTempFileName } from "../services/temporaryFiles.js";
10
+ import { workbookRangeRef } from "../services/workbookRange.js";
11
+ import deleteDriveItemWithRetry from "./deleteDriveItemWithRetry.js";
12
+ import { getWorkbookTableVisibleBody } from "./getWorkbookTableVisibleBody.js";
13
+ describe("getWorkbookTableVisibleBody", () => {
14
+ it("can retrieve the visible body range of a table", { timeout: 10000 }, async () => {
15
+ const workbookName = generateTempFileName("xlsx");
16
+ const workbookPath = driveItemPath(workbookName);
17
+ const driveRef = getDefaultDriveRef();
18
+ const workbook = await createWorkbook(driveRef, workbookPath);
19
+ try {
20
+ const worksheet = await createWorkbookWorksheet(workbook);
21
+ const rangeRef = workbookRangeRef(worksheet, "A1:D4");
22
+ const table = await createWorkbookTable(rangeRef, true);
23
+ await updateWorkbookRange(rangeRef, {
24
+ values: [
25
+ ["Header1", "Header2", "Header3", "Header4"],
26
+ ["Value1", "Value2", "Value3", "Value4"],
27
+ ["Value5", "Value6", "Value7", "Value8"],
28
+ ["Value9", "Value10", "Value11", "Value12"]
29
+ ]
30
+ });
31
+ await calculateWorkbook(workbook);
32
+ const visibleBodyRange = await getWorkbookTableVisibleBody(table);
33
+ expect(visibleBodyRange.values).toEqual([
34
+ ["Value1", "Value2", "Value3", "Value4"],
35
+ ["Value5", "Value6", "Value7", "Value8"],
36
+ ["Value9", "Value10", "Value11", "Value12"]
37
+ ]);
38
+ }
39
+ finally {
40
+ await deleteDriveItemWithRetry(workbook);
41
+ }
42
+ });
43
+ it("omits hidden rows from the visible body range of a table", { timeout: 10000 }, async () => {
44
+ const workbookName = generateTempFileName("xlsx");
45
+ const workbookPath = driveItemPath(workbookName);
46
+ const driveRef = getDefaultDriveRef();
47
+ const workbook = await createWorkbook(driveRef, workbookPath);
48
+ try {
49
+ const worksheet = await createWorkbookWorksheet(workbook);
50
+ const rangeRef = workbookRangeRef(worksheet, "A1:D4");
51
+ const table = await createWorkbookTable(rangeRef, true);
52
+ await updateWorkbookRange(rangeRef, {
53
+ values: [
54
+ ["Header1", "Header2", "Header3", "Header4"],
55
+ ["Value1", "Value2", "Value3", "Value4"],
56
+ ["Value5", "Value6", "Value7", "Value8"],
57
+ ["Value9", "Value10", "Value11", "Value12"]
58
+ ]
59
+ });
60
+ const hiddenRange = workbookRangeRef(worksheet, "2:2");
61
+ await updateWorkbookRange(hiddenRange, { rowHidden: true });
62
+ await calculateWorkbook(workbook);
63
+ const visibleBodyRange = await getWorkbookTableVisibleBody(table);
64
+ expect(visibleBodyRange.values).toEqual([
65
+ ["Value5", "Value6", "Value7", "Value8"],
66
+ ["Value9", "Value10", "Value11", "Value12"]
67
+ ]);
68
+ }
69
+ finally {
70
+ await deleteDriveItemWithRetry(workbook);
71
+ }
72
+ });
73
+ it("omits hidden columns from the visible body range of a table", { timeout: 10000 }, async () => {
74
+ const workbookName = generateTempFileName("xlsx");
75
+ const workbookPath = driveItemPath(workbookName);
76
+ const driveRef = getDefaultDriveRef();
77
+ const workbook = await createWorkbook(driveRef, workbookPath);
78
+ try {
79
+ const worksheet = await createWorkbookWorksheet(workbook);
80
+ const rangeRef = workbookRangeRef(worksheet, "A1:D4");
81
+ const table = await createWorkbookTable(rangeRef, true);
82
+ await updateWorkbookRange(rangeRef, {
83
+ values: [
84
+ ["Header1", "Header2", "Header3", "Header4"],
85
+ ["Value1", "Value2", "Value3", "Value4"],
86
+ ["Value5", "Value6", "Value7", "Value8"],
87
+ ["Value9", "Value10", "Value11", "Value12"]
88
+ ]
89
+ });
90
+ const hiddenRange = workbookRangeRef(worksheet, "B:B");
91
+ await updateWorkbookRange(hiddenRange, { columnHidden: true });
92
+ await calculateWorkbook(workbook);
93
+ const visibleBodyRange = await getWorkbookTableVisibleBody(table);
94
+ expect(visibleBodyRange.values).toEqual([
95
+ ["Value1", "Value3", "Value4"],
96
+ ["Value5", "Value7", "Value8"],
97
+ ["Value9", "Value11", "Value12"]
98
+ ]);
99
+ }
100
+ finally {
101
+ await deleteDriveItemWithRetry(workbook);
102
+ }
103
+ });
104
+ });
@@ -0,0 +1,10 @@
1
+ import listWorkbookWorksheets from "../operations/workbookWorksheet/listWorkbookWorksheets.js";
2
+ export default async function getWorkbookWorksheetByName(workbookRef, name) {
3
+ // TODO: Probably can get this from a direct API call without fetching all worksheets
4
+ const worksheets = await listWorkbookWorksheets(workbookRef);
5
+ const worksheet = worksheets.find(worksheetRef => worksheetRef.name === name);
6
+ if (!worksheet) {
7
+ throw new Error(`Worksheet '${name}' not found.`); // TODO: Make NotFoundError
8
+ }
9
+ return worksheet;
10
+ }
@@ -0,0 +1,7 @@
1
+ import updateWorkbookRange from "../operations/workbookRange/updateWorkbookRange.js";
2
+ export default async function setColumnHidden(rangeRef, hidden) {
3
+ await updateWorkbookRange(rangeRef, {
4
+ columnHidden: hidden
5
+ });
6
+ }
7
+ ;
@@ -0,0 +1,43 @@
1
+ import { describe, expect, it } from "vitest";
2
+ import calculateWorkbook from "../operations/workbook/calculateWorkbook.js";
3
+ import createWorkbook from "../operations/workbook/createWorkbook.js";
4
+ import getWorkbookVisibleRange from "../operations/workbookRange/getWorkbookVisibleRange.js";
5
+ import updateWorkbookRange from "../operations/workbookRange/updateWorkbookRange.js";
6
+ import { getDefaultDriveRef } from "../services/drive.js";
7
+ import { driveItemPath, } from "../services/driveItem.js";
8
+ import { generateTempFileName } from "../services/temporaryFiles.js";
9
+ import { workbookRangeRef } from "../services/workbookRange.js";
10
+ import { defaultWorkbookWorksheetId, workbookWorksheetRef } from "../services/workbookWorksheet.js";
11
+ import deleteDriveItemWithRetry from "./deleteDriveItemWithRetry.js";
12
+ import setColumnHidden from "./setColumnHidden.js";
13
+ describe("setColumnHidden", () => {
14
+ it("hides a column in an existing workbook", { timeout: 10000 }, async () => {
15
+ const workbookName = generateTempFileName("xlsx");
16
+ const workbookPath = driveItemPath(workbookName);
17
+ const driveRef = getDefaultDriveRef();
18
+ const workbook = await createWorkbook(driveRef, workbookPath);
19
+ const worksheetRef = workbookWorksheetRef(workbook, defaultWorkbookWorksheetId);
20
+ try {
21
+ const rangeRef = workbookRangeRef(worksheetRef, "A1:C3");
22
+ await updateWorkbookRange(rangeRef, {
23
+ values: [
24
+ [1, 2, 3],
25
+ [4, 5, 6],
26
+ [7, 8, 9]
27
+ ]
28
+ });
29
+ const hiddenRange = workbookRangeRef(worksheetRef, "B:B");
30
+ await setColumnHidden(hiddenRange, true);
31
+ await calculateWorkbook(workbook);
32
+ const visibleView = await getWorkbookVisibleRange(rangeRef);
33
+ expect(visibleView.values).toEqual([
34
+ [1, 3],
35
+ [4, 6],
36
+ [7, 9]
37
+ ]);
38
+ }
39
+ finally {
40
+ await deleteDriveItemWithRetry(workbook);
41
+ }
42
+ });
43
+ });
@@ -0,0 +1,7 @@
1
+ import updateWorkbookRange from "../operations/workbookRange/updateWorkbookRange.js";
2
+ export default async function setRowHidden(rangeRef, hidden) {
3
+ await updateWorkbookRange(rangeRef, {
4
+ rowHidden: hidden
5
+ });
6
+ }
7
+ ;
@@ -0,0 +1,42 @@
1
+ import { describe, expect, it } from "vitest";
2
+ import calculateWorkbook from "../operations/workbook/calculateWorkbook.js";
3
+ import createWorkbook from "../operations/workbook/createWorkbook.js";
4
+ import getWorkbookVisibleRange from "../operations/workbookRange/getWorkbookVisibleRange.js";
5
+ import updateWorkbookRange from "../operations/workbookRange/updateWorkbookRange.js";
6
+ import { getDefaultDriveRef } from "../services/drive.js";
7
+ import { driveItemPath, } from "../services/driveItem.js";
8
+ import { generateTempFileName } from "../services/temporaryFiles.js";
9
+ import { workbookRangeRef } from "../services/workbookRange.js";
10
+ import { defaultWorkbookWorksheetId, workbookWorksheetRef } from "../services/workbookWorksheet.js";
11
+ import deleteDriveItemWithRetry from "./deleteDriveItemWithRetry.js";
12
+ import setRowHidden from "./setRowHidden.js";
13
+ describe("setRowHidden", () => {
14
+ it("hides a row in an existing workbook", { timeout: 10000 }, async () => {
15
+ const workbookName = generateTempFileName("xlsx");
16
+ const workbookPath = driveItemPath(workbookName);
17
+ const driveRef = getDefaultDriveRef();
18
+ const workbook = await createWorkbook(driveRef, workbookPath);
19
+ const worksheetRef = workbookWorksheetRef(workbook, defaultWorkbookWorksheetId);
20
+ try {
21
+ const rangeRef = workbookRangeRef(worksheetRef, "A1:C3");
22
+ await updateWorkbookRange(rangeRef, {
23
+ values: [
24
+ [1, 2, 3],
25
+ [4, 5, 6],
26
+ [7, 8, 9]
27
+ ]
28
+ });
29
+ const hiddenRange = workbookRangeRef(worksheetRef, "2:2");
30
+ await setRowHidden(hiddenRange, true);
31
+ await calculateWorkbook(workbook);
32
+ const visibleView = await getWorkbookVisibleRange(rangeRef);
33
+ expect(visibleView.values).toEqual([
34
+ [1, 2, 3],
35
+ [7, 8, 9]
36
+ ]);
37
+ }
38
+ finally {
39
+ await deleteDriveItemWithRetry(workbook);
40
+ }
41
+ });
42
+ });
@@ -0,0 +1,7 @@
1
+ # Justification/Approach
2
+ Why not just use the default SDK for this? Well - there isn't really one. Sure, there is [`@microsoft/microsoft-graph-client`](https://www.npmjs.com/package/@microsoft/microsoft-graph-client), however it's thin and doesn't define
3
+ individual methods we can call. And there's also [`msgraph-typescript-typings`](https://github.com/microsoftgraph/msgraph-typescript-typings) which defines
4
+ the models, however nothing appears to exist to pull these basic concepts together into a SDK.
5
+
6
+ This project implements `msgraph-typescript-typings` DTOs without using `@microsoft/microsoft-graph-client` as it added negligible value.
7
+
@@ -0,0 +1,20 @@
1
+ # `calculateWorkbook()`
2
+ Here's a gotcha. If you write to a workbook and immediately afterwards read from it, the change you just wrote might not have taken effect yet. This is due to the eventual consistency model Sharepoint uses. To ensure consistency you must call `calculateWorkbook()` before your reads. You can use this as part of a `sequential` batch as follows for the best performance:
3
+
4
+ ```typescript
5
+ await updateWorkbookRange(rangeRef, { values: values });
6
+ await calculateWorkbook(workbookRef); // IMPORTANT!!
7
+ const usedRange = await getWorkbookUsedRange(worksheetRef);
8
+ ```
9
+
10
+ The same applies to the batch version:
11
+
12
+ ```typescript
13
+ const [_, __, usedRange] = await sequential(
14
+ updateWorkbookRange(rangeRef, {
15
+ values: values
16
+ }),
17
+ calculateWorkbook(workbookRef), // IMPORTANT!!
18
+ getWorkbookUsedRange(worksheetRef)
19
+ );
20
+ ```
@@ -0,0 +1,4 @@
1
+ # Key concepts
2
+ GraphAPI exposes a set of operations. This SDK has a series of `operations` defined that maps to this API. If we want to do some rich manipulation of those operations we use a `task`.
3
+
4
+ When using the SDK you can use low-level operations directly (ie `createWorkbookSession`) or you can use higher-level opinionated tasks (ie `createWorkbookAndOpenSession`). The opinionated tasks are useful for quick development, however they include assumptions that may not match your circumstance, and also do not lend themselves to optimizations like parallel requesting. You choose what works for you, and you can mix-and-match both.
package/docs/envs.md ADDED
@@ -0,0 +1,11 @@
1
+ # Environment variables
2
+ Environment variables are read just-in-time. A missing env will result in an exception.
3
+
4
+ | Env | Mandatory | Purpose |
5
+ | ----------------------------- | --------- | ------------------------------------------------------------------ |
6
+ | `AZURE_TENANT_ID` | Y | Entra Tenant ID, used for authentication |
7
+ | `AZURE_CLIENT_ID` | Y | Entra Client ID, used for authentication |
8
+ | `AZURE_CLIENT_SECRET` | Y | Entra Client secret, used for authentication |
9
+ | `SHAREPOINT_DEFAULT_SITE_ID` | N | ID of the Sharepoint site to use when none is specified otherwise. |
10
+ | `SHAREPOINT_DEFAULT_DRIVE_ID` | N | ID of the Drive to use when none is specified otherwise |
11
+ | `HTTP_PROXY` | N | HTTP proxy to route requests via. ie 'http://proxy:3128' |
@@ -0,0 +1,24 @@
1
+ # Performance
2
+ If your particular use-case requires enhanced performance there are a couple of tools available to help.
3
+
4
+ ## Sessions
5
+ If you have performing multiple operations on a workbook, first open a session using `createWorkbookSession` and close the session at the end with `closeWorkbookSession` (or it times out after 5mins or so). Without this GraphAPI effectively opens and closes the spreadsheet on every operation. With a session in place it keeps the document open.
6
+
7
+ ## Parallel batch
8
+ If you have a number of operations that can be performed at the same time, use `await parallel(operation1(), operation2(), ...)`. This allows you to send up to 20 operations to GraphAPI in a single request and have them be performed in parallel.
9
+
10
+ ## Parallel batch
11
+ Sometimes the order of operations are important. To facilitate this variant of a batch operation you can use `await sequential(operation1(), operation2(), ...)`.
12
+
13
+ ```typescript
14
+ const [_, __, ___, clearedRange] = await sequential(
15
+ updateWorkbookRange(rangeRef, {
16
+ values: values
17
+ }),
18
+ clearWorkbookRange(rangeRef),
19
+ calculateWorkbook(workbookRef),
20
+ getWorkbookRange(rangeRef)
21
+ );
22
+ ```
23
+
24
+ While this allows for up to 20 operations to be submitted to GraphAPI in one request, it waits for the former operation to complete before starting the next.