@foxtware/mineral 1.0.1 → 1.0.3
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/api/google/googlesheetsSpreadsheetSheetAdd.js +24 -11
- package/api/google/googlesheetsSpreadsheetSheetDelete.js +157 -0
- package/api/google/googlesheetsSpreadsheetTrim.js +2 -0
- package/api/linear/linearOrganizationInviteCreate.js +114 -0
- package/api/workspace.js +1 -0
- package/hosting/hosting.utils.js +1 -0
- package/package.json +1 -1
|
@@ -1,8 +1,11 @@
|
|
|
1
|
-
const { ArgsWarden, objHasAny } = require('../utils');
|
|
1
|
+
const { ArgsWarden, arrayToChunks, objHasAny } = require('../utils');
|
|
2
2
|
const { credsValidator } = require('../validators');
|
|
3
3
|
const { getGoogleSheets, googleApiCall } = require('../google/google.utils');
|
|
4
4
|
const { googlesheetsSpreadsheetTrim } = require('../google/googlesheetsSpreadsheetTrim');
|
|
5
5
|
|
|
6
|
+
// Sheets rejects oversized request bodies, so big exports go up in slices.
|
|
7
|
+
const MAX_ROWS_PER_UPDATE = 5000;
|
|
8
|
+
|
|
6
9
|
const spreadsheetIdentifierValidator = (spreadsheetIdentifier) => {
|
|
7
10
|
return objHasAny(spreadsheetIdentifier, ['spreadsheetId']);
|
|
8
11
|
};
|
|
@@ -81,6 +84,10 @@ const googlesheetsSpreadsheetSheetAdd = async (
|
|
|
81
84
|
addSheet: {
|
|
82
85
|
properties: {
|
|
83
86
|
title: String(sheetName),
|
|
87
|
+
gridProperties: {
|
|
88
|
+
rowCount: values.length,
|
|
89
|
+
columnCount: headers.length,
|
|
90
|
+
},
|
|
84
91
|
},
|
|
85
92
|
},
|
|
86
93
|
},
|
|
@@ -94,17 +101,23 @@ const googlesheetsSpreadsheetSheetAdd = async (
|
|
|
94
101
|
|
|
95
102
|
const newSheetId = batchUpdateResponse.data.replies[0].addSheet.properties.sheetId;
|
|
96
103
|
|
|
97
|
-
const
|
|
98
|
-
spreadsheetId,
|
|
99
|
-
range: `${ sheetName }!A1`,
|
|
100
|
-
valueInputOption: 'RAW',
|
|
101
|
-
requestBody: {
|
|
102
|
-
values,
|
|
103
|
-
},
|
|
104
|
-
}));
|
|
104
|
+
const chunks = arrayToChunks(values, MAX_ROWS_PER_UPDATE);
|
|
105
105
|
|
|
106
|
-
|
|
107
|
-
|
|
106
|
+
let updateResponse;
|
|
107
|
+
|
|
108
|
+
for (const [chunkIndex, chunk] of chunks.entries()) {
|
|
109
|
+
updateResponse = await googleApiCall(() => client.spreadsheets.values.update({
|
|
110
|
+
spreadsheetId,
|
|
111
|
+
range: `${ sheetName }!A${ (chunkIndex * MAX_ROWS_PER_UPDATE) + 1 }`,
|
|
112
|
+
valueInputOption: 'RAW',
|
|
113
|
+
requestBody: {
|
|
114
|
+
values: chunk,
|
|
115
|
+
},
|
|
116
|
+
}));
|
|
117
|
+
|
|
118
|
+
if (!updateResponse.ok) {
|
|
119
|
+
return updateResponse;
|
|
120
|
+
}
|
|
108
121
|
}
|
|
109
122
|
|
|
110
123
|
if (trim) {
|
|
@@ -0,0 +1,157 @@
|
|
|
1
|
+
// https://developers.google.com/sheets/api/reference/rest/v4/spreadsheets/request#deletesheetrequest
|
|
2
|
+
|
|
3
|
+
const { ArgsWarden, ensureArray, objHasAny } = require('../utils');
|
|
4
|
+
const { credsValidator } = require('../validators');
|
|
5
|
+
const { getGoogleSheets, googleApiCall } = require('../google/google.utils');
|
|
6
|
+
|
|
7
|
+
const spreadsheetIdentifierValidator = (spreadsheetIdentifier) => {
|
|
8
|
+
return objHasAny(spreadsheetIdentifier, ['spreadsheetId']);
|
|
9
|
+
};
|
|
10
|
+
|
|
11
|
+
const sheetIdentifierValidator = (sheetIdentifier) => {
|
|
12
|
+
const sheetIdentifiers = ensureArray(sheetIdentifier);
|
|
13
|
+
|
|
14
|
+
return sheetIdentifiers.length > 0
|
|
15
|
+
&& sheetIdentifiers.every((identifier) => {
|
|
16
|
+
return objHasAny(identifier, ['sheetName', 'sheetId']);
|
|
17
|
+
});
|
|
18
|
+
};
|
|
19
|
+
|
|
20
|
+
const argsWarden = new ArgsWarden([
|
|
21
|
+
['credsPayload', credsValidator],
|
|
22
|
+
['spreadsheetIdentifier', spreadsheetIdentifierValidator],
|
|
23
|
+
['sheetIdentifier', sheetIdentifierValidator],
|
|
24
|
+
]);
|
|
25
|
+
|
|
26
|
+
const googlesheetsSpreadsheetSheetDelete = async (
|
|
27
|
+
credsPayload,
|
|
28
|
+
spreadsheetIdentifier,
|
|
29
|
+
sheetIdentifier,
|
|
30
|
+
{
|
|
31
|
+
missingOk = true,
|
|
32
|
+
} = {},
|
|
33
|
+
) => {
|
|
34
|
+
|
|
35
|
+
const rejectResponse = await argsWarden.responseIfRejectingArgs({
|
|
36
|
+
credsPayload,
|
|
37
|
+
spreadsheetIdentifier,
|
|
38
|
+
sheetIdentifier,
|
|
39
|
+
});
|
|
40
|
+
if (rejectResponse) {
|
|
41
|
+
return rejectResponse;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
const { spreadsheetId } = spreadsheetIdentifier;
|
|
45
|
+
const sheetIdentifiers = ensureArray(sheetIdentifier);
|
|
46
|
+
|
|
47
|
+
const { client, error } = await getGoogleSheets(credsPayload);
|
|
48
|
+
|
|
49
|
+
if (error) {
|
|
50
|
+
return error;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
const spreadsheetResponse = await googleApiCall(() => client.spreadsheets.get({
|
|
54
|
+
spreadsheetId,
|
|
55
|
+
}));
|
|
56
|
+
|
|
57
|
+
if (!spreadsheetResponse.ok) {
|
|
58
|
+
return spreadsheetResponse;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
const sheetsArray = spreadsheetResponse.data?.sheets || [];
|
|
62
|
+
|
|
63
|
+
const resolved = [];
|
|
64
|
+
const missing = [];
|
|
65
|
+
|
|
66
|
+
for (const { sheetName, sheetId } of sheetIdentifiers) {
|
|
67
|
+
const sheet = sheetsArray.find(({ properties }) => {
|
|
68
|
+
return sheetId !== undefined
|
|
69
|
+
? properties.sheetId === sheetId
|
|
70
|
+
: properties.title === sheetName;
|
|
71
|
+
});
|
|
72
|
+
|
|
73
|
+
if (!sheet) {
|
|
74
|
+
missing.push({ sheetName, sheetId });
|
|
75
|
+
continue;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
resolved.push({
|
|
79
|
+
sheetId: sheet.properties.sheetId,
|
|
80
|
+
sheetName: sheet.properties.title,
|
|
81
|
+
});
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
if (missing.length && !missingOk) {
|
|
85
|
+
return {
|
|
86
|
+
ok: false,
|
|
87
|
+
error: {
|
|
88
|
+
code: 'SHEET_NOT_FOUND',
|
|
89
|
+
message: `Sheet not found: ${ JSON.stringify(missing) }`,
|
|
90
|
+
},
|
|
91
|
+
};
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
// Sheets refuses to remove the last remaining tab, so say so plainly rather
|
|
95
|
+
// than letting the API error surface without context.
|
|
96
|
+
if (resolved.length && resolved.length === sheetsArray.length) {
|
|
97
|
+
return {
|
|
98
|
+
ok: false,
|
|
99
|
+
error: {
|
|
100
|
+
code: 'CANNOT_DELETE_ALL_SHEETS',
|
|
101
|
+
message: 'A spreadsheet must keep at least one sheet',
|
|
102
|
+
},
|
|
103
|
+
};
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
if (!resolved.length) {
|
|
107
|
+
return {
|
|
108
|
+
ok: true,
|
|
109
|
+
data: {
|
|
110
|
+
deleted: [],
|
|
111
|
+
missing,
|
|
112
|
+
},
|
|
113
|
+
};
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
const batchUpdateResponse = await googleApiCall(() => client.spreadsheets.batchUpdate({
|
|
117
|
+
spreadsheetId,
|
|
118
|
+
requestBody: {
|
|
119
|
+
requests: resolved.map(({ sheetId }) => ({
|
|
120
|
+
deleteSheet: {
|
|
121
|
+
sheetId,
|
|
122
|
+
},
|
|
123
|
+
})),
|
|
124
|
+
},
|
|
125
|
+
}));
|
|
126
|
+
|
|
127
|
+
if (!batchUpdateResponse.ok) {
|
|
128
|
+
return batchUpdateResponse;
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
return {
|
|
132
|
+
ok: true,
|
|
133
|
+
data: {
|
|
134
|
+
deleted: resolved,
|
|
135
|
+
missing,
|
|
136
|
+
},
|
|
137
|
+
};
|
|
138
|
+
};
|
|
139
|
+
|
|
140
|
+
const funcApiConfig = {
|
|
141
|
+
argsWarden,
|
|
142
|
+
};
|
|
143
|
+
|
|
144
|
+
module.exports = {
|
|
145
|
+
googlesheetsSpreadsheetSheetDelete,
|
|
146
|
+
funcApiConfig,
|
|
147
|
+
};
|
|
148
|
+
|
|
149
|
+
/*
|
|
150
|
+
curl -X POST "http://localhost:8000/googlesheetsSpreadsheetSheetDelete" \
|
|
151
|
+
-H "Content-Type: application/json" \
|
|
152
|
+
-d '{
|
|
153
|
+
"credsPayload": { "credsPath": "google" },
|
|
154
|
+
"spreadsheetIdentifier": { "spreadsheetId": "ABC123" },
|
|
155
|
+
"sheetIdentifier": [{ "sheetName": "AU" }, { "sheetName": "US" }]
|
|
156
|
+
}'
|
|
157
|
+
*/
|
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
// https://linear.app/developers/graphql
|
|
2
|
+
|
|
3
|
+
const { ArgsWarden, actionSingleOrMultiple, everyIfArray, valueProvided } = require('../utils');
|
|
4
|
+
const { credsValidator } = require('../validators');
|
|
5
|
+
const { linearClient } = require('../linear/linear.utils');
|
|
6
|
+
|
|
7
|
+
const invitePayloadValidator = (invitePayload) => {
|
|
8
|
+
return valueProvided(invitePayload?.email);
|
|
9
|
+
};
|
|
10
|
+
|
|
11
|
+
const argsWarden = new ArgsWarden([
|
|
12
|
+
['credsPayload', credsValidator],
|
|
13
|
+
['invitePayload', (invitePayload) => everyIfArray(invitePayloadValidator, invitePayload)],
|
|
14
|
+
]);
|
|
15
|
+
|
|
16
|
+
const linearOrganizationInviteCreateSingle = async (
|
|
17
|
+
credsPayload,
|
|
18
|
+
invitePayload,
|
|
19
|
+
{
|
|
20
|
+
inspect = false,
|
|
21
|
+
fetchClient = linearClient,
|
|
22
|
+
} = {},
|
|
23
|
+
) => {
|
|
24
|
+
const {
|
|
25
|
+
email,
|
|
26
|
+
role = 'user',
|
|
27
|
+
teamIds,
|
|
28
|
+
} = invitePayload;
|
|
29
|
+
|
|
30
|
+
return fetchClient.fetch({
|
|
31
|
+
requestPayload: {
|
|
32
|
+
body: {
|
|
33
|
+
query: `
|
|
34
|
+
mutation OrganizationInviteCreate($input: OrganizationInviteCreateInput!) {
|
|
35
|
+
organizationInviteCreate(input: $input) {
|
|
36
|
+
success
|
|
37
|
+
organizationInvite {
|
|
38
|
+
id
|
|
39
|
+
email
|
|
40
|
+
role
|
|
41
|
+
createdAt
|
|
42
|
+
expiresAt
|
|
43
|
+
acceptedAt
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
`,
|
|
48
|
+
variables: {
|
|
49
|
+
input: {
|
|
50
|
+
email,
|
|
51
|
+
role,
|
|
52
|
+
...teamIds && { teamIds },
|
|
53
|
+
},
|
|
54
|
+
},
|
|
55
|
+
},
|
|
56
|
+
},
|
|
57
|
+
context: {
|
|
58
|
+
credsPayload,
|
|
59
|
+
resultPath: 'data.organizationInviteCreate',
|
|
60
|
+
},
|
|
61
|
+
inspect,
|
|
62
|
+
});
|
|
63
|
+
};
|
|
64
|
+
|
|
65
|
+
const linearOrganizationInviteCreate = async (
|
|
66
|
+
credsPayload,
|
|
67
|
+
invitePayload,
|
|
68
|
+
{
|
|
69
|
+
inspect = false,
|
|
70
|
+
fetchClient = linearClient,
|
|
71
|
+
queueRunOptions,
|
|
72
|
+
} = {},
|
|
73
|
+
) => {
|
|
74
|
+
|
|
75
|
+
const rejectResponse = await argsWarden.responseIfRejectingArgs({
|
|
76
|
+
credsPayload,
|
|
77
|
+
invitePayload,
|
|
78
|
+
});
|
|
79
|
+
if (rejectResponse) {
|
|
80
|
+
return rejectResponse;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
return actionSingleOrMultiple(
|
|
84
|
+
invitePayload,
|
|
85
|
+
linearOrganizationInviteCreateSingle,
|
|
86
|
+
(invitePayloadItem) => ({
|
|
87
|
+
args: [credsPayload, invitePayloadItem, { inspect, fetchClient }],
|
|
88
|
+
}),
|
|
89
|
+
{
|
|
90
|
+
...(queueRunOptions ? { queueRunOptions } : {}),
|
|
91
|
+
},
|
|
92
|
+
);
|
|
93
|
+
};
|
|
94
|
+
|
|
95
|
+
const funcApiConfig = {
|
|
96
|
+
argsWarden,
|
|
97
|
+
};
|
|
98
|
+
|
|
99
|
+
module.exports = {
|
|
100
|
+
linearOrganizationInviteCreate,
|
|
101
|
+
funcApiConfig,
|
|
102
|
+
};
|
|
103
|
+
|
|
104
|
+
/*
|
|
105
|
+
curl -X POST "http://localhost:8000/linearOrganizationInviteCreate" \
|
|
106
|
+
-H "Content-Type: application/json" \
|
|
107
|
+
-d '{
|
|
108
|
+
"credsPayload": { "credsPath": "linear" },
|
|
109
|
+
"invitePayload": [
|
|
110
|
+
{ "email": "someone@whitefoxboutique.com" },
|
|
111
|
+
{ "email": "someoneelse@whitefoxboutique.com" }
|
|
112
|
+
]
|
|
113
|
+
}'
|
|
114
|
+
*/
|
package/api/workspace.js
CHANGED
package/hosting/hosting.utils.js
CHANGED