@guayaba/workflow-piece-google-cloud-storage 0.1.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.
@@ -0,0 +1,171 @@
1
+ import { createTrigger, TriggerStrategy, Property } from '@guayaba/workflows-framework';
2
+ import { googleCloudStorageAuth } from '../common/auth';
3
+ import { gcsCommon } from '../common/client';
4
+ import { bucketDropdown, projectIdProperty } from '../common/props';
5
+ import { HttpMethod } from '@guayaba/workflows-common';
6
+
7
+ interface TriggerData {
8
+ topicName: string;
9
+ subscriptionName: string;
10
+ notificationId: string;
11
+ }
12
+
13
+ export const objectUpdated = createTrigger({
14
+ auth: googleCloudStorageAuth,
15
+ name: 'object_updated',
16
+ displayName: 'Object Updated',
17
+ description: 'Triggers when an existing object is updated in a bucket',
18
+ props: {
19
+ projectId: projectIdProperty,
20
+ bucket: bucketDropdown,
21
+ prefix: Property.ShortText({
22
+ displayName: 'Prefix Filter',
23
+ description: 'Only trigger for objects with this prefix',
24
+ required: false,
25
+ }),
26
+ },
27
+ type: TriggerStrategy.WEBHOOK,
28
+ sampleData: {
29
+ kind: 'storage#object',
30
+ id: 'example-bucket/example-object/1234567890',
31
+ name: 'example-object.txt',
32
+ bucket: 'example-bucket',
33
+ generation: '1234567890',
34
+ contentType: 'text/plain',
35
+ timeCreated: '2023-01-01T00:00:00.000Z',
36
+ updated: '2023-01-01T01:00:00.000Z',
37
+ size: '2048',
38
+ },
39
+ onEnable: async (context) => {
40
+ const { projectId, bucket, prefix } = context.propsValue;
41
+ const auth = context.auth;
42
+
43
+ // Generate unique names for this trigger instance
44
+ const triggerId = `ap-gcs-update-${bucket}-${Date.now()}`;
45
+ const topicName = `projects/${projectId}/topics/${triggerId}`;
46
+ const subscriptionName = `projects/${projectId}/subscriptions/${triggerId}`;
47
+
48
+ try {
49
+ // 1. Create Pub/Sub topic
50
+ await gcsCommon.makePubSubRequest(
51
+ HttpMethod.PUT,
52
+ `/projects/${projectId}/topics/${triggerId}`,
53
+ auth.access_token
54
+ );
55
+
56
+ // 2. Create GCS notification configuration for metadata updates
57
+ const notificationConfig: any = {
58
+ topic: topicName,
59
+ payload_format: 'JSON_API_V1',
60
+ event_types: ['OBJECT_METADATA_UPDATE'],
61
+ };
62
+
63
+ if (prefix) {
64
+ notificationConfig.object_name_prefix = prefix;
65
+ }
66
+
67
+ const notificationResponse = await gcsCommon.makeGCSRequest(
68
+ HttpMethod.POST,
69
+ `/b/${bucket}/notificationConfigs`,
70
+ auth.access_token,
71
+ notificationConfig
72
+ );
73
+
74
+ // 3. Create Pub/Sub subscription with webhook push
75
+ const subscriptionConfig = {
76
+ topic: topicName,
77
+ pushConfig: {
78
+ pushEndpoint: context.webhookUrl,
79
+ },
80
+ ackDeadlineSeconds: 60,
81
+ };
82
+
83
+ await gcsCommon.makePubSubRequest(
84
+ HttpMethod.PUT,
85
+ `/projects/${projectId}/subscriptions/${triggerId}`,
86
+ auth.access_token,
87
+ subscriptionConfig
88
+ );
89
+
90
+ // Store trigger data for cleanup
91
+ await context.store.put<TriggerData>('_trigger', {
92
+ topicName: triggerId,
93
+ subscriptionName: triggerId,
94
+ notificationId: notificationResponse.id,
95
+ });
96
+
97
+ } catch (error) {
98
+ // Cleanup on failure
99
+ try {
100
+ await gcsCommon.makePubSubRequest(
101
+ HttpMethod.DELETE,
102
+ `/projects/${projectId}/subscriptions/${triggerId}`,
103
+ auth.access_token
104
+ );
105
+ } catch (e) { /* ignore */ }
106
+
107
+ try {
108
+ await gcsCommon.makePubSubRequest(
109
+ HttpMethod.DELETE,
110
+ `/projects/${projectId}/topics/${triggerId}`,
111
+ auth.access_token
112
+ );
113
+ } catch (e) { /* ignore */ }
114
+
115
+ throw new Error(`Failed to setup Pub/Sub notifications: ${(error as any)?.message || 'Unknown error'}`);
116
+ }
117
+ },
118
+ onDisable: async (context) => {
119
+ const triggerData = await context.store.get<TriggerData>('_trigger');
120
+ if (!triggerData) return;
121
+
122
+ const { projectId } = context.propsValue;
123
+ const { bucket } = context.propsValue;
124
+ const auth = context.auth;
125
+
126
+ // Clean up in reverse order
127
+ try {
128
+ // Delete subscription
129
+ await gcsCommon.makePubSubRequest(
130
+ HttpMethod.DELETE,
131
+ `/projects/${projectId}/subscriptions/${triggerData.subscriptionName}`,
132
+ auth.access_token
133
+ );
134
+ } catch (e) { /* ignore */ }
135
+
136
+ try {
137
+ // Delete notification config
138
+ await gcsCommon.makeGCSRequest(
139
+ HttpMethod.DELETE,
140
+ `/b/${bucket}/notificationConfigs/${triggerData.notificationId}`,
141
+ auth.access_token
142
+ );
143
+ } catch (e) { /* ignore */ }
144
+
145
+ try {
146
+ // Delete topic
147
+ await gcsCommon.makePubSubRequest(
148
+ HttpMethod.DELETE,
149
+ `/projects/${projectId}/topics/${triggerData.topicName}`,
150
+ auth.access_token
151
+ );
152
+ } catch (e) { /* ignore */ }
153
+ },
154
+ run: async (context) => {
155
+ const payload = context.payload.body as any;
156
+
157
+ if (!payload?.message?.data) {
158
+ return [];
159
+ }
160
+
161
+ // Decode Pub/Sub message
162
+ const messageData = JSON.parse(
163
+ Buffer.from(payload.message.data, 'base64').toString()
164
+ );
165
+
166
+ // Extract GCS object from notification payload
167
+ const gcsObject = messageData;
168
+
169
+ return [gcsObject];
170
+ },
171
+ });
package/tsconfig.json ADDED
@@ -0,0 +1,20 @@
1
+ {
2
+ "extends": "../../../../tsconfig.base.json",
3
+ "compilerOptions": {
4
+ "module": "commonjs",
5
+ "forceConsistentCasingInFileNames": true,
6
+ "strict": true,
7
+ "importHelpers": true,
8
+ "noImplicitOverride": true,
9
+ "noImplicitReturns": true,
10
+ "noFallthroughCasesInSwitch": true,
11
+ "noPropertyAccessFromIndexSignature": true
12
+ },
13
+ "files": [],
14
+ "include": [],
15
+ "references": [
16
+ {
17
+ "path": "./tsconfig.lib.json"
18
+ }
19
+ ]
20
+ }
@@ -0,0 +1,13 @@
1
+ {
2
+ "extends": "./tsconfig.json",
3
+ "compilerOptions": {
4
+ "rootDir": ".",
5
+ "baseUrl": ".",
6
+ "paths": {},
7
+ "outDir": "./dist",
8
+ "declaration": true,
9
+ "declarationMap": true,
10
+ "types": ["node"]
11
+ },
12
+ "include": ["src/**/*.ts"]
13
+ }