@mcpher/gas-fakes 1.0.18 → 1.0.19
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/.aiexclude +6 -0
- package/fakeadvdocuments.js +0 -0
- package/gasmess/bruce/pbx.js +58 -8
- package/gasmess/bruce/sorting.js +106 -0
- package/ghissues/issue-formapp-create-title-inconsistency.sh +51 -0
- package/ghissues/protection-editors-issue.sh +33 -0
- package/package.json +5 -2
- package/regenerate-progress-reports.sh +38 -0
- package/src/index.js +3 -1
- package/src/services/advforms/app.js +31 -0
- package/src/services/advforms/fakeadvforms.js +221 -0
- package/src/services/advforms/fakeadvformsform.js +80 -0
- package/src/services/advforms/formsapis.js +15 -0
- package/src/services/documentapp/appenderhelpers.js +32 -2
- package/src/services/documentapp/elementblasters.js +78 -2
- package/src/services/documentapp/elementhelpers.js +106 -1
- package/src/services/documentapp/elementoptions.js +98 -17
- package/src/services/documentapp/fakebody.js +244 -1
- package/src/services/documentapp/fakedocument.js +55 -4
- package/src/services/documentapp/fakedocumentapp.js +14 -5
- package/src/services/documentapp/fakelistitem.js +72 -1
- package/src/services/documentapp/fakeparagraph.js +101 -63
- package/src/services/documentapp/shadowdocument.js +27 -33
- package/src/services/documentapp/stylehelpers.js +1 -0
- package/src/services/enums/docsenums.js +2 -1
- package/src/services/enums/formsenums.js +62 -0
- package/src/services/formapp/app.js +44 -0
- package/src/services/formapp/fakecheckboxitem.js +142 -0
- package/src/services/formapp/fakechoice.js +45 -0
- package/src/services/formapp/fakeform.js +167 -0
- package/src/services/formapp/fakeformapp.js +107 -0
- package/src/services/formapp/fakeformitem.js +218 -0
- package/src/services/formapp/formitemregistry.js +23 -0
- package/src/services/formapp/formitems.js +6 -0
- package/src/services/scriptapp/app.js +2 -1
- package/src/services/scriptapp/behavior.js +2 -1
- package/src/services/spreadsheetapp/fakeprotection.js +39 -38
- package/src/services/spreadsheetapp/fakesheet.js +2 -4
- package/src/services/spreadsheetapp/fakesheetrange.js +0 -1
- package/src/support/formscacher.js +7 -0
- package/src/support/helpers.js +1 -3
- package/src/support/sxdocs.js +3 -3
- package/src/support/sxforms.js +50 -0
- package/src/support/syncit.js +4 -0
- package/src/support/workersync/sxfunctions.js +1 -0
- /package/src/services/{session → base}/app.js +0 -0
- /package/src/services/{session → base}/fakesession.js +0 -0
package/.aiexclude
ADDED
|
File without changes
|
package/gasmess/bruce/pbx.js
CHANGED
|
@@ -2,8 +2,58 @@ import '../../main.js';
|
|
|
2
2
|
import { moveToTempFolder, deleteTempFile } from '../tempfolder.js';
|
|
3
3
|
import { report, scl } from './dreport.js';
|
|
4
4
|
|
|
5
|
+
|
|
6
|
+
|
|
5
7
|
const suffix = "-bruce"
|
|
6
8
|
|
|
9
|
+
const tx2 = () => {
|
|
10
|
+
let doc = DocumentApp.create("abc")
|
|
11
|
+
const id = doc.getId()
|
|
12
|
+
moveToTempFolder(id, suffix)
|
|
13
|
+
|
|
14
|
+
let body = doc.getBody()
|
|
15
|
+
|
|
16
|
+
const p1 = body.appendParagraph("p1")
|
|
17
|
+
doc = scl(doc)
|
|
18
|
+
let d = Docs.Documents.get(id)
|
|
19
|
+
console.log(JSON.stringify(d))
|
|
20
|
+
|
|
21
|
+
const attributesToSet = {
|
|
22
|
+
[DocumentApp.Attribute.HORIZONTAL_ALIGNMENT]: DocumentApp.HorizontalAlignment.CENTER,
|
|
23
|
+
[DocumentApp.Attribute.ITALIC]: true,
|
|
24
|
+
[DocumentApp.Attribute.FONT_FAMILY]: 'Comic Sans MS'
|
|
25
|
+
};
|
|
26
|
+
|
|
27
|
+
body = doc.getBody()
|
|
28
|
+
body.setAttributes(attributesToSet);
|
|
29
|
+
doc = scl(doc)
|
|
30
|
+
d = Docs.Documents.get(id)
|
|
31
|
+
console.log(JSON.stringify(d))
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
deleteTempFile(id)
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
tx2()
|
|
38
|
+
const tx1 = () => {
|
|
39
|
+
let doc = DocumentApp.create("abc")
|
|
40
|
+
const id = doc.getId()
|
|
41
|
+
moveToTempFolder(id, suffix)
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
const body = doc.getBody();
|
|
46
|
+
const paraText = "p1";
|
|
47
|
+
|
|
48
|
+
const appendedPara = body.appendParagraph(paraText);
|
|
49
|
+
const paraToTest = body.getChild(1);
|
|
50
|
+
doc = scl(doc)
|
|
51
|
+
console.log(report(Docs.Documents.get(id, { includeTabsContent: true }), `\n1.empty document`))
|
|
52
|
+
const attributes = paraToTest.getAttributes();
|
|
53
|
+
|
|
54
|
+
deleteTempFile(id)
|
|
55
|
+
}
|
|
56
|
+
tx1()
|
|
7
57
|
const tabsa = () => {
|
|
8
58
|
|
|
9
59
|
let doc = DocumentApp.create("abc")
|
|
@@ -11,11 +61,11 @@ const tabsa = () => {
|
|
|
11
61
|
moveToTempFolder(id, suffix)
|
|
12
62
|
|
|
13
63
|
let body = doc.getBody()
|
|
14
|
-
console.log
|
|
64
|
+
console.log(body)
|
|
15
65
|
doc = scl(doc)
|
|
16
|
-
console.log(report(Docs.Documents.get(id, {includeTabsContent: true}), `\n1.empty document`))
|
|
66
|
+
console.log(report(Docs.Documents.get(id, { includeTabsContent: true }), `\n1.empty document`))
|
|
67
|
+
|
|
17
68
|
|
|
18
|
-
|
|
19
69
|
deleteTempFile(id)
|
|
20
70
|
}
|
|
21
71
|
|
|
@@ -70,7 +120,7 @@ const pbnew = () => {
|
|
|
70
120
|
console.log(report(Docs.Documents.get(id), `\n4.inserted para 2`))
|
|
71
121
|
|
|
72
122
|
body = doc.getBody()
|
|
73
|
-
body.insertTable(1, [['eboo', 'fbar']]
|
|
123
|
+
body.insertTable(1, [['eboo', 'fbar']])
|
|
74
124
|
doc = scl(doc)
|
|
75
125
|
console.log(report(Docs.Documents.get(id), `\nt4.inserted table at child 1`))
|
|
76
126
|
|
|
@@ -81,12 +131,12 @@ const pbnew = () => {
|
|
|
81
131
|
|
|
82
132
|
body = doc.getBody()
|
|
83
133
|
let c = body.getChild(2)
|
|
84
|
-
c.appendText(
|
|
134
|
+
c.appendText('p2 appended text')
|
|
85
135
|
doc = scl(doc)
|
|
86
136
|
console.log(report(Docs.Documents.get(id), `\n6.appended text to para 2`))
|
|
87
137
|
|
|
88
138
|
body = doc.getBody()
|
|
89
|
-
body.insertTable(3, [['bar', 'foo'], ['barfoo', 'foobar'], ['eboo', 'fbar']]
|
|
139
|
+
body.insertTable(3, [['bar', 'foo'], ['barfoo', 'foobar'], ['eboo', 'fbar']])
|
|
90
140
|
doc = scl(doc)
|
|
91
141
|
console.log(report(Docs.Documents.get(id), `\nt6.inserted table at child 3`))
|
|
92
142
|
|
|
@@ -107,7 +157,7 @@ const pbnew = () => {
|
|
|
107
157
|
console.log(report(Docs.Documents.get(id), `\n9.inserted page break in para 3`))
|
|
108
158
|
|
|
109
159
|
body = doc.getBody()
|
|
110
|
-
c=body.getChild(3)
|
|
160
|
+
c = body.getChild(3)
|
|
111
161
|
c.appendText('after pagebreak in 3')
|
|
112
162
|
doc = scl(doc)
|
|
113
163
|
console.log(report(Docs.Documents.get(id), `\n10.inserted text after pagebreak in 3`))
|
|
@@ -120,7 +170,7 @@ const pbnew = () => {
|
|
|
120
170
|
deleteTempFile(id)
|
|
121
171
|
}
|
|
122
172
|
|
|
123
|
-
|
|
173
|
+
|
|
124
174
|
|
|
125
175
|
|
|
126
176
|
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
|
|
2
|
+
/**
|
|
3
|
+
* Compares two values using Google Sheets' ascending sort logic.
|
|
4
|
+
* @param {*} a first value to compare
|
|
5
|
+
* @param {*} b second value to compare
|
|
6
|
+
* @returns {number} a<b: -1, a===b: 0, a>b: 1
|
|
7
|
+
*/
|
|
8
|
+
const compareMixedValues = (a, b) => {
|
|
9
|
+
const isBlankA = (a === null || a === undefined || a === "");
|
|
10
|
+
const isBlankB = (b === null || b === undefined || b === "");
|
|
11
|
+
|
|
12
|
+
// Handle blanks - always sorted to the top in this ascending comparator
|
|
13
|
+
if (isBlankA && !isBlankB) return -1;
|
|
14
|
+
if (!isBlankA && isBlankB) return 1;
|
|
15
|
+
if (isBlankA && isBlankB) return 0;
|
|
16
|
+
|
|
17
|
+
// Get type priorities (lower number = sorts first)
|
|
18
|
+
const getTypePriority = (val) => {
|
|
19
|
+
// Correct Google Sheets ascending priority
|
|
20
|
+
if (typeof val === 'number') return 1;
|
|
21
|
+
if (typeof val === 'string') return 2;
|
|
22
|
+
if (typeof val === 'boolean') return 3;
|
|
23
|
+
if (val instanceof Date) return 4;
|
|
24
|
+
return 5;
|
|
25
|
+
};
|
|
26
|
+
|
|
27
|
+
const priorityA = getTypePriority(a);
|
|
28
|
+
const priorityB = getTypePriority(b);
|
|
29
|
+
|
|
30
|
+
// If different types, sort by type priority
|
|
31
|
+
if (priorityA !== priorityB) {
|
|
32
|
+
return priorityA < priorityB ? -1 : 1;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
// Same types - compare values
|
|
36
|
+
if (typeof a === 'number' && typeof b === 'number') {
|
|
37
|
+
return a - b;
|
|
38
|
+
}
|
|
39
|
+
if (typeof a === 'string' && typeof b === 'string') {
|
|
40
|
+
return a.localeCompare(b, undefined, { sensitivity: 'base', numeric: true });
|
|
41
|
+
}
|
|
42
|
+
if (typeof a === 'boolean' && typeof b === 'boolean') {
|
|
43
|
+
return (a === b) ? 0 : (a ? 1 : -1);
|
|
44
|
+
}
|
|
45
|
+
if (a instanceof Date && b instanceof Date) {
|
|
46
|
+
return a.getTime() - b.getTime();
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
// Fallback for other types
|
|
50
|
+
const strA = String(a);
|
|
51
|
+
const strB = String(b);
|
|
52
|
+
return strA.localeCompare(strB, undefined, { sensitivity: 'base' });
|
|
53
|
+
};
|
|
54
|
+
// ... your sort2d function remains correct, just pass the 'ascending' flag to compareMixedValues.
|
|
55
|
+
export const sort2d = (spec, arr) => {
|
|
56
|
+
const deepCopy = arr.map(row => [...row]);
|
|
57
|
+
return deepCopy.sort((a, b) => {
|
|
58
|
+
for (const s of spec) {
|
|
59
|
+
const index = (typeof s === 'object' && s !== null) ? s.column - 1 : s - 1;
|
|
60
|
+
const ascending = (typeof s === 'object' && s !== null) ? s.ascending !== false : true;
|
|
61
|
+
let result = compareMixedValues(a[index], b[index]);
|
|
62
|
+
if (!ascending) {
|
|
63
|
+
result = -result;
|
|
64
|
+
}
|
|
65
|
+
if (result !== 0) {
|
|
66
|
+
return result;
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
return 0;
|
|
70
|
+
});
|
|
71
|
+
};
|
|
72
|
+
|
|
73
|
+
// Test function to verify behavior
|
|
74
|
+
export const testSorting = (spec) => {
|
|
75
|
+
const testData = [
|
|
76
|
+
[false, 120, 'cheese', 'peach', 'armpit', 10],
|
|
77
|
+
['kiwi', 'orange', 'apple', 'bub ble', 'bucket', 'armpit'],
|
|
78
|
+
['plum', 'butter', 'buckle', 'grape', 65, 65],
|
|
79
|
+
[false, "", 'armpit', 'orange', 21, 'buckle'],
|
|
80
|
+
['cherry', 'foo', 'butter', 21, 'red eye', 'foo'],
|
|
81
|
+
['kiwi', 77, false, 'banana', 'red eye', 'apple'],
|
|
82
|
+
['buckle', 'bar', 'foo', 21, 10, 'foo'],
|
|
83
|
+
['bub ble', 65, 'butter', 'buckle', 'bub ble', null],
|
|
84
|
+
['cheese', 'pear', 'pear', 77, 10, 'foo'],
|
|
85
|
+
[
|
|
86
|
+
'bub ble',
|
|
87
|
+
'armpit',
|
|
88
|
+
'melon',
|
|
89
|
+
'buckle',
|
|
90
|
+
'red eye',
|
|
91
|
+
3.141592653589793
|
|
92
|
+
],
|
|
93
|
+
['pear', false, 0.3963452962629548, 'foo', 'banana', 'plum'],
|
|
94
|
+
['cherry', true, 'apple', 'cheese', 'butter', 'peach']
|
|
95
|
+
]
|
|
96
|
+
|
|
97
|
+
console.log('Original:', testData);
|
|
98
|
+
|
|
99
|
+
// Sort by column 2 (booleans) ascending
|
|
100
|
+
const sorted = sort2d(spec, testData);
|
|
101
|
+
console.log('Sorted by column 2 (booleans ASC):', sorted);
|
|
102
|
+
|
|
103
|
+
return sorted;
|
|
104
|
+
};
|
|
105
|
+
const spec = [1,{column:2 , ascending:false}]
|
|
106
|
+
testSorting(spec)
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
|
|
3
|
+
# A shell script to create a GitHub issue for the FormApp.create() title inconsistency.
|
|
4
|
+
|
|
5
|
+
# Issue title
|
|
6
|
+
TITLE="Discrepancy between FormApp.create(title) and Forms API behavior regarding form title and file name"
|
|
7
|
+
|
|
8
|
+
# Issue body in markdown
|
|
9
|
+
BODY=$(cat <<'EOF'
|
|
10
|
+
### Summary
|
|
11
|
+
|
|
12
|
+
There is a significant and counter-intuitive discrepancy between the documented behavior of `FormApp.create(title)` in Google Apps Script and the constraints of the underlying Google Forms API. `FormApp.create(title)` sets the Google Drive *file name* to the provided `title`, but leaves the form's internal title (retrieved by `form.getTitle()`) blank.
|
|
13
|
+
|
|
14
|
+
This makes it impossible to accurately emulate the Apps Script behavior using the public Forms API, as the API requires a non-empty title for both creation and updates.
|
|
15
|
+
|
|
16
|
+
### Live Apps Script Behavior
|
|
17
|
+
|
|
18
|
+
When the following Apps Script code is executed:
|
|
19
|
+
|
|
20
|
+
```javascript
|
|
21
|
+
function testFormCreation() {
|
|
22
|
+
const formName = "foo-form";
|
|
23
|
+
const form = FormApp.create(formName);
|
|
24
|
+
const id = form.getId();
|
|
25
|
+
|
|
26
|
+
console.log('Internal form title:', form.getTitle()); // Logs: "" (an empty string)
|
|
27
|
+
|
|
28
|
+
const file = DriveApp.getFileById(id);
|
|
29
|
+
console.log('Drive file name:', file.getName()); // Logs: "foo-form"
|
|
30
|
+
|
|
31
|
+
// cleanup
|
|
32
|
+
file.setTrashed(true);
|
|
33
|
+
}
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
The output demonstrates that `FormApp.create()` uses the argument to set the Drive file name, but the form's own title property remains empty.
|
|
37
|
+
|
|
38
|
+
### The Problem with API Emulation
|
|
39
|
+
|
|
40
|
+
This behavior is problematic to replicate with the v1 Google Forms API because the `forms.create` method requires a non-empty `info.title`. Attempting to then `batchUpdate` the title to an empty string also fails, as the API rejects an empty title on update.
|
|
41
|
+
|
|
42
|
+
### Conclusion
|
|
43
|
+
|
|
44
|
+
This disconnect means it's impossible for developers using the public Forms API to programmatically create a form that exactly matches the state of one created by `FormApp.create()`. This poses a significant challenge for testing and emulation frameworks.
|
|
45
|
+
|
|
46
|
+
The expected behavior would be for `FormApp.create(title)` to set both the Drive file name and the internal form title, or for the API to allow setting the title to an empty string via an update.
|
|
47
|
+
EOF
|
|
48
|
+
)
|
|
49
|
+
|
|
50
|
+
# Create the issue using GitHub CLI
|
|
51
|
+
gh issue create --title "$TITLE" --body "$BODY" --label "bug" --label "emulation-accuracy" --label "form-app"
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
|
|
3
|
+
# A shell script to create a GitHub issue for the Protection.getEditors() behavior.
|
|
4
|
+
|
|
5
|
+
# Issue title
|
|
6
|
+
TITLE="Align Protection.getEditors() behavior with Live Apps Script"
|
|
7
|
+
|
|
8
|
+
# Issue body in markdown
|
|
9
|
+
BODY=$(cat <<'EOF'
|
|
10
|
+
## Summary
|
|
11
|
+
|
|
12
|
+
There is a discrepancy between the live Google Apps Script environment and the `gas-fakes` emulation regarding the return value of `Protection.getEditors()` on a newly created protection.
|
|
13
|
+
|
|
14
|
+
- **Live Apps Script:** `protection.getEditors().length` returns `0`.
|
|
15
|
+
- **`gas-fakes` (current):** `protection.getEditors().length` incorrectly returns `1`.
|
|
16
|
+
|
|
17
|
+
## Live Behavior Details
|
|
18
|
+
|
|
19
|
+
In the live environment, the owner of a spreadsheet can always edit a protected range or sheet (`protection.canEdit()` returns `true`). However, the owner is **not** automatically included in the explicit list of editors returned by `getEditors()`. This is confirmed by live testing.
|
|
20
|
+
|
|
21
|
+
## `gas-fakes` Behavior
|
|
22
|
+
|
|
23
|
+
The `gas-fakes` implementation in `fakeprotection.js` was incorrectly adding the current user (as the owner) to the list of editors upon creation of a new `Protection` object.
|
|
24
|
+
|
|
25
|
+
## TODO
|
|
26
|
+
|
|
27
|
+
1. Modify `fakeprotection.js` to no longer automatically add the owner to the `editors` list when a new protection is created. The `canEdit()` method should still return `true` for the owner.
|
|
28
|
+
2. Run the full test suite to ensure this change doesn't have unintended side effects and that the `Protection` tests now pass in both environments.
|
|
29
|
+
EOF
|
|
30
|
+
)
|
|
31
|
+
|
|
32
|
+
# Create the issue using GitHub CLI
|
|
33
|
+
gh issue create --title "$TITLE" --body "$BODY" --label "bug" --label "emulation-accuracy" --label "spreadsheet-app"
|
package/package.json
CHANGED
|
@@ -42,6 +42,8 @@
|
|
|
42
42
|
"testsheetsdata": "cp mainlocal.js main.js && node --env-file=.env ./test/testsheetsdata.js execute",
|
|
43
43
|
"testdocsadv": "cp mainlocal.js main.js && node --env-file=.env ./test/testdocsadv.js execute",
|
|
44
44
|
"testslidesadv": "cp mainlocal.js main.js && node --env-file=.env ./test/testslidesadv.js execute",
|
|
45
|
+
"testform": "cp mainlocal.js main.js && node --env-file=.env ./test/testform.js execute",
|
|
46
|
+
"testformsadv": "cp mainlocal.js main.js && node --env-file=.env ./test/testformsadv.js execute",
|
|
45
47
|
"testdocs": "cp mainlocal.js main.js && node --env-file=.env ./test/testdocs.js execute",
|
|
46
48
|
"testslides": "cp mainlocal.js main.js && node --env-file=.env ./test/testslides.js execute",
|
|
47
49
|
"testdocsnext": "cp mainlocal.js main.js && node --env-file=.env ./test/testdocsnext.js execute",
|
|
@@ -53,14 +55,15 @@
|
|
|
53
55
|
"testdocsfooters": "cp mainlocal.js main.js && node --env-file=.env ./test/testdocsfooters.js execute",
|
|
54
56
|
"testdocsfootnotes": "cp mainlocal.js main.js && node --env-file=.env ./test/testdocsfootnotes.js execute",
|
|
55
57
|
"testdocsimages": "cp mainlocal.js main.js && node --env-file=.env ./test/testdocsimages.js execute",
|
|
58
|
+
"testdocsstyles": "cp mainlocal.js main.js && node --env-file=.env ./test/testdocsstyles.js execute",
|
|
56
59
|
"testsandbox": "cp mainlocal.js main.js && node --env-file=.env ./test/testsandbox.js execute",
|
|
57
60
|
"pub": "cp mainlocal.js main.js && npm publish --access public"
|
|
58
61
|
},
|
|
59
62
|
"name": "@mcpher/gas-fakes",
|
|
60
|
-
"version": "1.0.
|
|
63
|
+
"version": "1.0.19",
|
|
61
64
|
"license": "MIT",
|
|
62
65
|
"main": "main.js",
|
|
63
66
|
"description": "A proof of concept implementation of Apps Script Environment on Node",
|
|
64
67
|
"repository": "github:brucemcpherson/gas-fakes",
|
|
65
68
|
"homepage": "https://ramblings.mcpher.com/a-proof-of-concept-implementation-of-apps-script-environment-on-node/"
|
|
66
|
-
}
|
|
69
|
+
}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
# This script contains the prompts to regenerate the progress reports for all services.
|
|
5
|
+
# Execute this script and paste the output to the Gemini agent.
|
|
6
|
+
|
|
7
|
+
echo "Generating progress report for Docs service..."
|
|
8
|
+
echo "now do https://developers.google.com/apps-script/reference/document into ./progress/Docs.MD"
|
|
9
|
+
|
|
10
|
+
echo "Generating progress report for Drive service..."
|
|
11
|
+
echo "now do https://developers.google.com/apps-script/reference/drive into ./progress/drive.MD"
|
|
12
|
+
|
|
13
|
+
echo "Generating progress report for Forms service..."
|
|
14
|
+
echo "now do https://developers.google.com/apps-script/reference/forms into ./progress/forms.MD"
|
|
15
|
+
|
|
16
|
+
echo "Generating progress report for Slides service..."
|
|
17
|
+
echo "now do https://developers.google.com/apps-script/reference/slides into ./progress/slides.MD"
|
|
18
|
+
|
|
19
|
+
echo "Generating progress report for Spreadsheet service..."
|
|
20
|
+
echo "now do https://developers.google.com/apps-script/reference/spreadsheet into ./progress/sheets.MD"
|
|
21
|
+
|
|
22
|
+
echo "Generating progress report for Utilities service..."
|
|
23
|
+
echo "now do https://developers.google.com/apps-script/reference/utilities into ./progress/utilities.MD"
|
|
24
|
+
|
|
25
|
+
echo "Generating progress report for UrlFetch service..."
|
|
26
|
+
echo "now do https://developers.google.com/apps-script/reference/url-fetch into ./progress/urlFetch.MD"
|
|
27
|
+
|
|
28
|
+
echo "Generating progress report for Cache service..."
|
|
29
|
+
echo "now do https://developers.google.com/apps-script/reference/cache into ./progress/cache.MD"
|
|
30
|
+
|
|
31
|
+
echo "Generating progress report for Properties service..."
|
|
32
|
+
echo "now do https://developers.google.com/apps-script/reference/properties into ./progress/properties.MD"
|
|
33
|
+
|
|
34
|
+
echo "Generating progress report for Script service..."
|
|
35
|
+
echo "now do https://developers.google.com/apps-script/reference/script into ./progress/script.MD"
|
|
36
|
+
|
|
37
|
+
echo "Generating progress report for Base service..."
|
|
38
|
+
echo "now do https://developers.google.com/apps-script/reference/base into ./progress/base.MD"
|
package/src/index.js
CHANGED
|
@@ -4,10 +4,12 @@ import './services/urlfetchapp/app.js'
|
|
|
4
4
|
import './services/utilities/app.js'
|
|
5
5
|
import './services/spreadsheetapp/app.js'
|
|
6
6
|
import './services/stores/app.js'
|
|
7
|
-
import './services/
|
|
7
|
+
import './services/base/app.js'
|
|
8
8
|
import './services/advdrive/app.js'
|
|
9
9
|
import './services/advsheets/app.js'
|
|
10
10
|
import './services/advdocs/app.js'
|
|
11
11
|
import './services/advslides/app.js'
|
|
12
12
|
import './services/documentapp/app.js'
|
|
13
|
+
import './services/advforms/app.js'
|
|
14
|
+
import './services/formapp/app.js'
|
|
13
15
|
import './services/slidesapp/app.js'
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
|
|
2
|
+
/**
|
|
3
|
+
* NOTE - Although Apps Script doesnt yet have a Forms advanced service
|
|
4
|
+
* we're going to funnel everything through here as if there was one.
|
|
5
|
+
*
|
|
6
|
+
*/
|
|
7
|
+
import { newFakeAdvForms } from './fakeadvforms.js'
|
|
8
|
+
import { Proxies } from '../../support/proxies.js'
|
|
9
|
+
|
|
10
|
+
// This will eventually hold a proxy for Formsapp
|
|
11
|
+
let _app = null
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* adds to global space to mimic Apps Script behavior
|
|
15
|
+
*/
|
|
16
|
+
const name = "Forms"
|
|
17
|
+
if (typeof globalThis[name] === typeof undefined) {
|
|
18
|
+
|
|
19
|
+
const getApp = () => {
|
|
20
|
+
// if it hasne been intialized yet then do that
|
|
21
|
+
if (!_app) {
|
|
22
|
+
console.log('...activating proxy for', name)
|
|
23
|
+
_app = newFakeAdvForms()
|
|
24
|
+
}
|
|
25
|
+
// this is the actual formsapp we'll return from the proxy
|
|
26
|
+
return _app
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
Proxies.registerProxy(name, getApp)
|
|
30
|
+
|
|
31
|
+
}
|
|
@@ -0,0 +1,221 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Advanced Forms service
|
|
3
|
+
*/
|
|
4
|
+
import { Proxies } from '../../support/proxies.js';
|
|
5
|
+
import { advClassMaker } from '../../support/helpers.js';
|
|
6
|
+
import { formsCacher } from '../../support/formscacher.js';
|
|
7
|
+
import { newFakeAdvFormsForm } from './fakeadvformsform.js';
|
|
8
|
+
|
|
9
|
+
class FakeAdvForms {
|
|
10
|
+
constructor() {
|
|
11
|
+
this.__fakeObjectType = "Forms"
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
const propLists = {
|
|
15
|
+
// Top-level batch update request
|
|
16
|
+
"newBatchUpdateFormRequest": [
|
|
17
|
+
"includeFormInResponse",
|
|
18
|
+
"requests",
|
|
19
|
+
"writeControl"
|
|
20
|
+
],
|
|
21
|
+
// Individual request types that can be part of a batch update
|
|
22
|
+
"newCreateItemRequest": [
|
|
23
|
+
"item",
|
|
24
|
+
"location"
|
|
25
|
+
],
|
|
26
|
+
"newDeleteItemRequest": [
|
|
27
|
+
"location"
|
|
28
|
+
],
|
|
29
|
+
"newMoveItemRequest": [
|
|
30
|
+
"newLocation",
|
|
31
|
+
"originalLocation"
|
|
32
|
+
],
|
|
33
|
+
"newUpdateFormInfoRequest": [
|
|
34
|
+
"info",
|
|
35
|
+
"updateMask"
|
|
36
|
+
],
|
|
37
|
+
"newUpdateItemRequest": [
|
|
38
|
+
"item",
|
|
39
|
+
"location",
|
|
40
|
+
"updateMask"
|
|
41
|
+
],
|
|
42
|
+
"newFormInfo": [
|
|
43
|
+
"title",
|
|
44
|
+
"documentTitle",
|
|
45
|
+
"description"
|
|
46
|
+
],
|
|
47
|
+
"newUpdateSettingsRequest": [
|
|
48
|
+
"settings",
|
|
49
|
+
"updateMask"
|
|
50
|
+
],
|
|
51
|
+
"newFormSettings": [
|
|
52
|
+
"emailCollectionType",
|
|
53
|
+
"quizSettings"
|
|
54
|
+
],
|
|
55
|
+
"newQuizSettings" : [
|
|
56
|
+
"isQuiz"
|
|
57
|
+
],
|
|
58
|
+
"newItem": [
|
|
59
|
+
"description",
|
|
60
|
+
"imageItem",
|
|
61
|
+
"item",
|
|
62
|
+
"itemId",
|
|
63
|
+
"pageBreakItem",
|
|
64
|
+
"questionGroupItem",
|
|
65
|
+
"questionItem",
|
|
66
|
+
"textItem",
|
|
67
|
+
"title",
|
|
68
|
+
"videoItem"
|
|
69
|
+
],
|
|
70
|
+
"newQuestionItem": [
|
|
71
|
+
"question",
|
|
72
|
+
"image"
|
|
73
|
+
],
|
|
74
|
+
"newQuestion": [
|
|
75
|
+
"choiceQuestion",
|
|
76
|
+
"dateQuestion",
|
|
77
|
+
"fileUploadQuestion",
|
|
78
|
+
"grading",
|
|
79
|
+
"questionId",
|
|
80
|
+
"ratingQuestion",
|
|
81
|
+
"required",
|
|
82
|
+
"rowQuestion",
|
|
83
|
+
"scaleQuestion",
|
|
84
|
+
"textQuestion",
|
|
85
|
+
"timeQuestion"
|
|
86
|
+
],
|
|
87
|
+
"newLocation": [
|
|
88
|
+
"index"
|
|
89
|
+
],
|
|
90
|
+
"newWriteControl": [
|
|
91
|
+
"requiredRevisionId",
|
|
92
|
+
"targetRevisionId"
|
|
93
|
+
],
|
|
94
|
+
// The 'Request' schema itself, which is a oneOf type for batch updates
|
|
95
|
+
"newRequest": [
|
|
96
|
+
"createItem",
|
|
97
|
+
"deleteItem",
|
|
98
|
+
"moveItem",
|
|
99
|
+
"updateFormInfo",
|
|
100
|
+
"updateItem",
|
|
101
|
+
"updateSettings"
|
|
102
|
+
],
|
|
103
|
+
// Further nested items that are builders
|
|
104
|
+
"newImageItem": [
|
|
105
|
+
"image"
|
|
106
|
+
],
|
|
107
|
+
"newPageBreakItem": [], // No direct properties to set
|
|
108
|
+
"newQuestionGroupItem": [
|
|
109
|
+
"grid",
|
|
110
|
+
"image",
|
|
111
|
+
"questions"
|
|
112
|
+
],
|
|
113
|
+
"newTextItem": [], // No direct properties to set
|
|
114
|
+
"newVideoItem": [
|
|
115
|
+
"caption",
|
|
116
|
+
"video"
|
|
117
|
+
],
|
|
118
|
+
"newImage": [
|
|
119
|
+
"altText",
|
|
120
|
+
"contentUri",
|
|
121
|
+
"properties",
|
|
122
|
+
"sourceUri"
|
|
123
|
+
],
|
|
124
|
+
"newMediaProperties": [
|
|
125
|
+
"alignment",
|
|
126
|
+
"width"
|
|
127
|
+
],
|
|
128
|
+
"newGrid": [
|
|
129
|
+
"columns",
|
|
130
|
+
"shuffleQuestions"
|
|
131
|
+
],
|
|
132
|
+
"newChoiceQuestion": [
|
|
133
|
+
"options",
|
|
134
|
+
"shuffle",
|
|
135
|
+
"type"
|
|
136
|
+
],
|
|
137
|
+
"newDateQuestion": [
|
|
138
|
+
"includeTime",
|
|
139
|
+
"includeYear"
|
|
140
|
+
],
|
|
141
|
+
"newFileUploadQuestion": [
|
|
142
|
+
"folderId",
|
|
143
|
+
"maxFiles",
|
|
144
|
+
"maxFileSize",
|
|
145
|
+
"types"
|
|
146
|
+
],
|
|
147
|
+
"newGrading": [
|
|
148
|
+
"correctAnswers",
|
|
149
|
+
"generalFeedback",
|
|
150
|
+
"pointValue",
|
|
151
|
+
"whenRight",
|
|
152
|
+
"whenWrong"
|
|
153
|
+
],
|
|
154
|
+
"newRatingQuestion": [
|
|
155
|
+
"iconType",
|
|
156
|
+
"ratingScaleLevel"
|
|
157
|
+
],
|
|
158
|
+
"newRowQuestion": [
|
|
159
|
+
"title"
|
|
160
|
+
],
|
|
161
|
+
"newScaleQuestion": [
|
|
162
|
+
"high",
|
|
163
|
+
"highLabel",
|
|
164
|
+
"low",
|
|
165
|
+
"lowLabel"
|
|
166
|
+
],
|
|
167
|
+
"newTextQuestion": [
|
|
168
|
+
"paragraph"
|
|
169
|
+
],
|
|
170
|
+
"newTimeQuestion": [
|
|
171
|
+
"duration"
|
|
172
|
+
],
|
|
173
|
+
"newCorrectAnswers": [
|
|
174
|
+
"answers"
|
|
175
|
+
],
|
|
176
|
+
"newCorrectAnswer": [
|
|
177
|
+
"value"
|
|
178
|
+
],
|
|
179
|
+
"newOption": [
|
|
180
|
+
"goToAction",
|
|
181
|
+
"goToSectionId",
|
|
182
|
+
"image",
|
|
183
|
+
"isOther",
|
|
184
|
+
"value"
|
|
185
|
+
],
|
|
186
|
+
"newVideo": [
|
|
187
|
+
"properties",
|
|
188
|
+
"youtubeUri"
|
|
189
|
+
],
|
|
190
|
+
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
Reflect.ownKeys(propLists).forEach(p => {
|
|
194
|
+
this[p] = () => advClassMaker(propLists[p])
|
|
195
|
+
})
|
|
196
|
+
|
|
197
|
+
}
|
|
198
|
+
toString() {
|
|
199
|
+
return 'AdvancedServiceIdentifier{name=forms, version=v1}'
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
getVersion() {
|
|
203
|
+
return 'v1'
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
get Form() {
|
|
207
|
+
return newFakeAdvFormsForm(this)
|
|
208
|
+
}
|
|
209
|
+
__addAllowed(id) {
|
|
210
|
+
if (ScriptApp.__behavior.sandBoxMode) {
|
|
211
|
+
ScriptApp.__behavior.addFile(id);
|
|
212
|
+
}
|
|
213
|
+
return id
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
__getFormsPerformance() {
|
|
217
|
+
return formsCacher.getPerformance()
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
export const newFakeAdvForms = (...args) => Proxies.guard(new FakeAdvForms(...args))
|