@mcpher/gas-fakes 1.2.32 → 2.0.1

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 (49) hide show
  1. package/.gcloudignore +24 -0
  2. package/README.md +5 -5
  3. package/appsscript.json +101 -0
  4. package/exgcp.sh +4 -4
  5. package/package.json +5 -6
  6. package/src/cli/app.js +9 -1
  7. package/src/cli/lib-manager.js +0 -1
  8. package/src/cli/setup.js +330 -228
  9. package/src/services/advcalendar/clapis.js +4 -2
  10. package/src/services/advdocs/docapis.js +4 -3
  11. package/src/services/advdrive/drapis.js +8 -5
  12. package/src/services/advforms/formsapis.js +4 -3
  13. package/src/services/advgmail/gmailapis.js +4 -3
  14. package/src/services/advsheets/shapis.js +5 -10
  15. package/src/services/advslides/slapis.js +4 -3
  16. package/src/services/driveapp/fakedriveapp.js +10 -2
  17. package/src/services/logger/fakelogger.js +6 -3
  18. package/src/services/scriptapp/app.js +16 -11
  19. package/src/services/scriptapp/behavior.js +132 -107
  20. package/src/services/session/fakesession.js +24 -9
  21. package/src/services/slidesapp/fakepresentation.js +19 -8
  22. package/src/services/slidesapp/fakeslide.js +45 -20
  23. package/src/services/spreadsheetapp/fakesheet.js +9 -7
  24. package/src/services/stores/fakestores.js +20 -18
  25. package/src/services/stores/gasflex.js +13 -14
  26. package/src/services/urlfetchapp/app.js +3 -3
  27. package/src/support/auth.js +227 -55
  28. package/src/support/slogger.js +42 -0
  29. package/src/support/sxauth.js +42 -39
  30. package/src/support/sxcalendar.js +9 -43
  31. package/src/support/sxdocs.js +6 -42
  32. package/src/support/sxdrive.js +19 -76
  33. package/src/support/sxfetch.js +36 -30
  34. package/src/support/sxforms.js +9 -40
  35. package/src/support/sxgmail.js +9 -37
  36. package/src/support/sxretry.js +79 -0
  37. package/src/support/sxsheets.js +6 -37
  38. package/src/support/sxslides.js +5 -36
  39. package/src/support/sxtoken.js +15 -0
  40. package/src/support/syncit.js +27 -10
  41. package/src/support/workersync/sxfunctions.js +2 -0
  42. package/src/support/workersync/synclogger.js +22 -5
  43. package/src/support/workersync/worker.js +8 -11
  44. package/README.RU.md +0 -373
  45. package/env.setup.template +0 -16
  46. package/run.js +0 -35
  47. package/setup.js +0 -689
  48. package/src/Code.js +0 -3
  49. package/src/appsscript.json +0 -5
@@ -17,10 +17,10 @@ export class FakePresentation {
17
17
  constructor(resource) {
18
18
  this.__id = resource.presentationId;
19
19
  }
20
- get __file () {
20
+ get __file() {
21
21
  return DriveApp.getFileById(this.__id);
22
22
  }
23
- get __resource () {
23
+ get __resource() {
24
24
  return Slides.Presentations.get(this.__id);
25
25
  }
26
26
  saveAndClose() {
@@ -73,14 +73,20 @@ export class FakePresentation {
73
73
  * @returns {FakeSlide} The new slide.
74
74
  */
75
75
  appendSlide(layout) {
76
+ const objectId = `slide_${Math.random().toString(36).substring(2, 11)}`;
76
77
  const requests = [{
77
78
  createSlide: {
79
+ objectId,
78
80
  slideLayoutReference: layout ? { predefinedLayout: layout } : { predefinedLayout: 'BLANK' }
79
81
  }
80
82
  }];
81
- const result = Slides.Presentations.batchUpdate(requests, this.getId());
82
- const newObjectId = result.replies[0].createSlide.objectId;
83
- return this.getSlideById(newObjectId);
83
+ try {
84
+ Slides.Presentations.batchUpdate(requests, this.getId());
85
+ } catch (err) {
86
+ // If it already exists, it means a previous attempt succeeded but timed out
87
+ if (!err?.message?.includes('already exists')) throw err;
88
+ }
89
+ return this.getSlideById(objectId);
84
90
  }
85
91
 
86
92
  /**
@@ -90,15 +96,20 @@ export class FakePresentation {
90
96
  * @returns {FakeSlide} The new slide.
91
97
  */
92
98
  insertSlide(index, layout) {
99
+ const objectId = `slide_${Math.random().toString(36).substring(2, 11)}`;
93
100
  const requests = [{
94
101
  createSlide: {
102
+ objectId,
95
103
  insertionIndex: index,
96
104
  slideLayoutReference: layout ? { predefinedLayout: layout } : { predefinedLayout: 'BLANK' }
97
105
  }
98
106
  }];
99
- const result = Slides.Presentations.batchUpdate(requests, this.getId());
100
- const newObjectId = result.replies[0].createSlide.objectId;
101
- return this.getSlideById(newObjectId);
107
+ try {
108
+ Slides.Presentations.batchUpdate(requests, this.getId());
109
+ } catch (err) {
110
+ if (!err?.message?.includes('already exists')) throw err;
111
+ }
112
+ return this.getSlideById(objectId);
102
113
  }
103
114
 
104
115
  toString() {
@@ -91,11 +91,16 @@ export class FakeSlide {
91
91
  */
92
92
  remove() {
93
93
  const presentationId = this.__presentation.getId();
94
- Slides.Presentations.batchUpdate([{
95
- deleteObject: {
96
- objectId: this.getObjectId()
97
- }
98
- }], presentationId);
94
+ try {
95
+ Slides.Presentations.batchUpdate([{
96
+ deleteObject: {
97
+ objectId: this.getObjectId()
98
+ }
99
+ }], presentationId);
100
+ } catch (err) {
101
+ // If not found, it's already deleted (perhaps on a previous timeouted attempt)
102
+ if (!err?.message?.includes('not found')) throw err;
103
+ }
99
104
  }
100
105
 
101
106
  /**
@@ -111,8 +116,10 @@ export class FakeSlide {
111
116
  // Default size and position if not provided
112
117
  // shapeType should be string e.g. TEXT_BOX
113
118
  const presentationId = this.__presentation.getId();
114
- const result = Slides.Presentations.batchUpdate([{
119
+ const objectId = `shape_${Math.random().toString(36).substring(2, 11)}`;
120
+ const requests = [{
115
121
  createShape: {
122
+ objectId,
116
123
  shapeType: shapeType,
117
124
  elementProperties: {
118
125
  pageObjectId: this.getObjectId(),
@@ -129,10 +136,13 @@ export class FakeSlide {
129
136
  }
130
137
  }
131
138
  }
132
- }], presentationId);
139
+ }];
133
140
 
134
- // The result contains the new object ID
135
- const newObjectId = result.replies[0].createShape.objectId;
141
+ try {
142
+ Slides.Presentations.batchUpdate(requests, presentationId);
143
+ } catch (err) {
144
+ if (!err?.message?.includes('already exists')) throw err;
145
+ }
136
146
 
137
147
  // We assume the shape is added to the slide and we can retrieve it
138
148
  // Wait, we need to return a FakeShape.
@@ -152,7 +162,7 @@ export class FakeSlide {
152
162
  // So subsequent access should be fresh.
153
163
 
154
164
  const elements = this.getPageElements();
155
- const newElement = elements.find(e => e.getObjectId() === newObjectId);
165
+ const newElement = elements.find(e => e.getObjectId() === objectId);
156
166
  if (!newElement) throw new Error('New shape not found');
157
167
  return newElement.asShape();
158
168
  }
@@ -168,8 +178,10 @@ export class FakeSlide {
168
178
  */
169
179
  insertLine(lineCategory, left = 0, top = 0, width = 100, height = 100) {
170
180
  const presentationId = this.__presentation.getId();
171
- const result = Slides.Presentations.batchUpdate([{
181
+ const objectId = `line_${Math.random().toString(36).substring(2, 11)}`;
182
+ const requests = [{
172
183
  createLine: {
184
+ objectId,
173
185
  lineCategory: lineCategory,
174
186
  elementProperties: {
175
187
  pageObjectId: this.getObjectId(),
@@ -186,28 +198,41 @@ export class FakeSlide {
186
198
  }
187
199
  }
188
200
  }
189
- }], presentationId);
201
+ }];
202
+
203
+ try {
204
+ Slides.Presentations.batchUpdate(requests, presentationId);
205
+ } catch (err) {
206
+ if (!err?.message?.includes('already exists')) throw err;
207
+ }
190
208
 
191
- const newObjectId = result.replies[0].createLine.objectId;
192
209
  const elements = this.getPageElements();
193
- const newElement = elements.find(e => e.getObjectId() === newObjectId);
210
+ const newElement = elements.find(e => e.getObjectId() === objectId);
194
211
  if (!newElement) throw new Error('New line not found');
195
212
  return newElement.asLine();
196
213
  }
197
214
 
198
215
  duplicate() {
199
216
  const presentationId = this.__presentation.getId();
200
- const result = Slides.Presentations.batchUpdate([{
217
+ const objectId = `slide_${Math.random().toString(36).substring(2, 11)}`;
218
+ const requests = [{
201
219
  duplicateObject: {
202
- objectId: this.getObjectId()
220
+ objectId: this.getObjectId(),
221
+ objectIds: {
222
+ [this.getObjectId()]: objectId
223
+ }
203
224
  }
204
- }], presentationId);
225
+ }];
226
+
227
+ try {
228
+ Slides.Presentations.batchUpdate(requests, presentationId);
229
+ } catch (err) {
230
+ if (!err?.message?.includes('already exists')) throw err;
231
+ }
205
232
 
206
- // The result contains the new object ID
207
- const newObjectId = result.replies[0].duplicateObject.objectId;
208
233
  // We need to get the updated presentation to find the new slide resource
209
234
  const updatedPresentation = Slides.Presentations.get(presentationId);
210
- const newSlideResource = updatedPresentation.slides.find(s => s.objectId === newObjectId);
235
+ const newSlideResource = updatedPresentation.slides.find(s => s.objectId === objectId);
211
236
  return newFakeSlide(newSlideResource, this.__presentation);
212
237
  }
213
238
 
@@ -16,7 +16,7 @@ import { newFakeProtection } from "./fakeprotection.js";
16
16
  import { newFakeOverGridImage } from "./fakeovergridimage.js";
17
17
 
18
18
  import { XMLParser } from "fast-xml-parser";
19
- import {slogger } from "../../support/slogger.js";
19
+ import { slogger } from "../../support/slogger.js";
20
20
  const { is, isEnum } = Utils;
21
21
 
22
22
  export const newFakeSheet = (properties, parent) => {
@@ -1075,12 +1075,14 @@ export class FakeSheet {
1075
1075
  const unzipped = Utilities.unzip(blob);
1076
1076
  const xmlObj = unzipped.reduce((o, b) => {
1077
1077
  const filename = b.getName();
1078
- const parser = new XMLParser({ ignoreAttributes: false });
1079
- try {
1080
- const p = parser.parse(b.getDataAsString());
1081
- o[filename] = p;
1082
- } catch (err) {
1083
- slogger.error(err)
1078
+ if (filename.endsWith('.xml') || filename.endsWith('.rels')) {
1079
+ const parser = new XMLParser({ ignoreAttributes: false });
1080
+ try {
1081
+ const p = parser.parse(b.getDataAsString());
1082
+ o[filename] = p;
1083
+ } catch (err) {
1084
+ slogger.error(`Error parsing ${filename}: ${err.message}`);
1085
+ }
1084
1086
  }
1085
1087
  return o;
1086
1088
  }, {});
@@ -6,7 +6,7 @@ import { storeModels } from './gasflex.js'
6
6
  import { newCacheDropin } from '@mcpher/gas-flex-cache'
7
7
  import { notYetImplemented } from '../../support/helpers.js'
8
8
  const { is } = Utils
9
- import {slogger } from "../../support/slogger.js";
9
+ import { slogger } from "../../support/slogger.js";
10
10
  /**
11
11
  * what these props mean
12
12
  * store_type = currently upstash or file - it defines the back end and maps to env variable
@@ -87,7 +87,7 @@ const validateProp = (prop, vob, name = '') => {
87
87
  // this checks to see if we want to override a service with gas-flex-cache
88
88
  const whichCache = () => {
89
89
  // this will return the actual value of the enum, not the property name
90
- const type = validateProp (process.env.STORE_TYPE || "file", StoreType, 'store_type')
90
+ const type = validateProp(process.env.STORE_TYPE || "file", StoreType, 'store_type')
91
91
  if (type === StoreType.UPSTASH) {
92
92
  const url = process.env.UPSTASH_REDIS_REST_URL
93
93
  const token = process.env.UPSTASH_REDIS_REST_TOKEN
@@ -136,7 +136,7 @@ class FakePropertiesService {
136
136
  * @returns {FakeProperties}
137
137
  */
138
138
  getScriptProperties() {
139
- return selectCache(StoreDomain.SCRIPT, this.kind)
139
+ return selectCache(StoreDomain.SCRIPT, this.kind)
140
140
  }
141
141
  }
142
142
 
@@ -145,7 +145,7 @@ class FakePropertiesService {
145
145
  */
146
146
  class FakeCacheService {
147
147
  constructor(type) {
148
- this.kind = ServiceKind.CACHE
148
+ this.kind = ServiceKind.CACHE
149
149
  this.type = type
150
150
  }
151
151
 
@@ -164,7 +164,7 @@ class FakeCacheService {
164
164
  * @returns {FakeCache}
165
165
  */
166
166
  getUserCache() {
167
- return selectCache(StoreDomain.USER, this.kind,DEFAULT_CACHE_EXPIRY)
167
+ return selectCache(StoreDomain.USER, this.kind, DEFAULT_CACHE_EXPIRY)
168
168
  }
169
169
 
170
170
  /**
@@ -172,7 +172,7 @@ class FakeCacheService {
172
172
  * @returns {FakeCache}
173
173
  */
174
174
  getScriptCache() {
175
- return selectCache(StoreDomain.SCRIPT, this.kind, DEFAULT_CACHE_EXPIRY)
175
+ return selectCache(StoreDomain.SCRIPT, this.kind, DEFAULT_CACHE_EXPIRY)
176
176
  }
177
177
  }
178
178
 
@@ -182,28 +182,30 @@ class FakeCacheService {
182
182
  * @returns {FakePropertiesService | FakeCacheService}
183
183
  */
184
184
  export const newFakeService = (kind) => {
185
- kind = validateProp (kind, ServiceKind, 'service_kind')
186
- const w = whichCache ()
187
- slogger.log (`...${kind} store service is using store type ${w.type} as backend`)
185
+ kind = validateProp(kind, ServiceKind, 'service_kind')
186
+ const w = whichCache()
187
+ slogger.log(`...${kind} store service is using store type ${w.type} as backend`)
188
188
  return Proxies.guard(kind === ServiceKind.CACHE ? new FakeCacheService(w.type) : new FakePropertiesService(w.type))
189
189
  }
190
190
 
191
191
  const selectCache = (domain, kind, defaultExpirationSeconds) => {
192
192
  // actually we might be overriding the type of service
193
- domain = validateProp (domain, StoreDomain, 'store_domain')
193
+ domain = validateProp(domain, StoreDomain, 'store_domain')
194
194
  const which = whichCache()
195
195
  if (which.type === "UPSTASH") {
196
196
  const model = storeModels[domain]
197
197
  if (!model) {
198
- throw new Error(`invalid store type model for ${cacheType}`)
198
+ throw new Error(`invalid store type model for ${cacheType}`)
199
199
  }
200
- return newCacheDropin ({ creds: {
201
- ...model,
202
- ...which,
203
- type: "upstash",
204
- kind: kind.toLowerCase(),
205
- defaultExpirationSeconds
206
- }})
200
+ return newCacheDropin({
201
+ creds: {
202
+ ...model,
203
+ ...which,
204
+ type: "upstash",
205
+ kind: kind.toLowerCase(),
206
+ defaultExpirationSeconds
207
+ }
208
+ })
207
209
  } else if (which.type === StoreType.FILE) {
208
210
  const store = kind === ServiceKind.CACHE ? FakeCache : FakeProperties
209
211
  return Proxies.guard(new store(domain))
@@ -1,18 +1,17 @@
1
- import {Auth} from '../../support/auth.js'
2
- import { getUserIdFromToken} from '@mcpher/gas-flex-cache'
1
+ import { Auth } from '../../support/auth.js'
2
+ import { getUserIdFromToken } from '@mcpher/gas-flex-cache'
3
3
 
4
4
  // these are the default models to match live apps script
5
5
  export const storeModels = {
6
- SCRIPT: {
7
- scriptId: ScriptApp.getScriptId()
8
- },
9
- USER: {
10
- scriptId: ScriptApp.getScriptId(),
11
- userId: getUserIdFromToken(ScriptApp.getOAuthToken())
12
- },
13
- DOCUMENT: {
14
- scriptId: ScriptApp.getScriptId(),
15
- documentId: Auth.getDocumentId()
16
- }
6
+ SCRIPT: {
7
+ scriptId: ScriptApp.getScriptId()
8
+ },
9
+ USER: {
10
+ scriptId: ScriptApp.getScriptId(),
11
+ userId: getUserIdFromToken(ScriptApp.getOAuthToken())
12
+ },
13
+ DOCUMENT: {
14
+ scriptId: ScriptApp.getScriptId(),
15
+ documentId: Auth.getDocumentId()
16
+ }
17
17
  }
18
-
@@ -86,8 +86,8 @@ const fetch = (url, options = {}) => {
86
86
  'rawBody'
87
87
  ]
88
88
 
89
- const response = Syncit.fxFetch(url, options, responseFields)
90
- return responsify(response)
89
+ const { data } = Syncit.fxFetch(url, options, responseFields)
90
+ return responsify(data)
91
91
  }
92
92
 
93
93
  // this has been syncified
@@ -102,7 +102,7 @@ const fetchAll = (requests) => {
102
102
  ]
103
103
 
104
104
  const responses = Syncit.fxFetchAll(requests, responseFields)
105
- return responses.map(r => responsify(r))
105
+ return responses.map(({ data }) => responsify(data))
106
106
  }
107
107
 
108
108