fix: only show valid organizations in CreateTemplateForm (#14174)

This commit is contained in:
Kayla Washburn-Love
2024-08-06 12:10:44 -06:00
committed by GitHub
parent ff785588fe
commit 3b53f5ab47
4 changed files with 100 additions and 2 deletions
@@ -9,8 +9,9 @@ import {
useState,
} from "react";
import { useQuery } from "react-query";
import { checkAuthorization } from "api/queries/authCheck";
import { organizations } from "api/queries/organizations";
import type { Organization } from "api/typesGenerated";
import type { AuthorizationCheck, Organization } from "api/typesGenerated";
import { Avatar } from "components/Avatar/Avatar";
import { AvatarData } from "components/AvatarData/AvatarData";
import { useDebouncedFunction } from "hooks/debounce";
@@ -22,6 +23,7 @@ export type OrganizationAutocompleteProps = {
className?: string;
size?: ComponentProps<typeof TextField>["size"];
required?: boolean;
check?: AuthorizationCheck;
};
export const OrganizationAutocomplete: FC<OrganizationAutocompleteProps> = ({
@@ -31,6 +33,7 @@ export const OrganizationAutocomplete: FC<OrganizationAutocompleteProps> = ({
className,
size = "small",
required,
check,
}) => {
const [autoComplete, setAutoComplete] = useState<{
value: string;
@@ -41,6 +44,22 @@ export const OrganizationAutocomplete: FC<OrganizationAutocompleteProps> = ({
});
const organizationsQuery = useQuery(organizations());
const permissionsQuery = useQuery(
check && organizationsQuery.data
? checkAuthorization({
checks: Object.fromEntries(
organizationsQuery.data.map((org) => [
org.id,
{
...check,
object: { ...check.object, organization_id: org.id },
},
]),
),
})
: { enabled: false },
);
const { debounced: debouncedInputOnChange } = useDebouncedFunction(
(event: ChangeEvent<HTMLInputElement>) => {
setAutoComplete((state) => ({
@@ -51,11 +70,20 @@ export const OrganizationAutocomplete: FC<OrganizationAutocompleteProps> = ({
750,
);
// If an authorization check was provided, filter the organizations based on
// the results of that check.
let options = organizationsQuery.data ?? [];
if (check) {
options = permissionsQuery.data
? options.filter((org) => permissionsQuery.data[org.id])
: [];
}
return (
<Autocomplete
noOptionsText="No organizations found"
className={className}
options={organizationsQuery.data ?? []}
options={options}
loading={organizationsQuery.isLoading}
value={value}
data-testid="organization-autocomplete"
+1
View File
@@ -41,6 +41,7 @@ export const permissionsToCheck = {
[checks.createTemplates]: {
object: {
resource_type: "template",
any_org: true,
},
action: "update",
},
@@ -61,6 +61,18 @@ export const StarterTemplateWithOrgPicker: Story = {
},
};
const canCreateTemplate = (organizationId: string) => {
return {
[organizationId]: {
object: {
resource_type: "template",
organization_id: organizationId,
},
action: "create",
},
};
};
export const StarterTemplateWithProvisionerWarning: Story = {
parameters: {
queries: [
@@ -68,6 +80,21 @@ export const StarterTemplateWithProvisionerWarning: Story = {
key: organizationsKey,
data: [MockDefaultOrganization, MockOrganization2],
},
{
key: [
"authorization",
{
checks: {
...canCreateTemplate(MockDefaultOrganization.id),
...canCreateTemplate(MockOrganization2.id),
},
},
],
data: {
[MockDefaultOrganization.id]: true,
[MockOrganization2.id]: true,
},
},
{
key: getProvisionerDaemonsKey(MockOrganization2.id),
data: [],
@@ -86,6 +113,44 @@ export const StarterTemplateWithProvisionerWarning: Story = {
},
};
export const StarterTemplatePermissionsCheck: Story = {
parameters: {
queries: [
{
key: organizationsKey,
data: [MockDefaultOrganization, MockOrganization2],
},
{
key: [
"authorization",
{
checks: {
...canCreateTemplate(MockDefaultOrganization.id),
...canCreateTemplate(MockOrganization2.id),
},
},
],
data: {
[MockDefaultOrganization.id]: true,
[MockOrganization2.id]: false,
},
},
{
key: getProvisionerDaemonsKey(MockOrganization2.id),
data: [],
},
],
},
args: {
...StarterTemplate.args,
showOrganizationPicker: true,
},
play: async () => {
const organizationPicker = screen.getByPlaceholderText("Organization name");
await userEvent.click(organizationPicker);
},
};
export const DuplicateTemplateWithVariables: Story = {
args: {
copiedTemplate: MockTemplate,
@@ -267,6 +267,10 @@ export const CreateTemplateForm: FC<CreateTemplateFormProps> = (props) => {
void form.setFieldValue("organization", newValue?.name || "");
}}
size="medium"
check={{
object: { resource_type: "template" },
action: "create",
}}
/>
</>
)}