diff --git a/packages/builder/src/components/backend/DatasourceNavigator/icons/Firebase.svelte b/packages/builder/src/components/backend/DatasourceNavigator/icons/Firebase.svelte
new file mode 100644
index 0000000000..3a776a9217
--- /dev/null
+++ b/packages/builder/src/components/backend/DatasourceNavigator/icons/Firebase.svelte
@@ -0,0 +1,54 @@
+
+
+
diff --git a/packages/builder/src/components/backend/DatasourceNavigator/icons/firebase.svg b/packages/builder/src/components/backend/DatasourceNavigator/icons/firebase.svg
new file mode 100644
index 0000000000..dc569606ad
--- /dev/null
+++ b/packages/builder/src/components/backend/DatasourceNavigator/icons/firebase.svg
@@ -0,0 +1,91 @@
+
+
diff --git a/packages/builder/src/components/backend/DatasourceNavigator/icons/index.js b/packages/builder/src/components/backend/DatasourceNavigator/icons/index.js
index 350fccf73f..515f20a93b 100644
--- a/packages/builder/src/components/backend/DatasourceNavigator/icons/index.js
+++ b/packages/builder/src/components/backend/DatasourceNavigator/icons/index.js
@@ -12,6 +12,7 @@ import Rest from "./Rest.svelte"
import Budibase from "./Budibase.svelte"
import Oracle from "./Oracle.svelte"
import GoogleSheets from "./GoogleSheets.svelte"
+import Firebase from "./Firebase.svelte"
export default {
BUDIBASE: Budibase,
@@ -28,4 +29,5 @@ export default {
REST: Rest,
ORACLE: Oracle,
GOOGLE_SHEETS: GoogleSheets,
+ FIREBASE: Firebase,
}
diff --git a/packages/builder/src/constants/backend/index.js b/packages/builder/src/constants/backend/index.js
index 4d6a7e3884..9a623241ca 100644
--- a/packages/builder/src/constants/backend/index.js
+++ b/packages/builder/src/constants/backend/index.js
@@ -178,6 +178,7 @@ export const IntegrationTypes = {
ORACLE: "ORACLE",
INTERNAL: "INTERNAL",
GOOGLE_SHEETS: "GOOGLE_SHEETS",
+ FIREBASE: "FIREBASE",
}
export const IntegrationNames = {
@@ -195,6 +196,7 @@ export const IntegrationNames = {
[IntegrationTypes.ORACLE]: "Oracle",
[IntegrationTypes.INTERNAL]: "Internal",
[IntegrationTypes.GOOGLE_SHEETS]: "Google Sheets",
+ [IntegrationTypes.FIREBASE]: "Firebase",
}
export const SchemaTypeOptions = [
diff --git a/packages/server/package.json b/packages/server/package.json
index 30e21a7170..b80b99ab69 100644
--- a/packages/server/package.json
+++ b/packages/server/package.json
@@ -77,6 +77,7 @@
"@bull-board/api": "^3.7.0",
"@bull-board/koa": "^3.7.0",
"@elastic/elasticsearch": "7.10.0",
+ "@google-cloud/firestore": "^5.0.2",
"@koa/router": "8.0.0",
"@sendgrid/mail": "7.1.1",
"@sentry/node": "^6.0.0",
diff --git a/packages/server/src/api/routes/public/index.ts b/packages/server/src/api/routes/public/index.ts
index 800eae6101..63a6946606 100644
--- a/packages/server/src/api/routes/public/index.ts
+++ b/packages/server/src/api/routes/public/index.ts
@@ -29,7 +29,7 @@ function getApiLimitPerSecond(): number {
return parseInt(env.API_REQ_LIMIT_PER_SEC)
}
-if (!env.isTest()) {
+/*if (!env.isTest()) {
const REDIS_OPTS = getRedisOptions()
RateLimit.defaultOptions({
store: new Stores.Redis({
@@ -42,7 +42,7 @@ if (!env.isTest()) {
database: 1,
}),
})
-}
+}*/
// rate limiting, allows for 2 requests per second
const limiter = RateLimit.middleware({
interval: { sec: 1 },
diff --git a/packages/server/src/definitions/datasource.ts b/packages/server/src/definitions/datasource.ts
index 6c8c8dc07d..208b2dc0a3 100644
--- a/packages/server/src/definitions/datasource.ts
+++ b/packages/server/src/definitions/datasource.ts
@@ -48,6 +48,7 @@ export enum SourceNames {
REST = "REST",
ORACLE = "ORACLE",
GOOGLE_SHEETS = "GOOGLE_SHEETS",
+ FIREBASE = "FIREBASE",
}
export enum IncludeRelationships {
diff --git a/packages/server/src/integrations/firebase.ts b/packages/server/src/integrations/firebase.ts
new file mode 100644
index 0000000000..8bcf198b7d
--- /dev/null
+++ b/packages/server/src/integrations/firebase.ts
@@ -0,0 +1,161 @@
+import {
+ DatasourceFieldTypes,
+ Integration,
+ QueryTypes,
+} from "../definitions/datasource"
+import { IntegrationBase } from "./base/IntegrationBase"
+import { Firestore, WhereFilterOp } from "@google-cloud/firestore"
+
+module Firebase {
+ interface FirebaseConfig {
+ email: string
+ privateKey: string
+ projectId: string
+ }
+
+ const SCHEMA: Integration = {
+ docs: "https://firebase.google.com/docs/firestore/quickstart",
+ friendlyName: "Firestore",
+ description:
+ "Cloud Firestore is a flexible, scalable database for mobile, web, and server development from Firebase and Google Cloud.",
+ datasource: {
+ email: {
+ type: DatasourceFieldTypes.STRING,
+ required: true,
+ },
+ privateKey: {
+ type: DatasourceFieldTypes.STRING,
+ required: true,
+ },
+ projectId: {
+ type: DatasourceFieldTypes.STRING,
+ required: true,
+ },
+ },
+ query: {
+ create: {
+ type: QueryTypes.JSON,
+ },
+ read: {
+ type: QueryTypes.JSON,
+ },
+ update: {
+ type: QueryTypes.JSON,
+ },
+ delete: {
+ type: QueryTypes.JSON,
+ },
+ },
+ extra: {
+ collection: {
+ displayName: "Collection",
+ type: DatasourceFieldTypes.STRING,
+ required: true,
+ },
+ filter: {
+ displayName: "Filter query",
+ type: DatasourceFieldTypes.LIST,
+ required: false,
+ data: {
+ read: [
+ "==",
+ "<",
+ "<=",
+ "==",
+ "!=",
+ ">=",
+ ">",
+ "array-contains",
+ "in",
+ "not-in",
+ "array-contains-any",
+ ],
+ },
+ },
+ queryValue: {
+ displayName: "Query value",
+ type: DatasourceFieldTypes.STRING,
+ required: false,
+ },
+ },
+ }
+
+ class FirebaseIntegration implements IntegrationBase {
+ private config: FirebaseConfig
+ private db: Firestore
+
+ constructor(config: FirebaseConfig) {
+ this.config = config
+ this.db = new Firestore({
+ projectId: config.projectId,
+ credential: {
+ clientEmail: config.email,
+ privateKey: config.privateKey,
+ },
+ })
+ }
+
+ async create(query: { json: object; extra: { [key: string]: string } }) {
+ try {
+ return await this.db.collection(query.extra.collection).add(query.json)
+ } catch (err) {
+ console.error("Error writing to Firestore", err)
+ throw err
+ }
+ }
+
+ async read(query: {
+ field: string
+ opStr: WhereFilterOp
+ value: any
+ extra: { [key: string]: string }
+ }) {
+ try {
+ const snapshot = await this.db
+ .collection(query.extra.collection)
+ .where(query.field, query.opStr, query.value)
+ .get()
+ const result: any[] = []
+ snapshot.forEach(doc => result.push(doc.data()))
+
+ return result
+ } catch (err) {
+ console.error("Error querying Firestore", err)
+ throw err
+ }
+ }
+
+ async update(query: {
+ id: string
+ json: object
+ extra: { [key: string]: string }
+ }) {
+ try {
+ return await this.db
+ .collection(query.extra.collection)
+ .doc(query.id)
+ .update(query.json)
+ } catch (err) {
+ console.error("Error writing to mongodb", err)
+ throw err
+ }
+ }
+
+ async delete(query: { id: string; extra: { [key: string]: string } }) {
+ try {
+ return await this.db
+ .collection(query.extra.collection)
+ .doc(query.id)
+ .delete()
+ } catch (err) {
+ console.error("Error writing to mongodb", err)
+ throw err
+ }
+ }
+ }
+
+ module.exports = {
+ schema: SCHEMA,
+ integration: FirebaseIntegration,
+ }
+}
diff --git a/packages/server/src/integrations/index.ts b/packages/server/src/integrations/index.ts
index 00b00c25fb..07f3211fcb 100644
--- a/packages/server/src/integrations/index.ts
+++ b/packages/server/src/integrations/index.ts
@@ -10,6 +10,7 @@ const mysql = require("./mysql")
const arangodb = require("./arangodb")
const rest = require("./rest")
const googlesheets = require("./googlesheets")
+const firebase = require("./firebase")
const { SourceNames } = require("../definitions/datasource")
const environment = require("../environment")
@@ -25,6 +26,7 @@ const DEFINITIONS = {
[SourceNames.MYSQL]: mysql.schema,
[SourceNames.ARANGODB]: arangodb.schema,
[SourceNames.REST]: rest.schema,
+ [SourceNames.FIREBASE]: firebase.schema,
}
const INTEGRATIONS = {
@@ -39,6 +41,7 @@ const INTEGRATIONS = {
[SourceNames.MYSQL]: mysql.integration,
[SourceNames.ARANGODB]: arangodb.integration,
[SourceNames.REST]: rest.integration,
+ [SourceNames.FIREBASE]: firebase.integration,
}
// optionally add oracle integration if the oracle binary can be installed