@mcpher/gas-fakes 1.0.14 → 1.0.16
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.RU.md +20 -1
- package/README.md +2 -1
- package/gasmess/bruce/d.js +4 -0
- package/gasmess/bruce/d1.js +4 -0
- package/gasmess/bruce/dreport.js +81 -0
- package/gasmess/bruce/pbx.js +33 -45
- package/gasmess/tanaike/for_simple_test.js +7 -1
- package/gasmess/tanaike/sample4.js +59 -0
- package/ghissues/issue-positioned-image.sh +25 -0
- package/ghissues/post-issue.sh +53 -0
- package/ghissues/sandbox-issue.sh +31 -0
- package/ghissues/setup-under-construction.sh +107 -0
- package/package.json +16 -7
- package/src/services/advdocs/docapis.js +10 -6
- package/src/services/advdocs/fakeadvdocs.js +1 -3
- package/src/services/advdocs/fakeadvdocuments.js +22 -8
- package/src/services/advdrive/drapis.js +10 -7
- package/src/services/advdrive/fakeadvdrive.js +19 -2
- package/src/services/advdrive/fakeadvdrivefiles.js +8 -5
- package/src/services/advdrive/fakeadvdrivepermissions.js +4 -4
- package/src/services/advsheets/fakeadvsheets.js +15 -5
- package/src/services/advsheets/fakeadvsheetsdevelopermetadata.js +3 -2
- package/src/services/advsheets/fakeadvsheetsspreadsheets.js +5 -6
- package/src/services/advsheets/fakeadvsheetsvalues.js +3 -2
- package/src/services/advsheets/shapis.js +18 -8
- package/src/services/advslides/fakeadvslides.js +20 -2
- package/src/services/advslides/slapis.js +10 -6
- package/src/services/documentapp/appenderhelpers.js +247 -56
- package/src/services/documentapp/elementblasters.js +61 -13
- package/src/services/documentapp/elementhelpers.js +50 -3
- package/src/services/documentapp/elementoptions.js +331 -47
- package/src/services/documentapp/elements.js +7 -0
- package/src/services/documentapp/fakebody.js +59 -8
- package/src/services/documentapp/fakebookmark.js +96 -0
- package/src/services/documentapp/fakecontainerelement.js +46 -12
- package/src/services/documentapp/fakedocument.js +186 -5
- package/src/services/documentapp/fakedocumentapp.js +3 -2
- package/src/services/documentapp/fakedocumenttab.js +10 -2
- package/src/services/documentapp/fakeelement.js +22 -13
- package/src/services/documentapp/fakefootersection.js +44 -0
- package/src/services/documentapp/fakefootnote.js +189 -0
- package/src/services/documentapp/fakefootnotereference.js +48 -0
- package/src/services/documentapp/fakefootnotesection.js +127 -0
- package/src/services/documentapp/fakeheadersection.js +44 -0
- package/src/services/documentapp/fakehorizontalrule.js +2 -2
- package/src/services/documentapp/fakeinlineimage.js +138 -0
- package/src/services/documentapp/fakelistitem.js +188 -0
- package/src/services/documentapp/fakepagebreak.js +2 -2
- package/src/services/documentapp/fakeparagraph.js +52 -10
- package/src/services/documentapp/fakeposition.js +84 -0
- package/src/services/documentapp/fakepositionedimage.js +86 -0
- package/src/services/documentapp/fakesectionelement.js +209 -0
- package/src/services/documentapp/faketable.js +28 -2
- package/src/services/documentapp/faketablecell.js +2 -2
- package/src/services/documentapp/faketablerow.js +2 -2
- package/src/services/documentapp/faketext.js +2 -2
- package/src/services/documentapp/fakeui.js +27 -0
- package/src/services/documentapp/nrhelpers.js +55 -20
- package/src/services/documentapp/shadowdocument.js +216 -55
- package/src/services/driveapp/driveiterators.js +8 -2
- package/src/services/driveapp/fakefolderapp.js +1 -0
- package/src/services/scriptapp/app.js +6 -6
- package/src/services/scriptapp/behavior.js +93 -0
- package/src/services/spreadsheetapp/fakenamedrange.js +120 -0
- package/src/services/spreadsheetapp/fakesheet.js +40 -2
- package/src/services/spreadsheetapp/fakespreadsheet.js +86 -3
- package/src/services/urlfetchapp/app.js +5 -5
- package/src/support/auth.js +14 -20
- package/src/support/backoff.js +52 -0
- package/src/support/helpers.js +10 -2
- package/src/support/sxauth.js +9 -11
- package/src/support/sxdocs.js +7 -9
- package/src/support/sxdrive.js +10 -14
- package/src/support/sxfetch.js +19 -5
- package/src/support/sxsheets.js +8 -8
- package/src/support/sxslides.js +8 -8
- package/src/support/sxstore.js +1 -1
- package/src/support/sxzip.js +1 -1
- package/src/support/syncit.js +5 -5
- package/src/support/utils.js +32 -4
- package/src/support/workersync/worker.js +5 -3
- package/src/services/advdrive/fakeadvvalues.js +0 -0
- package/src/services/documentapp/shadow.js +0 -121
- /package/{shadowhelpers.js → fakepositionedimage.js} +0 -0
package/README.RU.md
CHANGED
|
@@ -312,4 +312,23 @@ const getParentsIterator = ({
|
|
|
312
312
|
|
|
313
313
|
## Помощь
|
|
314
314
|
|
|
315
|
-
Как я уже упоминал ранее, чтобы развивать это дальше, мне понадобится много помощи для расширения поддерживаемых методов и сервисов - поэтому, если вы считаете, что это будет полезно для вас, и хотите сотрудничать, пожалуйста, свяжитесь со мной по [bruce@mcpher.com](mailto:bruce@mcpher.com) и мы поговорим.
|
|
315
|
+
Как я уже упоминал ранее, чтобы развивать это дальше, мне понадобится много помощи для расширения поддерживаемых методов и сервисов - поэтому, если вы считаете, что это будет полезно для вас, и хотите сотрудничать, пожалуйста, свяжитесь со мной по [bruce@mcpher.com](mailto:bruce@mcpher.com) и мы поговорим.
|
|
316
|
+
|
|
317
|
+
|
|
318
|
+
## Translations and writeups
|
|
319
|
+
|
|
320
|
+
- [initial idea and thoughts](https://ramblings.mcpher.com/a-proof-of-concept-implementation-of-apps-script-environment-on-node/)
|
|
321
|
+
- [Inside the volatile world of a Google Document](https://ramblings.mcpher.com/inside-the-volatile-world-of-a-google-document/
|
|
322
|
+
- [Apps Script Services on Node – using apps script libraries](https://ramblings.mcpher.com/apps-script-services-on-node-using-apps-script-libraries/)
|
|
323
|
+
- [Apps Script environment on Node – more services](https://ramblings.mcpher.com/apps-script-environment-on-node-more-services/)
|
|
324
|
+
- [Turning async into synch on Node using workers](https://ramblings.mcpher.com/turning-async-into-synch-on-node-using-workers/)
|
|
325
|
+
- [All about Apps Script Enums and how to fake them](https://ramblings.mcpher.com/all-about-apps-script-enums-and-how-to-fake-them/)
|
|
326
|
+
- [Russian version](README.RU.md) ([credit Alex Ivanov](https://github.com/oshliaer)) - needs updating
|
|
327
|
+
- [colaborators](collaborators.md) - additional information for collaborators
|
|
328
|
+
- [oddities](oddities.md) - a collection of oddities uncovered during this project
|
|
329
|
+
- [gemini](gemini.md) - some reflections and experiences on using gemini to help code large projects
|
|
330
|
+
- [named colors](named-colors.md) - colors supported by Apps Script
|
|
331
|
+
- [setup env](setup-env.md) - ([credit Eric Shapiro] - additional info on contents of .env file
|
|
332
|
+
- [this file](README.md)
|
|
333
|
+
- [named colors](named-colors.md)
|
|
334
|
+
- [sandbox](sandbox.md)
|
package/README.md
CHANGED
|
@@ -143,4 +143,5 @@ As I mentioned earlier, to take this further, I'm going to need a lot of help to
|
|
|
143
143
|
- [named colors](named-colors.md) - colors supported by Apps Script
|
|
144
144
|
- [setup env](setup-env.md) - ([credit Eric Shapiro] - additional info on contents of .env file
|
|
145
145
|
- [this file](README.md)
|
|
146
|
-
- [named colors](named-colors.md)
|
|
146
|
+
- [named colors](named-colors.md)
|
|
147
|
+
- [sandbox](sandbox.md)
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
import is from '@sindresorhus/is';
|
|
2
|
+
export const whichType = (element) => {
|
|
3
|
+
const ts = ["paragraph", "pageBreak", "textRun", "table", "tableRows", "tableCells", "content"]
|
|
4
|
+
const [t] = ts.filter(f => Reflect.has(element, f))
|
|
5
|
+
return t
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
export const report = (doc, what) => {
|
|
9
|
+
let newdoc = doc
|
|
10
|
+
let body = doc.body
|
|
11
|
+
let tabs = doc.tabs
|
|
12
|
+
// handle case where tabs is handling legacy doc.
|
|
13
|
+
if (!body) {
|
|
14
|
+
if (!tabs) {
|
|
15
|
+
console.log('missing tabs')
|
|
16
|
+
console.log(JSON.stringify(doc))
|
|
17
|
+
} else {
|
|
18
|
+
console.log('getting body from tabs')
|
|
19
|
+
newdoc = tabs[0].documentTab
|
|
20
|
+
body = newdoc.body
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
// drop the section break
|
|
25
|
+
const children = body.content.slice(1)
|
|
26
|
+
what += ` -children:${children.length}`
|
|
27
|
+
console.log(what)
|
|
28
|
+
let text = ' '
|
|
29
|
+
console.log(stringCircular(children))
|
|
30
|
+
const childProps = ["elements", "tableRows", "tableCells", "content", "listItem"]
|
|
31
|
+
const typer = (child, text, spaces = " ") => {
|
|
32
|
+
const type = whichType(child)
|
|
33
|
+
if (type) {
|
|
34
|
+
text += `\n${spaces}-${type} ${child.startIndex}:${child.endIndex}`
|
|
35
|
+
if (type === 'textRun') {
|
|
36
|
+
text += ` ${JSON.stringify(child[type].content)}`
|
|
37
|
+
}
|
|
38
|
+
const key = Reflect.ownKeys(child[type]).find(f => childProps.includes(f))
|
|
39
|
+
let arr = key && child[type][key]
|
|
40
|
+
if (!arr && is.array(child[type])) arr = child[type]
|
|
41
|
+
|
|
42
|
+
if (is.array(arr)) {
|
|
43
|
+
//text += spaces
|
|
44
|
+
arr.forEach(f => text = typer(f, text, spaces + " "))
|
|
45
|
+
//text += ''
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
return text
|
|
49
|
+
}
|
|
50
|
+
return children.map(f => typer(f, text)).join("\n")
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
export const scl = (doc) => {
|
|
55
|
+
if (!DocumentApp.isFake) {
|
|
56
|
+
const id = doc.getId()
|
|
57
|
+
doc.saveAndClose()
|
|
58
|
+
doc = DocumentApp.openById(id)
|
|
59
|
+
}
|
|
60
|
+
return doc
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
// The custom replacer function
|
|
64
|
+
const getCircularReplacer = () => {
|
|
65
|
+
const seen = new WeakSet(); // Use WeakSet to avoid memory leaks
|
|
66
|
+
return (_, value) => {
|
|
67
|
+
// If the value is an object and not null
|
|
68
|
+
if (typeof value === "object" && value !== null) {
|
|
69
|
+
// If we have already seen this object, it's a circular reference
|
|
70
|
+
if (seen.has(value)) {
|
|
71
|
+
return "[Circular]"; // Replace it with a placeholder
|
|
72
|
+
}
|
|
73
|
+
// If it's a new object, add it to our cache
|
|
74
|
+
seen.add(value);
|
|
75
|
+
}
|
|
76
|
+
// Return the value to be serialized
|
|
77
|
+
return value;
|
|
78
|
+
};
|
|
79
|
+
};
|
|
80
|
+
|
|
81
|
+
export const stringCircular = (ob) => JSON.stringify(ob, getCircularReplacer());
|
package/gasmess/bruce/pbx.js
CHANGED
|
@@ -1,54 +1,26 @@
|
|
|
1
1
|
import '../../main.js';
|
|
2
2
|
import { moveToTempFolder, deleteTempFile } from '../tempfolder.js';
|
|
3
|
-
import
|
|
3
|
+
import { report, scl } from './dreport.js';
|
|
4
|
+
|
|
4
5
|
const suffix = "-bruce"
|
|
5
6
|
|
|
6
|
-
const
|
|
7
|
-
const ts = ["paragraph", "pageBreak", "textRun","table","tableRows","tableCells","content"]
|
|
8
|
-
const [t] = ts.filter(f => Reflect.has(element, f))
|
|
9
|
-
//if (!t) console.log('skipping element', element)
|
|
10
|
-
return t
|
|
11
|
-
}
|
|
7
|
+
const tabsa = () => {
|
|
12
8
|
|
|
13
|
-
|
|
14
|
-
const
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
console.log(
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
if (type) {
|
|
25
|
-
text += `\n${spaces}-${type} ${child.startIndex}:${child.endIndex}`
|
|
26
|
-
if (type === 'textRun') {
|
|
27
|
-
text += ` ${JSON.stringify(child[type].content)}`
|
|
28
|
-
}
|
|
29
|
-
const key = Reflect.ownKeys(child[type]).find (f=>childProps.includes(f))
|
|
30
|
-
let arr = key && child[type][key]
|
|
31
|
-
if (!arr && is.array (child[type])) arr = child[type]
|
|
32
|
-
|
|
33
|
-
if (is.array(arr)) {
|
|
34
|
-
//text += spaces
|
|
35
|
-
arr.forEach(f => text = typer(f, text, spaces + " "))
|
|
36
|
-
//text += ''
|
|
37
|
-
}
|
|
38
|
-
}
|
|
39
|
-
return text
|
|
40
|
-
}
|
|
41
|
-
return children.map(f => typer(f, text)).join("\n")
|
|
42
|
-
}
|
|
43
|
-
const scl = (doc) => {
|
|
44
|
-
if (!DocumentApp.isFake) {
|
|
45
|
-
const id = doc.getId()
|
|
46
|
-
doc.saveAndClose()
|
|
47
|
-
doc = DocumentApp.openById(id)
|
|
48
|
-
}
|
|
49
|
-
return doc
|
|
9
|
+
let doc = DocumentApp.create("abc")
|
|
10
|
+
const id = doc.getId()
|
|
11
|
+
moveToTempFolder(id, suffix)
|
|
12
|
+
|
|
13
|
+
let body = doc.getBody()
|
|
14
|
+
console.log (body)
|
|
15
|
+
doc = scl(doc)
|
|
16
|
+
console.log(report(Docs.Documents.get(id, {includeTabsContent: true}), `\n1.empty document`))
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
deleteTempFile(id)
|
|
50
20
|
}
|
|
51
21
|
|
|
22
|
+
//tabsa()
|
|
23
|
+
|
|
52
24
|
// this is testing the new simplified method
|
|
53
25
|
const pbnew = () => {
|
|
54
26
|
let doc = DocumentApp.create("abc")
|
|
@@ -56,6 +28,16 @@ const pbnew = () => {
|
|
|
56
28
|
moveToTempFolder(id, suffix)
|
|
57
29
|
let body
|
|
58
30
|
|
|
31
|
+
body = doc.getBody()
|
|
32
|
+
body.appendListItem('l0a append')
|
|
33
|
+
doc = scl(doc)
|
|
34
|
+
console.log(report(Docs.Documents.get(id), `\nl0a.appended list item l0a`))
|
|
35
|
+
|
|
36
|
+
body = doc.getBody()
|
|
37
|
+
body.appendListItem('l01b append')
|
|
38
|
+
doc = scl(doc)
|
|
39
|
+
console.log(report(Docs.Documents.get(id), `\nl0a.appended list item l0b`))
|
|
40
|
+
|
|
59
41
|
body = doc.getBody()
|
|
60
42
|
body.appendTable([['']])
|
|
61
43
|
doc = scl(doc)
|
|
@@ -66,6 +48,12 @@ const pbnew = () => {
|
|
|
66
48
|
doc = scl(doc)
|
|
67
49
|
console.log(report(Docs.Documents.get(id), `\n1.appended para`))
|
|
68
50
|
|
|
51
|
+
body = doc.getBody()
|
|
52
|
+
body.appendListItem('l1 append')
|
|
53
|
+
doc = scl(doc)
|
|
54
|
+
console.log(report(Docs.Documents.get(id), `\nl1.appended list item l1`))
|
|
55
|
+
|
|
56
|
+
|
|
69
57
|
body = doc.getBody()
|
|
70
58
|
body.insertParagraph(0, 'para 0')
|
|
71
59
|
doc = scl(doc)
|
|
@@ -131,8 +119,8 @@ const pbnew = () => {
|
|
|
131
119
|
|
|
132
120
|
deleteTempFile(id)
|
|
133
121
|
}
|
|
134
|
-
pbnew()
|
|
135
122
|
|
|
123
|
+
pbnew()
|
|
136
124
|
|
|
137
125
|
|
|
138
126
|
|
|
@@ -2,5 +2,11 @@ import "../../main.js";
|
|
|
2
2
|
import { FakeSpreadsheetApp } from "../../src/services/spreadsheetapp/fakespreadsheetapp.js";
|
|
3
3
|
import { moveToTempFolder, deleteTempFile } from "./tempfolder.js";
|
|
4
4
|
|
|
5
|
-
const spreadsheet = SpreadsheetApp.openById("");
|
|
5
|
+
const spreadsheet = SpreadsheetApp.openById("###");
|
|
6
6
|
const sheet = spreadsheet.getSheets()[0];
|
|
7
|
+
sheet.setName("sample");
|
|
8
|
+
|
|
9
|
+
const r = spreadsheet.getRangeByName("test111");
|
|
10
|
+
if (r) {
|
|
11
|
+
console.log(r.getA1Notation());
|
|
12
|
+
}
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
import "../../main.js";
|
|
2
|
+
import { FakeSpreadsheetApp } from "../../src/services/spreadsheetapp/fakespreadsheetapp.js";
|
|
3
|
+
import { moveToTempFolder, deleteTempFile } from "./tempfolder.js";
|
|
4
|
+
|
|
5
|
+
const spreadsheet = SpreadsheetApp.create("sample");
|
|
6
|
+
const sheet = spreadsheet.getSheets()[0];
|
|
7
|
+
const spreadsheetId = spreadsheet.getId();
|
|
8
|
+
moveToTempFolder(spreadsheetId);
|
|
9
|
+
|
|
10
|
+
// Create named ranges
|
|
11
|
+
const name1 = "sampleNamedRange1";
|
|
12
|
+
const range1 = sheet.getRange("A1:B2");
|
|
13
|
+
const name2 = "sampleNamedRange2";
|
|
14
|
+
const range2 = sheet.getRange("C1:E2");
|
|
15
|
+
spreadsheet.setNamedRange(name1, range1);
|
|
16
|
+
spreadsheet.setNamedRange(name2, range2);
|
|
17
|
+
|
|
18
|
+
// Get created named ranges from Spreadsheet
|
|
19
|
+
const res1 = spreadsheet
|
|
20
|
+
.getNamedRanges()
|
|
21
|
+
.map(
|
|
22
|
+
(rr) =>
|
|
23
|
+
`${rr.getName()}: '${rr.getRange().getSheet().getSheetName()}'!${rr
|
|
24
|
+
.getRange()
|
|
25
|
+
.getA1Notation()}`
|
|
26
|
+
);
|
|
27
|
+
console.log(res1);
|
|
28
|
+
|
|
29
|
+
// Get created named ranges from Sheet
|
|
30
|
+
const res2 = sheet
|
|
31
|
+
.getNamedRanges()
|
|
32
|
+
.map(
|
|
33
|
+
(rr) =>
|
|
34
|
+
`${rr.getName()}: '${rr.getRange().getSheet().getSheetName()}'!${rr
|
|
35
|
+
.getRange()
|
|
36
|
+
.getA1Notation()}`
|
|
37
|
+
);
|
|
38
|
+
console.log(res2);
|
|
39
|
+
|
|
40
|
+
// Update named ranges.
|
|
41
|
+
spreadsheet.getNamedRanges().forEach((r, i) => {
|
|
42
|
+
r.setName(`${r.getName()}_${i + 10}`);
|
|
43
|
+
const range = r.getRange().offset(0, 2);
|
|
44
|
+
r.setRange(range);
|
|
45
|
+
});
|
|
46
|
+
const res3 = spreadsheet
|
|
47
|
+
.getNamedRanges()
|
|
48
|
+
.map(
|
|
49
|
+
(rr) =>
|
|
50
|
+
`${rr.getName()}: '${rr.getRange().getSheet().getSheetName()}'!${rr
|
|
51
|
+
.getRange()
|
|
52
|
+
.getA1Notation()}`
|
|
53
|
+
);
|
|
54
|
+
console.log(res3);
|
|
55
|
+
|
|
56
|
+
// Remove named ranges
|
|
57
|
+
spreadsheet.getNamedRanges().forEach((r) => r.remove());
|
|
58
|
+
|
|
59
|
+
deleteTempFile(spreadsheetId);
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
|
|
3
|
+
# A script to create a GitHub issue for the PositionedImage API limitation.
|
|
4
|
+
|
|
5
|
+
# Issue details
|
|
6
|
+
TITLE="Cannot emulate Paragraph.addPositionedImage() due to missing public API"
|
|
7
|
+
BODY=$(cat <<'EOF'
|
|
8
|
+
The `DocumentApp.Paragraph.addPositionedImage()` method exists in the live Google Apps Script environment, allowing for the creation of positioned images anchored to a paragraph.
|
|
9
|
+
|
|
10
|
+
However, as of May 2024, there is no corresponding public endpoint in the Google Docs API v1 to programmatically create a `PositionedObject`. The `createPositionedObject` request does not exist in the public API.
|
|
11
|
+
|
|
12
|
+
This has been a requested feature, but it appears that the Apps Script service uses a private, non-public API to achieve this functionality.
|
|
13
|
+
|
|
14
|
+
Because `gas-fakes` relies exclusively on the public Google Workspace APIs, it is not possible to emulate this method. Tests that use `addPositionedImage` are skipped when run in the fake environment.
|
|
15
|
+
|
|
16
|
+
This issue is to track this limitation and can be closed if Google exposes a public API for this feature.
|
|
17
|
+
|
|
18
|
+
See this related issue tracker for the API feature request: https://issuetracker.google.com/issues/183845501 (Note: This is a guess at a relevant issue, a real one might need to be found or created).
|
|
19
|
+
EOF
|
|
20
|
+
)
|
|
21
|
+
LABELS="emulation-accuracy,document-app"
|
|
22
|
+
|
|
23
|
+
# Create the issue using the GitHub CLI
|
|
24
|
+
gh issue create --title "$TITLE" --body "$BODY" --label "$LABELS"
|
|
25
|
+
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
|
|
3
|
+
# A shell script to post a markdown file from ./ghissues as a GitHub issue.
|
|
4
|
+
# It automatically extracts the title, labels, and body from the file.
|
|
5
|
+
#
|
|
6
|
+
# Usage: ./ghissues/post-issue.sh <filename>
|
|
7
|
+
# Example: ./ghissues/post-issue.sh document-clear-behavior.md
|
|
8
|
+
|
|
9
|
+
set -e
|
|
10
|
+
|
|
11
|
+
# The directory where issue files are stored, relative to the script's location.
|
|
12
|
+
SCRIPT_DIR=$(dirname "$0")
|
|
13
|
+
|
|
14
|
+
# Check for filename argument
|
|
15
|
+
if [ -z "$1" ]; then
|
|
16
|
+
echo "Usage: $0 <filename_in_ghissues_dir>"
|
|
17
|
+
echo "Example: $0 document-clear-behavior.md"
|
|
18
|
+
exit 1
|
|
19
|
+
fi
|
|
20
|
+
|
|
21
|
+
ISSUE_FILE="$SCRIPT_DIR/$1"
|
|
22
|
+
|
|
23
|
+
# Check if file exists
|
|
24
|
+
if [ ! -f "$ISSUE_FILE" ]; then
|
|
25
|
+
echo "Error: Issue file not found at $ISSUE_FILE"
|
|
26
|
+
exit 1
|
|
27
|
+
fi
|
|
28
|
+
|
|
29
|
+
echo "Reading issue from $ISSUE_FILE..."
|
|
30
|
+
|
|
31
|
+
# Extract the title (first line, remove markdown heading)
|
|
32
|
+
TITLE=$(head -n 1 "$ISSUE_FILE" | sed 's/^# //')
|
|
33
|
+
|
|
34
|
+
# Extract labels from the second line (e.g., **Labels**: `label1`, `label2`)
|
|
35
|
+
LABELS_LINE=$(sed -n '2p' "$ISSUE_FILE")
|
|
36
|
+
LABELS_STRING=$(echo "$LABELS_LINE" | grep -o '`.*`' | sed 's/`//g' || echo "")
|
|
37
|
+
|
|
38
|
+
LABEL_ARGS=()
|
|
39
|
+
if [ -n "$LABELS_STRING" ]; then
|
|
40
|
+
IFS=',' read -ra ADDR <<< "$LABELS_STRING"
|
|
41
|
+
for label in "${ADDR[@]}"; do
|
|
42
|
+
trimmed_label=$(echo "$label" | xargs) # trim whitespace
|
|
43
|
+
if [ -n "$trimmed_label" ]; then
|
|
44
|
+
LABEL_ARGS+=("--label" "$trimmed_label")
|
|
45
|
+
fi
|
|
46
|
+
done
|
|
47
|
+
fi
|
|
48
|
+
|
|
49
|
+
# Use the rest of the file (from line 3) as the body
|
|
50
|
+
gh issue create --title "$TITLE" --body-file <(tail -n +3 "$ISSUE_FILE") "${LABEL_ARGS[@]}"
|
|
51
|
+
|
|
52
|
+
echo "Issue '$TITLE' created successfully."
|
|
53
|
+
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
|
|
3
|
+
# A shell script to create a GitHub issue for the sandbox feature.
|
|
4
|
+
|
|
5
|
+
# Issue title
|
|
6
|
+
TITLE="Improve Sandbox Mode to fully emulate drive.file scope"
|
|
7
|
+
|
|
8
|
+
# Issue body in markdown
|
|
9
|
+
BODY=$(cat <<'EOF'
|
|
10
|
+
## Summary
|
|
11
|
+
|
|
12
|
+
The current implementation of `gas-fakes` requires the full `https://www.googleapis.com/auth/drive` scope for initialization, primarily to access metadata about the root Drive folder. This is a stumbling block for users who want to run tests in a more restrictive environment using only the `drive.file` scope.
|
|
13
|
+
|
|
14
|
+
The `ScriptApp.__behavior.sandBoxMode` was introduced as a clever workaround to *emulate* the `drive.file` scope during test execution, but it doesn't solve the initial authorization requirement.
|
|
15
|
+
|
|
16
|
+
For more details on the current sandbox implementation, see sandbox.md.
|
|
17
|
+
|
|
18
|
+
## TODO
|
|
19
|
+
|
|
20
|
+
We need to investigate ways to remove the dependency on root folder access during the `FakeDriveApp` initialization. This would allow `gas-fakes` to operate with only the `drive.file` scope, making the sandbox feature a true end-to-end emulation.
|
|
21
|
+
|
|
22
|
+
Possible areas to explore:
|
|
23
|
+
- Can we mock or bypass the root folder check during initialization if a specific `drive.file` mode is detected?
|
|
24
|
+
- Can we lazy-initialize parts of `FakeDriveApp` that require root access, and only fail if those specific methods (like `getRootFolder()`) are called without sufficient permissions?
|
|
25
|
+
|
|
26
|
+
This would be a significant improvement for security-conscious development and testing workflows.
|
|
27
|
+
EOF
|
|
28
|
+
)
|
|
29
|
+
|
|
30
|
+
# Create the issue using GitHub CLI
|
|
31
|
+
gh issue create --title "$TITLE" --body "$BODY" --label "enhancement" --label "help wanted"
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
|
|
3
|
+
echo "Welcome to the gas-fakes configuration tool!"
|
|
4
|
+
echo "This script will help you set up your .env and gasfakes.json files."
|
|
5
|
+
|
|
6
|
+
# --- Check for existing .env file ---
|
|
7
|
+
if [ -f ".env" ]; then
|
|
8
|
+
echo ""
|
|
9
|
+
echo "An existing .env file was found. We will update the values."
|
|
10
|
+
UPDATE_MODE=true
|
|
11
|
+
else
|
|
12
|
+
echo ""
|
|
13
|
+
echo "No .env file found. We will create a new one."
|
|
14
|
+
UPDATE_MODE=false
|
|
15
|
+
fi
|
|
16
|
+
|
|
17
|
+
# --- Get .env file details ---
|
|
18
|
+
echo ""
|
|
19
|
+
echo "First, let's configure your .env file for Application Default Credentials (ADC)."
|
|
20
|
+
echo "These are necessary for gas-fakes to access Google Workspace APIs on your behalf."
|
|
21
|
+
|
|
22
|
+
read -p "Enter your Google Cloud Project ID: " gcp_project_id
|
|
23
|
+
read -p "Enter the ID of a Google Drive file you have access to: " drive_test_file_id
|
|
24
|
+
read -p "Enter the default scopes (e.g., \"https://www.googleapis.com/auth/userinfo.email,openid,https://www.googleapis.com/auth/cloud-platform\"): " default_scopes
|
|
25
|
+
read -p "Enter any EXTRA_SCOPES (e.g., ,https://www.googleapis.com/auth/drive,https://www.googleapis.com/auth/spreadsheets): " extra_scopes
|
|
26
|
+
|
|
27
|
+
# --- Update or create .env file ---
|
|
28
|
+
if [ "$UPDATE_MODE" = true ]; then
|
|
29
|
+
# Use sed to replace the values in the existing file
|
|
30
|
+
sed -i "s|^GCP_PROJECT_ID=.*|GCP_PROJECT_ID=$gcp_project_id|" .env
|
|
31
|
+
sed -i "s|^DRIVE_TEST_FILE_ID=.*|DRIVE_TEST_FILE_ID=$drive_test_file_id|" .env
|
|
32
|
+
sed -i "s|^DEFAULT_SCOPES=.*|DEFAULT_SCOPES=\"$default_scopes\"|" .env
|
|
33
|
+
sed -i "s|^EXTRA_SCOPES=.*|EXTRA_SCOPES=\"$extra_scopes\"|" .env
|
|
34
|
+
echo ""
|
|
35
|
+
echo "Successfully updated the .env file."
|
|
36
|
+
else
|
|
37
|
+
# Write a new .env file
|
|
38
|
+
cat <<EOF > .env
|
|
39
|
+
# Required for gas-fakes to use Application Default Credentials
|
|
40
|
+
GCP_PROJECT_ID="$gcp_project_id"
|
|
41
|
+
DRIVE_TEST_FILE_ID="$drive_test_file_id"
|
|
42
|
+
|
|
43
|
+
# This should generally be left as default for ADC
|
|
44
|
+
AC=default
|
|
45
|
+
|
|
46
|
+
# These are the scopes set by default - customize as needed
|
|
47
|
+
DEFAULT_SCOPES="$default_scopes"
|
|
48
|
+
|
|
49
|
+
# Add additional scopes here
|
|
50
|
+
EXTRA_SCOPES="$extra_scopes"
|
|
51
|
+
EOF
|
|
52
|
+
echo ""
|
|
53
|
+
echo "Successfully created the .env file."
|
|
54
|
+
fi
|
|
55
|
+
|
|
56
|
+
# --- Get gasfakes.json details ---
|
|
57
|
+
echo ""
|
|
58
|
+
echo "Next, let's configure your gasfakes.json file."
|
|
59
|
+
echo "This file informs gas-fakes about your local environment."
|
|
60
|
+
|
|
61
|
+
# Using default values as suggested by the repository
|
|
62
|
+
DEFAULT_MANIFEST="./appsscript.json"
|
|
63
|
+
DEFAULT_CLASP="./.clasp.json"
|
|
64
|
+
DEFAULT_CACHE="/tmp/gas-fakes/cache"
|
|
65
|
+
DEFAULT_PROPERTIES="/tmp/gas-fakes/properties"
|
|
66
|
+
DEFAULT_SCRIPT_ID=$(uuidgen) # A fake script ID if clasp file is not used
|
|
67
|
+
|
|
68
|
+
read -p "Enter the path to your manifest file (default: $DEFAULT_MANIFEST): " manifest_path
|
|
69
|
+
manifest_path=${manifest_path:-$DEFAULT_MANIFEST}
|
|
70
|
+
|
|
71
|
+
read -p "Enter the path to your clasp file (default: $DEFAULT_CLASP): " clasp_path
|
|
72
|
+
clasp_path=${clasp_path:-$DEFAULT_CLASP}
|
|
73
|
+
|
|
74
|
+
read -p "Enter a bound document ID if applicable (leave blank for none): " document_id
|
|
75
|
+
if [ -z "$document_id" ]; then
|
|
76
|
+
document_id="null"
|
|
77
|
+
else
|
|
78
|
+
document_id="\"$document_id\""
|
|
79
|
+
fi
|
|
80
|
+
|
|
81
|
+
read -p "Enter the local cache directory (default: $DEFAULT_CACHE): " cache_path
|
|
82
|
+
cache_path=${cache_path:-$DEFAULT_CACHE}
|
|
83
|
+
|
|
84
|
+
read -p "Enter the local properties directory (default: $DEFAULT_PROPERTIES): " properties_path
|
|
85
|
+
properties_path=${properties_path:-$DEFAULT_PROPERTIES}
|
|
86
|
+
|
|
87
|
+
read -p "Enter the script ID (default: a new random ID will be created): " script_id
|
|
88
|
+
script_id=${script_id:-$DEFAULT_SCRIPT_ID}
|
|
89
|
+
|
|
90
|
+
# --- Write gasfakes.json file ---
|
|
91
|
+
cat <<EOF > gasfakes.json
|
|
92
|
+
{
|
|
93
|
+
"manifest": "$manifest_path",
|
|
94
|
+
"clasp": "$clasp_path",
|
|
95
|
+
"documentId": $document_id,
|
|
96
|
+
"cache": "$cache_path",
|
|
97
|
+
"properties": "$properties_path",
|
|
98
|
+
"scriptId": "$script_id"
|
|
99
|
+
}
|
|
100
|
+
EOF
|
|
101
|
+
|
|
102
|
+
echo ""
|
|
103
|
+
echo "Successfully created the gasfakes.json file."
|
|
104
|
+
|
|
105
|
+
echo ""
|
|
106
|
+
echo "Setup complete! You can now run your gas-fakes project locally."
|
|
107
|
+
echo "Be sure to follow the remaining setup instructions in the gas-fakes repository."
|
package/package.json
CHANGED
|
@@ -4,16 +4,18 @@
|
|
|
4
4
|
},
|
|
5
5
|
"dependencies": {
|
|
6
6
|
"@mcpher/fake-gasenum": "^1.0.2",
|
|
7
|
+
"@mcpher/gas-fakes": "^1.0.14",
|
|
7
8
|
"@mcpher/unit": "^1.1.11",
|
|
8
9
|
"@sindresorhus/is": "^7.0.1",
|
|
9
10
|
"archiver": "^7.0.1",
|
|
10
11
|
"get-stream": "^9.0.1",
|
|
11
|
-
"googleapis": "^
|
|
12
|
-
"got": "^14.4.
|
|
12
|
+
"googleapis": "^157.0.0",
|
|
13
|
+
"got": "^14.4.7",
|
|
13
14
|
"into-stream": "^8.0.1",
|
|
14
|
-
"
|
|
15
|
-
"keyv
|
|
16
|
-
"
|
|
15
|
+
"is": "^3.3.2",
|
|
16
|
+
"keyv": "^5.5.0",
|
|
17
|
+
"keyv-file": "^5.1.3",
|
|
18
|
+
"mime": "^4.0.7",
|
|
17
19
|
"sleep-synchronously": "^2.0.0",
|
|
18
20
|
"subsume": "^4.0.0",
|
|
19
21
|
"to-readable-stream": "^4.0.0",
|
|
@@ -45,13 +47,20 @@
|
|
|
45
47
|
"testslides": "cp mainlocal.js main.js && node --env-file=.env ./test/testslides.js execute",
|
|
46
48
|
"testdocsnext": "cp mainlocal.js main.js && node --env-file=.env ./test/testdocsnext.js execute",
|
|
47
49
|
"testsheetstext": "cp mainlocal.js main.js && node --env-file=.env ./test/testsheetstext.js execute",
|
|
50
|
+
"testsheetsrange": "cp mainlocal.js main.js && node --env-file=.env ./test/testsheetsrange.js execute",
|
|
51
|
+
"testdocslistitems": "cp mainlocal.js main.js && node --env-file=.env ./test/testdocslistitems.js execute",
|
|
52
|
+
"testdocsall": "cp mainlocal.js main.js && node --env-file=.env ./test/testdocsall.js",
|
|
53
|
+
"testdocsheaders": "cp mainlocal.js main.js && node --env-file=.env ./test/testdocsheaders.js execute",
|
|
54
|
+
"testdocsfooters": "cp mainlocal.js main.js && node --env-file=.env ./test/testdocsfooters.js execute",
|
|
55
|
+
"testdocsfootnotes": "cp mainlocal.js main.js && node --env-file=.env ./test/testdocsfootnotes.js execute",
|
|
56
|
+
"testdocsimages": "cp mainlocal.js main.js && node --env-file=.env ./test/testdocsimages.js execute",
|
|
48
57
|
"pub": "cp mainlocal.js main.js && npm publish --access public"
|
|
49
58
|
},
|
|
50
59
|
"name": "@mcpher/gas-fakes",
|
|
51
|
-
"version": "1.0.
|
|
60
|
+
"version": "1.0.16",
|
|
52
61
|
"license": "MIT",
|
|
53
62
|
"main": "main.js",
|
|
54
63
|
"description": "A proof of concept implementation of Apps Script Environment on Node",
|
|
55
64
|
"repository": "github:brucemcpherson/gas-fakes",
|
|
56
65
|
"homepage": "https://ramblings.mcpher.com/a-proof-of-concept-implementation-of-apps-script-environment-on-node/"
|
|
57
|
-
}
|
|
66
|
+
}
|
|
@@ -1,11 +1,15 @@
|
|
|
1
1
|
import { google } from "googleapis";
|
|
2
2
|
import { Auth } from '../../support/auth.js'
|
|
3
|
+
import { syncLog} from '../../support/workersync/synclogger.js'
|
|
3
4
|
|
|
4
|
-
|
|
5
|
-
return google.docs({ version: 'v1', auth });
|
|
6
|
-
}
|
|
5
|
+
let __client = null;
|
|
7
6
|
|
|
8
|
-
export const
|
|
7
|
+
export const getDocsApiClient = () => {
|
|
9
8
|
const auth = Auth.getAuth()
|
|
10
|
-
|
|
11
|
-
|
|
9
|
+
if (!__client) {
|
|
10
|
+
syncLog('Creating new Docs API client');
|
|
11
|
+
__client = google.docs({ version: 'v1', auth });
|
|
12
|
+
}
|
|
13
|
+
return __client;
|
|
14
|
+
}
|
|
15
|
+
|
|
@@ -2,14 +2,12 @@
|
|
|
2
2
|
* Advanced sheets service
|
|
3
3
|
*/
|
|
4
4
|
import { Proxies } from '../../support/proxies.js'
|
|
5
|
-
import { advClassMaker
|
|
6
|
-
import { getAuthedClient } from './docapis.js'
|
|
5
|
+
import { advClassMaker } from '../../support/helpers.js'
|
|
7
6
|
import { newFakeAdvDocuments } from './fakeadvdocuments.js'
|
|
8
7
|
import { docsCacher } from '../../support/docscacher.js';
|
|
9
8
|
|
|
10
9
|
class FakeAdvDocs {
|
|
11
10
|
constructor() {
|
|
12
|
-
this.client = Proxies.guard(getAuthedClient())
|
|
13
11
|
this.__fakeObjectType = "Docs"
|
|
14
12
|
|
|
15
13
|
const propLists = {
|