@mcpher/gas-fakes 1.1.7 → 1.2.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.
package/README.md CHANGED
@@ -12,7 +12,7 @@ You can get the package from npm:
12
12
  npm i @mcpher/gas-fakes
13
13
  ```
14
14
 
15
- For a complete guide on how to set up your local environment for authentication and development, please see the consolidated guide: [Getting Started with `gas-fakes`](gas-fakes/GETTING_STARTED.md)
15
+ For a complete guide on how to set up your local environment for authentication and development, please see the consolidated guide: [Getting Started with `gas-fakes`](GETTING_STARTED.md)
16
16
 
17
17
  Collaborators should fork the repo and use the local versions of these files - see collaborators info.
18
18
 
package/package.json CHANGED
@@ -4,6 +4,7 @@
4
4
  },
5
5
  "dependencies": {
6
6
  "@mcpher/fake-gasenum": "^1.0.2",
7
+ "@mcpher/gas-flex-cache": "^1.1.0",
7
8
  "@sindresorhus/is": "^7.0.1",
8
9
  "archiver": "^7.0.1",
9
10
  "exceljs": "^4.4.0",
@@ -25,47 +26,47 @@
25
26
  },
26
27
  "type": "module",
27
28
  "scripts": {
28
- "test": "node ./test/test.js",
29
- "testdrive": "node ./test/testdrive.js execute",
30
- "testsheetsdatavalidations": "node ./test/testsheetsdatavalidations.js execute",
31
- "testsheetspermissions": "node ./test/testsheetspermissions.js execute",
32
- "testsheetsvalues": "node ./test/testsheetsvalues.js execute",
33
- "testsheets": "node ./test/testsheets.js execute",
34
- "testfetch": "node ./test/testfetch.js execute",
35
- "testsession": "node ./test/testsession.js execute",
36
- "testutilities": "node ./test/testutilities.js execute",
37
- "teststores": "node ./test/teststores.js execute",
38
- "testscriptapp": "node ./test/testscriptapp.js execute",
39
- "testfiddler": "node ./test/testfiddler.js execute",
40
- "testenums": "node ./test/testenums.js execute",
41
- "testsheetssets": "node ./test/testsheetssets.js execute",
42
- "testsheetsvui": "node ./test/testsheetsvui.js execute",
43
- "testsheetsdeveloper": "node ./test/testsheetsdeveloper.js execute",
44
- "testsheetsexotics": "node ./test/testsheetsexotics.js execute",
45
- "testsheetsdata": "node ./test/testsheetsdata.js execute",
46
- "testdocsadv": "node ./test/testdocsadv.js execute",
47
- "testslidesadv": "node ./test/testslidesadv.js execute",
48
- "testform": "node ./test/testform.js execute",
49
- "testformsadv": "node ./test/testformsadv.js execute",
50
- "testdocs": "node ./test/testdocs.js execute",
51
- "testslides": "node ./test/testslides.js execute",
52
- "testdocsnext": "node ./test/testdocsnext.js execute",
53
- "testsheetstext": "node ./test/testsheetstext.js execute",
54
- "testsheetsrange": "node ./test/testsheetsrange.js execute",
55
- "testdocslistitems": "node ./test/testdocslistitems.js execute",
56
- "testdocsall": "node ./test/testdocsall.js",
57
- "testdocsheaders": "node ./test/testdocsheaders.js execute",
58
- "testdocsfooters": "node ./test/testdocsfooters.js execute",
59
- "testdocsfootnotes": "node ./test/testdocsfootnotes.js execute",
60
- "testdocsimages": "node ./test/testdocsimages.js execute",
61
- "testdocsstyles": "node ./test/testdocsstyles.js execute",
29
+ "test": "node --env-file=./.env ./test/test.js",
30
+ "testdrive": "node --env-file=./.env ./test/testdrive.js execute",
31
+ "testsheetsdatavalidations": "node --env-file=./.env ./test/testsheetsdatavalidations.js execute",
32
+ "testsheetspermissions": "node --env-file=./.env ./test/testsheetspermissions.js execute",
33
+ "testsheetsvalues": "node --env-file=./.env ./test/testsheetsvalues.js execute",
34
+ "testsheets": "node --env-file=./.env ./test/testsheets.js execute",
35
+ "testfetch": "node --env-file=./.env ./test/testfetch.js execute",
36
+ "testsession": "node --env-file=./.env ./test/testsession.js execute",
37
+ "testutilities": "node --env-file=./.env ./test/testutilities.js execute",
38
+ "teststores": "node --env-file=./.env ./test/teststores.js execute",
39
+ "testscriptapp": "node --env-file=./.env ./test/testscriptapp.js execute",
40
+ "testfiddler": "node --env-file=./.env ./test/testfiddler.js execute",
41
+ "testenums": "node --env-file=./.env ./test/testenums.js execute",
42
+ "testsheetssets": "node --env-file=./.env ./test/testsheetssets.js execute",
43
+ "testsheetsvui": "node --env-file=./.env ./test/testsheetsvui.js execute",
44
+ "testsheetsdeveloper": "node --env-file=./.env ./test/testsheetsdeveloper.js execute",
45
+ "testsheetsexotics": "node --env-file=./.env ./test/testsheetsexotics.js execute",
46
+ "testsheetsdata": "node --env-file=./.env ./test/testsheetsdata.js execute",
47
+ "testdocsadv": "node --env-file=./.env ./test/testdocsadv.js execute",
48
+ "testslidesadv": "node --env-file=./.env ./test/testslidesadv.js execute",
49
+ "testform": "node --env-file=./.env ./test/testform.js execute",
50
+ "testformsadv": "node --env-file=./.env ./test/testformsadv.js execute",
51
+ "testdocs": "node --env-file=./.env ./test/testdocs.js execute",
52
+ "testslides": "node --env-file=./.env ./test/testslides.js execute",
53
+ "testdocsnext": "node --env-file=./.env ./test/testdocsnext.js execute",
54
+ "testsheetstext": "node --env-file=./.env ./test/testsheetstext.js execute",
55
+ "testsheetsrange": "node --env-file=./.env ./test/testsheetsrange.js execute",
56
+ "testdocslistitems": "node --env-file=./.env ./test/testdocslistitems.js execute",
57
+ "testdocsall": "node --env-file=./.env ./test/testdocsall.js",
58
+ "testdocsheaders": "node --env-file=./.env ./test/testdocsheaders.js execute",
59
+ "testdocsfooters": "node --env-file=./.env ./test/testdocsfooters.js execute",
60
+ "testdocsfootnotes": "node --env-file=./.env ./test/testdocsfootnotes.js execute",
61
+ "testdocsimages": "node --env-file=./.env ./test/testdocsimages.js execute",
62
+ "testdocsstyles": "node --env-file=./.env ./test/testdocsstyles.js execute",
62
63
  "testsandbox": "node --trace-warnings ./test/testsandbox.js execute",
63
- "testgmail": "node ./test/testgmail.js execute",
64
- "testlogger": "node ./test/testlogger.js execute",
64
+ "testgmail": "node --env-file=./.env ./test/testgmail.js execute",
65
+ "testlogger": "node --env-file=./.env ./test/testlogger.js execute",
65
66
  "pub": "npm publish --access public"
66
67
  },
67
68
  "name": "@mcpher/gas-fakes",
68
- "version": "1.1.7",
69
+ "version": "1.2.0",
69
70
  "license": "MIT",
70
71
  "main": "main.js",
71
72
  "description": "A proof of concept implementation of Apps Script Environment on Node",
package/src/index.js CHANGED
@@ -5,7 +5,6 @@ import './services/logger/app.js'
5
5
  import './services/urlfetchapp/app.js'
6
6
  import './services/utilities/app.js'
7
7
  import './services/spreadsheetapp/app.js'
8
- import './services/stores/app.js'
9
8
  import './services/gmailapp/app.js'
10
9
  import './services/session/app.js'
11
10
  import './services/advdrive/app.js'
@@ -17,4 +16,5 @@ import './services/documentapp/app.js'
17
16
  import './services/advforms/app.js'
18
17
  import './services/formapp/app.js'
19
18
  import './services/slidesapp/app.js'
20
- ScriptApp.isFake
19
+ // should be last
20
+ import './services/stores/app.js'
@@ -8,6 +8,7 @@ import { Syncit } from '../../support/syncit.js'
8
8
  import { Auth } from '../../support/auth.js'
9
9
  import { Proxies } from '../../support/proxies.js'
10
10
  import { newFakeBehavior } from './behavior.js'
11
+ import { newCacheDropin } from '@mcpher/gas-flex-cache'
11
12
  /**
12
13
  * fake ScriptApp.getOAuthToken
13
14
  * @return {string} token
@@ -126,6 +127,7 @@ if (typeof globalThis[name] === typeof undefined) {
126
127
  FULL: 'FULL'
127
128
  },
128
129
  __behavior: newFakeBehavior(),
130
+ __newCacheDropin: newCacheDropin
129
131
  }
130
132
 
131
133
 
@@ -1,11 +1,46 @@
1
1
  import { Proxies } from '../../support/proxies.js'
2
2
  import { Syncit } from '../../support/syncit.js'
3
3
  import { Auth } from '../../support/auth.js'
4
+ import { Utils } from '../../support/utils.js'
5
+ import { storeModels } from './gasflex.js'
6
+ import { newCacheDropin } from '@mcpher/gas-flex-cache'
7
+ import { notYetImplemented } from '../../support/helpers.js'
8
+ import { file } from 'googleapis/build/src/apis/file/index.js'
9
+ const { is } = Utils
4
10
 
11
+ /**
12
+ * what these props mean
13
+ * store_type = currently upstash or file - it defines the back end and maps to env variable
14
+ * service_kind = cache or property
15
+ * store_domain = script, document, user
16
+ */
17
+ /**
18
+ * domains of store supported
19
+ * @enum {string}
20
+ */
21
+ const StoreDomain = Object.freeze({
22
+ SCRIPT: 'SCRIPT',
23
+ USER: 'USER',
24
+ DOCUMENT: 'DOCUMENT'
25
+ })
5
26
 
6
27
  /**
7
- * caching support uses node files storage
28
+ * kinds of store service supported
29
+ * @enum {string}
8
30
  */
31
+ const ServiceKind = Object.freeze({
32
+ PROPERTIES: 'PROPERTY',
33
+ CACHE: 'CACHE'
34
+ })
35
+
36
+
37
+ /**
38
+ * types of store supported
39
+ */
40
+ const StoreType = Object.freeze({
41
+ UPSTASH: 'UPSTASH',
42
+ FILE: 'FILE'
43
+ })
9
44
 
10
45
  /**
11
46
  * default expiration of cache in seconds
@@ -38,78 +73,92 @@ const normExpiry = (secs) => {
38
73
  */
39
74
  const fixun = (value) => typeof value === typeof undefined ? null : value
40
75
 
41
- /**
42
- * types of store supported
43
- * @enum {string}
44
- */
45
- const StoreType = Object.freeze({
46
- SCRIPT: 'SCRIPT',
47
- USER: 'USER',
48
- DOCUMENT: 'DOCUMENT'
49
- })
50
76
 
51
- /**
52
- * kinds of store service supported
53
- * @enum {string}
54
- */
55
- export const ServiceKind = Object.freeze({
56
- PROPERTIES: 'PROPERTIES',
57
- CACHE: 'CACHE'
58
- })
59
-
60
- /**
61
- * check store type is valid
62
- * @param {StoreType} type
63
- * @returns {StoreType}
64
- */
65
- const checkStoreType = (type) => {
66
- if (!Reflect.has(StoreType, type)) {
67
- throw new Error(`invalid store type ${type}`)
77
+ const validateProp = (prop, vob, name = '') => {
78
+ // this will return the actual value of the enum, not the property name
79
+ prop = prop.toUpperCase(prop)
80
+ if (!Reflect.has(vob, prop)) {
81
+ throw new Error(`invalid ${name} property ${prop}`)
68
82
  }
69
- return type
83
+ return vob[prop]
70
84
  }
71
85
 
72
86
 
73
- /**
74
- * check store kind is valid
75
- * @param {ServiceKind} kind
76
- * @returns {ServiceKind}
77
- */
78
- const checkServiceKind = (kind) => {
79
- if (!ServiceKind[kind]) {
80
- throw new Error(`invalid store kind ${kind}`)
87
+
88
+ // this checks to see if we want to override a service with gas-flex-cache
89
+ const whichCache = () => {
90
+ // this will return the actual value of the enum, not the property name
91
+ const type = validateProp (process.env.STORE_TYPE || "file", StoreType, 'store_type')
92
+ if (type === StoreType.UPSTASH) {
93
+ const url = process.env.UPSTASH_REDIS_REST_URL
94
+ const token = process.env.UPSTASH_REDIS_REST_TOKEN
95
+ if (!is.nonEmptyString(token)) throw new Error('UPSTASH_REDIS_REST_TOKEN not found or invalid in .env file')
96
+ if (!is.nonEmptyString(url)) throw new Error('UPSTASH_REDIS_REST_URL not found or invalid in .env file')
97
+ return {
98
+ url,
99
+ token,
100
+ type
101
+ }
102
+ }
103
+ return {
104
+ type
81
105
  }
82
- return kind
83
106
  }
107
+
84
108
  /**
85
109
  * create a new FakeService instance
86
110
  * @param {ServiceKind} kind of service
87
111
  * @returns {FakePropertiesService || FakeCacheService}
88
112
  */
89
113
  export const newFakeService = (kind) => {
90
- checkServiceKind(kind)
91
- return Proxies.guard(kind === ServiceKind.CACHE ? new FakeCacheService() : new FakePropertiesService())
114
+ kind = validateProp (kind, ServiceKind, 'service_kind')
115
+ const w = whichCache ()
116
+ console.log (`...${kind} store service is using store type ${w.type} as backend`)
117
+ return Proxies.guard(kind === ServiceKind.CACHE ? new FakeCacheService(w.type) : new FakePropertiesService(w.type))
92
118
  }
93
119
 
120
+ const selectCache = (domain, kind, defaultExpirationSeconds) => {
121
+ // actually we might be overriding the type of service
122
+ domain = validateProp (domain, StoreDomain, 'store_domain')
123
+ const which = whichCache()
124
+ if (which.type === "UPSTASH") {
125
+ const model = storeModels[domain]
126
+ if (!model) {
127
+ throw new Error(`invalid store type model for ${cacheType}`)
128
+ }
129
+ return newCacheDropin ({ creds: {
130
+ ...model,
131
+ ...which,
132
+ type: "upstash",
133
+ kind: kind.toLowerCase(),
134
+ defaultExpirationSeconds
135
+ }})
136
+ } else if (which.type === StoreType.FILE) {
137
+ const store = kind === ServiceKind.CACHE ? FakeCache : FakeProperties
138
+ return Proxies.guard(new store(domain))
139
+ } else {
140
+ throw new Error(`invalid store type ${which.type} found in .env file`)
141
+ }
142
+ }
94
143
 
95
144
  /**
96
145
  * @class FakePropertiesService
97
146
  */
98
147
  class FakePropertiesService {
99
- constructor() {
100
- this.kind = StoreType.PROPERTIES
101
-
148
+ constructor(type) {
149
+ this.kind = ServiceKind.PROPERTIES
150
+ this.type = type
102
151
  }
103
152
  getDocumentProperties() {
104
153
  // apps script needs a bound document to use this store
105
154
  if (!Auth.getDocumentId()) return null
106
- return Proxies.guard(new FakeProperties(StoreType.DOCUMENT))
155
+ return selectCache(StoreDomain.DOCUMENT, this.kind)
107
156
  }
108
157
  getUserProperties() {
109
- return Proxies.guard(new FakeProperties(StoreType.USER))
158
+ return selectCache(StoreDomain.USER, this.kind)
110
159
  }
111
160
  getScriptProperties() {
112
- return Proxies.guard(new FakeProperties(StoreType.SCRIPT))
161
+ return selectCache(StoreDomain.SCRIPT, this.kind)
113
162
  }
114
163
  }
115
164
 
@@ -117,19 +166,20 @@ class FakePropertiesService {
117
166
  * @class FakePropertiesService
118
167
  */
119
168
  class FakeCacheService {
120
- constructor() {
121
- this.kind = StoreType.CACHE
169
+ constructor(type) {
170
+ this.kind = ServiceKind.CACHE
171
+ this.type = type
122
172
  }
123
173
  getDocumentCache() {
124
174
  // apps script needs a bound document to use this store
125
175
  if (!Auth.getDocumentId()) return null
126
- return Proxies.guard(new FakeCache(StoreType.DOCUMENT))
176
+ return selectCache(StoreDomain.DOCUMENT, this.kind, DEFAULT_CACHE_EXPIRY)
127
177
  }
128
178
  getUserCache() {
129
- return Proxies.guard(new FakeCache(StoreType.USER))
179
+ return selectCache(StoreDomain.USER, this.kind,DEFAULT_CACHE_EXPIRY)
130
180
  }
131
181
  getScriptCache() {
132
- return Proxies.guard(new FakeCache(StoreType.SCRIPT))
182
+ return selectCache(StoreDomain.SCRIPT, this.kind, DEFAULT_CACHE_EXPIRY)
133
183
  }
134
184
  }
135
185
 
@@ -140,32 +190,32 @@ class FakeCacheService {
140
190
  class FakeStore {
141
191
  /**
142
192
  * @constructor
143
- * @param {StoreType} type
193
+ * @param {string} domain
144
194
  */
145
- constructor(type) {
146
- checkStoreType(type)
147
- this._type = type
195
+ constructor(domain) {
196
+ domain = validateProp(domain, StoreDomain, 'store_domain')
197
+ this._domain = domain
148
198
  }
149
199
 
150
200
  /**
151
- * @returns {StoreType}
201
+ * @returns {string}
152
202
  */
153
- get type() {
154
- return this._type
203
+ get domain() {
204
+ return this._domain
155
205
  }
156
206
 
157
207
  getFileName(kind) {
158
208
 
159
- const t = this.type.substring(0, 1).toLowerCase()
209
+ const d = this.domain.substring(0, 1).toLowerCase()
160
210
  const k = kind.substring(0, 1).toLowerCase()
161
211
  const scriptId = Auth.getScriptId()
162
212
  const documentId = Auth.getDocumentId()
163
213
 
164
- let fileName = `${k}${t}-${scriptId}`
165
- if (this.type === StoreType.USER) {
214
+ let fileName = `${k}${d}-${scriptId}`
215
+ if (this.domain === StoreDomain.USER) {
166
216
  fileName += `-${Auth.getHashedUserId()}`
167
217
  }
168
- else if (this.type === StoreType.DOCUMENT && documentId) {
218
+ else if (this.domain === StoreDomain.DOCUMENT && documentId) {
169
219
  fileName += `-${documentId}`
170
220
  }
171
221
 
@@ -182,20 +232,20 @@ class FakeStore {
182
232
  class FakeProperties extends FakeStore {
183
233
  /**
184
234
  * @constructor
185
- * @param {StoreType} type
235
+ * @param {string} domain
186
236
  */
187
- constructor(type) {
188
- super(type)
189
- this._storeArgs = {
237
+ constructor(domain) {
238
+ super(domain)
239
+ this._storeArgs = {
190
240
  inMemory: false,
191
- fileName: this.getFileName (ServiceKind.PROPERTIES),
192
- storeDir: Auth.getPropertiesPath()
241
+ fileName: this.getFileName(ServiceKind.PROPERTIES).toLowerCase(),
242
+ storeDir: Auth.getPropertiesPath()
193
243
  }
194
244
  }
195
245
 
196
246
 
197
247
  deleteAllProperties() {
198
-
248
+ return notYetImplemented('....try using gas-flex-cache store instead')
199
249
  }
200
250
  /**
201
251
  * delete the entry associated with the given key
@@ -207,10 +257,10 @@ class FakeProperties extends FakeStore {
207
257
  return this
208
258
  }
209
259
  getKeys() {
210
-
260
+ return notYetImplemented('....try using gas-flex-cache store instead')
211
261
  }
212
262
  getProperties() {
213
-
263
+ return notYetImplemented('....try using gas-flex-cache store instead')
214
264
  }
215
265
  /**
216
266
  * get a value from prop store
@@ -220,10 +270,10 @@ class FakeProperties extends FakeStore {
220
270
  getProperty(key) {
221
271
  return fixun(Syncit.fxStore(this._storeArgs, "get", key))
222
272
 
223
-
273
+
224
274
  }
225
275
  setProperties(properties, deleteAllOthers) {
226
-
276
+ return notYetImplemented('....try using gas-flex-cache store instead')
227
277
  }
228
278
  /**
229
279
  * set the entry associated with the given key
@@ -239,16 +289,16 @@ class FakeProperties extends FakeStore {
239
289
  }
240
290
 
241
291
  class FakeCache extends FakeStore {
242
- constructor(type) {
243
- super(type)
244
- this._storeArgs = {
292
+ constructor(domain) {
293
+ super(domain)
294
+ this._storeArgs = {
245
295
  inMemory: false,
246
- fileName: this.getFileName (ServiceKind.CACHE),
247
- storeDir: Auth.getCachePath()
296
+ fileName: this.getFileName(ServiceKind.CACHE).toLowerCase(),
297
+ storeDir: Auth.getCachePath()
248
298
  }
249
299
  }
250
300
  getAll(keys) {
251
-
301
+ return notYetImplemented('....try using gas-flex-cache store instead')
252
302
  }
253
303
 
254
304
  /**
@@ -264,7 +314,7 @@ class FakeCache extends FakeStore {
264
314
  }
265
315
 
266
316
  putAll(values, expirationInSeconds) {
267
-
317
+ return notYetImplemented('....try using gas-flex-cache store instead')
268
318
  }
269
319
 
270
320
  /**
@@ -278,12 +328,10 @@ class FakeCache extends FakeStore {
278
328
  }
279
329
 
280
330
  removeAll(keys) {
281
-
331
+ return notYetImplemented('....try using gas-flex-cache store instead')
282
332
  }
283
333
  get(key) {
284
334
  return fixun(Syncit.fxStore(this._storeArgs, "get", key))
285
335
  }
286
336
 
287
337
  }
288
-
289
-
@@ -0,0 +1,16 @@
1
+ import {Auth} from '../../support/auth.js'
2
+ // these are the default models to match live apps script
3
+ export const storeModels = {
4
+ SCRIPT: {
5
+ scriptId: ScriptApp.getScriptId()
6
+ },
7
+ USER: {
8
+ scriptId: ScriptApp.getScriptId(),
9
+ userId: Session.getEffectiveUser().getEmail()
10
+ },
11
+ DOCUMENT: {
12
+ scriptId: ScriptApp.getScriptId(),
13
+ documentId: Auth.getDocumentId
14
+ }
15
+ }
16
+