Skip to content

Commit 692c13d

Browse files
authored
Merge pull request #2946 from appwrite/codex/render-deprecated-api-key-scopes
[codex] Render deprecated API key scopes
2 parents 00d9765 + 8a7736c commit 692c13d

4 files changed

Lines changed: 46 additions & 84 deletions

File tree

src/lib/constants.ts

Lines changed: 18 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -155,12 +155,15 @@ export const defaultRoles: string[] = ['owner'];
155155

156156
// these are kept for backwards compatibility with keys and events that already exists.
157157
// for the new ones, we use the new terminology.
158-
export const scopes: {
158+
export type ScopeDefinition = {
159159
scope: string;
160160
description: string;
161161
category: string;
162162
icon: string;
163-
}[] = [
163+
deprecated?: boolean;
164+
};
165+
166+
export const scopes: ScopeDefinition[] = [
164167
{
165168
scope: 'sessions.write',
166169
description: "Access to create, update and delete your project's sessions",
@@ -207,13 +210,15 @@ export const scopes: {
207210
scope: 'collections.read',
208211
description: "Access to read your project's database collections",
209212
category: 'Database',
210-
icon: 'database'
213+
icon: 'database',
214+
deprecated: true
211215
},
212216
{
213217
scope: 'collections.write',
214218
description: "Access to create, update, and delete your project's database collections",
215219
category: 'Database',
216-
icon: 'database'
220+
icon: 'database',
221+
deprecated: true
217222
},
218223
{
219224
scope: 'tables.read',
@@ -231,14 +236,16 @@ export const scopes: {
231236
scope: 'attributes.read',
232237
description: "Access to read your project's database collection's attributes",
233238
category: 'Database',
234-
icon: 'database'
239+
icon: 'database',
240+
deprecated: true
235241
},
236242
{
237243
scope: 'attributes.write',
238244
description:
239245
"Access to create, update, and delete your project's database collection's attributes",
240246
category: 'Database',
241-
icon: 'database'
247+
icon: 'database',
248+
deprecated: true
242249
},
243250
{
244251
scope: 'columns.read',
@@ -268,13 +275,15 @@ export const scopes: {
268275
scope: 'documents.read',
269276
description: "Access to read your project's database documents",
270277
category: 'Database',
271-
icon: 'database'
278+
icon: 'database',
279+
deprecated: true
272280
},
273281
{
274282
scope: 'documents.write',
275283
description: "Access to create, update, and delete your project's database documents",
276284
category: 'Database',
277-
icon: 'database'
285+
icon: 'database',
286+
deprecated: true
278287
},
279288
{
280289
scope: 'rows.read',
@@ -466,7 +475,7 @@ export const scopes: {
466475
}
467476
];
468477

469-
export const cloudOnlyBackupScopes = [
478+
export const cloudOnlyBackupScopes: ScopeDefinition[] = [
470479
{
471480
scope: 'policies.read',
472481
description: 'Access to read your database backup policies',

src/routes/(console)/project-[region]-[project]/overview/(components)/keyDetails.svelte

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,6 @@
1616
import { symmetricDifference } from '$lib/helpers/array';
1717
import Scopes from '../api-keys/scopes.svelte';
1818
import { InteractiveText, Layout, Typography } from '@appwrite.io/pink-svelte';
19-
import { getEffectiveScopes } from '../api-keys/scopes.svelte';
2019
2120
export let key: Models.DevKey | Models.Key;
2221
export let keyType: 'api' | 'dev' = 'api';
@@ -163,8 +162,6 @@
163162
{#if isApiKey}
164163
<Form onSubmit={updateScopes}>
165164
{@const apiKey = asApiKey(key)}
166-
{@const apiKeyCorrectScopes = getEffectiveScopes(apiKey.scopes)}
167-
{@const currentEffective = scopes ? getEffectiveScopes(scopes) : null}
168165
<CardGrid>
169166
<svelte:fragment slot="title">Scopes</svelte:fragment>
170167
You can choose which permission scope to grant your application. It is a best practice
@@ -178,8 +175,7 @@
178175
<svelte:fragment slot="actions">
179176
<Button
180177
submit
181-
disabled={scopes &&
182-
!symmetricDifference(currentEffective, apiKeyCorrectScopes).length}
178+
disabled={scopes && !symmetricDifference(scopes, apiKey.scopes).length}
183179
>Update</Button>
184180
</svelte:fragment>
185181
</CardGrid>

src/routes/(console)/project-[region]-[project]/overview/(components)/table.svelte

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,6 @@
1111
import { Badge, Layout, Table } from '@appwrite.io/pink-svelte';
1212
import DeleteBatch from './deleteBatch.svelte';
1313
import { capitalize } from '$lib/helpers/string';
14-
import { getEffectiveScopes } from '../api-keys/scopes.svelte';
1514
1615
let {
1716
keyType = 'api',
@@ -31,7 +30,7 @@
3130
3231
function getApiKeyScopeCount(key: Models.Key | Models.DevKey) {
3332
const apiKey = key as Models.Key;
34-
return getEffectiveScopes(apiKey.scopes).length;
33+
return apiKey.scopes.length;
3534
}
3635
3736
function getExpiryDetails(key: Models.Key | Models.DevKey): {

src/routes/(console)/project-[region]-[project]/overview/api-keys/scopes.svelte

Lines changed: 26 additions & 68 deletions
Original file line numberDiff line numberDiff line change
@@ -31,29 +31,21 @@
3131
import { Button } from '$lib/elements/forms';
3232
import { symmetricDifference } from '$lib/helpers/array';
3333
import { scopes as allScopes, cloudOnlyBackupScopes } from '$lib/constants';
34-
import { Accordion, Divider, Layout, Selector } from '@appwrite.io/pink-svelte';
34+
import { Accordion, Badge, Divider, Layout, Selector } from '@appwrite.io/pink-svelte';
3535
import type { Scopes } from '@appwrite.io/console';
3636
3737
export let scopes: Scopes[];
3838
39-
const baseFilteredScopes = allScopes.filter((scope) => {
40-
const val = scope.scope;
41-
if (!val) return false;
42-
43-
const legacyPrefixes = ['collections.', 'attributes.', 'documents.'];
44-
return !legacyPrefixes.some((prefix) => val.startsWith(prefix));
45-
});
46-
4739
// insert cloud-only scopes right after databases.write
48-
const databasesWriteIndex = baseFilteredScopes.findIndex((s) => s.scope === 'databases.write');
40+
const databasesWriteIndex = allScopes.findIndex((s) => s.scope === 'databases.write');
4941
const filteredScopes =
5042
isCloud && databasesWriteIndex !== -1
5143
? [
52-
...baseFilteredScopes.slice(0, databasesWriteIndex + 1),
44+
...allScopes.slice(0, databasesWriteIndex + 1),
5345
...cloudOnlyBackupScopes,
54-
...baseFilteredScopes.slice(databasesWriteIndex + 1)
46+
...allScopes.slice(databasesWriteIndex + 1)
5547
]
56-
: baseFilteredScopes;
48+
: allScopes;
5749
5850
// include all scopes
5951
const scopeCatalog = new Set([
@@ -90,9 +82,8 @@
9082
9183
onMount(() => {
9284
scopes.forEach((scope) => {
93-
const newerScope = toNewerScope(scope);
94-
if (newerScope in activeScopes) {
95-
activeScopes[newerScope] = true;
85+
if (scope in activeScopes) {
86+
activeScopes[scope] = true;
9687
}
9788
});
9889
@@ -111,36 +102,11 @@
111102
}
112103
}
113104
114-
function toNewerScope(scope: string): string {
115-
for (const pair of compatPairs) {
116-
if (scope.startsWith(pair.legacy)) {
117-
return scope.replace(pair.legacy, pair.newer);
118-
}
119-
}
120-
return scope;
121-
}
122-
123-
function getAllScopeVariants(scope: string): string[] {
124-
const variants = new Set([scope]);
125-
126-
for (const pair of compatPairs) {
127-
if (scope.startsWith(pair.newer)) {
128-
variants.add(scope.replace(pair.newer, pair.legacy));
129-
} else if (scope.startsWith(pair.legacy)) {
130-
variants.add(scope.replace(pair.legacy, pair.newer));
131-
}
132-
}
133-
134-
return Array.from(variants);
135-
}
136-
137105
function categoryState(category: string, s: string[]): boolean | 'indeterminate' {
138106
const scopesByCategory = filteredScopes.filter((n) => n.category === category);
139-
140-
const activeInCategory = scopesByCategory.filter((scopeItem) => {
141-
const newerScope = scopeItem.scope;
142-
return s.some((scope) => toNewerScope(scope) === newerScope);
143-
});
107+
const activeInCategory = scopesByCategory.filter((scopeItem) =>
108+
s.includes(scopeItem.scope as Scopes)
109+
);
144110
145111
if (activeInCategory.length === 0) {
146112
return false;
@@ -154,27 +120,16 @@
154120
function onCategoryChange(event: CustomEvent<boolean | 'indeterminate'>, category: Category) {
155121
if (event.detail === 'indeterminate') return;
156122
filteredScopes.forEach((s) => {
157-
if (s.category === category) {
123+
if (s.category === category && !s.deprecated) {
158124
activeScopes[s.scope] = event.detail;
159125
}
160126
});
161127
}
162128
163129
function generateSyncedScopes(activeScopesObj: Record<string, boolean>): Scopes[] {
164-
const result = new Set<string>();
165-
166-
Object.entries(activeScopesObj).forEach(([scope, isActive]) => {
167-
if (isActive) {
168-
const variants = getAllScopeVariants(scope);
169-
variants.forEach((variant) => {
170-
if (scopeCatalog.has(variant)) {
171-
result.add(variant);
172-
}
173-
});
174-
}
175-
});
176-
177-
return Array.from(result) as Scopes[];
130+
return Object.entries(activeScopesObj)
131+
.filter(([scope, isActive]) => isActive && scopeCatalog.has(scope))
132+
.map(([scope]) => scope as Scopes);
178133
}
179134
180135
$: {
@@ -203,9 +158,7 @@
203158
{@const checked = categoryState(category, scopes)}
204159
{@const isLastItem = index === categories.length - 1}
205160
{@const scopesLength = filteredScopes.filter(
206-
(n) =>
207-
n.category === category &&
208-
scopes.some((scope) => toNewerScope(scope) === n.scope)
161+
(n) => n.category === category && scopes.includes(n.scope as Scopes)
209162
).length}
210163
<Accordion
211164
selectable
@@ -216,12 +169,17 @@
216169
on:change={(event) => onCategoryChange(event, category)}>
217170
<Layout.Stack>
218171
{#each filteredScopes.filter((s) => s.category === category) as scope}
219-
<Selector.Checkbox
220-
size="s"
221-
id={scope.scope}
222-
label={scope.scope}
223-
description={scope.description}
224-
bind:checked={activeScopes[scope.scope]} />
172+
<Layout.Stack direction="row" alignItems="center" gap="s">
173+
<Selector.Checkbox
174+
size="s"
175+
id={scope.scope}
176+
label={`${scope.scope}${scope.deprecated ? ' (Deprecated)' : ''}`}
177+
description={scope.description}
178+
bind:checked={activeScopes[scope.scope]} />
179+
{#if scope.deprecated}
180+
<Badge size="xs" variant="secondary" content="Deprecated" />
181+
{/if}
182+
</Layout.Stack>
225183
{/each}
226184
</Layout.Stack>
227185
</Accordion>

0 commit comments

Comments
 (0)