Skip to content

Commit 3006b8f

Browse files
committed
feat: firestore datasource
1 parent 620d491 commit 3006b8f

File tree

3 files changed

+120
-12
lines changed

3 files changed

+120
-12
lines changed

server/node-service/src/plugins/firebase/queryConfig.ts

Lines changed: 28 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -31,8 +31,10 @@ const firestoreDocIdParamConfig = {
3131

3232
const firestoreParentDocIdParamConfig = {
3333
key: "parentDocumentId",
34-
label: "Parent Document ID",
34+
label: "Parent",
3535
type: "textInput",
36+
tooltip:
37+
"The parent document id of collections you want to list. Leave empty for top-level collections.",
3638
} as const;
3739

3840
const firestoreDataParamConfig = {
@@ -86,14 +88,37 @@ const queryConfig = {
8688
{
8789
label: "Query Firestore",
8890
actionName: "FS.QueryFireStore",
89-
params: [firestoreCollectionParamConfig],
91+
params: [
92+
firestoreCollectionParamConfig,
93+
{
94+
key: "orderBy",
95+
label: "Order by",
96+
type: "textInput",
97+
},
98+
{
99+
key: "orderDirection",
100+
label: "Order direction",
101+
type: "textInput",
102+
defaultValue: "asc",
103+
placeholder: "asc",
104+
},
105+
{
106+
key: "limit",
107+
label: "Limit",
108+
type: "numberInput",
109+
defaultValue: 10,
110+
},
111+
],
90112
},
91113
{
92114
label: "Insert Document",
93115
actionName: "FS.InsertDocument",
94116
params: [
95117
firestoreCollectionParamConfig,
96-
firestoreDocIdParamConfig,
118+
{
119+
...firestoreDocIdParamConfig,
120+
tooltip: "Leaving empty will use auto generated document id.",
121+
},
97122
firestoreDataParamConfig,
98123
],
99124
},
@@ -121,11 +146,6 @@ const queryConfig = {
121146
actionName: "FS.GetCollections",
122147
params: [firestoreParentDocIdParamConfig],
123148
},
124-
{
125-
label: "Get Collection Group",
126-
actionName: "FS.GetDocumentGroup",
127-
params: [firestoreCollectionParamConfig],
128-
} as const,
129149
] as const
130150
).map((i) => ({ ...i, category: [FirebaseCategory.Firestore] })),
131151
],

server/node-service/src/plugins/firebase/run.test.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ const privateKey = process.env["GOOGLE_PRIVATE_KEY"] || "";
44

55
test("realtime database", async () => {
66
const res = await runFirebasePlugin(
7-
{ actionName: "QueryDatabase", databaseRef: "/hello" },
7+
{ actionName: "RTDB.QueryDatabase", databaseRef: "/hello" },
88
{
99
databaseUrl: "https://sarike-a3de9-default-rtdb.asia-southeast1.firebasedatabase.app/",
1010
privateKey,

server/node-service/src/plugins/firebase/run.ts

Lines changed: 91 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,12 @@
11
import { badRequest } from "../../common/error";
22
import { initializeApp, deleteApp, cert } from "firebase-admin/app";
33
import { getDatabase, Reference } from "firebase-admin/database";
4+
import {
5+
CollectionReference,
6+
DocumentReference,
7+
getFirestore,
8+
OrderByDirection,
9+
} from "firebase-admin/firestore";
410
import { DataSourceDataType } from "./dataSourceConfig";
511
import { ActionDataType } from "./queryConfig";
612

@@ -25,6 +31,24 @@ export async function runFirebasePlugin(
2531
return fn(ref);
2632
};
2733

34+
const withFirestoreCollection = <T>(fn: (ref: CollectionReference) => T): T => {
35+
if (!("collection" in actionData)) {
36+
throw badRequest("not a firestore action with collection:" + actionName);
37+
}
38+
const ref = getFirestore().collection(actionData.collection);
39+
return fn(ref);
40+
};
41+
42+
const withFirestoreDoc = <T>(fn: (ref: DocumentReference) => T): T => {
43+
if (!("collection" in actionData) || !("documentId" in actionData)) {
44+
throw badRequest("not a firestore action with collection and documentId:" + actionName);
45+
}
46+
const ref = getFirestore().collection(actionData.collection).doc(actionData.documentId);
47+
return fn(ref);
48+
};
49+
50+
const successResult = { success: true };
51+
2852
try {
2953
// firebase
3054
if (actionName === "RTDB.QueryDatabase") {
@@ -33,15 +57,79 @@ export async function runFirebasePlugin(
3357
}
3458

3559
if (actionName === "RTDB.SetData") {
36-
return await witDbRef((ref) => ref.set(actionData.data));
60+
await witDbRef((ref) => ref.set(actionData.data));
61+
return successResult;
3762
}
3863

3964
if (actionName === "RTDB.UpdateData") {
40-
return await witDbRef((ref) => ref.update(actionData.data));
65+
await witDbRef((ref) => ref.update(actionData.data));
66+
return successResult;
4167
}
4268

4369
if (actionName === "RTDB.AppendDataToList") {
44-
return await witDbRef((ref) => ref.push(actionData.data));
70+
await witDbRef((ref) => ref.push(actionData.data));
71+
return successResult;
72+
}
73+
74+
// firebase
75+
if (actionName === "FS.GetCollections") {
76+
let collections;
77+
if (actionData.parentDocumentId) {
78+
collections = await getFirestore().doc(actionData.parentDocumentId).listCollections();
79+
} else {
80+
collections = await getFirestore().listCollections();
81+
}
82+
return collections.map((i) => i.id);
83+
}
84+
85+
if (actionName === "FS.QueryFireStore") {
86+
const data = await withFirestoreCollection(async (ref) => {
87+
let query;
88+
if (actionData.orderBy) {
89+
query = ref.orderBy(
90+
actionData.orderBy,
91+
(actionData.orderDirection || "asc") as OrderByDirection
92+
);
93+
}
94+
if (actionData.limit > 0) {
95+
query = (query || ref).limit(actionData.limit);
96+
}
97+
const snapshot = await (query || ref).get();
98+
if (snapshot.empty) {
99+
return [];
100+
}
101+
return snapshot.docs.map((i) => i.data());
102+
});
103+
return data;
104+
}
105+
106+
if (actionName === "FS.GetDocument") {
107+
return await withFirestoreDoc(async (ref) => (await ref.get()).data());
108+
}
109+
110+
if (actionName === "FS.InsertDocument") {
111+
return await withFirestoreCollection(async (ref) => {
112+
if (actionData.documentId) {
113+
await ref.doc(actionData.documentId).set(actionData.data);
114+
} else {
115+
await ref.add(actionData.data);
116+
}
117+
return successResult;
118+
});
119+
}
120+
121+
if (actionName === "FS.UpdateDocument") {
122+
return await withFirestoreDoc(async (ref) => {
123+
await ref.update(actionData.data);
124+
return successResult;
125+
});
126+
}
127+
128+
if (actionName === "FS.DeleteDocument") {
129+
return await withFirestoreDoc(async (ref) => {
130+
await ref.delete();
131+
return successResult;
132+
});
45133
}
46134
} finally {
47135
deleteApp(app);

0 commit comments

Comments
 (0)