@quatrain/cloudwrapper-supabase 1.1.16 → 1.1.18
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/lib/SupabaseCloudWrapper.d.ts +11 -2
- package/lib/SupabaseCloudWrapper.js +25 -29
- package/package.json +12 -7
- package/src/SupabaseCloudWrapper.ts +215 -0
- package/src/index.ts +3 -0
|
@@ -6,6 +6,15 @@ export type SupabaseParams = {
|
|
|
6
6
|
url?: string;
|
|
7
7
|
key?: string;
|
|
8
8
|
};
|
|
9
|
+
export type Channel = {
|
|
10
|
+
name: string;
|
|
11
|
+
channel: RealtimeChannel | undefined;
|
|
12
|
+
state: any;
|
|
13
|
+
};
|
|
14
|
+
export type Channels = {
|
|
15
|
+
database: Channel[];
|
|
16
|
+
storage: Channel[];
|
|
17
|
+
};
|
|
9
18
|
export declare const eventMap: {
|
|
10
19
|
create: string;
|
|
11
20
|
update: string;
|
|
@@ -15,6 +24,7 @@ export declare class SupabaseCloudWrapper extends AbstractCloudWrapper {
|
|
|
15
24
|
protected _supabaseClient: SupabaseClient | undefined;
|
|
16
25
|
protected _realtimeClient: RealtimeChannel | undefined;
|
|
17
26
|
protected _isInitialized: boolean;
|
|
27
|
+
protected _channels: Channels;
|
|
18
28
|
constructor(params: SupabaseParams);
|
|
19
29
|
databaseTrigger(trigger: DatabaseTriggerType): void | {
|
|
20
30
|
event: any;
|
|
@@ -26,9 +36,8 @@ export declare class SupabaseCloudWrapper extends AbstractCloudWrapper {
|
|
|
26
36
|
schema: string;
|
|
27
37
|
table: string;
|
|
28
38
|
};
|
|
29
|
-
triggersEnable(): void;
|
|
30
|
-
triggersDisable(): void;
|
|
31
39
|
protected _initialize(): void;
|
|
40
|
+
poll(): void;
|
|
32
41
|
protected _payload2File(payload: {
|
|
33
42
|
[x: string]: any;
|
|
34
43
|
}): FileType | undefined;
|
|
@@ -33,12 +33,13 @@ class SupabaseCloudWrapper extends cloudwrapper_1.AbstractCloudWrapper {
|
|
|
33
33
|
constructor(params) {
|
|
34
34
|
super(params);
|
|
35
35
|
this._isInitialized = false;
|
|
36
|
+
this._channels = { database: [], storage: [] };
|
|
36
37
|
this._initialize();
|
|
37
38
|
}
|
|
38
39
|
databaseTrigger(trigger) {
|
|
39
40
|
var _a;
|
|
40
41
|
this._initialize();
|
|
41
|
-
if (!this.
|
|
42
|
+
if (!(this._supabaseClient instanceof supabase_js_1.SupabaseClient)) {
|
|
42
43
|
throw new Error(`Realtime channel is not enabled`);
|
|
43
44
|
}
|
|
44
45
|
if (typeof trigger.script !== 'function') {
|
|
@@ -58,7 +59,7 @@ class SupabaseCloudWrapper extends cloudwrapper_1.AbstractCloudWrapper {
|
|
|
58
59
|
table: trigger.model,
|
|
59
60
|
};
|
|
60
61
|
cloudwrapper_1.CloudWrapper.info(`Set up DB trigger ${trigger.name} for ${trigger.event} event on table ${schema}.${params.table}`);
|
|
61
|
-
(_a = this._supabaseClient) === null || _a === void 0 ? void 0 : _a.channel(trigger.name).on('postgres_changes', params, (_b) => __awaiter(this, void 0, void 0, function* () {
|
|
62
|
+
const channel = (_a = this._supabaseClient) === null || _a === void 0 ? void 0 : _a.channel(trigger.name).on('postgres_changes', params, (_b) => __awaiter(this, void 0, void 0, function* () {
|
|
62
63
|
var { old: before, new: after } = _b, context = __rest(_b, ["old", "new"]);
|
|
63
64
|
cloudwrapper_1.CloudWrapper.info(`Triggering function on event ${trigger.name}`);
|
|
64
65
|
try {
|
|
@@ -69,6 +70,11 @@ class SupabaseCloudWrapper extends cloudwrapper_1.AbstractCloudWrapper {
|
|
|
69
70
|
console.log(err);
|
|
70
71
|
}
|
|
71
72
|
})).subscribe();
|
|
73
|
+
this._channels.database.push({
|
|
74
|
+
name: trigger.name,
|
|
75
|
+
channel,
|
|
76
|
+
state: channel.state,
|
|
77
|
+
});
|
|
72
78
|
return params;
|
|
73
79
|
}
|
|
74
80
|
catch (err) {
|
|
@@ -79,9 +85,6 @@ class SupabaseCloudWrapper extends cloudwrapper_1.AbstractCloudWrapper {
|
|
|
79
85
|
storageTrigger(trigger) {
|
|
80
86
|
var _a;
|
|
81
87
|
this._initialize();
|
|
82
|
-
if (!this._realtimeClient) {
|
|
83
|
-
throw new Error(`Realtime channel is not enabled`);
|
|
84
|
-
}
|
|
85
88
|
if (typeof trigger.script !== 'function') {
|
|
86
89
|
throw new Error(`Passed script value is not a function`);
|
|
87
90
|
}
|
|
@@ -98,7 +101,7 @@ class SupabaseCloudWrapper extends cloudwrapper_1.AbstractCloudWrapper {
|
|
|
98
101
|
table: 'objects',
|
|
99
102
|
};
|
|
100
103
|
cloudwrapper_1.CloudWrapper.info(`Set up Storage trigger ${trigger.name} for ${trigger.event} event`);
|
|
101
|
-
(_a = this._supabaseClient) === null || _a === void 0 ? void 0 : _a.channel(trigger.name).on('postgres_changes', params, (_b) => __awaiter(this, void 0, void 0, function* () {
|
|
104
|
+
const channel = (_a = this._supabaseClient) === null || _a === void 0 ? void 0 : _a.channel(trigger.name).on('postgres_changes', params, (_b) => __awaiter(this, void 0, void 0, function* () {
|
|
102
105
|
var { old: before, new: after } = _b, context = __rest(_b, ["old", "new"]);
|
|
103
106
|
cloudwrapper_1.CloudWrapper.info(`Triggering storage function on event ${trigger.name}`);
|
|
104
107
|
try {
|
|
@@ -112,9 +115,13 @@ class SupabaseCloudWrapper extends cloudwrapper_1.AbstractCloudWrapper {
|
|
|
112
115
|
}
|
|
113
116
|
catch (err) {
|
|
114
117
|
cloudwrapper_1.CloudWrapper.error(err.message);
|
|
115
|
-
console.log(err);
|
|
116
118
|
}
|
|
117
119
|
})).subscribe();
|
|
120
|
+
this._channels.storage.push({
|
|
121
|
+
name: trigger.name,
|
|
122
|
+
channel,
|
|
123
|
+
state: channel === null || channel === void 0 ? void 0 : channel.state,
|
|
124
|
+
});
|
|
118
125
|
return params;
|
|
119
126
|
}
|
|
120
127
|
catch (err) {
|
|
@@ -122,42 +129,31 @@ class SupabaseCloudWrapper extends cloudwrapper_1.AbstractCloudWrapper {
|
|
|
122
129
|
throw new Error(`Realtime channel subscription failed`);
|
|
123
130
|
}
|
|
124
131
|
}
|
|
125
|
-
triggersEnable() {
|
|
126
|
-
if (!this._realtimeClient) {
|
|
127
|
-
throw new Error(`Realtime client is not enabled`);
|
|
128
|
-
}
|
|
129
|
-
this._realtimeClient.subscribe((res) => {
|
|
130
|
-
cloudwrapper_1.CloudWrapper.info(`Enabling Realtime triggers: ${res}`);
|
|
131
|
-
if (res !== 'SUBSCRIBED') {
|
|
132
|
-
throw new Error(`Failed to subscribe to Realtime channels`);
|
|
133
|
-
}
|
|
134
|
-
});
|
|
135
|
-
}
|
|
136
|
-
triggersDisable() {
|
|
137
|
-
var _a;
|
|
138
|
-
if (!this._realtimeClient) {
|
|
139
|
-
throw new Error(`Realtime channel is not enabled`);
|
|
140
|
-
}
|
|
141
|
-
cloudwrapper_1.CloudWrapper.info(`Disabling Realtime triggers`);
|
|
142
|
-
(_a = this._supabaseClient) === null || _a === void 0 ? void 0 : _a.removeAllChannels();
|
|
143
|
-
}
|
|
144
132
|
_initialize() {
|
|
145
133
|
if (this._isInitialized === false) {
|
|
146
134
|
this._supabaseClient = (0, supabase_js_1.createClient)(this._params.url, this._params.key);
|
|
147
|
-
this._realtimeClient = this._supabaseClient.channel('table-db-changes');
|
|
148
135
|
this._isInitialized = true;
|
|
149
136
|
cloudwrapper_1.CloudWrapper.info(`Supabase App initialized`);
|
|
150
137
|
}
|
|
151
138
|
}
|
|
139
|
+
poll() {
|
|
140
|
+
var _a;
|
|
141
|
+
cloudwrapper_1.CloudWrapper.info(`Supabase client status: ${(_a = this._supabaseClient) === null || _a === void 0 ? void 0 : _a.channel}`);
|
|
142
|
+
this._channels.storage.forEach((channel) => {
|
|
143
|
+
var _a;
|
|
144
|
+
cloudwrapper_1.CloudWrapper.info(`${channel.name}: ${channel.state}`);
|
|
145
|
+
console.log((_a = channel.channel) === null || _a === void 0 ? void 0 : _a.params);
|
|
146
|
+
});
|
|
147
|
+
setTimeout(() => this.poll(), 10000);
|
|
148
|
+
}
|
|
152
149
|
_payload2File(payload) {
|
|
153
|
-
console.log('received payload', payload);
|
|
154
150
|
if (!payload.metadata) {
|
|
155
151
|
return undefined;
|
|
156
152
|
}
|
|
157
153
|
if (Object.keys(payload).length === 0) {
|
|
158
154
|
return undefined;
|
|
159
155
|
}
|
|
160
|
-
const { size, mimetype } = payload === null || payload === void 0 ? void 0 : payload.metadata;
|
|
156
|
+
const { size, mimetype } = (payload === null || payload === void 0 ? void 0 : payload.metadata) || {};
|
|
161
157
|
return {
|
|
162
158
|
host: 'supabase',
|
|
163
159
|
bucket: payload.bucket_id,
|
package/package.json
CHANGED
|
@@ -1,31 +1,36 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@quatrain/cloudwrapper-supabase",
|
|
3
|
-
"version": "1.1.
|
|
3
|
+
"version": "1.1.18",
|
|
4
4
|
"license": "MIT",
|
|
5
5
|
"description": "Cloud Wrapper adapter for Supabase",
|
|
6
|
+
"repository": {
|
|
7
|
+
"type": "git",
|
|
8
|
+
"url": "https://github.com/quatrain/Core.git",
|
|
9
|
+
"directory": "packages/cloudwrapper-supabase"
|
|
10
|
+
},
|
|
6
11
|
"main": "lib/index.js",
|
|
7
12
|
"types": "lib/index.d.ts",
|
|
8
13
|
"files": [
|
|
14
|
+
"src/",
|
|
9
15
|
"lib/",
|
|
10
16
|
"README.md"
|
|
11
17
|
],
|
|
12
18
|
"author": "Quatrain Développement SAS <developers@quatrain.com>",
|
|
13
19
|
"peerDependencies": {
|
|
14
|
-
"@quatrain/core": "^1.1.
|
|
20
|
+
"@quatrain/core": "^1.1.22"
|
|
15
21
|
},
|
|
16
22
|
"dependencies": {
|
|
17
|
-
"@quatrain/backend": "^1.1.
|
|
23
|
+
"@quatrain/backend": "^1.1.21",
|
|
18
24
|
"@quatrain/cloudwrapper": "^1.1.14",
|
|
19
|
-
"@
|
|
20
|
-
"@supabase/supabase-js": "^2.49.4"
|
|
25
|
+
"@supabase/supabase-js": "^2.53.0"
|
|
21
26
|
},
|
|
22
27
|
"devDependencies": {
|
|
23
28
|
"@tsconfig/recommended": "^1.0.1",
|
|
24
29
|
"@types/jest": "^27.0.3",
|
|
25
30
|
"@types/node": "^22.10.1",
|
|
26
|
-
"jest": "^
|
|
31
|
+
"jest": "^30.1.2",
|
|
27
32
|
"jest-node-exports-resolver": "^1.1.6",
|
|
28
|
-
"jest-serial-runner": "^1.2.
|
|
33
|
+
"jest-serial-runner": "^1.2.2",
|
|
29
34
|
"trace-unhandled": "^2.0.1",
|
|
30
35
|
"ts-jest": "^27.1.2",
|
|
31
36
|
"ts-node": "^10.9.1",
|
|
@@ -0,0 +1,215 @@
|
|
|
1
|
+
import {
|
|
2
|
+
CloudWrapper,
|
|
3
|
+
AbstractCloudWrapper,
|
|
4
|
+
DatabaseTriggerType,
|
|
5
|
+
StorageTriggerType,
|
|
6
|
+
StorageEventPayloadType,
|
|
7
|
+
} from '@quatrain/cloudwrapper'
|
|
8
|
+
import { BackendAction } from '@quatrain/backend'
|
|
9
|
+
import {
|
|
10
|
+
createClient,
|
|
11
|
+
SupabaseClient,
|
|
12
|
+
RealtimeChannel,
|
|
13
|
+
} from '@supabase/supabase-js'
|
|
14
|
+
import { FileType } from '@quatrain/storage'
|
|
15
|
+
|
|
16
|
+
export type SupabaseParams = {
|
|
17
|
+
useEmulator?: boolean
|
|
18
|
+
url?: string
|
|
19
|
+
key?: string
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
export type Channel = {
|
|
23
|
+
name: string
|
|
24
|
+
channel: RealtimeChannel | undefined
|
|
25
|
+
state: any
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
export type Channels = {
|
|
29
|
+
database: Channel[]
|
|
30
|
+
storage: Channel[]
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
export const eventMap = {
|
|
34
|
+
[BackendAction.CREATE]: 'INSERT',
|
|
35
|
+
[BackendAction.UPDATE]: 'UPDATE',
|
|
36
|
+
[BackendAction.DELETE]: 'DELETE',
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
export class SupabaseCloudWrapper extends AbstractCloudWrapper {
|
|
40
|
+
protected _supabaseClient: SupabaseClient | undefined
|
|
41
|
+
protected _realtimeClient: RealtimeChannel | undefined
|
|
42
|
+
protected _isInitialized = false
|
|
43
|
+
protected _channels: Channels = { database: [], storage: [] }
|
|
44
|
+
|
|
45
|
+
constructor(params: SupabaseParams) {
|
|
46
|
+
super(params)
|
|
47
|
+
this._initialize()
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
databaseTrigger(trigger: DatabaseTriggerType) {
|
|
51
|
+
this._initialize()
|
|
52
|
+
|
|
53
|
+
if (!(this._supabaseClient instanceof SupabaseClient)) {
|
|
54
|
+
throw new Error(`Realtime channel is not enabled`)
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
if (typeof trigger.script !== 'function') {
|
|
58
|
+
throw new Error(`Passed script value is not a function`)
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
if (Array.isArray(trigger.event)) {
|
|
62
|
+
const params = trigger.event.forEach((event) => {
|
|
63
|
+
return this.databaseTrigger({
|
|
64
|
+
...trigger,
|
|
65
|
+
event,
|
|
66
|
+
name: `${trigger.name}-${event}`,
|
|
67
|
+
})
|
|
68
|
+
})
|
|
69
|
+
return params
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
try {
|
|
73
|
+
const schema = trigger.schema || 'public'
|
|
74
|
+
const params = {
|
|
75
|
+
event: Reflect.get(eventMap, trigger.event),
|
|
76
|
+
schema,
|
|
77
|
+
table: trigger.model,
|
|
78
|
+
}
|
|
79
|
+
CloudWrapper.info(
|
|
80
|
+
`Set up DB trigger ${trigger.name} for ${trigger.event} event on table ${schema}.${params.table}`
|
|
81
|
+
)
|
|
82
|
+
const channel = this._supabaseClient
|
|
83
|
+
?.channel(trigger.name)
|
|
84
|
+
.on(
|
|
85
|
+
'postgres_changes',
|
|
86
|
+
params,
|
|
87
|
+
async ({ old: before, new: after, ...context }) => {
|
|
88
|
+
CloudWrapper.info(
|
|
89
|
+
`Triggering function on event ${trigger.name}`
|
|
90
|
+
)
|
|
91
|
+
try {
|
|
92
|
+
return await trigger.script({ before, after, context })
|
|
93
|
+
} catch (err) {
|
|
94
|
+
CloudWrapper.error((err as Error).message)
|
|
95
|
+
console.log(err)
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
)
|
|
99
|
+
.subscribe()
|
|
100
|
+
this._channels.database.push({
|
|
101
|
+
name: trigger.name,
|
|
102
|
+
channel,
|
|
103
|
+
state: channel.state,
|
|
104
|
+
})
|
|
105
|
+
|
|
106
|
+
return params
|
|
107
|
+
} catch (err) {
|
|
108
|
+
CloudWrapper.error(err)
|
|
109
|
+
throw new Error(`Realtime channel subscription failed`)
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
storageTrigger(trigger: StorageTriggerType) {
|
|
114
|
+
this._initialize()
|
|
115
|
+
|
|
116
|
+
if (typeof trigger.script !== 'function') {
|
|
117
|
+
throw new Error(`Passed script value is not a function`)
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
if (Array.isArray(trigger.event)) {
|
|
121
|
+
const params = trigger.event.forEach((event) => {
|
|
122
|
+
return this.storageTrigger({
|
|
123
|
+
...trigger,
|
|
124
|
+
event,
|
|
125
|
+
name: `${trigger.name}-${event}`,
|
|
126
|
+
})
|
|
127
|
+
})
|
|
128
|
+
|
|
129
|
+
return params
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
try {
|
|
133
|
+
const params = {
|
|
134
|
+
event: Reflect.get(eventMap, trigger.event),
|
|
135
|
+
schema: 'storage',
|
|
136
|
+
table: 'objects',
|
|
137
|
+
}
|
|
138
|
+
CloudWrapper.info(
|
|
139
|
+
`Set up Storage trigger ${trigger.name} for ${trigger.event} event`
|
|
140
|
+
)
|
|
141
|
+
const channel = this._supabaseClient
|
|
142
|
+
?.channel(trigger.name)
|
|
143
|
+
.on(
|
|
144
|
+
'postgres_changes',
|
|
145
|
+
params,
|
|
146
|
+
async ({ old: before, new: after, ...context }) => {
|
|
147
|
+
CloudWrapper.info(
|
|
148
|
+
`Triggering storage function on event ${trigger.name}`
|
|
149
|
+
)
|
|
150
|
+
try {
|
|
151
|
+
const payload: StorageEventPayloadType = {
|
|
152
|
+
before: this._payload2File(before),
|
|
153
|
+
after: this._payload2File(after),
|
|
154
|
+
context,
|
|
155
|
+
}
|
|
156
|
+
CloudWrapper.debug(payload)
|
|
157
|
+
return await trigger.script(payload)
|
|
158
|
+
} catch (err) {
|
|
159
|
+
CloudWrapper.error((err as Error).message)
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
)
|
|
163
|
+
.subscribe()
|
|
164
|
+
this._channels.storage.push({
|
|
165
|
+
name: trigger.name,
|
|
166
|
+
channel,
|
|
167
|
+
state: channel?.state,
|
|
168
|
+
})
|
|
169
|
+
return params
|
|
170
|
+
} catch (err) {
|
|
171
|
+
console.log(err)
|
|
172
|
+
throw new Error(`Realtime channel subscription failed`)
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
protected _initialize() {
|
|
177
|
+
if (this._isInitialized === false) {
|
|
178
|
+
this._supabaseClient = createClient(this._params.url, this._params.key)
|
|
179
|
+
this._isInitialized = true
|
|
180
|
+
CloudWrapper.info(`Supabase App initialized`)
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
public poll() {
|
|
185
|
+
CloudWrapper.info(
|
|
186
|
+
`Supabase client status: ${this._supabaseClient?.channel}`
|
|
187
|
+
)
|
|
188
|
+
this._channels.storage.forEach((channel) => {
|
|
189
|
+
CloudWrapper.info(`${channel.name}: ${channel.state}`)
|
|
190
|
+
console.log(channel.channel?.params)
|
|
191
|
+
})
|
|
192
|
+
setTimeout(() => this.poll(), 10000)
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
protected _payload2File(payload: {
|
|
196
|
+
[x: string]: any
|
|
197
|
+
}): FileType | undefined {
|
|
198
|
+
if (!payload.metadata) {
|
|
199
|
+
return undefined
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
if (Object.keys(payload).length === 0) {
|
|
203
|
+
return undefined
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
const { size, mimetype } = payload?.metadata || {}
|
|
207
|
+
return {
|
|
208
|
+
host: 'supabase',
|
|
209
|
+
bucket: payload.bucket_id,
|
|
210
|
+
ref: payload.name,
|
|
211
|
+
contentType: mimetype,
|
|
212
|
+
size,
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
}
|
package/src/index.ts
ADDED