Skip to content
Merged
Show file tree
Hide file tree
Changes from 2 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
2 changes: 1 addition & 1 deletion client/config/jest.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ const config: JestConfigWithTsJest = {
"^.+\\.[cm]?[jt]sx?$": "ts-jest",
},
transformIgnorePatterns: [
"node_modules/(?!(keycloak-js|react-error-boundary)/)", // process esm only modules
"node_modules/(?!(keycloak-js|react-error-boundary|lodash-es)/)", // process esm only modules
],

// Code to set up the testing framework before each test file in the suite is executed
Expand Down
4 changes: 3 additions & 1 deletion client/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@
"immer": "^10.2.0",
"js-yaml": "^4.1.0",
"keycloak-js": "^26.1.0",
"lodash-es": "^4.17.21",
"monaco-editor": "^0.52.2",
"radash": "^12.1.0",
"react": "^18.3.1",
Expand All @@ -68,6 +69,7 @@
"@types/file-saver": "^2.0.7",
"@types/jest": "^29.5.4",
"@types/js-yaml": "^4.0.9",
"@types/lodash-es": "^4.17.12",
"@types/react": "^18.0.0",
"@types/react-dom": "^18.0.0",
"@types/react-measure": "^2.0.12",
Expand All @@ -80,10 +82,10 @@
"css-minimizer-webpack-plugin": "^7.0.2",
"fork-ts-checker-webpack-plugin": "^9.1.0",
"html-webpack-plugin": "^5.6.3",
"jest": "^29.7.0",
"jest-environment-jsdom": "^29.7.0",
"jest-resolve": "^29.7.0",
"jest-watch-typeahead": "^2.2.2",
"jest": "^29.7.0",
"mini-css-extract-plugin": "^2.8.1",
"monaco-editor-webpack-plugin": "^7.1.1",
"msw": "^1.2.3",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -61,38 +61,10 @@ const RemoveButton = ({
);
};

/*
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"properties": {
"names": {
"description": "Application names. Each may be a glob expression.",
"items": {
"minLength": 1,
"type": "string"
},
"minItems": 0,
"type": "array"
},
"spaces": {
"description": "Space names.",
"items": {
"minLength": 1,
"type": "string"
},
"minItems": 1,
"type": "array"
}
},
"required": ["spaces"],
"title": "CloudFoundry Discover Filter",
"type": "object"
}
*/

interface FormValues {
names: { value: string }[];
organizations: { value: string }[];
spaces: { value: string }[];
names: { value: string }[];
}

const stringsToFormValue = (values?: string[]) =>
Expand All @@ -108,40 +80,40 @@ export interface FilterInputCloudFoundryProps {
}

/**
* Inputs for CloudFoundry discover applications filter. This is based on the json schema.
* Inputs for CloudFoundry discover applications filter.
*
* This is based on the JSON schema for the Cloud Foundry discovery filter as defined in
* {@link ./validate-cloudfoundry-schema.tsx}.
*/
export const FilterInputCloudFoundry: React.FC<
FilterInputCloudFoundryProps
> = ({ id, values, onDocumentChanged }) => {
const validationSchema = yup.object().shape({
names: yup
.array()
.of(
yup.object().shape({
value: yup
.string()
.min(1, "Name must be at least 1 character")
.trim(),
})
)
.min(0),
spaces: yup
.array()
.of(
yup.object().shape({
value: yup
.string()
.min(1, "Space must be at least 1 character")
.trim(),
})
)
.min(1),
organizations: yup.array().of(
yup.object().shape({
value: yup
.string()
.min(1, "Organization name must be at least 1 character")
.trim(),
})
),
spaces: yup.array().of(
yup.object().shape({
value: yup.string().min(1, "Space must be at least 1 character").trim(),
})
),
names: yup.array().of(
yup.object().shape({
value: yup.string().min(1, "Name must be at least 1 character").trim(),
})
),
});

const form = useForm<FormValues>({
defaultValues: {
names: stringsToFormValue(values?.names as string[]),
organizations: stringsToFormValue(values?.organizations as string[]),
spaces: stringsToFormValue(values?.spaces as string[]),
names: stringsToFormValue(values?.names as string[]),
},
resolver: yupResolver(validationSchema),
mode: "all",
Expand All @@ -154,8 +126,9 @@ export const FilterInputCloudFoundry: React.FC<
formState: { values: true },
callback: ({ values }) => {
const asDocument = {
names: formValueToStrings(values.names),
organizations: formValueToStrings(values.organizations),
spaces: formValueToStrings(values.spaces),
names: formValueToStrings(values.names),
};
onDocumentChanged(asDocument);
},
Expand All @@ -168,12 +141,13 @@ export const FilterInputCloudFoundry: React.FC<
<StackItem>
<StringFieldsGroup
control={form.control}
groupTitle="Names"
groupDescription="Enter application name (glob expressions allowed)"
fieldName="names"
addLabel="Add a name"
removeLabel="Remove this name"
emptyMessage="No application names specified"
groupTitle="Organizations"
groupDescription="Enter organization name"
fieldName="organizations"
addLabel="Add an organization"
removeLabel="Remove this organization"
emptyMessage="No organizations specified"
isRequired={false}
/>
</StackItem>

Expand All @@ -185,8 +159,21 @@ export const FilterInputCloudFoundry: React.FC<
fieldName="spaces"
addLabel="Add a space"
removeLabel="Remove this space"
emptyMessage="No spaces specified (at least one space is required)"
isRequired={true}
emptyMessage="No spaces specified"
isRequired={false}
/>
</StackItem>

<StackItem>
<StringFieldsGroup
control={form.control}
groupTitle="Names"
groupDescription="Enter application name (glob expressions allowed)"
fieldName="names"
addLabel="Add a name"
removeLabel="Remove this name"
emptyMessage="No application names specified"
isRequired={false}
/>
</StackItem>
</Stack>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import { useFetchPlatformDiscoveryFilterSchema } from "@app/queries/schemas";
import { wrapAsEvent } from "@app/utils/utils";

import { FilterInputCloudFoundry } from "./filter-input-cloudfoundry";
import { useCloudFoundryCheck } from "./validate-cloudfoundry-schema";

interface FiltersFormValues {
filterRequired: boolean;
Expand Down Expand Up @@ -106,6 +107,11 @@ export const FilterInput: React.FC<{

useFilterStateChangeHandler(form, onFiltersChanged);

const useCloudFoundryFilterInput = useCloudFoundryCheck(
platform,
filtersSchema
);

return (
<div>
<TextContent style={{ marginBottom: "var(--pf-v5-global--spacer--lg)" }}>
Expand Down Expand Up @@ -140,7 +146,7 @@ export const FilterInput: React.FC<{
})}
fieldId="document"
renderInput={({ field: { value, name, onChange } }) =>
platform.kind === "cloudfoundry" ? (
useCloudFoundryFilterInput ? (
<FilterInputCloudFoundry
key={platform.kind}
id={name}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,20 +26,21 @@ export const ReviewInputCloudFoundry: React.FC<
return <EmptyTextMessage message="No filter values to display" />;
}

const names = (values.names as string[]) ?? [];
const organizations = (values.organizations as string[]) ?? [];
const spaces = (values.spaces as string[]) ?? [];
const names = (values.names as string[]) ?? [];

return (
<DescriptionList isHorizontal isCompact id={id}>
<DescriptionListGroup>
<DescriptionListTerm>Names</DescriptionListTerm>
<DescriptionListTerm>Organizations</DescriptionListTerm>
<DescriptionListDescription>
{names.length === 0 ? (
<EmptyTextMessage message="No names specified" />
{organizations.length === 0 ? (
<EmptyTextMessage message="No organizations specified" />
) : (
<List isPlain>
{names.map((name) => (
<ListItem key={name}>{name}</ListItem>
{organizations.map((organization) => (
<ListItem key={organization}>{organization}</ListItem>
))}
</List>
)}
Expand All @@ -60,6 +61,21 @@ export const ReviewInputCloudFoundry: React.FC<
)}
</DescriptionListDescription>
</DescriptionListGroup>

<DescriptionListGroup>
<DescriptionListTerm>Names</DescriptionListTerm>
<DescriptionListDescription>
{names.length === 0 ? (
<EmptyTextMessage message="No names specified" />
) : (
<List isPlain>
{names.map((name) => (
<ListItem key={name}>{name}</ListItem>
))}
</List>
)}
</DescriptionListDescription>
</DescriptionListGroup>
</DescriptionList>
);
};
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ import { usePlatformKindList } from "@app/hooks/usePlatformKindList";

import { FilterState } from "./filter-input";
import { ReviewInputCloudFoundry } from "./review-input-cloudfoundry";
import { useCloudFoundryCheck } from "./validate-cloudfoundry-schema";

export const Review: React.FC<{
platform: SourcePlatform;
Expand All @@ -27,6 +28,8 @@ export const Review: React.FC<{
const showFilters =
filters.filterRequired && filters.schema && filters.document;

const useCloudFoundryReview = useCloudFoundryCheck(platform, filters.schema);

return (
<div>
<TextContent style={{ marginBottom: "var(--pf-v5-global--spacer--lg)" }}>
Expand Down Expand Up @@ -82,7 +85,7 @@ export const Review: React.FC<{
padding: "16px",
}}
>
{platform.kind === "cloudfoundry" ? (
{useCloudFoundryReview ? (
<ReviewInputCloudFoundry
id="platform-discovery-filters-review"
values={filters.document}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
import * as React from "react";

import {
JsonSchemaObject,
SourcePlatform,
TargetedSchema,
} from "@app/api/models";
import { validatorGenerator } from "@app/utils/json-schema";

/**
* The schema that is implemented by `FilterInputCloudFoundry`.
*/
const SUPPORTED_SCHEMA: JsonSchemaObject = {
$schema: "https://json-schema.org/draft/2020-12/schema",
properties: {
organizations: {
description: "Organization names.",
items: {
minLength: 1,
type: "string",
},
type: "array",
},
spaces: {
description: "Space names.",
items: {
minLength: 1,
type: "string",
},
type: "array",
},
names: {
description: "Application names. Each may be a glob expression.",
items: {
minLength: 1,
type: "string",
},
type: "array",
},
},
type: "object",
};

/**
* Check the given schema and return if it is functionally equivalent to the schema used
* to build CloudFoundry forms.
*/
export const validateCloudFoundrySchema = validatorGenerator(SUPPORTED_SCHEMA);

export const useCloudFoundryCheck = (
platform: SourcePlatform,
schema?: TargetedSchema
) => {
return React.useMemo(() => {
return (
platform.kind === "cloudfoundry" &&
schema &&
validateCloudFoundrySchema(schema.definition)
);
}, [platform.kind, schema]);
};
Loading
Loading