new function wrappers with defs

This commit is contained in:
shamsmosowi
2022-03-04 09:54:37 +08:00
parent eb0f534303
commit 7b80691f4a
14 changed files with 217 additions and 38 deletions

View File

@@ -96,9 +96,3 @@ interface Rowy {
}
declare const rowy: Rowy;
type SecretNames = "sendgrid" | "unsplash" | "twilio";
enum secrets {
sendgrid = "sendgrid",
unsplash = "unsplash",
twilio = "twilio",
}

View File

@@ -23,7 +23,8 @@ import firebaseStorageDefs from "!!raw-loader!./firebaseStorage.d.ts";
import utilsDefs from "!!raw-loader!./utils.d.ts";
import rowyUtilsDefs from "!!raw-loader!./rowy.d.ts";
import extensionsDefs from "!!raw-loader!./extensions.d.ts";
import derivativeDefs from "!!raw-loader!./derivative.d.ts";
import defaultValueDefs from "!!raw-loader!./defaultValue.d.ts";
import { runRoutes } from "@src/constants/runRoutes";
export interface IUseMonacoCustomizationsProps {
minHeight?: number;
@@ -53,7 +54,7 @@ export default function useMonacoCustomizations({
fullScreen,
}: IUseMonacoCustomizationsProps) {
const theme = useTheme();
const { tableState } = useProjectContext();
const { tableState, rowyRun } = useProjectContext();
const monaco = useMonaco();
@@ -104,9 +105,6 @@ export default function useMonacoCustomizations({
"ts:filename/utils.d.ts"
);
monaco.languages.typescript.javascriptDefaults.addExtraLib(rowyUtilsDefs);
monaco.languages.typescript.javascriptDefaults.addExtraLib(
derivativeDefs
);
} catch (error) {
console.error(
"An error occurred during initialization of Monaco: ",
@@ -144,7 +142,6 @@ export default function useMonacoCustomizations({
}, [monaco, stringifiedDiagnosticsOptions]);
const addJsonFieldDefinition = async (columnKey, interfaceName) => {
// add delay
const samples = tableState?.rows
.map((row) => row[columnKey])
.filter((entry) => entry !== undefined)
@@ -168,19 +165,38 @@ export default function useMonacoCustomizations({
rendererOptions: { "just-types": "true" },
});
const newLib = result.lines.join("\n").replaceAll("export ", "");
console.log(newLib);
// console.log(newLib);
monaco?.languages.typescript.javascriptDefaults.addExtraLib(newLib);
}
};
const setSecrets = async (monaco, rowyRun) => {
// set secret options
try {
const listSecrets = await rowyRun({
route: runRoutes.listSecrets,
});
const secretsDef = `type SecretNames = ${listSecrets
.map((secret) => `"${secret}"`)
.join(" | ")}
enum secrets {
${listSecrets.map((secret) => `${secret} = "${secret}"`).join("\n")}
}
`;
monaco.languages.typescript.javascriptDefaults.addExtraLib(secretsDef);
} catch (error) {
console.error("Could not set secret definitions: ", error);
}
};
// Set row definitions
useEffect(() => {
if (!monaco) return;
if (!monaco || !rowyRun || !tableState?.columns) return;
console.log("setting row definitions");
try {
const rowDefinition =
Object.keys(tableState?.columns!)
Object.keys(tableState.columns)
.map((columnKey: string) => {
const column = tableState?.columns[columnKey];
const column = tableState.columns[columnKey];
if (getColumnType(column) === "JSON") {
const interfaceName =
columnKey[0].toUpperCase() + columnKey.slice(1);
@@ -194,7 +210,7 @@ export default function useMonacoCustomizations({
})
.join(";\n") + ";";
const availableFields = Object.keys(tableState?.columns!)
const availableFields = Object.keys(tableState.columns)
.map((columnKey: string) => `"${columnKey}"`)
.join("|\n");
@@ -232,7 +248,13 @@ export default function useMonacoCustomizations({
} catch (error) {
console.error("Could not set row definitions: ", error);
}
}, [monaco, tableState?.columns]);
// set available secrets from secretManager
try {
setSecrets(monaco, rowyRun);
} catch (error) {
console.error("Could not set secrets: ", error);
}
}, [monaco, tableState?.columns, rowyRun]);
let boxSx: SystemStyleObject<Theme> = {
minWidth: 400,

View File

@@ -13,16 +13,54 @@ import FormAutosave from "./FormAutosave";
import { FieldType } from "@src/constants/fields";
import { WIKI_LINKS } from "@src/constants/externalLinks";
import { name } from "@root/package.json";
const CodeEditor = lazy(
/* eslint-disable import/no-webpack-loader-syntax */
import defaultValueDefs from "!!raw-loader!./defaultValue.d.ts";
const _CodeEditor = lazy(
() =>
import("@src/components/CodeEditor" /* webpackChunkName: "CodeEditor" */)
);
const diagnosticsOptions = {
noSemanticValidation: false,
noSyntaxValidation: false,
noSuggestionDiagnostics: true,
};
export interface IDefaultValueInputProps extends IMenuModalProps {
handleChange: (key: any) => (update: any) => void;
}
const CodeEditor = ({ type, config, handleChange }) => {
const returnType = getFieldProp("dataType", type) ?? "any";
const defaultValueFn = config.defaultValue?.defaultValueFn
? config.defaultValue?.defaultValueFn
: config.defaultValue?.script
? `const defaultValueFn : DefaultValue = async ({row,ref,db,storage,auth})=>{
${config.defaultValue.script}
}`
: `const defaultValueFn: DefaultValue = async ({row,ref,db,storage,auth})=>{
// Write your default value code here
// for example:
// generate random hex color
// const color = "#" + Math.floor(Math.random() * 16777215).toString(16);
// return color;
// checkout the documentation for more info: https://docs.rowy.io/how-to/default-values#dynamic
}`;
return (
<_CodeEditor
value={defaultValueFn}
diagnosticsOptions={diagnosticsOptions}
extraLibs={[
defaultValueDefs.replace(
`"PLACEHOLDER_OUTPUT_TYPE"`,
`${returnType} | Promise<${returnType}>`
),
]}
onChange={handleChange("defaultValue.defaultValueFn")}
/>
);
};
export default function DefaultValueInput({
config,
handleChange,
@@ -42,6 +80,7 @@ export default function DefaultValueInput({
config.defaultValue?.value ?? getFieldProp("initialValue", _type),
},
});
return (
<>
<TextField
@@ -136,8 +175,9 @@ export default function DefaultValueInput({
<CodeEditorHelper docLink={WIKI_LINKS.howToDefaultValues} />
<Suspense fallback={<FieldSkeleton height={100} />}>
<CodeEditor
value={config.defaultValue?.script}
onChange={handleChange("defaultValue.script")}
config={config}
type={type}
handleChange={handleChange}
/>
</Suspense>
</>

View File

@@ -0,0 +1,8 @@
type DefaultValueContext = {
row: Row;
ref: FirebaseFirestore.DocumentReference;
storage: firebasestorage.Storage;
db: FirebaseFirestore.Firestore;
auth: firebaseauth.BaseAuth;
};
type DefaultValue = (context: DefaultValueContext) => "PLACEHOLDER_OUTPUT_TYPE";

View File

@@ -14,7 +14,6 @@ export default function ContextMenu() {
const configActions =
getFieldProp("contextMenuActions", selectedColumn.type) ||
function empty() {};
console.log(configActions);
const actions = configActions(selectedCell, resetContextMenu) || [];
if (!anchorEle || actions.length === 0) return <></>;

View File

@@ -32,6 +32,14 @@ import FormFieldSnippets from "./FormFieldSnippets";
import { useProjectContext } from "@src/contexts/ProjectContext";
import { WIKI_LINKS } from "@src/constants/externalLinks";
import { useAppContext } from "@src/contexts/AppContext";
/* eslint-disable import/no-webpack-loader-syntax */
import actionDefs from "!!raw-loader!./action.d.ts";
const diagnosticsOptions = {
noSemanticValidation: false,
noSyntaxValidation: false,
noSuggestionDiagnostics: true,
};
const CodeEditor = lazy(
() =>
@@ -62,7 +70,7 @@ const Settings = ({ config, onChange }) => {
const scriptExtraLibs = [
[
"declare class actionParams {",
"declare class ActionParams {",
" /**",
" * actionParams are provided by dialog popup form",
" */",
@@ -76,6 +84,7 @@ const Settings = ({ config, onChange }) => {
}),
"}",
].join("\n"),
actionDefs,
];
// Backwards-compatibility: previously user could set `confirmation` without
@@ -86,6 +95,72 @@ const Settings = ({ config, onChange }) => {
typeof config.confirmation === "string" &&
config.confirmation !== "");
const runFn = config.runFn
? config.derivativeFn
: config?.script
? `const action:Action = async ({row,ref,db,storage,auth,actionParams,user}) => {
${config.script.replace(/utilFns.getSecret/g, "rowy.secrets.getSecret")}
}`
: `const action:Action = async ({row,ref,db,storage,auth,actionParams,user}) => {
// Write your action code here
// for example:
// const authToken = await rowy.secrets.getSecret("service")
// try {
// const resp = await fetch('https://example.com/api/v1/users/'+ref.id,{
// method: 'PUT',
// headers: {
// 'Content-Type': 'application/json',
// 'Authorization': authToken
// },
// body: JSON.stringify(row)
// })
//
// return {
// success: true,
// message: 'User updated successfully on example service',
// status: "upto date"
// }
// } catch (error) {
// return {
// success: false,
// message: 'User update failed on example service',
// }
// }
// checkout the documentation for more info: https://docs.rowy.io/field-types/action#script
}`;
const undoFn = config.undoFn
? config.undoFn
: _get(config, "undo.script")
? `const action:Action = async ({row,ref,db,storage,auth,actionParams,user}) => {
${_get(config, "undo.script")}
}`
: `const action:Action = async ({row,ref,db,storage,auth,actionParams,user}) => {
// Write your undo code here
// for example:
// const authToken = await rowy.secrets.getSecret("service")
// try {
// const resp = await fetch('https://example.com/api/v1/users/'+ref.id,{
// method: 'DELETE',
// headers: {
// 'Content-Type': 'application/json',
// 'Authorization': authToken
// },
// body: JSON.stringify(row)
// })
//
// return {
// success: true,
// message: 'User deleted successfully on example service',
// status: null
// }
// } catch (error) {
// return {
// success: false,
// message: 'User delete failed on example service',
// }
// }
}`;
return (
<SteppedAccordion
steps={[
@@ -338,9 +413,10 @@ const Settings = ({ config, onChange }) => {
<Suspense fallback={<FieldSkeleton height={300} />}>
<CodeEditor
minHeight={200}
value={config.script}
onChange={onChange("script")}
value={runFn}
onChange={onChange("runFn")}
extraLibs={scriptExtraLibs}
diagnosticsOptions={diagnosticsOptions}
/>
</Suspense>
<CodeEditorHelper
@@ -447,8 +523,8 @@ const Settings = ({ config, onChange }) => {
<InputLabel variant="filled">Undo script</InputLabel>
<Suspense fallback={<FieldSkeleton height={300} />}>
<CodeEditor
value={_get(config, "undo.script")}
onChange={onChange("undo.script")}
value={undoFn}
onChange={onChange("undoFn")}
extraLibs={scriptExtraLibs}
/>
</Suspense>

View File

@@ -0,0 +1,26 @@
type ActionUser = {
timestamp: Date;
displayName: string;
email: string;
uid: string;
emailVerified: boolean;
photoURL: string;
roles: string[];
};
type ActionContext = {
row: Row;
ref: FirebaseFirestore.DocumentReference;
storage: firebasestorage.Storage;
db: FirebaseFirestore.Firestore;
auth: firebaseauth.BaseAuth;
actionParams: ActionParams;
user: ActionUser;
};
type ActionResult = {
success: boolean;
message?: any;
status?: string | number | null | undefined;
};
type Action = (context: ActionContext) => Promise<ActionResult> | ActionResult;

View File

@@ -29,6 +29,7 @@ export default function ContextMenuActions(
const selectedRowIndex = selectedCell.rowIndex as number;
const selectedColIndex = selectedCell?.colIndex;
const selectedCol = _find(columns, { index: selectedColIndex });
if (!selectedCol) return [];
const selectedRow = rows?.[selectedRowIndex];
const cellValue = _get(selectedRow, selectedCol.key);
console.log({

View File

@@ -11,10 +11,15 @@ import { FieldType } from "@src/constants/fields";
import { useProjectContext } from "@src/contexts/ProjectContext";
import { WIKI_LINKS } from "@src/constants/externalLinks";
import { getFieldProp } from "@src/components/fields";
/* eslint-disable import/no-webpack-loader-syntax */
import derivativeDefs from "!!raw-loader!./derivative.d.ts";
const CodeEditor = lazy(
() =>
import("@src/components/CodeEditor" /* webpackChunkName: "CodeEditor" */)
);
const diagnosticsOptions = {
noSemanticValidation: false,
noSyntaxValidation: false,
@@ -30,14 +35,15 @@ export default function Settings({
}: ISettingsProps) {
const { tableState } = useProjectContext();
if (!tableState?.columns) return <></>;
const columnOptions = Object.values(tableState.columns)
const columns = Object.values(tableState.columns);
const returnType = getFieldProp("dataType", config.renderFieldType) ?? "any";
console.log({ returnType, r: config.renderFieldType });
const columnOptions = columns
.filter((column) => column.fieldName !== fieldName)
.filter((column) => column.type !== FieldType.subTable)
.map((c) => ({ label: c.name, value: c.key }));
console.log({ config });
const code = config.code
? config.code
const code = config.derivativeFn
? config.derivativeFn
: config?.script
? `const derivative:Derivative = async ({row,ref,db,storage,auth})=>{
${config.script.replace(/utilFns.getSecret/g, "rowy.secrets.getSecret")}
@@ -114,6 +120,12 @@ export default function Settings({
<CodeEditor
diagnosticsOptions={diagnosticsOptions}
value={code}
extraLibs={[
derivativeDefs.replace(
`"PLACEHOLDER_OUTPUT_TYPE"`,
`${returnType} | Promise<${returnType}>`
),
]}
onChange={onChange("code")}
/>
</Suspense>

View File

@@ -7,4 +7,4 @@ type DerivativeContext = {
change: any;
};
type Derivative = (context: DerivativeContext) => Promise<any>;
type Derivative = (context: DerivativeContext) => "PLACEHOLDER_OUTPUT_TYPE";

View File

@@ -18,8 +18,7 @@ export const config: IFieldConfig = {
type: FieldType.image,
name: "Image",
group: "File",
dataType:
"{ downloadURL: string; lastModifiedTS: number; name: string; type: string; ref: string; }[]",
dataType: "RowyFile[]",
initialValue: [],
icon: <ImageIcon />,
description:

View File

@@ -27,6 +27,7 @@ export default function BasicContextMenuActions(
const selectedRowIndex = selectedCell.rowIndex as number;
const selectedColIndex = selectedCell?.colIndex;
const selectedCol = _find(columns, { index: selectedColIndex });
if (!selectedCol) return [];
const selectedRow = rows?.[selectedRowIndex];
const cellValue = _get(selectedRow, selectedCol.key);

View File

@@ -36,6 +36,7 @@ export const runRoutes = {
firestoreRules: { path: "/firestoreRules", method: "GET" } as RunRoute,
setFirestoreRules: { path: "/setFirestoreRules", method: "POST" } as RunRoute,
listCollections: { path: "/listCollections", method: "GET" } as RunRoute,
listSecrets: { path: "/listSecrets", method: "GET" } as RunRoute,
serviceAccountAccess: {
path: "/serviceAccountAccess",
method: "GET",

View File

@@ -17,7 +17,7 @@ export const rowyRun = async ({
route,
body,
params,
localhost = false,
localhost = true,
json = true,
signal,
}: IRowyRunRequestProps) => {