@mcpher/gas-fakes 1.0.5 → 1.0.6
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/package.json +1 -1
- package/src/services/advdrive/fakeadvdrivefiles.js +69 -52
- package/src/services/driveapp/fakedriveapp.js +1 -2
- package/src/services/driveapp/fakedrivefile.js +41 -11
- package/src/services/driveapp/fakedriveiterators.js +11 -4
- package/src/services/driveapp/fakedrivemeta.js +144 -26
- package/src/services/utilities/fakeblob.js +4 -0
- package/src/services/utilities/fakeutilities.js +1 -1
- package/src/support/filecache.js +62 -14
- package/src/support/helpers.js +43 -1
- package/src/support/sxauth.js +116 -0
- package/src/support/sxdrive.js +188 -0
- package/src/support/sxfetch.js +31 -0
- package/src/support/sxstore.js +19 -0
- package/src/support/sxzip.js +91 -0
- package/src/support/syncit.js +175 -377
package/package.json
CHANGED
|
@@ -21,7 +21,7 @@
|
|
|
21
21
|
"pub": "npm publish --access public"
|
|
22
22
|
},
|
|
23
23
|
"name": "@mcpher/gas-fakes",
|
|
24
|
-
"version": "1.0.
|
|
24
|
+
"version": "1.0.6",
|
|
25
25
|
"main": "main.js",
|
|
26
26
|
"description": "A proof of concept implementation of Apps Script Environment on Node",
|
|
27
27
|
"repository": "github:brucemcpherson/gas-fakes",
|
|
@@ -1,9 +1,12 @@
|
|
|
1
1
|
import { Proxies } from '../../support/proxies.js'
|
|
2
|
-
import { notYetImplemented, isGood, throwResponse, minFields } from '../../support/helpers.js'
|
|
2
|
+
import { notYetImplemented, isGood, throwResponse, minFields, isFolder } from '../../support/helpers.js'
|
|
3
3
|
import { Syncit } from '../../support/syncit.js'
|
|
4
|
-
import {
|
|
5
|
-
import {Utils} from '../../support/utils.js'
|
|
6
|
-
|
|
4
|
+
import { improveFileCache, checkResponse } from '../../support/filecache.js';
|
|
5
|
+
import { Utils, mergeParamStrings } from '../../support/utils.js'
|
|
6
|
+
|
|
7
|
+
const { is } = Utils
|
|
8
|
+
|
|
9
|
+
const apiProp = 'files'
|
|
7
10
|
|
|
8
11
|
/**
|
|
9
12
|
* these apply to Drive.files
|
|
@@ -12,7 +15,7 @@ class FakeAdvDriveFiles {
|
|
|
12
15
|
constructor(drive) {
|
|
13
16
|
this.drive = drive
|
|
14
17
|
this.name = 'Drive.Files'
|
|
15
|
-
this.apiProp =
|
|
18
|
+
this.apiProp = apiProp
|
|
16
19
|
}
|
|
17
20
|
|
|
18
21
|
toString() {
|
|
@@ -27,24 +30,14 @@ class FakeAdvDriveFiles {
|
|
|
27
30
|
return notYetImplemented()
|
|
28
31
|
}
|
|
29
32
|
|
|
30
|
-
|
|
31
|
-
return notYetImplemented()
|
|
32
|
-
}
|
|
33
|
+
|
|
33
34
|
|
|
34
35
|
list(params = {}) {
|
|
35
36
|
// this is pretty straightforward as the onus is on thecaller to provide a valid queryOb
|
|
36
37
|
// and validation will be done by the api.
|
|
37
38
|
// however to support caching, we'll fiddle with the fields parameter
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
const far = tidyFieldsFar(fob)
|
|
41
|
-
params = {
|
|
42
|
-
...params, fields: {
|
|
43
|
-
...params.fields,
|
|
44
|
-
files: far.join(",")
|
|
45
|
-
}
|
|
46
|
-
}
|
|
47
|
-
}
|
|
39
|
+
|
|
40
|
+
params.fields = mergeParamStrings(params.fields || "", `files(${minFields})`)
|
|
48
41
|
|
|
49
42
|
// sincify that call
|
|
50
43
|
const { response, data } = Syncit.fxDrive({ prop: this.apiProp, method: 'list', params })
|
|
@@ -55,8 +48,11 @@ class FakeAdvDriveFiles {
|
|
|
55
48
|
}
|
|
56
49
|
|
|
57
50
|
// lets improve cache with any enhanced data we've found
|
|
51
|
+
/// extract out the fieldslist
|
|
52
|
+
const fields = params.fields.replace(/.*files\(([^)]*)\).*/,"$1")
|
|
53
|
+
|
|
58
54
|
data.files.forEach(f => {
|
|
59
|
-
improveFileCache(f.id, f)
|
|
55
|
+
improveFileCache(f.id, f,fields)
|
|
60
56
|
})
|
|
61
57
|
return data
|
|
62
58
|
|
|
@@ -117,70 +113,70 @@ class FakeAdvDriveFiles {
|
|
|
117
113
|
* @returns {Drive.File}
|
|
118
114
|
*/
|
|
119
115
|
get(id, params = {}, { allow404 = true } = {}) {
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
let far = tidyFieldsFar(params)
|
|
123
|
-
|
|
124
|
-
// the cache will check the fields it already has against those requested
|
|
125
|
-
const { cachedFile, good } = getFromFileCache(id, far)
|
|
126
|
-
if (good) return cachedFile
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
// we'll enhance the cache with the current value of any already fetched field by fetching it again
|
|
130
|
-
params = { ...params, fileId: id, fields: enhanceFar({ cachedFile, far }) }
|
|
131
|
-
|
|
132
|
-
// run as a sub process to unasync it
|
|
133
|
-
const { response, data: file } = Syncit.fxDrive({ prop: this.apiProp, method: 'get', params })
|
|
134
|
-
|
|
135
|
-
// maybe we need to throw an error
|
|
136
|
-
checkResponse(id, response, allow404)
|
|
137
|
-
|
|
138
|
-
// finally register in cache for next time
|
|
139
|
-
return improveFileCache(id, file)
|
|
116
|
+
const {data} = Syncit.fxDriveGet ({ id, params, allow404, allowCache: true })
|
|
117
|
+
return data
|
|
140
118
|
}
|
|
141
119
|
|
|
142
120
|
/**
|
|
143
121
|
* ceate a file and optionally upload some data
|
|
144
122
|
* @param {File} [file] file resource
|
|
145
123
|
* @param {Blob} [blob] the mediadata
|
|
124
|
+
* @param {string} [fields] (not and advanced drive option but allow me to use this from driveapp and avoid an extra fetch)
|
|
146
125
|
*/
|
|
147
|
-
create(file = {}, blob) {
|
|
126
|
+
create(file = {}, blob, fields,params) {
|
|
127
|
+
|
|
148
128
|
if (!is.undefined(blob) && !Utils.isBlob(blob)) {
|
|
149
129
|
throw new Error("The mediaData parameter only supports Blob types for upload.")
|
|
150
130
|
}
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
const
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
improveFileCache(data.id, data)
|
|
157
|
-
return data
|
|
131
|
+
|
|
132
|
+
// must have some kind of name so derive if not given
|
|
133
|
+
const name = file.name || blob?.getName() || (isFolder(file) ? "New Folder" : "Untitled")
|
|
134
|
+
return updateOrCreate ({method: "create", file: { ...file, name }, blob, fields, params })
|
|
135
|
+
|
|
158
136
|
}
|
|
159
137
|
|
|
160
138
|
generateIds() {
|
|
161
139
|
return notYetImplemented()
|
|
162
140
|
}
|
|
163
141
|
|
|
142
|
+
/**
|
|
143
|
+
* patch a file and ioptionally upoad new content
|
|
144
|
+
* @param {Drive.File} file the resource metadata
|
|
145
|
+
* @param {string} fileId the fileid to update
|
|
146
|
+
* @param {FakeBlob} [blob] new media if required
|
|
147
|
+
* @param {string} [fields] (not and advanced drive option but allow me to use this from driveapp and avoid an extra fetch)
|
|
148
|
+
* @param {object} [params] any extra params
|
|
149
|
+
* @returns {Drive.File} updated
|
|
150
|
+
*/
|
|
151
|
+
update(file, fileId, blob, fields,params) {
|
|
152
|
+
if (!is.nonEmptyString(fileId)) {
|
|
153
|
+
throw new Error(`API call to drive.files.update failed with error: Required`)
|
|
154
|
+
}
|
|
155
|
+
return updateOrCreate ({method: 'update', file, blob, fileId , fields, params})
|
|
156
|
+
}
|
|
164
157
|
/**
|
|
165
158
|
* ceate a file and optionally upload some data
|
|
166
159
|
* @param {File} [file] file resource to patch
|
|
167
160
|
* @param {string} fileId the file to copy
|
|
168
161
|
* @param {object} [options] request options
|
|
169
162
|
*/
|
|
170
|
-
copy(file, fileId, options) {
|
|
163
|
+
copy(file, fileId, options={}) {
|
|
164
|
+
|
|
171
165
|
if (!is.nonEmptyString(fileId)) {
|
|
172
|
-
throw new Error
|
|
166
|
+
throw new Error(`API call to drive.files.copy failed with error: Required`)
|
|
173
167
|
}
|
|
168
|
+
const fields = mergeParamStrings(options.fields || "",minFields)
|
|
174
169
|
const params = {
|
|
175
|
-
fields
|
|
170
|
+
fields,
|
|
176
171
|
fileId,
|
|
177
172
|
resource: file
|
|
178
173
|
}
|
|
179
174
|
|
|
180
|
-
const { response, data } =
|
|
175
|
+
const { response, data } = Syncit.fxDrive({ prop: apiProp, method: 'copy', params, options })
|
|
181
176
|
checkResponse(data?.id, response, false)
|
|
182
|
-
improveFileCache(data.id, data)
|
|
177
|
+
improveFileCache(data.id, data,fields)
|
|
183
178
|
return data
|
|
179
|
+
|
|
184
180
|
}
|
|
185
181
|
|
|
186
182
|
export() {
|
|
@@ -224,4 +220,25 @@ const enhanceFar = ({ cachedFile, far }) => {
|
|
|
224
220
|
return far.join(",")
|
|
225
221
|
}
|
|
226
222
|
|
|
223
|
+
|
|
224
|
+
/**
|
|
225
|
+
* ceate/patch a file and optionally upload some data
|
|
226
|
+
* update and copy are virtually the same payload
|
|
227
|
+
* @param {string} method the api method
|
|
228
|
+
* @param {File} [file] file resource to patch/create
|
|
229
|
+
* @param {string} fileId the file to update
|
|
230
|
+
* @param {FakeBlob} [blob] blob if media is provided
|
|
231
|
+
*/
|
|
232
|
+
const updateOrCreate = ( {method, file = {}, blob, fileId, fields="" , params}) => {
|
|
233
|
+
|
|
234
|
+
if (!Utils.isNU(blob) && !Utils.isBlob(blob)) {
|
|
235
|
+
throw new Error("The mediaData parameter only supports Blob types for upload.")
|
|
236
|
+
}
|
|
237
|
+
fields = mergeParamStrings(fields, minFields)
|
|
238
|
+
// streamupmedia takes care of improving the cache
|
|
239
|
+
const result = Syncit.fxStreamUpMedia({ method, fields, blob, file , fileId, params })
|
|
240
|
+
const { data } = result
|
|
241
|
+
return data
|
|
242
|
+
}
|
|
243
|
+
|
|
227
244
|
export const newFakeAdvDriveFiles = (...args) => Proxies.guard(new FakeAdvDriveFiles(...args))
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { FakeDriveFolder, newFakeDriveFolder } from './fakedrivefolder.js'
|
|
2
|
-
import { newFakeFolderApp } from './fakefolderapp.js'
|
|
3
2
|
import { newFakeDriveFile } from './fakedrivefile.js'
|
|
3
|
+
import { newFakeFolderApp } from './fakefolderapp.js'
|
|
4
4
|
import { notYetImplemented, isFolder } from '../../support/helpers.js'
|
|
5
5
|
import { Proxies } from '../../support/proxies.js'
|
|
6
6
|
/**
|
|
@@ -12,7 +12,6 @@ export class FakeDriveApp {
|
|
|
12
12
|
|
|
13
13
|
constructor() {
|
|
14
14
|
const rf = Drive.Files.get('root', {}, { allow404: true })
|
|
15
|
-
// because the parent folder prop isnt returned we'll spoof it
|
|
16
15
|
this.__rootFolder = newFakeDriveFolder(rf)
|
|
17
16
|
this.folderApp = newFakeFolderApp()
|
|
18
17
|
this.__settleClass = (file) => isFolder(file) ? newFakeDriveFolder(file) : newFakeDriveFile(file)
|
|
@@ -1,9 +1,12 @@
|
|
|
1
1
|
import { FakeDriveMeta } from "./fakedrivemeta.js"
|
|
2
2
|
import { Proxies } from '../../support/proxies.js'
|
|
3
|
-
import { isFolder } from '../../support/helpers.js'
|
|
3
|
+
import { isFolder, isFakeFolder, argsMatchThrow } from '../../support/helpers.js'
|
|
4
4
|
import { Syncit } from "../../support/syncit.js"
|
|
5
5
|
import { FakeDriveFolder } from "./fakedrivefolder.js"
|
|
6
6
|
import { Utils } from "../../support/utils.js"
|
|
7
|
+
import { settleAsBlob } from "../utilities/fakeblob.js"
|
|
8
|
+
import { improveFileCache } from "../../support/filecache.js"
|
|
9
|
+
|
|
7
10
|
const { is } = Utils
|
|
8
11
|
|
|
9
12
|
/**
|
|
@@ -65,48 +68,75 @@ class FakeDriveFile extends FakeDriveMeta {
|
|
|
65
68
|
return this.__getDecorated("webContentLink")
|
|
66
69
|
}
|
|
67
70
|
|
|
71
|
+
/**
|
|
72
|
+
* set the content to something else
|
|
73
|
+
* @param {string} content apparently this can only be a string and not a blob
|
|
74
|
+
* @return {FakeDriveFile} self
|
|
75
|
+
*/
|
|
76
|
+
setContent(content) {
|
|
77
|
+
// for param checking
|
|
78
|
+
const matchThrow = () => argsMatchThrow(Array.from(arguments))
|
|
79
|
+
// apps script does a toString on the arg rather than failing
|
|
80
|
+
if (!is.function (content?.toString)) {
|
|
81
|
+
matchThrow()
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
// this remains its current mimetype even though its now text
|
|
85
|
+
const blob = settleAsBlob(content.toString(), this.getMimeType(), this.getName())
|
|
86
|
+
const data = Drive.Files.update({}, this.getId(), blob)
|
|
87
|
+
|
|
88
|
+
// merge this with already known fields and improve cache
|
|
89
|
+
this.meta = {...this.meta, ...data}
|
|
90
|
+
improveFileCache(this.getId(), this.meta)
|
|
91
|
+
return this
|
|
92
|
+
|
|
93
|
+
}
|
|
94
|
+
|
|
68
95
|
/**
|
|
69
96
|
* make a copy
|
|
70
97
|
* @param {FakeDriveFolder|string|null} [destinationOrName] where to copy it to/chaneg the name if required
|
|
71
98
|
* @param {FakeDriveFolder} [destination] where to copy it to
|
|
72
99
|
*/
|
|
73
100
|
makeCopy(destinationOrName, destination) {
|
|
74
|
-
|
|
101
|
+
|
|
75
102
|
// default is same name as copied file
|
|
76
103
|
let name = this.__getDecorated("name")
|
|
77
104
|
|
|
78
105
|
// default is the parent of the file to be copied
|
|
79
106
|
let parents = this.__getDecorated("parents")
|
|
80
|
-
const nargs = arguments.length
|
|
81
107
|
|
|
82
108
|
// for param checking
|
|
83
|
-
const
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
}
|
|
109
|
+
const matchThrow = () => argsMatchThrow(Array.from(arguments))
|
|
110
|
+
|
|
111
|
+
|
|
112
|
+
// cant move the root folder
|
|
113
|
+
this.__preventRootDamage("copy", this)
|
|
89
114
|
|
|
90
115
|
// check args make sense
|
|
91
116
|
if (Utils.isNU(destination) && Utils.isNU(destinationOrName)) {
|
|
92
117
|
// makecopy()
|
|
93
118
|
// no args provided, we use the defaults
|
|
94
|
-
|
|
119
|
+
|
|
120
|
+
} else if (isFakeFolder(destinationOrName)) {
|
|
95
121
|
// makecopy (afolder)
|
|
96
122
|
// destination is a folder, so no 2nd arg required
|
|
97
123
|
parents = [destinationOrName.__getDecorated("id")]
|
|
98
124
|
if (!Utils.isNU(destination)) matchThrow()
|
|
125
|
+
|
|
99
126
|
} else if (!is.nonEmptyString(destinationOrName)) {
|
|
100
127
|
// makecopy (notastring,...)
|
|
101
128
|
// they tried to give a name but its not a string
|
|
102
129
|
matchThrow()
|
|
103
|
-
|
|
130
|
+
|
|
131
|
+
} else if (isFakeFolder(destination)) {
|
|
104
132
|
// makecopy (a string,a folder)
|
|
105
133
|
name = destinationOrName
|
|
106
134
|
parents = [destination.__getDecorated("id")]
|
|
135
|
+
|
|
107
136
|
} else if (Utils.isNU(destination)) {
|
|
108
137
|
// makecopy (string)
|
|
109
138
|
name = destinationOrName
|
|
139
|
+
|
|
110
140
|
} else {
|
|
111
141
|
// makecopy (string, notafolder)
|
|
112
142
|
matchThrow()
|
|
@@ -72,7 +72,7 @@ export const getFilesIterator = ({
|
|
|
72
72
|
assert.boolean(folderTypes)
|
|
73
73
|
assert.boolean(fileTypes)
|
|
74
74
|
|
|
75
|
-
// DriveApp doesnt give option to specify these so this will be
|
|
75
|
+
// DriveApp doesnt give option to specify these so this will be fixed
|
|
76
76
|
const fields = `files(${minFields}),nextPageToken`
|
|
77
77
|
|
|
78
78
|
/**
|
|
@@ -97,6 +97,8 @@ export const getFilesIterator = ({
|
|
|
97
97
|
|
|
98
98
|
|
|
99
99
|
// format the results into the folder or file object
|
|
100
|
+
assert.array (data.files)
|
|
101
|
+
assert.function (DriveApp.__settleClass)
|
|
100
102
|
tank = data.files.map(DriveApp.__settleClass)
|
|
101
103
|
|
|
102
104
|
}
|
|
@@ -130,12 +132,16 @@ export const getParentsIterator = ({
|
|
|
130
132
|
}) => {
|
|
131
133
|
const { assert } = Utils
|
|
132
134
|
assert.object(file)
|
|
133
|
-
|
|
135
|
+
// if its rott folder can be null
|
|
136
|
+
const parents = is.null (file.parents) ? [] : file.parents
|
|
137
|
+
assert.array(parents)
|
|
134
138
|
|
|
135
139
|
function* filesink() {
|
|
136
|
-
// the result tank, we just get them all by id
|
|
137
|
-
|
|
140
|
+
// the result tank, we just get them all by id - will return the usual minfields
|
|
141
|
+
// and will also stick them in cache
|
|
142
|
+
let tank = parents.map(id => Drive.Files.get(id, {}, { allow404: false }))
|
|
138
143
|
|
|
144
|
+
// let them out, 1 at a time
|
|
139
145
|
while (tank.length) {
|
|
140
146
|
yield DriveApp.__settleClass(tank.splice(0, 1)[0])
|
|
141
147
|
}
|
|
@@ -186,6 +192,7 @@ const fileLister = ({
|
|
|
186
192
|
params.pageToken = pageToken
|
|
187
193
|
}
|
|
188
194
|
|
|
195
|
+
|
|
189
196
|
// this will have be synced from async
|
|
190
197
|
try {
|
|
191
198
|
const result = Drive.Files.list(params)
|
|
@@ -9,12 +9,15 @@
|
|
|
9
9
|
*/
|
|
10
10
|
|
|
11
11
|
import is from '@sindresorhus/is';
|
|
12
|
-
import { notYetImplemented } from '../../support/helpers.js'
|
|
13
|
-
import { getParentsIterator, getPermissionIterator} from './fakedriveiterators.js';
|
|
12
|
+
import { isFolder, notYetImplemented, argsMatchThrow, isFakeFolder, minFields } from '../../support/helpers.js'
|
|
13
|
+
import { getParentsIterator, getPermissionIterator } from './fakedriveiterators.js';
|
|
14
14
|
import { newFakeUser } from '../session/fakeuser.js';
|
|
15
|
+
import { improveFileCache } from "../../support/filecache.js"
|
|
16
|
+
|
|
15
17
|
|
|
16
18
|
/**
|
|
17
19
|
* basic fake File meta data
|
|
20
|
+
* these are shared between folders and files
|
|
18
21
|
* @class FakeDriveMeta
|
|
19
22
|
* @returns {FakeDriveMeta}
|
|
20
23
|
*/
|
|
@@ -27,9 +30,19 @@ export class FakeDriveMeta {
|
|
|
27
30
|
*/
|
|
28
31
|
constructor(meta) {
|
|
29
32
|
this.meta = meta
|
|
30
|
-
|
|
33
|
+
this.__gas_fake_service = "DriveApp"
|
|
31
34
|
}
|
|
32
35
|
|
|
36
|
+
__preventRootDamage = (operation) => {
|
|
37
|
+
if (this.__isRoot) {
|
|
38
|
+
console.log (`Can't do ${operation} on root folder`)
|
|
39
|
+
throw new Error("Access denied: DriveApp")
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
get __isRoot () {
|
|
43
|
+
const parents = this.__getDecorated("parents")
|
|
44
|
+
return is.null (parents)
|
|
45
|
+
}
|
|
33
46
|
/**
|
|
34
47
|
* for enhancing the file with fields not retrieved by default
|
|
35
48
|
* @param {string} fields='' the required fields
|
|
@@ -41,7 +54,6 @@ export class FakeDriveMeta {
|
|
|
41
54
|
throw new Error('decorate fields was not a non empty string')
|
|
42
55
|
}
|
|
43
56
|
const sf = fields.split(",")
|
|
44
|
-
|
|
45
57
|
if (sf.every(f => Reflect.has(this.meta, f))) {
|
|
46
58
|
return this
|
|
47
59
|
}
|
|
@@ -49,9 +61,17 @@ export class FakeDriveMeta {
|
|
|
49
61
|
const newMeta = Drive.Files.get(this.getId(), { fields }, { allow404: false })
|
|
50
62
|
// need to merge this with already known fields
|
|
51
63
|
this.meta = { ...this.meta, ...newMeta }
|
|
64
|
+
improveFileCache (this.getId(), this.meta, fields)
|
|
52
65
|
return this
|
|
53
66
|
}
|
|
54
67
|
|
|
68
|
+
/**
|
|
69
|
+
* this will return the type DriveApp.Folder or DriveApp.File
|
|
70
|
+
* return {string}
|
|
71
|
+
*/
|
|
72
|
+
__getFakeType() {
|
|
73
|
+
return isFolder(this.meta) ? "DriveApp.Folder" : "DriveApp.File"
|
|
74
|
+
}
|
|
55
75
|
/**
|
|
56
76
|
* the meta data for the following fields are not fetched by default
|
|
57
77
|
*/
|
|
@@ -59,6 +79,33 @@ export class FakeDriveMeta {
|
|
|
59
79
|
return this.__decorateWithFields(prop).meta[prop]
|
|
60
80
|
}
|
|
61
81
|
|
|
82
|
+
/**
|
|
83
|
+
* __updateMeta - used to set simple felds using update
|
|
84
|
+
* @param {string} prop the meta data to set
|
|
85
|
+
* @param {*} value what to set it to
|
|
86
|
+
* @param {string} type whhat type it should be
|
|
87
|
+
* @param {object} args array like item with what was passed to the original function
|
|
88
|
+
* @returns this self
|
|
89
|
+
*/
|
|
90
|
+
__updateMeta(prop, value, type, ...args) {
|
|
91
|
+
|
|
92
|
+
// cant update any meta on root folder
|
|
93
|
+
this.__preventRootDamage (`set ${prop}`)
|
|
94
|
+
|
|
95
|
+
const matchThrow = () => argsMatchThrow(args)
|
|
96
|
+
if (!is[type](value)) {
|
|
97
|
+
matchThrow()
|
|
98
|
+
}
|
|
99
|
+
const file = {}
|
|
100
|
+
file[prop] = value
|
|
101
|
+
|
|
102
|
+
const data = Drive.Files.update(file, this.getId(), null, prop)
|
|
103
|
+
this.meta = {...this.meta, ...data}
|
|
104
|
+
improveFileCache (this.getId(), data)
|
|
105
|
+
|
|
106
|
+
return this
|
|
107
|
+
}
|
|
108
|
+
|
|
62
109
|
// shared between folder and file
|
|
63
110
|
toString() {
|
|
64
111
|
return this.getName()
|
|
@@ -144,34 +191,116 @@ export class FakeDriveMeta {
|
|
|
144
191
|
return getSharers(this.getId(), 'writer')
|
|
145
192
|
}
|
|
146
193
|
|
|
194
|
+
/**
|
|
195
|
+
* get the file url
|
|
196
|
+
* @returns {string} the webviewlink
|
|
197
|
+
*/
|
|
147
198
|
getUrl() {
|
|
148
199
|
return this.__getDecorated("webViewLink")
|
|
149
200
|
}
|
|
150
201
|
|
|
202
|
+
/**
|
|
203
|
+
* moves a file to a mew destination
|
|
204
|
+
* @param {FakeDriveFolder} destination
|
|
205
|
+
* @returns self for chaining
|
|
206
|
+
*/
|
|
207
|
+
moveTo(destination) {
|
|
208
|
+
// prepare for any arg errors
|
|
209
|
+
const matchThrow = () => argsMatchThrow(Array.from(arguments))
|
|
210
|
+
if (!isFakeFolder(destination)) {
|
|
211
|
+
matchThrow()
|
|
212
|
+
}
|
|
213
|
+
// pick up parents for destination if not already known
|
|
214
|
+
const newParent = destination.getId()
|
|
215
|
+
if (!is.nonEmptyString(newParent)) {
|
|
216
|
+
throw new Error (`expected to find destination id as a string but got ${newParent}`)
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
// cant move the root folder
|
|
220
|
+
this.__preventRootDamage("move")
|
|
151
221
|
|
|
222
|
+
// we cant just fix the resource parents, we have to add and remove
|
|
223
|
+
// so that's a 2 step
|
|
224
|
+
const params = {
|
|
225
|
+
addParents: newParent,
|
|
226
|
+
removeParents: this.__getDecorated("parents")[0]
|
|
227
|
+
}
|
|
152
228
|
|
|
153
|
-
|
|
229
|
+
// need to make sure we get the new parents field back to improve cache with
|
|
230
|
+
const data = Drive.Files.update({}, this.getId(), null, "parents", params)
|
|
231
|
+
|
|
232
|
+
// merge this with already known fields and improve cache
|
|
233
|
+
this.meta = { ...this.meta, ...data }
|
|
234
|
+
|
|
235
|
+
improveFileCache(this.getId(), this.meta)
|
|
236
|
+
return this
|
|
237
|
+
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
/**
|
|
241
|
+
* @param {string} value the updated value
|
|
242
|
+
* @returns {FakeDriveFile|FakeDriveFolder} this self
|
|
243
|
+
*/
|
|
244
|
+
setDescription(value) {
|
|
245
|
+
return this.__updateMeta("description", value, "string", arguments)
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
/**
|
|
249
|
+
* @param {string} value the updated value
|
|
250
|
+
* @returns {FakeDriveFile|FakeDriveFolder} this self
|
|
251
|
+
*/
|
|
252
|
+
setName(value) {
|
|
253
|
+
return this.__updateMeta("name", value, "string", arguments)
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
/**
|
|
257
|
+
* Sets whether users with edit permissions to the Folder are allowed to share with other users or change the permissions.
|
|
258
|
+
* @param {boolean} value the updated value
|
|
259
|
+
* @returns {FakeDriveFile|FakeDriveFolder} this self
|
|
260
|
+
*/
|
|
261
|
+
setShareableByEditors(value) {
|
|
262
|
+
return this.__updateMeta("writersCanShare", value, "boolean", arguments)
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
/**
|
|
266
|
+
* Determines whether users with edit permissions to the Folder/File are allowed to share with other users or change the permissions
|
|
267
|
+
* @returns {Boolean}
|
|
268
|
+
*/
|
|
269
|
+
isShareableByEditors() {
|
|
270
|
+
return this.__getDecorated("writersCanShare")
|
|
271
|
+
}
|
|
154
272
|
|
|
155
|
-
|
|
156
|
-
|
|
273
|
+
/**
|
|
274
|
+
* Sets whether the Folder/File is starred in the user's Drive.
|
|
275
|
+
* @param {boolean} value the updated value
|
|
276
|
+
* @returns {FakeDriveFile|FakeDriveFolder} this self
|
|
277
|
+
*/
|
|
278
|
+
setStarred(value) {
|
|
279
|
+
return this.__updateMeta("starred", value, "boolean", arguments)
|
|
157
280
|
}
|
|
158
281
|
|
|
282
|
+
/**
|
|
283
|
+
* Whether the file has been trashed, either explicitly or from a trashed parent folder
|
|
284
|
+
* @param {boolean} value the updated value
|
|
285
|
+
* @returns {FakeDriveFile|FakeDriveFolder} this self
|
|
286
|
+
*/
|
|
287
|
+
setTrashed(value) {
|
|
288
|
+
this.__preventRootDamage("trash")
|
|
289
|
+
return this.__updateMeta("trashed", value, "boolean", arguments)
|
|
290
|
+
}
|
|
159
291
|
|
|
292
|
+
// TODO-----------
|
|
160
293
|
|
|
161
294
|
getSharingPermission() {
|
|
162
295
|
return notYetImplemented('getSharingPermission')
|
|
163
296
|
}
|
|
164
|
-
|
|
165
|
-
return notYetImplemented('setTrashed')
|
|
166
|
-
}
|
|
297
|
+
|
|
167
298
|
|
|
168
299
|
getSharingAccess() {
|
|
169
300
|
return notYetImplemented('getSharingAccess')
|
|
170
301
|
}
|
|
171
302
|
|
|
172
|
-
|
|
173
|
-
return notYetImplemented('setStarred')
|
|
174
|
-
}
|
|
303
|
+
|
|
175
304
|
getResourceKey() {
|
|
176
305
|
return notYetImplemented('getResourceKey')
|
|
177
306
|
}
|
|
@@ -191,24 +320,13 @@ export class FakeDriveMeta {
|
|
|
191
320
|
getAccess() {
|
|
192
321
|
return notYetImplemented('getAccess')
|
|
193
322
|
}
|
|
194
|
-
moveTo() {
|
|
195
|
-
return notYetImplemented('moveTo')
|
|
196
|
-
}
|
|
197
|
-
|
|
198
|
-
setName() {
|
|
199
|
-
return notYetImplemented('setName')
|
|
200
|
-
}
|
|
201
323
|
|
|
202
324
|
|
|
203
325
|
revokePermissions() {
|
|
204
326
|
return notYetImplemented('revokePermissions')
|
|
205
327
|
}
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
}
|
|
209
|
-
setShareableByEditors() {
|
|
210
|
-
return notYetImplemented('setShareableByEditors')
|
|
211
|
-
}
|
|
328
|
+
|
|
329
|
+
|
|
212
330
|
setOwner() {
|
|
213
331
|
return notYetImplemented('setOwner')
|
|
214
332
|
}
|
|
@@ -67,7 +67,7 @@ class FakeUtilities {
|
|
|
67
67
|
* @returns {FakeBlob[]}
|
|
68
68
|
*/
|
|
69
69
|
unzip (blob) {
|
|
70
|
-
const unzipped = Syncit.
|
|
70
|
+
const unzipped = Syncit.fxUnzipper ({blob})
|
|
71
71
|
// the content type is lost in a zipped file, same as Apps Script behavior - which seems to be to use the extension to reassert content type
|
|
72
72
|
return unzipped.map (f=> newFakeBlob (f.bytes, null, f.name)).map(f=>f.setContentTypeFromExtension())
|
|
73
73
|
}
|