mirror of
https://github.com/rowyio/rowy.git
synced 2025-12-29 00:16:39 +01:00
Feat: Runtime options (#898)
* feat(runtime-options): add runtime options, update rowy run github link * Update src/components/TableModals/ExtensionsModal/ExtensionList.tsx * Update src/components/TableModals/ExtensionsModal/RuntimeOptions.tsx
This commit is contained in:
@@ -23,7 +23,6 @@ import {
|
||||
} from "@src/atoms/tableScope";
|
||||
import { useSnackLogContext } from "@src/contexts/SnackLogContext";
|
||||
|
||||
import { emptyExtensionObject, IExtension, ExtensionType } from "./utils";
|
||||
import { runRoutes } from "@src/constants/runRoutes";
|
||||
import { analytics, logEvent } from "@src/analytics";
|
||||
import {
|
||||
@@ -31,6 +30,14 @@ import {
|
||||
getTableBuildFunctionPathname,
|
||||
} from "@src/utils/table";
|
||||
|
||||
import {
|
||||
emptyExtensionObject,
|
||||
IExtension,
|
||||
ExtensionType,
|
||||
IRuntimeOptions,
|
||||
} from "./utils";
|
||||
import RuntimeOptions from "./RuntimeOptions";
|
||||
|
||||
export default function ExtensionsModal({ onClose }: ITableModalProps) {
|
||||
const [currentUser] = useAtom(currentUserAtom, projectScope);
|
||||
const [rowyRun] = useAtom(rowyRunAtom, projectScope);
|
||||
@@ -39,12 +46,24 @@ export default function ExtensionsModal({ onClose }: ITableModalProps) {
|
||||
const [tableSchema] = useAtom(tableSchemaAtom, tableScope);
|
||||
const [updateTableSchema] = useAtom(updateTableSchemaAtom, tableScope);
|
||||
|
||||
const currentExtensionObjects = (tableSchema.extensionObjects ??
|
||||
[]) as IExtension[];
|
||||
const [localExtensionsObjects, setLocalExtensionsObjects] = useState(
|
||||
currentExtensionObjects
|
||||
tableSchema.extensionObjects ?? []
|
||||
);
|
||||
|
||||
const [localRuntimeOptions, setLocalRuntimeOptions] = useState(
|
||||
tableSchema.runtimeOptions ?? {}
|
||||
);
|
||||
|
||||
const errors = {
|
||||
runtimeOptions: {
|
||||
timeoutSeconds: !(
|
||||
!!localRuntimeOptions.timeoutSeconds &&
|
||||
localRuntimeOptions.timeoutSeconds > 0 &&
|
||||
localRuntimeOptions.timeoutSeconds <= 540
|
||||
),
|
||||
},
|
||||
};
|
||||
|
||||
const [openMigrationGuide, setOpenMigrationGuide] = useState(false);
|
||||
useEffect(() => {
|
||||
if (tableSchema.sparks) setOpenMigrationGuide(true);
|
||||
@@ -57,7 +76,9 @@ export default function ExtensionsModal({ onClose }: ITableModalProps) {
|
||||
} | null>(null);
|
||||
|
||||
const snackLogContext = useSnackLogContext();
|
||||
const edited = !isEqual(currentExtensionObjects, localExtensionsObjects);
|
||||
const edited =
|
||||
!isEqual(tableSchema.extensionObjects ?? [], localExtensionsObjects) ||
|
||||
!isEqual(tableSchema.runtimeOptions ?? {}, localRuntimeOptions);
|
||||
|
||||
const handleClose = (
|
||||
_setOpen: React.Dispatch<React.SetStateAction<boolean>>
|
||||
@@ -70,7 +91,8 @@ export default function ExtensionsModal({ onClose }: ITableModalProps) {
|
||||
cancel: "Keep",
|
||||
handleConfirm: () => {
|
||||
_setOpen(false);
|
||||
setLocalExtensionsObjects(currentExtensionObjects);
|
||||
setLocalExtensionsObjects(tableSchema.extensionObjects ?? []);
|
||||
setLocalRuntimeOptions(tableSchema.runtimeOptions ?? {});
|
||||
onClose();
|
||||
},
|
||||
});
|
||||
@@ -79,15 +101,18 @@ export default function ExtensionsModal({ onClose }: ITableModalProps) {
|
||||
}
|
||||
};
|
||||
|
||||
const handleSaveExtensions = async (callback?: Function) => {
|
||||
const handleSave = async (callback?: Function) => {
|
||||
if (updateTableSchema)
|
||||
await updateTableSchema({ extensionObjects: localExtensionsObjects });
|
||||
await updateTableSchema({
|
||||
extensionObjects: localExtensionsObjects,
|
||||
runtimeOptions: localRuntimeOptions,
|
||||
});
|
||||
if (callback) callback();
|
||||
onClose();
|
||||
};
|
||||
|
||||
const handleSaveDeploy = async () => {
|
||||
handleSaveExtensions(() => {
|
||||
handleSave(() => {
|
||||
try {
|
||||
snackLogContext.requestSnackLog();
|
||||
rowyRun({
|
||||
@@ -132,6 +157,13 @@ export default function ExtensionsModal({ onClose }: ITableModalProps) {
|
||||
setExtensionModal(null);
|
||||
};
|
||||
|
||||
const handleUpdateRuntimeOptions = (update: IRuntimeOptions) => {
|
||||
setLocalRuntimeOptions((runtimeOptions) => ({
|
||||
...runtimeOptions,
|
||||
...update,
|
||||
}));
|
||||
};
|
||||
|
||||
const handleUpdateActive = (index: number, active: boolean) => {
|
||||
setLocalExtensionsObjects(
|
||||
localExtensionsObjects.map((extensionObject, i) => {
|
||||
@@ -217,24 +249,31 @@ export default function ExtensionsModal({ onClose }: ITableModalProps) {
|
||||
/>
|
||||
}
|
||||
children={
|
||||
<ExtensionList
|
||||
extensions={localExtensionsObjects}
|
||||
handleUpdateActive={handleUpdateActive}
|
||||
handleEdit={handleEdit}
|
||||
handleDuplicate={handleDuplicate}
|
||||
handleDelete={handleDelete}
|
||||
/>
|
||||
<>
|
||||
<ExtensionList
|
||||
extensions={localExtensionsObjects}
|
||||
handleUpdateActive={handleUpdateActive}
|
||||
handleEdit={handleEdit}
|
||||
handleDuplicate={handleDuplicate}
|
||||
handleDelete={handleDelete}
|
||||
/>
|
||||
<RuntimeOptions
|
||||
runtimeOptions={localRuntimeOptions}
|
||||
handleUpdate={handleUpdateRuntimeOptions}
|
||||
errors={errors.runtimeOptions}
|
||||
/>
|
||||
</>
|
||||
}
|
||||
actions={{
|
||||
primary: {
|
||||
children: "Save & Deploy",
|
||||
onClick: handleSaveDeploy,
|
||||
disabled: !edited,
|
||||
disabled: !edited || errors.runtimeOptions.timeoutSeconds,
|
||||
},
|
||||
secondary: {
|
||||
children: "Save",
|
||||
onClick: () => handleSaveExtensions(),
|
||||
disabled: !edited,
|
||||
onClick: () => handleSave(),
|
||||
disabled: !edited || errors.runtimeOptions.timeoutSeconds,
|
||||
},
|
||||
}}
|
||||
/>
|
||||
|
||||
117
src/components/TableModals/ExtensionsModal/RuntimeOptions.tsx
Normal file
117
src/components/TableModals/ExtensionsModal/RuntimeOptions.tsx
Normal file
@@ -0,0 +1,117 @@
|
||||
import { useState } from "react";
|
||||
import { useAtom, useSetAtom } from "jotai";
|
||||
|
||||
import {
|
||||
Accordion,
|
||||
AccordionDetails,
|
||||
AccordionSummary,
|
||||
Button,
|
||||
Grid,
|
||||
InputAdornment,
|
||||
TextField,
|
||||
Typography,
|
||||
} from "@mui/material";
|
||||
import { ChevronDown } from "@src/assets/icons";
|
||||
import MultiSelect from "@rowy/multiselect";
|
||||
|
||||
import {
|
||||
compatibleRowyRunVersionAtom,
|
||||
projectScope,
|
||||
rowyRunModalAtom,
|
||||
} from "@src/atoms/projectScope";
|
||||
|
||||
import { IRuntimeOptions } from "./utils";
|
||||
|
||||
export default function RuntimeOptions({
|
||||
runtimeOptions,
|
||||
handleUpdate,
|
||||
errors,
|
||||
}: {
|
||||
runtimeOptions: IRuntimeOptions;
|
||||
handleUpdate: (runtimeOptions: IRuntimeOptions) => void;
|
||||
errors: { timeoutSeconds: boolean };
|
||||
}) {
|
||||
const [compatibleRowyRunVersion] = useAtom(
|
||||
compatibleRowyRunVersionAtom,
|
||||
projectScope
|
||||
);
|
||||
const openRowyRunModal = useSetAtom(rowyRunModalAtom, projectScope);
|
||||
|
||||
const [expanded, setExpanded] = useState(false);
|
||||
|
||||
const isCompatibleRowyRun = compatibleRowyRunVersion({ minVersion: "1.6.4" });
|
||||
|
||||
return (
|
||||
<Accordion
|
||||
sx={{
|
||||
padding: 0,
|
||||
boxShadow: "none",
|
||||
backgroundImage: "inherit",
|
||||
backgroundColor: "inherit",
|
||||
}}
|
||||
expanded={isCompatibleRowyRun && expanded}
|
||||
>
|
||||
<AccordionSummary
|
||||
sx={{ padding: 0 }}
|
||||
expandIcon={
|
||||
isCompatibleRowyRun ? (
|
||||
<ChevronDown />
|
||||
) : (
|
||||
<Button>Update Rowy Run</Button>
|
||||
)
|
||||
}
|
||||
onClick={() =>
|
||||
isCompatibleRowyRun
|
||||
? setExpanded(!expanded)
|
||||
: openRowyRunModal({
|
||||
version: "1.6.4",
|
||||
feature: "Runtime options",
|
||||
})
|
||||
}
|
||||
>
|
||||
<Typography variant="subtitle1">Runtime options</Typography>
|
||||
</AccordionSummary>
|
||||
<AccordionDetails sx={{ padding: 0 }}>
|
||||
<Grid container spacing={2}>
|
||||
<Grid item xs={12} md={6}>
|
||||
<MultiSelect
|
||||
label="Memory Allocated"
|
||||
value={runtimeOptions.memory ?? "256MB"}
|
||||
onChange={(value) => handleUpdate({ memory: value ?? "256MB" })}
|
||||
multiple={false}
|
||||
options={["128MB", "256MB", "512MB", "1GB", "2GB", "4GB", "8GB"]}
|
||||
/>
|
||||
</Grid>
|
||||
|
||||
<Grid item xs={12} md={6}>
|
||||
<TextField
|
||||
value={runtimeOptions.timeoutSeconds ?? 60}
|
||||
label="Timeout"
|
||||
fullWidth
|
||||
InputProps={{
|
||||
endAdornment: (
|
||||
<InputAdornment position="end">seconds</InputAdornment>
|
||||
),
|
||||
}}
|
||||
onChange={(e) =>
|
||||
!isNaN(Number(e.target.value)) &&
|
||||
handleUpdate({
|
||||
timeoutSeconds: Number(e.target.value),
|
||||
})
|
||||
}
|
||||
inputProps={{
|
||||
inputMode: "numeric",
|
||||
}}
|
||||
error={errors.timeoutSeconds}
|
||||
helperText={
|
||||
errors.timeoutSeconds
|
||||
? "Timeout must be an integer between 1 and 540"
|
||||
: "The maximum timeout that can be specified is 9 mins (540 seconds)"
|
||||
}
|
||||
/>
|
||||
</Grid>
|
||||
</Grid>
|
||||
</AccordionDetails>
|
||||
</Accordion>
|
||||
);
|
||||
}
|
||||
@@ -52,6 +52,12 @@ export interface IExtension {
|
||||
trackedFields?: string[];
|
||||
}
|
||||
|
||||
// https://firebase.google.com/docs/functions/manage-functions#set_runtime_options
|
||||
export interface IRuntimeOptions {
|
||||
memory?: "128MB" | "256MB" | "512MB" | "1GB" | "2GB" | "4GB" | "8GB";
|
||||
timeoutSeconds?: number;
|
||||
}
|
||||
|
||||
export const triggerTypes: ExtensionTrigger[] = ["create", "update", "delete"];
|
||||
|
||||
const extensionBodyTemplate = {
|
||||
|
||||
@@ -15,8 +15,8 @@ export const EXTERNAL_LINKS = {
|
||||
twitter: "https://twitter.com/rowyio",
|
||||
productHunt: "https://www.producthunt.com/products/rowy-2",
|
||||
|
||||
rowyRun: meta.repository.url.replace(".git", "Run"),
|
||||
rowyRunGitHub: meta.repository.url.replace(".git", "Run"),
|
||||
rowyRun: meta.repository.url.replace("rowy.git", "backend"),
|
||||
rowyRunGitHub: meta.repository.url.replace("rowy.git", "backend"),
|
||||
// prettier-ignore
|
||||
rowyRunDeploy: `https://deploy.cloud.run/?git_repo=${meta.repository.url.replace(".git", "Run")}.git`,
|
||||
|
||||
|
||||
6
src/types/table.d.ts
vendored
6
src/types/table.d.ts
vendored
@@ -4,7 +4,10 @@ import type {
|
||||
DocumentData,
|
||||
DocumentReference,
|
||||
} from "firebase/firestore";
|
||||
import { IExtension } from "@src/components/TableModals/ExtensionsModal/utils";
|
||||
import {
|
||||
IExtension,
|
||||
IRuntimeOptions,
|
||||
} from "@src/components/TableModals/ExtensionsModal/utils";
|
||||
import { IWebhook } from "@src/components/TableModals/WebhooksModal/utils";
|
||||
|
||||
/**
|
||||
@@ -104,6 +107,7 @@ export type TableSchema = {
|
||||
extensionObjects?: IExtension[];
|
||||
compiledExtension?: string;
|
||||
webhooks?: IWebhook[];
|
||||
runtimeOptions?: IRuntimeOptions;
|
||||
|
||||
/** @deprecated Migrate to Extensions */
|
||||
sparks?: string;
|
||||
|
||||
Reference in New Issue
Block a user