@mcpher/gas-fakes 1.0.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/LICENSE +21 -0
- package/README.md +305 -0
- package/main.js +1 -0
- package/package.json +23 -0
- package/src/index.js +4 -0
- package/src/services/drive/app.js +31 -0
- package/src/services/drive/drapis.js +23 -0
- package/src/services/drive/fakedrive.js +568 -0
- package/src/services/drive/fakedrivehelpers.js +141 -0
- package/src/services/scriptapp/app.js +133 -0
- package/src/services/urlfetchapp/app.js +128 -0
- package/src/services/utilities/app.js +37 -0
- package/src/services/utilities/fakeblob.js +73 -0
- package/src/support/auth.js +154 -0
- package/src/support/constants.js +16 -0
- package/src/support/peeker.js +44 -0
- package/src/support/proxies.js +74 -0
- package/src/support/syncit.js +235 -0
- package/src/support/utils.js +144 -0
|
@@ -0,0 +1,235 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* these are utilities to return sync versions of functions
|
|
3
|
+
*/
|
|
4
|
+
import makeSynchronous from 'make-synchronous';
|
|
5
|
+
import path from 'path'
|
|
6
|
+
import { Auth } from "./auth.js"
|
|
7
|
+
|
|
8
|
+
const authPath = "../support/auth.js"
|
|
9
|
+
const drapisPath = "../services/drive/drapis.js"
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* note that the relpath of exports file
|
|
13
|
+
* is relative from the entrypoint, since all this sync stuff runs in a subprocess
|
|
14
|
+
* @constant
|
|
15
|
+
* @type {string}
|
|
16
|
+
* @default
|
|
17
|
+
*/
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* @param {string} [relTarget=relExports] the target module relative to this script
|
|
21
|
+
* @returns {string} the full path
|
|
22
|
+
*/
|
|
23
|
+
const getModulePath = (relTarget) => path.resolve(import.meta.dirname, relTarget)
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* sync a call to Drive api
|
|
29
|
+
* @param {object} p pargs
|
|
30
|
+
* @param {string} p.prop the prop of drive eg 'files' for drive.files
|
|
31
|
+
* @param {string} p.method the method of drive eg 'list' for drive.files.list
|
|
32
|
+
* @param {object} p.params the params to add to the request
|
|
33
|
+
* @return {DriveResponse} from the drive api
|
|
34
|
+
*/
|
|
35
|
+
const fxDrive = ({ prop, method, params }) => {
|
|
36
|
+
|
|
37
|
+
// this will run a node child process
|
|
38
|
+
// note that nothing is inherited, so consider it as a standalone script
|
|
39
|
+
const fx = makeSynchronous(async ({ prop, method, drapisPath, authPath, scopes, params }) => {
|
|
40
|
+
|
|
41
|
+
const { Auth } = await import(authPath)
|
|
42
|
+
const { getDriveClient, responseSyncify } = await import(drapisPath)
|
|
43
|
+
|
|
44
|
+
// the scopes are required to set up an appropriate auth
|
|
45
|
+
Auth.setAuth(scopes)
|
|
46
|
+
const auth = Auth.getAuth()
|
|
47
|
+
|
|
48
|
+
// this is the node drive service
|
|
49
|
+
const drive = getDriveClient(auth)
|
|
50
|
+
const response = await drive[prop][method](params)
|
|
51
|
+
|
|
52
|
+
return {
|
|
53
|
+
data: response.data,
|
|
54
|
+
response: responseSyncify(response)
|
|
55
|
+
}
|
|
56
|
+
})
|
|
57
|
+
|
|
58
|
+
const scopes = Array.from(Auth.getAuthedScopes().keys())
|
|
59
|
+
const result = fx({
|
|
60
|
+
prop,
|
|
61
|
+
method,
|
|
62
|
+
drapisPath: getModulePath(drapisPath),
|
|
63
|
+
authPath: getModulePath(authPath),
|
|
64
|
+
scopes,
|
|
65
|
+
params
|
|
66
|
+
})
|
|
67
|
+
return result
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
/**
|
|
71
|
+
* sync a call to Drive api to stream a download
|
|
72
|
+
* @param {object} p pargs
|
|
73
|
+
* @param {string} p.prop the prop of drive eg 'files' for drive.files
|
|
74
|
+
* @param {string} p.method the method of drive eg 'list' for drive.files.list
|
|
75
|
+
* @param {object} p.params the params to add to the request
|
|
76
|
+
* @return {DriveResponse} from the drive api
|
|
77
|
+
*/
|
|
78
|
+
const fxDriveMedia = ({ id }) => {
|
|
79
|
+
|
|
80
|
+
// this will run a node child process
|
|
81
|
+
// note that nothing is inherited, so consider it as a standalone script
|
|
82
|
+
const fx = makeSynchronous(async ({ id, drapisPath, authPath, scopes }) => {
|
|
83
|
+
|
|
84
|
+
const { Auth } = await import(authPath)
|
|
85
|
+
const { getDriveClient, responseSyncify } = await import(drapisPath)
|
|
86
|
+
const { getStreamAsBuffer } = await import('get-stream');
|
|
87
|
+
|
|
88
|
+
// the scopes are required to set up an appropriate auth
|
|
89
|
+
Auth.setAuth(scopes)
|
|
90
|
+
const auth = Auth.getAuth()
|
|
91
|
+
|
|
92
|
+
// this is the node drive service
|
|
93
|
+
const drive = getDriveClient(auth)
|
|
94
|
+
const streamed = await drive.files.get({
|
|
95
|
+
fileId: id,
|
|
96
|
+
alt: 'media'
|
|
97
|
+
}, {
|
|
98
|
+
responseType: 'stream'
|
|
99
|
+
})
|
|
100
|
+
const response = responseSyncify(streamed)
|
|
101
|
+
|
|
102
|
+
if (response.status === 200) {
|
|
103
|
+
const buf = await getStreamAsBuffer(streamed.data)
|
|
104
|
+
const data = Array.from(buf)
|
|
105
|
+
|
|
106
|
+
return {
|
|
107
|
+
data,
|
|
108
|
+
response
|
|
109
|
+
}
|
|
110
|
+
} else {
|
|
111
|
+
return {
|
|
112
|
+
data: null,
|
|
113
|
+
response
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
})
|
|
118
|
+
|
|
119
|
+
const scopes = Array.from(Auth.getAuthedScopes().keys())
|
|
120
|
+
const result = fx({
|
|
121
|
+
id,
|
|
122
|
+
drapisPath: getModulePath(drapisPath),
|
|
123
|
+
authPath: getModulePath(authPath),
|
|
124
|
+
scopes
|
|
125
|
+
})
|
|
126
|
+
return result
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
const fxGetManifest = (manifestPath = './appsscript.json') => {
|
|
130
|
+
|
|
131
|
+
const fx = makeSynchronous(async (manifestFile) => {
|
|
132
|
+
const { readFile } = await import('node:fs/promises')
|
|
133
|
+
console.log(`using manifest file:${manifestFile}`)
|
|
134
|
+
const contents = await readFile(manifestFile, { encoding: 'utf8' })
|
|
135
|
+
return JSON.parse(contents)
|
|
136
|
+
})
|
|
137
|
+
|
|
138
|
+
// we assume that the manifest file is in the same path as the executing main
|
|
139
|
+
// TODO allow a yargs option to put it somewhere else
|
|
140
|
+
const mainDir = path.dirname(process.argv[1])
|
|
141
|
+
const manifestFile = path.resolve(mainDir, manifestPath)
|
|
142
|
+
const result = fx(manifestFile)
|
|
143
|
+
return result
|
|
144
|
+
}
|
|
145
|
+
/**
|
|
146
|
+
* get any prop from an auth object, syncjronously
|
|
147
|
+
* @param {prop} prop the prop that should be executed
|
|
148
|
+
* @returns {*} the value of the prop
|
|
149
|
+
*/
|
|
150
|
+
const fxGet = (prop) => {
|
|
151
|
+
|
|
152
|
+
// now turn all that into a synchronous function - it runs as a subprocess, so we need to start from scratch
|
|
153
|
+
const fx = makeSynchronous(async (scopes, prop, authPath) => {
|
|
154
|
+
const { Auth } = await import(authPath)
|
|
155
|
+
|
|
156
|
+
// these are the scopes needed from the manifest file
|
|
157
|
+
Auth.setAuth(scopes)
|
|
158
|
+
const auth = Auth.getAuth()
|
|
159
|
+
const value = await auth[prop]()
|
|
160
|
+
return value
|
|
161
|
+
})
|
|
162
|
+
|
|
163
|
+
|
|
164
|
+
// these will already have been set from the manifest
|
|
165
|
+
const scopes = Array.from(Auth.getAuthedScopes().keys())
|
|
166
|
+
const result = fx(scopes, prop, getModulePath(authPath))
|
|
167
|
+
return result
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
/**
|
|
171
|
+
* wrap function in synch convertor, run and get access token
|
|
172
|
+
* @returns {string}
|
|
173
|
+
*/
|
|
174
|
+
const fxGetAccessToken = () => {
|
|
175
|
+
return fxGet("getAccessToken")
|
|
176
|
+
}
|
|
177
|
+
/**
|
|
178
|
+
* wrap function in synch convertor, run and get access token
|
|
179
|
+
* @returns {string}
|
|
180
|
+
*/
|
|
181
|
+
const fxGetProjectId = () => {
|
|
182
|
+
return fxGet("getProjectId")
|
|
183
|
+
}
|
|
184
|
+
/**
|
|
185
|
+
* a sync version of token checking
|
|
186
|
+
* @param {string} token the token to check
|
|
187
|
+
* @returns {object} access token info
|
|
188
|
+
*/
|
|
189
|
+
const fxCheckToken = (accessToken) => {
|
|
190
|
+
|
|
191
|
+
// now turn all that into a synchronous function - it runs as a subprocess, so we need to start from scratch
|
|
192
|
+
const fx = makeSynchronous(async accessToken => {
|
|
193
|
+
const { default: got } = await import('got')
|
|
194
|
+
const tokenInfo = await got(`https://www.googleapis.com/oauth2/v3/tokeninfo?access_token=${accessToken}`).json()
|
|
195
|
+
return tokenInfo
|
|
196
|
+
})
|
|
197
|
+
|
|
198
|
+
const result = fx(accessToken)
|
|
199
|
+
return result
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
/**
|
|
203
|
+
* a sync version of fetching
|
|
204
|
+
* @param {string} url the url to check
|
|
205
|
+
* @param {object} options the options
|
|
206
|
+
* @param {string[]} responseField the reponse fields to extract (we cant serialize native code)
|
|
207
|
+
* @returns {reponse} urlfetch style reponse
|
|
208
|
+
*/
|
|
209
|
+
const fxFetch = (url, options, responseFields) => {
|
|
210
|
+
// TODO need to handle muteHttpExceptions
|
|
211
|
+
// now turn all that into a synchronous function - it runs as a subprocess, so we need to start from scratch
|
|
212
|
+
const fx = makeSynchronous(async (url, options, responseFields) => {
|
|
213
|
+
const { default: got } = await import('got')
|
|
214
|
+
const response = await got(url, {
|
|
215
|
+
...options
|
|
216
|
+
})
|
|
217
|
+
// we cant return the response from this as it cant be serialized
|
|
218
|
+
// so we;ll extract oout the fields required
|
|
219
|
+
return responseFields.reduce((p, c) => {
|
|
220
|
+
p[c] = response[c]
|
|
221
|
+
return p
|
|
222
|
+
}, {})
|
|
223
|
+
})
|
|
224
|
+
return fx(url, options, responseFields)
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
export const Syncit = {
|
|
228
|
+
fxGetAccessToken,
|
|
229
|
+
fxCheckToken,
|
|
230
|
+
fxFetch,
|
|
231
|
+
fxGetManifest,
|
|
232
|
+
fxGetProjectId,
|
|
233
|
+
fxDrive,
|
|
234
|
+
fxDriveMedia
|
|
235
|
+
}
|
|
@@ -0,0 +1,144 @@
|
|
|
1
|
+
const isUndefined = (item) => typeof item === typeof undefined
|
|
2
|
+
const isNull = (item) => item === null
|
|
3
|
+
const isNU = (item) => isNull(item) || isUndefined(item)
|
|
4
|
+
const isObject = (item) => typeof item === 'object'
|
|
5
|
+
const isFunction = (item) => typeof item === 'function'
|
|
6
|
+
const isArray = (item) => Array.isArray(item)
|
|
7
|
+
const isString = (item) => typeof item === "string"
|
|
8
|
+
const isNumber = (item) => Number.isFinite(item)
|
|
9
|
+
const isBoolean = (item) => typeof item === "boolean"
|
|
10
|
+
const isBuffer = (item) => Buffer.isBuffer (item)
|
|
11
|
+
const arrify = (item) => isArray(item)
|
|
12
|
+
? item
|
|
13
|
+
: (isNU(item) ? item : [item])
|
|
14
|
+
|
|
15
|
+
const isPromise = (item) => !isNU(item) && (isObject(item) || isFunction(item)) && isFunction(item.then)
|
|
16
|
+
const fromJson = (text, failOnError = false) => {
|
|
17
|
+
try {
|
|
18
|
+
return JSON.parse(text)
|
|
19
|
+
} catch (err) {
|
|
20
|
+
console.log(text)
|
|
21
|
+
if (failOnError) {
|
|
22
|
+
throw err
|
|
23
|
+
}
|
|
24
|
+
return null
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* merge a series of url params
|
|
30
|
+
* @param {object|object[]} pob an object eg [{pa:a,pb:b},{pc:c}]
|
|
31
|
+
* @returns {string} would return pa=a&pb=pb&pc=c and encoded URI
|
|
32
|
+
*/
|
|
33
|
+
const makeUrlParams = (pob) => {
|
|
34
|
+
return makeParams(pob).map(([k, v]) => `${k}=${encodeURIComponent(v)}`).join("&")
|
|
35
|
+
}
|
|
36
|
+
/**
|
|
37
|
+
* merge a series of url params
|
|
38
|
+
* @param {object|object[]} pob an object eg [{pa:a,pb:b},{pc:c}]
|
|
39
|
+
* @returns {object} merged and dedupped paramaters reduced
|
|
40
|
+
*/
|
|
41
|
+
const makeParamOb = (pob) => {
|
|
42
|
+
const a = makeParams (pob)
|
|
43
|
+
return a.reduce ((p, [k,v])=>{
|
|
44
|
+
p[k] = v
|
|
45
|
+
return p
|
|
46
|
+
},{})
|
|
47
|
+
}
|
|
48
|
+
/**
|
|
49
|
+
* merge a series of url params
|
|
50
|
+
* @param {object|object[]} pob an object eg [{pa:a,pb:b},{pc:c}]
|
|
51
|
+
* @returns {object[]} merged and dedupped paramaters
|
|
52
|
+
*/
|
|
53
|
+
const makeParams = (pob = []) => {
|
|
54
|
+
// dups will be removed
|
|
55
|
+
const mapob = arrify(pob).reduce((p, c) => {
|
|
56
|
+
Reflect.ownKeys(c).forEach(k => p.set(k, c[k]))
|
|
57
|
+
return p
|
|
58
|
+
}, new Map())
|
|
59
|
+
return Array.from(mapob.entries())
|
|
60
|
+
}
|
|
61
|
+
const is = {
|
|
62
|
+
nu: isNU,
|
|
63
|
+
null: isNull,
|
|
64
|
+
undefined: isUndefined,
|
|
65
|
+
object: isObject,
|
|
66
|
+
function: isFunction,
|
|
67
|
+
array: isArray,
|
|
68
|
+
promise: isPromise,
|
|
69
|
+
string: isString,
|
|
70
|
+
number: isNumber,
|
|
71
|
+
boolean: isBoolean,
|
|
72
|
+
buffer: isBuffer
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
/**
|
|
76
|
+
* assert is of correct type
|
|
77
|
+
* @param {} value
|
|
78
|
+
* @param {string} type
|
|
79
|
+
* @param {string} [mess] override default thrown message
|
|
80
|
+
* @returns {*} value-
|
|
81
|
+
*/
|
|
82
|
+
const assertType = (value, type, mess) => {
|
|
83
|
+
if (!Reflect.has(is, type)) {
|
|
84
|
+
throw new Error(`dont know how to check asserted type ${type}`)
|
|
85
|
+
}
|
|
86
|
+
mess = mess || `value is not asserted ${type} : it's a ${typeof value}`
|
|
87
|
+
if (!is[type](value)) {
|
|
88
|
+
throw new Error(mess)
|
|
89
|
+
}
|
|
90
|
+
return value
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
const settleAsString = (data, charset) => {
|
|
94
|
+
if (isBuffer(data)) {
|
|
95
|
+
return bytesToString (Array.from(data), charset)
|
|
96
|
+
} else if (isArray(data)) {
|
|
97
|
+
return bytesToString (data, charset)
|
|
98
|
+
} else {
|
|
99
|
+
return assertType (data, "string")
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
const settleAsBytes = (data, charset) => {
|
|
105
|
+
|
|
106
|
+
if (isString(data)) {
|
|
107
|
+
return stringToBytes (data, charset)
|
|
108
|
+
} else if (isBuffer(data)) {
|
|
109
|
+
return Array.from (data)
|
|
110
|
+
} else {
|
|
111
|
+
return assertType (data, "array")
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
const stringToBytes = (string, charset) => Array.from(Buffer.from(string, charset))
|
|
117
|
+
const bytesToString = (data, charset) => Buffer.from(data).toString (charset)
|
|
118
|
+
|
|
119
|
+
export const Utils = {
|
|
120
|
+
stringToBytes,
|
|
121
|
+
bytesToString,
|
|
122
|
+
settleAsBytes,
|
|
123
|
+
settleAsString,
|
|
124
|
+
is,
|
|
125
|
+
isBuffer,
|
|
126
|
+
isNU,
|
|
127
|
+
isNull,
|
|
128
|
+
isUndefined,
|
|
129
|
+
isObject,
|
|
130
|
+
isFunction,
|
|
131
|
+
isArray,
|
|
132
|
+
isPromise,
|
|
133
|
+
isNumber,
|
|
134
|
+
isString,
|
|
135
|
+
fromJson,
|
|
136
|
+
arrify,
|
|
137
|
+
assertType,
|
|
138
|
+
makeUrlParams,
|
|
139
|
+
makeParams,
|
|
140
|
+
makeParamOb
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
|
|
144
|
+
|