This commit is contained in:
Shams mosowi
2020-10-23 20:34:33 +11:00
parent d71a95013a
commit b8cdaa7911
11 changed files with 3167 additions and 0 deletions

View File

@@ -0,0 +1,8 @@
{
"functions": {
"predeploy": [
"npm --prefix \"$RESOURCE_DIR\" run lint",
"npm --prefix \"$RESOURCE_DIR\" run build"
]
}
}

View File

@@ -0,0 +1,70 @@
module.exports = {
env: {
browser: true,
es6: true,
node: true,
},
extends: [
"plugin:import/errors",
"plugin:import/warnings",
],
parser: "@typescript-eslint/parser",
parserOptions: {
project: "tsconfig.json",
sourceType: "module",
},
plugins: [
"@typescript-eslint",
"import",
],
rules: {
"@typescript-eslint/adjacent-overload-signatures": "error",
"@typescript-eslint/no-empty-function": "error",
"@typescript-eslint/no-empty-interface": "warn",
"@typescript-eslint/no-floating-promises": "error",
"@typescript-eslint/no-namespace": "error",
"@typescript-eslint/no-unnecessary-type-assertion": "error",
"@typescript-eslint/prefer-for-of": "warn",
"@typescript-eslint/triple-slash-reference": "error",
"@typescript-eslint/unified-signatures": "warn",
"comma-dangle": "warn",
"constructor-super": "error",
eqeqeq: ["warn", "always"],
"import/no-deprecated": "warn",
"import/no-extraneous-dependencies": "error",
"import/no-unassigned-import": "warn",
"no-cond-assign": "error",
"no-duplicate-case": "error",
"no-duplicate-imports": "error",
"no-empty": [
"error",
{
allowEmptyCatch: true,
},
],
"no-invalid-this": "error",
"no-new-wrappers": "error",
"no-param-reassign": "error",
"no-redeclare": "error",
"no-sequences": "error",
"no-shadow": [
"error",
{
hoist: "all",
},
],
"no-throw-literal": "error",
"no-unsafe-finally": "error",
"no-unused-labels": "error",
"no-var": "warn",
"no-void": "error",
"prefer-const": "warn",
},
settings: {
jsdoc: {
tagNamePreference: {
returns: "return",
},
},
},
};

12
cloud_actions/functions/.gitignore vendored Normal file
View File

@@ -0,0 +1,12 @@
# Compiled JavaScript files
**/*.js
**/*.js.map
# Except the ESLint config file
!.eslintrc.js
# TypeScript v1 declaration files
typings/
# Node.js dependency directory
node_modules/

View File

@@ -0,0 +1,31 @@
{
"name": "functions",
"scripts": {
"lint": "eslint \"src/**/*\"",
"build": "tsc",
"serve": "npm run build && firebase emulators:start --only functions",
"shell": "npm run build && firebase functions:shell",
"start": "npm run shell",
"deploy": "firebase deploy --only functions",
"logs": "firebase functions:log"
},
"engines": {
"node": "10"
},
"main": "lib/index.js",
"dependencies": {
"firebase-admin": "^9.2.0",
"firebase-functions": "^3.11.0"
},
"devDependencies": {
"@typescript-eslint/eslint-plugin": "^3.9.1",
"@typescript-eslint/parser": "^3.8.0",
"eslint": "^7.6.0",
"eslint-plugin-import": "^2.22.0",
"firebase-functions-test": "^0.2.0",
"typescript": "^3.8.0",
"husky": "^4.2.5",
"prettier": "^2.1.1"
},
"private": true
}

View File

@@ -0,0 +1,13 @@
// Initialize Firebase Admin
import * as functions from "firebase-functions";
import * as admin from "firebase-admin";
admin.initializeApp();
// Initialize Cloud Firestore Database
export const db = admin.firestore();
// Initialize Auth
export const auth = admin.auth();
const settings = { timestampsInSnapshots: true };
db.settings(settings);
export const env = functions.config();

View File

@@ -0,0 +1,126 @@
import * as functions from "firebase-functions";
import { db, auth } from "./config";
import * as admin from "firebase-admin";
import utilFns from "./utils";
type ActionData = {
ref: {
id: string;
path: string;
parentId: string;
tablePath: string;
};
schemaDocPath?: string;
row: any;
column: any;
action: "run" | "redo" | "undo";
actionParams: any;
};
// import {
// makeId,
// hasGoogleMailServer,
// hasMissingFields,
// } from "../utils";
import { hasAnyRole } from "./utils/auth";
const missingFieldsReducer = (data: any) => (acc: string[], curr: string) => {
if (data[curr] === undefined) {
return [...acc, curr];
} else return acc;
};
const generateSchemaDocPath = (tablePath:string) => {
const pathComponents = tablePath.split("/");
return `_FIRETABLE_/settings/${
pathComponents[1] === "table" ? "schema" : "groupSchema"
}/${pathComponents[2]}`;
};
const serverTimestamp = admin.firestore.FieldValue.serverTimestamp;
export const actionScript = functions.https.onCall(
async (data: ActionData, context: functions.https.CallableContext) => {
try {
if (!context) {
throw Error(`You are unauthenticated`);
}
const { ref, actionParams, row, column, action, schemaDocPath } = data;
const _schemaDocPath =
schemaDocPath ?? generateSchemaDocPath(ref.tablePath);
const schemaDoc = await db.doc(_schemaDocPath).get();
const schemaDocData = schemaDoc.data();
if (!schemaDocData) {
return {
success: false,
message: "no schema found",
};
}
const config = schemaDocData.columns[column.key].config;
const { script, requiredRoles, requiredFields, undo, redo } = config;
if (!hasAnyRole(requiredRoles, context)) {
throw Error(`You don't have the required roles permissions`);
}
const missingRequiredFields = requiredFields
? requiredFields.reduce(missingFieldsReducer(row), [])
: [];
if (missingRequiredFields.length > 0) {
throw new Error(
`Missing required fields:${missingRequiredFields.join(", ")}`
);
}
//
// get auth
console.log(
JSON.stringify({
undo,
redo,
row,
ref,
actionParams,
column,
schemaDocData,
script,
requiredRoles,
requiredFields,
})
);
const result: {
message: string;
status: string;
success: boolean;
} = await eval(
`async({row,db, ref,auth,utilFns,actionParams,context})=>{${
action === "undo" ? config["undo.script"] : script
}}`
)({ row, db, auth, utilFns, ref, actionParams, context });
if (result.success)
return {
success: result.success,
message: result.message,
cellValue: {
redo: config["redo.enabled"],
status: result.status,
completedAt: serverTimestamp(),
meta: { ranBy: context.auth!.token.email },
undo: action !== "undo" && config["undo.enabled"],
},
undo: config["undo.enabled"],
redo: config["redo.enabled"],
};
else
return {
success: false,
message: result.message,
};
} catch (error) {
return {
success: false,
error,
message: error.message,
};
}
}
);

View File

@@ -0,0 +1,17 @@
import * as functions from "firebase-functions";
export const hasAnyRole = (
authorizedRoles: string[],
context: functions.https.CallableContext
) => {
if (!context.auth || !context.auth.token.roles) return false;
const userRoles = context.auth.token.roles as string[];
const authorization = authorizedRoles.reduce(
(authorized: boolean, role: string) => {
if (userRoles.includes(role)) return true;
else return authorized;
},
false
);
return authorization;
};

View File

@@ -0,0 +1,4 @@
export const sendEmail = (msg: any) => {
// pubsub.push
}

View File

@@ -0,0 +1,38 @@
import * as admin from "firebase-admin";
export const serverTimestamp = admin.firestore.FieldValue.serverTimestamp;
import { sendEmail } from "./email";
import { hasAnyRole } from "./auth";
var characters =
"0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
export function generateId(length:number):string {
var result = "";
var charactersLength = characters.length;
for (var i = 0; i < length; i++) {
result += characters.charAt(Math.floor(Math.random() * charactersLength));
}
return result;
}
const hasRequiredFields = (requiredFields: string[], data: any) =>
requiredFields.reduce((acc: boolean, currField: string) => {
if (data[currField] === undefined || data[currField] === null) return false;
else return acc;
}, true);
async function asyncForEach(array: any[], callback: Function) {
for (let index = 0; index < array.length; index++) {
await callback(array[index], index, array);
}
}
const identifyTriggerType = (beforeData:any, afterData:any) =>
Boolean(beforeData) && Boolean(afterData)
? "update"
: Boolean(afterData)
? "create"
: "delete";
export default { hasRequiredFields,generateId, sendEmail, serverTimestamp, hasAnyRole,asyncForEach,identifyTriggerType };

View File

@@ -0,0 +1,15 @@
{
"compilerOptions": {
"module": "commonjs",
"noImplicitReturns": true,
"noUnusedLocals": true,
"outDir": "lib",
"sourceMap": true,
"strict": true,
"target": "es2017"
},
"compileOnSave": true,
"include": [
"src"
]
}

File diff suppressed because it is too large Load Diff