@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.
@@ -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
+