Skip to content

Commit 620d491

Browse files
committed
feat: firebase datasource
1 parent 820da4a commit 620d491

File tree

11 files changed

+1459
-15
lines changed

11 files changed

+1459
-15
lines changed

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
11
.idea/
22
logs/
33
client/.yarn/cache/*.zip
4+
server/node-service/.yarn/cache/*.zip

server/node-service/.prettierrc

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
{
2+
"printWidth": 100
3+
}

server/node-service/jest.config.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,6 @@
22
module.exports = {
33
preset: "ts-jest",
44
testEnvironment: "node",
5-
testTimeout: 60000,
5+
testTimeout: 10 * 60000,
66
testPathIgnorePatterns: ["/node_modules/", "/build/"],
77
};

server/node-service/package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@
3535
"dynamodb-data-types": "^4.0.1",
3636
"express": "^4.18.2",
3737
"express-async-errors": "^3.1.1",
38+
"firebase-admin": "^11.5.0",
3839
"jsonpath": "^1.1.1",
3940
"lodash": "^4.17.21",
4041
"loglevel": "^1.8.1",
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
import { ConfigToType } from "openblocks-sdk/dataSource";
2+
3+
const dataSourceConfig = {
4+
type: "dataSource",
5+
params: [
6+
{
7+
key: "databaseUrl",
8+
label: "Firebase Database URL",
9+
tooltip:
10+
"You can find your database URL and Firestore ID in your [Firebase project console](https://console.firebase.google.com/)",
11+
type: "textInput",
12+
},
13+
{
14+
key: "firestoreId",
15+
label: "Firestore Project ID",
16+
type: "textInput",
17+
},
18+
{
19+
key: "privateKey",
20+
label: "Private Key",
21+
type: "password",
22+
tooltip:
23+
"The [document](https://firebase.google.com/docs/admin/setup) on how to obtain the private key.",
24+
},
25+
],
26+
} as const;
27+
28+
export type DataSourceDataType = ConfigToType<typeof dataSourceConfig>;
29+
30+
export default dataSourceConfig;
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
import { DataSourcePlugin } from "openblocks-sdk/dataSource";
2+
import dataSourceConfig, { DataSourceDataType } from "./dataSourceConfig";
3+
import queryConfig, { ActionDataType } from "./queryConfig";
4+
import { runFirebasePlugin } from "./run";
5+
6+
const firebasePlugin: DataSourcePlugin<ActionDataType, DataSourceDataType> = {
7+
id: "firebase",
8+
name: "Firebase",
9+
category: "api",
10+
queryConfig,
11+
dataSourceConfig,
12+
run: function (actionData, dataSourceConfig): Promise<any> {
13+
return runFirebasePlugin(actionData, dataSourceConfig)
14+
},
15+
};
16+
17+
export default firebasePlugin;
Lines changed: 136 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,136 @@
1+
import { ConfigToType, QueryConfig } from "openblocks-sdk/dataSource";
2+
3+
enum FirebaseCategory {
4+
RealtimeDatabase = "RealtimeDatabase",
5+
Firestore = "Firestore",
6+
}
7+
8+
const databaseRefParamConfig = {
9+
key: "databaseRef",
10+
label: "Database Ref",
11+
type: "textInput",
12+
} as const;
13+
14+
const dataParamConfig = {
15+
key: "data",
16+
label: "Data",
17+
type: "jsonInput",
18+
} as const;
19+
20+
const firestoreCollectionParamConfig = {
21+
key: "collection",
22+
label: "Collection",
23+
type: "textInput",
24+
} as const;
25+
26+
const firestoreDocIdParamConfig = {
27+
key: "documentId",
28+
label: "Document ID",
29+
type: "textInput",
30+
} as const;
31+
32+
const firestoreParentDocIdParamConfig = {
33+
key: "parentDocumentId",
34+
label: "Parent Document ID",
35+
type: "textInput",
36+
} as const;
37+
38+
const firestoreDataParamConfig = {
39+
key: "data",
40+
label: "Data",
41+
type: "jsonInput",
42+
} as const;
43+
44+
const categories = {
45+
label: "Service",
46+
items: [
47+
{ label: "Realtime Database", value: FirebaseCategory.RealtimeDatabase },
48+
{ label: "Firestore", value: FirebaseCategory.Firestore },
49+
],
50+
};
51+
52+
const queryConfig = {
53+
type: "query",
54+
categories,
55+
label: "Action",
56+
actions: [
57+
// actions of realtime database
58+
...(
59+
[
60+
{
61+
label: "Query Database",
62+
actionName: "RTDB.QueryDatabase",
63+
params: [databaseRefParamConfig],
64+
},
65+
{
66+
label: "Set Data",
67+
actionName: "RTDB.SetData",
68+
params: [databaseRefParamConfig, dataParamConfig],
69+
},
70+
{
71+
label: "Update Data",
72+
actionName: "RTDB.UpdateData",
73+
params: [databaseRefParamConfig, dataParamConfig],
74+
},
75+
{
76+
label: "Append Data to a list",
77+
actionName: "RTDB.AppendDataToList",
78+
params: [databaseRefParamConfig, dataParamConfig],
79+
},
80+
] as const
81+
).map((i) => ({ ...i, category: [FirebaseCategory.RealtimeDatabase] })),
82+
83+
// actions of firestore
84+
...(
85+
[
86+
{
87+
label: "Query Firestore",
88+
actionName: "FS.QueryFireStore",
89+
params: [firestoreCollectionParamConfig],
90+
},
91+
{
92+
label: "Insert Document",
93+
actionName: "FS.InsertDocument",
94+
params: [
95+
firestoreCollectionParamConfig,
96+
firestoreDocIdParamConfig,
97+
firestoreDataParamConfig,
98+
],
99+
},
100+
{
101+
label: "Update Document",
102+
actionName: "FS.UpdateDocument",
103+
params: [
104+
firestoreCollectionParamConfig,
105+
firestoreDocIdParamConfig,
106+
firestoreDataParamConfig,
107+
],
108+
},
109+
{
110+
label: "Get Document",
111+
actionName: "FS.GetDocument",
112+
params: [firestoreCollectionParamConfig, firestoreDocIdParamConfig],
113+
},
114+
{
115+
label: "Delete Document",
116+
actionName: "FS.DeleteDocument",
117+
params: [firestoreCollectionParamConfig, firestoreDocIdParamConfig],
118+
},
119+
{
120+
label: "Get Collections",
121+
actionName: "FS.GetCollections",
122+
params: [firestoreParentDocIdParamConfig],
123+
},
124+
{
125+
label: "Get Collection Group",
126+
actionName: "FS.GetDocumentGroup",
127+
params: [firestoreCollectionParamConfig],
128+
} as const,
129+
] as const
130+
).map((i) => ({ ...i, category: [FirebaseCategory.Firestore] })),
131+
],
132+
} as const;
133+
134+
export type ActionDataType = ConfigToType<typeof queryConfig>;
135+
136+
export default queryConfig;
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
import { runFirebasePlugin } from "./run";
2+
3+
const privateKey = process.env["GOOGLE_PRIVATE_KEY"] || "";
4+
5+
test("realtime database", async () => {
6+
const res = await runFirebasePlugin(
7+
{ actionName: "QueryDatabase", databaseRef: "/hello" },
8+
{
9+
databaseUrl: "https://sarike-a3de9-default-rtdb.asia-southeast1.firebasedatabase.app/",
10+
privateKey,
11+
firestoreId: "",
12+
}
13+
);
14+
console.info(res);
15+
});
Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
import { badRequest } from "../../common/error";
2+
import { initializeApp, deleteApp, cert } from "firebase-admin/app";
3+
import { getDatabase, Reference } from "firebase-admin/database";
4+
import { DataSourceDataType } from "./dataSourceConfig";
5+
import { ActionDataType } from "./queryConfig";
6+
7+
export async function runFirebasePlugin(
8+
actionData: ActionDataType,
9+
dataSourceConfig: DataSourceDataType
10+
) {
11+
const { actionName } = actionData;
12+
const { privateKey, databaseUrl } = dataSourceConfig;
13+
const serviceAccount = JSON.parse(privateKey);
14+
15+
const app = initializeApp({
16+
credential: cert(serviceAccount),
17+
databaseURL: databaseUrl,
18+
});
19+
20+
const witDbRef = <T>(fn: (ref: Reference) => T): T => {
21+
if (!("databaseRef" in actionData)) {
22+
throw badRequest("not a realtime database action:" + actionName);
23+
}
24+
const ref = getDatabase().ref(actionData.databaseRef);
25+
return fn(ref);
26+
};
27+
28+
try {
29+
// firebase
30+
if (actionName === "RTDB.QueryDatabase") {
31+
const data = await witDbRef((ref) => ref.once("value"));
32+
return data.val();
33+
}
34+
35+
if (actionName === "RTDB.SetData") {
36+
return await witDbRef((ref) => ref.set(actionData.data));
37+
}
38+
39+
if (actionName === "RTDB.UpdateData") {
40+
return await witDbRef((ref) => ref.update(actionData.data));
41+
}
42+
43+
if (actionName === "RTDB.AppendDataToList") {
44+
return await witDbRef((ref) => ref.push(actionData.data));
45+
}
46+
} finally {
47+
deleteApp(app);
48+
}
49+
}

server/node-service/src/plugins/index.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,13 +4,15 @@ import s3Plugin from "./s3";
44
import n8nPlugin from "./n8n";
55
import openApiPlugin from "./openApi";
66
import dynamoDBPlugin from "./dynamodb";
7+
import firebasePlugin from "./firebase";
78

89
const plugins: (DataSourcePlugin | DataSourcePluginFactory)[] = [
910
// helloWorldPlugin,
1011
s3Plugin,
1112
openApiPlugin,
1213
n8nPlugin,
1314
dynamoDBPlugin,
15+
firebasePlugin,
1416
];
1517

1618
export default plugins;

0 commit comments

Comments
 (0)