@mcpher/gas-fakes 1.0.4 → 1.0.5
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 +90 -78
- package/package.json +3 -1
- package/src/index.js +1 -1
- package/src/services/advdrive/fakeadvdrive.js +30 -82
- package/src/services/advdrive/fakeadvdriveabout.js +15 -0
- package/src/services/advdrive/fakeadvdriveapps.js +47 -0
- package/src/services/advdrive/fakeadvdrivefiles.js +227 -0
- package/src/services/advdrive/fakeadvdrivepermissions.js +35 -0
- package/src/services/{drive → driveapp}/drapis.js +10 -0
- package/src/services/driveapp/fakedrive.js +9 -0
- package/src/services/driveapp/fakedriveapp.js +163 -0
- package/src/services/driveapp/fakedrivefile.js +134 -0
- package/src/services/driveapp/fakedrivefolder.js +104 -0
- package/src/services/driveapp/fakedriveiterators.js +198 -0
- package/src/services/driveapp/fakedrivemeta.js +257 -0
- package/src/services/driveapp/fakefolderapp.js +156 -0
- package/src/services/scriptapp/app.js +0 -1
- package/src/services/session/fakeuser.js +13 -1
- package/src/services/sheets/fakesheet.js +1 -1
- package/src/services/urlfetchapp/app.js +37 -7
- package/src/services/utilities/fakeblob.js +18 -2
- package/src/services/utilities/fakeutilities.js +1 -1
- package/src/support/auth.js +4 -1
- package/src/support/filecache.js +106 -0
- package/src/support/helpers.js +75 -0
- package/src/support/proxies.js +10 -1
- package/src/support/syncit.js +116 -13
- package/src/support/utils.js +79 -1
- package/src/services/drive/fakedrive.js +0 -570
- package/src/services/drive/fakedrivehelpers.js +0 -142
- package/src/support/constants.js +0 -22
- /package/src/services/{drive → driveapp}/app.js +0 -0
package/README.md
CHANGED
|
@@ -16,7 +16,11 @@ You can get the package from npm
|
|
|
16
16
|
npm i @mcpher/gas-fakes
|
|
17
17
|
```
|
|
18
18
|
|
|
19
|
-
The idea is that you can run GAS services (so far implemented) locally on Node, and it will use various Google Workspace APIS to emulate what would happen if you were to run the same thing in the GAS environment.
|
|
19
|
+
The idea is that you can run GAS services (so far implemented) locally on Node, and it will use various Google Workspace APIS to emulate what would happen if you were to run the same thing in the GAS environment. Other than logging in with application default credentials (see below) you don't have to do any intitialization and can start using the implemented Apps Script services directly from Node using the same syntax and getting equivalent responses.
|
|
20
|
+
|
|
21
|
+
Just as on Apps Script, everything is executed synchronously so you don't need to bother with handling Promises/async/await. Note that the intended audience is Apps Script developers who want to run the same code and access the same services in both Node and Apps Script.
|
|
22
|
+
|
|
23
|
+
If you don't plan on using Apps Script at all, the Node Workspace APIs (which I use in the background for all these services in any case) will be more efficient if operating in their normal asynchronous mode.
|
|
20
24
|
|
|
21
25
|
### Cloud project
|
|
22
26
|
|
|
@@ -24,12 +28,13 @@ You don't have access to the GAS maintained cloud project, so you'll need to cre
|
|
|
24
28
|
|
|
25
29
|
### Testing
|
|
26
30
|
|
|
27
|
-
I recommend you use the test project included in the repo to make sure all is set up correctly. It uses a Fake DriveApp service to excercise Auth etc. Just change the fixtures to values present in your own Drive, then `npm i && npm test`. Note that I use a [unit tester](https://ramblings.mcpher.com/apps-script-test-runner-library-ported-to-node/) that runs in both GAS and Node, so the exact same tests will run in both environments. There are some example tests in the repo. Each test has been proved on both Node and GAS.
|
|
31
|
+
I recommend you use the test project included in the repo to make sure all is set up correctly. It uses a Fake DriveApp service to excercise Auth etc. Just change the fixtures to values present in your own Drive, then `npm i && npm test`. Note that I use a [unit tester](https://ramblings.mcpher.com/apps-script-test-runner-library-ported-to-node/) that runs in both GAS and Node, so the exact same tests will run in both environments. There are some example tests in the repo. Each test has been proved on both Node and GAS. There's also a shell (togas.sh) which will use clasp to push the test code to Apps Script.
|
|
28
32
|
|
|
29
33
|
### Settings
|
|
30
34
|
|
|
31
35
|
gasfakes.json holds various location and behavior parameters to inform about your Node environment. It's not required on GAS as you can't change anything over there. If you don't have one, it'll create one for you and use some sensible defaults. Here's an example of one with the defaults. It should be in the same folder as your main script.
|
|
32
|
-
|
|
36
|
+
|
|
37
|
+
```
|
|
33
38
|
{
|
|
34
39
|
"manifest": "./appsscript.json",
|
|
35
40
|
"clasp": "./.clasp.json",
|
|
@@ -38,15 +43,16 @@ gasfakes.json holds various location and behavior parameters to inform about you
|
|
|
38
43
|
"properties": "/tmp/gas-fakes/properties",
|
|
39
44
|
"scriptId": "1bc79bd3-fe02-425f-9653-525e5ae0b678"
|
|
40
45
|
}
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
|
44
|
-
|
|
|
45
|
-
|
|
|
46
|
-
|
|
|
47
|
-
|
|
|
48
|
-
|
|
|
49
|
-
|
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
| property | type | default | description |
|
|
49
|
+
| ---------- | ------ | -------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
50
|
+
| manifest | string | ./appsscript.json | the manifest path and name relative to your main module |
|
|
51
|
+
| clasp | string | ./clasp.json | where to look for an optional clasp file |
|
|
52
|
+
| documentId | string | null | a bound document id. This will allow testing of container bound script. The documentId will become your activeDocument (for the appropriate service) |
|
|
53
|
+
| cache | string | /tmp/gas-fakes/cache | gas-fakes uses a local file to emulate apps script's CacheService. This is where it should put the files |
|
|
54
|
+
| properties | string | /tmp/gas-fakes/properties | gas-fakes uses a local file to emulate apps script's PropertiesService. This is where it should put the files. You may want to put it somewhere other than /tmp to avoid accidental deletion, but don't put it in a place that'll get commited to public git repo |
|
|
55
|
+
| scriptId | string | from clasp, or some random value | If you have a clasp file, it'll pick up the scriptId from there. If not you can enter your scriptId manually, or just leave it to create a fake one. It's use for the moment is to return something useful from ScriptApp.getScriptId() and to partition the cache and properties stores |
|
|
50
56
|
|
|
51
57
|
More on all this later.
|
|
52
58
|
|
|
@@ -80,7 +86,7 @@ Beyond that, implementation is just a lot of busy work. If you are interested, h
|
|
|
80
86
|
|
|
81
87
|
Although Apps Script supports async/await/promise syntax, it operates in blocking mode. I didn't really want to have to insist on async coding in code targeted at GAS, so I needed to find a way to emulate what the GAS environment probably does.
|
|
82
88
|
|
|
83
|
-
Since asynchonicity is fundamental to Node, there's no real simple way to convert async to sync. However, there is such a thing as a [child-process](https://nodejs.org/api/child_process.html#child-process) which you can start up to run things, and it features an [execSync](https://nodejs.org/api/child_process.html#child_processexecsynccommand-options)
|
|
89
|
+
Since asynchonicity is fundamental to Node, there's no real simple way to convert async to sync. However, there is such a thing as a [child-process](https://nodejs.org/api/child_process.html#child-process) which you can start up to run things, and it features an [execSync](https://nodejs.org/api/child_process.html#child_processexecsynccommand-options) method which delays the return from the child process until the promise queue is all settled. So the simplest solution is to run an async method in a child process, wait till it's done, and return the results synchronously. I found that [Sindre Sorhus](https://github.com/sindresorhus) uses this approach with [make-synchronous](https://github.com/sindresorhus/make-synchronous), so I'm using that.
|
|
84
90
|
|
|
85
91
|
Here's a simple example of how to get info on an access token made synchronous
|
|
86
92
|
|
|
@@ -91,17 +97,18 @@ Here's a simple example of how to get info on an access token made synchronous
|
|
|
91
97
|
* @returns {object} access token info
|
|
92
98
|
*/
|
|
93
99
|
const fxCheckToken = (accessToken) => {
|
|
94
|
-
|
|
95
100
|
// now turn all that into a synchronous function - it runs as a subprocess, so we need to start from scratch
|
|
96
|
-
const fx = makeSynchronous(async accessToken => {
|
|
97
|
-
const { default: got } = await import(
|
|
98
|
-
const tokenInfo = await got(
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
101
|
+
const fx = makeSynchronous(async (accessToken) => {
|
|
102
|
+
const { default: got } = await import("got");
|
|
103
|
+
const tokenInfo = await got(
|
|
104
|
+
`https://www.googleapis.com/oauth2/v3/tokeninfo?access_token=${accessToken}`
|
|
105
|
+
).json();
|
|
106
|
+
return tokenInfo;
|
|
107
|
+
});
|
|
108
|
+
|
|
109
|
+
const result = fx(accessToken);
|
|
110
|
+
return result;
|
|
111
|
+
};
|
|
105
112
|
```
|
|
106
113
|
|
|
107
114
|
### OAuth
|
|
@@ -157,19 +164,27 @@ I recommend you do this to make sure Auth it's all good before you start coding
|
|
|
157
164
|
|
|
158
165
|
### Global intialization
|
|
159
166
|
|
|
160
|
-
This was a little problematic to sequence, but I wanted to make sure that any GAS services being imitated were available and initialized on the Node side, just as they are in GAS. At the time of writing these services and classes are implemented.
|
|
167
|
+
This was a little problematic to sequence, but I wanted to make sure that any GAS services being imitated were available and initialized on the Node side, just as they are in GAS. At the time of writing these services and classes are partially implemented.
|
|
168
|
+
|
|
169
|
+
Only a subset of methods are currently available for some of them - the rest are work in progress. My approach is to start with a little bit of each service to prove feasibility and provide a base to build on.
|
|
170
|
+
|
|
171
|
+
v1.0.5
|
|
161
172
|
|
|
162
|
-
|
|
163
|
-
- `
|
|
164
|
-
- `
|
|
165
|
-
- `
|
|
166
|
-
- `
|
|
167
|
-
- `
|
|
168
|
-
- `
|
|
169
|
-
- `
|
|
170
|
-
- `
|
|
171
|
-
- `
|
|
172
|
-
- `
|
|
173
|
+
- `DriveApp` - 50%
|
|
174
|
+
- `ScriptApp` - almost all
|
|
175
|
+
- `UrlFetchApp` - 80%
|
|
176
|
+
- `Utilities` - 60%
|
|
177
|
+
- `Sheets` - `minimal`
|
|
178
|
+
- `CacheService` - 80%
|
|
179
|
+
- `PropertiesService` - 80%
|
|
180
|
+
- `Session` - almost all
|
|
181
|
+
- `Blob` - all
|
|
182
|
+
- `User` - all
|
|
183
|
+
- `Drive (Advanced Service)` - 40%
|
|
184
|
+
|
|
185
|
+
### Testing coverage
|
|
186
|
+
|
|
187
|
+
Tests for all methods are added as we go to the cumulative unit tests and run on both Apps Script and Node. The goal is to try to get the behavior as exactly equivalent as possible. See/updated the issues section for detected anomalies. There are currently 1182 active tests.
|
|
173
188
|
|
|
174
189
|
#### Proxies and globalThis
|
|
175
190
|
|
|
@@ -178,39 +193,36 @@ Each service has a FakeClass but I needed the Auth cycle to be initiated and don
|
|
|
178
193
|
Here's the code for `Utilities`
|
|
179
194
|
|
|
180
195
|
```js
|
|
181
|
-
|
|
182
196
|
/**
|
|
183
197
|
* adds to global space to mimic Apps Script behavior
|
|
184
198
|
*/
|
|
185
|
-
import { Proxies } from
|
|
186
|
-
import { newFakeUtilities } from
|
|
187
|
-
|
|
199
|
+
import { Proxies } from "../../support/proxies.js";
|
|
200
|
+
import { newFakeUtilities } from "./fakeutilities.js";
|
|
188
201
|
|
|
189
202
|
// This will eventually hold a proxy for Utilties
|
|
190
|
-
let _app = null
|
|
203
|
+
let _app = null;
|
|
191
204
|
|
|
192
205
|
/**
|
|
193
206
|
* adds to global space to mimic Apps Script behavior
|
|
194
207
|
*/
|
|
195
|
-
const name = "Utilities"
|
|
208
|
+
const name = "Utilities";
|
|
196
209
|
if (typeof globalThis[name] === typeof undefined) {
|
|
197
210
|
const getApp = () => {
|
|
198
211
|
// if it hasnt been intialized yet then do that
|
|
199
212
|
if (!_app) {
|
|
200
|
-
console.log
|
|
201
|
-
_app = newFakeUtilities()
|
|
213
|
+
console.log(`setting ${name} to global`);
|
|
214
|
+
_app = newFakeUtilities();
|
|
202
215
|
}
|
|
203
216
|
// this is the actual driveApp we'll return from the proxy
|
|
204
|
-
return _app
|
|
205
|
-
}
|
|
206
|
-
Proxies.registerProxy
|
|
217
|
+
return _app;
|
|
218
|
+
};
|
|
219
|
+
Proxies.registerProxy(name, getApp);
|
|
207
220
|
}
|
|
208
221
|
```
|
|
209
222
|
|
|
210
223
|
Here's how the proxies are registered
|
|
211
224
|
|
|
212
225
|
```js
|
|
213
|
-
|
|
214
226
|
/**
|
|
215
227
|
* diverts the property get to another object returned by the getApp function
|
|
216
228
|
* @param {function} a function to get the proxy object to substitutes
|
|
@@ -218,20 +230,19 @@ Here's how the proxies are registered
|
|
|
218
230
|
*/
|
|
219
231
|
const getAppHandler = (getApp) => {
|
|
220
232
|
return {
|
|
221
|
-
|
|
222
233
|
get(_, prop, receiver) {
|
|
223
|
-
// this will let the caller know we're not really running in Apps Script
|
|
224
|
-
return
|
|
234
|
+
// this will let the caller know we're not really running in Apps Script
|
|
235
|
+
return prop === "isFake" ? true : Reflect.get(getApp(), prop, receiver);
|
|
225
236
|
},
|
|
226
237
|
|
|
227
238
|
ownKeys(_) {
|
|
228
|
-
return Reflect.ownKeys(getApp())
|
|
229
|
-
}
|
|
230
|
-
}
|
|
231
|
-
}
|
|
239
|
+
return Reflect.ownKeys(getApp());
|
|
240
|
+
},
|
|
241
|
+
};
|
|
242
|
+
};
|
|
232
243
|
|
|
233
244
|
const registerProxy = (name, getApp) => {
|
|
234
|
-
const value = new Proxy({}, getAppHandler(getApp))
|
|
245
|
+
const value = new Proxy({}, getAppHandler(getApp));
|
|
235
246
|
// add it to the global space to mimic what apps script does
|
|
236
247
|
Object.defineProperty(globalThis, name, {
|
|
237
248
|
value,
|
|
@@ -239,7 +250,7 @@ const registerProxy = (name, getApp) => {
|
|
|
239
250
|
configurable: false,
|
|
240
251
|
writable: false,
|
|
241
252
|
});
|
|
242
|
-
}
|
|
253
|
+
};
|
|
243
254
|
```
|
|
244
255
|
|
|
245
256
|
In short, the service us registered as an empty object, but when any attempt is made to access it actually returns a different object which handles the request. In the `ScriptApp` example, `ScriptApp` is an empty object, but accessing `ScriptApp.getOAuthToken()` returns an Fake `ScriptApp` object which has been initialized.
|
|
@@ -251,55 +262,55 @@ There's also a test available to see if you are running in GAS or on Node - `Scr
|
|
|
251
262
|
An iterator created by a generator does not have a `hasNext()` function, whereas GAS iterators do. To get round this, we can create a regular Node iterator, but introduce a wrapper so the constructor actually gets the first one, and `next()` uses the value we've already peeked at. Here's a wrapper to convert an iterator into a GAS style one.
|
|
252
263
|
|
|
253
264
|
```js
|
|
254
|
-
import { Proxies } from
|
|
265
|
+
import { Proxies } from "./proxies.js";
|
|
255
266
|
/**
|
|
256
267
|
* this is a class to add a hasnext to a generator
|
|
257
268
|
* @class Peeker
|
|
258
|
-
*
|
|
269
|
+
*
|
|
259
270
|
*/
|
|
260
271
|
class Peeker {
|
|
261
272
|
/**
|
|
262
|
-
* @constructor
|
|
273
|
+
* @constructor
|
|
263
274
|
* @param {function} generator the generator function to add a hasNext() to
|
|
264
275
|
* @returns {Peeker}
|
|
265
276
|
*/
|
|
266
277
|
constructor(generator) {
|
|
267
|
-
this.generator = generator
|
|
278
|
+
this.generator = generator;
|
|
268
279
|
// in order to be able to do a hasnext we have to actually get the value
|
|
269
280
|
// this is the next value stored
|
|
270
|
-
this.peeked = generator.next()
|
|
281
|
+
this.peeked = generator.next();
|
|
271
282
|
}
|
|
272
283
|
|
|
273
284
|
/**
|
|
274
285
|
* we see if there's a next if the peeked at is all over
|
|
275
286
|
* @returns {Boolean}
|
|
276
287
|
*/
|
|
277
|
-
hasNext
|
|
278
|
-
return !this.peeked.done
|
|
288
|
+
hasNext() {
|
|
289
|
+
return !this.peeked.done;
|
|
279
290
|
}
|
|
280
291
|
|
|
281
292
|
/**
|
|
282
293
|
* get the next value - actually its already got and storef in peeked
|
|
283
294
|
* @returns {object} {value, done}
|
|
284
295
|
*/
|
|
285
|
-
next
|
|
296
|
+
next() {
|
|
286
297
|
if (!this.hasNext()) {
|
|
287
298
|
// TODO find out what driveapp does
|
|
288
|
-
throw new Error
|
|
299
|
+
throw new Error("iterator is exhausted - there is no more");
|
|
289
300
|
}
|
|
290
301
|
// instead of returning the next, we return the prepeeked next
|
|
291
|
-
const value = this.peeked.value
|
|
292
|
-
this.peeked = this.generator.next()
|
|
293
|
-
return value
|
|
302
|
+
const value = this.peeked.value;
|
|
303
|
+
this.peeked = this.generator.next();
|
|
304
|
+
return value;
|
|
294
305
|
}
|
|
295
306
|
}
|
|
296
307
|
|
|
297
|
-
export const newPeeker = (...args) => Proxies.guard(new Peeker
|
|
308
|
+
export const newPeeker = (...args) => Proxies.guard(new Peeker(...args));
|
|
298
309
|
```
|
|
299
310
|
|
|
300
311
|
And an example of usage, creating a parents iterator from a Drive API file.
|
|
301
312
|
|
|
302
|
-
|
|
313
|
+
```
|
|
303
314
|
const getParentsIterator = ({
|
|
304
315
|
file
|
|
305
316
|
}) => {
|
|
@@ -319,22 +330,20 @@ const getParentsIterator = ({
|
|
|
319
330
|
// create the iterator
|
|
320
331
|
const parentsIt = filesink()
|
|
321
332
|
|
|
322
|
-
// a regular iterator doesnt support the same methods
|
|
333
|
+
// a regular iterator doesnt support the same methods
|
|
323
334
|
// as Apps Script so we'll fake that too
|
|
324
335
|
return newPeeker(parentsIt)
|
|
325
336
|
|
|
326
337
|
}
|
|
327
|
-
|
|
328
|
-
|
|
338
|
+
```
|
|
329
339
|
|
|
330
340
|
### Cache and Property services
|
|
331
341
|
|
|
332
342
|
These are currently implemented using [keyv](https://github.com/jaredwray/keyv) with storage adaptor [keyv-file](https://github.com/zaaack/keyv-file).The `gasfakes.json` file is used to commiicate where these files should be. I've gone for local file storage rather than something like redis to avoid adding local service requirements, but keyv takes a wide range of storage adaptors if you want to do something fancier. A small modificaion to kv.js is all you need.
|
|
333
343
|
|
|
334
|
-
|
|
335
344
|
#### Script, user and document store varieties
|
|
336
345
|
|
|
337
|
-
All 3 are supported for both properties and cache.
|
|
346
|
+
All 3 are supported for both properties and cache.
|
|
338
347
|
|
|
339
348
|
##### scriptId
|
|
340
349
|
|
|
@@ -343,19 +352,22 @@ The local version may have no knowledge of the Apps ScriptId. If you are using c
|
|
|
343
352
|
##### userId
|
|
344
353
|
|
|
345
354
|
The userId is extracted from an accessToken and will match the id derived from Application Default Credentials. This means that you can logon as a different user to test user data isolation. All user level property and cache stores use the scriptId and userId to partition data.
|
|
355
|
+
|
|
346
356
|
##### documentId
|
|
347
357
|
|
|
348
|
-
The documentId is only meaningful if you are working on a container bound scrip. We use the the documentId property of gasfakes.json to identify a container file.
|
|
358
|
+
The documentId is only meaningful if you are working on a container bound scrip. We use the the documentId property of gasfakes.json to identify a container file. All document level property and cache stores use the scriptId and documentId to partition data.
|
|
349
359
|
|
|
350
360
|
### Settings and temporary files
|
|
351
361
|
|
|
352
362
|
As you will have noticed, there are various local support files for props/caching etc. Be careful that these do not get committed to a public repo if you are adding sensitive values to your stores. Note that the real user Id is not used when creating files, but rather an encrypted version of it. This avoids real user ids being revealed in your file system.
|
|
353
363
|
|
|
354
|
-
|
|
355
364
|
## Noticed differences
|
|
356
365
|
|
|
357
|
-
I'll make a note
|
|
366
|
+
I'll make a note in thre repos issues on implementation differences. In the main will be slight differences in error message text, which I'll normalize over time, or where Apps Script has a fundamental obstacle. Please report any differences in behavior you find in the repo issues.
|
|
367
|
+
|
|
368
|
+
### Tradeoffs
|
|
358
369
|
|
|
370
|
+
I've come across various Apps Script bugs/issues as I work through this which I've reported to the GAS team, and added workarounds in the gas fakes code - not sure at this point whether to duplicate the buggy behavior or simulate what would seem to be the correct one. Again - any things you come across please use the issues in the repo to report.
|
|
359
371
|
|
|
360
372
|
## Help
|
|
361
373
|
|
package/package.json
CHANGED
|
@@ -6,11 +6,13 @@
|
|
|
6
6
|
"google-auth-library": "^9.15.0",
|
|
7
7
|
"googleapis": "^144.0.0",
|
|
8
8
|
"got": "^14.4.5",
|
|
9
|
+
"into-stream": "^8.0.1",
|
|
9
10
|
"keyv": "^5.2.3",
|
|
10
11
|
"keyv-file": "^5.1.1",
|
|
11
12
|
"make-synchronous": "^1.0.0",
|
|
12
13
|
"mime": "^4.0.6",
|
|
13
14
|
"sleep-synchronously": "^2.0.0",
|
|
15
|
+
"to-readable-stream": "^4.0.0",
|
|
14
16
|
"unzipper": "^0.12.3"
|
|
15
17
|
},
|
|
16
18
|
"type": "module",
|
|
@@ -19,7 +21,7 @@
|
|
|
19
21
|
"pub": "npm publish --access public"
|
|
20
22
|
},
|
|
21
23
|
"name": "@mcpher/gas-fakes",
|
|
22
|
-
"version": "1.0.
|
|
24
|
+
"version": "1.0.5",
|
|
23
25
|
"main": "main.js",
|
|
24
26
|
"description": "A proof of concept implementation of Apps Script Environment on Node",
|
|
25
27
|
"repository": "github:brucemcpherson/gas-fakes",
|
package/src/index.js
CHANGED
|
@@ -2,11 +2,21 @@
|
|
|
2
2
|
* Advanced drive service
|
|
3
3
|
*/
|
|
4
4
|
import { Proxies } from '../../support/proxies.js'
|
|
5
|
-
import { notYetImplemented } from '../../support/
|
|
5
|
+
import { notYetImplemented } from '../../support/helpers.js'
|
|
6
|
+
import { getAuthedClient } from '../driveapp/drapis.js'
|
|
7
|
+
import { newFakeAdvDriveAbout } from './fakeadvdriveabout.js'
|
|
8
|
+
import { newFakeAdvDriveFiles } from './fakeadvdrivefiles.js';
|
|
9
|
+
import { newFakeAdvDriveApps } from './fakeadvdriveapps.js'
|
|
10
|
+
import { newFakeDrivePermissions } from './fakeadvdrivepermissions.js'
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* the advanced Drive Apps Script service faked
|
|
14
|
+
* @class FakeAdvDrive
|
|
15
|
+
*/
|
|
6
16
|
|
|
7
17
|
class FakeAdvDrive {
|
|
8
18
|
constructor() {
|
|
9
|
-
|
|
19
|
+
this.client = Proxies.guard(getAuthedClient())
|
|
10
20
|
}
|
|
11
21
|
toString() {
|
|
12
22
|
return `AdvancedServiceIdentifier{name=drive, version=v3}`
|
|
@@ -21,113 +31,51 @@ class FakeAdvDrive {
|
|
|
21
31
|
return newFakeAdvDriveAbout(this)
|
|
22
32
|
}
|
|
23
33
|
get Accessproposals() {
|
|
24
|
-
return notYetImplemented
|
|
34
|
+
return notYetImplemented()
|
|
25
35
|
}
|
|
26
36
|
get Apps() {
|
|
27
|
-
return
|
|
37
|
+
return newFakeAdvDriveApps(this)
|
|
28
38
|
}
|
|
29
39
|
get Changes() {
|
|
30
|
-
return notYetImplemented
|
|
40
|
+
return notYetImplemented()
|
|
31
41
|
}
|
|
32
42
|
get Channels() {
|
|
33
|
-
return notYetImplemented
|
|
43
|
+
return notYetImplemented()
|
|
34
44
|
}
|
|
35
45
|
get Comments() {
|
|
36
|
-
return notYetImplemented
|
|
46
|
+
return notYetImplemented()
|
|
37
47
|
}
|
|
38
48
|
get Drives() {
|
|
39
|
-
return notYetImplemented
|
|
49
|
+
return notYetImplemented()
|
|
40
50
|
}
|
|
41
51
|
get Operations() {
|
|
42
|
-
return
|
|
52
|
+
return blanketProxy(
|
|
53
|
+
"GoogleJsonResponseException: API call to drive.operations.list failed with error: Operation is not implemented, or supported, or enabled."
|
|
54
|
+
)
|
|
43
55
|
}
|
|
44
56
|
get Permissions() {
|
|
45
|
-
return
|
|
57
|
+
return newFakeDrivePermissions(this)
|
|
46
58
|
}
|
|
47
59
|
get Replies() {
|
|
48
|
-
return notYetImplemented
|
|
60
|
+
return notYetImplemented()
|
|
49
61
|
}
|
|
50
62
|
get Revisions() {
|
|
51
|
-
return notYetImplemented
|
|
63
|
+
return notYetImplemented()
|
|
52
64
|
}
|
|
53
65
|
get Teamdrives() {
|
|
54
|
-
return notYetImplemented
|
|
66
|
+
return notYetImplemented()
|
|
55
67
|
}
|
|
56
68
|
|
|
57
69
|
}
|
|
58
70
|
|
|
59
|
-
class FakeAdvDriveAbout {
|
|
60
|
-
constructor(drive) {
|
|
61
|
-
this.toString = drive.toString
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
// this is a schema and needs the fields parameter
|
|
65
|
-
get() {
|
|
66
|
-
return notYetImplemented
|
|
67
|
-
}
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
class FakeAdvDriveFiles {
|
|
71
|
-
constructor(drive) {
|
|
72
|
-
this.toString = drive.toString
|
|
73
|
-
}
|
|
74
|
-
|
|
75
|
-
listLabels() {
|
|
76
|
-
return notYetImplemented
|
|
77
|
-
}
|
|
78
|
-
|
|
79
|
-
emptyTrash() {
|
|
80
|
-
return notYetImplemented
|
|
81
|
-
}
|
|
82
71
|
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
list() {
|
|
88
|
-
return notYetImplemented
|
|
89
|
-
}
|
|
90
|
-
remove() {
|
|
91
|
-
return notYetImplemented
|
|
92
|
-
}
|
|
72
|
+
// will always fail no matter which method is selected
|
|
73
|
+
const blanketProxy = (message) => Proxies.blanketProxy(() => {
|
|
74
|
+
throw new Error(message)
|
|
75
|
+
})
|
|
93
76
|
|
|
94
|
-
download() {
|
|
95
|
-
return notYetImplemented
|
|
96
|
-
}
|
|
97
|
-
|
|
98
|
-
modifyLabels() {
|
|
99
|
-
return notYetImplemented
|
|
100
|
-
}
|
|
101
|
-
|
|
102
|
-
watch() {
|
|
103
|
-
return notYetImplemented
|
|
104
|
-
}
|
|
105
|
-
|
|
106
|
-
get() {
|
|
107
|
-
return notYetImplemented
|
|
108
|
-
}
|
|
109
|
-
|
|
110
|
-
create() {
|
|
111
|
-
return notYetImplemented
|
|
112
|
-
}
|
|
113
|
-
|
|
114
|
-
generateIds() {
|
|
115
|
-
return notYetImplemented
|
|
116
|
-
}
|
|
117
|
-
|
|
118
|
-
copy() {
|
|
119
|
-
return notYetImplemented
|
|
120
|
-
}
|
|
121
|
-
|
|
122
|
-
export() {
|
|
123
|
-
return notYetImplemented
|
|
124
|
-
}
|
|
125
|
-
|
|
126
|
-
}
|
|
127
77
|
|
|
128
|
-
const
|
|
129
|
-
const newFakeAdvDriveAbout = (...args) => Proxies.guard(new FakeAdvDriveAbout(...args))
|
|
130
|
-
export const newFakeAdvDrive = (...args) => Proxies.guard(new FakeAdvDrive)
|
|
78
|
+
export const newFakeAdvDrive = (...args) => Proxies.guard(new FakeAdvDrive(...args))
|
|
131
79
|
|
|
132
80
|
|
|
133
81
|
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { Proxies } from '../../support/proxies.js'
|
|
2
|
+
import { notYetImplemented } from '../../support/helpers.js'
|
|
3
|
+
|
|
4
|
+
class FakeAdvDriveAbout {
|
|
5
|
+
constructor(drive) {
|
|
6
|
+
this.toString = drive.toString
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
// this is a schema and needs the fields parameter
|
|
10
|
+
get() {
|
|
11
|
+
return notYetImplemented()
|
|
12
|
+
}
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export const newFakeAdvDriveAbout = (...args) => Proxies.guard(new FakeAdvDriveAbout(...args))
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
|
|
2
|
+
import { Proxies } from '../../support/proxies.js'
|
|
3
|
+
import { isGood, throwResponse } from '../../support/helpers.js'
|
|
4
|
+
import { Syncit } from '../../support/syncit.js'
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* these apply to Drive.apps
|
|
8
|
+
* note that this can't work with ADC yet as it's blocked - waiting for feedback from google on the issue
|
|
9
|
+
*/
|
|
10
|
+
class FakeAdvDriveApps {
|
|
11
|
+
constructor(drive) {
|
|
12
|
+
this.drive = drive
|
|
13
|
+
this.name = 'Drive.Apps'
|
|
14
|
+
this.apiProp = 'apps'
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
toString() {
|
|
18
|
+
return this.drive.toString()
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
get(appId, params = {}) {
|
|
22
|
+
// sincify that call
|
|
23
|
+
params = { ...params, appId }
|
|
24
|
+
const { response, data } = Syncit.fxDrive({ prop: this.apiProp, method: 'get', params })
|
|
25
|
+
|
|
26
|
+
// maybe we need to throw an error
|
|
27
|
+
if (!isGood(response)) {
|
|
28
|
+
throwResponse(response)
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
return data
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
list(params) {
|
|
35
|
+
// sincify that call
|
|
36
|
+
const { response, data } = Syncit.fxDrive({ prop: this.apiProp, method: 'list', params })
|
|
37
|
+
|
|
38
|
+
// maybe we need to throw an error
|
|
39
|
+
if (!isGood(response)) {
|
|
40
|
+
throwResponse(response)
|
|
41
|
+
}
|
|
42
|
+
return data
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
export const newFakeAdvDriveApps = (...args) => Proxies.guard(new FakeAdvDriveApps(...args))
|