@guayaba/workflow-piece-google-docs 0.4.1
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/.eslintrc.json +18 -0
- package/README.md +5 -0
- package/assets/logo.png +0 -0
- package/package.json +23 -0
- package/src/i18n/ca.json +52 -0
- package/src/i18n/de.json +61 -0
- package/src/i18n/es.json +61 -0
- package/src/i18n/fr.json +61 -0
- package/src/i18n/hi.json +52 -0
- package/src/i18n/id.json +52 -0
- package/src/i18n/ja.json +61 -0
- package/src/i18n/nl.json +61 -0
- package/src/i18n/pt.json +61 -0
- package/src/i18n/ru.json +52 -0
- package/src/i18n/translation.json +61 -0
- package/src/i18n/vi.json +52 -0
- package/src/i18n/zh.json +61 -0
- package/src/index.ts +46 -0
- package/src/lib/actions/append-text.ts +29 -0
- package/src/lib/actions/create-document-based-on-template.action.ts +102 -0
- package/src/lib/actions/create-document.ts +28 -0
- package/src/lib/actions/find-document.ts +126 -0
- package/src/lib/actions/read-document.action.ts +32 -0
- package/src/lib/auth.ts +84 -0
- package/src/lib/common/index.ts +56 -0
- package/src/lib/common/props.ts +54 -0
- package/src/lib/triggers/new-document.ts +101 -0
- package/tsconfig.json +16 -0
- package/tsconfig.lib.json +15 -0
package/src/lib/auth.ts
ADDED
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
import { AppConnectionValueForAuthProperty, PieceAuth, Property } from '@guayaba/workflows-framework';
|
|
2
|
+
import { AppConnectionType } from '@guayaba/workflows-shared';
|
|
3
|
+
import { google } from 'googleapis';
|
|
4
|
+
import { OAuth2Client } from 'googleapis-common';
|
|
5
|
+
|
|
6
|
+
export const googleDocsScopes = [
|
|
7
|
+
'https://www.googleapis.com/auth/documents',
|
|
8
|
+
'https://www.googleapis.com/auth/drive.readonly',
|
|
9
|
+
'https://www.googleapis.com/auth/drive',
|
|
10
|
+
];
|
|
11
|
+
|
|
12
|
+
export const googleDocsAuth = [PieceAuth.OAuth2({
|
|
13
|
+
description: '',
|
|
14
|
+
authUrl: 'https://accounts.google.com/o/oauth2/auth',
|
|
15
|
+
tokenUrl: 'https://oauth2.googleapis.com/token',
|
|
16
|
+
required: true,
|
|
17
|
+
scope: googleDocsScopes,
|
|
18
|
+
}), PieceAuth.CustomAuth({
|
|
19
|
+
displayName: 'Service Account (Advanced)',
|
|
20
|
+
description: 'Authenticate via service account from https://console.cloud.google.com/ > IAM & Admin > Service Accounts > Create Service Account > Keys > Add key. <br> <br> You can optionally use domain-wide delegation (https://support.google.com/a/answer/162106?hl=en#zippy=%2Cset-up-domain-wide-delegation-for-a-client) to access documents without adding the service account to each one. <br> <br> **Note:** Without a user email, the service account only has access to files/folders you explicitly share with it.',
|
|
21
|
+
required: true,
|
|
22
|
+
props: {
|
|
23
|
+
serviceAccount: Property.LongText({
|
|
24
|
+
displayName: 'Service Account JSON Key',
|
|
25
|
+
required: true,
|
|
26
|
+
}),
|
|
27
|
+
userEmail: Property.ShortText({
|
|
28
|
+
displayName: 'User Email',
|
|
29
|
+
required: false,
|
|
30
|
+
description: 'Email address of the user to impersonate for domain-wide delegation.',
|
|
31
|
+
}),
|
|
32
|
+
},
|
|
33
|
+
validate: async ({ auth }) => {
|
|
34
|
+
try {
|
|
35
|
+
await getAccessToken({
|
|
36
|
+
type: AppConnectionType.CUSTOM_AUTH,
|
|
37
|
+
props: { ...auth },
|
|
38
|
+
});
|
|
39
|
+
} catch (e) {
|
|
40
|
+
return {
|
|
41
|
+
valid: false,
|
|
42
|
+
error: (e as Error).message,
|
|
43
|
+
};
|
|
44
|
+
}
|
|
45
|
+
return {
|
|
46
|
+
valid: true,
|
|
47
|
+
};
|
|
48
|
+
},
|
|
49
|
+
})];
|
|
50
|
+
|
|
51
|
+
export type GoogleDocsAuthValue = AppConnectionValueForAuthProperty<typeof googleDocsAuth>;
|
|
52
|
+
|
|
53
|
+
export async function createGoogleClient(auth: GoogleDocsAuthValue): Promise<OAuth2Client> {
|
|
54
|
+
if (auth.type === AppConnectionType.CUSTOM_AUTH) {
|
|
55
|
+
let serviceAccount;
|
|
56
|
+
try {
|
|
57
|
+
serviceAccount = JSON.parse(auth.props.serviceAccount);
|
|
58
|
+
} catch {
|
|
59
|
+
throw new Error('Invalid Service Account JSON Key. Please provide a valid JSON string.');
|
|
60
|
+
}
|
|
61
|
+
return new google.auth.JWT({
|
|
62
|
+
email: serviceAccount.client_email,
|
|
63
|
+
key: serviceAccount.private_key,
|
|
64
|
+
scopes: googleDocsScopes,
|
|
65
|
+
subject: auth.props.userEmail,
|
|
66
|
+
});
|
|
67
|
+
}
|
|
68
|
+
const authClient = new OAuth2Client();
|
|
69
|
+
authClient.setCredentials(auth);
|
|
70
|
+
return authClient;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
export const getAccessToken = async (auth: GoogleDocsAuthValue): Promise<string> => {
|
|
74
|
+
if (auth.type === AppConnectionType.CUSTOM_AUTH) {
|
|
75
|
+
const googleClient = await createGoogleClient(auth);
|
|
76
|
+
const response = await googleClient.getAccessToken();
|
|
77
|
+
if (response.token) {
|
|
78
|
+
return response.token;
|
|
79
|
+
} else {
|
|
80
|
+
throw new Error('Could not retrieve access token from service account json');
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
return auth.access_token;
|
|
84
|
+
};
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
/* eslint-disable @typescript-eslint/no-explicit-any */
|
|
2
|
+
import { Property } from '@guayaba/workflows-framework';
|
|
3
|
+
import { HttpMethod, httpClient, AuthenticationType } from '@guayaba/workflows-common';
|
|
4
|
+
|
|
5
|
+
export const docsCommon = {
|
|
6
|
+
baseUrl: 'https://docs.googleapis.com/v1',
|
|
7
|
+
title: Property.ShortText({
|
|
8
|
+
displayName: 'Document Title',
|
|
9
|
+
required: true,
|
|
10
|
+
}),
|
|
11
|
+
body: Property.LongText({
|
|
12
|
+
displayName: 'Document Content',
|
|
13
|
+
required: true,
|
|
14
|
+
}),
|
|
15
|
+
|
|
16
|
+
// Creates an empty document with the title provided
|
|
17
|
+
createDocument: async (title: string, accessToken: string) => {
|
|
18
|
+
const createRequest = await httpClient.sendRequest({
|
|
19
|
+
url: `${docsCommon.baseUrl}/documents`,
|
|
20
|
+
method: HttpMethod.POST,
|
|
21
|
+
authentication: {
|
|
22
|
+
type: AuthenticationType.BEARER_TOKEN,
|
|
23
|
+
token: accessToken,
|
|
24
|
+
},
|
|
25
|
+
body: {
|
|
26
|
+
title: title,
|
|
27
|
+
},
|
|
28
|
+
});
|
|
29
|
+
|
|
30
|
+
return createRequest.body;
|
|
31
|
+
},
|
|
32
|
+
|
|
33
|
+
// Writes provided content to the end of an existing document
|
|
34
|
+
writeToDocument: async (documentId: string, body: string, accessToken: string) => {
|
|
35
|
+
const writeRequest = await httpClient.sendRequest({
|
|
36
|
+
url: `${docsCommon.baseUrl}/documents/${documentId}:batchUpdate`,
|
|
37
|
+
method: HttpMethod.POST,
|
|
38
|
+
authentication: {
|
|
39
|
+
type: AuthenticationType.BEARER_TOKEN,
|
|
40
|
+
token: accessToken,
|
|
41
|
+
},
|
|
42
|
+
body: {
|
|
43
|
+
requests: [
|
|
44
|
+
{
|
|
45
|
+
insertText: {
|
|
46
|
+
text: body,
|
|
47
|
+
endOfSegmentLocation: {},
|
|
48
|
+
},
|
|
49
|
+
},
|
|
50
|
+
],
|
|
51
|
+
},
|
|
52
|
+
});
|
|
53
|
+
|
|
54
|
+
return writeRequest.body;
|
|
55
|
+
},
|
|
56
|
+
};
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import { googleDocsAuth, createGoogleClient, GoogleDocsAuthValue } from '../auth';
|
|
2
|
+
import { DropdownOption, Property } from '@guayaba/workflows-framework';
|
|
3
|
+
import { google, drive_v3 } from 'googleapis';
|
|
4
|
+
|
|
5
|
+
export const folderIdProp = Property.Dropdown({
|
|
6
|
+
displayName: 'Folder',
|
|
7
|
+
refreshers: [],
|
|
8
|
+
auth: googleDocsAuth,
|
|
9
|
+
required: false,
|
|
10
|
+
options: async ({ auth }) => {
|
|
11
|
+
if (!auth) {
|
|
12
|
+
return {
|
|
13
|
+
disabled: true,
|
|
14
|
+
placeholder: 'Please connect to your Google Drive account.',
|
|
15
|
+
options: [],
|
|
16
|
+
};
|
|
17
|
+
}
|
|
18
|
+
const authValue = auth as GoogleDocsAuthValue;
|
|
19
|
+
|
|
20
|
+
const authClient = await createGoogleClient(authValue);
|
|
21
|
+
|
|
22
|
+
const drive = google.drive({ version: 'v3', auth: authClient });
|
|
23
|
+
|
|
24
|
+
const options: DropdownOption<string>[] = [];
|
|
25
|
+
|
|
26
|
+
let nextPageToken;
|
|
27
|
+
|
|
28
|
+
do {
|
|
29
|
+
const response: any = await drive.files.list({
|
|
30
|
+
q: "mimeType='application/vnd.google-apps.folder' and trashed = false",
|
|
31
|
+
supportsAllDrives: true,
|
|
32
|
+
orderBy:'createdTime desc',
|
|
33
|
+
includeItemsFromAllDrives: true,
|
|
34
|
+
pageToken: nextPageToken,
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
const fileList: drive_v3.Schema$FileList = response.data;
|
|
38
|
+
|
|
39
|
+
if (fileList.files) {
|
|
40
|
+
for (const file of fileList.files)
|
|
41
|
+
options.push({
|
|
42
|
+
label: file.name!,
|
|
43
|
+
value: file.id!,
|
|
44
|
+
});
|
|
45
|
+
}
|
|
46
|
+
nextPageToken = response.data.nextPageToken;
|
|
47
|
+
} while (nextPageToken);
|
|
48
|
+
|
|
49
|
+
return {
|
|
50
|
+
disabled: false,
|
|
51
|
+
options,
|
|
52
|
+
};
|
|
53
|
+
},
|
|
54
|
+
});
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
import { googleDocsAuth, createGoogleClient } from '../auth';
|
|
2
|
+
import { DedupeStrategy, Polling, pollingHelper } from '@guayaba/workflows-common';
|
|
3
|
+
import {
|
|
4
|
+
AppConnectionValueForAuthProperty,
|
|
5
|
+
createTrigger,
|
|
6
|
+
TriggerStrategy,
|
|
7
|
+
} from '@guayaba/workflows-framework';
|
|
8
|
+
import { folderIdProp } from '../common/props';
|
|
9
|
+
import dayjs from 'dayjs';
|
|
10
|
+
import { google, drive_v3 } from 'googleapis';
|
|
11
|
+
|
|
12
|
+
type Props = {
|
|
13
|
+
folderId?: string;
|
|
14
|
+
};
|
|
15
|
+
|
|
16
|
+
const polling: Polling<AppConnectionValueForAuthProperty<typeof googleDocsAuth>, Props> = {
|
|
17
|
+
strategy: DedupeStrategy.TIMEBASED,
|
|
18
|
+
async items({ auth, propsValue, lastFetchEpochMS }) {
|
|
19
|
+
const folderId = propsValue.folderId;
|
|
20
|
+
|
|
21
|
+
const q = ["mimeType='application/vnd.google-apps.document'", 'trashed = false'];
|
|
22
|
+
if (lastFetchEpochMS) {
|
|
23
|
+
q.push(`createdTime > '${dayjs(lastFetchEpochMS).toISOString()}'`);
|
|
24
|
+
}
|
|
25
|
+
if (folderId) {
|
|
26
|
+
q.push(`'${folderId}' in parents`);
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
const authClient = await createGoogleClient(auth);
|
|
30
|
+
|
|
31
|
+
const drive = google.drive({ version: 'v3', auth: authClient });
|
|
32
|
+
|
|
33
|
+
let nextPageToken;
|
|
34
|
+
const items = [];
|
|
35
|
+
|
|
36
|
+
do {
|
|
37
|
+
const response: any = await drive.files.list({
|
|
38
|
+
q: q.join(' and '),
|
|
39
|
+
fields: '*',
|
|
40
|
+
orderBy: 'createdTime desc',
|
|
41
|
+
supportsAllDrives: true,
|
|
42
|
+
includeItemsFromAllDrives: true,
|
|
43
|
+
pageToken: nextPageToken,
|
|
44
|
+
});
|
|
45
|
+
|
|
46
|
+
const fileList: drive_v3.Schema$FileList = response.data;
|
|
47
|
+
|
|
48
|
+
if (fileList.files) {
|
|
49
|
+
items.push(...fileList.files);
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
if (lastFetchEpochMS === 0) break;
|
|
53
|
+
|
|
54
|
+
nextPageToken = response.data.nextPageToken;
|
|
55
|
+
} while (nextPageToken);
|
|
56
|
+
|
|
57
|
+
return items.map((item) => ({
|
|
58
|
+
epochMilliSeconds: dayjs(item.createdTime).valueOf(),
|
|
59
|
+
data: item,
|
|
60
|
+
}));
|
|
61
|
+
},
|
|
62
|
+
};
|
|
63
|
+
|
|
64
|
+
export const newDocumentTrigger = createTrigger({
|
|
65
|
+
auth: googleDocsAuth,
|
|
66
|
+
name: 'new-document',
|
|
67
|
+
displayName: 'New Document',
|
|
68
|
+
description: 'Triggers when a new document is added to a specific folder(optional).',
|
|
69
|
+
type: TriggerStrategy.POLLING,
|
|
70
|
+
props: {
|
|
71
|
+
folderId: folderIdProp,
|
|
72
|
+
},
|
|
73
|
+
async onEnable(context) {
|
|
74
|
+
await pollingHelper.onEnable(polling, {
|
|
75
|
+
auth: context.auth,
|
|
76
|
+
store: context.store,
|
|
77
|
+
propsValue: context.propsValue,
|
|
78
|
+
});
|
|
79
|
+
},
|
|
80
|
+
async onDisable(context) {
|
|
81
|
+
await pollingHelper.onDisable(polling, {
|
|
82
|
+
auth: context.auth,
|
|
83
|
+
store: context.store,
|
|
84
|
+
propsValue: context.propsValue,
|
|
85
|
+
});
|
|
86
|
+
},
|
|
87
|
+
async test(context) {
|
|
88
|
+
return await pollingHelper.test(polling, context);
|
|
89
|
+
},
|
|
90
|
+
async run(context) {
|
|
91
|
+
return await pollingHelper.poll(polling, context);
|
|
92
|
+
},
|
|
93
|
+
sampleData: {
|
|
94
|
+
kind: 'drive#file',
|
|
95
|
+
mimeType: 'application/vnd.google-apps.document',
|
|
96
|
+
webViewLink:
|
|
97
|
+
'https://docs.google.com/document/d/1_9xjsrYFgHVvgqYwAJ8KcsDcNU/edit?usp=drivesdk',
|
|
98
|
+
id: '1_9xjsrYFgHVvgqYwAJ8KcsDcN3AzPelsux',
|
|
99
|
+
name: 'Test Document',
|
|
100
|
+
},
|
|
101
|
+
});
|
package/tsconfig.json
ADDED
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
{
|
|
2
|
+
"extends": "../../../../tsconfig.base.json",
|
|
3
|
+
"files": [],
|
|
4
|
+
"include": [],
|
|
5
|
+
"references": [
|
|
6
|
+
{
|
|
7
|
+
"path": "./tsconfig.lib.json"
|
|
8
|
+
}
|
|
9
|
+
],
|
|
10
|
+
"compilerOptions": {
|
|
11
|
+
"forceConsistentCasingInFileNames": true,
|
|
12
|
+
"strict": true,
|
|
13
|
+
"noImplicitReturns": true,
|
|
14
|
+
"noFallthroughCasesInSwitch": true
|
|
15
|
+
}
|
|
16
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
{
|
|
2
|
+
"extends": "./tsconfig.json",
|
|
3
|
+
"compilerOptions": {
|
|
4
|
+
"module": "commonjs",
|
|
5
|
+
"rootDir": ".",
|
|
6
|
+
"baseUrl": ".",
|
|
7
|
+
"paths": {},
|
|
8
|
+
"outDir": "./dist",
|
|
9
|
+
"declaration": true,
|
|
10
|
+
"declarationMap": true,
|
|
11
|
+
"types": ["node"]
|
|
12
|
+
},
|
|
13
|
+
"exclude": ["jest.config.ts", "src/**/*.spec.ts", "src/**/*.test.ts"],
|
|
14
|
+
"include": ["src/**/*.ts"]
|
|
15
|
+
}
|