Merge pull request #4864 from mslourens/google_firebase_integration

Google firebase integration
This commit is contained in:
Michael Drury 2022-03-18 10:07:30 +00:00 committed by GitHub
commit 2d011bce0d
8 changed files with 335 additions and 0 deletions

View File

@ -0,0 +1,54 @@
<script>
export let width = "100"
export let height = "100"
</script>
<svg
xmlns="http://www.w3.org/2000/svg"
xmlns:xlink="http://www.w3.org/1999/xlink"
viewBox="23 6 469 132"
{width}
{height}
>
<defs id="defs202">
<linearGradient id="a" x1="-3.49%" x2="100.83%" y1="17.02%" y2="92.9%">
<stop offset="0%" stop-color="#fff" stop-opacity=".1" id="stop192" />
<stop offset="14%" stop-color="#fff" stop-opacity=".08" id="stop194" />
<stop offset="61%" stop-color="#fff" stop-opacity=".02" id="stop196" />
<stop offset="100%" stop-color="#fff" stop-opacity="0" id="stop198" />
</linearGradient>
<path
id="b"
d="M106.687 35.2742c-.186-1.0977-.967-2-2.0244-2.338s-2.2148-.057-3.0002.73L86.2473 49.166l-12.12-23.1455c-.5133-.9786-1.525-1.5914-2.6273-1.5914s-2.114.6128-2.6273 1.5914l-6.6277 12.656L45.62 7.5726c-.603-1.1297-1.8588-1.746-3.118-1.5297s-2.2394 1.216-2.4335 2.4827L24 111.701l42.9727 24.1654c2.6985 1.5113 5.985 1.5113 8.6836 0L119 111.701l-12.313-76.427z"
/>
</defs>
<g id="g305" transform="matrix(2.9011579,0,0,2.9011579,43.533284,-135.93685)">
<path
fill="#ffa000"
d="M 23.8266,111.7182 39.9588,8.4901 c 0.1972,-1.266 1.1818,-2.264 2.445,-2.4786 1.2632,-0.2146 2.522,0.4028 3.126,1.5327 L 62.2133,38.6615 68.8633,26 c 0.515,-0.979 1.5303,-1.592 2.6366,-1.592 1.1063,0 2.1215,0.613 2.6366,1.592 l 45.0227,85.718 H 23.8266 Z"
id="path204"
/>
<path
fill="#f57c00"
d="M 79.566,71.5074 62.2124,38.6472 23.8334,111.7187 Z"
id="path206"
/>
<path
fill="#ffca28"
d="m 119.1666,111.7187 -12.356,-76.4603 c -0.1867,-1.098 -0.9703,-2 -2.0315,-2.34 -1.0612,-0.34 -2.2226,-0.057 -3.0107,0.7302 l -77.935,78.069 43.1234,24.1834 c 2.708,1.512 6.006,1.512 8.714,0 l 43.4958,-24.1834 z"
id="path208"
/>
<path
fill="#ffffff"
fill-opacity="0.2"
d="m 106.8105,35.2584 c -0.1867,-1.098 -0.9703,-2 -2.0315,-2.34 -1.0612,-0.34 -2.2226,-0.057 -3.0107,0.7302 L 86.3,49.1562 74.1365,26 c -0.515,-0.979 -1.5303,-1.592 -2.6366,-1.592 -1.1063,0 -2.1215,0.613 -2.6366,1.592 L 62.2133,38.6615 45.529,7.5447 C 44.924,6.4145 43.6637,5.7981 42.399,6.0143 41.1343,6.2305 40.153,7.231 39.958,8.498 L 23.8333,111.7187 h -0.052 l 0.052,0.0596 0.4245,0.2085 77.488,-77.5775 c 0.7877,-0.7915 1.952,-1.076 3.016,-0.737 1.064,0.339 1.849,1.2445 2.0338,2.3457 l 12.2518,75.775 0.1192,-0.0745 -12.356,-76.4603 z M 23.9748,111.5772 39.9655,9.228 c 0.1948,-1.267 1.1784,-2.2675 2.442,-2.4837 1.2636,-0.2162 2.524,0.4 3.13,1.5304 L 62.22,39.392 68.87,26.7305 c 0.515,-0.979 1.5303,-1.592 2.6366,-1.592 1.1063,0 2.1215,0.613 2.6366,1.592 l 11.9167,22.664 -62.0858,62.1827 z"
id="path210"
/>
<path
fill="#a52714"
opacity="0.2"
d="m 75.6708,135.1722 c -2.708,1.512 -6.006,1.512 -8.714,0 l -43.0192,-24.1162 -0.1043,0.663 43.1234,24.176 c 2.708,1.512 6.006,1.512 8.714,0 l 43.4958,-24.176 -0.1117,-0.6852 -43.384,24.1387 z"
id="path212"
/>
</g>
</svg>

View File

@ -0,0 +1,67 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg
viewBox="23 6 469 132"
width="100"
height="100"
xmlns="http://www.w3.org/2000/svg"
xmlns:svg="http://www.w3.org/2000/svg">
<defs
id="defs202">
<linearGradient
id="a"
x1="-3.49%"
x2="100.83%"
y1="17.02%"
y2="92.9%">
<stop
offset="0%"
stop-color="#fff"
stop-opacity=".1"
id="stop192" />
<stop
offset="14%"
stop-color="#fff"
stop-opacity=".08"
id="stop194" />
<stop
offset="61%"
stop-color="#fff"
stop-opacity=".02"
id="stop196" />
<stop
offset="100%"
stop-color="#fff"
stop-opacity="0"
id="stop198" />
</linearGradient>
<path
id="b"
d="M106.687 35.2742c-.186-1.0977-.967-2-2.0244-2.338s-2.2148-.057-3.0002.73L86.2473 49.166l-12.12-23.1455c-.5133-.9786-1.525-1.5914-2.6273-1.5914s-2.114.6128-2.6273 1.5914l-6.6277 12.656L45.62 7.5726c-.603-1.1297-1.8588-1.746-3.118-1.5297s-2.2394 1.216-2.4335 2.4827L24 111.701l42.9727 24.1654c2.6985 1.5113 5.985 1.5113 8.6836 0L119 111.701l-12.313-76.427z" />
</defs>
<g
id="g305"
transform="matrix(2.9011579,0,0,2.9011579,43.533284,-135.93685)">
<path
fill="#ffa000"
d="M 23.8266,111.7182 39.9588,8.4901 c 0.1972,-1.266 1.1818,-2.264 2.445,-2.4786 1.2632,-0.2146 2.522,0.4028 3.126,1.5327 L 62.2133,38.6615 68.8633,26 c 0.515,-0.979 1.5303,-1.592 2.6366,-1.592 1.1063,0 2.1215,0.613 2.6366,1.592 l 45.0227,85.718 H 23.8266 Z"
id="path204" />
<path
fill="#f57c00"
d="M 79.566,71.5074 62.2124,38.6472 23.8334,111.7187 Z"
id="path206" />
<path
fill="#ffca28"
d="m 119.1666,111.7187 -12.356,-76.4603 c -0.1867,-1.098 -0.9703,-2 -2.0315,-2.34 -1.0612,-0.34 -2.2226,-0.057 -3.0107,0.7302 l -77.935,78.069 43.1234,24.1834 c 2.708,1.512 6.006,1.512 8.714,0 l 43.4958,-24.1834 z"
id="path208" />
<path
fill="#ffffff"
fill-opacity="0.2"
d="m 106.8105,35.2584 c -0.1867,-1.098 -0.9703,-2 -2.0315,-2.34 -1.0612,-0.34 -2.2226,-0.057 -3.0107,0.7302 L 86.3,49.1562 74.1365,26 c -0.515,-0.979 -1.5303,-1.592 -2.6366,-1.592 -1.1063,0 -2.1215,0.613 -2.6366,1.592 L 62.2133,38.6615 45.529,7.5447 C 44.924,6.4145 43.6637,5.7981 42.399,6.0143 41.1343,6.2305 40.153,7.231 39.958,8.498 L 23.8333,111.7187 h -0.052 l 0.052,0.0596 0.4245,0.2085 77.488,-77.5775 c 0.7877,-0.7915 1.952,-1.076 3.016,-0.737 1.064,0.339 1.849,1.2445 2.0338,2.3457 l 12.2518,75.775 0.1192,-0.0745 -12.356,-76.4603 z M 23.9748,111.5772 39.9655,9.228 c 0.1948,-1.267 1.1784,-2.2675 2.442,-2.4837 1.2636,-0.2162 2.524,0.4 3.13,1.5304 L 62.22,39.392 68.87,26.7305 c 0.515,-0.979 1.5303,-1.592 2.6366,-1.592 1.1063,0 2.1215,0.613 2.6366,1.592 l 11.9167,22.664 -62.0858,62.1827 z"
id="path210" />
<path
fill="#a52714"
opacity="0.2"
d="m 75.6708,135.1722 c -2.708,1.512 -6.006,1.512 -8.714,0 l -43.0192,-24.1162 -0.1043,0.663 43.1234,24.176 c 2.708,1.512 6.006,1.512 8.714,0 l 43.4958,-24.176 -0.1117,-0.6852 -43.384,24.1387 z"
id="path212" />
</g>
</svg>

After

Width:  |  Height:  |  Size: 3.1 KiB

View File

@ -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,
}

View File

@ -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 = [

View File

@ -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",

View File

@ -48,6 +48,7 @@ export enum SourceNames {
REST = "REST",
ORACLE = "ORACLE",
GOOGLE_SHEETS = "GOOGLE_SHEETS",
FIREBASE = "FIREBASE",
}
export enum IncludeRelationships {

View File

@ -0,0 +1,205 @@
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
serviceAccount?: 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,
},
serviceAccount: {
type: DatasourceFieldTypes.JSON,
required: false,
},
},
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,
},
filterField: {
displayName: "Filter field",
type: DatasourceFieldTypes.STRING,
required: false,
},
filter: {
displayName: "Filter comparison",
type: DatasourceFieldTypes.LIST,
required: false,
data: {
read: [
"==",
"<",
"<=",
"==",
"!=",
">=",
">",
"array-contains",
"in",
"not-in",
"array-contains-any",
],
},
},
filterValue: {
displayName: "Filter value",
type: DatasourceFieldTypes.STRING,
required: false,
},
},
}
class FirebaseIntegration implements IntegrationBase {
private config: FirebaseConfig
private db: Firestore
constructor(config: FirebaseConfig) {
this.config = config
if (config.serviceAccount) {
const serviceAccount = JSON.parse(config.serviceAccount)
this.db = new Firestore({
projectId: serviceAccount.project_id,
credentials: {
client_email: serviceAccount.client_email,
private_key: serviceAccount.private_key,
},
})
} else {
this.db = new Firestore({
projectId: config.projectId,
credentials: {
client_email: config.email,
private_key: config.privateKey,
},
})
}
}
async create(query: { json: object; extra: { [key: string]: string } }) {
try {
const documentReference = this.db
.collection(query.extra.collection)
.doc()
await documentReference.set({ ...query.json, id: documentReference.id })
const snapshot = await documentReference.get()
return snapshot.data()
} catch (err) {
console.error("Error writing to Firestore", err)
throw err
}
}
async read(query: { json: object; extra: { [key: string]: string } }) {
try {
let snapshot
const collectionRef = this.db.collection(query.extra.collection)
if (
query.extra.filterField &&
query.extra.filter &&
query.extra.filterValue
) {
snapshot = await collectionRef
.where(
query.extra.filterField,
query.extra.filter as WhereFilterOp,
query.extra.filterValue
)
.get()
} else {
snapshot = await collectionRef.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: {
json: Record<string, any>
extra: { [key: string]: string }
}) {
try {
await this.db
.collection(query.extra.collection)
.doc(query.json.id)
.update(query.json)
return (
await this.db
.collection(query.extra.collection)
.doc(query.json.id)
.get()
).data()
} catch (err) {
console.error("Error writing to firebase", err)
throw err
}
}
async delete(query: {
json: { id: string }
extra: { [key: string]: string }
}) {
try {
await this.db
.collection(query.extra.collection)
.doc(query.json.id)
.delete()
return true
} catch (err) {
console.error("Error writing to mongodb", err)
throw err
}
}
}
module.exports = {
schema: SCHEMA,
integration: FirebaseIntegration,
}
}

View File

@ -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