mirror of
https://github.com/rowyio/rowy.git
synced 2025-12-29 00:16:39 +01:00
Merge branch 'develop' of https://github.com/rowyio/rowy into develop
This commit is contained in:
13
README.md
13
README.md
@@ -13,12 +13,12 @@ Low-code for Firebase and Google Cloud.
|
||||
|
||||
<div align="center">
|
||||
|
||||
[](https://discord.gg/fjBugmvzZP)
|
||||
[](https://discord.gg/fjBugmvzZP)
|
||||
|
||||
<p align="center">
|
||||
<a href="http://www.rowy.io"><b>Website</b></a> •
|
||||
<a href="http://docs.rowy.io"><b>Documentation</b></a> •
|
||||
<a href="https://discord.gg/fjBugmvzZP"><b>Discord</b></a> •
|
||||
<a href="https://discord.gg/fjBugmvzZP"><b>Chat with us</b></a> •
|
||||
<a href="https://twitter.com/rowyio"><b>Twitter</b></a>
|
||||
</p>
|
||||
|
||||
@@ -27,11 +27,12 @@ Low-code for Firebase and Google Cloud.
|
||||
|
||||
</div>
|
||||
|
||||
## Live Demo
|
||||
## Live Demo 🛝
|
||||
|
||||
💥 Check out the [live demo](https://demo.rowy.io/) of Rowy 💥
|
||||
💥 Explore Rowy on [live demo playground](https://demo.rowy.io/) 💥
|
||||
|
||||
## Features ✨
|
||||
|
||||
## Features
|
||||
|
||||
<!-- <table>
|
||||
<tr>
|
||||
@@ -39,7 +40,7 @@ Low-code for Firebase and Google Cloud.
|
||||
<a href="#">Database</a>
|
||||
</th>
|
||||
<th>
|
||||
<a href="#">Code</a>
|
||||
<a href="#">Automation</a>
|
||||
</th>
|
||||
</tr>
|
||||
<tr>
|
||||
|
||||
@@ -133,3 +133,16 @@ export const FunctionsIndexAtom = atom<FunctionSettings[]>([]);
|
||||
export const updateFunctionAtom = atom<
|
||||
UpdateCollectionDocFunction<FunctionSettings> | undefined
|
||||
>(undefined);
|
||||
|
||||
export interface ISecretNames {
|
||||
loading: boolean;
|
||||
secretNames: null | string[];
|
||||
}
|
||||
|
||||
export const secretNamesAtom = atom<ISecretNames>({
|
||||
loading: true,
|
||||
secretNames: null,
|
||||
});
|
||||
export const updateSecretNamesAtom = atom<
|
||||
((clearSecretNames?: boolean) => Promise<void>) | undefined
|
||||
>(undefined);
|
||||
|
||||
@@ -19,8 +19,7 @@ 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 { runRoutes } from "@src/constants/runRoutes";
|
||||
import { rowyRunAtom, projectScope } from "@src/atoms/projectScope";
|
||||
import { projectScope, secretNamesAtom } from "@src/atoms/projectScope";
|
||||
import { getFieldProp } from "@src/components/fields";
|
||||
|
||||
export interface IUseMonacoCustomizationsProps {
|
||||
@@ -53,8 +52,8 @@ export default function useMonacoCustomizations({
|
||||
const theme = useTheme();
|
||||
const monaco = useMonaco();
|
||||
const [tableRows] = useAtom(tableRowsAtom, tableScope);
|
||||
const [rowyRun] = useAtom(rowyRunAtom, projectScope);
|
||||
const [tableColumnsOrdered] = useAtom(tableColumnsOrderedAtom, tableScope);
|
||||
const [secretNames] = useAtom(secretNamesAtom, projectScope);
|
||||
|
||||
useEffect(() => {
|
||||
return () => {
|
||||
@@ -206,26 +205,6 @@ export default function useMonacoCustomizations({
|
||||
//}
|
||||
};
|
||||
|
||||
const setSecrets = async () => {
|
||||
// set secret options
|
||||
try {
|
||||
const listSecrets = await rowyRun({
|
||||
route: runRoutes.listSecrets,
|
||||
});
|
||||
const secretsDef = `type SecretNames = ${listSecrets
|
||||
.map((secret: string) => `"${secret}"`)
|
||||
.join(" | ")}
|
||||
enum secrets {
|
||||
${listSecrets
|
||||
.map((secret: string) => `${secret} = "${secret}"`)
|
||||
.join("\n")}
|
||||
}
|
||||
`;
|
||||
monaco?.languages.typescript.javascriptDefaults.addExtraLib(secretsDef);
|
||||
} catch (error) {
|
||||
console.error("Could not set secret definitions: ", error);
|
||||
}
|
||||
};
|
||||
//TODO: types
|
||||
const setBaseDefinitions = () => {
|
||||
const rowDefinition =
|
||||
@@ -275,14 +254,24 @@ export default function useMonacoCustomizations({
|
||||
} catch (error) {
|
||||
console.error("Could not set basic", error);
|
||||
}
|
||||
// set available secrets from secretManager
|
||||
try {
|
||||
setSecrets();
|
||||
} catch (error) {
|
||||
console.error("Could not set secrets: ", error);
|
||||
}
|
||||
}, [monaco, tableColumnsOrdered]);
|
||||
|
||||
useEffect(() => {
|
||||
if (!monaco) return;
|
||||
if (secretNames.loading) return;
|
||||
if (!secretNames.secretNames) return;
|
||||
const secretsDef = `type SecretNames = ${secretNames.secretNames
|
||||
.map((secret: string) => `"${secret}"`)
|
||||
.join(" | ")}
|
||||
enum secrets {
|
||||
${secretNames.secretNames
|
||||
.map((secret: string) => `${secret} = "${secret}"`)
|
||||
.join("\n")}
|
||||
}
|
||||
`;
|
||||
monaco?.languages.typescript.javascriptDefaults.addExtraLib(secretsDef);
|
||||
}, [monaco, secretNames]);
|
||||
|
||||
let boxSx: SystemStyleObject<Theme> = {
|
||||
minWidth: 400,
|
||||
minHeight,
|
||||
|
||||
@@ -61,15 +61,19 @@ function CodeEditor({ type, column, handleChange }: ICodeEditorProps) {
|
||||
// WRITE YOUR CODE ONLY ABOVE THIS LINE. DO NOT WRITE CODE/COMMENTS OUTSIDE THE FUNCTION BODY
|
||||
}`;
|
||||
} else {
|
||||
dynamicValueFn = `const dynamicValueFn: DefaultValue = async ({row,ref,db,storage,auth,logging})=>{
|
||||
// WRITE YOUR CODE ONLY BELOW THIS LINE. DO NOT WRITE CODE/COMMENTS OUTSIDE THE FUNCTION BODY
|
||||
logging.log("dynamicValueFn started")
|
||||
|
||||
dynamicValueFn = `// Import any NPM package needed
|
||||
// import _ from "lodash";
|
||||
|
||||
const defaultValue: DefaultValue = async ({ row, ref, db, storage, auth, logging }) => {
|
||||
logging.log("dynamicValueFn started");
|
||||
|
||||
// Example: generate random hex color
|
||||
// const color = "#" + Math.floor(Math.random() * 16777215).toString(16);
|
||||
// return color;
|
||||
// WRITE YOUR CODE ONLY ABOVE THIS LINE. DO NOT WRITE CODE/COMMENTS OUTSIDE THE FUNCTION BODY
|
||||
}`;
|
||||
};
|
||||
|
||||
export default defaultValue;
|
||||
`;
|
||||
}
|
||||
|
||||
return (
|
||||
|
||||
@@ -80,6 +80,9 @@ export default function GetStartedChecklist({
|
||||
marginRight: `max(env(safe-area-inset-right), 8px)`,
|
||||
width: 360,
|
||||
},
|
||||
".MuiStepLabel-iconContainer.Mui-active svg": {
|
||||
transform: "rotate(0deg) !important",
|
||||
},
|
||||
},
|
||||
]}
|
||||
>
|
||||
|
||||
@@ -94,7 +94,7 @@ export function useMenuAction(
|
||||
try {
|
||||
text = await navigator.clipboard.readText();
|
||||
} catch (e) {
|
||||
enqueueSnackbar(`Read clilboard permission denied.`, {
|
||||
enqueueSnackbar(`Read clipboard permission denied.`, {
|
||||
variant: "error",
|
||||
});
|
||||
return;
|
||||
|
||||
@@ -67,7 +67,7 @@ export const fieldParser = (fieldType: FieldType) => {
|
||||
case FieldType.dateTime:
|
||||
return (v: string) => {
|
||||
const date = parseISO(v);
|
||||
return isValidDate(date) ? date.getTime() : null;
|
||||
return isValidDate(date) ? new Date(date) : null;
|
||||
};
|
||||
default:
|
||||
return (v: any) => v;
|
||||
|
||||
@@ -8,7 +8,7 @@ import { tableScope, updateFieldAtom } from "@src/atoms/tableScope";
|
||||
import { TableRowRef } from "@src/types/table";
|
||||
import SnackbarProgress from "@src/components/SnackbarProgress";
|
||||
|
||||
const MAX_CONCURRENT_TASKS = 10;
|
||||
const MAX_CONCURRENT_TASKS = 1000;
|
||||
|
||||
type UploadParamTypes = {
|
||||
docRef: TableRowRef;
|
||||
|
||||
@@ -1,10 +1,7 @@
|
||||
import { Typography } from "@mui/material";
|
||||
import WarningIcon from "@mui/icons-material/WarningAmber";
|
||||
import { TableSettings } from "@src/types/table";
|
||||
import {
|
||||
IWebhook,
|
||||
ISecret,
|
||||
} from "@src/components/TableModals/WebhooksModal/utils";
|
||||
import { IWebhook } from "@src/components/TableModals/WebhooksModal/utils";
|
||||
|
||||
const requestType = [
|
||||
"declare type WebHookRequest {",
|
||||
@@ -101,11 +98,7 @@ export const webhookBasic = {
|
||||
// WRITE YOUR CODE ONLY ABOVE THIS LINE. DO NOT WRITE CODE/COMMENTS OUTSIDE THE FUNCTION BODY
|
||||
}`,
|
||||
},
|
||||
auth: (
|
||||
webhookObject: IWebhook,
|
||||
setWebhookObject: (w: IWebhook) => void,
|
||||
secrets: ISecret
|
||||
) => {
|
||||
Auth: (webhookObject: IWebhook, setWebhookObject: (w: IWebhook) => void) => {
|
||||
return (
|
||||
<Typography color="text.disabled">
|
||||
<WarningIcon aria-label="Warning" style={{ verticalAlign: "bottom" }} />
|
||||
|
||||
@@ -1,10 +1,7 @@
|
||||
import { Typography, Link, TextField } from "@mui/material";
|
||||
import InlineOpenInNewIcon from "@src/components/InlineOpenInNewIcon";
|
||||
import { TableSettings } from "@src/types/table";
|
||||
import {
|
||||
IWebhook,
|
||||
ISecret,
|
||||
} from "@src/components/TableModals/WebhooksModal/utils";
|
||||
import { IWebhook } from "@src/components/TableModals/WebhooksModal/utils";
|
||||
|
||||
export const webhookFirebaseAuth = {
|
||||
name: "firebaseAuth",
|
||||
@@ -41,11 +38,7 @@ export const webhookFirebaseAuth = {
|
||||
// WRITE YOUR CODE ONLY ABOVE THIS LINE. DO NOT WRITE CODE/COMMENTS OUTSIDE THE FUNCTION BODY
|
||||
}`,
|
||||
},
|
||||
auth: (
|
||||
webhookObject: IWebhook,
|
||||
setWebhookObject: (w: IWebhook) => void,
|
||||
secrets: ISecret
|
||||
) => {
|
||||
Auth: (webhookObject: IWebhook, setWebhookObject: (w: IWebhook) => void) => {
|
||||
return (
|
||||
<>
|
||||
<Typography variant="inherit" paragraph>
|
||||
|
||||
@@ -1,10 +1,7 @@
|
||||
import { Typography, Link, TextField } from "@mui/material";
|
||||
import InlineOpenInNewIcon from "@src/components/InlineOpenInNewIcon";
|
||||
import { TableSettings } from "@src/types/table";
|
||||
import {
|
||||
IWebhook,
|
||||
ISecret,
|
||||
} from "@src/components/TableModals/WebhooksModal/utils";
|
||||
import { IWebhook } from "@src/components/TableModals/WebhooksModal/utils";
|
||||
|
||||
export const webhookSendgrid = {
|
||||
name: "SendGrid",
|
||||
@@ -51,11 +48,7 @@ export const webhookSendgrid = {
|
||||
// WRITE YOUR CODE ONLY ABOVE THIS LINE. DO NOT WRITE CODE/COMMENTS OUTSIDE THE FUNCTION BODY
|
||||
}`,
|
||||
},
|
||||
auth: (
|
||||
webhookObject: IWebhook,
|
||||
setWebhookObject: (w: IWebhook) => void,
|
||||
secrets: ISecret
|
||||
) => {
|
||||
Auth: (webhookObject: IWebhook, setWebhookObject: (w: IWebhook) => void) => {
|
||||
return (
|
||||
<>
|
||||
<Typography gutterBottom>
|
||||
|
||||
@@ -1,14 +1,18 @@
|
||||
import { Typography, Link, TextField, Alert } from "@mui/material";
|
||||
import { useAtom } from "jotai";
|
||||
import { Typography, Link, TextField, Alert, Box } from "@mui/material";
|
||||
import InlineOpenInNewIcon from "@src/components/InlineOpenInNewIcon";
|
||||
import { TableSettings } from "@src/types/table";
|
||||
import { IWebhook } from "@src/components/TableModals/WebhooksModal/utils";
|
||||
import {
|
||||
IWebhook,
|
||||
ISecret,
|
||||
} from "@src/components/TableModals/WebhooksModal/utils";
|
||||
projectScope,
|
||||
secretNamesAtom,
|
||||
updateSecretNamesAtom,
|
||||
} from "@src/atoms/projectScope";
|
||||
import InputLabel from "@mui/material/InputLabel";
|
||||
import MenuItem from "@mui/material/MenuItem";
|
||||
import FormControl from "@mui/material/FormControl";
|
||||
import Select from "@mui/material/Select";
|
||||
import LoadingButton from "@mui/lab/LoadingButton";
|
||||
|
||||
export const webhookStripe = {
|
||||
name: "Stripe",
|
||||
@@ -49,11 +53,10 @@ export const webhookStripe = {
|
||||
// WRITE YOUR CODE ONLY ABOVE THIS LINE. DO NOT WRITE CODE/COMMENTS OUTSIDE THE FUNCTION BODY
|
||||
}`,
|
||||
},
|
||||
auth: (
|
||||
webhookObject: IWebhook,
|
||||
setWebhookObject: (w: IWebhook) => void,
|
||||
secrets: ISecret
|
||||
) => {
|
||||
Auth: (webhookObject: IWebhook, setWebhookObject: (w: IWebhook) => void) => {
|
||||
const [secretNames] = useAtom(secretNamesAtom, projectScope);
|
||||
const [updateSecretNames] = useAtom(updateSecretNamesAtom, projectScope);
|
||||
|
||||
return (
|
||||
<>
|
||||
<Typography gutterBottom>
|
||||
@@ -77,8 +80,9 @@ export const webhookStripe = {
|
||||
</Typography>
|
||||
|
||||
{webhookObject.auth.secretKey &&
|
||||
!secrets.loading &&
|
||||
!secrets.keys.includes(webhookObject.auth.secretKey) && (
|
||||
!secretNames.loading &&
|
||||
secretNames.secretNames &&
|
||||
!secretNames.secretNames.includes(webhookObject.auth.secretKey) && (
|
||||
<Alert severity="error" sx={{ height: "auto!important" }}>
|
||||
Your previously selected key{" "}
|
||||
<code>{webhookObject.auth.secretKey}</code> does not exist in
|
||||
@@ -86,34 +90,55 @@ export const webhookStripe = {
|
||||
</Alert>
|
||||
)}
|
||||
|
||||
<FormControl fullWidth margin={"normal"}>
|
||||
<InputLabel id="stripe-secret-key">Secret key</InputLabel>
|
||||
<Select
|
||||
labelId="stripe-secret-key"
|
||||
id="stripe-secret-key"
|
||||
label="Secret key"
|
||||
variant="filled"
|
||||
value={webhookObject.auth.secretKey}
|
||||
onChange={(e) => {
|
||||
setWebhookObject({
|
||||
...webhookObject,
|
||||
auth: { ...webhookObject.auth, secretKey: e.target.value },
|
||||
});
|
||||
}}
|
||||
>
|
||||
{secrets.keys.map((secret) => {
|
||||
return <MenuItem value={secret}>{secret}</MenuItem>;
|
||||
})}
|
||||
<MenuItem
|
||||
onClick={() => {
|
||||
const secretManagerLink = `https://console.cloud.google.com/security/secret-manager/create?project=${secrets.projectId}`;
|
||||
window?.open?.(secretManagerLink, "_blank")?.focus();
|
||||
<Box
|
||||
sx={{
|
||||
display: "flex",
|
||||
alignItems: "center",
|
||||
justifyContent: "space-between",
|
||||
marginY: 1,
|
||||
}}
|
||||
>
|
||||
<FormControl fullWidth>
|
||||
<InputLabel id="stripe-secret-key">Secret key</InputLabel>
|
||||
<Select
|
||||
labelId="stripe-secret-key"
|
||||
id="stripe-secret-key"
|
||||
label="Secret key"
|
||||
variant="filled"
|
||||
value={webhookObject.auth.secretKey}
|
||||
onChange={(e) => {
|
||||
setWebhookObject({
|
||||
...webhookObject,
|
||||
auth: { ...webhookObject.auth, secretKey: e.target.value },
|
||||
});
|
||||
}}
|
||||
>
|
||||
Add a key in Secret Manager
|
||||
</MenuItem>
|
||||
</Select>
|
||||
</FormControl>
|
||||
{secretNames.secretNames?.map((secret) => {
|
||||
return <MenuItem value={secret}>{secret}</MenuItem>;
|
||||
})}
|
||||
<MenuItem
|
||||
onClick={() => {
|
||||
const secretManagerLink = `https://console.cloud.google.com/security/secret-manager/create`;
|
||||
window?.open?.(secretManagerLink, "_blank")?.focus();
|
||||
}}
|
||||
>
|
||||
Add a key in Secret Manager
|
||||
</MenuItem>
|
||||
</Select>
|
||||
</FormControl>
|
||||
<LoadingButton
|
||||
sx={{
|
||||
height: "100%",
|
||||
marginLeft: 1,
|
||||
}}
|
||||
loading={secretNames.loading}
|
||||
onClick={() => {
|
||||
updateSecretNames?.();
|
||||
}}
|
||||
>
|
||||
Refresh
|
||||
</LoadingButton>
|
||||
</Box>
|
||||
<TextField
|
||||
id="stripe-signing-secret"
|
||||
label="Signing key"
|
||||
|
||||
@@ -1,10 +1,7 @@
|
||||
import { Typography, Link, TextField } from "@mui/material";
|
||||
import InlineOpenInNewIcon from "@src/components/InlineOpenInNewIcon";
|
||||
import { TableSettings } from "@src/types/table";
|
||||
import {
|
||||
IWebhook,
|
||||
ISecret,
|
||||
} from "@src/components/TableModals/WebhooksModal/utils";
|
||||
import { IWebhook } from "@src/components/TableModals/WebhooksModal/utils";
|
||||
|
||||
export const webhookTypeform = {
|
||||
name: "Typeform",
|
||||
@@ -83,11 +80,7 @@ export const webhookTypeform = {
|
||||
// WRITE YOUR CODE ONLY ABOVE THIS LINE. DO NOT WRITE CODE/COMMENTS OUTSIDE THE FUNCTION BODY
|
||||
}`,
|
||||
},
|
||||
auth: (
|
||||
webhookObject: IWebhook,
|
||||
setWebhookObject: (w: IWebhook) => void,
|
||||
secrets: ISecret
|
||||
) => {
|
||||
Auth: (webhookObject: IWebhook, setWebhookObject: (w: IWebhook) => void) => {
|
||||
return (
|
||||
<>
|
||||
<Typography gutterBottom>
|
||||
|
||||
@@ -1,10 +1,7 @@
|
||||
import { Typography, Link, TextField } from "@mui/material";
|
||||
import InlineOpenInNewIcon from "@src/components/InlineOpenInNewIcon";
|
||||
import { TableSettings } from "@src/types/table";
|
||||
import {
|
||||
IWebhook,
|
||||
ISecret,
|
||||
} from "@src/components/TableModals/WebhooksModal/utils";
|
||||
import { IWebhook } from "@src/components/TableModals/WebhooksModal/utils";
|
||||
|
||||
export const webhook = {
|
||||
name: "Web Form",
|
||||
@@ -51,11 +48,7 @@ export const webhook = {
|
||||
// WRITE YOUR CODE ONLY ABOVE THIS LINE. DO NOT WRITE CODE/COMMENTS OUTSIDE THE FUNCTION BODY
|
||||
}`,
|
||||
},
|
||||
auth: (
|
||||
webhookObject: IWebhook,
|
||||
setWebhookObject: (w: IWebhook) => void,
|
||||
secrets: ISecret
|
||||
) => {
|
||||
Auth: (webhookObject: IWebhook, setWebhookObject: (w: IWebhook) => void) => {
|
||||
return (
|
||||
<>
|
||||
<Typography gutterBottom>
|
||||
|
||||
@@ -1,41 +1,13 @@
|
||||
import { useAtom } from "jotai";
|
||||
import { useEffect, useState } from "react";
|
||||
import { IWebhookModalStepProps } from "./WebhookModal";
|
||||
|
||||
import { FormControlLabel, Checkbox, Typography } from "@mui/material";
|
||||
|
||||
import {
|
||||
projectIdAtom,
|
||||
projectScope,
|
||||
rowyRunAtom,
|
||||
} from "@src/atoms/projectScope";
|
||||
import { runRoutes } from "@src/constants/runRoutes";
|
||||
import { webhookSchemas, ISecret } from "./utils";
|
||||
import { webhookSchemas } from "./utils";
|
||||
|
||||
export default function Step1Endpoint({
|
||||
webhookObject,
|
||||
setWebhookObject,
|
||||
}: IWebhookModalStepProps) {
|
||||
const [rowyRun] = useAtom(rowyRunAtom, projectScope);
|
||||
const [projectId] = useAtom(projectIdAtom, projectScope);
|
||||
const [secrets, setSecrets] = useState<ISecret>({
|
||||
loading: true,
|
||||
keys: [],
|
||||
projectId,
|
||||
});
|
||||
|
||||
useEffect(() => {
|
||||
rowyRun({
|
||||
route: runRoutes.listSecrets,
|
||||
}).then((secrets) => {
|
||||
setSecrets({
|
||||
loading: false,
|
||||
keys: secrets as string[],
|
||||
projectId,
|
||||
});
|
||||
});
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<>
|
||||
<Typography variant="inherit" paragraph>
|
||||
@@ -63,10 +35,9 @@ export default function Step1Endpoint({
|
||||
/>
|
||||
|
||||
{webhookObject.auth?.enabled &&
|
||||
webhookSchemas[webhookObject.type].auth(
|
||||
webhookSchemas[webhookObject.type].Auth(
|
||||
webhookObject,
|
||||
setWebhookObject,
|
||||
secrets
|
||||
setWebhookObject
|
||||
)}
|
||||
{}
|
||||
</>
|
||||
|
||||
@@ -119,12 +119,6 @@ export interface IWebhook {
|
||||
auth?: any;
|
||||
}
|
||||
|
||||
export interface ISecret {
|
||||
loading: boolean;
|
||||
keys: string[];
|
||||
projectId: string;
|
||||
}
|
||||
|
||||
export const webhookSchemas = {
|
||||
basic,
|
||||
typeform,
|
||||
|
||||
@@ -12,16 +12,21 @@ export interface ITableNameProps extends IShortTextComponentProps {
|
||||
|
||||
export default function TableName({ watchedField, ...props }: ITableNameProps) {
|
||||
const {
|
||||
field: { onChange },
|
||||
field: { onChange, value },
|
||||
useFormMethods: { control },
|
||||
disabled,
|
||||
} = props;
|
||||
|
||||
const watchedValue = useWatch({ control, name: watchedField } as any);
|
||||
useEffect(() => {
|
||||
if (!disabled && typeof watchedValue === "string" && !!watchedValue)
|
||||
onChange(startCase(watchedValue));
|
||||
}, [watchedValue, disabled]);
|
||||
if (!disabled) {
|
||||
if (typeof value === "string" && value.trim() !== "") {
|
||||
onChange(value);
|
||||
} else if (typeof watchedValue === "string" && !!watchedValue) {
|
||||
onChange(startCase(watchedValue));
|
||||
}
|
||||
}
|
||||
}, [watchedValue, disabled, onChange, value]);
|
||||
|
||||
return <ShortTextComponent {...props} />;
|
||||
}
|
||||
|
||||
@@ -1,67 +1,69 @@
|
||||
export const RUN_ACTION_TEMPLATE = `const action:Action = async ({row,ref,db,storage,auth,actionParams,user,logging}) => {
|
||||
// WRITE YOUR CODE ONLY BELOW THIS LINE. DO NOT WRITE CODE/COMMENTS OUTSIDE THE FUNCTION BODY
|
||||
logging.log("action started")
|
||||
|
||||
// Import any NPM package needed
|
||||
// const lodash = require('lodash');
|
||||
|
||||
// Example:
|
||||
/*
|
||||
const authToken = await rowy.secrets.get("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',
|
||||
}
|
||||
}
|
||||
*/
|
||||
// WRITE YOUR CODE ONLY ABOVE THIS LINE. DO NOT WRITE CODE/COMMENTS OUTSIDE THE FUNCTION BODY
|
||||
}`;
|
||||
export const RUN_ACTION_TEMPLATE = `// Import any NPM package needed
|
||||
// import _ from "lodash";
|
||||
|
||||
const action: Action = async ({ row, ref, db, storage, auth, actionParams, user, logging }) => {
|
||||
logging.log("action started");
|
||||
|
||||
export const UNDO_ACTION_TEMPLATE = `const action : Action = async ({row,ref,db,storage,auth,actionParams,user,logging}) => {
|
||||
// WRITE YOUR CODE ONLY BELOW THIS LINE. DO NOT WRITE CODE/COMMENTS OUTSIDE THE FUNCTION BODY
|
||||
logging.log("action started")
|
||||
|
||||
// Import any NPM package needed
|
||||
// const lodash = require('lodash');
|
||||
|
||||
// Example:
|
||||
/*
|
||||
const authToken = await rowy.secrets.get("service")
|
||||
// Example:
|
||||
const authToken = await rowy.secrets.get("service");
|
||||
try {
|
||||
const resp = await fetch('https://example.com/api/v1/users/'+ref.id,{
|
||||
method: 'DELETE',
|
||||
const resp = await fetch("https://example.com/api/v1/users/" + ref.id, {
|
||||
method: "PUT",
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
'Authorization': authToken
|
||||
"Content-Type": "application/json",
|
||||
Authorization: authToken,
|
||||
},
|
||||
body: JSON.stringify(row)
|
||||
})
|
||||
body: JSON.stringify(row),
|
||||
});
|
||||
return {
|
||||
success: true,
|
||||
message: 'User deleted successfully on example service',
|
||||
status: null
|
||||
}
|
||||
message: "User updated successfully on example service",
|
||||
status: "upto date",
|
||||
};
|
||||
} catch (error) {
|
||||
return {
|
||||
success: false,
|
||||
message: 'User delete failed on example service',
|
||||
}
|
||||
message: "User update failed on example service",
|
||||
};
|
||||
}
|
||||
*/
|
||||
// WRITE YOUR CODE ONLY ABOVE THIS LINE. DO NOT WRITE CODE/COMMENTS OUTSIDE THE FUNCTION BODY
|
||||
}`;
|
||||
};
|
||||
|
||||
export default action;
|
||||
`;
|
||||
|
||||
export const UNDO_ACTION_TEMPLATE = `// Import any NPM package needed
|
||||
// import _ from "lodash";
|
||||
|
||||
const action: Action = async ({ row, ref, db, storage, auth, actionParams, user, logging }) => {
|
||||
logging.log("action started");
|
||||
|
||||
/*
|
||||
// Example:
|
||||
const authToken = await rowy.secrets.get("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",
|
||||
};
|
||||
}
|
||||
*/
|
||||
};
|
||||
|
||||
export default action;
|
||||
`;
|
||||
|
||||
@@ -108,7 +108,7 @@ export default function PopupContents({
|
||||
onChange={(e) => setQuery(e.target.value)}
|
||||
fullWidth
|
||||
variant="filled"
|
||||
label="Search items"
|
||||
// label="Search items"
|
||||
hiddenLabel
|
||||
placeholder="Search items"
|
||||
InputProps={{
|
||||
|
||||
@@ -11,16 +11,19 @@ export const replacer = (data: any) => (m: string, key: string) => {
|
||||
return get(data, objKey, defaultValue);
|
||||
};
|
||||
|
||||
export const baseFunction = `const connectorFn: Connector = async ({query, row, user, logging}) => {
|
||||
// WRITE YOUR CODE ONLY BELOW THIS LINE. DO NOT WRITE CODE/COMMENTS OUTSIDE THE FUNCTION BODY
|
||||
logging.log("connectorFn started")
|
||||
|
||||
// Import any NPM package needed
|
||||
// const lodash = require('lodash');
|
||||
|
||||
return [];
|
||||
// WRITE YOUR CODE ONLY ABOVE THIS LINE. DO NOT WRITE CODE/COMMENTS OUTSIDE THE FUNCTION BODY
|
||||
};`;
|
||||
export const baseFunction = `// Import any NPM package needed
|
||||
// import _ from "lodash";
|
||||
|
||||
const connector: Connector = async ({ query, row, user, logging }) => {
|
||||
logging.log("connector started");
|
||||
// return [
|
||||
// { id: "a", name: "Apple" },
|
||||
// { id: "b", name: "Banana" },
|
||||
// ];
|
||||
};
|
||||
|
||||
export default connector;
|
||||
`;
|
||||
|
||||
export const getLabel = (config: any, row: TableRow) => {
|
||||
if (!config.labelFormatter) {
|
||||
|
||||
@@ -75,18 +75,19 @@ export default function Settings({
|
||||
${config.script.replace(/utilFns.getSecret/g, "rowy.secrets.get")}
|
||||
// WRITE YOUR CODE ONLY ABOVE THIS LINE. DO NOT WRITE CODE/COMMENTS OUTSIDE THE FUNCTION BODY
|
||||
}`
|
||||
: `const derivative:Derivative = async ({row,ref,db,storage,auth,logging})=>{
|
||||
// WRITE YOUR CODE ONLY BELOW THIS LINE. DO NOT WRITE CODE/COMMENTS OUTSIDE THE FUNCTION BODY
|
||||
logging.log("derivative started")
|
||||
|
||||
// Import any NPM package needed
|
||||
// const lodash = require('lodash');
|
||||
|
||||
: `// Import any NPM package needed
|
||||
// import _ from "lodash";
|
||||
|
||||
const derivative: Derivative = async ({ row, ref, db, storage, auth, logging }) => {
|
||||
logging.log("derivative started");
|
||||
|
||||
// Example:
|
||||
// const sum = row.a + row.b;
|
||||
// return sum;
|
||||
// WRITE YOUR CODE ONLY ABOVE THIS LINE. DO NOT WRITE CODE/COMMENTS OUTSIDE THE FUNCTION BODY
|
||||
}`;
|
||||
};
|
||||
|
||||
export default derivative;
|
||||
`;
|
||||
|
||||
return (
|
||||
<>
|
||||
|
||||
@@ -22,6 +22,10 @@ import {
|
||||
AdditionalTableSettings,
|
||||
MinimumTableSettings,
|
||||
currentUserAtom,
|
||||
updateSecretNamesAtom,
|
||||
projectIdAtom,
|
||||
rowyRunAtom,
|
||||
secretNamesAtom,
|
||||
} from "@src/atoms/projectScope";
|
||||
import { firebaseDbAtom } from "./init";
|
||||
|
||||
@@ -34,10 +38,15 @@ import { rowyUser } from "@src/utils/table";
|
||||
import { TableSettings, TableSchema, SubTablesSchema } from "@src/types/table";
|
||||
import { FieldType } from "@src/constants/fields";
|
||||
import { getFieldProp } from "@src/components/fields";
|
||||
import { runRoutes } from "@src/constants/runRoutes";
|
||||
|
||||
export function useTableFunctions() {
|
||||
const [firebaseDb] = useAtom(firebaseDbAtom, projectScope);
|
||||
const [currentUser] = useAtom(currentUserAtom, projectScope);
|
||||
const [projectId] = useAtom(projectIdAtom, projectScope);
|
||||
const [rowyRun] = useAtom(rowyRunAtom, projectScope);
|
||||
const [secretNames, setSecretNames] = useAtom(secretNamesAtom, projectScope);
|
||||
const [updateSecretNames] = useAtom(updateSecretNamesAtom, projectScope);
|
||||
|
||||
// Create a function to get the latest tables from project settings,
|
||||
// so we don’t create new functions when tables change
|
||||
@@ -330,4 +339,36 @@ export function useTableFunctions() {
|
||||
return tableSchema as TableSchema;
|
||||
});
|
||||
}, [firebaseDb, readTables, setGetTableSchema]);
|
||||
|
||||
// Set the deleteTable function
|
||||
const setUpdateSecretNames = useSetAtom(updateSecretNamesAtom, projectScope);
|
||||
useEffect(() => {
|
||||
if (!projectId || !rowyRun || !secretNamesAtom) return;
|
||||
setUpdateSecretNames(() => async (clearSecretNames?: boolean) => {
|
||||
setSecretNames({
|
||||
loading: true,
|
||||
secretNames: clearSecretNames ? null : secretNames.secretNames,
|
||||
});
|
||||
rowyRun({
|
||||
route: runRoutes.listSecrets,
|
||||
})
|
||||
.then((secrets: string[]) => {
|
||||
setSecretNames({
|
||||
loading: false,
|
||||
secretNames: secrets,
|
||||
});
|
||||
})
|
||||
.catch((e) => {
|
||||
setSecretNames({
|
||||
loading: false,
|
||||
secretNames: clearSecretNames ? null : secretNames.secretNames,
|
||||
});
|
||||
});
|
||||
});
|
||||
}, [projectId, rowyRun, setUpdateSecretNames]);
|
||||
useEffect(() => {
|
||||
if (updateSecretNames) {
|
||||
updateSecretNames(true);
|
||||
}
|
||||
}, [updateSecretNames]);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user