Skip to content

Commit f558311

Browse files
authored
feat: load data before saving resource variable (#5392)
Ref #3691 - new resource loader with queue to avoid bloating network with huge responses - data can be loaded for new variable before saving, easier to preview result https://github.com/user-attachments/assets/5c26c5a2-fd33-401b-a4d2-a321c34ca083
1 parent 91588c9 commit f558311

24 files changed

+927
-744
lines changed

apps/builder/app/builder/features/pages/page-utils.test.ts

Lines changed: 22 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import {
77
ROOT_FOLDER_ID,
88
type Page,
99
SYSTEM_VARIABLE_ID,
10+
Resource,
1011
} from "@webstudio-is/sdk";
1112
import {
1213
cleanupChildRefsMutable,
@@ -23,11 +24,12 @@ import {
2324
$dataSourceVariables,
2425
$dataSources,
2526
$pages,
26-
$resourceValues,
27+
$resources,
2728
} from "~/shared/nano-states";
2829
import { registerContainers } from "~/shared/sync";
2930
import { $awareness } from "~/shared/awareness";
3031
import { updateCurrentSystem } from "~/shared/system";
32+
import { $resourcesCache, getResourceKey } from "~/shared/resources";
3133

3234
setEnv("*");
3335
registerContainers();
@@ -521,7 +523,25 @@ test("page root scope should use variable and resource values", () => {
521523
$dataSourceVariables.set(
522524
new Map([["valueVariableId", "value variable value"]])
523525
);
524-
$resourceValues.set(new Map([["resourceId", "resource variable value"]]));
526+
const resourceKey = getResourceKey({
527+
name: "my-resource",
528+
url: "",
529+
searchParams: [],
530+
method: "get",
531+
headers: [],
532+
});
533+
$resources.set(
534+
toMap<Resource>([
535+
{
536+
id: "resourceId",
537+
name: "my-resource",
538+
url: `""`,
539+
method: "get",
540+
headers: [],
541+
},
542+
])
543+
);
544+
$resourcesCache.set(new Map([[resourceKey, "resource variable value"]]));
525545
expect($pageRootScope.get()).toEqual({
526546
aliases: new Map([
527547
["$ws$system", "system"],

apps/builder/app/builder/features/settings-panel/resource-panel.tsx

Lines changed: 40 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -75,7 +75,7 @@ export const parseResource = ({
7575
formData,
7676
}: {
7777
id: string;
78-
name: string;
78+
name?: string;
7979
formData: FormData;
8080
}) => {
8181
const searchParamNames = formData.getAll("search-param-name") as string[];
@@ -84,7 +84,7 @@ export const parseResource = ({
8484
const headerValues = formData.getAll("header-value") as string[];
8585
return Resource.parse({
8686
id,
87-
name,
87+
name: name ?? formData.get("name"),
8888
url: formData.get("url"),
8989
searchParams: searchParamNames
9090
.map((name, index) => ({ name, value: searchParamValues[index] }))
@@ -175,17 +175,21 @@ export const UrlField = ({
175175
}
176176
try {
177177
const url = new URL(value);
178-
const searchParams: Resource["searchParams"] = [];
179-
for (const [name, value] of url.searchParams) {
180-
searchParams.push({ name, value: JSON.stringify(value) });
178+
if (url.searchParams.size > 0) {
179+
const searchParams: Resource["searchParams"] = [];
180+
for (const [name, value] of url.searchParams) {
181+
searchParams.push({ name, value: JSON.stringify(value) });
182+
}
183+
// remove all search params from url
184+
url.search = "";
185+
// update text value as string literal
186+
onChange(JSON.stringify(url.href), searchParams);
187+
return;
181188
}
182-
// remove all search params from url
183-
url.search = "";
184-
// update text value as string literal
185-
onChange(JSON.stringify(url.href), searchParams);
186189
} catch {
187-
onChange(JSON.stringify(value));
190+
// serialize without changes when url is invalid
188191
}
192+
onChange(JSON.stringify(value));
189193
}}
190194
onBlur={(event) => event.currentTarget.checkValidity()}
191195
onInvalid={(event) =>
@@ -515,9 +519,9 @@ export const getResourceScopeForInstance = ({
515519
}
516520
if (dataSource) {
517521
const name = encodeDataVariableId(dataSourceId);
518-
variableValues.set(dataSourceId, value);
519522
scope[name] = value;
520523
aliases.set(name, dataSource.name);
524+
variableValues.set(dataSource.name, value);
521525
}
522526
}
523527
}
@@ -544,7 +548,7 @@ const getVariableInstanceKey = ({
544548
return getInstanceKey(instancePath[0].instanceSelector);
545549
};
546550

547-
const useScope = ({ variable }: { variable?: DataSource }) => {
551+
export const useResourceScope = ({ variable }: { variable?: DataSource }) => {
548552
return useStore(
549553
useMemo(
550554
() =>
@@ -561,22 +565,32 @@ const useScope = ({ variable }: { variable?: DataSource }) => {
561565
variableValuesByInstanceSelector,
562566
dataSources
563567
) => {
564-
const { scope, aliases } = getResourceScopeForInstance({
565-
page,
566-
instanceKey: getVariableInstanceKey({
567-
variable,
568-
instancePath,
569-
}),
570-
dataSources,
571-
variableValuesByInstanceSelector,
572-
});
568+
const { scope, aliases, variableValues } =
569+
getResourceScopeForInstance({
570+
page,
571+
instanceKey: getVariableInstanceKey({
572+
variable,
573+
instancePath,
574+
}),
575+
dataSources,
576+
variableValuesByInstanceSelector,
577+
});
573578
// prevent showing currently edited variable in suggestions
574579
// to avoid cirular dependeny
575580
const newScope = { ...scope };
581+
const newAliases = new Map(aliases);
582+
const newVariableValues = new Map(variableValues);
576583
if (variable) {
577-
delete newScope[encodeDataVariableId(variable.id)];
584+
const key = encodeDataVariableId(variable.id);
585+
delete newScope[key];
586+
newAliases.delete(key);
587+
newVariableValues.delete(variable.name);
578588
}
579-
return { scope: newScope, aliases };
589+
return {
590+
scope: newScope,
591+
aliases: newAliases,
592+
variableValues: newVariableValues,
593+
};
580594
}
581595
),
582596
[variable]
@@ -706,7 +720,7 @@ export const ResourceForm = forwardRef<
706720
undefined | PanelApi,
707721
{ variable?: DataSource }
708722
>(({ variable }, ref) => {
709-
const { scope, aliases } = useScope({ variable });
723+
const { scope, aliases } = useResourceScope({ variable });
710724

711725
const resources = useStore($resources);
712726
const resource =
@@ -739,16 +753,14 @@ export const ResourceForm = forwardRef<
739753
if (scopeInstanceId === undefined) {
740754
return;
741755
}
742-
const name = z.string().parse(formData.get("name"));
743756
const newResource = parseResource({
744757
id: resource?.id ?? nanoid(),
745-
name,
746758
formData,
747759
});
748760
const newVariable: DataSource = {
749761
id: variable?.id ?? nanoid(),
750762
scopeInstanceId,
751-
name,
763+
name: newResource.name,
752764
type: "resource",
753765
resourceId: newResource.id,
754766
};
@@ -942,7 +954,7 @@ export const GraphqlResourceForm = forwardRef<
942954
undefined | PanelApi,
943955
{ variable?: DataSource }
944956
>(({ variable }, ref) => {
945-
const { scope, aliases } = useScope({ variable });
957+
const { scope, aliases } = useResourceScope({ variable });
946958

947959
const resources = useStore($resources);
948960
const resource =

0 commit comments

Comments
 (0)