diff --git a/.changeset/three-dryers-wave.md b/.changeset/three-dryers-wave.md new file mode 100644 index 000000000..15f13c49a --- /dev/null +++ b/.changeset/three-dryers-wave.md @@ -0,0 +1,5 @@ +--- +'@shopify/shopify-app-remix': patch +--- + +Return revoked scopes instead of querying for scopes after revoking diff --git a/packages/apps/shopify-app-remix/src/server/authenticate/admin/scope/__tests__/mock-responses.ts b/packages/apps/shopify-app-remix/src/server/authenticate/admin/scope/__tests__/mock-responses.ts index 30634b999..4c478336b 100644 --- a/packages/apps/shopify-app-remix/src/server/authenticate/admin/scope/__tests__/mock-responses.ts +++ b/packages/apps/shopify-app-remix/src/server/authenticate/admin/scope/__tests__/mock-responses.ts @@ -27,21 +27,37 @@ export const WITH_GRANTED_AND_DECLARED = buildGraphqlResponseContent({ }); export const REVOKED_WITHOUT_ERROR = buildGraphqlResponseContent({ - revoked: [ - { - handle: 'read_orders', - }, - ], + appRevokeAccessScopes: { + revoked: [ + { + handle: 'write_discounts', + }, + { + handle: 'read_orders', + }, + ], + userErrors: [], + }, +}); + +export const REVOKED_NOTHING = buildGraphqlResponseContent({ + appRevokeAccessScopes: { + revoked: [], + userErrors: [], + }, }); export const REVOKED_WITH_ERROR = buildGraphqlResponseContent({ - userErrors: [ - { - field: 'scopes', - messages: - 'The requested list of scopes to revoke includes invalid handles.', - }, - ], + appRevokeAccessScopes: { + revoked: null, + userErrors: [ + { + field: 'scopes', + messages: + 'The requested list of scopes to revoke includes invalid handles.', + }, + ], + }, }); function buildGraphqlResponseContent(content: any) { diff --git a/packages/apps/shopify-app-remix/src/server/authenticate/admin/scope/__tests__/revoke.test.ts b/packages/apps/shopify-app-remix/src/server/authenticate/admin/scope/__tests__/revoke.test.ts index 6509b4be6..fe97d8618 100644 --- a/packages/apps/shopify-app-remix/src/server/authenticate/admin/scope/__tests__/revoke.test.ts +++ b/packages/apps/shopify-app-remix/src/server/authenticate/admin/scope/__tests__/revoke.test.ts @@ -12,28 +12,36 @@ import { import * as responses from './mock-responses'; -it('returns scopes information', async () => { +it('returns successfully revoked scopes', async () => { // GIVEN const {scopes} = await setUpEmbeddedFlow(); - await mockGraphqlRequests()( - { - body: 'AppRevokeAccessScopes', - responseContent: responses.REVOKED_WITHOUT_ERROR, - }, - { - body: 'FetchAccessScopes', - responseContent: responses.WITH_GRANTED_AND_DECLARED, - }, - ); + await mockGraphqlRequests()({ + body: 'AppRevokeAccessScopes', + responseContent: responses.REVOKED_WITHOUT_ERROR, + }); + + // WHEN + const result = await scopes.revoke(['write_discounts', 'read_orders']); + + // THEN + expect(result).not.toBeUndefined(); + expect(result.revoked).toEqual(['write_discounts', 'read_orders']); +}); + +it('returns successfully with empty list when graphql returns an empty list for the revoke operation', async () => { + // GIVEN + const {scopes} = await setUpEmbeddedFlow(); + await mockGraphqlRequests()({ + body: 'AppRevokeAccessScopes', + responseContent: responses.REVOKED_NOTHING, + }); // WHEN const result = await scopes.revoke(['read_orders']); // THEN expect(result).not.toBeUndefined(); - expect(result.detail.granted).toEqual(['read_orders', 'write_customers']); - expect(result.detail.required).toEqual(['read_orders', 'read_reports']); - expect(result.detail.optional).toEqual(['write_customers']); + expect(result.revoked).toEqual([]); }); it('returns error if the list of scopes is empty', async () => { diff --git a/packages/apps/shopify-app-remix/src/server/authenticate/admin/scope/client/revoke-scopes.ts b/packages/apps/shopify-app-remix/src/server/authenticate/admin/scope/client/revoke-scopes.ts index 3a10c5954..5a216ec20 100644 --- a/packages/apps/shopify-app-remix/src/server/authenticate/admin/scope/client/revoke-scopes.ts +++ b/packages/apps/shopify-app-remix/src/server/authenticate/admin/scope/client/revoke-scopes.ts @@ -34,5 +34,5 @@ export async function revokeScopes( }); const resultContent = await revokeScopesResult.json(); - return resultContent.data; + return resultContent.data.appRevokeAccessScopes; } diff --git a/packages/apps/shopify-app-remix/src/server/authenticate/admin/scope/revoke.ts b/packages/apps/shopify-app-remix/src/server/authenticate/admin/scope/revoke.ts index cb6d44195..232fce397 100644 --- a/packages/apps/shopify-app-remix/src/server/authenticate/admin/scope/revoke.ts +++ b/packages/apps/shopify-app-remix/src/server/authenticate/admin/scope/revoke.ts @@ -4,8 +4,6 @@ import {AdminApiContext} from '../../../clients'; import type {BasicParams} from '../../../types'; import {revokeScopes} from './client/revoke-scopes'; -import {fetchScopeDetail} from './client/fetch-scopes-details'; -import {mapFetchScopeDetail} from './query'; export function revokeScopesFactory( params: BasicParams, @@ -15,13 +13,13 @@ export function revokeScopesFactory( return async function revoke(scopes: string[]) { const {logger} = params; + await validateScopes(scopes); + logger.debug('Revoke scopes: ', { shop: session.shop, scopes, }); - await validateScopes(scopes); - const revokeScopesResult = await revokeScopes(admin, scopes); if (revokeScopesResult.userErrors?.length > 0) { logger.error('Failed to revoke scopes: ', { @@ -37,9 +35,8 @@ export function revokeScopesFactory( }); } - const scopesDetail = await fetchScopeDetail(admin); return { - detail: mapFetchScopeDetail(scopesDetail), + revoked: revokeScopesResult.revoked.map((scope) => scope.handle), }; }; } diff --git a/packages/apps/shopify-app-remix/src/server/authenticate/admin/scope/types.ts b/packages/apps/shopify-app-remix/src/server/authenticate/admin/scope/types.ts index 2fcf324a7..c552e4cd3 100644 --- a/packages/apps/shopify-app-remix/src/server/authenticate/admin/scope/types.ts +++ b/packages/apps/shopify-app-remix/src/server/authenticate/admin/scope/types.ts @@ -5,7 +5,7 @@ export interface ScopesApiContext { } export interface RevokeResponse { - detail: ScopesDetail; + revoked: string[]; } export interface ScopesDetail { granted: string[];