@mcpher/gas-fakes 2.3.10 → 2.3.13

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 (50) hide show
  1. package/README.md +14 -14
  2. package/gas-fakes.js +1 -0
  3. package/gf_agent/scripts/builder.js +26 -1
  4. package/package.json +1 -1
  5. package/src/cli/lib-manager.js +14 -4
  6. package/src/cli/setup.js +17 -4
  7. package/src/services/chartsapp/fakechartsapp.js +6 -1
  8. package/src/services/documentapp/elementhelpers.js +27 -0
  9. package/src/services/documentapp/fakeelement.js +8 -0
  10. package/src/services/documentapp/fakeparagraph.js +14 -0
  11. package/src/services/documentapp/faketext.js +174 -4
  12. package/src/services/driveapp/driveiterators.js +53 -8
  13. package/src/services/driveapp/fakedriveapp.js +49 -15
  14. package/src/services/driveapp/fakedrivefile.js +105 -0
  15. package/src/services/driveapp/fakedrivefolder.js +8 -0
  16. package/src/services/driveapp/fakedrivemeta.js +68 -16
  17. package/src/services/driveapp/fakefolderapp.js +19 -6
  18. package/src/services/enums/chartsenums.js +53 -20
  19. package/src/services/enums/driveenums.js +1 -0
  20. package/src/services/slidesapp/fakeautofit.js +23 -15
  21. package/src/services/slidesapp/fakecolorscheme.js +160 -0
  22. package/src/services/slidesapp/fakelayout.js +11 -1
  23. package/src/services/slidesapp/fakemaster.js +10 -0
  24. package/src/services/slidesapp/fakepresentation.js +27 -0
  25. package/src/services/slidesapp/fakeslide.js +9 -0
  26. package/src/services/slidesapp/faketextrange.js +6 -11
  27. package/src/services/spreadsheetapp/chartenummapping.js +15 -0
  28. package/src/services/spreadsheetapp/fakebooleancondition.js +119 -0
  29. package/src/services/spreadsheetapp/fakecellimage.js +42 -0
  30. package/src/services/spreadsheetapp/fakecellimagebuilder.js +59 -0
  31. package/src/services/spreadsheetapp/fakeconditionalformatrule.js +55 -0
  32. package/src/services/spreadsheetapp/fakeconditionalformatrulebuilder.js +330 -0
  33. package/src/services/spreadsheetapp/fakedevelopermetadata.js +32 -4
  34. package/src/services/spreadsheetapp/fakedevelopermetadatalocation.js +27 -5
  35. package/src/services/spreadsheetapp/fakeembeddedchartbuilder.js +155 -21
  36. package/src/services/spreadsheetapp/fakegradientcondition.js +71 -0
  37. package/src/services/spreadsheetapp/fakesheet.js +63 -0
  38. package/src/services/spreadsheetapp/fakesheetrange.js +12 -1
  39. package/src/services/spreadsheetapp/fakespreadsheet.js +30 -11
  40. package/src/services/spreadsheetapp/fakespreadsheetapp.js +21 -3
  41. package/src/services/urlfetchapp/app.js +33 -1
  42. package/src/support/fileiterators.js +3 -1
  43. package/src/support/filesharers.js +7 -3
  44. package/src/support/peeker.js +8 -2
  45. package/src/support/sheetutils.js +1 -0
  46. package/src/support/sxdrive.js +26 -15
  47. package/src/support/syncit.js +4 -6
  48. package/src/support/workersync/synchronizer.js +24 -4
  49. package/src/support/workersync/worker.js +13 -2
  50. package/summarize_advanced.js +0 -69
@@ -5,6 +5,8 @@ import { notYetImplemented, isFolder } from '../../support/helpers.js'
5
5
  import { Proxies } from '../../support/proxies.js'
6
6
  import { Utils } from '../../support/utils.js'
7
7
  import { Access, Permission } from '../enums/driveenums.js'
8
+ import { getFilesIterator } from './driveiterators.js'
9
+ import { Syncit } from '../../support/syncit.js'
8
10
  const { is } = Utils
9
11
 
10
12
 
@@ -19,6 +21,7 @@ export class FakeDriveApp {
19
21
  this.__rootFolder = null
20
22
  this.folderApp = newFakeFolderApp()
21
23
  this.__settleClass = (file) => isFolder(file) ? newFakeDriveFolder(file) : newFakeDriveFile(file)
24
+ this.__enforceSingleParent = true
22
25
  }
23
26
 
24
27
 
@@ -133,37 +136,68 @@ export class FakeDriveApp {
133
136
  return this.getRootFolder().createFolder(name)
134
137
  }
135
138
 
136
- //-- TODO ---
139
+ createShortcut(targetId, resourceKey) {
140
+ return this.getRootFolder().createShortcut(targetId, resourceKey)
141
+ }
137
142
 
138
- getFolderByIdAndResourceKey() {
139
- return notYetImplemented('getFolderByIdAndResourceKey')
143
+ createShortcutForTargetIdAndResourceKey(targetId, resourceKey) {
144
+ return this.getRootFolder().createShortcutForTargetIdAndResourceKey(targetId, resourceKey)
140
145
  }
141
- getFileByIdAndResourceKey() {
142
- return notYetImplemented('getFileByIdAndResourceKey')
146
+
147
+ getFolderByIdAndResourceKey(id, resourceKey) {
148
+ return this.getFolderById(id)
143
149
  }
144
150
 
145
- continueFileIterator() {
146
- return notYetImplemented('continueFileIterator')
151
+ getFileByIdAndResourceKey(id, resourceKey) {
152
+ return this.getFileById(id)
147
153
  }
148
- continueFolderIterator() {
149
- return notYetImplemented('continueFolderIterator')
154
+
155
+ continueFileIterator(token) {
156
+ return getFilesIterator({ token })
157
+ }
158
+
159
+ continueFolderIterator(token) {
160
+ return getFilesIterator({ token })
150
161
  }
162
+
151
163
  getTrashedFiles() {
152
- return notYetImplemented('getTrashedFiles')
164
+ return getFilesIterator({
165
+ folderTypes: false,
166
+ fileTypes: true,
167
+ qob: ['trashed = true']
168
+ })
153
169
  }
170
+
154
171
  getTrashedFolders() {
155
- return notYetImplemented('getTrashedFolders')
172
+ return getFilesIterator({
173
+ folderTypes: true,
174
+ fileTypes: false,
175
+ qob: ['trashed = true']
176
+ })
156
177
  }
157
178
 
158
179
  getStorageLimit() {
159
- return notYetImplemented('getStorageLimit')
180
+ const { data } = Syncit.fxDrive({
181
+ prop: 'about',
182
+ method: 'get',
183
+ params: { fields: 'storageQuota' }
184
+ })
185
+ return parseInt(data.storageQuota.limit, 10)
160
186
  }
187
+
161
188
  getStorageUsed() {
162
- return notYetImplemented('getStorageUsed')
189
+ const { data } = Syncit.fxDrive({
190
+ prop: 'about',
191
+ method: 'get',
192
+ params: { fields: 'storageQuota' }
193
+ })
194
+ return parseInt(data.storageQuota.usage, 10)
163
195
  }
164
- enforceSingleParent() {
165
- return notYetImplemented('enforceSingleParent')
196
+
197
+ enforceSingleParent(enabled) {
198
+ this.__enforceSingleParent = enabled
166
199
  }
200
+
167
201
  get Access() {
168
202
  return Access
169
203
  }
@@ -68,6 +68,96 @@ class FakeDriveFile extends FakeDriveMeta {
68
68
  return this.__getDecorated("webContentLink")
69
69
  }
70
70
 
71
+ /**
72
+ * get as a blob
73
+ * @param {string} contentType
74
+ * @returns {FakeBlob}
75
+ */
76
+ getAs(contentType) {
77
+ if (contentType === this.getMimeType()) {
78
+ return this.getBlob()
79
+ }
80
+ const result = Syncit.fxDriveExport({ id: this.getId(), mimeType: contentType })
81
+ if (result.error) {
82
+ // The error might be a string (from catch in sxStreamer) or an object
83
+ let isNotExportable = false;
84
+ let message = result.error;
85
+ if (typeof result.error === 'string' && result.error.startsWith('{')) {
86
+ try {
87
+ const parsed = JSON.parse(result.error);
88
+ isNotExportable = parsed.error?.errors?.[0]?.reason === 'fileNotExportable' ||
89
+ parsed.error?.reason === 'fileNotExportable';
90
+ message = parsed.error?.message || parsed.message || result.error;
91
+ } catch (e) {
92
+ // ignore
93
+ }
94
+ } else if (typeof result.error === 'object') {
95
+ isNotExportable = result.error.error?.errors?.[0]?.reason === 'fileNotExportable' ||
96
+ result.error.errors?.[0]?.reason === 'fileNotExportable' ||
97
+ result.error.error?.reason === 'fileNotExportable' ||
98
+ result.error.reason === 'fileNotExportable';
99
+ message = result.error.error?.message || result.error.message || JSON.stringify(result.error);
100
+ }
101
+
102
+ // Live GAS automatically handles exporting plain text/images to PDF etc.
103
+ // The REST API doesn't support this directly. We workaround it by
104
+ // temporarily converting the file to a Google Doc, exporting that, and trashing it.
105
+ if (isNotExportable) {
106
+ let targetMimeType = 'application/vnd.google-apps.document';
107
+ const currentMime = this.getMimeType();
108
+ if (currentMime === 'text/csv' || currentMime === 'text/tab-separated-values' || currentMime === 'application/vnd.ms-excel' || currentMime === 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet') {
109
+ targetMimeType = 'application/vnd.google-apps.spreadsheet';
110
+ } else if (currentMime === 'application/vnd.ms-powerpoint' || currentMime === 'application/vnd.openxmlformats-officedocument.presentationml.presentation') {
111
+ targetMimeType = 'application/vnd.google-apps.presentation';
112
+ }
113
+
114
+ try {
115
+ // 1. Copy to temp Google Doc format
116
+ const copyResult = Syncit.fxDrive({
117
+ prop: 'files',
118
+ method: 'copy',
119
+ params: {
120
+ fileId: this.getId(),
121
+ resource: {
122
+ mimeType: targetMimeType,
123
+ name: `Temp_gasfakes_conversion_${this.getName()}`
124
+ }
125
+ }
126
+ });
127
+
128
+ if (!copyResult.data || !copyResult.data.id) {
129
+ throw new Error(`Failed to copy to intermediate format: ${JSON.stringify(copyResult.response)}`);
130
+ }
131
+ const tempFileId = copyResult.data.id;
132
+
133
+ // 2. Export the temp file
134
+ const tempExportResult = Syncit.fxDriveExport({ id: tempFileId, mimeType: contentType });
135
+
136
+ // 3. Delete the temp file
137
+ Syncit.fxDrive({
138
+ prop: 'files',
139
+ method: 'update',
140
+ params: {
141
+ fileId: tempFileId,
142
+ resource: { trashed: true }
143
+ }
144
+ });
145
+
146
+ if (tempExportResult.error) {
147
+ throw new Error(tempExportResult.error.error?.message || tempExportResult.error.message || JSON.stringify(tempExportResult.error));
148
+ }
149
+
150
+ return Utilities.newBlob(tempExportResult.data, contentType, this.getName());
151
+ } catch (workaroundError) {
152
+ throw new Error(`getAs API returned: ${message}. Then, a temporary two-step conversion workaround failed: ${workaroundError.message}`);
153
+ }
154
+ }
155
+
156
+ throw new Error(message)
157
+ }
158
+ return Utilities.newBlob(result.data, contentType, this.getName())
159
+ }
160
+
71
161
  /**
72
162
  * set the content to something else
73
163
  * @param {string} content apparently this can only be a string and not a blob
@@ -151,6 +241,21 @@ class FakeDriveFile extends FakeDriveMeta {
151
241
  return newFakeDriveFile(data);
152
242
  }
153
243
 
244
+ getTargetId() {
245
+ this.__decorateWithFields("shortcutDetails")
246
+ return this.meta.shortcutDetails?.targetId || null
247
+ }
248
+
249
+ getTargetMimeType() {
250
+ this.__decorateWithFields("shortcutDetails")
251
+ return this.meta.shortcutDetails?.targetMimeType || null
252
+ }
253
+
254
+ getTargetResourceKey() {
255
+ this.__decorateWithFields("shortcutDetails")
256
+ return this.meta.shortcutDetails?.targetResourceKey || null
257
+ }
258
+
154
259
  }
155
260
 
156
261
  /**
@@ -58,6 +58,14 @@ export class FakeDriveFolder extends FakeDriveMeta {
58
58
  })
59
59
  }
60
60
 
61
+ createShortcut(targetId, resourceKey) {
62
+ return this.folderApp.createShortcut({ targetId, resourceKey, file: { parents: [this.getId()] } })
63
+ }
64
+
65
+ createShortcutForTargetIdAndResourceKey(targetId, resourceKey) {
66
+ return this.folderApp.createShortcutForTargetIdAndResourceKey({ targetId, resourceKey, file: { parents: [this.getId()] } })
67
+ }
68
+
61
69
  /**
62
70
  * get files in this folder
63
71
  * @return {FakeDriveFileIterator}
@@ -88,7 +88,7 @@ export class FakeDriveMeta {
88
88
  return this
89
89
  }
90
90
 
91
- const newMeta = this.__withPlatform(() => Drive.Files.get(this.getId(), { fields }, { allow404: false }))
91
+ console.log("CHECK", fields, Reflect.has(this.meta, fields), Object.keys(this.meta)); const newMeta = this.__withPlatform(() => Drive.Files.get(this.getId(), { fields }, { allow404: false }))
92
92
  // need to merge this with already known fields
93
93
  this.meta = { ...this.meta, ...newMeta }
94
94
  improveFileCache(this.getId(), this.meta, fields)
@@ -207,10 +207,10 @@ export class FakeDriveMeta {
207
207
 
208
208
  /**
209
209
  * get the file viewers
210
- * @returns {FakeUser} the file viewers
210
+ * @returns {FakeUser[]} the file viewers and commenters
211
211
  */
212
212
  getViewers() {
213
- return getSharers(this.getId(), 'reader')
213
+ return getSharers(this.getId(), ['reader', 'commenter'])
214
214
  }
215
215
 
216
216
  /**
@@ -399,7 +399,8 @@ export class FakeDriveMeta {
399
399
  }
400
400
 
401
401
  __managePermissions(emails, role, add = true) {
402
- const emailAddresses = is.array(emails) ? emails : [emails];
402
+ const arr = is.array(emails) ? emails : [emails];
403
+ const emailAddresses = arr.map(e => is.string(e) ? e : e?.getEmail?.() || e?.emailAddress || e);
403
404
 
404
405
  if (add) {
405
406
  // In Apps Script, this is not atomic. It just loops.
@@ -499,59 +500,110 @@ export class FakeDriveMeta {
499
500
  setSecurityUpdateEnabled() {
500
501
  return notYetImplemented('setSecurityUpdateEnabled')
501
502
  }
502
- getAccess() {
503
- return notYetImplemented('getAccess')
503
+ getAccess(user) {
504
+ const email = is.string(user) ? user : user?.getEmail?.() || user?.emailAddress || user;
505
+ const { permissions } = this.__withPlatform(() => Drive.Permissions.list(this.getId(), {
506
+ fields: 'permissions(role,emailAddress,type)'
507
+ }));
508
+ const p = permissions.find(p => p.emailAddress === email && p.type === 'user');
509
+ if (!p) return Permission.NONE;
510
+ if (p.role === 'owner') return Permission.OWNER;
511
+ if (p.role === 'writer') return Permission.EDIT;
512
+ if (p.role === 'commenter') return Permission.COMMENT;
513
+ if (p.role === 'reader') return Permission.VIEW;
514
+ return Permission.NONE;
504
515
  }
505
516
 
506
517
 
507
- revokePermissions() {
508
- return notYetImplemented('revokePermissions')
518
+ revokePermissions(user) {
519
+ const email = is.string(user) ? user : user?.getEmail?.() || user?.emailAddress || user;
520
+ const { permissions } = this.__withPlatform(() => Drive.Permissions.list(this.getId(), {
521
+ fields: 'permissions(id,emailAddress,type)'
522
+ }));
523
+ // Revoke all permissions for this user
524
+ const userPermissions = permissions.filter(p => p.emailAddress === email && p.type === 'user');
525
+ userPermissions.forEach(p => {
526
+ this.__withPlatform(() => Drive.Permissions.delete(this.getId(), p.id));
527
+ });
528
+ if (userPermissions.length > 0) {
529
+ improveFileCache(this.getId(), null);
530
+ }
531
+ return this;
509
532
  }
510
533
 
511
534
 
512
- setOwner() {
513
- return notYetImplemented('setOwner')
535
+ setOwner(user) {
536
+ const email = is.string(user) ? user : user?.getEmail?.() || user?.emailAddress || user;
537
+ this.__withPlatform(() => Drive.Permissions.create({
538
+ role: 'owner',
539
+ type: 'user',
540
+ emailAddress: email
541
+ }, this.getId(), { transferOwnership: true }));
542
+
543
+ improveFileCache(this.getId(), null);
544
+ return this;
514
545
  }
515
546
 
516
547
  addViewers() {
517
548
  const { nargs, matchThrow } = signatureArgs(arguments, "addViewers");
518
549
  const [emailAddresses] = arguments;
519
- if (nargs !== 1 || !is.array(emailAddresses) || !emailAddresses.every(is.string)) matchThrow();
550
+ if (nargs !== 1 || !is.array(emailAddresses)) matchThrow();
520
551
  return this.__managePermissions(emailAddresses, 'reader', true);
521
552
  }
522
553
 
523
554
  addViewer() {
524
555
  const { nargs, matchThrow } = signatureArgs(arguments, "addViewer");
525
556
  const [emailAddress] = arguments;
526
- if (nargs !== 1 || !is.string(emailAddress)) matchThrow();
557
+ if (nargs !== 1) matchThrow();
527
558
  return this.__managePermissions(emailAddress, 'reader', true);
528
559
  }
529
560
 
530
561
  removeEditor() {
531
562
  const { nargs, matchThrow } = signatureArgs(arguments, "removeEditor");
532
563
  const [emailAddress] = arguments;
533
- if (nargs !== 1 || !is.string(emailAddress)) matchThrow();
564
+ if (nargs !== 1) matchThrow();
534
565
  return this.__managePermissions(emailAddress, 'writer', false);
535
566
  }
536
567
 
537
568
  addEditor() {
538
569
  const { nargs, matchThrow } = signatureArgs(arguments, "addEditor");
539
570
  const [emailAddress] = arguments;
540
- if (nargs !== 1 || !is.string(emailAddress)) matchThrow();
571
+ if (nargs !== 1) matchThrow();
541
572
  return this.__managePermissions(emailAddress, 'writer', true);
542
573
  }
543
574
 
544
575
  removeViewer() {
545
576
  const { nargs, matchThrow } = signatureArgs(arguments, "removeViewer");
546
577
  const [emailAddress] = arguments;
547
- if (nargs !== 1 || !is.string(emailAddress)) matchThrow();
578
+ if (nargs !== 1) matchThrow();
548
579
  return this.__managePermissions(emailAddress, 'reader', false);
549
580
  }
550
581
 
551
582
  addEditors() {
552
583
  const { nargs, matchThrow } = signatureArgs(arguments, "addEditors");
553
584
  const [emailAddresses] = arguments;
554
- if (nargs !== 1 || !is.array(emailAddresses) || !emailAddresses.every(is.string)) matchThrow();
585
+ if (nargs !== 1 || !is.array(emailAddresses)) matchThrow();
555
586
  return this.__managePermissions(emailAddresses, 'writer', true);
556
587
  }
588
+
589
+ addCommenter() {
590
+ const { nargs, matchThrow } = signatureArgs(arguments, "addCommenter");
591
+ const [emailAddress] = arguments;
592
+ if (nargs !== 1) matchThrow();
593
+ return this.__managePermissions(emailAddress, 'commenter', true);
594
+ }
595
+
596
+ addCommenters() {
597
+ const { nargs, matchThrow } = signatureArgs(arguments, "addCommenters");
598
+ const [emailAddresses] = arguments;
599
+ if (nargs !== 1 || !is.array(emailAddresses)) matchThrow();
600
+ return this.__managePermissions(emailAddresses, 'commenter', true);
601
+ }
602
+
603
+ removeCommenter() {
604
+ const { nargs, matchThrow } = signatureArgs(arguments, "removeCommenter");
605
+ const [emailAddress] = arguments;
606
+ if (nargs !== 1) matchThrow();
607
+ return this.__managePermissions(emailAddress, 'commenter', false);
608
+ }
557
609
  }
@@ -61,8 +61,25 @@ class FakeFolderApp {
61
61
 
62
62
 
63
63
 
64
- createShortcutForTargetIdAndResourceKey() {
65
- return notYetImplemented('createShortcutForTargetIdAndResourceKey')
64
+ createShortcut({ targetId, resourceKey, file = {} }) {
65
+ const target = Drive.Files.get(targetId, { fields: 'name' })
66
+ return this.createFile({
67
+ nargs: 2,
68
+ name: target.name,
69
+ content: "",
70
+ file: {
71
+ mimeType: 'application/vnd.google-apps.shortcut',
72
+ shortcutDetails: {
73
+ targetId,
74
+ targetResourceKey: resourceKey
75
+ },
76
+ ...file
77
+ }
78
+ })
79
+ }
80
+
81
+ createShortcutForTargetIdAndResourceKey({ targetId, resourceKey, file = {} }) {
82
+ return this.createShortcut({ targetId, resourceKey, file })
66
83
  }
67
84
 
68
85
 
@@ -92,10 +109,6 @@ class FakeFolderApp {
92
109
  return notYetImplemented('removeFile')
93
110
  }
94
111
 
95
- createShortcut() {
96
- return notYetImplemented('createShortcut')
97
- }
98
-
99
112
  /**
100
113
  * the args are a bit flexible
101
114
  * we can have 1 arg which mucst be ablob
@@ -1,24 +1,57 @@
1
1
  import { newFakeGasenum } from "@mcpher/fake-gasenum";
2
2
 
3
3
  export const ChartType = newFakeGasenum([
4
- "AREA", // Enum Area chart.
5
- "BAR", // Enum Bar chart.
6
- "COLUMN", // Enum Column chart.
7
- "COMBO", // Enum Combo chart.
8
- "HISTOGRAM", // Enum Histogram chart.
9
- "LINE", // Enum Line chart.
10
- "PIE", // Enum Pie chart.
11
- "SCATTER", // Enum Scatter chart.
12
- "STEPPED_AREA", // Enum Stepped area chart.
13
- "WATERFALL", // Enum Waterfall chart.
14
- "SCORECARD", // Enum Scorecard chart.
15
- "RADAR", // Enum Radar chart.
16
- "GAUGE", // Enum Gauge chart.
17
- "ORG", // Enum Org chart.
18
- "TIMELINE", // Enum Timeline chart.
19
- "TREE_MAP", // Enum Tree map chart.
20
- "TABLE", // Enum Table chart.
21
- "CANDLESTICK", // Enum Candlestick chart.
22
- "GEOMAP", // Enum Geo map chart.
23
- "BUBBLE", // Enum Bubble chart.
4
+ "AREA", // Enum Area chart.
5
+ "BAR", // Enum Bar chart.
6
+ "COLUMN", // Enum Column chart.
7
+ "COMBO", // Enum Combo chart.
8
+ "HISTOGRAM", // Enum Histogram chart.
9
+ "LINE", // Enum Line chart.
10
+ "PIE", // Enum Pie chart.
11
+ "SCATTER", // Enum Scatter chart.
12
+ "STEPPED_AREA", // Enum Stepped area chart.
13
+ "WATERFALL", // Enum Waterfall chart.
14
+ "SCORECARD", // Enum Scorecard chart.
15
+ "RADAR", // Enum Radar chart.
16
+ "GAUGE", // Enum Gauge chart.
17
+ "ORG", // Enum Org chart.
18
+ "TIMELINE", // Enum Timeline chart.
19
+ "TREE_MAP", // Enum Tree map chart.
20
+ "TABLE", // Enum Table chart.
21
+ "CANDLESTICK", // Enum Candlestick chart.
22
+ "GEOMAP", // Enum Geo map chart.
23
+ "BUBBLE", // Enum Bubble chart.
24
+ ]);
25
+
26
+ export const ChartHiddenDimensionStrategy = newFakeGasenum([
27
+ "IGNORE_BOTH", // Enum Ignore both hidden rows and columns.
28
+ "IGNORE_COLUMNS", // Enum Ignore hidden columns.
29
+ "IGNORE_ROWS", // Enum Ignore hidden rows.
30
+ "SHOW_BOTH", // Enum Show all hidden rows and columns.
31
+ ]);
32
+
33
+ export const ChartMergeStrategy = newFakeGasenum([
34
+ "MERGE_COLUMNS", // Enum Merge columns across multiple ranges.
35
+ "MERGE_ROWS", // Enum Merge rows across multiple ranges.
36
+ ]);
37
+
38
+ export const CurveStyle = newFakeGasenum([
39
+ "NORMAL", // Enum Straight lines without curve.
40
+ "SMOOTH", // Enum Smooth curves.
41
+ ]);
42
+
43
+ export const PointStyle = newFakeGasenum([
44
+ "NONE", // Enum No point style.
45
+ "TINY", // Enum Tiny points.
46
+ "MEDIUM", // Enum Medium points.
47
+ "LARGE", // Enum Large points.
48
+ "HUGE", // Enum Huge points.
49
+ ]);
50
+
51
+ export const Position = newFakeGasenum([
52
+ "TOP", // Enum Position at the top.
53
+ "BOTTOM", // Enum Position at the bottom.
54
+ "RIGHT", // Enum Position on the right.
55
+ "LEFT", // Enum Position on the left.
56
+ "NONE", // Enum No position.
24
57
  ]);
@@ -11,6 +11,7 @@ export const Access = newFakeGasenum([
11
11
  export const Permission = newFakeGasenum([
12
12
  "COMMENT",
13
13
  "EDIT",
14
+ "FILE_ORGANIZER",
14
15
  "NONE",
15
16
  "ORGANIZER",
16
17
  "OWNER",
@@ -8,39 +8,47 @@ export const newFakeAutofit = (...args) => {
8
8
  export class FakeAutofit {
9
9
  constructor(shape) {
10
10
  this.__shape = shape;
11
- // Store autofit type on the shape resource or locally?
12
- // Ideally on the resource so it persists if we reload the shape.
13
- // But for now, let's look at the shape's property if meaningful or just a local property default.
14
- // The resource might have 'text.autoFit'.
15
11
  }
16
12
 
17
13
  get __resource() {
18
- // Ensure shape.text exists
19
- if (!this.__shape.__resource.shape.text) {
20
- this.__shape.__resource.shape.text = {};
14
+ const shapeResource = this.__shape.__resource;
15
+ if (shapeResource && shapeResource.shape && shapeResource.shape.shapeProperties && shapeResource.shape.shapeProperties.autofit) {
16
+ return shapeResource.shape.shapeProperties.autofit;
21
17
  }
22
- return this.__shape.__resource.shape.text;
18
+ return {};
23
19
  }
24
20
 
25
21
  getAutofitType() {
26
- const type = (this.__resource.autoFit && this.__resource.autoFit.autofitType) || 'NONE';
22
+ const type = this.__resource.autofitType || 'NONE';
27
23
  return AutofitType[type] || AutofitType.NONE;
28
24
  }
29
25
 
30
26
  disableAutofit() {
31
- if (!this.__resource.autoFit) {
32
- this.__resource.autoFit = {};
33
- }
34
- this.__resource.autoFit.autofitType = AutofitType.NONE.toString();
27
+ const objectId = this.__shape.getObjectId();
28
+ const presentationId = this.__shape.__presentation.getId();
29
+
30
+ const requests = [{
31
+ updateShapeProperties: {
32
+ objectId: objectId,
33
+ shapeProperties: {
34
+ autofit: {
35
+ autofitType: 'NONE'
36
+ }
37
+ },
38
+ fields: 'autofit.autofitType'
39
+ }
40
+ }];
41
+
42
+ Slides.Presentations.batchUpdate(requests, presentationId);
35
43
  return this;
36
44
  }
37
45
 
38
46
  getFontScale() {
39
- return (this.__resource.autoFit && this.__resource.autoFit.fontScale) !== undefined ? this.__resource.autoFit.fontScale : 1;
47
+ return this.__resource.fontScale !== undefined ? this.__resource.fontScale : 1;
40
48
  }
41
49
 
42
50
  getLineSpacingReduction() {
43
- return (this.__resource.autoFit && this.__resource.autoFit.lineSpacingReduction) !== undefined ? this.__resource.autoFit.lineSpacingReduction : 0;
51
+ return this.__resource.lineSpacingReduction !== undefined ? this.__resource.lineSpacingReduction : 0;
44
52
  }
45
53
 
46
54
  toString() {