@lindle/sharepoint_requests 0.1.17 → 0.1.19

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 (47) hide show
  1. package/README.md +121 -116
  2. package/dist/index.d.ts +2 -2
  3. package/dist/root/index.d.ts +106 -31
  4. package/dist/root/internal/context.d.ts +2 -2
  5. package/dist/root/internal/digest.d.ts +1 -1
  6. package/dist/root/internal/emailRequests.d.ts +3 -3
  7. package/dist/root/internal/listRequests/createAttachment.d.ts +2 -2
  8. package/dist/root/internal/listRequests/createListItem.d.ts +3 -3
  9. package/dist/root/internal/listRequests/deleteFile.d.ts +2 -2
  10. package/dist/root/internal/listRequests/deleteItem.d.ts +2 -2
  11. package/dist/root/internal/listRequests/getFiles.d.ts +2 -2
  12. package/dist/root/internal/listRequests/getListItems.d.ts +3 -3
  13. package/dist/root/internal/listRequests/getOnly.d.ts +2 -2
  14. package/dist/root/internal/listRequests/normalizePayload.d.ts +4 -0
  15. package/dist/root/internal/listRequests/updateListItem.d.ts +3 -3
  16. package/dist/root/internal/listRequests/uploadFile.d.ts +3 -3
  17. package/dist/root/internal/odata.d.ts +1 -1
  18. package/dist/root/internal/sharepointUrl.d.ts +2 -0
  19. package/dist/root/internal/userRequests.d.ts +3 -3
  20. package/dist/sharepoint_requests.cjs.development.js +572 -229
  21. package/dist/sharepoint_requests.cjs.development.js.map +1 -1
  22. package/dist/sharepoint_requests.cjs.production.min.js +1 -1
  23. package/dist/sharepoint_requests.cjs.production.min.js.map +1 -1
  24. package/dist/sharepoint_requests.esm.js +572 -229
  25. package/dist/sharepoint_requests.esm.js.map +1 -1
  26. package/dist/types.d.ts +30 -11
  27. package/package.json +1 -1
  28. package/src/index.ts +3 -5
  29. package/src/root/index.ts +263 -144
  30. package/src/root/internal/context.ts +2 -2
  31. package/src/root/internal/digest.ts +4 -3
  32. package/src/root/internal/emailRequests.ts +4 -7
  33. package/src/root/internal/listRequests/createAttachment.ts +14 -3
  34. package/src/root/internal/listRequests/createListItem.ts +24 -13
  35. package/src/root/internal/listRequests/deleteFile.ts +4 -3
  36. package/src/root/internal/listRequests/deleteItem.ts +17 -3
  37. package/src/root/internal/listRequests/getFiles.ts +4 -10
  38. package/src/root/internal/listRequests/getListItems.ts +16 -9
  39. package/src/root/internal/listRequests/getOnly.ts +10 -7
  40. package/src/root/internal/listRequests/normalizePayload.ts +165 -0
  41. package/src/root/internal/listRequests/updateListItem.ts +31 -13
  42. package/src/root/internal/listRequests/uploadFile.ts +5 -8
  43. package/src/root/internal/listRequests/utils.ts +1 -1
  44. package/src/root/internal/odata.ts +2 -4
  45. package/src/root/internal/sharepointUrl.ts +7 -0
  46. package/src/root/internal/userRequests.ts +43 -11
  47. package/src/types.ts +138 -105
package/src/root/index.ts CHANGED
@@ -2,41 +2,58 @@ import { AxiosInstance, CreateAxiosDefaults as AxiosConfig } from 'axios';
2
2
  import instance from './instance';
3
3
  import {
4
4
  CreateFileProps,
5
- DeleteFileProps,
6
5
  CurrentUser,
6
+ DeleteFileProps,
7
7
  EmailProps,
8
8
  Field_Options,
9
9
  Folder_Options,
10
- UploadFileProps,
11
- IListName,
12
10
  ItemID,
13
11
  ListData,
14
12
  Options,
15
13
  PersonField,
16
14
  SHAREPOINT_VER,
15
+ UploadFileProps,
17
16
  UserPermision,
18
17
  UserProfile,
19
18
  } from '../types';
20
19
  import { RequestContext } from './internal/context';
20
+ import { sendEmail as sendEmailRequest } from './internal/emailRequests';
21
+ import { fetchDigest } from './internal/digest';
21
22
  import {
22
23
  createAttachment,
23
24
  createListItem,
24
- deleteItem,
25
25
  deleteFile as deleteLibraryFile,
26
+ deleteItem,
26
27
  getFiles,
27
28
  getListItems,
28
29
  getOnly,
29
30
  updateListItem,
30
31
  uploadFile,
31
32
  } from './internal/listRequests';
32
- import { sendEmail as sendEmailRequest } from './internal/emailRequests';
33
+ import {
34
+ buildListByTitleEndpoint,
35
+ escapeODataStringLiteral,
36
+ } from './internal/sharepointUrl';
33
37
  import {
34
38
  currentUserPermissions as resolveCurrentUserPermissions,
35
39
  currentUserProperties as loadCurrentUserProperties,
36
40
  getSiteUser as fetchSiteUser,
37
41
  typeAhead,
38
42
  } from './internal/userRequests';
39
- import { fetchDigest } from './internal/digest';
43
+
44
+ type ListKey<T extends { LISTS: Record<string, unknown> }> = Extract<
45
+ keyof T['LISTS'],
46
+ string
47
+ >;
48
+
49
+ type EmailApi = ((
50
+ from: string,
51
+ to: string[] | string,
52
+ subject: string | undefined,
53
+ body: string
54
+ ) => ReturnType<typeof sendEmailRequest>) & {
55
+ send: (props: EmailProps) => ReturnType<typeof sendEmailRequest>;
56
+ };
40
57
 
41
58
  class HTTPSharePointRequests<
42
59
  T extends {
@@ -44,52 +61,70 @@ class HTTPSharePointRequests<
44
61
  FOLDERS?: Record<string, unknown>;
45
62
  }
46
63
  > {
47
- private baseURL: string;
48
- private endpoint: string;
49
- private listName: string;
50
- private instance: AxiosInstance;
51
- private sharepointVersion: SHAREPOINT_VER;
52
- private listItemEntityTypeFullName?: string;
64
+ private readonly baseURL: string;
65
+ private readonly instance: AxiosInstance;
66
+ private readonly listItemEntityTypeCache: Map<string, string>;
67
+ public readonly email: EmailApi;
53
68
 
54
69
  constructor(baseURL: string, config?: AxiosConfig) {
55
70
  this.baseURL = baseURL.endsWith('/') ? baseURL : `${baseURL}/`;
56
- this.endpoint = '/api';
57
- this.listName = '';
58
71
  this.instance = instance(this.baseURL, config);
59
- this.sharepointVersion = '2013';
60
- this.listItemEntityTypeFullName = undefined;
72
+ this.listItemEntityTypeCache = new Map<string, string>();
73
+ this.email = Object.assign(
74
+ (
75
+ from: string,
76
+ to: string[] | string,
77
+ subject: string | undefined,
78
+ body: string
79
+ ) => {
80
+ return this.sendEmail({
81
+ From: from,
82
+ To: to,
83
+ Subject: subject,
84
+ Body: body,
85
+ });
86
+ },
87
+ {
88
+ send: (props: EmailProps) => this.sendEmail(props),
89
+ }
90
+ );
61
91
  }
62
92
 
63
- private getContext<V extends SHAREPOINT_VER = '2013'>(): RequestContext<V> {
93
+ private createContext<V extends SHAREPOINT_VER = '2013'>({
94
+ endpoint,
95
+ listName,
96
+ sharepointVersion,
97
+ listItemEntityTypeFullName,
98
+ resolveListItemEntityType,
99
+ }: {
100
+ endpoint: string;
101
+ listName: string;
102
+ sharepointVersion: V;
103
+ listItemEntityTypeFullName?: string;
104
+ resolveListItemEntityType?: () => Promise<string>;
105
+ }): RequestContext<V> {
64
106
  return {
65
107
  instance: this.instance,
66
108
  baseURL: this.baseURL,
67
- endpoint: this.endpoint,
68
- listName: this.listName,
69
- sharepointVersion: this.sharepointVersion as V,
70
- listItemEntityTypeFullName: this.listItemEntityTypeFullName,
71
- resolveListItemEntityType: this.resolveListItemEntityTypeFullName.bind(
72
- this
73
- ),
109
+ endpoint,
110
+ listName,
111
+ sharepointVersion,
112
+ listItemEntityTypeFullName,
113
+ resolveListItemEntityType,
74
114
  };
75
115
  }
76
116
 
77
- private async resolveListItemEntityTypeFullName(): Promise<string> {
78
- if (this.listItemEntityTypeFullName) {
79
- return this.listItemEntityTypeFullName;
80
- }
81
-
82
- if (!this.listName) {
83
- throw new Error('List name is not set.');
84
- }
85
-
86
- if (this.sharepointVersion !== '2013') {
87
- this.listItemEntityTypeFullName = `SP.Data.${this.listName}ListItem`;
88
- return this.listItemEntityTypeFullName;
117
+ private async resolveListItemEntityTypeFullName(
118
+ listName: string
119
+ ): Promise<string> {
120
+ const cacheKey = listName;
121
+ const cachedType = this.listItemEntityTypeCache.get(cacheKey);
122
+ if (cachedType) {
123
+ return cachedType;
89
124
  }
90
125
 
91
126
  const response = await this.instance.get(
92
- `_api/web/lists/GetByTitle('${this.listName}')?$select=ListItemEntityTypeFullName`
127
+ `${buildListByTitleEndpoint(listName)}?$select=ListItemEntityTypeFullName`
93
128
  );
94
129
 
95
130
  const entityType =
@@ -98,157 +133,241 @@ class HTTPSharePointRequests<
98
133
 
99
134
  if (!entityType || typeof entityType !== 'string') {
100
135
  throw new Error(
101
- `Unable to resolve ListItemEntityTypeFullName for list "${this.listName}".`
136
+ `Unable to resolve ListItemEntityTypeFullName for list "${listName}".`
102
137
  );
103
138
  }
104
139
 
105
- this.listItemEntityTypeFullName = entityType;
106
- return this.listItemEntityTypeFullName;
140
+ this.listItemEntityTypeCache.set(cacheKey, entityType);
141
+ return entityType;
107
142
  }
108
143
 
109
- /**
110
- * @deprecated Use `from` instead.
111
- */
112
- list(listName: IListName<T>) {
113
- this.listName = listName;
114
- this.endpoint = `_vti_bin/ListData.svc/${listName}`;
115
- this.sharepointVersion = '2010';
116
- this.listItemEntityTypeFullName = undefined;
117
- return this;
118
- }
119
-
120
- public readonly lists = {
121
- get: () => {
122
- this.endpoint = '_api/web/lists';
123
- this.sharepointVersion = '2013';
124
- return getListItems(this.getContext());
125
- },
126
- };
144
+ private buildListScope<
145
+ K extends ListKey<T>,
146
+ V extends SHAREPOINT_VER = '2013'
147
+ >(listName: K, version: V) {
148
+ const listNameValue = String(listName);
149
+ const endpoint =
150
+ version === '2013'
151
+ ? `${buildListByTitleEndpoint(listNameValue)}/items`
152
+ : `_vti_bin/ListData.svc/${encodeURIComponent(listNameValue)}`;
127
153
 
128
- public getDigest() {
129
- return fetchDigest(this.instance, this.baseURL);
130
- }
154
+ let scopedEntityType: string | undefined;
155
+ const resolveListItemEntityType = async () => {
156
+ if (scopedEntityType) {
157
+ return scopedEntityType;
158
+ }
131
159
 
132
- public from<
133
- K extends Extract<keyof T['LISTS'], string>,
134
- V extends SHAREPOINT_VER = '2013'
135
- >(listName: K, sharepoint_ver?: V) {
136
- const version = (sharepoint_ver ?? '2013') as V;
160
+ if (version !== '2013') {
161
+ scopedEntityType = `SP.Data.${listNameValue}ListItem`;
162
+ return scopedEntityType;
163
+ }
137
164
 
138
- this.listName = listName;
139
- this.sharepointVersion = version;
140
- this.listItemEntityTypeFullName = undefined;
141
- this.endpoint =
142
- version === '2013'
143
- ? `_api/web/lists/GetByTitle('${this.listName}')/items`
144
- : `_vti_bin/ListData.svc/${listName}`;
165
+ scopedEntityType = await this.resolveListItemEntityTypeFullName(
166
+ listNameValue
167
+ );
168
+ return scopedEntityType;
169
+ };
145
170
 
146
- const getContext = () => this.getContext<V>();
171
+ const getContext = () =>
172
+ this.createContext<V>({
173
+ endpoint,
174
+ listName: listNameValue,
175
+ sharepointVersion: version,
176
+ listItemEntityTypeFullName: scopedEntityType,
177
+ resolveListItemEntityType,
178
+ });
179
+
180
+ const removeItem = (id: ItemID) => deleteItem(getContext(), id);
181
+ const removeFile = (props: DeleteFileProps) =>
182
+ deleteLibraryFile(getContext(), props);
147
183
 
148
184
  return {
149
- create: (data: ListData) => createListItem(getContext(), data),
185
+ create: (data: ListData<T['LISTS'][K]>) =>
186
+ createListItem<T['LISTS'][K], V>(getContext(), data),
150
187
  get: (options?: Options<T['LISTS'][K], V>) =>
151
188
  getListItems<T['LISTS'][K], V>(getContext(), options),
152
- update: (id: ItemID, data: ListData, digest?: string) =>
153
- updateListItem(getContext(), id, data, digest),
154
- delete: (id: ItemID) => deleteItem(getContext(), id),
189
+ update: (id: ItemID, data: ListData<T['LISTS'][K]>, digest?: string) =>
190
+ updateListItem<T['LISTS'][K], V>(getContext(), id, data, digest),
191
+ delete: removeItem,
192
+ remove: removeItem,
155
193
  createFile: (props: CreateFileProps) =>
156
194
  createAttachment(getContext(), props),
157
195
  uploadFile: (props: UploadFileProps) => uploadFile(getContext(), props),
158
- deleteFile: (props: DeleteFileProps) =>
159
- deleteLibraryFile(getContext(), props),
196
+ deleteFile: removeFile,
197
+ removeFile,
160
198
  fields: (options?: Field_Options<T['LISTS'][K], V>) => {
161
- this.endpoint = !options?.fieldName
162
- ? `_api/web/lists/GetByTitle('${this.listName}')/fields`
163
- : `_api/web/lists/GetByTitle('${this.listName}')/fields/getbytitle('${options.fieldName}')`;
199
+ const fieldsEndpoint = !options?.fieldName
200
+ ? `${buildListByTitleEndpoint(listNameValue)}/fields`
201
+ : `${buildListByTitleEndpoint(
202
+ listNameValue
203
+ )}/fields/getbytitle('${escapeODataStringLiteral(
204
+ String(options.fieldName)
205
+ )}')`;
164
206
  return {
165
207
  get: () =>
166
- getOnly<T['LISTS'][K], V>(this.instance, this.endpoint, options),
208
+ getOnly<T['LISTS'][K], V>(this.instance, fieldsEndpoint, options),
167
209
  };
168
210
  },
211
+ items: {
212
+ create: (data: ListData<T['LISTS'][K]>) =>
213
+ createListItem<T['LISTS'][K], V>(getContext(), data),
214
+ get: (options?: Options<T['LISTS'][K], V>) =>
215
+ getListItems<T['LISTS'][K], V>(getContext(), options),
216
+ update: (id: ItemID, data: ListData<T['LISTS'][K]>, digest?: string) =>
217
+ updateListItem<T['LISTS'][K], V>(getContext(), id, data, digest),
218
+ delete: removeItem,
219
+ remove: removeItem,
220
+ },
221
+ files: {
222
+ attach: (props: CreateFileProps) =>
223
+ createAttachment(getContext(), props),
224
+ upload: (props: UploadFileProps) => uploadFile(getContext(), props),
225
+ delete: removeFile,
226
+ remove: removeFile,
227
+ },
169
228
  };
170
229
  }
171
230
 
172
- public folders = {
173
- get: () => {
174
- this.endpoint = '_api/web/folders';
175
- this.sharepointVersion = '2013';
176
- getListItems(this.getContext());
177
- },
178
- };
231
+ /**
232
+ * @deprecated Use `from(listName, '2010')` instead.
233
+ */
234
+ list<K extends ListKey<T>>(listName: K) {
235
+ return this.from<K, '2010'>(listName, '2010');
236
+ }
179
237
 
180
- public folder(folderName?: string | string[]) {
181
- let filePath = ['Shared Documents'];
238
+ public from<K extends ListKey<T>, V extends SHAREPOINT_VER = '2013'>(
239
+ listName: K,
240
+ sharepointVersion?: V
241
+ ) {
242
+ const version = (sharepointVersion ?? '2013') as V;
243
+ return this.buildListScope<K, V>(listName, version);
244
+ }
182
245
 
183
- if (folderName) {
184
- filePath = Array.isArray(folderName)
185
- ? [...filePath, ...folderName]
186
- : [...filePath, folderName];
187
- }
246
+ public field<K extends ListKey<T>, V extends SHAREPOINT_VER = '2013'>(
247
+ listName: K,
248
+ fieldName?: Extract<keyof T['LISTS'][K], string>,
249
+ sharepointVersion?: V
250
+ ) {
251
+ return this.from<K, V>(listName, sharepointVersion)
252
+ .fields({
253
+ fieldName,
254
+ } as Field_Options<T['LISTS'][K], V>)
255
+ .get();
256
+ }
257
+
258
+ public lists(options?: Options<Record<string, unknown>, '2013'>) {
259
+ const context = this.createContext<'2013'>({
260
+ endpoint: '_api/web/lists',
261
+ listName: '',
262
+ sharepointVersion: '2013',
263
+ });
264
+
265
+ return {
266
+ get: () => getListItems(context, options),
267
+ };
268
+ }
188
269
 
189
- this.endpoint = `_api/web/GetFolderByServerRelativeUrl('${filePath.join(
190
- '/'
191
- )}')`;
192
- this.sharepointVersion = '2013';
270
+ public getDigest() {
271
+ return fetchDigest(this.instance, this.baseURL);
272
+ }
193
273
 
194
- const getContext = () => this.getContext();
274
+ public folders(options?: Folder_Options<Record<string, unknown>>) {
275
+ const context = this.createContext<'2013'>({
276
+ endpoint: '_api/web/folders',
277
+ listName: '',
278
+ sharepointVersion: '2013',
279
+ });
195
280
 
196
281
  return {
197
- get: (options?: Folder_Options<any>) => {
198
- if (!options?.type) {
199
- this.endpoint += '/Files';
200
- } else {
201
- this.endpoint += `/${options.type}`;
202
- }
203
- return getFiles(getContext(), options);
282
+ get: () => getFiles<Record<string, unknown>, '2013'>(context, options),
283
+ };
284
+ }
285
+
286
+ public folder(folderName?: string | string[]) {
287
+ const defaultPath = ['Shared Documents'];
288
+ const segments = folderName
289
+ ? Array.isArray(folderName)
290
+ ? [...defaultPath, ...folderName]
291
+ : [...defaultPath, ...folderName.split('/').filter(Boolean)]
292
+ : defaultPath;
293
+
294
+ const serverRelativePath = escapeODataStringLiteral(segments.join('/'));
295
+ const baseEndpoint = `_api/web/GetFolderByServerRelativeUrl('${serverRelativePath}')`;
296
+
297
+ return {
298
+ get: (options?: Folder_Options<Record<string, unknown>>) => {
299
+ const folderType = options?.type ?? 'Files';
300
+ const endpoint = `${baseEndpoint}/${folderType}`;
301
+ const context = this.createContext<'2013'>({
302
+ endpoint,
303
+ listName: '',
304
+ sharepointVersion: '2013',
305
+ });
306
+
307
+ return getFiles<Record<string, unknown>, '2013'>(context, {
308
+ ...options,
309
+ type: folderType,
310
+ });
204
311
  },
205
312
  };
206
313
  }
207
314
 
208
- public users(options?: Options<any>) {
209
- this.endpoint = '_api/web/SiteUserInfoList/items';
210
- this.sharepointVersion = '2013';
315
+ public users(options?: Options<Record<string, unknown>, '2013'>) {
316
+ const context = this.createContext<'2013'>({
317
+ endpoint: '_api/web/SiteUserInfoList/items',
318
+ listName: '',
319
+ sharepointVersion: '2013',
320
+ });
321
+
211
322
  return {
212
- get: () => getListItems(this.getContext(), options),
323
+ get: () => getListItems(context, options),
213
324
  };
214
325
  }
215
326
 
216
- public email = {
217
- send: (props: EmailProps) => {
218
- this.sharepointVersion = '2013';
219
- return sendEmailRequest(this.getContext(), props);
220
- },
221
- };
327
+ public sendEmail(props: EmailProps) {
328
+ const context = this.createContext<'2013'>({
329
+ endpoint: '_api/SP.Utilities.Utility.SendEmail',
330
+ listName: '',
331
+ sharepointVersion: '2013',
332
+ });
333
+ return sendEmailRequest(context, props);
334
+ }
222
335
 
223
- public user = {
336
+ public readonly user = {
224
337
  properties: () => ({
225
- get: (): Promise<UserProfile> => {
226
- this.sharepointVersion = '2013';
227
- return loadCurrentUserProperties(this.instance);
228
- },
338
+ get: (): Promise<UserProfile> => loadCurrentUserProperties(this.instance),
339
+ }),
340
+ searchSiteUser: (searchValue: string) =>
341
+ fetchSiteUser(this.instance, searchValue),
342
+ searchUser: (input: string, filter?: string): Promise<PersonField> =>
343
+ typeAhead(this.instance, input, filter),
344
+ current: () => ({
345
+ get: (): Promise<CurrentUser> =>
346
+ getOnly(this.instance, '_api/web/currentUser'),
229
347
  }),
230
- searchSiteUser: (searchValue: string) => {
231
- this.sharepointVersion = '2013';
232
- return fetchSiteUser(this.instance, searchValue);
233
- },
234
- searchUser: (input: string, filter?: string): Promise<PersonField> => {
235
- this.sharepointVersion = '2013';
236
- return typeAhead(this.instance, input, filter);
237
- },
238
- current: () => {
239
- this.endpoint = '_api/web/currentUser';
240
- this.sharepointVersion = '2013';
241
- return {
242
- get: (): Promise<CurrentUser> => getOnly(this.instance, this.endpoint),
243
- };
244
- },
245
348
  currentPermission: () => ({
246
- get: (): Promise<UserPermision> => {
247
- this.sharepointVersion = '2013';
248
- return resolveCurrentUserPermissions(this.getContext());
249
- },
349
+ get: (): Promise<UserPermision> =>
350
+ resolveCurrentUserPermissions(
351
+ this.createContext<'2013'>({
352
+ endpoint: '_api/web/effectiveBasePermissions',
353
+ listName: '',
354
+ sharepointVersion: '2013',
355
+ })
356
+ ),
250
357
  }),
251
358
  };
359
+
360
+ public getCurrentUser() {
361
+ return this.user.current().get();
362
+ }
363
+
364
+ public currentUserProperties() {
365
+ return this.user.properties().get();
366
+ }
367
+
368
+ public currentUserPermission() {
369
+ return this.user.currentPermission().get();
370
+ }
252
371
  }
253
372
 
254
373
  export default HTTPSharePointRequests;
@@ -1,5 +1,5 @@
1
- import type { AxiosInstance } from 'axios';
2
- import type { SHAREPOINT_VER } from '../../types';
1
+ import { AxiosInstance } from 'axios';
2
+ import { SHAREPOINT_VER } from '../../types';
3
3
 
4
4
  export interface RequestContext<V extends SHAREPOINT_VER = '2013'> {
5
5
  instance: AxiosInstance;
@@ -1,4 +1,4 @@
1
- import type { AxiosInstance } from 'axios';
1
+ import { AxiosInstance } from 'axios';
2
2
 
3
3
  export async function fetchDigest(
4
4
  instance: AxiosInstance,
@@ -16,7 +16,8 @@ export async function fetchDigest(
16
16
 
17
17
  return formDigestValue;
18
18
  } catch (error) {
19
- console.error('Failed to retrieve the Form Digest Value:', error);
20
- throw new Error(`Error retrieving Form Digest Value`);
19
+ const cause =
20
+ error instanceof Error ? error.message : 'Unknown contextinfo error';
21
+ throw new Error(`Error retrieving Form Digest Value: ${cause}`);
21
22
  }
22
23
  }
@@ -1,7 +1,7 @@
1
- import type { AxiosResponse } from 'axios';
2
- import type { EmailProps } from '../../types';
1
+ import { AxiosResponse } from 'axios';
2
+ import { EmailProps } from '../../types';
3
3
  import { fetchDigest } from './digest';
4
- import type { RequestContext } from './context';
4
+ import { RequestContext } from './context';
5
5
 
6
6
  type SendEmailResponse = AxiosResponse<{
7
7
  d: {
@@ -19,10 +19,7 @@ export async function sendEmail(
19
19
  url: httpRequest,
20
20
  method: 'POST',
21
21
  headers: {
22
- 'X-RequestDigest': await fetchDigest(
23
- context.instance,
24
- context.baseURL
25
- ),
22
+ 'X-RequestDigest': await fetchDigest(context.instance, context.baseURL),
26
23
  'X-Http-Method': 'POST',
27
24
  'Content-Type': 'application/json;odata=verbose',
28
25
  },
@@ -1,12 +1,23 @@
1
- import type { CreateFileProps, SHAREPOINT_VER } from '../../../types';
1
+ import { CreateFileProps, SHAREPOINT_VER } from '../../../types';
2
2
  import { fetchDigest } from '../digest';
3
- import type { RequestContext } from '../context';
3
+ import { RequestContext } from '../context';
4
+ import { buildListByTitleEndpoint } from '../sharepointUrl';
5
+ import { encodeFileName } from './utils';
4
6
 
5
7
  export async function createAttachment<V extends SHAREPOINT_VER = '2013'>(
6
8
  context: RequestContext<V>,
7
9
  { file, itemId }: CreateFileProps
8
10
  ) {
9
- const requestUrl = `_api/lists/getbytitle('${context.listName}')/items(${itemId})/AttachmentFiles/add(FileName='${file.name}')`;
11
+ if (context.sharepointVersion !== '2013') {
12
+ throw new Error(
13
+ 'createAttachment is only supported for SharePoint 2013 REST endpoints.'
14
+ );
15
+ }
16
+
17
+ const encodedFileName = encodeFileName(file.name);
18
+ const requestUrl = `${buildListByTitleEndpoint(
19
+ context.listName
20
+ )}/items(${itemId})/AttachmentFiles/add(FileName='${encodedFileName}')`;
10
21
  const response = await context.instance.post(requestUrl, file, {
11
22
  headers: {
12
23
  'X-RequestDigest': await fetchDigest(context.instance, context.baseURL),
@@ -1,6 +1,7 @@
1
- import type { ListData, SHAREPOINT_VER } from '../../../types';
1
+ import { ListData, SHAREPOINT_VER } from '../../../types';
2
2
  import { fetchDigest } from '../digest';
3
- import type { RequestContext } from '../context';
3
+ import { RequestContext } from '../context';
4
+ import { normalizeListPayload } from './normalizePayload';
4
5
 
5
6
  async function resolveMetadataType<V extends SHAREPOINT_VER>(
6
7
  context: RequestContext<V>
@@ -16,17 +17,27 @@ async function resolveMetadataType<V extends SHAREPOINT_VER>(
16
17
  return `SP.Data.${context.listName}ListItem`;
17
18
  }
18
19
 
19
- export async function createListItem<V extends SHAREPOINT_VER = '2013'>(
20
- context: RequestContext<V>,
21
- listData: ListData
22
- ) {
23
- const metadataType = await resolveMetadataType(context);
24
- const payload = {
25
- ...listData,
26
- __metadata: {
27
- type: metadataType,
28
- },
29
- };
20
+ export async function createListItem<
21
+ D = Record<string, unknown>,
22
+ V extends SHAREPOINT_VER = '2013'
23
+ >(context: RequestContext<V>, listData: ListData<D>) {
24
+ const normalizedData = normalizeListPayload(listData);
25
+ let payload: Record<string, unknown> = normalizedData;
26
+
27
+ if (context.sharepointVersion === '2013') {
28
+ const metadataType = await resolveMetadataType(context);
29
+ const userMetadata = (listData as { __metadata?: Record<string, unknown> })
30
+ .__metadata;
31
+ payload = {
32
+ ...normalizedData,
33
+ __metadata: {
34
+ ...(userMetadata && typeof userMetadata === 'object'
35
+ ? userMetadata
36
+ : {}),
37
+ type: metadataType,
38
+ },
39
+ };
40
+ }
30
41
 
31
42
  const response = await context.instance({
32
43
  url: context.endpoint,
@@ -1,7 +1,8 @@
1
- import type { SHAREPOINT_VER, DeleteFileProps } from '../../../types';
1
+ import { SHAREPOINT_VER, DeleteFileProps } from '../../../types';
2
2
  import { fetchDigest } from '../digest';
3
- import type { RequestContext } from '../context';
3
+ import { RequestContext } from '../context';
4
4
  import { buildLibraryPath, encodeFileName } from './utils';
5
+ import { buildListByTitleEndpoint } from '../sharepointUrl';
5
6
 
6
7
  const SUPPORTED_VERSION: SHAREPOINT_VER = '2013';
7
8
 
@@ -16,7 +17,7 @@ export async function deleteFile<V extends SHAREPOINT_VER = '2013'>(
16
17
  }
17
18
 
18
19
  const encodedFileName = encodeFileName(fileName);
19
- let requestUrl = `_api/web/lists/getbytitle('${context.listName}')/RootFolder`;
20
+ let requestUrl = `${buildListByTitleEndpoint(context.listName)}/RootFolder`;
20
21
  requestUrl += buildLibraryPath(folderPath);
21
22
  requestUrl += `/Files('${encodedFileName}')`;
22
23