Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
91 changes: 49 additions & 42 deletions apps/builder/app/builder/features/settings-panel/resource-panel.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -66,18 +66,17 @@ import {
type InstancePath,
} from "~/shared/awareness";
import { updateWebstudioData } from "~/shared/instance-utils";
import {
computeExpression,
rebindTreeVariablesMutable,
} from "~/shared/data-variables";
import { rebindTreeVariablesMutable } from "~/shared/data-variables";
import { parseCurl, type CurlRequest } from "./curl";

export const parseResource = ({
id,
control,
name,
formData,
}: {
id: string;
control?: string;
name?: string;
formData: FormData;
}) => {
Expand All @@ -87,6 +86,7 @@ export const parseResource = ({
const headerValues = formData.getAll("header-value") as string[];
return Resource.parse({
id,
control,
name: name ?? formData.get("name"),
url: formData.get("url"),
searchParams: searchParamNames
Expand Down Expand Up @@ -253,7 +253,7 @@ const SearchParamPair = ({
}) => {
const evaluatedValue = evaluateExpressionWithinScope(value, scope);
// expressions with variables or objects cannot be edited from input
const isValueUnbound =
const isValueUnboundString =
isLiteralExpression(value) && typeof evaluatedValue === "string";
return (
<Grid
Expand All @@ -274,7 +274,7 @@ const SearchParamPair = ({
<InputField
placeholder="Value"
name="search-param-value-literal"
disabled={!isValueUnbound}
disabled={!isValueUnboundString}
value={serializeValue(evaluatedValue)}
// update text value as string literal
onChange={(event) =>
Expand All @@ -284,7 +284,7 @@ const SearchParamPair = ({
<BindingPopover
scope={scope}
aliases={aliases}
variant={isValueUnbound ? "default" : "bound"}
variant={isLiteralExpression(value) ? "default" : "bound"}
value={value}
onChange={(newValue) => onChange(name, newValue)}
onRemove={(evaluatedValue) =>
Expand Down Expand Up @@ -377,7 +377,7 @@ const HeaderPair = ({
}) => {
const evaluatedValue = evaluateExpressionWithinScope(value, scope);
// expressions with variables or objects cannot be edited from input
const isValueUnbound =
const isValueUnboundString =
isLiteralExpression(value) && typeof evaluatedValue === "string";
return (
<Grid
Expand All @@ -398,7 +398,7 @@ const HeaderPair = ({
<InputField
placeholder="Value"
name="header-value-validator"
disabled={!isValueUnbound}
disabled={!isValueUnboundString}
value={serializeValue(evaluatedValue)}
// update text value as string literal
onChange={(event) =>
Expand All @@ -408,7 +408,7 @@ const HeaderPair = ({
<BindingPopover
scope={scope}
aliases={aliases}
variant={isValueUnbound ? "default" : "bound"}
variant={isLiteralExpression(value) ? "default" : "bound"}
value={value}
onChange={(newValue) => onChange(name, newValue)}
onRemove={(evaluatedValue) =>
Expand Down Expand Up @@ -558,7 +558,7 @@ export const getResourceScopeForInstance = ({
const name = encodeDataVariableId(dataSourceId);
scope[name] = value;
aliases.set(name, dataSource.name);
variableValues.set(dataSource.name, value);
variableValues.set(dataSourceId, value);
}
}
}
Expand Down Expand Up @@ -621,7 +621,7 @@ export const useResourceScope = ({ variable }: { variable?: DataSource }) => {
const key = encodeDataVariableId(variable.id);
delete newScope[key];
newAliases.delete(key);
newVariableValues.delete(variable.name);
newVariableValues.delete(variable.id);
}
return {
scope: newScope,
Expand Down Expand Up @@ -796,7 +796,10 @@ const parseHeaders = (headers: Resource["headers"]) => {
let maxAge: undefined | string;
let bodyType: BodyType;
const newHeaders = headers.filter((header) => {
const value = computeExpression(header.value, new Map()).toLowerCase();
// cast raw expression result to string
const value = String(
evaluateExpressionWithinScope(header.value, {})
).toLowerCase();
if (isCacheControl(header.name)) {
// move simple header like Cache-Control: max-age=10 to dedicated input
// preserve more complex cache-control
Expand All @@ -818,7 +821,7 @@ const parseHeaders = (headers: Resource["headers"]) => {
return false;
}
}
return false;
return true;
});
return { headers: newHeaders, maxAge, bodyType };
};
Expand Down Expand Up @@ -980,8 +983,6 @@ export const SystemResourceForm = forwardRef<
? resources.get(variable.resourceId)
: undefined;

const method = "get";

const localResources = [
{
label: "Sitemap",
Expand All @@ -1006,19 +1007,15 @@ export const SystemResourceForm = forwardRef<
if (scopeInstanceId === undefined) {
return;
}
const name = z.string().parse(formData.get("name"));
const newResource: Resource = {
const newResource: Resource = parseResource({
id: resource?.id ?? nanoid(),
name,
control: "system",
url: localResource.value,
method,
headers: [],
};
formData,
});
const newVariable: DataSource = {
id: variable?.id ?? nanoid(),
scopeInstanceId,
name,
name: newResource.name,
type: "resource",
resourceId: newResource.id,
};
Expand All @@ -1037,6 +1034,8 @@ export const SystemResourceForm = forwardRef<

return (
<>
<input type="hidden" name="method" value="get" />
<input type="hidden" name="url" value={localResource.value} />
<Flex direction="column" css={{ gap: theme.spacing[3] }}>
<Label htmlFor={resourceId}>Resource</Label>
<Select
Expand Down Expand Up @@ -1116,29 +1115,15 @@ export const GraphqlResourceForm = forwardRef<
if (scopeInstanceId === undefined) {
return;
}
const name = z.string().parse(formData.get("name"));
const body = generateObjectExpression(
new Map([
["query", JSON.stringify(query)],
["variables", variables],
])
);
const newResource: Resource = {
const newResource = parseResource({
id: resource?.id ?? nanoid(),
name,
control: "graphql",
url,
method: "post",
headers: [
...headers,
{ name: "Content-Type", value: "application/json" },
],
body,
};
formData,
});
const newVariable: DataSource = {
id: variable?.id ?? nanoid(),
scopeInstanceId,
name,
name: newResource.name,
type: "resource",
resourceId: newResource.id,
};
Expand All @@ -1155,6 +1140,28 @@ export const GraphqlResourceForm = forwardRef<

return (
<>
<input type="hidden" name="method" value="post" />
{!headers.some(({ name }) => isContentType(name)) && (
<>
<input type="hidden" name="header-name" value="Content-Type" />
<input
type="hidden"
name="header-value"
value={`"application/json"`}
/>
</>
)}
<input
type="hidden"
name="body"
value={generateObjectExpression(
new Map([
["query", JSON.stringify(query)],
["variables", variables],
])
)}
/>

<UrlField
scope={scope}
aliases={aliases}
Expand Down
22 changes: 14 additions & 8 deletions packages/sdk/src/resource-loader.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,13 +25,19 @@ export const loadResource = async (
) => {
try {
const { method, searchParams, headers, body } = resourceRequest;
// cloudflare workers fail when fetching url contains spaces
// even though new URL suppose to trim them on parsing by spec
const url = new URL(resourceRequest.url.trim());
if (searchParams) {
for (const { name, value } of searchParams) {
url.searchParams.append(name, serializeValue(value));
let href = resourceRequest.url;
try {
// cloudflare workers fail when fetching url contains spaces
// even though new URL suppose to trim them on parsing by spec
const url = new URL(resourceRequest.url.trim());
if (searchParams) {
for (const { name, value } of searchParams) {
url.searchParams.append(name, serializeValue(value));
}
}
href = url.href;
} catch {
// empty block
}
const requestHeaders = new Headers(
headers.map(({ name, value }): [string, string] => [
Expand All @@ -46,7 +52,7 @@ export const loadResource = async (
if (method !== "get" && body !== undefined) {
requestInit.body = serializeValue(body);
}
const response = await customFetch(url.href, requestInit);
const response = await customFetch(href, requestInit);

let data = await response.text();

Expand All @@ -59,7 +65,7 @@ export const loadResource = async (

if (!response.ok) {
console.error(
`Failed to load resource: ${url} - ${response.status}: ${JSON.stringify(data).slice(0, 300)}`
`Failed to load resource: ${href} - ${response.status}: ${JSON.stringify(data).slice(0, 300)}`
);
}

Expand Down
Loading