Skip to content

Commit 057d55c

Browse files
committed
view single access token
1 parent 67f377f commit 057d55c

File tree

4 files changed

+52
-4
lines changed

4 files changed

+52
-4
lines changed

packages/services/api/src/modules/organization/module.graphql.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -328,6 +328,10 @@ export default gql`
328328
Paginated organization access tokens.
329329
"""
330330
accessTokens(first: Int, after: String): OrganizationAccessTokenConnection!
331+
"""
332+
Get organization access token by id.
333+
"""
334+
accessToken(id: ID!): OrganizationAccessToken
331335
}
332336
333337
type OrganizationAccessTokenEdge {

packages/services/api/src/modules/organization/providers/organization-access-tokens.ts

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -305,6 +305,30 @@ export class OrganizationAccessTokens {
305305
},
306306
};
307307
}
308+
309+
async get(args: { organizationId: string; id: string }) {
310+
await this.session.assertPerformAction({
311+
organizationId: args.organizationId,
312+
params: { organizationId: args.organizationId },
313+
action: 'accessToken:modify',
314+
});
315+
316+
const row = await this.pool.maybeOne<unknown>(sql` /* OrganizationAccessTokens.getPaginated */
317+
SELECT
318+
${organizationAccessTokenFields}
319+
FROM
320+
"organization_access_tokens"
321+
WHERE
322+
"id" = ${args.id}
323+
AND "organization_id" = ${args.organizationId}
324+
`);
325+
326+
if (row === null) {
327+
return null;
328+
}
329+
330+
return OrganizationAccessTokenModel.parse(row);
331+
}
308332
}
309333

310334
/**

packages/services/api/src/modules/organization/resolvers/Organization.ts

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import type { OrganizationResolvers } from './../../../__generated__/types';
99

1010
export const Organization: Pick<
1111
OrganizationResolvers,
12+
| 'accessToken'
1213
| 'accessTokens'
1314
| 'availableMemberPermissionGroups'
1415
| 'availableOrganizationPermissionGroups'
@@ -216,4 +217,10 @@ export const Organization: Pick<
216217
},
217218
});
218219
},
220+
accessToken: async (organization, args, { injector }) => {
221+
return injector.get(OrganizationAccessTokens).get({
222+
organizationId: organization.id,
223+
id: args.id,
224+
});
225+
},
219226
};

packages/web/app/src/components/organization/settings/access-tokens/access-tokens-table.tsx

Lines changed: 17 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import * as Table from '@/components/ui/table';
88
import { useToast } from '@/components/ui/use-toast';
99
import { TimeAgo } from '@/components/v2';
1010
import { graphql, useFragment, type FragmentType } from '@/gql';
11+
import { AccessTokenDetailViewSheet } from './access-token-detail-view-sheet';
1112

1213
const privateKeyFiller = new Array(20).fill('•').join('');
1314

@@ -58,6 +59,7 @@ export function AccessTokensTable(props: AccessTokensTable) {
5859
const client = useClient();
5960
const [isLoadingMore, setIsLoadingMore] = useState(false);
6061
const [deleteAccessTokenId, setDeleteAccessTokenId] = useState<string | null>(null);
62+
const [detailViewId, setDetailViewId] = useState<string | null>(null);
6163

6264
if (accessTokens.edges.length === 0) {
6365
return null;
@@ -105,7 +107,7 @@ export function AccessTokensTable(props: AccessTokensTable) {
105107
</Table.TableHeader>
106108
<Table.TableBody>
107109
{accessTokens.edges.map(edge => (
108-
<Table.TableRow>
110+
<Table.TableRow key={edge.cursor}>
109111
<Table.TableCell className="font-medium">{edge.node.title}</Table.TableCell>
110112
<Table.TableCell className="font-mono">
111113
{edge.node.firstCharacters + privateKeyFiller}
@@ -120,7 +122,9 @@ export function AccessTokensTable(props: AccessTokensTable) {
120122
</DropDownMenu.DropdownMenuTrigger>
121123
<DropDownMenu.DropdownMenuContent>
122124
<DropDownMenu.DropdownMenuLabel>Options</DropDownMenu.DropdownMenuLabel>
123-
<DropDownMenu.DropdownMenuItem>View Details</DropDownMenu.DropdownMenuItem>
125+
<DropDownMenu.DropdownMenuItem onClick={() => setDetailViewId(edge.node.id)}>
126+
View Details
127+
</DropDownMenu.DropdownMenuItem>
124128
<DropDownMenu.DropdownMenuItem
125129
onClick={() => setDeleteAccessTokenId(edge.node.id)}
126130
>
@@ -142,6 +146,13 @@ export function AccessTokensTable(props: AccessTokensTable) {
142146
}}
143147
/>
144148
)}
149+
{detailViewId && (
150+
<AccessTokenDetailViewSheet
151+
organizationSlug={props.organizationSlug}
152+
accessTokenId={detailViewId}
153+
onClose={() => setDetailViewId(null)}
154+
/>
155+
)}
145156
</Table.Table>
146157
);
147158
}
@@ -161,11 +172,13 @@ const DeleteAccessTokenConfirmationDialogue_DeleteOrganizationAccessToken = grap
161172
}
162173
`);
163174

164-
function DeleteAccessTokenConfirmationDialogue(props: {
175+
type DeleteAccessTokenConfirmationDialogueProps = {
165176
accessTokenId: string;
166177
onConfirm: () => void;
167178
onCancel: () => void;
168-
}) {
179+
};
180+
181+
function DeleteAccessTokenConfirmationDialogue(props: DeleteAccessTokenConfirmationDialogueProps) {
169182
const [mutationState, mutate] = useMutation(
170183
DeleteAccessTokenConfirmationDialogue_DeleteOrganizationAccessToken,
171184
);

0 commit comments

Comments
 (0)