@divyacon/piece-google-cloud-storage 0.0.0-stage → 0.1.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.
Files changed (93) hide show
  1. package/LICENSE.MIT-AP +24 -0
  2. package/dist/index.d.ts +2 -0
  3. package/dist/index.d.ts.map +1 -0
  4. package/dist/index.js +50 -0
  5. package/dist/index.js.map +1 -0
  6. package/dist/lib/actions/clone-object.d.ts +9 -0
  7. package/dist/lib/actions/clone-object.d.ts.map +1 -0
  8. package/dist/lib/actions/clone-object.js +77 -0
  9. package/dist/lib/actions/clone-object.js.map +1 -0
  10. package/dist/lib/actions/create-bucket-acl.d.ts +7 -0
  11. package/dist/lib/actions/create-bucket-acl.d.ts.map +1 -0
  12. package/dist/lib/actions/create-bucket-acl.js +60 -0
  13. package/dist/lib/actions/create-bucket-acl.js.map +1 -0
  14. package/dist/lib/actions/create-bucket-default-object-acl.d.ts +7 -0
  15. package/dist/lib/actions/create-bucket-default-object-acl.d.ts.map +1 -0
  16. package/dist/lib/actions/create-bucket-default-object-acl.js +60 -0
  17. package/dist/lib/actions/create-bucket-default-object-acl.js.map +1 -0
  18. package/dist/lib/actions/create-bucket.d.ts +10 -0
  19. package/dist/lib/actions/create-bucket.d.ts.map +1 -0
  20. package/dist/lib/actions/create-bucket.js +80 -0
  21. package/dist/lib/actions/create-bucket.js.map +1 -0
  22. package/dist/lib/actions/create-object-acl.d.ts +9 -0
  23. package/dist/lib/actions/create-object-acl.d.ts.map +1 -0
  24. package/dist/lib/actions/create-object-acl.js +72 -0
  25. package/dist/lib/actions/create-object-acl.js.map +1 -0
  26. package/dist/lib/actions/delete-bucket-acl.d.ts +6 -0
  27. package/dist/lib/actions/delete-bucket-acl.d.ts.map +1 -0
  28. package/dist/lib/actions/delete-bucket-acl.js +56 -0
  29. package/dist/lib/actions/delete-bucket-acl.js.map +1 -0
  30. package/dist/lib/actions/delete-bucket-default-object-acl.d.ts +6 -0
  31. package/dist/lib/actions/delete-bucket-default-object-acl.d.ts.map +1 -0
  32. package/dist/lib/actions/delete-bucket-default-object-acl.js +56 -0
  33. package/dist/lib/actions/delete-bucket-default-object-acl.js.map +1 -0
  34. package/dist/lib/actions/delete-empty-bucket.d.ts +5 -0
  35. package/dist/lib/actions/delete-empty-bucket.d.ts.map +1 -0
  36. package/dist/lib/actions/delete-empty-bucket.js +50 -0
  37. package/dist/lib/actions/delete-empty-bucket.js.map +1 -0
  38. package/dist/lib/actions/delete-object-acl.d.ts +8 -0
  39. package/dist/lib/actions/delete-object-acl.d.ts.map +1 -0
  40. package/dist/lib/actions/delete-object-acl.js +71 -0
  41. package/dist/lib/actions/delete-object-acl.js.map +1 -0
  42. package/dist/lib/actions/delete-object.d.ts +7 -0
  43. package/dist/lib/actions/delete-object.d.ts.map +1 -0
  44. package/dist/lib/actions/delete-object.js +66 -0
  45. package/dist/lib/actions/delete-object.js.map +1 -0
  46. package/dist/lib/actions/search-buckets.d.ts +9 -0
  47. package/dist/lib/actions/search-buckets.d.ts.map +1 -0
  48. package/dist/lib/actions/search-buckets.js +96 -0
  49. package/dist/lib/actions/search-buckets.js.map +1 -0
  50. package/dist/lib/actions/search-objects.d.ts +12 -0
  51. package/dist/lib/actions/search-objects.d.ts.map +1 -0
  52. package/dist/lib/actions/search-objects.js +105 -0
  53. package/dist/lib/actions/search-objects.js.map +1 -0
  54. package/dist/lib/common/auth.d.ts +2 -0
  55. package/dist/lib/common/auth.d.ts.map +1 -0
  56. package/dist/lib/common/auth.js +20 -0
  57. package/dist/lib/common/auth.js.map +1 -0
  58. package/dist/lib/common/client.d.ts +9 -0
  59. package/dist/lib/common/client.d.ts.map +1 -0
  60. package/dist/lib/common/client.js +48 -0
  61. package/dist/lib/common/client.js.map +1 -0
  62. package/dist/lib/common/props.d.ts +12 -0
  63. package/dist/lib/common/props.d.ts.map +1 -0
  64. package/dist/lib/common/props.js +231 -0
  65. package/dist/lib/common/props.js.map +1 -0
  66. package/dist/lib/triggers/new-object-created.d.ts +19 -0
  67. package/dist/lib/triggers/new-object-created.d.ts.map +1 -0
  68. package/dist/lib/triggers/new-object-created.js +127 -0
  69. package/dist/lib/triggers/new-object-created.js.map +1 -0
  70. package/dist/lib/triggers/object-updated.d.ts +19 -0
  71. package/dist/lib/triggers/object-updated.d.ts.map +1 -0
  72. package/dist/lib/triggers/object-updated.js +127 -0
  73. package/dist/lib/triggers/object-updated.js.map +1 -0
  74. package/package.json +44 -4
  75. package/src/index.ts +46 -0
  76. package/src/lib/actions/clone-object.ts +75 -0
  77. package/src/lib/actions/create-bucket-acl.ts +58 -0
  78. package/src/lib/actions/create-bucket-default-object-acl.ts +58 -0
  79. package/src/lib/actions/create-bucket.ts +75 -0
  80. package/src/lib/actions/create-object-acl.ts +81 -0
  81. package/src/lib/actions/delete-bucket-acl.ts +52 -0
  82. package/src/lib/actions/delete-bucket-default-object-acl.ts +52 -0
  83. package/src/lib/actions/delete-empty-bucket.ts +42 -0
  84. package/src/lib/actions/delete-object-acl.ts +64 -0
  85. package/src/lib/actions/delete-object.ts +59 -0
  86. package/src/lib/actions/search-buckets.ts +92 -0
  87. package/src/lib/actions/search-objects.ts +101 -0
  88. package/src/lib/common/auth.ts +15 -0
  89. package/src/lib/common/client.ts +63 -0
  90. package/src/lib/common/props.ts +249 -0
  91. package/src/lib/triggers/new-object-created.ts +174 -0
  92. package/src/lib/triggers/object-updated.ts +174 -0
  93. package/README.md +0 -3
@@ -0,0 +1,249 @@
1
+ import { Property, OAuth2PropertyValue } from '@agnocon/pieces-framework';
2
+ import { gcsCommon } from './client';
3
+ import { HttpMethod } from '@agnocon/pieces-common';
4
+ import { googleCloudStorageAuth } from './auth';
5
+
6
+ export const bucketDropdown = Property.Dropdown<string,true,typeof googleCloudStorageAuth>({
7
+ displayName: 'Bucket',
8
+ required: true,
9
+ refreshers: ['projectId'],
10
+ auth: googleCloudStorageAuth,
11
+ options: async ({ auth, projectId }) => {
12
+ if (!auth || !projectId) {
13
+ return {
14
+ disabled: true,
15
+ options: [],
16
+ placeholder: projectId
17
+ ? 'Please connect your account first'
18
+ : 'Please enter project ID first',
19
+ };
20
+ }
21
+
22
+ try {
23
+ const authValue = auth as OAuth2PropertyValue;
24
+ const response = await gcsCommon.makeRequest(
25
+ HttpMethod.GET,
26
+ `/b?project=${projectId}`,
27
+ authValue.access_token
28
+ );
29
+
30
+ return {
31
+ disabled: false,
32
+ options:
33
+ response.items?.map((bucket: any) => ({
34
+ label: bucket.name,
35
+ value: bucket.name,
36
+ })) || [],
37
+ };
38
+ } catch (error) {
39
+ return {
40
+ disabled: true,
41
+ options: [],
42
+ placeholder: 'Failed to load buckets',
43
+ };
44
+ }
45
+ },
46
+ });
47
+
48
+ export const objectDropdown = (bucketProperty: string) =>
49
+ Property.Dropdown<string,true,typeof googleCloudStorageAuth>({
50
+ displayName: 'Object',
51
+ required: true,
52
+ auth: googleCloudStorageAuth,
53
+ refreshers: [bucketProperty],
54
+ options: async ({ auth, [bucketProperty]: bucket }) => {
55
+ if (!auth || !bucket) {
56
+ return {
57
+ disabled: true,
58
+ options: [],
59
+ placeholder: bucket
60
+ ? 'Please connect your account first'
61
+ : 'Please select a bucket first',
62
+ };
63
+ }
64
+
65
+ try {
66
+ const authValue = auth as OAuth2PropertyValue;
67
+ const response = await gcsCommon.makeRequest(
68
+ HttpMethod.GET,
69
+ `/b/${bucket}/o?maxResults=100`,
70
+ authValue.access_token
71
+ );
72
+
73
+ return {
74
+ disabled: false,
75
+ options:
76
+ response.items?.map((object: any) => ({
77
+ label: object.name,
78
+ value: object.name,
79
+ })) || [],
80
+ };
81
+ } catch (error) {
82
+ return {
83
+ disabled: true,
84
+ options: [],
85
+ placeholder: 'Failed to load objects',
86
+ };
87
+ }
88
+ },
89
+ });
90
+
91
+ export const projectIdProperty = Property.Dropdown<string,true,typeof googleCloudStorageAuth>({
92
+ displayName: 'Project',
93
+ required: true,
94
+ auth: googleCloudStorageAuth,
95
+ refreshers: [],
96
+ options: async ({ auth }) => {
97
+ if (!auth) {
98
+ return {
99
+ disabled: true,
100
+ options: [],
101
+ placeholder: 'Please connect your account first',
102
+ };
103
+ }
104
+
105
+ try {
106
+ const authValue = auth as OAuth2PropertyValue;
107
+ // Use Google Cloud Resource Manager API to list projects
108
+ const response = await gcsCommon.makeRequest(
109
+ HttpMethod.GET,
110
+ 'https://cloudresourcemanager.googleapis.com/v1/projects?filter=lifecycleState:ACTIVE',
111
+ authValue.access_token
112
+ );
113
+
114
+ return {
115
+ disabled: false,
116
+ options: response.projects?.map((project: any) => ({
117
+ label: `${project.displayName || project.name} (${project.projectId})`,
118
+ value: project.projectId,
119
+ })) || [],
120
+ };
121
+ } catch (error) {
122
+ return {
123
+ disabled: true,
124
+ options: [],
125
+ placeholder: 'Failed to load projects. Check your permissions.',
126
+ };
127
+ }
128
+ },
129
+ });
130
+
131
+ export const bucketNameProperty = Property.ShortText({
132
+ displayName: 'Bucket Name',
133
+ description: 'Unique name for your bucket (must be globally unique, 3-63 characters)',
134
+ required: true,
135
+ });
136
+
137
+ export const objectNameProperty = Property.ShortText({
138
+ displayName: 'Object Name',
139
+ required: true,
140
+ });
141
+
142
+ export const locationProperty = Property.StaticDropdown({
143
+ displayName: 'Location',
144
+ required: false,
145
+ options: {
146
+ options: [
147
+ // Multi-region
148
+ { label: 'US (Multi-region)', value: 'US' },
149
+ { label: 'EU (Multi-region)', value: 'EU' },
150
+ { label: 'ASIA (Multi-region)', value: 'ASIA' },
151
+ // US regions
152
+ { label: 'us-central1 (Iowa)', value: 'us-central1' },
153
+ { label: 'us-east1 (South Carolina)', value: 'us-east1' },
154
+ { label: 'us-east4 (Northern Virginia)', value: 'us-east4' },
155
+ { label: 'us-west1 (Oregon)', value: 'us-west1' },
156
+ { label: 'us-west2 (Los Angeles)', value: 'us-west2' },
157
+ { label: 'us-west3 (Salt Lake City)', value: 'us-west3' },
158
+ { label: 'us-west4 (Las Vegas)', value: 'us-west4' },
159
+ { label: 'us-south1 (Dallas)', value: 'us-south1' },
160
+ // Europe regions
161
+ { label: 'europe-central2 (Warsaw)', value: 'europe-central2' },
162
+ { label: 'europe-north1 (Finland)', value: 'europe-north1' },
163
+ { label: 'europe-southwest1 (Madrid)', value: 'europe-southwest1' },
164
+ { label: 'europe-west1 (Belgium)', value: 'europe-west1' },
165
+ { label: 'europe-west2 (London)', value: 'europe-west2' },
166
+ { label: 'europe-west3 (Frankfurt)', value: 'europe-west3' },
167
+ { label: 'europe-west4 (Netherlands)', value: 'europe-west4' },
168
+ { label: 'europe-west6 (Zurich)', value: 'europe-west6' },
169
+ { label: 'europe-west8 (Milan)', value: 'europe-west8' },
170
+ { label: 'europe-west9 (Paris)', value: 'europe-west9' },
171
+ // Asia regions
172
+ { label: 'asia-east1 (Taiwan)', value: 'asia-east1' },
173
+ { label: 'asia-east2 (Hong Kong)', value: 'asia-east2' },
174
+ { label: 'asia-northeast1 (Tokyo)', value: 'asia-northeast1' },
175
+ { label: 'asia-northeast2 (Osaka)', value: 'asia-northeast2' },
176
+ { label: 'asia-northeast3 (Seoul)', value: 'asia-northeast3' },
177
+ { label: 'asia-south1 (Mumbai)', value: 'asia-south1' },
178
+ { label: 'asia-south2 (Delhi)', value: 'asia-south2' },
179
+ { label: 'asia-southeast1 (Singapore)', value: 'asia-southeast1' },
180
+ { label: 'asia-southeast2 (Jakarta)', value: 'asia-southeast2' },
181
+ // Other regions
182
+ { label: 'australia-southeast1 (Sydney)', value: 'australia-southeast1' },
183
+ { label: 'australia-southeast2 (Melbourne)', value: 'australia-southeast2' },
184
+ { label: 'northamerica-northeast1 (Montreal)', value: 'northamerica-northeast1' },
185
+ { label: 'northamerica-northeast2 (Toronto)', value: 'northamerica-northeast2' },
186
+ { label: 'southamerica-east1 (São Paulo)', value: 'southamerica-east1' },
187
+ { label: 'southamerica-west1 (Santiago)', value: 'southamerica-west1' },
188
+ ],
189
+ },
190
+ });
191
+
192
+ export const storageClassProperty = Property.StaticDropdown({
193
+ displayName: 'Storage Class',
194
+ required: false,
195
+ options: {
196
+ options: [
197
+ { label: 'Standard', value: 'STANDARD' },
198
+ { label: 'Nearline', value: 'NEARLINE' },
199
+ { label: 'Coldline', value: 'COLDLINE' },
200
+ { label: 'Archive', value: 'ARCHIVE' },
201
+ { label: 'Multi-regional', value: 'MULTI_REGIONAL' },
202
+ { label: 'Regional', value: 'REGIONAL' },
203
+ { label: 'Durable Reduced Availability', value: 'DURABLE_REDUCED_AVAILABILITY' },
204
+ ],
205
+ },
206
+ });
207
+
208
+ export const aclEntityProperty = Property.ShortText({
209
+ displayName: 'Entity',
210
+ description: 'The entity to grant access to. Must include the entity type prefix. Format: user-emailAddress, group-groupId, group-emailAddress, domain-domainName, project-team-projectId, allUsers, or allAuthenticatedUsers. Examples: user-liz@example.com, group-mygroup@googlegroups.com, domain-example.com, allUsers',
211
+ required: true,
212
+ });
213
+
214
+ export const aclRoleProperty = Property.StaticDropdown({
215
+ displayName: 'Role',
216
+ required: true,
217
+ options: {
218
+ options: [
219
+ { label: 'Reader', value: 'READER' },
220
+ { label: 'Writer', value: 'WRITER' },
221
+ { label: 'Owner', value: 'OWNER' },
222
+ ],
223
+ },
224
+ });
225
+
226
+ // For bucket ACLs - supports OWNER, WRITER, READER
227
+ export const bucketAclRoleProperty = Property.StaticDropdown({
228
+ displayName: 'Role',
229
+ required: true,
230
+ options: {
231
+ options: [
232
+ { label: 'Reader', value: 'READER' },
233
+ { label: 'Writer', value: 'WRITER' },
234
+ { label: 'Owner', value: 'OWNER' },
235
+ ],
236
+ },
237
+ });
238
+
239
+ // For object ACLs - supports OWNER, READER only
240
+ export const objectAclRoleProperty = Property.StaticDropdown({
241
+ displayName: 'Role',
242
+ required: true,
243
+ options: {
244
+ options: [
245
+ { label: 'Reader', value: 'READER' },
246
+ { label: 'Owner', value: 'OWNER' },
247
+ ],
248
+ },
249
+ });
@@ -0,0 +1,174 @@
1
+ import { createTrigger, TriggerStrategy, Property } from '@agnocon/pieces-framework';
2
+ import { googleCloudStorageAuth } from '../common/auth';
3
+ import { gcsCommon } from '../common/client';
4
+ import { bucketDropdown, projectIdProperty } from '../common/props';
5
+ import { HttpMethod } from '@agnocon/pieces-common';
6
+
7
+ interface TriggerData {
8
+ topicName: string;
9
+ subscriptionName: string;
10
+ notificationId: string;
11
+ }
12
+
13
+ export const newObjectCreated = createTrigger({
14
+ auth: googleCloudStorageAuth,
15
+ name: 'new_object_created',
16
+ displayName: 'New Object Created',
17
+ description: 'Triggers when a new object is created in a bucket',
18
+ aiMetadata: {
19
+ description: 'Fires when a new object is finalized (fully written) in the configured bucket, optionally limited to objects whose name starts with a given prefix. Represents a newly created/uploaded file; delivered via a Cloud Pub/Sub push notification (OBJECT_FINALIZE event).',
20
+ },
21
+ props: {
22
+ projectId: projectIdProperty,
23
+ bucket: bucketDropdown,
24
+ prefix: Property.ShortText({
25
+ displayName: 'Prefix Filter',
26
+ description: 'Only trigger for objects with this prefix',
27
+ required: false,
28
+ }),
29
+ },
30
+ type: TriggerStrategy.WEBHOOK,
31
+ sampleData: {
32
+ kind: 'storage#object',
33
+ id: 'example-bucket/example-object/1234567890',
34
+ name: 'example-object.txt',
35
+ bucket: 'example-bucket',
36
+ generation: '1234567890',
37
+ contentType: 'text/plain',
38
+ timeCreated: '2023-01-01T00:00:00.000Z',
39
+ updated: '2023-01-01T00:00:00.000Z',
40
+ size: '1024',
41
+ },
42
+ onEnable: async (context) => {
43
+ const { projectId, bucket, prefix } = context.propsValue;
44
+ const auth = context.auth;
45
+
46
+ // Generate unique names for this trigger instance
47
+ const triggerId = `ap-gcs-${bucket}-${Date.now()}`;
48
+ const topicName = `projects/${projectId}/topics/${triggerId}`;
49
+ const subscriptionName = `projects/${projectId}/subscriptions/${triggerId}`;
50
+
51
+ try {
52
+ // 1. Create Pub/Sub topic
53
+ await gcsCommon.makePubSubRequest(
54
+ HttpMethod.PUT,
55
+ `/projects/${projectId}/topics/${triggerId}`,
56
+ auth.access_token
57
+ );
58
+
59
+ // 2. Create GCS notification configuration
60
+ const notificationConfig: any = {
61
+ topic: topicName,
62
+ payload_format: 'JSON_API_V1',
63
+ event_types: ['OBJECT_FINALIZE'],
64
+ };
65
+
66
+ if (prefix) {
67
+ notificationConfig.object_name_prefix = prefix;
68
+ }
69
+
70
+ const notificationResponse = await gcsCommon.makeGCSRequest(
71
+ HttpMethod.POST,
72
+ `/b/${bucket}/notificationConfigs`,
73
+ auth.access_token,
74
+ notificationConfig
75
+ );
76
+
77
+ // 3. Create Pub/Sub subscription with webhook push
78
+ const subscriptionConfig = {
79
+ topic: topicName,
80
+ pushConfig: {
81
+ pushEndpoint: context.webhookUrl,
82
+ },
83
+ ackDeadlineSeconds: 60,
84
+ };
85
+
86
+ await gcsCommon.makePubSubRequest(
87
+ HttpMethod.PUT,
88
+ `/projects/${projectId}/subscriptions/${triggerId}`,
89
+ auth.access_token,
90
+ subscriptionConfig
91
+ );
92
+
93
+ // Store trigger data for cleanup
94
+ await context.store.put<TriggerData>('_trigger', {
95
+ topicName: triggerId,
96
+ subscriptionName: triggerId,
97
+ notificationId: notificationResponse.id,
98
+ });
99
+
100
+ } catch (error) {
101
+ // Cleanup on failure
102
+ try {
103
+ await gcsCommon.makePubSubRequest(
104
+ HttpMethod.DELETE,
105
+ `/projects/${projectId}/subscriptions/${triggerId}`,
106
+ auth.access_token
107
+ );
108
+ } catch (e) { /* ignore */ }
109
+
110
+ try {
111
+ await gcsCommon.makePubSubRequest(
112
+ HttpMethod.DELETE,
113
+ `/projects/${projectId}/topics/${triggerId}`,
114
+ auth.access_token
115
+ );
116
+ } catch (e) { /* ignore */ }
117
+
118
+ throw new Error(`Failed to setup Pub/Sub notifications: ${(error as any)?.message || 'Unknown error'}`);
119
+ }
120
+ },
121
+ onDisable: async (context) => {
122
+ const triggerData = await context.store.get<TriggerData>('_trigger');
123
+ if (!triggerData) return;
124
+
125
+ const { projectId } = context.propsValue;
126
+ const { bucket } = context.propsValue;
127
+ const auth = context.auth;
128
+
129
+ // Clean up in reverse order
130
+ try {
131
+ // Delete subscription
132
+ await gcsCommon.makePubSubRequest(
133
+ HttpMethod.DELETE,
134
+ `/projects/${projectId}/subscriptions/${triggerData.subscriptionName}`,
135
+ auth.access_token
136
+ );
137
+ } catch (e) { /* ignore */ }
138
+
139
+ try {
140
+ // Delete notification config
141
+ await gcsCommon.makeGCSRequest(
142
+ HttpMethod.DELETE,
143
+ `/b/${bucket}/notificationConfigs/${triggerData.notificationId}`,
144
+ auth.access_token
145
+ );
146
+ } catch (e) { /* ignore */ }
147
+
148
+ try {
149
+ // Delete topic
150
+ await gcsCommon.makePubSubRequest(
151
+ HttpMethod.DELETE,
152
+ `/projects/${projectId}/topics/${triggerData.topicName}`,
153
+ auth.access_token
154
+ );
155
+ } catch (e) { /* ignore */ }
156
+ },
157
+ run: async (context) => {
158
+ const payload = context.payload.body as any;
159
+
160
+ if (!payload?.message?.data) {
161
+ return [];
162
+ }
163
+
164
+ // Decode Pub/Sub message
165
+ const messageData = JSON.parse(
166
+ Buffer.from(payload.message.data, 'base64').toString()
167
+ );
168
+
169
+ // Extract GCS object from notification payload
170
+ const gcsObject = messageData;
171
+
172
+ return [gcsObject];
173
+ },
174
+ });
@@ -0,0 +1,174 @@
1
+ import { createTrigger, TriggerStrategy, Property } from '@agnocon/pieces-framework';
2
+ import { googleCloudStorageAuth } from '../common/auth';
3
+ import { gcsCommon } from '../common/client';
4
+ import { bucketDropdown, projectIdProperty } from '../common/props';
5
+ import { HttpMethod } from '@agnocon/pieces-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
+ aiMetadata: {
19
+ description: 'Fires when the metadata of an existing object in the configured bucket is updated, optionally limited to objects whose name starts with a given prefix. Represents a change to an existing file\'s metadata; delivered via a Cloud Pub/Sub push notification (OBJECT_METADATA_UPDATE event).',
20
+ },
21
+ props: {
22
+ projectId: projectIdProperty,
23
+ bucket: bucketDropdown,
24
+ prefix: Property.ShortText({
25
+ displayName: 'Prefix Filter',
26
+ description: 'Only trigger for objects with this prefix',
27
+ required: false,
28
+ }),
29
+ },
30
+ type: TriggerStrategy.WEBHOOK,
31
+ sampleData: {
32
+ kind: 'storage#object',
33
+ id: 'example-bucket/example-object/1234567890',
34
+ name: 'example-object.txt',
35
+ bucket: 'example-bucket',
36
+ generation: '1234567890',
37
+ contentType: 'text/plain',
38
+ timeCreated: '2023-01-01T00:00:00.000Z',
39
+ updated: '2023-01-01T01:00:00.000Z',
40
+ size: '2048',
41
+ },
42
+ onEnable: async (context) => {
43
+ const { projectId, bucket, prefix } = context.propsValue;
44
+ const auth = context.auth;
45
+
46
+ // Generate unique names for this trigger instance
47
+ const triggerId = `ap-gcs-update-${bucket}-${Date.now()}`;
48
+ const topicName = `projects/${projectId}/topics/${triggerId}`;
49
+ const subscriptionName = `projects/${projectId}/subscriptions/${triggerId}`;
50
+
51
+ try {
52
+ // 1. Create Pub/Sub topic
53
+ await gcsCommon.makePubSubRequest(
54
+ HttpMethod.PUT,
55
+ `/projects/${projectId}/topics/${triggerId}`,
56
+ auth.access_token
57
+ );
58
+
59
+ // 2. Create GCS notification configuration for metadata updates
60
+ const notificationConfig: any = {
61
+ topic: topicName,
62
+ payload_format: 'JSON_API_V1',
63
+ event_types: ['OBJECT_METADATA_UPDATE'],
64
+ };
65
+
66
+ if (prefix) {
67
+ notificationConfig.object_name_prefix = prefix;
68
+ }
69
+
70
+ const notificationResponse = await gcsCommon.makeGCSRequest(
71
+ HttpMethod.POST,
72
+ `/b/${bucket}/notificationConfigs`,
73
+ auth.access_token,
74
+ notificationConfig
75
+ );
76
+
77
+ // 3. Create Pub/Sub subscription with webhook push
78
+ const subscriptionConfig = {
79
+ topic: topicName,
80
+ pushConfig: {
81
+ pushEndpoint: context.webhookUrl,
82
+ },
83
+ ackDeadlineSeconds: 60,
84
+ };
85
+
86
+ await gcsCommon.makePubSubRequest(
87
+ HttpMethod.PUT,
88
+ `/projects/${projectId}/subscriptions/${triggerId}`,
89
+ auth.access_token,
90
+ subscriptionConfig
91
+ );
92
+
93
+ // Store trigger data for cleanup
94
+ await context.store.put<TriggerData>('_trigger', {
95
+ topicName: triggerId,
96
+ subscriptionName: triggerId,
97
+ notificationId: notificationResponse.id,
98
+ });
99
+
100
+ } catch (error) {
101
+ // Cleanup on failure
102
+ try {
103
+ await gcsCommon.makePubSubRequest(
104
+ HttpMethod.DELETE,
105
+ `/projects/${projectId}/subscriptions/${triggerId}`,
106
+ auth.access_token
107
+ );
108
+ } catch (e) { /* ignore */ }
109
+
110
+ try {
111
+ await gcsCommon.makePubSubRequest(
112
+ HttpMethod.DELETE,
113
+ `/projects/${projectId}/topics/${triggerId}`,
114
+ auth.access_token
115
+ );
116
+ } catch (e) { /* ignore */ }
117
+
118
+ throw new Error(`Failed to setup Pub/Sub notifications: ${(error as any)?.message || 'Unknown error'}`);
119
+ }
120
+ },
121
+ onDisable: async (context) => {
122
+ const triggerData = await context.store.get<TriggerData>('_trigger');
123
+ if (!triggerData) return;
124
+
125
+ const { projectId } = context.propsValue;
126
+ const { bucket } = context.propsValue;
127
+ const auth = context.auth;
128
+
129
+ // Clean up in reverse order
130
+ try {
131
+ // Delete subscription
132
+ await gcsCommon.makePubSubRequest(
133
+ HttpMethod.DELETE,
134
+ `/projects/${projectId}/subscriptions/${triggerData.subscriptionName}`,
135
+ auth.access_token
136
+ );
137
+ } catch (e) { /* ignore */ }
138
+
139
+ try {
140
+ // Delete notification config
141
+ await gcsCommon.makeGCSRequest(
142
+ HttpMethod.DELETE,
143
+ `/b/${bucket}/notificationConfigs/${triggerData.notificationId}`,
144
+ auth.access_token
145
+ );
146
+ } catch (e) { /* ignore */ }
147
+
148
+ try {
149
+ // Delete topic
150
+ await gcsCommon.makePubSubRequest(
151
+ HttpMethod.DELETE,
152
+ `/projects/${projectId}/topics/${triggerData.topicName}`,
153
+ auth.access_token
154
+ );
155
+ } catch (e) { /* ignore */ }
156
+ },
157
+ run: async (context) => {
158
+ const payload = context.payload.body as any;
159
+
160
+ if (!payload?.message?.data) {
161
+ return [];
162
+ }
163
+
164
+ // Decode Pub/Sub message
165
+ const messageData = JSON.parse(
166
+ Buffer.from(payload.message.data, 'base64').toString()
167
+ );
168
+
169
+ // Extract GCS object from notification payload
170
+ const gcsObject = messageData;
171
+
172
+ return [gcsObject];
173
+ },
174
+ });
package/README.md DELETED
@@ -1,3 +0,0 @@
1
- # Temporary Holding Version
2
-
3
- This version is a temporary placeholder for this package. An operational version to replace this has been submitted for review and is awaiting a staged release.