mirror of
https://github.com/graphql-hive/console
synced 2026-05-24 01:28:32 +00:00
refactor: remove usage of scopes for target access tokens (#6330)
This commit is contained in:
parent
7d3291f954
commit
24391ab6e3
5 changed files with 12 additions and 234 deletions
|
|
@ -330,7 +330,7 @@ export class SchemaVersionHelper {
|
|||
return schemaLog?.sdl ?? null;
|
||||
}
|
||||
|
||||
async getIsValid(schemaVersion: SchemaVersion) {
|
||||
getIsValid(schemaVersion: SchemaVersion) {
|
||||
return schemaVersion.isComposable && schemaVersion.hasContractCompositionErrors === false;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,9 +1,9 @@
|
|||
import { useState } from 'react';
|
||||
import { useForm, UseFormReturn } from 'react-hook-form';
|
||||
import { AnyVariables, useMutation, UseMutationState, useQuery } from 'urql';
|
||||
import { AnyVariables, useMutation, UseMutationState } from 'urql';
|
||||
import { z } from 'zod';
|
||||
import { Tag } from '@/components//v2/tag';
|
||||
import { PermissionScopeItem, usePermissionsManager } from '@/components/organization/Permissions';
|
||||
import { PermissionScopeItem } from '@/components/organization/Permissions';
|
||||
import { Button } from '@/components/ui/button';
|
||||
import {
|
||||
Dialog,
|
||||
|
|
@ -18,7 +18,7 @@ import { Input } from '@/components/ui/input';
|
|||
import { InputCopy } from '@/components/ui/input-copy';
|
||||
import { useToast } from '@/components/ui/use-toast';
|
||||
import { Accordion } from '@/components/v2/accordion';
|
||||
import { FragmentType, graphql, useFragment } from '@/gql';
|
||||
import { graphql } from '@/gql';
|
||||
import { TargetAccessScope } from '@/gql/graphql';
|
||||
import { RegistryAccessScope } from '@/lib/access/common';
|
||||
import { zodResolver } from '@hookform/resolvers/zod';
|
||||
|
|
@ -48,16 +48,6 @@ export const CreateAccessToken_CreateTokenMutation = graphql(`
|
|||
}
|
||||
`);
|
||||
|
||||
const CreateAccessTokenModalQuery = graphql(`
|
||||
query CreateAccessTokenModalQuery($organizationSlug: String!) {
|
||||
organization(selector: { organizationSlug: $organizationSlug }) {
|
||||
organization {
|
||||
...CreateAccessTokenModalContent_OrganizationFragment
|
||||
}
|
||||
}
|
||||
}
|
||||
`);
|
||||
|
||||
export function CreateAccessTokenModal(props: {
|
||||
isOpen: boolean;
|
||||
toggleModalOpen: () => void;
|
||||
|
|
@ -66,52 +56,19 @@ export function CreateAccessTokenModal(props: {
|
|||
targetSlug: string;
|
||||
}) {
|
||||
const { isOpen, toggleModalOpen } = props;
|
||||
const [organizationQuery] = useQuery({
|
||||
query: CreateAccessTokenModalQuery,
|
||||
variables: {
|
||||
organizationSlug: props.organizationSlug,
|
||||
},
|
||||
});
|
||||
|
||||
const organization = organizationQuery.data?.organization?.organization;
|
||||
|
||||
return (
|
||||
<Dialog open={isOpen} onOpenChange={toggleModalOpen}>
|
||||
{organization ? (
|
||||
<ModalContent
|
||||
organization={organization}
|
||||
organizationSlug={props.organizationSlug}
|
||||
projectSlug={props.projectSlug}
|
||||
targetSlug={props.targetSlug}
|
||||
toggleModalOpen={toggleModalOpen}
|
||||
/>
|
||||
) : (
|
||||
<DialogContent className="container w-4/5 max-w-[600px] md:w-3/5">
|
||||
<DialogHeader>
|
||||
<DialogTitle>Organization not found</DialogTitle>
|
||||
<DialogDescription>
|
||||
The organization you are trying to access does not exist.
|
||||
</DialogDescription>
|
||||
</DialogHeader>
|
||||
<DialogFooter>
|
||||
<Button onClick={toggleModalOpen}>Ok, got it!</Button>
|
||||
</DialogFooter>
|
||||
</DialogContent>
|
||||
)}
|
||||
<ModalContent
|
||||
organizationSlug={props.organizationSlug}
|
||||
projectSlug={props.projectSlug}
|
||||
targetSlug={props.targetSlug}
|
||||
toggleModalOpen={toggleModalOpen}
|
||||
/>
|
||||
</Dialog>
|
||||
);
|
||||
}
|
||||
|
||||
const CreateAccessTokenModalContent_OrganizationFragment = graphql(`
|
||||
fragment CreateAccessTokenModalContent_OrganizationFragment on Organization {
|
||||
id
|
||||
...UsePermissionManager_OrganizationFragment
|
||||
me {
|
||||
...UsePermissionManager_MemberFragment
|
||||
}
|
||||
}
|
||||
`);
|
||||
|
||||
function getFinalTargetAccessScopes(
|
||||
selectedScope: 'no-access' | TargetAccessScope,
|
||||
): Array<TargetAccessScope> {
|
||||
|
|
@ -142,25 +99,14 @@ const createRegistryTokenFormSchema = z.object({
|
|||
});
|
||||
|
||||
export function ModalContent(props: {
|
||||
organization: FragmentType<typeof CreateAccessTokenModalContent_OrganizationFragment>;
|
||||
organizationSlug: string;
|
||||
projectSlug: string;
|
||||
targetSlug: string;
|
||||
toggleModalOpen: () => void;
|
||||
}) {
|
||||
const { toast } = useToast();
|
||||
const organization = useFragment(
|
||||
CreateAccessTokenModalContent_OrganizationFragment,
|
||||
props.organization,
|
||||
);
|
||||
const [selectedScope, setSelectedScope] = useState<'no-access' | TargetAccessScope>('no-access');
|
||||
|
||||
const manager = usePermissionsManager({
|
||||
organization,
|
||||
member: organization.me,
|
||||
passMemberScopes: false,
|
||||
});
|
||||
|
||||
const form = useForm<z.infer<typeof createRegistryTokenFormSchema>>({
|
||||
mode: 'onChange',
|
||||
resolver: zodResolver(createRegistryTokenFormSchema),
|
||||
|
|
@ -205,7 +151,6 @@ export function ModalContent(props: {
|
|||
return (
|
||||
<GenerateTokenContent
|
||||
form={form}
|
||||
manager={manager}
|
||||
noPermissionsSelected={noPermissionsSelected}
|
||||
onSubmit={onSubmit}
|
||||
selectedScope={selectedScope} // Ensure selectedScope is passed correctly
|
||||
|
|
@ -246,7 +191,6 @@ export function CreatedTokenContent(props: {
|
|||
export function GenerateTokenContent(props: {
|
||||
form: UseFormReturn<z.infer<typeof createRegistryTokenFormSchema>>;
|
||||
onSubmit: (values: z.infer<typeof createRegistryTokenFormSchema>) => void;
|
||||
manager: ReturnType<typeof usePermissionsManager>;
|
||||
setSelectedScope: (scope: 'no-access' | TargetAccessScope) => void;
|
||||
selectedScope: 'no-access' | TargetAccessScope;
|
||||
toggleModalOpen: () => void;
|
||||
|
|
@ -292,11 +236,8 @@ export function GenerateTokenContent(props: {
|
|||
dataCy="registry-access-scope"
|
||||
key={props.selectedScope}
|
||||
scope={RegistryAccessScope}
|
||||
canManageScope={
|
||||
props.manager.canAccessTarget(RegistryAccessScope.mapping['read-only']) ||
|
||||
props.manager.canAccessTarget(RegistryAccessScope.mapping['read-write'])
|
||||
}
|
||||
checkAccess={props.manager.canAccessTarget}
|
||||
canManageScope
|
||||
checkAccess={() => true}
|
||||
onChange={value => {
|
||||
if (value === 'no-access') {
|
||||
props.setSelectedScope('no-access');
|
||||
|
|
|
|||
|
|
@ -62,9 +62,6 @@ const MemberFields = graphql(`
|
|||
email
|
||||
}
|
||||
isOwner
|
||||
organizationAccessScopes
|
||||
projectAccessScopes
|
||||
targetAccessScopes
|
||||
}
|
||||
`);
|
||||
|
||||
|
|
@ -240,9 +237,6 @@ export const TransferOrganizationOwnershipModal = ({
|
|||
>
|
||||
{({ selected }: { selected?: boolean }) => (
|
||||
<div className="flex flex-row items-center justify-between gap-2">
|
||||
{/* <div className="ml-2.5 flex h-8 w-8 shrink-0 items-center justify-center overflow-hidden rounded-full bg-gray-800">
|
||||
<img src={member.image} className="block h-full w-full" />
|
||||
</div> */}
|
||||
<div className="ml-2 flex flex-1 flex-col gap-x-2">
|
||||
<div className="block truncate text-sm">{member.user.displayName}</div>
|
||||
<div className="text-xs font-normal text-gray-400">
|
||||
|
|
|
|||
|
|
@ -1,6 +1,5 @@
|
|||
import { FragmentType, graphql, useFragment } from '@/gql';
|
||||
import { TargetAccessScope } from '@/gql/graphql';
|
||||
import { useRedirect } from './common';
|
||||
|
||||
export { TargetAccessScope };
|
||||
|
||||
|
|
@ -23,40 +22,3 @@ export function canAccessTarget(
|
|||
|
||||
return member.targetAccessScopes.includes(scope);
|
||||
}
|
||||
|
||||
export function useTargetAccess({
|
||||
scope,
|
||||
member: mmember,
|
||||
redirect = false,
|
||||
organizationSlug,
|
||||
projectSlug,
|
||||
targetSlug,
|
||||
}: {
|
||||
scope: TargetAccessScope;
|
||||
member: null | FragmentType<typeof CanAccessTarget_MemberFragment>;
|
||||
redirect?: boolean;
|
||||
organizationSlug: string;
|
||||
projectSlug: string;
|
||||
targetSlug: string;
|
||||
}) {
|
||||
const member = useFragment(CanAccessTarget_MemberFragment, mmember);
|
||||
const canAccess = canAccessTarget(scope, mmember);
|
||||
useRedirect({
|
||||
canAccess,
|
||||
redirectTo: redirect
|
||||
? router => {
|
||||
void router.navigate({
|
||||
to: '/$organizationSlug/$projectSlug/$targetSlug',
|
||||
params: {
|
||||
organizationSlug,
|
||||
projectSlug,
|
||||
targetSlug,
|
||||
},
|
||||
});
|
||||
}
|
||||
: undefined,
|
||||
entity: member,
|
||||
});
|
||||
|
||||
return canAccess;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,119 +0,0 @@
|
|||
import { useState } from 'react';
|
||||
import { useForm } from 'react-hook-form';
|
||||
import { z } from 'zod';
|
||||
import { usePermissionsManager } from '@/components/organization/Permissions';
|
||||
import {
|
||||
CreatedTokenContent,
|
||||
GenerateTokenContent,
|
||||
} from '@/components/target/settings/registry-access-token';
|
||||
import { Button } from '@/components/ui/button';
|
||||
import { Dialog, DialogTrigger } from '@/components/ui/dialog';
|
||||
import { TargetAccessScope } from '@/gql/graphql';
|
||||
import { zodResolver } from '@hookform/resolvers/zod';
|
||||
import { Meta, StoryObj } from '@storybook/react';
|
||||
|
||||
const meta: Meta<typeof CreatedTokenContent> = {
|
||||
title: 'Modals/Create Access Token',
|
||||
component: CreatedTokenContent,
|
||||
};
|
||||
|
||||
export default meta;
|
||||
type Story = StoryObj<typeof CreatedTokenContent>;
|
||||
|
||||
const formSchema = z.object({
|
||||
tokenDescription: z
|
||||
.string()
|
||||
.min(2, { message: 'Token description must be at least 2 characters long' }),
|
||||
});
|
||||
|
||||
export const GenerateToken: Story = {
|
||||
render: () => {
|
||||
const [openModal, setOpenModal] = useState(false);
|
||||
const toggleModalOpen = () => setOpenModal(!openModal);
|
||||
|
||||
const form = useForm<z.infer<typeof formSchema>>({
|
||||
mode: 'onChange',
|
||||
resolver: zodResolver(formSchema),
|
||||
defaultValues: {
|
||||
tokenDescription: '',
|
||||
},
|
||||
});
|
||||
|
||||
const manager = {
|
||||
canAccessOrganization: () => true,
|
||||
canAccessProject: () => true,
|
||||
canAccessTarget: () => true,
|
||||
noneSelected: false,
|
||||
organizationScopes: [],
|
||||
projectScopes: [],
|
||||
targetScopes: [],
|
||||
setOrganizationScopes: () => {},
|
||||
setProjectScopes: () => {},
|
||||
setTargetScopes: () => {},
|
||||
submit: () => {},
|
||||
} as unknown as ReturnType<typeof usePermissionsManager>;
|
||||
|
||||
const [selectedScope, setSelectedScope] = useState<'no-access' | TargetAccessScope>(
|
||||
'no-access',
|
||||
);
|
||||
const noPermissionsSelected = selectedScope === 'no-access';
|
||||
|
||||
return (
|
||||
<Dialog open={openModal} onOpenChange={toggleModalOpen}>
|
||||
<DialogTrigger asChild>
|
||||
<Button onClick={toggleModalOpen}>Open Modal</Button>
|
||||
</DialogTrigger>
|
||||
<GenerateTokenContent
|
||||
form={form}
|
||||
manager={manager}
|
||||
noPermissionsSelected={noPermissionsSelected}
|
||||
onSubmit={values => console.log('Submit:', values)}
|
||||
selectedScope={selectedScope}
|
||||
setSelectedScope={setSelectedScope}
|
||||
toggleModalOpen={toggleModalOpen}
|
||||
/>
|
||||
</Dialog>
|
||||
);
|
||||
},
|
||||
};
|
||||
|
||||
export const CreatedToken: Story = {
|
||||
render: () => {
|
||||
const [openModal, setOpenModal] = useState(false);
|
||||
const toggleModalOpen = () => setOpenModal(!openModal);
|
||||
|
||||
return (
|
||||
<Dialog open={openModal} onOpenChange={toggleModalOpen}>
|
||||
<DialogTrigger asChild>
|
||||
<Button onClick={toggleModalOpen}>Open Modal</Button>
|
||||
</DialogTrigger>
|
||||
<CreatedTokenContent
|
||||
mutation={{
|
||||
fetching: false,
|
||||
data: {
|
||||
createToken: {
|
||||
ok: {
|
||||
selector: {
|
||||
organization: '',
|
||||
project: '',
|
||||
target: '',
|
||||
},
|
||||
createdToken: {
|
||||
id: 'token-id',
|
||||
name: 'Token Name',
|
||||
alias: 'token-alias',
|
||||
date: '2023-07-17',
|
||||
lastUsedAt: null,
|
||||
},
|
||||
secret: 'token-secret',
|
||||
},
|
||||
},
|
||||
},
|
||||
stale: false,
|
||||
}}
|
||||
toggleModalOpen={toggleModalOpen}
|
||||
/>
|
||||
</Dialog>
|
||||
);
|
||||
},
|
||||
};
|
||||
Loading…
Reference in a new issue