From b742407d62f3f23473769b7c1d4d473484acc04f Mon Sep 17 00:00:00 2001 From: Richard Powell Date: Tue, 8 Oct 2024 15:21:36 -0400 Subject: [PATCH 01/13] Add a future flag to remove REST --- .../server/authenticate/admin/authenticate.ts | 2 +- .../admin/helpers/create-admin-api-context.ts | 6 +- .../admin/helpers/trigger-after-auth-hook.ts | 16 +- .../scope/client/fetch-scopes-details.ts | 6 +- .../admin/scope/client/revoke-scopes.ts | 6 +- .../authenticate/admin/scope/factory.ts | 5 +- .../server/authenticate/admin/scope/query.ts | 5 +- .../authenticate/admin/scope/request.ts | 10 +- .../server/authenticate/admin/scope/revoke.ts | 5 +- .../admin/strategies/auth-code-flow.ts | 2 +- .../src/server/authenticate/admin/types.ts | 2 +- .../server/authenticate/flow/authenticate.ts | 8 +- .../src/server/authenticate/flow/types.ts | 7 +- .../fulfillment-service/authenticate.ts | 8 +- .../authenticate/fulfillment-service/types.ts | 9 +- .../public/appProxy/authenticate.ts | 12 +- .../authenticate/public/appProxy/types.ts | 13 +- .../src/server/authenticate/public/factory.ts | 9 +- .../src/server/authenticate/public/types.ts | 8 +- .../authenticate/webhooks/authenticate.ts | 8 +- .../src/server/authenticate/webhooks/types.ts | 10 +- .../src/server/clients/admin/factory.ts | 12 +- .../src/server/clients/admin/types.ts | 218 +++++++++--------- .../src/server/config-types.ts | 16 +- .../src/server/future/flags.ts | 20 ++ .../src/server/shopify-app.ts | 14 +- .../src/server/types-contexts.ts | 18 +- .../shopify-app-remix/src/server/types.ts | 13 +- .../server/unauthenticated/admin/factory.ts | 6 +- .../src/server/unauthenticated/admin/types.ts | 9 +- .../src/server/unauthenticated/types.ts | 9 +- 31 files changed, 310 insertions(+), 182 deletions(-) diff --git a/packages/apps/shopify-app-remix/src/server/authenticate/admin/authenticate.ts b/packages/apps/shopify-app-remix/src/server/authenticate/admin/authenticate.ts index aa75601ee4..4bba384d69 100644 --- a/packages/apps/shopify-app-remix/src/server/authenticate/admin/authenticate.ts +++ b/packages/apps/shopify-app-remix/src/server/authenticate/admin/authenticate.ts @@ -87,7 +87,7 @@ export function authStrategyFactory< sessionToken?: JwtPayload, ): AdminContext { let context: AdminContextBase = { - admin: createAdminApiContext( + admin: createAdminApiContext( session, params, authStrategy.handleClientError(request), diff --git a/packages/apps/shopify-app-remix/src/server/authenticate/admin/helpers/create-admin-api-context.ts b/packages/apps/shopify-app-remix/src/server/authenticate/admin/helpers/create-admin-api-context.ts index 00caf4bb87..1a62f3ffbb 100644 --- a/packages/apps/shopify-app-remix/src/server/authenticate/admin/helpers/create-admin-api-context.ts +++ b/packages/apps/shopify-app-remix/src/server/authenticate/admin/helpers/create-admin-api-context.ts @@ -1,4 +1,5 @@ import {Session, ShopifyRestResources} from '@shopify/shopify-api'; +import type {AppConfigArg} from 'src/server/config-types'; import type {BasicParams} from '../../../types'; import { @@ -8,13 +9,14 @@ import { } from '../../../clients/admin'; export function createAdminApiContext< + ConfigArg extends AppConfigArg, Resources extends ShopifyRestResources = ShopifyRestResources, >( session: Session, params: BasicParams, handleClientError: HandleAdminClientError, -): AdminApiContext { - return adminClientFactory({ +): AdminApiContext { + return adminClientFactory({ session, params, handleClientError, diff --git a/packages/apps/shopify-app-remix/src/server/authenticate/admin/helpers/trigger-after-auth-hook.ts b/packages/apps/shopify-app-remix/src/server/authenticate/admin/helpers/trigger-after-auth-hook.ts index b27dd3f1a2..2d7fbcca3d 100644 --- a/packages/apps/shopify-app-remix/src/server/authenticate/admin/helpers/trigger-after-auth-hook.ts +++ b/packages/apps/shopify-app-remix/src/server/authenticate/admin/helpers/trigger-after-auth-hook.ts @@ -1,4 +1,6 @@ import {Session, ShopifyRestResources} from '@shopify/shopify-api'; +import type {AppConfigArg} from 'src/server/config-types'; +import {AdminApiContextWithRest} from 'src/server/clients'; import type {BasicParams} from '../../../types'; import {AuthorizationStrategy} from '../strategies/types'; @@ -6,6 +8,7 @@ import {AuthorizationStrategy} from '../strategies/types'; import {createAdminApiContext} from './create-admin-api-context'; export async function triggerAfterAuthHook< + ConfigArg extends AppConfigArg, Resources extends ShopifyRestResources = ShopifyRestResources, >( params: BasicParams, @@ -16,13 +19,16 @@ export async function triggerAfterAuthHook< const {config, logger} = params; if (config.hooks.afterAuth) { logger.info('Running afterAuth hook'); + + const admin = createAdminApiContext( + session, + params, + authStrategy.handleClientError(request), + ) as AdminApiContextWithRest; + await config.hooks.afterAuth({ session, - admin: createAdminApiContext( - session, - params, - authStrategy.handleClientError(request), - ), + admin, }); } } diff --git a/packages/apps/shopify-app-remix/src/server/authenticate/admin/scope/client/fetch-scopes-details.ts b/packages/apps/shopify-app-remix/src/server/authenticate/admin/scope/client/fetch-scopes-details.ts index d965269004..0ce158c68d 100644 --- a/packages/apps/shopify-app-remix/src/server/authenticate/admin/scope/client/fetch-scopes-details.ts +++ b/packages/apps/shopify-app-remix/src/server/authenticate/admin/scope/client/fetch-scopes-details.ts @@ -1,3 +1,5 @@ +import {AppConfigArg} from 'src/server/config-types'; + import {AdminApiContext} from '../../../../clients'; export interface FetchScopesDetailResponse { @@ -33,8 +35,8 @@ query FetchAccessScopes{ } }`; -export async function fetchScopeDetail( - admin: AdminApiContext, +export async function fetchScopeDetail( + admin: AdminApiContext, ): Promise { const fetchScopeDetailResult = await admin.graphql(FETCH_SCOPES_DETAIL_QUERY); 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 5a216ec208..3e07242fe6 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 @@ -1,3 +1,5 @@ +import {AppConfigArg} from 'src/server/config-types'; + import {AdminApiContext} from '../../../../clients'; export interface RevokeScopesResponse { @@ -23,8 +25,8 @@ mutation AppRevokeAccessScopes($scopes: [String!]!) { } }`; -export async function revokeScopes( - admin: AdminApiContext, +export async function revokeScopes( + admin: AdminApiContext, scopes: string[], ): Promise { const revokeScopesResult = await admin.graphql(REVOKE_SCOPE_MUTATION, { diff --git a/packages/apps/shopify-app-remix/src/server/authenticate/admin/scope/factory.ts b/packages/apps/shopify-app-remix/src/server/authenticate/admin/scope/factory.ts index 7e8300a404..6305f31c08 100644 --- a/packages/apps/shopify-app-remix/src/server/authenticate/admin/scope/factory.ts +++ b/packages/apps/shopify-app-remix/src/server/authenticate/admin/scope/factory.ts @@ -1,4 +1,5 @@ import {Session} from '@shopify/shopify-api'; +import {AppConfigArg} from 'src/server/config-types'; import {BasicParams} from '../../../types'; import {AdminApiContext} from '../../../clients'; @@ -8,10 +9,10 @@ import {requestScopesFactory} from './request'; import {queryScopesFactory} from './query'; import {revokeScopesFactory} from './revoke'; -export function scopesApiFactory( +export function scopesApiFactory( params: BasicParams, session: Session, - admin: AdminApiContext, + admin: AdminApiContext, ): ScopesApiContext { return { query: queryScopesFactory(params, session, admin), diff --git a/packages/apps/shopify-app-remix/src/server/authenticate/admin/scope/query.ts b/packages/apps/shopify-app-remix/src/server/authenticate/admin/scope/query.ts index 6bd7e8060f..63cd540993 100644 --- a/packages/apps/shopify-app-remix/src/server/authenticate/admin/scope/query.ts +++ b/packages/apps/shopify-app-remix/src/server/authenticate/admin/scope/query.ts @@ -1,4 +1,5 @@ import {AuthScopes, Session} from '@shopify/shopify-api'; +import {AppConfigArg} from 'src/server/config-types'; import {AdminApiContext} from '../../../clients'; import type {BasicParams} from '../../../types'; @@ -9,10 +10,10 @@ import { fetchScopeDetail, } from './client/fetch-scopes-details'; -export function queryScopesFactory( +export function queryScopesFactory( params: BasicParams, session: Session, - admin: AdminApiContext, + admin: AdminApiContext, ) { return async function queryScopes() { const {logger} = params; diff --git a/packages/apps/shopify-app-remix/src/server/authenticate/admin/scope/request.ts b/packages/apps/shopify-app-remix/src/server/authenticate/admin/scope/request.ts index faffc89dcf..d7cb3d54dd 100644 --- a/packages/apps/shopify-app-remix/src/server/authenticate/admin/scope/request.ts +++ b/packages/apps/shopify-app-remix/src/server/authenticate/admin/scope/request.ts @@ -1,4 +1,5 @@ import {AuthScopes, Session} from '@shopify/shopify-api'; +import {AppConfigArg} from 'src/server/config-types'; import type {BasicParams} from '../../../types'; import {redirectToInstallPage} from '../helpers/redirect-to-install-page'; @@ -6,10 +7,10 @@ import {AdminApiContext} from '../../../clients'; import {fetchScopeDetail} from './client/fetch-scopes-details'; -export function requestScopesFactory( +export function requestScopesFactory( params: BasicParams, session: Session, - admin: AdminApiContext, + admin: AdminApiContext, ) { return async function requestScopes(scopes: string[]) { const {logger} = params; @@ -22,7 +23,10 @@ export function requestScopesFactory( throw await redirectToInstallPage(params, session.shop, scopes); }; - async function alreadyGranted(scopes: string[], admin: AdminApiContext) { + async function alreadyGranted( + scopes: string[], + admin: AdminApiContext, + ) { const scopesDetail = await fetchScopeDetail(admin); const grantedScopes = scopesDetail.app.installation.accessScopes.map( (scope) => scope.handle, 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 232fce397a..b4c2b90cd6 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 @@ -1,14 +1,15 @@ import {Session} from '@shopify/shopify-api'; +import {AppConfigArg} from 'src/server/config-types'; import {AdminApiContext} from '../../../clients'; import type {BasicParams} from '../../../types'; import {revokeScopes} from './client/revoke-scopes'; -export function revokeScopesFactory( +export function revokeScopesFactory( params: BasicParams, session: Session, - admin: AdminApiContext, + admin: AdminApiContext, ) { return async function revoke(scopes: string[]) { const {logger} = params; diff --git a/packages/apps/shopify-app-remix/src/server/authenticate/admin/strategies/auth-code-flow.ts b/packages/apps/shopify-app-remix/src/server/authenticate/admin/strategies/auth-code-flow.ts index cbc787b949..fb5bf9f6ae 100644 --- a/packages/apps/shopify-app-remix/src/server/authenticate/admin/strategies/auth-code-flow.ts +++ b/packages/apps/shopify-app-remix/src/server/authenticate/admin/strategies/auth-code-flow.ts @@ -209,7 +209,7 @@ export class AuthCodeFlowStrategy< isOnline: session.isOnline, }); - await triggerAfterAuthHook( + await triggerAfterAuthHook( {api, config, logger}, session, request, diff --git a/packages/apps/shopify-app-remix/src/server/authenticate/admin/types.ts b/packages/apps/shopify-app-remix/src/server/authenticate/admin/types.ts index 58af51e50e..85771dbe0d 100644 --- a/packages/apps/shopify-app-remix/src/server/authenticate/admin/types.ts +++ b/packages/apps/shopify-app-remix/src/server/authenticate/admin/types.ts @@ -76,7 +76,7 @@ interface AdminContextInternal< /** * Methods for interacting with the GraphQL / REST Admin APIs for the store that made the request. */ - admin: AdminApiContext; + admin: AdminApiContext; /** * Billing methods for this store, based on the plans defined in the `billing` config option. diff --git a/packages/apps/shopify-app-remix/src/server/authenticate/flow/authenticate.ts b/packages/apps/shopify-app-remix/src/server/authenticate/flow/authenticate.ts index 5b6cf00bf7..8262ccdd3b 100644 --- a/packages/apps/shopify-app-remix/src/server/authenticate/flow/authenticate.ts +++ b/packages/apps/shopify-app-remix/src/server/authenticate/flow/authenticate.ts @@ -1,4 +1,5 @@ import {ShopifyRestResources} from '@shopify/shopify-api'; +import {AppConfigArg} from 'src/server/config-types'; import {adminClientFactory} from '../../clients/admin'; import {BasicParams} from '../../types'; @@ -6,13 +7,14 @@ import {BasicParams} from '../../types'; import type {AuthenticateFlow, FlowContext} from './types'; export function authenticateFlowFactory< + ConfigArg extends AppConfigArg, Resources extends ShopifyRestResources = ShopifyRestResources, ->(params: BasicParams): AuthenticateFlow { +>(params: BasicParams): AuthenticateFlow { const {api, config, logger} = params; return async function authenticate( request: Request, - ): Promise> { + ): Promise> { logger.info('Authenticating flow request'); if (request.method !== 'POST') { @@ -65,7 +67,7 @@ export function authenticateFlowFactory< return { session, payload, - admin: adminClientFactory({params, session}), + admin: adminClientFactory({params, session}), }; }; } diff --git a/packages/apps/shopify-app-remix/src/server/authenticate/flow/types.ts b/packages/apps/shopify-app-remix/src/server/authenticate/flow/types.ts index e9ee8200ac..2d4f75fc3b 100644 --- a/packages/apps/shopify-app-remix/src/server/authenticate/flow/types.ts +++ b/packages/apps/shopify-app-remix/src/server/authenticate/flow/types.ts @@ -1,8 +1,10 @@ import {Session, ShopifyRestResources} from '@shopify/shopify-api'; +import {AppConfigArg} from 'src/server/config-types'; import type {AdminApiContext} from '../../clients'; export interface FlowContext< + ConfigArg extends AppConfigArg, Resources extends ShopifyRestResources = ShopifyRestResources, > { /** @@ -82,9 +84,10 @@ export interface FlowContext< * } * ``` */ - admin: AdminApiContext; + admin: AdminApiContext; } export type AuthenticateFlow< + ConfigArg extends AppConfigArg, Resources extends ShopifyRestResources = ShopifyRestResources, -> = (request: Request) => Promise>; +> = (request: Request) => Promise>; diff --git a/packages/apps/shopify-app-remix/src/server/authenticate/fulfillment-service/authenticate.ts b/packages/apps/shopify-app-remix/src/server/authenticate/fulfillment-service/authenticate.ts index 92452e80ba..3d61d19318 100644 --- a/packages/apps/shopify-app-remix/src/server/authenticate/fulfillment-service/authenticate.ts +++ b/packages/apps/shopify-app-remix/src/server/authenticate/fulfillment-service/authenticate.ts @@ -1,4 +1,5 @@ import {ShopifyRestResources, ShopifyHeader} from '@shopify/shopify-api'; +import {AppConfigArg} from 'src/server/config-types'; import {adminClientFactory} from '../../clients/admin'; import {BasicParams} from '../../types'; @@ -10,13 +11,14 @@ import type { } from './types'; export function authenticateFulfillmentServiceFactory< + ConfigArg extends AppConfigArg, Resources extends ShopifyRestResources = ShopifyRestResources, ->(params: BasicParams): AuthenticateFulfillmentService { +>(params: BasicParams): AuthenticateFulfillmentService { const {api, logger} = params; return async function authenticate( request: Request, - ): Promise> { + ): Promise> { logger.info('Authenticating fulfillment service request'); if (request.method !== 'POST') { @@ -76,7 +78,7 @@ export function authenticateFulfillmentServiceFactory< return { session, payload, - admin: adminClientFactory({params, session}), + admin: adminClientFactory({params, session}), }; }; } diff --git a/packages/apps/shopify-app-remix/src/server/authenticate/fulfillment-service/types.ts b/packages/apps/shopify-app-remix/src/server/authenticate/fulfillment-service/types.ts index a45ecdf69f..a5dca3c02c 100644 --- a/packages/apps/shopify-app-remix/src/server/authenticate/fulfillment-service/types.ts +++ b/packages/apps/shopify-app-remix/src/server/authenticate/fulfillment-service/types.ts @@ -1,4 +1,5 @@ import {Session, ShopifyRestResources} from '@shopify/shopify-api'; +import {AppConfigArg} from 'src/server/config-types'; import type {AdminApiContext} from '../../clients'; @@ -7,6 +8,7 @@ export type FulfillmentServicePayload = Record & { }; export interface FulfillmentServiceContext< + ConfigArg extends AppConfigArg, Resources extends ShopifyRestResources = ShopifyRestResources, > { /** @@ -87,7 +89,7 @@ export interface FulfillmentServiceContext< * } * ``` */ - admin: AdminApiContext; + admin: AdminApiContext; /** * The payload from the fulfillment service request. @@ -114,5 +116,8 @@ export interface FulfillmentServiceContext< } export type AuthenticateFulfillmentService< + ConfigArg extends AppConfigArg, Resources extends ShopifyRestResources = ShopifyRestResources, -> = (request: Request) => Promise>; +> = ( + request: Request, +) => Promise>; diff --git a/packages/apps/shopify-app-remix/src/server/authenticate/public/appProxy/authenticate.ts b/packages/apps/shopify-app-remix/src/server/authenticate/public/appProxy/authenticate.ts index b5eaed9770..a01035b30d 100644 --- a/packages/apps/shopify-app-remix/src/server/authenticate/public/appProxy/authenticate.ts +++ b/packages/apps/shopify-app-remix/src/server/authenticate/public/appProxy/authenticate.ts @@ -1,4 +1,5 @@ import {ShopifyRestResources} from '@shopify/shopify-api'; +import {AppConfigArg} from 'src/server/config-types'; import {adminClientFactory, storefrontClientFactory} from '../../../clients'; import {BasicParams} from '../../../types'; @@ -11,13 +12,16 @@ import { } from './types'; export function authenticateAppProxyFactory< + ConfigArg extends AppConfigArg, Resources extends ShopifyRestResources, ->(params: BasicParams): AuthenticateAppProxy { +>(params: BasicParams): AuthenticateAppProxy { const {api, config, logger} = params; return async function authenticate( request, - ): Promise> { + ): Promise< + AppProxyContext | AppProxyContextWithSession + > { logger.info('Authenticating app proxy request'); const url = new URL(request.url); @@ -50,10 +54,10 @@ export function authenticateAppProxyFactory< return context; } - const context: AppProxyContextWithSession = { + const context: AppProxyContextWithSession = { liquid, session, - admin: adminClientFactory({params, session}), + admin: adminClientFactory({params, session}), storefront: storefrontClientFactory({params, session}), }; diff --git a/packages/apps/shopify-app-remix/src/server/authenticate/public/appProxy/types.ts b/packages/apps/shopify-app-remix/src/server/authenticate/public/appProxy/types.ts index 246017f762..ce3f31a5c4 100644 --- a/packages/apps/shopify-app-remix/src/server/authenticate/public/appProxy/types.ts +++ b/packages/apps/shopify-app-remix/src/server/authenticate/public/appProxy/types.ts @@ -1,10 +1,16 @@ import {Session, ShopifyRestResources} from '@shopify/shopify-api'; +import {AppConfigArg} from 'src/server/config-types'; import {AdminApiContext, StorefrontContext} from '../../../clients'; -export type AuthenticateAppProxy = ( +export type AuthenticateAppProxy< + ConfigArg extends AppConfigArg, + Resources extends ShopifyRestResources = ShopifyRestResources, +> = ( request: Request, -) => Promise; +) => Promise< + AppProxyContext | AppProxyContextWithSession +>; interface Options { /** @@ -113,6 +119,7 @@ export interface AppProxyContext extends Context { } export interface AppProxyContextWithSession< + ConfigArg extends AppConfigArg, Resources extends ShopifyRestResources = ShopifyRestResources, > extends Context { /** @@ -180,7 +187,7 @@ export interface AppProxyContextWithSession< * } * ``` */ - admin: AdminApiContext; + admin: AdminApiContext; /** * Method for interacting with the Shopify Storefront Graphql API for the store that made the request. diff --git a/packages/apps/shopify-app-remix/src/server/authenticate/public/factory.ts b/packages/apps/shopify-app-remix/src/server/authenticate/public/factory.ts index 141c29541c..ef4e3967b2 100644 --- a/packages/apps/shopify-app-remix/src/server/authenticate/public/factory.ts +++ b/packages/apps/shopify-app-remix/src/server/authenticate/public/factory.ts @@ -1,4 +1,5 @@ import {ShopifyRestResources} from '@shopify/shopify-api'; +import {AppConfigArg} from 'src/server/config-types'; import {BasicParams} from '../../types'; @@ -8,14 +9,18 @@ import {authenticateCustomerAccountFactory} from './customer-account/authenticat import {AuthenticatePublic} from './types'; export function authenticatePublicFactory< + ConfigArg extends AppConfigArg, Resources extends ShopifyRestResources, >(params: BasicParams) { const authenticateCheckout = authenticateCheckoutFactory(params); - const authenticateAppProxy = authenticateAppProxyFactory(params); + const authenticateAppProxy = authenticateAppProxyFactory< + ConfigArg, + Resources + >(params); const authenticateCustomerAccount = authenticateCustomerAccountFactory(params); - const context: AuthenticatePublic = { + const context: AuthenticatePublic = { checkout: authenticateCheckout, appProxy: authenticateAppProxy, customerAccount: authenticateCustomerAccount, diff --git a/packages/apps/shopify-app-remix/src/server/authenticate/public/types.ts b/packages/apps/shopify-app-remix/src/server/authenticate/public/types.ts index cfe5aedbbb..93db6da257 100644 --- a/packages/apps/shopify-app-remix/src/server/authenticate/public/types.ts +++ b/packages/apps/shopify-app-remix/src/server/authenticate/public/types.ts @@ -1,13 +1,15 @@ +import type {AppConfigArg} from 'src/server/config-types'; + import type {AuthenticateCheckout} from './checkout/types'; import type {AuthenticateAppProxy} from './appProxy/types'; -import {AuthenticateCustomerAccount} from './customer-account/types'; +import type {AuthenticateCustomerAccount} from './customer-account/types'; // Eventually this will be just the `{}` bit without `AuthenticateCheckout &` // We have this is because in v1 public WAS the only public authenticate method // But it became tightly coupled to authentictaing Checkout requests. // In V2 you will have only public.checkout() and public.appProxy(), no public() -export interface AuthenticatePublic { +export interface AuthenticatePublic { /** * Authenticate a request from a checkout extension * @@ -51,7 +53,7 @@ export interface AuthenticatePublic { * }; * ``` */ - appProxy: AuthenticateAppProxy; + appProxy: AuthenticateAppProxy; /** * Authenticate a request from a customer account extension diff --git a/packages/apps/shopify-app-remix/src/server/authenticate/webhooks/authenticate.ts b/packages/apps/shopify-app-remix/src/server/authenticate/webhooks/authenticate.ts index 5b190d22be..4303613eba 100644 --- a/packages/apps/shopify-app-remix/src/server/authenticate/webhooks/authenticate.ts +++ b/packages/apps/shopify-app-remix/src/server/authenticate/webhooks/authenticate.ts @@ -2,6 +2,7 @@ import { ShopifyRestResources, WebhookValidationErrorReason, } from '@shopify/shopify-api'; +import {AppConfigArg} from 'src/server/config-types'; import type {BasicParams} from '../../types'; import {adminClientFactory} from '../../clients'; @@ -15,14 +16,15 @@ import type { } from './types'; export function authenticateWebhookFactory< + ConfigArg extends AppConfigArg, Resources extends ShopifyRestResources, Topics extends string, ->(params: BasicParams): AuthenticateWebhook { +>(params: BasicParams): AuthenticateWebhook { const {api, logger} = params; return async function authenticate( request: Request, - ): Promise> { + ): Promise> { if (request.method !== 'POST') { logger.debug( 'Received a non-POST request for a webhook. Only POST requests are allowed.', @@ -69,7 +71,7 @@ export function authenticateWebhookFactory< return webhookContext; } - const admin = adminClientFactory({ + const admin = adminClientFactory({ params, session, handleClientError: handleClientErrorFactory({request}), diff --git a/packages/apps/shopify-app-remix/src/server/authenticate/webhooks/types.ts b/packages/apps/shopify-app-remix/src/server/authenticate/webhooks/types.ts index 407cc7dbfe..094727f7a6 100644 --- a/packages/apps/shopify-app-remix/src/server/authenticate/webhooks/types.ts +++ b/packages/apps/shopify-app-remix/src/server/authenticate/webhooks/types.ts @@ -1,4 +1,5 @@ import {Session, ShopifyRestResources} from '@shopify/shopify-api'; +import {AppConfigArg} from 'src/server/config-types'; import type {AdminApiContext} from '../../clients'; @@ -140,6 +141,7 @@ export interface WebhookContextWithoutSession } export interface WebhookContextWithSession< + ConfigArg extends AppConfigArg, Resources extends ShopifyRestResources, Topics = string | number | symbol, > extends Context { @@ -215,17 +217,19 @@ export interface WebhookContextWithSession< * } * ``` */ - admin: AdminApiContext; + admin: AdminApiContext; } export type WebhookContext< + ConfigArg extends AppConfigArg, Resources extends ShopifyRestResources, Topics = string | number | symbol, > = | WebhookContextWithoutSession - | WebhookContextWithSession; + | WebhookContextWithSession; export type AuthenticateWebhook< + ConfigArg extends AppConfigArg, Resources extends ShopifyRestResources, Topics = string | number | symbol, -> = (request: Request) => Promise>; +> = (request: Request) => Promise>; diff --git a/packages/apps/shopify-app-remix/src/server/clients/admin/factory.ts b/packages/apps/shopify-app-remix/src/server/clients/admin/factory.ts index 16de59eac9..5aa7b0aaa5 100644 --- a/packages/apps/shopify-app-remix/src/server/clients/admin/factory.ts +++ b/packages/apps/shopify-app-remix/src/server/clients/admin/factory.ts @@ -1,4 +1,5 @@ import {Session, ShopifyRestResources} from '@shopify/shopify-api'; +import type {AppConfigArg} from 'src/server/config-types'; import {BasicParams} from '../../types'; @@ -13,12 +14,19 @@ interface RestClientOptions { } export function adminClientFactory< + ConfigArg extends AppConfigArg, Resources extends ShopifyRestResources = ShopifyRestResources, >({ params, handleClientError, session, -}: RestClientOptions): AdminApiContext { +}: RestClientOptions): AdminApiContext { + if (params.config.future.removeRest) { + return { + graphql: graphqlClientFactory({params, session, handleClientError}), + } as AdminApiContext; + } + return { rest: restClientFactory({ params, @@ -26,5 +34,5 @@ export function adminClientFactory< handleClientError, }), graphql: graphqlClientFactory({params, session, handleClientError}), - }; + } as AdminApiContext; } diff --git a/packages/apps/shopify-app-remix/src/server/clients/admin/types.ts b/packages/apps/shopify-app-remix/src/server/clients/admin/types.ts index eb55b58742..77b6566242 100644 --- a/packages/apps/shopify-app-remix/src/server/clients/admin/types.ts +++ b/packages/apps/shopify-app-remix/src/server/clients/admin/types.ts @@ -1,5 +1,7 @@ import {Session, ShopifyRestResources} from '@shopify/shopify-api'; import {AdminOperations} from '@shopify/admin-api-client'; +import type {AppConfigArg} from 'src/server/config-types'; +import type {FeatureEnabled} from 'src/server/future/flags'; import {BasicParams} from '../../types'; import {GraphQLClient} from '../types'; @@ -18,9 +20,120 @@ export type HandleAdminClientError = (arg: { session: Session; }) => Promise; -export interface AdminApiContext< +export type AdminApiContext< + ConfigArg extends AppConfigArg, Resources extends ShopifyRestResources = ShopifyRestResources, -> { +> = + FeatureEnabled extends true + ? AdminApiContextWithoutRest + : AdminApiContextWithRest; + +export interface AdminApiContextWithoutRest { + /** + * Methods for interacting with the Shopify Admin GraphQL API + * + * {@link https://shopify.dev/docs/api/admin-graphql} + * {@link https://github.com/Shopify/shopify-app-js/blob/main/packages/apps/shopify-api/docs/reference/clients/Graphql.md} + * + * @example + * Querying the GraphQL API. + * Use `admin.graphql` to make query / mutation requests. + * ```ts + * // /app/routes/**\/*.ts + * import { ActionFunctionArgs } from "@remix-run/node"; + * import { authenticate } from "../shopify.server"; + * + * export const action = async ({ request }: ActionFunctionArgs) => { + * const { admin } = await authenticate.admin(request); + * + * const response = await admin.graphql( + * `#graphql + * mutation populateProduct($input: ProductInput!) { + * productCreate(input: $input) { + * product { + * id + * } + * } + * }`, + * { + * variables: { + * input: { title: "Product Name" }, + * }, + * }, + * ); + * + * const productData = await response.json(); + * return json({ + * productId: productData.data?.productCreate?.product?.id, + * }); + * } + * ``` + * + * ```ts + * // /app/shopify.server.ts + * import { shopifyApp } from "@shopify/shopify-app-remix/server"; + * + * const shopify = shopifyApp({ + * // ... + * }); + * export default shopify; + * export const authenticate = shopify.authenticate; + * ``` + * + * @example + * Handling GraphQL errors. + * Catch `GraphqlQueryError` errors to see error messages from the API. + * ```ts + * // /app/routes/**\/*.ts + * import { ActionFunctionArgs } from "@remix-run/node"; + * import { authenticate } from "../shopify.server"; + * + * export const action = async ({ request }: ActionFunctionArgs) => { + * const { admin } = await authenticate.admin(request); + * + * try { + * const response = await admin.graphql( + * `#graphql + * query incorrectQuery { + * products(first: 10) { + * nodes { + * not_a_field + * } + * } + * }`, + * ); + * + * return json({ data: await response.json() }); + * } catch (error) { + * if (error instanceof GraphqlQueryError) { + * // error.body.errors: + * // { graphQLErrors: [ + * // { message: "Field 'not_a_field' doesn't exist on type 'Product'" } + * // ] } + * return json({ errors: error.body?.errors }, { status: 500 }); + * } + * return json({ message: "An error occurred" }, { status: 500 }); + * } + * } + * ``` + * + * ```ts + * // /app/shopify.server.ts + * import { shopifyApp } from "@shopify/shopify-app-remix/server"; + * + * const shopify = shopifyApp({ + * // ... + * }); + * export default shopify; + * export const authenticate = shopify.authenticate; + * ``` + */ + graphql: GraphQLClient; +} + +export interface AdminApiContextWithRest< + Resources extends ShopifyRestResources = ShopifyRestResources, +> extends AdminApiContextWithoutRest { /** * Methods for interacting with the Shopify Admin REST API * @@ -145,105 +258,4 @@ export interface AdminApiContext< * ``` */ rest: RestClientWithResources; - - /** - * Methods for interacting with the Shopify Admin GraphQL API - * - * {@link https://shopify.dev/docs/api/admin-graphql} - * {@link https://github.com/Shopify/shopify-app-js/blob/main/packages/apps/shopify-api/docs/reference/clients/Graphql.md} - * - * @example - * Querying the GraphQL API. - * Use `admin.graphql` to make query / mutation requests. - * ```ts - * // /app/routes/**\/*.ts - * import { ActionFunctionArgs } from "@remix-run/node"; - * import { authenticate } from "../shopify.server"; - * - * export const action = async ({ request }: ActionFunctionArgs) => { - * const { admin } = await authenticate.admin(request); - * - * const response = await admin.graphql( - * `#graphql - * mutation populateProduct($input: ProductInput!) { - * productCreate(input: $input) { - * product { - * id - * } - * } - * }`, - * { - * variables: { - * input: { title: "Product Name" }, - * }, - * }, - * ); - * - * const productData = await response.json(); - * return json({ - * productId: productData.data?.productCreate?.product?.id, - * }); - * } - * ``` - * - * ```ts - * // /app/shopify.server.ts - * import { shopifyApp } from "@shopify/shopify-app-remix/server"; - * - * const shopify = shopifyApp({ - * // ... - * }); - * export default shopify; - * export const authenticate = shopify.authenticate; - * ``` - * - * @example - * Handling GraphQL errors. - * Catch `GraphqlQueryError` errors to see error messages from the API. - * ```ts - * // /app/routes/**\/*.ts - * import { ActionFunctionArgs } from "@remix-run/node"; - * import { authenticate } from "../shopify.server"; - * - * export const action = async ({ request }: ActionFunctionArgs) => { - * const { admin } = await authenticate.admin(request); - * - * try { - * const response = await admin.graphql( - * `#graphql - * query incorrectQuery { - * products(first: 10) { - * nodes { - * not_a_field - * } - * } - * }`, - * ); - * - * return json({ data: await response.json() }); - * } catch (error) { - * if (error instanceof GraphqlQueryError) { - * // error.body.errors: - * // { graphQLErrors: [ - * // { message: "Field 'not_a_field' doesn't exist on type 'Product'" } - * // ] } - * return json({ errors: error.body?.errors }, { status: 500 }); - * } - * return json({ message: "An error occurred" }, { status: 500 }); - * } - * } - * ``` - * - * ```ts - * // /app/shopify.server.ts - * import { shopifyApp } from "@shopify/shopify-app-remix/server"; - * - * const shopify = shopifyApp({ - * // ... - * }); - * export default shopify; - * export const authenticate = shopify.authenticate; - * ``` - */ - graphql: GraphQLClient; } diff --git a/packages/apps/shopify-app-remix/src/server/config-types.ts b/packages/apps/shopify-app-remix/src/server/config-types.ts index b688fc2081..a84dbb06d3 100644 --- a/packages/apps/shopify-app-remix/src/server/config-types.ts +++ b/packages/apps/shopify-app-remix/src/server/config-types.ts @@ -172,7 +172,7 @@ export interface AppConfigArg< * }); * ``` */ - hooks?: HooksConfig; + hooks?: HooksConfig, Resources>; /** * Does your app render embedded inside the Shopify Admin or on its own. @@ -279,7 +279,10 @@ export interface AuthConfig { export type WebhookConfig = Record; -interface HooksConfig { +interface HooksConfig< + ConfigArg extends AppConfigArg = AppConfigArg, + Resources extends ShopifyRestResources = ShopifyRestResources, +> { /** * A function to call after a merchant installs your app * @@ -303,12 +306,15 @@ interface HooksConfig { * }); * ``` */ - afterAuth?: (options: AfterAuthOptions) => void | Promise; + afterAuth?: ( + options: AfterAuthOptions, + ) => void | Promise; } export interface AfterAuthOptions< - R extends ShopifyRestResources = ShopifyRestResources, + ConfigArg extends AppConfigArg, + Resources extends ShopifyRestResources = ShopifyRestResources, > { session: Session; - admin: AdminApiContext; + admin: AdminApiContext; } diff --git a/packages/apps/shopify-app-remix/src/server/future/flags.ts b/packages/apps/shopify-app-remix/src/server/future/flags.ts index a9db7951e2..54d938ef25 100644 --- a/packages/apps/shopify-app-remix/src/server/future/flags.ts +++ b/packages/apps/shopify-app-remix/src/server/future/flags.ts @@ -25,6 +25,26 @@ export interface FutureFlags { * @default false */ wip_optionalScopesApi?: boolean; + + /** + * When enabled, methods for interacting with the admin REST API will not be returned. + * + * This affects: + * + * * `authenticate.admin(request)` + * * `authenticate.webhook(request)` + * * `authenticate.flow(request)` + * * `authenticate.appProxy(request)` + * * `authenticate.fulfillmentService(request)` + * * `unauthenticated.admin(shop)` + * + * In a future release we will remove REST from the package completely. + * + * Please see: [https://www.shopify.com/ca/partners/blog/all-in-on-graphql](https://www.shopify.com/ca/partners/blog/all-in-on-graphql) + * + * @default false + */ + removeRest?: boolean; } // When adding new flags, use this format: diff --git a/packages/apps/shopify-app-remix/src/server/shopify-app.ts b/packages/apps/shopify-app-remix/src/server/shopify-app.ts index 548c093ba9..465845ff0b 100644 --- a/packages/apps/shopify-app-remix/src/server/shopify-app.ts +++ b/packages/apps/shopify-app-remix/src/server/shopify-app.ts @@ -99,14 +99,16 @@ export function shopifyApp< registerWebhooks: registerWebhooksFactory(params), authenticate: { admin: authStrategy, - flow: authenticateFlowFactory(params), - public: authenticatePublicFactory(params), - fulfillmentService: - authenticateFulfillmentServiceFactory(params), - webhook: authenticateWebhookFactory(params), + flow: authenticateFlowFactory(params), + public: authenticatePublicFactory(params), + fulfillmentService: authenticateFulfillmentServiceFactory< + Config, + Resources + >(params), + webhook: authenticateWebhookFactory(params), }, unauthenticated: { - admin: unauthenticatedAdminContextFactory(params), + admin: unauthenticatedAdminContextFactory(params), storefront: unauthenticatedStorefrontContextFactory(params), }, }; diff --git a/packages/apps/shopify-app-remix/src/server/types-contexts.ts b/packages/apps/shopify-app-remix/src/server/types-contexts.ts index 41dbd249eb..9e6accafdf 100644 --- a/packages/apps/shopify-app-remix/src/server/types-contexts.ts +++ b/packages/apps/shopify-app-remix/src/server/types-contexts.ts @@ -19,6 +19,7 @@ import type { import type {CheckoutContext as ICheckoutContext} from './authenticate/public/checkout/types'; import type {CustomerAccountContext as ICustomerAccountContext} from './authenticate/public/customer-account/types'; import type { + AdminApiContextWithRest, AdminApiContext as IAdminApiContext, StorefrontContext as IStorefrontContext, } from './clients'; @@ -35,6 +36,7 @@ type DefaultApp = ShopifyApp; export type UnauthenticatedAdminContext = IUnauthenticatedAdminContext< + ShopifyConfig, ConfigComponents>['resources'] >; @@ -50,31 +52,39 @@ export type UnauthenticatedStorefrontContext<_App = DefaultApp> = IUnauthenticatedStorefrontContext; export type FlowContext = IFlowContext< + ShopifyConfig, ConfigComponents>['resources'] >; export type FulfillmentServiceContext = - IFulfillmentServiceContext>['resources']>; + IFulfillmentServiceContext< + ShopifyConfig, + ConfigComponents>['resources'] + >; -export type AppProxyContext<_App = DefaultApp> = +export type AppProxyContext = | IAppProxyContext - | IAppProxyContextWithSession; + | IAppProxyContextWithSession>; export type CheckoutContext<_App = DefaultApp> = ICheckoutContext; export type CustomerAccountContext<_App = DefaultApp> = ICustomerAccountContext; export type WebhookContext = IWebhookContext< + ShopifyConfig, ConfigComponents>['resources'], string >; // Extra types for the Admin API context export type AdminApiContext = IAdminApiContext< + ShopifyConfig, ConfigComponents>['resources'] >; -export type AdminRestClient = AdminApiContext['rest']; +export type AdminRestClient = AdminApiContextWithRest< + ShopifyConfig +>['rest']; export type AdminGraphqlClient = AdminApiContext['graphql']; diff --git a/packages/apps/shopify-app-remix/src/server/types.ts b/packages/apps/shopify-app-remix/src/server/types.ts index 404704fd34..a651f4fcc0 100644 --- a/packages/apps/shopify-app-remix/src/server/types.ts +++ b/packages/apps/shopify-app-remix/src/server/types.ts @@ -141,7 +141,7 @@ interface Authenticate { * export const authenticate = shopify.authenticate; * ``` */ - flow: AuthenticateFlow>; + flow: AuthenticateFlow>; /** * Authenticate a request from a fulfillment service and get back an authenticated context. @@ -176,7 +176,10 @@ interface Authenticate { * } * ``` * */ - fulfillmentService: AuthenticateFulfillmentService>; + fulfillmentService: AuthenticateFulfillmentService< + Config, + RestResourcesType + >; /** * Authenticate a public request and get back a session token. @@ -197,7 +200,7 @@ interface Authenticate { * } * ``` */ - public: AuthenticatePublic; + public: AuthenticatePublic; /** * Authenticate a Shopify webhook request, get back an authenticated admin context and details on the webhook request @@ -267,7 +270,7 @@ interface Authenticate { * }); * ``` */ - webhook: AuthenticateWebhook, string>; + webhook: AuthenticateWebhook, string>; } export interface ShopifyAppBase { @@ -438,7 +441,7 @@ export interface ShopifyAppBase { * } * ``` */ - unauthenticated: Unauthenticated>; + unauthenticated: Unauthenticated>; } export interface ShopifyAppLogin { diff --git a/packages/apps/shopify-app-remix/src/server/unauthenticated/admin/factory.ts b/packages/apps/shopify-app-remix/src/server/unauthenticated/admin/factory.ts index fb7a285484..10d1f81333 100644 --- a/packages/apps/shopify-app-remix/src/server/unauthenticated/admin/factory.ts +++ b/packages/apps/shopify-app-remix/src/server/unauthenticated/admin/factory.ts @@ -1,4 +1,5 @@ import {ShopifyRestResources} from '@shopify/shopify-api'; +import {AppConfigArg} from 'src/server/config-types'; import {createOrLoadOfflineSession} from '../../authenticate/helpers/create-or-load-offline-session'; import {SessionNotFoundError} from '../../errors'; @@ -8,11 +9,12 @@ import {adminClientFactory} from '../../clients/admin'; import {UnauthenticatedAdminContext} from './types'; export function unauthenticatedAdminContextFactory< + ConfigArg extends AppConfigArg, Resources extends ShopifyRestResources, >(params: BasicParams) { return async ( shop: string, - ): Promise> => { + ): Promise> => { const session = await createOrLoadOfflineSession(shop, params); if (!session) { @@ -23,7 +25,7 @@ export function unauthenticatedAdminContextFactory< return { session, - admin: adminClientFactory({params, session}), + admin: adminClientFactory({params, session}), }; }; } diff --git a/packages/apps/shopify-app-remix/src/server/unauthenticated/admin/types.ts b/packages/apps/shopify-app-remix/src/server/unauthenticated/admin/types.ts index 6ae7311883..e506f28522 100644 --- a/packages/apps/shopify-app-remix/src/server/unauthenticated/admin/types.ts +++ b/packages/apps/shopify-app-remix/src/server/unauthenticated/admin/types.ts @@ -1,8 +1,10 @@ import {Session, ShopifyRestResources} from '@shopify/shopify-api'; +import {AppConfigArg} from 'src/server/config-types'; import {AdminApiContext} from '../../clients'; export interface UnauthenticatedAdminContext< + ConfigArg extends AppConfigArg, Resources extends ShopifyRestResources, > { /** @@ -109,9 +111,12 @@ export interface UnauthenticatedAdminContext< * export const unauthenticated = shopify.unauthenticated; * ``` */ - admin: AdminApiContext; + admin: AdminApiContext; } export type GetUnauthenticatedAdminContext< + ConfigArg extends AppConfigArg, Resources extends ShopifyRestResources, -> = (shop: string) => Promise>; +> = ( + shop: string, +) => Promise>; diff --git a/packages/apps/shopify-app-remix/src/server/unauthenticated/types.ts b/packages/apps/shopify-app-remix/src/server/unauthenticated/types.ts index 9183bede6a..d690b11245 100644 --- a/packages/apps/shopify-app-remix/src/server/unauthenticated/types.ts +++ b/packages/apps/shopify-app-remix/src/server/unauthenticated/types.ts @@ -1,9 +1,14 @@ import type {ShopifyRestResources} from '@shopify/shopify-api'; +import type {AppConfigArg} from '../config-types'; + import type {GetUnauthenticatedAdminContext} from './admin/types'; import type {GetUnauthenticatedStorefrontContext} from './storefront/types'; -export interface Unauthenticated { +export interface Unauthenticated< + ConfigArg extends AppConfigArg, + Resources extends ShopifyRestResources, +> { /** * Get an admin context by passing a shop * @@ -38,7 +43,7 @@ export interface Unauthenticated { * } * ``` */ - admin: GetUnauthenticatedAdminContext; + admin: GetUnauthenticatedAdminContext; /** * Get a storefront context by passing a shop From ccbf02cbcc79d21d0949af6d759a5a6d71e000bb Mon Sep 17 00:00:00 2001 From: Richard Powell Date: Mon, 21 Oct 2024 16:27:37 -0400 Subject: [PATCH 02/13] Make all existing tests pass with the new flag --- .../__test-helpers/expect-admin-api-client.ts | 3 +- .../__test-helpers/setup-embedded-flow.ts | 16 ++++--- .../server/__test-helpers/setup-fetch-flow.ts | 16 ++++--- .../__test-helpers/setup-non-embedded-flow.ts | 9 +++- .../src/server/__test-helpers/test-config.ts | 1 + .../src/server/__tests__/shopify-app.test.ts | 7 ++- .../admin/__tests__/admin-client.test.ts | 24 ++++++---- .../session-token-header-path.test.ts | 12 +++-- .../auth-code-flow/admin-client.test.ts | 24 ++++++---- .../auth-code-flow/authenticate.test.ts | 14 +++--- .../merchant-custom/admin-client.test.ts | 31 ++++++++----- .../token-exchange/admin-client.test.ts | 45 +++++++++++++------ .../token-exchange/authenticate.test.ts | 5 ++- .../flow/__tests__/authenticate.test.ts | 5 ++- .../__tests__/authenticate.test.ts | 5 ++- .../appProxy/__tests__/authenticate.test.ts | 5 ++- .../webhooks/__tests__/authenticate.test.ts | 5 ++- .../admin/__tests__/factory.test.ts | 14 +++--- 18 files changed, 164 insertions(+), 77 deletions(-) diff --git a/packages/apps/shopify-app-remix/src/server/__test-helpers/expect-admin-api-client.ts b/packages/apps/shopify-app-remix/src/server/__test-helpers/expect-admin-api-client.ts index d78d1e33bf..11159750a8 100644 --- a/packages/apps/shopify-app-remix/src/server/__test-helpers/expect-admin-api-client.ts +++ b/packages/apps/shopify-app-remix/src/server/__test-helpers/expect-admin-api-client.ts @@ -2,6 +2,7 @@ import {Session} from '@shopify/shopify-api'; import {LATEST_API_VERSION} from '..'; import type {AdminApiContext} from '../clients'; +import {AppConfigArg} from '../config-types'; import {mockExternalRequest} from './request-mock'; import {TEST_SHOP} from './const'; @@ -10,7 +11,7 @@ const REQUEST_URL = `https://${TEST_SHOP}/admin/api/${LATEST_API_VERSION}/custom export function expectAdminApiClient( factory: () => Promise<{ - admin: AdminApiContext; + admin: AdminApiContext; expectedSession: Session; actualSession: Session; }>, diff --git a/packages/apps/shopify-app-remix/src/server/__test-helpers/setup-embedded-flow.ts b/packages/apps/shopify-app-remix/src/server/__test-helpers/setup-embedded-flow.ts index 0bee4ff1d9..f421aa24ab 100644 --- a/packages/apps/shopify-app-remix/src/server/__test-helpers/setup-embedded-flow.ts +++ b/packages/apps/shopify-app-remix/src/server/__test-helpers/setup-embedded-flow.ts @@ -8,12 +8,16 @@ import {setUpValidSession} from './setup-valid-session'; import {testConfig} from './test-config'; export async function setUpEmbeddedFlow() { - const shopify = shopifyApp( - testConfig({ - future: {unstable_newEmbeddedAuthStrategy: false}, + const shopify = shopifyApp({ + ...testConfig({ restResources, }), - ); + future: { + removeRest: false, + unstable_newEmbeddedAuthStrategy: false, + wip_optionalScopesApi: true, + }, + }); const expectedSession = await setUpValidSession(shopify.sessionStorage); const {token} = getJwt(); @@ -21,10 +25,12 @@ export async function setUpEmbeddedFlow() { `${APP_URL}?embedded=1&shop=${TEST_SHOP}&host=${BASE64_HOST}&id_token=${token}`, ); + const result = await shopify.authenticate.admin(request); + return { shopify, expectedSession, - ...(await shopify.authenticate.admin(request)), + ...result, request, }; } diff --git a/packages/apps/shopify-app-remix/src/server/__test-helpers/setup-fetch-flow.ts b/packages/apps/shopify-app-remix/src/server/__test-helpers/setup-fetch-flow.ts index c597ea3e5e..92598a8c61 100644 --- a/packages/apps/shopify-app-remix/src/server/__test-helpers/setup-fetch-flow.ts +++ b/packages/apps/shopify-app-remix/src/server/__test-helpers/setup-fetch-flow.ts @@ -8,14 +8,16 @@ import {setUpValidSession} from './setup-valid-session'; import {testConfig} from './test-config'; export async function setUpFetchFlow(flags?: { - unstable_newEmbeddedAuthStrategy: boolean; + unstable_newEmbeddedAuthStrategy?: boolean; + wip_optionalScopesApi?: boolean; }) { - const shopify = shopifyApp( - testConfig({ - future: {...flags}, + const shopify = shopifyApp({ + ...testConfig({ restResources, }), - ); + future: {...flags, removeRest: false, wip_optionalScopesApi: true}, + }); + await setUpValidSession(shopify.sessionStorage); const {token} = getJwt(); @@ -23,8 +25,10 @@ export async function setUpFetchFlow(flags?: { headers: {Authorization: `Bearer ${token}`}, }); + const result = await shopify.authenticate.admin(request); + return { shopify, - ...(await shopify.authenticate.admin(request)), + ...result, }; } diff --git a/packages/apps/shopify-app-remix/src/server/__test-helpers/setup-non-embedded-flow.ts b/packages/apps/shopify-app-remix/src/server/__test-helpers/setup-non-embedded-flow.ts index e76aede8bd..e928bd59d6 100644 --- a/packages/apps/shopify-app-remix/src/server/__test-helpers/setup-non-embedded-flow.ts +++ b/packages/apps/shopify-app-remix/src/server/__test-helpers/setup-non-embedded-flow.ts @@ -9,7 +9,10 @@ import {signRequestCookie} from './sign-request-cookie'; import {testConfig} from './test-config'; export async function setUpNonEmbeddedFlow() { - const shopify = shopifyApp(testConfig({restResources, isEmbeddedApp: false})); + const shopify = shopifyApp({ + ...testConfig({restResources, isEmbeddedApp: false}), + future: {removeRest: false, wip_optionalScopesApi: true}, + }); const session = await setUpValidSession(shopify.sessionStorage); const request = new Request(`${APP_URL}?shop=${TEST_SHOP}`); @@ -19,9 +22,11 @@ export async function setUpNonEmbeddedFlow() { cookieValue: session.id, }); + const result = await shopify.authenticate.admin(request); + return { shopify, - ...(await shopify.authenticate.admin(request)), + ...result, request, session, }; diff --git a/packages/apps/shopify-app-remix/src/server/__test-helpers/test-config.ts b/packages/apps/shopify-app-remix/src/server/__test-helpers/test-config.ts index 6c469f065c..0096937e39 100644 --- a/packages/apps/shopify-app-remix/src/server/__test-helpers/test-config.ts +++ b/packages/apps/shopify-app-remix/src/server/__test-helpers/test-config.ts @@ -15,6 +15,7 @@ import type {FutureFlags, FutureFlagOptions} from '../future/flags'; const TEST_FUTURE_FLAGS: Required<{[key in keyof FutureFlags]: true}> = { unstable_newEmbeddedAuthStrategy: true, wip_optionalScopesApi: true, + removeRest: true, } as const; // Override the helper's future flags and logger settings for our purposes diff --git a/packages/apps/shopify-app-remix/src/server/__tests__/shopify-app.test.ts b/packages/apps/shopify-app-remix/src/server/__tests__/shopify-app.test.ts index 1e1e9ad486..36b39d4a22 100644 --- a/packages/apps/shopify-app-remix/src/server/__tests__/shopify-app.test.ts +++ b/packages/apps/shopify-app-remix/src/server/__tests__/shopify-app.test.ts @@ -25,9 +25,12 @@ describe('shopifyApp', () => { }); /* eslint-enable no-process-env */ - it('can create shopify object', () => { + it('can create shopify object', async () => { // GIVEN - const shopify = shopifyApp(testConfig()); + const shopify = shopifyApp({ + ...testConfig(), + future: {removeRest: false}, + }); // THEN expect(shopify).toBeDefined(); diff --git a/packages/apps/shopify-app-remix/src/server/authenticate/admin/__tests__/admin-client.test.ts b/packages/apps/shopify-app-remix/src/server/authenticate/admin/__tests__/admin-client.test.ts index 0228438601..600ccabbf3 100644 --- a/packages/apps/shopify-app-remix/src/server/authenticate/admin/__tests__/admin-client.test.ts +++ b/packages/apps/shopify-app-remix/src/server/authenticate/admin/__tests__/admin-client.test.ts @@ -14,7 +14,6 @@ import { mockGraphqlRequest, setUpNonEmbeddedFlow, } from '../../../__test-helpers'; -import {AdminApiContext} from '../../../clients'; describe('admin.authenticate context', () => { expectAdminApiClient(async () => { @@ -62,14 +61,18 @@ describe('admin.authenticate context', () => { { testGroup: 'REST client', mockRequest: mockRestRequest, - action: async (admin: AdminApiContext, _session: Session) => - admin.rest.get({path: '/customers.json'}), + action: async ( + admin: Awaited>['admin'], + _session: Session, + ) => admin.rest.get({path: '/customers.json'}), }, { testGroup: 'REST resources', mockRequest: mockRestRequest, - action: async (admin: AdminApiContext, session: Session) => - admin.rest.resources.Customer.all({session}), + action: async ( + admin: Awaited>['admin'], + session: Session, + ) => admin.rest.resources.Customer.all({session}), }, ])( '$testGroup re-authentication', @@ -95,13 +98,18 @@ describe('admin.authenticate context', () => { { testGroup: 'GraphQL client', mockRequest: mockGraphqlRequest(), - action: async (admin: AdminApiContext, _session: Session) => - admin.graphql('{ shop { name } }'), + action: async ( + admin: Awaited>['admin'], + _session: Session, + ) => admin.graphql('{ shop { name } }'), }, { testGroup: 'GraphQL client with options', mockRequest: mockGraphqlRequest('2021-01' as ApiVersion), - action: async (admin: AdminApiContext, _session: Session) => + action: async ( + admin: Awaited>['admin'], + _session: Session, + ) => admin.graphql( 'mutation myMutation($ID: String!) { shop(ID: $ID) { name } }', { diff --git a/packages/apps/shopify-app-remix/src/server/authenticate/admin/__tests__/session-token-header-path.test.ts b/packages/apps/shopify-app-remix/src/server/authenticate/admin/__tests__/session-token-header-path.test.ts index a1c8ba7d5e..29ad79ae03 100644 --- a/packages/apps/shopify-app-remix/src/server/authenticate/admin/__tests__/session-token-header-path.test.ts +++ b/packages/apps/shopify-app-remix/src/server/authenticate/admin/__tests__/session-token-header-path.test.ts @@ -36,7 +36,10 @@ describe('authorize.session token header path', () => { (isOnline) => { it('returns context when session exists for embedded apps', async () => { // GIVEN - const shopify = shopifyApp(testConfig({useOnlineTokens: isOnline})); + const shopify = shopifyApp({ + ...testConfig({useOnlineTokens: isOnline}), + future: {removeRest: false}, + }); const testSession = await setUpValidSession(shopify.sessionStorage, { isOnline, @@ -58,9 +61,10 @@ describe('authorize.session token header path', () => { it('returns context when session exists for non-embedded apps', async () => { // GIVEN - const shopify = shopifyApp( - testConfig({isEmbeddedApp: false, useOnlineTokens: isOnline}), - ); + const shopify = shopifyApp({ + ...testConfig({isEmbeddedApp: false, useOnlineTokens: isOnline}), + future: {removeRest: false}, + }); let testSession: Session; testSession = await setUpValidSession(shopify.sessionStorage); diff --git a/packages/apps/shopify-app-remix/src/server/authenticate/admin/strategies/__tests__/auth-code-flow/admin-client.test.ts b/packages/apps/shopify-app-remix/src/server/authenticate/admin/strategies/__tests__/auth-code-flow/admin-client.test.ts index 19aa642c68..8d4ab6e33c 100644 --- a/packages/apps/shopify-app-remix/src/server/authenticate/admin/strategies/__tests__/auth-code-flow/admin-client.test.ts +++ b/packages/apps/shopify-app-remix/src/server/authenticate/admin/strategies/__tests__/auth-code-flow/admin-client.test.ts @@ -13,7 +13,6 @@ import { setUpNonEmbeddedFlow, } from '../../../../../__test-helpers'; import {REAUTH_URL_HEADER} from '../../../../const'; -import {AdminApiContext} from '../../../../../clients'; describe('admin.authenticate context', () => { expectAdminApiClient(async () => { @@ -29,25 +28,34 @@ describe('admin.authenticate context', () => { { testGroup: 'REST client', mockRequest: mockRestRequest, - action: async (admin: AdminApiContext, _session: Session) => - admin.rest.get({path: '/customers.json'}), + action: async ( + admin: Awaited>['admin'], + _session: Session, + ) => admin.rest.get({path: '/customers.json'}), }, { testGroup: 'REST resources', mockRequest: mockRestRequest, - action: async (admin: AdminApiContext, session: Session) => - admin.rest.resources.Customer.all({session}), + action: async ( + admin: Awaited>['admin'], + session: Session, + ) => admin.rest.resources.Customer.all({session}), }, { testGroup: 'GraphQL client', mockRequest: mockGraphqlRequest(), - action: async (admin: AdminApiContext, _session: Session) => - admin.graphql('{ shop { name } }'), + action: async ( + admin: Awaited>['admin'], + _session: Session, + ) => admin.graphql('{ shop { name } }'), }, { testGroup: 'GraphQL client with options', mockRequest: mockGraphqlRequest('2021-01' as ApiVersion), - action: async (admin: AdminApiContext, _session: Session) => + action: async ( + admin: Awaited>['admin'], + _session: Session, + ) => admin.graphql( 'mutation myMutation($ID: String!) { shop(ID: $ID) { name } }', { diff --git a/packages/apps/shopify-app-remix/src/server/authenticate/admin/strategies/__tests__/auth-code-flow/authenticate.test.ts b/packages/apps/shopify-app-remix/src/server/authenticate/admin/strategies/__tests__/auth-code-flow/authenticate.test.ts index 93ac46f027..971648b8e2 100644 --- a/packages/apps/shopify-app-remix/src/server/authenticate/admin/strategies/__tests__/auth-code-flow/authenticate.test.ts +++ b/packages/apps/shopify-app-remix/src/server/authenticate/admin/strategies/__tests__/auth-code-flow/authenticate.test.ts @@ -97,13 +97,14 @@ describe('authenticate', () => { (isOnline) => { it('returns the context if the session is valid and the app is embedded', async () => { // GIVEN - const shopify = shopifyApp( - testConfig({ + const shopify = shopifyApp({ + ...testConfig({ useOnlineTokens: isOnline, future: {unstable_newEmbeddedAuthStrategy: false}, isEmbeddedApp: true, }), - ); + future: {removeRest: false}, + }); let testSession: Session; testSession = await setUpValidSession(shopify.sessionStorage); @@ -128,11 +129,12 @@ describe('authenticate', () => { it('returns the context if the session is valid and the app is not embedded', async () => { // GIVEN - const shopify = shopifyApp( - testConfig({ + const shopify = shopifyApp({ + ...testConfig({ isEmbeddedApp: false, }), - ); + future: {removeRest: false}, + }); let testSession: Session; testSession = await setUpValidSession(shopify.sessionStorage); diff --git a/packages/apps/shopify-app-remix/src/server/authenticate/admin/strategies/__tests__/merchant-custom/admin-client.test.ts b/packages/apps/shopify-app-remix/src/server/authenticate/admin/strategies/__tests__/merchant-custom/admin-client.test.ts index 86a33c36fd..5f7973db5f 100644 --- a/packages/apps/shopify-app-remix/src/server/authenticate/admin/strategies/__tests__/merchant-custom/admin-client.test.ts +++ b/packages/apps/shopify-app-remix/src/server/authenticate/admin/strategies/__tests__/merchant-custom/admin-client.test.ts @@ -7,7 +7,6 @@ import { } from '@shopify/shopify-api'; import {AppDistribution} from '../../../../../types'; -import {AdminApiContext} from '../../../../../clients'; import {shopifyApp} from '../../../../..'; import { APP_URL, @@ -34,25 +33,34 @@ describe('admin.authenticate context', () => { { testGroup: 'REST client', mockRequest: mockRestRequest, - action: async (admin: AdminApiContext, _session: Session) => - admin.rest.get({path: '/customers.json'}), + action: async ( + admin: Awaited>['admin'], + _session: Session, + ) => admin.rest.get({path: '/customers.json'}), }, { testGroup: 'REST resources', mockRequest: mockRestRequest, - action: async (admin: AdminApiContext, session: Session) => - admin.rest.resources.Customer.all({session}), + action: async ( + admin: Awaited>['admin'], + session: Session, + ) => admin.rest.resources.Customer.all({session}), }, { testGroup: 'GraphQL client', mockRequest: mockGraphqlRequest(), - action: async (admin: AdminApiContext, _session: Session) => - admin.graphql('{ shop { name } }'), + action: async ( + admin: Awaited>['admin'], + _session: Session, + ) => admin.graphql('{ shop { name } }'), }, { testGroup: 'GraphQL client with options', mockRequest: mockGraphqlRequest('2021-01' as ApiVersion), - action: async (admin: AdminApiContext, _session: Session) => + action: async ( + admin: Awaited>['admin'], + _session: Session, + ) => admin.graphql( 'mutation myMutation($ID: String!) { shop(ID: $ID) { name } }', { @@ -85,14 +93,15 @@ describe('admin.authenticate context', () => { }); async function setUpMerchantCustomFlow() { - const shopify = shopifyApp( - testConfig({ + const shopify = shopifyApp({ + ...testConfig({ restResources, isEmbeddedApp: false, distribution: AppDistribution.ShopifyAdmin, adminApiAccessToken: 'test-token', }), - ); + future: {removeRest: false}, + }); const expectedSession = setupValidCustomAppSession(TEST_SHOP); diff --git a/packages/apps/shopify-app-remix/src/server/authenticate/admin/strategies/__tests__/token-exchange/admin-client.test.ts b/packages/apps/shopify-app-remix/src/server/authenticate/admin/strategies/__tests__/token-exchange/admin-client.test.ts index 63f80d3914..f3a7d283d0 100644 --- a/packages/apps/shopify-app-remix/src/server/authenticate/admin/strategies/__tests__/token-exchange/admin-client.test.ts +++ b/packages/apps/shopify-app-remix/src/server/authenticate/admin/strategies/__tests__/token-exchange/admin-client.test.ts @@ -15,7 +15,6 @@ import { mockGraphqlRequest, } from '../../../../../__test-helpers'; import {shopifyApp} from '../../../../..'; -import {AdminApiContext} from '../../../../../clients'; describe('admin.authenticate context', () => { expectAdminApiClient(async () => { @@ -31,25 +30,34 @@ describe('admin.authenticate context', () => { { testGroup: 'REST client', mockRequest: mockRestRequest, - action: async (admin: AdminApiContext, _session: Session) => - admin.rest.get({path: '/customers.json'}), + action: async ( + admin: Awaited>['admin'], + _session: Session, + ) => admin.rest.get({path: '/customers.json'}), }, { testGroup: 'REST resources', mockRequest: mockRestRequest, - action: async (admin: AdminApiContext, session: Session) => - admin.rest.resources.Customer.all({session}), + action: async ( + admin: Awaited>['admin'], + session: Session, + ) => admin.rest.resources.Customer.all({session}), }, { testGroup: 'GraphQL client', mockRequest: mockGraphqlRequest(), - action: async (admin: AdminApiContext, _session: Session) => - admin.graphql('{ shop { name } }'), + action: async ( + admin: Awaited>['admin'], + _session: Session, + ) => admin.graphql('{ shop { name } }'), }, { testGroup: 'GraphQL client with options', mockRequest: mockGraphqlRequest('2021-01' as ApiVersion), - action: async (admin: AdminApiContext, _session: Session) => + action: async ( + admin: Awaited>['admin'], + _session: Session, + ) => admin.graphql( 'mutation myMutation($ID: String!) { shop(ID: $ID) { name } }', { @@ -90,7 +98,9 @@ describe('admin.authenticate context', () => { it('returns 401 when receives a 401 response on fetch requests', async () => { // GIVEN - const {admin, session, shopify} = await setUpFetchFlow(); + const {admin, session, shopify} = await setUpFetchFlow({ + unstable_newEmbeddedAuthStrategy: true, + }); const requestMock = await mockRequest(); // WHEN @@ -117,11 +127,18 @@ describe('admin.authenticate context', () => { }); async function setUpDocumentFlow() { - const shopify = shopifyApp( - testConfig({ - restResources, - }), - ); + const config = testConfig({ + restResources, + }); + + const shopify = shopifyApp({ + ...config, + future: { + ...config.future, + unstable_newEmbeddedAuthStrategy: true, + removeRest: false, + }, + }); const expectedSession = await setUpValidSession(shopify.sessionStorage); const {token} = getJwt(); diff --git a/packages/apps/shopify-app-remix/src/server/authenticate/admin/strategies/__tests__/token-exchange/authenticate.test.ts b/packages/apps/shopify-app-remix/src/server/authenticate/admin/strategies/__tests__/token-exchange/authenticate.test.ts index 0de4ab821a..9fc4765e34 100644 --- a/packages/apps/shopify-app-remix/src/server/authenticate/admin/strategies/__tests__/token-exchange/authenticate.test.ts +++ b/packages/apps/shopify-app-remix/src/server/authenticate/admin/strategies/__tests__/token-exchange/authenticate.test.ts @@ -128,7 +128,10 @@ describe('authenticate', () => { (isOnline) => { it('returns the context if the session is valid', async () => { // GIVEN - const shopify = shopifyApp(testConfig({useOnlineTokens: isOnline})); + const shopify = shopifyApp({ + ...testConfig({useOnlineTokens: isOnline}), + future: {removeRest: false}, + }); let testSession: Session; testSession = await setUpValidSession(shopify.sessionStorage); diff --git a/packages/apps/shopify-app-remix/src/server/authenticate/flow/__tests__/authenticate.test.ts b/packages/apps/shopify-app-remix/src/server/authenticate/flow/__tests__/authenticate.test.ts index 2f304fa2bf..b7a4e9a411 100644 --- a/packages/apps/shopify-app-remix/src/server/authenticate/flow/__tests__/authenticate.test.ts +++ b/packages/apps/shopify-app-remix/src/server/authenticate/flow/__tests__/authenticate.test.ts @@ -107,7 +107,10 @@ describe('authenticating flow requests', () => { describe('valid requests include an API client object', () => { expectAdminApiClient(async () => { const sessionStorage = new MemorySessionStorage(); - const shopify = shopifyApp(testConfig({sessionStorage})); + const shopify = shopifyApp({ + ...testConfig({sessionStorage}), + future: {removeRest: false}, + }); const {request, session: expectedSession} = await getValidRequest(sessionStorage); diff --git a/packages/apps/shopify-app-remix/src/server/authenticate/fulfillment-service/__tests__/authenticate.test.ts b/packages/apps/shopify-app-remix/src/server/authenticate/fulfillment-service/__tests__/authenticate.test.ts index 3e09958fc7..13e4ee7495 100644 --- a/packages/apps/shopify-app-remix/src/server/authenticate/fulfillment-service/__tests__/authenticate.test.ts +++ b/packages/apps/shopify-app-remix/src/server/authenticate/fulfillment-service/__tests__/authenticate.test.ts @@ -120,7 +120,10 @@ const MERCHANT_CUSTOM_APP_CONFIG = { describe('valid requests include an API client object', () => { expectAdminApiClient(async () => { const sessionStorage = new MemorySessionStorage(); - const shopify = shopifyApp(testConfig({sessionStorage})); + const shopify = shopifyApp({ + ...testConfig({sessionStorage}), + future: {removeRest: false}, + }); const {request, session: expectedSession} = await getValidRequest(sessionStorage); diff --git a/packages/apps/shopify-app-remix/src/server/authenticate/public/appProxy/__tests__/authenticate.test.ts b/packages/apps/shopify-app-remix/src/server/authenticate/public/appProxy/__tests__/authenticate.test.ts index 8cf573c879..5ceb21b94c 100644 --- a/packages/apps/shopify-app-remix/src/server/authenticate/public/appProxy/__tests__/authenticate.test.ts +++ b/packages/apps/shopify-app-remix/src/server/authenticate/public/appProxy/__tests__/authenticate.test.ts @@ -306,7 +306,10 @@ describe('authenticating app proxy requests', () => { describe('Valid requests with a session return an admin API client', () => { expectAdminApiClient(async () => { - const shopify = shopifyApp(testConfig()); + const shopify = shopifyApp({ + ...testConfig(), + future: {removeRest: false}, + }); const expectedSession = await setUpValidSession(shopify.sessionStorage, { isOnline: false, }); diff --git a/packages/apps/shopify-app-remix/src/server/authenticate/webhooks/__tests__/authenticate.test.ts b/packages/apps/shopify-app-remix/src/server/authenticate/webhooks/__tests__/authenticate.test.ts index ad358daff9..4b09bfab1d 100644 --- a/packages/apps/shopify-app-remix/src/server/authenticate/webhooks/__tests__/authenticate.test.ts +++ b/packages/apps/shopify-app-remix/src/server/authenticate/webhooks/__tests__/authenticate.test.ts @@ -60,7 +60,10 @@ describe('Webhook validation', () => { expectAdminApiClient(async () => { // GIVEN const sessionStorage = new MemorySessionStorage(); - const shopify = shopifyApp(testConfig({sessionStorage, restResources})); + const shopify = shopifyApp({ + ...testConfig({sessionStorage, restResources}), + future: {removeRest: false}, + }); const body = {some: 'data'}; const expectedSession = new Session({ diff --git a/packages/apps/shopify-app-remix/src/server/unauthenticated/admin/__tests__/factory.test.ts b/packages/apps/shopify-app-remix/src/server/unauthenticated/admin/__tests__/factory.test.ts index 01e1188356..1c880a3ade 100644 --- a/packages/apps/shopify-app-remix/src/server/unauthenticated/admin/__tests__/factory.test.ts +++ b/packages/apps/shopify-app-remix/src/server/unauthenticated/admin/__tests__/factory.test.ts @@ -14,7 +14,10 @@ import { describe('unauthenticated admin context', () => { it('throws an error if there is no offline session for the shop', async () => { // GIVEN - const shopify = shopifyApp(testConfig()); + const shopify = shopifyApp({ + ...testConfig(), + future: {removeRest: false}, + }); // EXPECT await expect(shopify.unauthenticated.admin(TEST_SHOP)).rejects.toThrow( @@ -23,7 +26,7 @@ describe('unauthenticated admin context', () => { }); expectAdminApiClient(async () => { - const shopify = shopifyApp(testConfig()); + const shopify = shopifyApp({...testConfig(), future: {removeRest: false}}); const expectedSession = await setUpValidSession(shopify.sessionStorage, { isOnline: false, }); @@ -36,13 +39,14 @@ describe('unauthenticated admin context', () => { describe('unauthenticated admin context for merchant custom apps', () => { expectAdminApiClient(async () => { - const shopify = shopifyApp( - testConfig({ + const shopify = shopifyApp({ + ...testConfig({ distribution: AppDistribution.ShopifyAdmin, adminApiAccessToken: 'admin-access-token', sessionStorage: undefined, }), - ); + future: {removeRest: false}, + }); const expectedSession = setupValidCustomAppSession(TEST_SHOP); const {admin, session: actualSession} = await shopify.unauthenticated.admin(TEST_SHOP); From eb44117ae2d78fea7d8db65fcf88f58434ad5256 Mon Sep 17 00:00:00 2001 From: Richard Powell Date: Wed, 23 Oct 2024 13:30:17 -0400 Subject: [PATCH 03/13] Add tests to check we never include REST in the admin context if removeRest is true --- .../__test-helpers/expect-admin-api-client.ts | 195 ++++++++++-------- .../__test-helpers/setup-embedded-flow.ts | 29 +++ .../admin/__tests__/admin-client.test.ts | 34 ++- .../auth-code-flow/admin-client.test.ts | 34 ++- .../merchant-custom/admin-client.test.ts | 53 +++-- .../token-exchange/admin-client.test.ts | 61 ++++-- .../flow/__tests__/authenticate.test.ts | 15 +- .../__tests__/authenticate.test.ts | 19 +- .../appProxy/__tests__/authenticate.test.ts | 12 +- .../webhooks/__tests__/authenticate.test.ts | 25 ++- .../admin/__tests__/factory.test.ts | 45 ++-- 11 files changed, 322 insertions(+), 200 deletions(-) diff --git a/packages/apps/shopify-app-remix/src/server/__test-helpers/expect-admin-api-client.ts b/packages/apps/shopify-app-remix/src/server/__test-helpers/expect-admin-api-client.ts index 11159750a8..6223912bb8 100644 --- a/packages/apps/shopify-app-remix/src/server/__test-helpers/expect-admin-api-client.ts +++ b/packages/apps/shopify-app-remix/src/server/__test-helpers/expect-admin-api-client.ts @@ -1,123 +1,140 @@ import {Session} from '@shopify/shopify-api'; import {LATEST_API_VERSION} from '..'; -import type {AdminApiContext} from '../clients'; -import {AppConfigArg} from '../config-types'; +import type { + AdminApiContextWithoutRest, + AdminApiContextWithRest, +} from '../clients'; -import {mockExternalRequest} from './request-mock'; import {TEST_SHOP} from './const'; +import {mockExternalRequest} from './request-mock'; const REQUEST_URL = `https://${TEST_SHOP}/admin/api/${LATEST_API_VERSION}/customers.json`; export function expectAdminApiClient( factory: () => Promise<{ - admin: AdminApiContext; + admin: AdminApiContextWithRest; + adminWithoutRest?: AdminApiContextWithoutRest; expectedSession: Session; actualSession: Session; }>, ) { - it('REST client can perform GET requests', async () => { - // GIVEN - const {admin} = await factory(); - await mockExternalRequest({ - request: new Request(REQUEST_URL), - response: new Response(JSON.stringify({customers: []})), + describe('when future.removeRest is falsey there is a REST client', () => { + it('can perform GET requests', async () => { + // GIVEN + const {admin} = await factory(); + await mockExternalRequest({ + request: new Request(REQUEST_URL), + response: new Response(JSON.stringify({customers: []})), + }); + + // WHEN + const response = await admin.rest.get({path: 'customers'}); + + // THEN + expect(response.status).toEqual(200); + expect(await response.json()).toEqual({customers: []}); }); - // WHEN - const response = await admin.rest.get({path: 'customers'}); - - // THEN - expect(response.status).toEqual(200); - expect(await response.json()).toEqual({customers: []}); - }); - - it('REST client can perform POST requests', async () => { - // GIVEN - const {admin} = await factory(); - await mockExternalRequest({ - request: new Request(REQUEST_URL, {method: 'POST'}), - response: new Response(JSON.stringify({customers: []})), + it('can perform POST requests', async () => { + // GIVEN + const {admin} = await factory(); + await mockExternalRequest({ + request: new Request(REQUEST_URL, {method: 'POST'}), + response: new Response(JSON.stringify({customers: []})), + }); + + // WHEN + const response = await admin.rest.post({ + path: '/customers.json', + data: '', + }); + + // THEN + expect(response.status).toEqual(200); + expect(await response.json()).toEqual({customers: []}); }); - // WHEN - const response = await admin.rest.post({ - path: '/customers.json', - data: '', + it('can perform PUT requests', async () => { + // GIVEN + const {admin} = await factory(); + await mockExternalRequest({ + request: new Request(REQUEST_URL, {method: 'PUT'}), + response: new Response(JSON.stringify({customers: []})), + }); + + // WHEN + const response = await admin.rest.put({ + path: '/customers.json', + data: '', + }); + + // THEN + expect(response.status).toEqual(200); + expect(await response.json()).toEqual({customers: []}); }); - // THEN - expect(response.status).toEqual(200); - expect(await response.json()).toEqual({customers: []}); - }); + it('can perform DELETE requests', async () => { + // GIVEN + const {admin} = await factory(); + await mockExternalRequest({ + request: new Request(REQUEST_URL, {method: 'DELETE'}), + response: new Response(JSON.stringify({customers: []})), + }); - it('REST client can perform PUT requests', async () => { - // GIVEN - const {admin} = await factory(); - await mockExternalRequest({ - request: new Request(REQUEST_URL, {method: 'PUT'}), - response: new Response(JSON.stringify({customers: []})), - }); + // WHEN + const response = await admin.rest.delete({path: '/customers.json'}); - // WHEN - const response = await admin.rest.put({ - path: '/customers.json', - data: '', + // THEN + expect(response.status).toEqual(200); + expect(await response.json()).toEqual({customers: []}); }); - - // THEN - expect(response.status).toEqual(200); - expect(await response.json()).toEqual({customers: []}); }); - it('REST client can perform DELETE requests', async () => { - // GIVEN - const {admin} = await factory(); - await mockExternalRequest({ - request: new Request(REQUEST_URL, {method: 'DELETE'}), - response: new Response(JSON.stringify({customers: []})), - }); - - // WHEN - const response = await admin.rest.delete({path: '/customers.json'}); + describe('when future.removeRest is truthy', () => { + it('does not include a rest property on the admin object', async () => { + // GIVEN + const {adminWithoutRest} = await factory(); - // THEN - expect(response.status).toEqual(200); - expect(await response.json()).toEqual({customers: []}); + // THEN + expect(adminWithoutRest).not.toHaveProperty('rest'); + }); }); - it('GraphQL client can perform requests', async () => { - // GIVEN - const {admin, actualSession} = await factory(); - await mockExternalRequest({ - request: new Request( - `https://${TEST_SHOP}/admin/api/${LATEST_API_VERSION}/graphql.json`, - { - method: 'POST', - headers: {'X-Shopify-Access-Token': actualSession.accessToken!}, - }, - ), - response: new Response( - JSON.stringify({data: {shop: {name: 'Test shop'}}}), - ), + describe('Graphql client', () => { + it('can perform requests', async () => { + // GIVEN + const {admin, actualSession} = await factory(); + await mockExternalRequest({ + request: new Request( + `https://${TEST_SHOP}/admin/api/${LATEST_API_VERSION}/graphql.json`, + { + method: 'POST', + headers: {'X-Shopify-Access-Token': actualSession.accessToken!}, + }, + ), + response: new Response( + JSON.stringify({data: {shop: {name: 'Test shop'}}}), + ), + }); + + // WHEN + const response = await admin.graphql('{ shop { name } }'); + + // THEN + expect(response.status).toEqual(200); + expect(await response.json()).toEqual({ + data: {shop: {name: 'Test shop'}}, + headers: {'Content-Type': ['application/json']}, + }); }); - // WHEN - const response = await admin.graphql('{ shop { name } }'); + it('returns a session object as part of the context', async () => { + // GIVEN + const {expectedSession, actualSession} = await factory(); - // THEN - expect(response.status).toEqual(200); - expect(await response.json()).toEqual({ - data: {shop: {name: 'Test shop'}}, - headers: {'Content-Type': ['application/json']}, + // THEN + expect(expectedSession).toEqual(actualSession); }); }); - - it('returns a session object as part of the context', async () => { - // GIVEN - const {expectedSession, actualSession} = await factory(); - - // THEN - expect(expectedSession).toEqual(actualSession); - }); } diff --git a/packages/apps/shopify-app-remix/src/server/__test-helpers/setup-embedded-flow.ts b/packages/apps/shopify-app-remix/src/server/__test-helpers/setup-embedded-flow.ts index f421aa24ab..9faf240154 100644 --- a/packages/apps/shopify-app-remix/src/server/__test-helpers/setup-embedded-flow.ts +++ b/packages/apps/shopify-app-remix/src/server/__test-helpers/setup-embedded-flow.ts @@ -34,3 +34,32 @@ export async function setUpEmbeddedFlow() { request, }; } + +export async function setUpEmbeddedFlowWithRemoveRestFlag() { + const shopify = shopifyApp({ + ...testConfig({ + restResources, + }), + future: { + removeRest: true, + unstable_newEmbeddedAuthStrategy: false, + wip_optionalScopesApi: true, + }, + }); + + const expectedSession = await setUpValidSession(shopify.sessionStorage); + + const {token} = getJwt(); + const request = new Request( + `${APP_URL}?embedded=1&shop=${TEST_SHOP}&host=${BASE64_HOST}&id_token=${token}`, + ); + + const result = await shopify.authenticate.admin(request); + + return { + shopify, + expectedSession, + ...result, + request, + }; +} diff --git a/packages/apps/shopify-app-remix/src/server/authenticate/admin/__tests__/admin-client.test.ts b/packages/apps/shopify-app-remix/src/server/authenticate/admin/__tests__/admin-client.test.ts index 600ccabbf3..bd0531ae64 100644 --- a/packages/apps/shopify-app-remix/src/server/authenticate/admin/__tests__/admin-client.test.ts +++ b/packages/apps/shopify-app-remix/src/server/authenticate/admin/__tests__/admin-client.test.ts @@ -4,14 +4,16 @@ import { LATEST_API_VERSION, Session, } from '@shopify/shopify-api'; +import {AdminApiContextWithRest} from 'src/server/clients'; import { TEST_SHOP, + expectAdminApiClient, getThrownResponse, mockExternalRequest, - expectAdminApiClient, - setUpEmbeddedFlow, mockGraphqlRequest, + setUpEmbeddedFlow, + setUpEmbeddedFlowWithRemoveRestFlag, setUpNonEmbeddedFlow, } from '../../../__test-helpers'; @@ -23,7 +25,10 @@ describe('admin.authenticate context', () => { session: actualSession, } = await setUpEmbeddedFlow(); - return {admin, expectedSession, actualSession}; + const {admin: adminWithoutRest} = + await setUpEmbeddedFlowWithRemoveRestFlag(); + + return {admin, adminWithoutRest, expectedSession, actualSession}; }); it('re-throws errors other than HttpResponseErrors on GraphQL requests', async () => { @@ -61,18 +66,14 @@ describe('admin.authenticate context', () => { { testGroup: 'REST client', mockRequest: mockRestRequest, - action: async ( - admin: Awaited>['admin'], - _session: Session, - ) => admin.rest.get({path: '/customers.json'}), + action: async (admin: AdminApiContextWithRest, _session: Session) => + admin.rest.get({path: '/customers.json'}), }, { testGroup: 'REST resources', mockRequest: mockRestRequest, - action: async ( - admin: Awaited>['admin'], - session: Session, - ) => admin.rest.resources.Customer.all({session}), + action: async (admin: AdminApiContextWithRest, session: Session) => + admin.rest.resources.Customer.all({session}), }, ])( '$testGroup re-authentication', @@ -98,18 +99,13 @@ describe('admin.authenticate context', () => { { testGroup: 'GraphQL client', mockRequest: mockGraphqlRequest(), - action: async ( - admin: Awaited>['admin'], - _session: Session, - ) => admin.graphql('{ shop { name } }'), + action: async (admin: AdminApiContextWithRest, _session: Session) => + admin.graphql('{ shop { name } }'), }, { testGroup: 'GraphQL client with options', mockRequest: mockGraphqlRequest('2021-01' as ApiVersion), - action: async ( - admin: Awaited>['admin'], - _session: Session, - ) => + action: async (admin: AdminApiContextWithRest, _session: Session) => admin.graphql( 'mutation myMutation($ID: String!) { shop(ID: $ID) { name } }', { diff --git a/packages/apps/shopify-app-remix/src/server/authenticate/admin/strategies/__tests__/auth-code-flow/admin-client.test.ts b/packages/apps/shopify-app-remix/src/server/authenticate/admin/strategies/__tests__/auth-code-flow/admin-client.test.ts index 8d4ab6e33c..0623bb2dfb 100644 --- a/packages/apps/shopify-app-remix/src/server/authenticate/admin/strategies/__tests__/auth-code-flow/admin-client.test.ts +++ b/packages/apps/shopify-app-remix/src/server/authenticate/admin/strategies/__tests__/auth-code-flow/admin-client.test.ts @@ -1,15 +1,17 @@ import {ApiVersion, LATEST_API_VERSION, Session} from '@shopify/shopify-api'; +import {AdminApiContextWithRest} from 'src/server/clients'; import { APP_URL, TEST_SHOP, + expectAdminApiClient, expectExitIframeRedirect, getThrownResponse, mockExternalRequest, - expectAdminApiClient, + mockGraphqlRequest, setUpEmbeddedFlow, + setUpEmbeddedFlowWithRemoveRestFlag, setUpFetchFlow, - mockGraphqlRequest, setUpNonEmbeddedFlow, } from '../../../../../__test-helpers'; import {REAUTH_URL_HEADER} from '../../../../const'; @@ -22,40 +24,34 @@ describe('admin.authenticate context', () => { session: actualSession, } = await setUpEmbeddedFlow(); - return {admin, expectedSession, actualSession}; + const {admin: adminWithoutRest} = + await setUpEmbeddedFlowWithRemoveRestFlag(); + + return {admin, adminWithoutRest, expectedSession, actualSession}; }); describe.each([ { testGroup: 'REST client', mockRequest: mockRestRequest, - action: async ( - admin: Awaited>['admin'], - _session: Session, - ) => admin.rest.get({path: '/customers.json'}), + action: async (admin: AdminApiContextWithRest, _session: Session) => + admin.rest.get({path: '/customers.json'}), }, { testGroup: 'REST resources', mockRequest: mockRestRequest, - action: async ( - admin: Awaited>['admin'], - session: Session, - ) => admin.rest.resources.Customer.all({session}), + action: async (admin: AdminApiContextWithRest, session: Session) => + admin.rest.resources.Customer.all({session}), }, { testGroup: 'GraphQL client', mockRequest: mockGraphqlRequest(), - action: async ( - admin: Awaited>['admin'], - _session: Session, - ) => admin.graphql('{ shop { name } }'), + action: async (admin: AdminApiContextWithRest, _session: Session) => + admin.graphql('{ shop { name } }'), }, { testGroup: 'GraphQL client with options', mockRequest: mockGraphqlRequest('2021-01' as ApiVersion), - action: async ( - admin: Awaited>['admin'], - _session: Session, - ) => + action: async (admin: AdminApiContextWithRest, _session: Session) => admin.graphql( 'mutation myMutation($ID: String!) { shop(ID: $ID) { name } }', { diff --git a/packages/apps/shopify-app-remix/src/server/authenticate/admin/strategies/__tests__/merchant-custom/admin-client.test.ts b/packages/apps/shopify-app-remix/src/server/authenticate/admin/strategies/__tests__/merchant-custom/admin-client.test.ts index 5f7973db5f..47d341fee1 100644 --- a/packages/apps/shopify-app-remix/src/server/authenticate/admin/strategies/__tests__/merchant-custom/admin-client.test.ts +++ b/packages/apps/shopify-app-remix/src/server/authenticate/admin/strategies/__tests__/merchant-custom/admin-client.test.ts @@ -1,23 +1,24 @@ -import {restResources} from '@shopify/shopify-api/rest/admin/2023-04'; import { ApiVersion, LATEST_API_VERSION, Session, ShopifyError, } from '@shopify/shopify-api'; +import {restResources} from '@shopify/shopify-api/rest/admin/2023-04'; +import {AdminApiContextWithRest} from 'src/server/clients'; -import {AppDistribution} from '../../../../../types'; import {shopifyApp} from '../../../../..'; import { APP_URL, TEST_SHOP, expectAdminApiClient, + getThrownError, mockExternalRequest, + mockGraphqlRequest, setupValidCustomAppSession, testConfig, - getThrownError, - mockGraphqlRequest, } from '../../../../../__test-helpers'; +import {AppDistribution} from '../../../../../types'; describe('admin.authenticate context', () => { expectAdminApiClient(async () => { @@ -27,40 +28,34 @@ describe('admin.authenticate context', () => { session: actualSession, } = await setUpMerchantCustomFlow(); - return {admin, expectedSession, actualSession}; + const {admin: adminWithoutRest} = + await setUpMerchantCustomFlowWithRemoveRestFlag(); + + return {admin, adminWithoutRest, expectedSession, actualSession}; }); describe.each([ { testGroup: 'REST client', mockRequest: mockRestRequest, - action: async ( - admin: Awaited>['admin'], - _session: Session, - ) => admin.rest.get({path: '/customers.json'}), + action: async (admin: AdminApiContextWithRest, _session: Session) => + admin.rest.get({path: '/customers.json'}), }, { testGroup: 'REST resources', mockRequest: mockRestRequest, - action: async ( - admin: Awaited>['admin'], - session: Session, - ) => admin.rest.resources.Customer.all({session}), + action: async (admin: AdminApiContextWithRest, session: Session) => + admin.rest.resources.Customer.all({session}), }, { testGroup: 'GraphQL client', mockRequest: mockGraphqlRequest(), - action: async ( - admin: Awaited>['admin'], - _session: Session, - ) => admin.graphql('{ shop { name } }'), + action: async (admin: AdminApiContextWithRest, _session: Session) => + admin.graphql('{ shop { name } }'), }, { testGroup: 'GraphQL client with options', mockRequest: mockGraphqlRequest('2021-01' as ApiVersion), - action: async ( - admin: Awaited>['admin'], - _session: Session, - ) => + action: async (admin: AdminApiContextWithRest, _session: Session) => admin.graphql( 'mutation myMutation($ID: String!) { shop(ID: $ID) { name } }', { @@ -114,6 +109,22 @@ async function setUpMerchantCustomFlow() { }; } +async function setUpMerchantCustomFlowWithRemoveRestFlag() { + const shopify = shopifyApp({ + ...testConfig({ + restResources, + isEmbeddedApp: false, + distribution: AppDistribution.ShopifyAdmin, + adminApiAccessToken: 'test-token', + }), + future: {removeRest: true}, + }); + + const request = new Request(`${APP_URL}?shop=${TEST_SHOP}`); + + return shopify.authenticate.admin(request); +} + async function mockRestRequest(status = 401) { const requestMock = new Request( `https://${TEST_SHOP}/admin/api/${LATEST_API_VERSION}/customers.json`, diff --git a/packages/apps/shopify-app-remix/src/server/authenticate/admin/strategies/__tests__/token-exchange/admin-client.test.ts b/packages/apps/shopify-app-remix/src/server/authenticate/admin/strategies/__tests__/token-exchange/admin-client.test.ts index f3a7d283d0..a5e1bce6a7 100644 --- a/packages/apps/shopify-app-remix/src/server/authenticate/admin/strategies/__tests__/token-exchange/admin-client.test.ts +++ b/packages/apps/shopify-app-remix/src/server/authenticate/admin/strategies/__tests__/token-exchange/admin-client.test.ts @@ -1,20 +1,21 @@ import {ApiVersion, LATEST_API_VERSION, Session} from '@shopify/shopify-api'; import {restResources} from '@shopify/shopify-api/rest/admin/2023-04'; +import {AdminApiContextWithRest} from 'src/server/clients'; +import {shopifyApp} from '../../../../..'; import { APP_URL, BASE64_HOST, TEST_SHOP, + expectAdminApiClient, getJwt, getThrownResponse, - setUpValidSession, - testConfig, mockExternalRequest, - expectAdminApiClient, - setUpFetchFlow, mockGraphqlRequest, + setUpFetchFlow, + setUpValidSession, + testConfig, } from '../../../../../__test-helpers'; -import {shopifyApp} from '../../../../..'; describe('admin.authenticate context', () => { expectAdminApiClient(async () => { @@ -24,40 +25,34 @@ describe('admin.authenticate context', () => { session: actualSession, } = await setUpDocumentFlow(); - return {admin, expectedSession, actualSession}; + const {admin: adminWithoutRest} = + await setUpDocumentFlowWithRemoveRestFlag(); + + return {admin, adminWithoutRest, expectedSession, actualSession}; }); describe.each([ { testGroup: 'REST client', mockRequest: mockRestRequest, - action: async ( - admin: Awaited>['admin'], - _session: Session, - ) => admin.rest.get({path: '/customers.json'}), + action: async (admin: AdminApiContextWithRest, _session: Session) => + admin.rest.get({path: '/customers.json'}), }, { testGroup: 'REST resources', mockRequest: mockRestRequest, - action: async ( - admin: Awaited>['admin'], - session: Session, - ) => admin.rest.resources.Customer.all({session}), + action: async (admin: AdminApiContextWithRest, session: Session) => + admin.rest.resources.Customer.all({session}), }, { testGroup: 'GraphQL client', mockRequest: mockGraphqlRequest(), - action: async ( - admin: Awaited>['admin'], - _session: Session, - ) => admin.graphql('{ shop { name } }'), + action: async (admin: AdminApiContextWithRest, _session: Session) => + admin.graphql('{ shop { name } }'), }, { testGroup: 'GraphQL client with options', mockRequest: mockGraphqlRequest('2021-01' as ApiVersion), - action: async ( - admin: Awaited>['admin'], - _session: Session, - ) => + action: async (admin: AdminApiContextWithRest, _session: Session) => admin.graphql( 'mutation myMutation($ID: String!) { shop(ID: $ID) { name } }', { @@ -153,6 +148,28 @@ async function setUpDocumentFlow() { }; } +async function setUpDocumentFlowWithRemoveRestFlag() { + const config = testConfig({ + restResources, + }); + + const shopify = shopifyApp({ + ...config, + future: { + ...config.future, + unstable_newEmbeddedAuthStrategy: true, + removeRest: true, + }, + }); + + const {token} = getJwt(); + const request = new Request( + `${APP_URL}?embedded=1&shop=${TEST_SHOP}&host=${BASE64_HOST}&id_token=${token}`, + ); + + return shopify.authenticate.admin(request); +} + async function mockRestRequest(status = 401) { const requestMock = new Request( `https://${TEST_SHOP}/admin/api/${LATEST_API_VERSION}/customers.json`, diff --git a/packages/apps/shopify-app-remix/src/server/authenticate/flow/__tests__/authenticate.test.ts b/packages/apps/shopify-app-remix/src/server/authenticate/flow/__tests__/authenticate.test.ts index b7a4e9a411..acb419d470 100644 --- a/packages/apps/shopify-app-remix/src/server/authenticate/flow/__tests__/authenticate.test.ts +++ b/packages/apps/shopify-app-remix/src/server/authenticate/flow/__tests__/authenticate.test.ts @@ -118,11 +118,18 @@ describe('authenticating flow requests', () => { const {admin, session: actualSession} = await shopify.authenticate.flow(request); - if (!admin) { - throw new Error('No admin client'); - } + const shopifyWithoutRest = shopifyApp({ + ...testConfig({sessionStorage}), + future: {removeRest: true}, + }); + + const {request: requestForWithoutRest} = + await getValidRequest(sessionStorage); + + const {admin: adminWithoutRest} = + await shopifyWithoutRest.authenticate.flow(requestForWithoutRest); - return {admin, expectedSession, actualSession}; + return {admin, adminWithoutRest, expectedSession, actualSession}; }); }); }); diff --git a/packages/apps/shopify-app-remix/src/server/authenticate/fulfillment-service/__tests__/authenticate.test.ts b/packages/apps/shopify-app-remix/src/server/authenticate/fulfillment-service/__tests__/authenticate.test.ts index 13e4ee7495..b3ef310c64 100644 --- a/packages/apps/shopify-app-remix/src/server/authenticate/fulfillment-service/__tests__/authenticate.test.ts +++ b/packages/apps/shopify-app-remix/src/server/authenticate/fulfillment-service/__tests__/authenticate.test.ts @@ -7,8 +7,8 @@ import { getHmac, getThrownResponse, setUpValidSession, - testConfig, TEST_SHOP, + testConfig, } from '../../../__test-helpers'; const FULFILLMENT_URL = @@ -131,11 +131,20 @@ const MERCHANT_CUSTOM_APP_CONFIG = { const {admin, session: actualSession} = await shopify.authenticate.fulfillmentService(request); - if (!admin) { - throw new Error('No admin client'); - } + const shopifyWithoutRest = shopifyApp({ + ...testConfig({sessionStorage}), + future: {removeRest: true}, + }); + + const {request: requestForWithoutRest} = + await getValidRequest(sessionStorage); + + const {admin: adminWithoutRest} = + await shopifyWithoutRest.authenticate.fulfillmentService( + requestForWithoutRest, + ); - return {admin, expectedSession, actualSession}; + return {admin, adminWithoutRest, expectedSession, actualSession}; }); }); }); diff --git a/packages/apps/shopify-app-remix/src/server/authenticate/public/appProxy/__tests__/authenticate.test.ts b/packages/apps/shopify-app-remix/src/server/authenticate/public/appProxy/__tests__/authenticate.test.ts index 5ceb21b94c..c1db30ecd5 100644 --- a/packages/apps/shopify-app-remix/src/server/authenticate/public/appProxy/__tests__/authenticate.test.ts +++ b/packages/apps/shopify-app-remix/src/server/authenticate/public/appProxy/__tests__/authenticate.test.ts @@ -321,7 +321,17 @@ describe('authenticating app proxy requests', () => { throw new Error('No admin client'); } - return {admin, expectedSession, actualSession}; + const shopifyWithoutRest = shopifyApp({ + ...testConfig(), + future: {removeRest: true}, + }); + + const {admin: adminWithoutRest} = + await shopifyWithoutRest.authenticate.public.appProxy( + await getValidRequest(), + ); + + return {admin, adminWithoutRest, expectedSession, actualSession}; }); }); diff --git a/packages/apps/shopify-app-remix/src/server/authenticate/webhooks/__tests__/authenticate.test.ts b/packages/apps/shopify-app-remix/src/server/authenticate/webhooks/__tests__/authenticate.test.ts index 4b09bfab1d..ce71012f46 100644 --- a/packages/apps/shopify-app-remix/src/server/authenticate/webhooks/__tests__/authenticate.test.ts +++ b/packages/apps/shopify-app-remix/src/server/authenticate/webhooks/__tests__/authenticate.test.ts @@ -75,6 +75,13 @@ describe('Webhook validation', () => { }); await sessionStorage.storeSession(expectedSession); + const requestURL = `${APP_URL}/webhooks`; + const requestOptions = { + method: 'POST', + body: JSON.stringify(body), + headers: webhookHeaders(JSON.stringify(body)), + }; + // WHEN const { admin, @@ -85,11 +92,7 @@ describe('Webhook validation', () => { webhookId, payload, } = await shopify.authenticate.webhook( - new Request(`${APP_URL}/webhooks`, { - method: 'POST', - body: JSON.stringify(body), - headers: webhookHeaders(JSON.stringify(body)), - }), + new Request(requestURL, requestOptions), ); // THEN @@ -103,7 +106,17 @@ describe('Webhook validation', () => { if (!admin) throw new Error('Expected admin to be defined'); if (!actualSession) throw new Error('Expected session to be defined'); - return {admin, expectedSession, actualSession}; + const shopifyWithoutRest = shopifyApp({ + ...testConfig({sessionStorage, restResources}), + future: {removeRest: true}, + }); + + const {admin: adminWithoutRest} = + await shopifyWithoutRest.authenticate.webhook( + new Request(requestURL, requestOptions), + ); + + return {admin, adminWithoutRest, expectedSession, actualSession}; }); }); diff --git a/packages/apps/shopify-app-remix/src/server/unauthenticated/admin/__tests__/factory.test.ts b/packages/apps/shopify-app-remix/src/server/unauthenticated/admin/__tests__/factory.test.ts index 1c880a3ade..32bdc7c319 100644 --- a/packages/apps/shopify-app-remix/src/server/unauthenticated/admin/__tests__/factory.test.ts +++ b/packages/apps/shopify-app-remix/src/server/unauthenticated/admin/__tests__/factory.test.ts @@ -1,15 +1,15 @@ -import { - AppDistribution, - SessionNotFoundError, - shopifyApp, -} from '../../../index'; import { TEST_SHOP, - setUpValidSession, - testConfig, expectAdminApiClient, + setUpValidSession, setupValidCustomAppSession, + testConfig, } from '../../../__test-helpers'; +import { + AppDistribution, + SessionNotFoundError, + shopifyApp, +} from '../../../index'; describe('unauthenticated admin context', () => { it('throws an error if there is no offline session for the shop', async () => { @@ -33,24 +33,41 @@ describe('unauthenticated admin context', () => { const {admin, session: actualSession} = await shopify.unauthenticated.admin(TEST_SHOP); - return {admin, expectedSession, actualSession}; + const shopifyWithoutRest = shopifyApp({ + ...testConfig(), + future: {removeRest: true}, + }); + + const {admin: adminWithoutRest} = + await shopifyWithoutRest.unauthenticated.admin(TEST_SHOP); + + return {admin, adminWithoutRest, expectedSession, actualSession}; }); }); describe('unauthenticated admin context for merchant custom apps', () => { expectAdminApiClient(async () => { + const config = testConfig({ + distribution: AppDistribution.ShopifyAdmin, + adminApiAccessToken: 'admin-access-token', + sessionStorage: undefined, + }); + const shopify = shopifyApp({ - ...testConfig({ - distribution: AppDistribution.ShopifyAdmin, - adminApiAccessToken: 'admin-access-token', - sessionStorage: undefined, - }), + ...config, future: {removeRest: false}, }); const expectedSession = setupValidCustomAppSession(TEST_SHOP); const {admin, session: actualSession} = await shopify.unauthenticated.admin(TEST_SHOP); - return {admin, expectedSession, actualSession}; + const shopifyWithoutRest = shopifyApp({ + ...config, + future: {removeRest: true}, + }); + const {admin: adminWithoutRest} = + await shopifyWithoutRest.unauthenticated.admin(TEST_SHOP); + + return {admin, adminWithoutRest, expectedSession, actualSession}; }); }); From 47103bcef857e62864534b936ae0b9f62d0c2f0e Mon Sep 17 00:00:00 2001 From: Richard Powell Date: Mon, 28 Oct 2024 14:14:14 -0400 Subject: [PATCH 04/13] Change future flag to v4_removeRest --- .../__test-helpers/expect-admin-api-client.ts | 4 ++-- .../server/__test-helpers/setup-embedded-flow.ts | 4 ++-- .../src/server/__test-helpers/setup-fetch-flow.ts | 2 +- .../__test-helpers/setup-non-embedded-flow.ts | 4 ++-- .../src/server/__test-helpers/test-config.ts | 6 +++--- .../src/server/__tests__/shopify-app.test.ts | 14 +++++++------- .../__tests__/session-token-header-path.test.ts | 4 ++-- .../__tests__/auth-code-flow/authenticate.test.ts | 6 +++--- .../__tests__/merchant-custom/admin-client.test.ts | 4 ++-- .../__tests__/token-exchange/admin-client.test.ts | 4 ++-- .../__tests__/token-exchange/authenticate.test.ts | 2 +- .../flow/__tests__/authenticate.test.ts | 4 ++-- .../__tests__/authenticate.test.ts | 4 ++-- .../public/appProxy/__tests__/authenticate.test.ts | 4 ++-- .../webhooks/__tests__/authenticate.test.ts | 4 ++-- .../src/server/clients/admin/factory.ts | 2 +- .../src/server/clients/admin/types.ts | 4 ++-- .../shopify-app-remix/src/server/future/flags.ts | 2 +- .../admin/__tests__/factory.test.ts | 13 ++++++++----- 19 files changed, 47 insertions(+), 44 deletions(-) diff --git a/packages/apps/shopify-app-remix/src/server/__test-helpers/expect-admin-api-client.ts b/packages/apps/shopify-app-remix/src/server/__test-helpers/expect-admin-api-client.ts index 6223912bb8..020b0f4bfd 100644 --- a/packages/apps/shopify-app-remix/src/server/__test-helpers/expect-admin-api-client.ts +++ b/packages/apps/shopify-app-remix/src/server/__test-helpers/expect-admin-api-client.ts @@ -19,7 +19,7 @@ export function expectAdminApiClient( actualSession: Session; }>, ) { - describe('when future.removeRest is falsey there is a REST client', () => { + describe('when future.v4_removeRest is falsey there is a REST client', () => { it('can perform GET requests', async () => { // GIVEN const {admin} = await factory(); @@ -91,7 +91,7 @@ export function expectAdminApiClient( }); }); - describe('when future.removeRest is truthy', () => { + describe('when future.v4_removeRest is truthy', () => { it('does not include a rest property on the admin object', async () => { // GIVEN const {adminWithoutRest} = await factory(); diff --git a/packages/apps/shopify-app-remix/src/server/__test-helpers/setup-embedded-flow.ts b/packages/apps/shopify-app-remix/src/server/__test-helpers/setup-embedded-flow.ts index 9faf240154..2228ca87f7 100644 --- a/packages/apps/shopify-app-remix/src/server/__test-helpers/setup-embedded-flow.ts +++ b/packages/apps/shopify-app-remix/src/server/__test-helpers/setup-embedded-flow.ts @@ -13,7 +13,7 @@ export async function setUpEmbeddedFlow() { restResources, }), future: { - removeRest: false, + v4_removeRest: false, unstable_newEmbeddedAuthStrategy: false, wip_optionalScopesApi: true, }, @@ -41,7 +41,7 @@ export async function setUpEmbeddedFlowWithRemoveRestFlag() { restResources, }), future: { - removeRest: true, + v4_removeRest: true, unstable_newEmbeddedAuthStrategy: false, wip_optionalScopesApi: true, }, diff --git a/packages/apps/shopify-app-remix/src/server/__test-helpers/setup-fetch-flow.ts b/packages/apps/shopify-app-remix/src/server/__test-helpers/setup-fetch-flow.ts index 92598a8c61..be0757069f 100644 --- a/packages/apps/shopify-app-remix/src/server/__test-helpers/setup-fetch-flow.ts +++ b/packages/apps/shopify-app-remix/src/server/__test-helpers/setup-fetch-flow.ts @@ -15,7 +15,7 @@ export async function setUpFetchFlow(flags?: { ...testConfig({ restResources, }), - future: {...flags, removeRest: false, wip_optionalScopesApi: true}, + future: {...flags, v4_removeRest: false, wip_optionalScopesApi: true}, }); await setUpValidSession(shopify.sessionStorage); diff --git a/packages/apps/shopify-app-remix/src/server/__test-helpers/setup-non-embedded-flow.ts b/packages/apps/shopify-app-remix/src/server/__test-helpers/setup-non-embedded-flow.ts index e928bd59d6..506d99802d 100644 --- a/packages/apps/shopify-app-remix/src/server/__test-helpers/setup-non-embedded-flow.ts +++ b/packages/apps/shopify-app-remix/src/server/__test-helpers/setup-non-embedded-flow.ts @@ -1,5 +1,5 @@ -import {restResources} from '@shopify/shopify-api/rest/admin/2023-04'; import {SESSION_COOKIE_NAME} from '@shopify/shopify-api'; +import {restResources} from '@shopify/shopify-api/rest/admin/2023-04'; import {shopifyApp} from '../shopify-app'; @@ -11,7 +11,7 @@ import {testConfig} from './test-config'; export async function setUpNonEmbeddedFlow() { const shopify = shopifyApp({ ...testConfig({restResources, isEmbeddedApp: false}), - future: {removeRest: false, wip_optionalScopesApi: true}, + future: {v4_removeRest: false, wip_optionalScopesApi: true}, }); const session = await setUpValidSession(shopify.sessionStorage); diff --git a/packages/apps/shopify-app-remix/src/server/__test-helpers/test-config.ts b/packages/apps/shopify-app-remix/src/server/__test-helpers/test-config.ts index 0096937e39..2737ba7dd0 100644 --- a/packages/apps/shopify-app-remix/src/server/__test-helpers/test-config.ts +++ b/packages/apps/shopify-app-remix/src/server/__test-helpers/test-config.ts @@ -1,9 +1,9 @@ import {LATEST_API_VERSION} from '@shopify/shopify-api'; import {MemorySessionStorage} from '@shopify/shopify-app-session-storage-memory'; -import {testConfig as testConfigImport} from '../test-helpers/test-config'; +import type {FutureFlagOptions, FutureFlags} from '../future/flags'; import type {TestOverridesArg} from '../test-helpers/test-config'; -import type {FutureFlags, FutureFlagOptions} from '../future/flags'; +import {testConfig as testConfigImport} from '../test-helpers/test-config'; /* * This object mandates that all existing future flags be activated for tests. If a new flag is added, this object must @@ -15,7 +15,7 @@ import type {FutureFlags, FutureFlagOptions} from '../future/flags'; const TEST_FUTURE_FLAGS: Required<{[key in keyof FutureFlags]: true}> = { unstable_newEmbeddedAuthStrategy: true, wip_optionalScopesApi: true, - removeRest: true, + v4_removeRest: true, } as const; // Override the helper's future flags and logger settings for our purposes diff --git a/packages/apps/shopify-app-remix/src/server/__tests__/shopify-app.test.ts b/packages/apps/shopify-app-remix/src/server/__tests__/shopify-app.test.ts index 36b39d4a22..d9288ba239 100644 --- a/packages/apps/shopify-app-remix/src/server/__tests__/shopify-app.test.ts +++ b/packages/apps/shopify-app-remix/src/server/__tests__/shopify-app.test.ts @@ -1,15 +1,15 @@ import {ShopifyError} from '@shopify/shopify-api'; +import {testConfig} from '../__test-helpers'; import { - shopifyApp, + ApiVersion, LATEST_API_VERSION as APP_LATEST_API_VERSION, - LogSeverity, - DeliveryMethod, - BillingInterval, AppDistribution, - ApiVersion, + BillingInterval, + DeliveryMethod, + LogSeverity, + shopifyApp, } from '../index'; -import {testConfig} from '../__test-helpers'; import {deriveApi} from '../shopify-app'; describe('shopifyApp', () => { @@ -29,7 +29,7 @@ describe('shopifyApp', () => { // GIVEN const shopify = shopifyApp({ ...testConfig(), - future: {removeRest: false}, + future: {v4_removeRest: false}, }); // THEN diff --git a/packages/apps/shopify-app-remix/src/server/authenticate/admin/__tests__/session-token-header-path.test.ts b/packages/apps/shopify-app-remix/src/server/authenticate/admin/__tests__/session-token-header-path.test.ts index 29ad79ae03..0b2f88a4b6 100644 --- a/packages/apps/shopify-app-remix/src/server/authenticate/admin/__tests__/session-token-header-path.test.ts +++ b/packages/apps/shopify-app-remix/src/server/authenticate/admin/__tests__/session-token-header-path.test.ts @@ -38,7 +38,7 @@ describe('authorize.session token header path', () => { // GIVEN const shopify = shopifyApp({ ...testConfig({useOnlineTokens: isOnline}), - future: {removeRest: false}, + future: {v4_removeRest: false}, }); const testSession = await setUpValidSession(shopify.sessionStorage, { @@ -63,7 +63,7 @@ describe('authorize.session token header path', () => { // GIVEN const shopify = shopifyApp({ ...testConfig({isEmbeddedApp: false, useOnlineTokens: isOnline}), - future: {removeRest: false}, + future: {v4_removeRest: false}, }); let testSession: Session; diff --git a/packages/apps/shopify-app-remix/src/server/authenticate/admin/strategies/__tests__/auth-code-flow/authenticate.test.ts b/packages/apps/shopify-app-remix/src/server/authenticate/admin/strategies/__tests__/auth-code-flow/authenticate.test.ts index 971648b8e2..3351d73674 100644 --- a/packages/apps/shopify-app-remix/src/server/authenticate/admin/strategies/__tests__/auth-code-flow/authenticate.test.ts +++ b/packages/apps/shopify-app-remix/src/server/authenticate/admin/strategies/__tests__/auth-code-flow/authenticate.test.ts @@ -10,8 +10,8 @@ import { getJwt, getThrownResponse, setUpValidSession, - testConfig, signRequestCookie, + testConfig, } from '../../../../../__test-helpers'; describe('authenticate', () => { @@ -103,7 +103,7 @@ describe('authenticate', () => { future: {unstable_newEmbeddedAuthStrategy: false}, isEmbeddedApp: true, }), - future: {removeRest: false}, + future: {v4_removeRest: false}, }); let testSession: Session; @@ -133,7 +133,7 @@ describe('authenticate', () => { ...testConfig({ isEmbeddedApp: false, }), - future: {removeRest: false}, + future: {v4_removeRest: false}, }); let testSession: Session; diff --git a/packages/apps/shopify-app-remix/src/server/authenticate/admin/strategies/__tests__/merchant-custom/admin-client.test.ts b/packages/apps/shopify-app-remix/src/server/authenticate/admin/strategies/__tests__/merchant-custom/admin-client.test.ts index 47d341fee1..2722aae538 100644 --- a/packages/apps/shopify-app-remix/src/server/authenticate/admin/strategies/__tests__/merchant-custom/admin-client.test.ts +++ b/packages/apps/shopify-app-remix/src/server/authenticate/admin/strategies/__tests__/merchant-custom/admin-client.test.ts @@ -95,7 +95,7 @@ async function setUpMerchantCustomFlow() { distribution: AppDistribution.ShopifyAdmin, adminApiAccessToken: 'test-token', }), - future: {removeRest: false}, + future: {v4_removeRest: false}, }); const expectedSession = setupValidCustomAppSession(TEST_SHOP); @@ -117,7 +117,7 @@ async function setUpMerchantCustomFlowWithRemoveRestFlag() { distribution: AppDistribution.ShopifyAdmin, adminApiAccessToken: 'test-token', }), - future: {removeRest: true}, + future: {v4_removeRest: true}, }); const request = new Request(`${APP_URL}?shop=${TEST_SHOP}`); diff --git a/packages/apps/shopify-app-remix/src/server/authenticate/admin/strategies/__tests__/token-exchange/admin-client.test.ts b/packages/apps/shopify-app-remix/src/server/authenticate/admin/strategies/__tests__/token-exchange/admin-client.test.ts index a5e1bce6a7..8a6126e988 100644 --- a/packages/apps/shopify-app-remix/src/server/authenticate/admin/strategies/__tests__/token-exchange/admin-client.test.ts +++ b/packages/apps/shopify-app-remix/src/server/authenticate/admin/strategies/__tests__/token-exchange/admin-client.test.ts @@ -131,7 +131,7 @@ async function setUpDocumentFlow() { future: { ...config.future, unstable_newEmbeddedAuthStrategy: true, - removeRest: false, + v4_removeRest: false, }, }); const expectedSession = await setUpValidSession(shopify.sessionStorage); @@ -158,7 +158,7 @@ async function setUpDocumentFlowWithRemoveRestFlag() { future: { ...config.future, unstable_newEmbeddedAuthStrategy: true, - removeRest: true, + v4_removeRest: true, }, }); diff --git a/packages/apps/shopify-app-remix/src/server/authenticate/admin/strategies/__tests__/token-exchange/authenticate.test.ts b/packages/apps/shopify-app-remix/src/server/authenticate/admin/strategies/__tests__/token-exchange/authenticate.test.ts index 9fc4765e34..05b06f46c1 100644 --- a/packages/apps/shopify-app-remix/src/server/authenticate/admin/strategies/__tests__/token-exchange/authenticate.test.ts +++ b/packages/apps/shopify-app-remix/src/server/authenticate/admin/strategies/__tests__/token-exchange/authenticate.test.ts @@ -130,7 +130,7 @@ describe('authenticate', () => { // GIVEN const shopify = shopifyApp({ ...testConfig({useOnlineTokens: isOnline}), - future: {removeRest: false}, + future: {v4_removeRest: false}, }); let testSession: Session; diff --git a/packages/apps/shopify-app-remix/src/server/authenticate/flow/__tests__/authenticate.test.ts b/packages/apps/shopify-app-remix/src/server/authenticate/flow/__tests__/authenticate.test.ts index acb419d470..8382a78dab 100644 --- a/packages/apps/shopify-app-remix/src/server/authenticate/flow/__tests__/authenticate.test.ts +++ b/packages/apps/shopify-app-remix/src/server/authenticate/flow/__tests__/authenticate.test.ts @@ -109,7 +109,7 @@ describe('authenticating flow requests', () => { const sessionStorage = new MemorySessionStorage(); const shopify = shopifyApp({ ...testConfig({sessionStorage}), - future: {removeRest: false}, + future: {v4_removeRest: false}, }); const {request, session: expectedSession} = @@ -120,7 +120,7 @@ describe('authenticating flow requests', () => { const shopifyWithoutRest = shopifyApp({ ...testConfig({sessionStorage}), - future: {removeRest: true}, + future: {v4_removeRest: true}, }); const {request: requestForWithoutRest} = diff --git a/packages/apps/shopify-app-remix/src/server/authenticate/fulfillment-service/__tests__/authenticate.test.ts b/packages/apps/shopify-app-remix/src/server/authenticate/fulfillment-service/__tests__/authenticate.test.ts index b3ef310c64..e9ce7ce1d9 100644 --- a/packages/apps/shopify-app-remix/src/server/authenticate/fulfillment-service/__tests__/authenticate.test.ts +++ b/packages/apps/shopify-app-remix/src/server/authenticate/fulfillment-service/__tests__/authenticate.test.ts @@ -122,7 +122,7 @@ const MERCHANT_CUSTOM_APP_CONFIG = { const sessionStorage = new MemorySessionStorage(); const shopify = shopifyApp({ ...testConfig({sessionStorage}), - future: {removeRest: false}, + future: {v4_removeRest: false}, }); const {request, session: expectedSession} = @@ -133,7 +133,7 @@ const MERCHANT_CUSTOM_APP_CONFIG = { const shopifyWithoutRest = shopifyApp({ ...testConfig({sessionStorage}), - future: {removeRest: true}, + future: {v4_removeRest: true}, }); const {request: requestForWithoutRest} = diff --git a/packages/apps/shopify-app-remix/src/server/authenticate/public/appProxy/__tests__/authenticate.test.ts b/packages/apps/shopify-app-remix/src/server/authenticate/public/appProxy/__tests__/authenticate.test.ts index c1db30ecd5..5b86e3632e 100644 --- a/packages/apps/shopify-app-remix/src/server/authenticate/public/appProxy/__tests__/authenticate.test.ts +++ b/packages/apps/shopify-app-remix/src/server/authenticate/public/appProxy/__tests__/authenticate.test.ts @@ -308,7 +308,7 @@ describe('authenticating app proxy requests', () => { expectAdminApiClient(async () => { const shopify = shopifyApp({ ...testConfig(), - future: {removeRest: false}, + future: {v4_removeRest: false}, }); const expectedSession = await setUpValidSession(shopify.sessionStorage, { isOnline: false, @@ -323,7 +323,7 @@ describe('authenticating app proxy requests', () => { const shopifyWithoutRest = shopifyApp({ ...testConfig(), - future: {removeRest: true}, + future: {v4_removeRest: true}, }); const {admin: adminWithoutRest} = diff --git a/packages/apps/shopify-app-remix/src/server/authenticate/webhooks/__tests__/authenticate.test.ts b/packages/apps/shopify-app-remix/src/server/authenticate/webhooks/__tests__/authenticate.test.ts index ce71012f46..1e4f75a3d8 100644 --- a/packages/apps/shopify-app-remix/src/server/authenticate/webhooks/__tests__/authenticate.test.ts +++ b/packages/apps/shopify-app-remix/src/server/authenticate/webhooks/__tests__/authenticate.test.ts @@ -62,7 +62,7 @@ describe('Webhook validation', () => { const sessionStorage = new MemorySessionStorage(); const shopify = shopifyApp({ ...testConfig({sessionStorage, restResources}), - future: {removeRest: false}, + future: {v4_removeRest: false}, }); const body = {some: 'data'}; @@ -108,7 +108,7 @@ describe('Webhook validation', () => { const shopifyWithoutRest = shopifyApp({ ...testConfig({sessionStorage, restResources}), - future: {removeRest: true}, + future: {v4_removeRest: true}, }); const {admin: adminWithoutRest} = diff --git a/packages/apps/shopify-app-remix/src/server/clients/admin/factory.ts b/packages/apps/shopify-app-remix/src/server/clients/admin/factory.ts index 5aa7b0aaa5..5e04899e89 100644 --- a/packages/apps/shopify-app-remix/src/server/clients/admin/factory.ts +++ b/packages/apps/shopify-app-remix/src/server/clients/admin/factory.ts @@ -21,7 +21,7 @@ export function adminClientFactory< handleClientError, session, }: RestClientOptions): AdminApiContext { - if (params.config.future.removeRest) { + if (params.config.future.v4_removeRest) { return { graphql: graphqlClientFactory({params, session, handleClientError}), } as AdminApiContext; diff --git a/packages/apps/shopify-app-remix/src/server/clients/admin/types.ts b/packages/apps/shopify-app-remix/src/server/clients/admin/types.ts index 77b6566242..5054313991 100644 --- a/packages/apps/shopify-app-remix/src/server/clients/admin/types.ts +++ b/packages/apps/shopify-app-remix/src/server/clients/admin/types.ts @@ -1,5 +1,5 @@ -import {Session, ShopifyRestResources} from '@shopify/shopify-api'; import {AdminOperations} from '@shopify/admin-api-client'; +import {Session, ShopifyRestResources} from '@shopify/shopify-api'; import type {AppConfigArg} from 'src/server/config-types'; import type {FeatureEnabled} from 'src/server/future/flags'; @@ -24,7 +24,7 @@ export type AdminApiContext< ConfigArg extends AppConfigArg, Resources extends ShopifyRestResources = ShopifyRestResources, > = - FeatureEnabled extends true + FeatureEnabled extends true ? AdminApiContextWithoutRest : AdminApiContextWithRest; diff --git a/packages/apps/shopify-app-remix/src/server/future/flags.ts b/packages/apps/shopify-app-remix/src/server/future/flags.ts index 54d938ef25..d658da391b 100644 --- a/packages/apps/shopify-app-remix/src/server/future/flags.ts +++ b/packages/apps/shopify-app-remix/src/server/future/flags.ts @@ -44,7 +44,7 @@ export interface FutureFlags { * * @default false */ - removeRest?: boolean; + v4_removeRest?: boolean; } // When adding new flags, use this format: diff --git a/packages/apps/shopify-app-remix/src/server/unauthenticated/admin/__tests__/factory.test.ts b/packages/apps/shopify-app-remix/src/server/unauthenticated/admin/__tests__/factory.test.ts index 32bdc7c319..39e2ce1699 100644 --- a/packages/apps/shopify-app-remix/src/server/unauthenticated/admin/__tests__/factory.test.ts +++ b/packages/apps/shopify-app-remix/src/server/unauthenticated/admin/__tests__/factory.test.ts @@ -16,7 +16,7 @@ describe('unauthenticated admin context', () => { // GIVEN const shopify = shopifyApp({ ...testConfig(), - future: {removeRest: false}, + future: {v4_removeRest: false}, }); // EXPECT @@ -26,7 +26,10 @@ describe('unauthenticated admin context', () => { }); expectAdminApiClient(async () => { - const shopify = shopifyApp({...testConfig(), future: {removeRest: false}}); + const shopify = shopifyApp({ + ...testConfig(), + future: {v4_removeRest: false}, + }); const expectedSession = await setUpValidSession(shopify.sessionStorage, { isOnline: false, }); @@ -35,7 +38,7 @@ describe('unauthenticated admin context', () => { const shopifyWithoutRest = shopifyApp({ ...testConfig(), - future: {removeRest: true}, + future: {v4_removeRest: true}, }); const {admin: adminWithoutRest} = @@ -55,7 +58,7 @@ describe('unauthenticated admin context for merchant custom apps', () => { const shopify = shopifyApp({ ...config, - future: {removeRest: false}, + future: {v4_removeRest: false}, }); const expectedSession = setupValidCustomAppSession(TEST_SHOP); const {admin, session: actualSession} = @@ -63,7 +66,7 @@ describe('unauthenticated admin context for merchant custom apps', () => { const shopifyWithoutRest = shopifyApp({ ...config, - future: {removeRest: true}, + future: {v4_removeRest: true}, }); const {admin: adminWithoutRest} = await shopifyWithoutRest.unauthenticated.admin(TEST_SHOP); From 89f28ebe2540338a9a2f1c2ef0440d3cac6469f0 Mon Sep 17 00:00:00 2001 From: Richard Powell Date: Mon, 28 Oct 2024 15:12:05 -0400 Subject: [PATCH 05/13] Update Docs to rely less on REST and to warn developers away from REST --- .../docs/staticPages/admin.doc.ts | 6 ++- .../docs/staticPages/future-flags.doc.ts | 17 +++++- .../src/server/authenticate/flow/types.ts | 5 +- .../authenticate/fulfillment-service/types.ts | 48 +++-------------- .../authenticate/public/appProxy/types.ts | 2 +- .../src/server/clients/admin/types.ts | 3 ++ .../shopify-app-remix/src/server/types.ts | 52 ++++++------------- .../src/server/unauthenticated/admin/types.ts | 37 ------------- .../src/server/unauthenticated/types.ts | 5 +- 9 files changed, 52 insertions(+), 123 deletions(-) diff --git a/packages/apps/shopify-app-remix/docs/staticPages/admin.doc.ts b/packages/apps/shopify-app-remix/docs/staticPages/admin.doc.ts index 29db5497ac..94f3c5d42c 100644 --- a/packages/apps/shopify-app-remix/docs/staticPages/admin.doc.ts +++ b/packages/apps/shopify-app-remix/docs/staticPages/admin.doc.ts @@ -92,9 +92,11 @@ const data: LandingTemplateSchema = { { type: 'Generic', anchorLink: 'rest-api', - title: 'Using the REST API', + title: 'Using the REST API (Deprecated)', sectionContent: - 'Once a request is authenticated, `authenticate.admin` will return an `admin` object that contains a REST client that can interact with the [REST Admin API](/docs/api/admin-rest).' + + '**Shopify is [all-in on graphql](https://www.shopify.com/ca/partners/blog/all-in-on-graphql). In the next major release, the REST API will be removed from the `@shopify/shopify-app-remix` package.' + + 'If the `v4_removeRest` [future flag](/docs/api/shopify-app-remix/v3/guide-future-flags) is true, then the REST API will not be available.**' + + '\n\nOnce a request is authenticated, `authenticate.admin` will return an `admin` object that contains a REST client that can interact with the [REST Admin API](/docs/api/admin-rest).' + '\n\nYou can also import a set of resource classes from the `@shopify/shopify-api` package, which is included in `@shopify/shopify-app-remix`.' + '\n\nThese classes map to the individual REST endpoints, and will be returned under `admin.rest.resources`.', codeblock: { diff --git a/packages/apps/shopify-app-remix/docs/staticPages/future-flags.doc.ts b/packages/apps/shopify-app-remix/docs/staticPages/future-flags.doc.ts index 4f8344c91e..ac8a45f9c5 100644 --- a/packages/apps/shopify-app-remix/docs/staticPages/future-flags.doc.ts +++ b/packages/apps/shopify-app-remix/docs/staticPages/future-flags.doc.ts @@ -51,7 +51,7 @@ const data: LandingTemplateSchema = { anchorLink: 'breaking-changes', title: 'Breaking changes', sectionContent: - 'Similarly to unstable APIs, breaking changes will be introduced behind a future flag, but the prefix will be the next major version (e.g. `v3_`).' + + 'Similarly to unstable APIs, breaking changes will be introduced behind a future flag, but the prefix will be the next major version (e.g. `v4_`).' + '\n\nThis allows apps to prepare for the next major version ahead of time, and to gradually adopt the new APIs.' + '\n\nWhen the next major version is released, the future flag will be removed, and the old code it changes will be removed. Apps that adopted the flag before then will continue to work the same way with no new changes.', }, @@ -70,6 +70,21 @@ const data: LandingTemplateSchema = { '\n\nLearn more about this [new embedded app auth strategy](https://shopify.dev/docs/api/shopify-app-remix#embedded-auth-strategy).', isOptional: true, }, + { + name: 'v4_removeRest', + value: '', + description: + 'Methods for interacting with the admin REST API will not be returned\n\n' + + 'This affects:\n\n' + + '* `authenticate.admin(request)`\n' + + '* `authenticate.webhook(request)`\n' + + '* `authenticate.flow(request)`\n' + + '* `authenticate.appProxy(request)`\n' + + '* `authenticate.fulfillmentService(request)`\n' + + '* `unauthenticated.admin(shop)`\n\n' + + 'Learn more about this change by reading [all-in on graphql](https://www.shopify.com/ca/partners/blog/all-in-on-graphql).', + isOptional: true, + }, ], }, ], diff --git a/packages/apps/shopify-app-remix/src/server/authenticate/flow/types.ts b/packages/apps/shopify-app-remix/src/server/authenticate/flow/types.ts index 2d4f75fc3b..f08d389b87 100644 --- a/packages/apps/shopify-app-remix/src/server/authenticate/flow/types.ts +++ b/packages/apps/shopify-app-remix/src/server/authenticate/flow/types.ts @@ -14,7 +14,7 @@ export interface FlowContext< * * @example * Shopify session for the Flow request. - * Use the session associated with this request to use REST resources. + * Use the session associated with this request. * ```ts * // /app/routes/flow.tsx * import { ActionFunctionArgs } from "@remix-run/node"; @@ -23,8 +23,7 @@ export interface FlowContext< * export const action = async ({ request }: ActionFunctionArgs) => { * const { session, admin } = await authenticate.flow(request); * - * const products = await admin?.rest.resources.Product.all({ session }); - * // Use products + * console.log(session.id) * * return new Response(); * }; diff --git a/packages/apps/shopify-app-remix/src/server/authenticate/fulfillment-service/types.ts b/packages/apps/shopify-app-remix/src/server/authenticate/fulfillment-service/types.ts index a5dca3c02c..bc4995333d 100644 --- a/packages/apps/shopify-app-remix/src/server/authenticate/fulfillment-service/types.ts +++ b/packages/apps/shopify-app-remix/src/server/authenticate/fulfillment-service/types.ts @@ -17,17 +17,16 @@ export interface FulfillmentServiceContext< * Returned only if there is a session for the shop. * @example * Shopify session for the fulfillment service notification request. - * Use the session associated with this request to use REST resources. + * Use the session associated with this request. * ```ts * // /app/routes/fulfillment_service_notification.tsx * import { ActionFunctionArgs } from "@remix-run/node"; * import { authenticate } from "../shopify.server"; * - * export const action = async ({ request }: ActionFunctionArgs) => { + * export const action = async ({ request }: ActionFunctionArgs) => { * const { session, admin } = await authenticate.fulfillmentService(request); * - * const products = await admin?.rest.resources.Product.all({ session }); - * // Use products + * console.log(session.id) * * return new Response(); * }; @@ -48,44 +47,11 @@ export interface FulfillmentServiceContext< * import { authenticate } from "../shopify.server"; * * export async function action({ request }: ActionFunctionArgs) { - * const { admin } = await authenticate.fulfillmentService(request); - * const response = await admin?.graphql( - * `#graphql - * query { - * shop { - * assignedFulfillmentOrders(first: 10, assignmentStatus: FULFILLMENT_REQUESTED) { - * edges { - * node { - * id - * destination { - * firstName - * lastName - * } - * lineItems(first: 10) { - * edges { - * node { - * id - * productTitle - * sku - * remainingQuantity - * } - * } - * } - * merchantRequests(first: 10, kind: FULFILLMENT_REQUEST) { - * edges { - * node { - * message - * } - * } - * } - * } - * } - * } - * } - * }`); + * const { admin, session } = await authenticate.fulfillmentService(request); * - * const fulfillments = await response.json(); - * return json({ data: fulfillments.data }); + * console.log(session.id) + * + * return new Response(); * } * ``` */ diff --git a/packages/apps/shopify-app-remix/src/server/authenticate/public/appProxy/types.ts b/packages/apps/shopify-app-remix/src/server/authenticate/public/appProxy/types.ts index ce3f31a5c4..550fc129da 100644 --- a/packages/apps/shopify-app-remix/src/server/authenticate/public/appProxy/types.ts +++ b/packages/apps/shopify-app-remix/src/server/authenticate/public/appProxy/types.ts @@ -157,7 +157,7 @@ export interface AppProxyContextWithSession< * * @example * Interacting with the Admin API. - * Use the `admin` object to interact with the REST or GraphQL APIs. + * Use the `admin` object to interact with the admin GraphQL API. * ```ts * // app/routes/**\/.ts * import { json } from "@remix-run/node"; diff --git a/packages/apps/shopify-app-remix/src/server/clients/admin/types.ts b/packages/apps/shopify-app-remix/src/server/clients/admin/types.ts index 5054313991..45d4dd88ac 100644 --- a/packages/apps/shopify-app-remix/src/server/clients/admin/types.ts +++ b/packages/apps/shopify-app-remix/src/server/clients/admin/types.ts @@ -137,6 +137,9 @@ export interface AdminApiContextWithRest< /** * Methods for interacting with the Shopify Admin REST API * + * **Shopify is [all-in on graphql](https://www.shopify.com/ca/partners/blog/all-in-on-graphql). In the next major release, the REST API will be removed from the `@shopify/shopify-app-remix` package. + * If the `v4_removeRest` [future flag](/docs/api/shopify-app-remix/v3/guide-future-flags) is true, then the REST API will not be available.** + * * There are methods for interacting with individual REST resources. You can also make `GET`, `POST`, `PUT` and `DELETE` requests should the REST resources not meet your needs. * * {@link https://shopify.dev/docs/api/admin-rest} diff --git a/packages/apps/shopify-app-remix/src/server/types.ts b/packages/apps/shopify-app-remix/src/server/types.ts index a651f4fcc0..c7584a19e9 100644 --- a/packages/apps/shopify-app-remix/src/server/types.ts +++ b/packages/apps/shopify-app-remix/src/server/types.ts @@ -5,21 +5,21 @@ import { } from '@shopify/shopify-api'; import {SessionStorage} from '@shopify/shopify-app-session-storage'; -import type {AppConfig, AppConfigArg} from './config-types'; +import type {AuthenticateAdmin} from './authenticate/admin/types'; +import type {AuthenticateFlow} from './authenticate/flow/types'; +import {AuthenticateFulfillmentService} from './authenticate/fulfillment-service/types'; +import type {AuthenticatePublic} from './authenticate/public/types'; import type { AuthenticateWebhook, RegisterWebhooksOptions, } from './authenticate/webhooks/types'; -import type {AuthenticatePublic} from './authenticate/public/types'; -import type {AuthenticateAdmin} from './authenticate/admin/types'; -import type {Unauthenticated} from './unauthenticated/types'; -import type {AuthenticateFlow} from './authenticate/flow/types'; +import type {AppConfig, AppConfigArg} from './config-types'; import type { ApiConfigWithFutureFlags, ApiFutureFlags, FutureFlagOptions, } from './future/flags'; -import {AuthenticateFulfillmentService} from './authenticate/fulfillment-service/types'; +import type {Unauthenticated} from './unauthenticated/types'; export interface BasicParams< Future extends FutureFlagOptions = FutureFlagOptions, @@ -85,17 +85,16 @@ interface Authenticate { * * export async function loader({ request }: LoaderFunctionArgs) { * const {admin, session, sessionToken, billing} = authenticate.admin(request); + * const response = await admin.graphql(`{ shop { name } }`) * - * return json(await admin.rest.resources.Product.count({ session })); + * return json(await response.json()); * } * ``` * ```ts * // /app/shopify.server.ts * import { LATEST_API_VERSION, shopifyApp } from "@shopify/shopify-app-remix/server"; - * import { restResources } from "@shopify/shopify-api/rest/admin/2023-04"; * * const shopify = shopifyApp({ - * restResources, * // ...etc * }); * export default shopify; @@ -131,10 +130,8 @@ interface Authenticate { * ```ts * // /app/shopify.server.ts * import { LATEST_API_VERSION, shopifyApp } from "@shopify/shopify-app-remix/server"; - * import { restResources } from "@shopify/shopify-api/rest/admin/2023-04"; * * const shopify = shopifyApp({ - * restResources, * // ...etc * }); * export default shopify; @@ -155,24 +152,11 @@ interface Authenticate { * import { authenticate } from "../shopify.server"; * * export async function action({ request }: ActionFunctionArgs) { - * const { admin } = await authenticate.fulfillmentService(request); - * - * const response = await admin.graphql( - * `#graphql - * mutation acceptFulfillmentRequest { - * fulfillmentOrderAcceptFulfillmentRequest( - * id: "gid://shopify/FulfillmentOrder/5014440902678", - * message: "Reminder that tomorrow is a holiday. We won't be able to ship this until Monday."){ - * fulfillmentOrder { - * status - * requestStatus - * } - * } - * } - * ); - * - * const productData = await response.json(); - * return json({ data: productData.data }); + * const { admin, session } = await authenticate.fulfillmentService(request); + * + * console.log(session.id) + * + * return new Response(); * } * ``` * */ @@ -388,10 +372,8 @@ export interface ShopifyAppBase { * ```ts * // /app/shopify.server.ts * import { LATEST_API_VERSION, shopifyApp } from "@shopify/shopify-app-remix/server"; - * import { restResources } from "@shopify/shopify-api/rest/admin/2023-04"; * * const shopify = shopifyApp({ - * restResources, * // ...etc * }); * export default shopify; @@ -403,8 +385,9 @@ export interface ShopifyAppBase { * * export async function loader({ request }: LoaderFunctionArgs) { * const {admin, session, sessionToken, billing} = shopify.authenticate.admin(request); + * const response = admin.graphql(`{ shop { name } }`) * - * return json(await admin.rest.resources.Product.count({ session })); + * return json(await response.json()); * } * ``` */ @@ -419,10 +402,8 @@ export interface ShopifyAppBase { * ```ts * // /app/shopify.server.ts * import { LATEST_API_VERSION, shopifyApp } from "@shopify/shopify-app-remix/server"; - * import { restResources } from "@shopify/shopify-api/rest/admin/2023-04"; * * const shopify = shopifyApp({ - * restResources, * // ...etc * }); * export default shopify; @@ -436,8 +417,9 @@ export interface ShopifyAppBase { * export async function loader({ request }: LoaderFunctionArgs) { * const shop = await authenticateExternal(request) * const {admin} = await shopify.unauthenticated.admin(shop); + * const response = admin.graphql(`{ shop { currencyCode } }`) * - * return json(await admin.rest.resources.Product.count({ session })); + * return json(await response.json()); * } * ``` */ diff --git a/packages/apps/shopify-app-remix/src/server/unauthenticated/admin/types.ts b/packages/apps/shopify-app-remix/src/server/unauthenticated/admin/types.ts index e506f28522..73f3fbe8fb 100644 --- a/packages/apps/shopify-app-remix/src/server/unauthenticated/admin/types.ts +++ b/packages/apps/shopify-app-remix/src/server/unauthenticated/admin/types.ts @@ -36,42 +36,6 @@ export interface UnauthenticatedAdminContext< * Methods for interacting with the GraphQL / REST Admin APIs for the given store. * * @example - * Performing a GET request to the REST API. - * Use `admin.rest.get` to make custom requests to make a request to to the `customer/count` endpoint - * - * ```ts - * // /app/routes/**\/*.ts - * import { LoaderFunctionArgs, json } from "@remix-run/node"; - * import { unauthenticated } from "../shopify.server"; - * - * export const loader = async ({ request }: LoaderFunctionArgs) => { - * const { admin, session } = await unauthenticated.admin(request); - * - * const response = await admin.rest.get( - * { - * path: "/customers/count.json" - * } - * ); - * const customers = await response.json(); - * - * return json({ customers }); - * }; - * ``` - * - * ```ts - * // /app/shopify.server.ts - * import { shopifyApp } from "@shopify/shopify-app-remix/server"; - * import { restResources } from "@shopify/shopify-api/rest/admin/2023-04"; - * - * const shopify = shopifyApp({ - * restResources, - * // ...etc - * }); - * - * export default shopify; - * export const unauthenticated = shopify.unauthenticated; - * ``` - * @example * Querying the GraphQL API. * Use `admin.graphql` to make query / mutation requests. * ```ts @@ -104,7 +68,6 @@ export interface UnauthenticatedAdminContext< * import { shopifyApp } from "@shopify/shopify-app-remix/server"; * * const shopify = shopifyApp({ - * restResources, * // ...etc * }); * export default shopify; diff --git a/packages/apps/shopify-app-remix/src/server/unauthenticated/types.ts b/packages/apps/shopify-app-remix/src/server/unauthenticated/types.ts index d690b11245..b6c1457b16 100644 --- a/packages/apps/shopify-app-remix/src/server/unauthenticated/types.ts +++ b/packages/apps/shopify-app-remix/src/server/unauthenticated/types.ts @@ -21,10 +21,8 @@ export interface Unauthenticated< * ```ts * // /app/shopify.server.ts * import { LATEST_API_VERSION, shopifyApp } from "@shopify/shopify-app-remix/server"; - * import { restResources } from "@shopify/shopify-api/rest/admin/2023-04"; * * const shopify = shopifyApp({ - * restResources, * // ...etc * }); * export default shopify; @@ -38,8 +36,9 @@ export interface Unauthenticated< * export async function loader({ request }: LoaderFunctionArgs) { * const shop = await authenticateExternal(request) * const {admin} = await shopify.unauthenticated.admin(shop); + * const response = await admin.graphql("{ shop { name} }") * - * return json(await admin.rest.resources.Product.count({ session })); + * return json(await response.json()); * } * ``` */ From 8d78d9e728bef480d7ebb471688950871c7bb957 Mon Sep 17 00:00:00 2001 From: Richard Powell Date: Mon, 28 Oct 2024 15:37:06 -0400 Subject: [PATCH 06/13] Mark some REST API's as deprecated --- .../shopify-app-remix/src/server/clients/admin/rest.ts | 8 ++++++++ .../shopify-app-remix/src/server/clients/admin/types.ts | 3 +-- 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/packages/apps/shopify-app-remix/src/server/clients/admin/rest.ts b/packages/apps/shopify-app-remix/src/server/clients/admin/rest.ts index d9cc75bb8f..eea484e8d4 100644 --- a/packages/apps/shopify-app-remix/src/server/clients/admin/rest.ts +++ b/packages/apps/shopify-app-remix/src/server/clients/admin/rest.ts @@ -66,6 +66,8 @@ class RemixRestClient { /** * Performs a GET request on the given path. + * + * @deprecated In the next major release REST will be removed from this package. Please see [all-in on graphql](https://www.shopify.com/ca/partners/blog/all-in-on-graphql). */ public async get(params: GetRequestParams) { return this.makeRequest({ @@ -76,6 +78,8 @@ class RemixRestClient { /** * Performs a POST request on the given path. + * + * @deprecated In the next major release REST will be removed from this package. Please see [all-in on graphql](https://www.shopify.com/ca/partners/blog/all-in-on-graphql). */ public async post(params: PostRequestParams) { return this.makeRequest({ @@ -86,6 +90,8 @@ class RemixRestClient { /** * Performs a PUT request on the given path. + * + * @deprecated In the next major release REST will be removed from this package. Please see [all-in on graphql](https://www.shopify.com/ca/partners/blog/all-in-on-graphql). */ public async put(params: PutRequestParams) { return this.makeRequest({ @@ -96,6 +102,8 @@ class RemixRestClient { /** * Performs a DELETE request on the given path. + * + * @deprecated In the next major release REST will be removed from this package. Please see [all-in on graphql](https://www.shopify.com/ca/partners/blog/all-in-on-graphql). */ public async delete(params: DeleteRequestParams) { return this.makeRequest({ diff --git a/packages/apps/shopify-app-remix/src/server/clients/admin/types.ts b/packages/apps/shopify-app-remix/src/server/clients/admin/types.ts index 45d4dd88ac..628f5c5cbf 100644 --- a/packages/apps/shopify-app-remix/src/server/clients/admin/types.ts +++ b/packages/apps/shopify-app-remix/src/server/clients/admin/types.ts @@ -137,8 +137,7 @@ export interface AdminApiContextWithRest< /** * Methods for interacting with the Shopify Admin REST API * - * **Shopify is [all-in on graphql](https://www.shopify.com/ca/partners/blog/all-in-on-graphql). In the next major release, the REST API will be removed from the `@shopify/shopify-app-remix` package. - * If the `v4_removeRest` [future flag](/docs/api/shopify-app-remix/v3/guide-future-flags) is true, then the REST API will not be available.** + * @deprecated In the next major release REST will be removed from this package. Please see [all-in on graphql](https://www.shopify.com/ca/partners/blog/all-in-on-graphql). * * There are methods for interacting with individual REST resources. You can also make `GET`, `POST`, `PUT` and `DELETE` requests should the REST resources not meet your needs. * From 9430529641b29d69d6a3f26d667cce6cc088a186 Mon Sep 17 00:00:00 2001 From: Richard Powell Date: Tue, 29 Oct 2024 09:41:37 -0400 Subject: [PATCH 07/13] Added an upcoming_change for REST removal --- packages/apps/shopify-app-remix/docs/upcoming_changes.md | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/packages/apps/shopify-app-remix/docs/upcoming_changes.md b/packages/apps/shopify-app-remix/docs/upcoming_changes.md index eeb37da847..0a65175652 100644 --- a/packages/apps/shopify-app-remix/docs/upcoming_changes.md +++ b/packages/apps/shopify-app-remix/docs/upcoming_changes.md @@ -22,3 +22,12 @@ This package will automatically use token exchange, but that only works if [Shop Before updating this package in your app, please ensure you've enabled managed installation. For more details on how this works, please see the [new embedded authorization strategy](../README.md#new-embedded-authorization-strategy) section in the README. + + +## Removing the REST API + +> [!NOTE] +> The `v4_removeRest` future flag removed the REST API. +> If you've already enabled the flag, you don't need to follow these instructions. + +In the next major release, the REST API will be removed from the `@shopify/shopify-app-remix` package. Please use the GraphQL API instead. See [Shopify is all-in on graphql](https://www.shopify.com/ca/partners/blog/all-in-on-graphql) for more information. From 4bba5d4eeb9e6fdf0d18ce0c8859dbe5db55b1a9 Mon Sep 17 00:00:00 2001 From: Richard Powell Date: Tue, 29 Oct 2024 10:04:17 -0400 Subject: [PATCH 08/13] Added changeset --- .changeset/eight-mails-hammer.md | 9 +++++++++ 1 file changed, 9 insertions(+) create mode 100644 .changeset/eight-mails-hammer.md diff --git a/.changeset/eight-mails-hammer.md b/.changeset/eight-mails-hammer.md new file mode 100644 index 0000000000..ac83e0c106 --- /dev/null +++ b/.changeset/eight-mails-hammer.md @@ -0,0 +1,9 @@ +--- +'@shopify/shopify-app-remix': minor +--- + +Added `v4_removeRest` future flag. + +When `v4_removeRest` is `true`, the REST API will no longer be available. Please use the GraphQL API instead. See [Shopify is all-in on graphql](https://www.shopify.com/ca/partners/blog/all-in-on-graphql) for more information. + +If your app doesn't use the REST API, you can safely set `v4_removeRest` to `true` and be ready for the next major release. If your app does use the REST API, you should migrate to the GraphQL API and then set `v4_removeRest` to `true`. From 6c6459354db82976b7509ff7011932a85a0e6edb Mon Sep 17 00:00:00 2001 From: Richard Powell Date: Wed, 6 Nov 2024 09:37:12 -0500 Subject: [PATCH 09/13] Remove v4_ from the removeRest API --- .changeset/eight-mails-hammer.md | 6 +++--- .../shopify-app-remix/docs/staticPages/admin.doc.ts | 2 +- .../docs/staticPages/future-flags.doc.ts | 4 ++-- .../apps/shopify-app-remix/docs/upcoming_changes.md | 4 ++-- .../server/__test-helpers/expect-admin-api-client.ts | 4 ++-- .../src/server/__test-helpers/setup-embedded-flow.ts | 4 ++-- .../src/server/__test-helpers/setup-fetch-flow.ts | 2 +- .../server/__test-helpers/setup-non-embedded-flow.ts | 2 +- .../src/server/__test-helpers/test-config.ts | 2 +- .../src/server/__tests__/shopify-app.test.ts | 2 +- .../admin/__tests__/session-token-header-path.test.ts | 4 ++-- .../__tests__/auth-code-flow/authenticate.test.ts | 4 ++-- .../__tests__/merchant-custom/admin-client.test.ts | 4 ++-- .../__tests__/token-exchange/admin-client.test.ts | 4 ++-- .../__tests__/token-exchange/authenticate.test.ts | 2 +- .../authenticate/flow/__tests__/authenticate.test.ts | 4 ++-- .../fulfillment-service/__tests__/authenticate.test.ts | 4 ++-- .../public/appProxy/__tests__/authenticate.test.ts | 4 ++-- .../webhooks/__tests__/authenticate.test.ts | 4 ++-- .../src/server/clients/admin/factory.ts | 2 +- .../src/server/clients/admin/types.ts | 2 +- .../apps/shopify-app-remix/src/server/future/flags.ts | 2 +- .../unauthenticated/admin/__tests__/factory.test.ts | 10 +++++----- 23 files changed, 41 insertions(+), 41 deletions(-) diff --git a/.changeset/eight-mails-hammer.md b/.changeset/eight-mails-hammer.md index ac83e0c106..54ad79a0ef 100644 --- a/.changeset/eight-mails-hammer.md +++ b/.changeset/eight-mails-hammer.md @@ -2,8 +2,8 @@ '@shopify/shopify-app-remix': minor --- -Added `v4_removeRest` future flag. +Added `removeRest` future flag. -When `v4_removeRest` is `true`, the REST API will no longer be available. Please use the GraphQL API instead. See [Shopify is all-in on graphql](https://www.shopify.com/ca/partners/blog/all-in-on-graphql) for more information. +When `removeRest` is `true`, the REST API will no longer be available. Please use the GraphQL API instead. See [Shopify is all-in on graphql](https://www.shopify.com/ca/partners/blog/all-in-on-graphql) for more information. -If your app doesn't use the REST API, you can safely set `v4_removeRest` to `true` and be ready for the next major release. If your app does use the REST API, you should migrate to the GraphQL API and then set `v4_removeRest` to `true`. +If your app doesn't use the REST API, you can safely set `v4_removeRest` to `true` and be ready for a future major release. If your app does use the REST API, you should migrate to the GraphQL API and then set `removeRest` to `true`. diff --git a/packages/apps/shopify-app-remix/docs/staticPages/admin.doc.ts b/packages/apps/shopify-app-remix/docs/staticPages/admin.doc.ts index 94f3c5d42c..9c49445196 100644 --- a/packages/apps/shopify-app-remix/docs/staticPages/admin.doc.ts +++ b/packages/apps/shopify-app-remix/docs/staticPages/admin.doc.ts @@ -95,7 +95,7 @@ const data: LandingTemplateSchema = { title: 'Using the REST API (Deprecated)', sectionContent: '**Shopify is [all-in on graphql](https://www.shopify.com/ca/partners/blog/all-in-on-graphql). In the next major release, the REST API will be removed from the `@shopify/shopify-app-remix` package.' + - 'If the `v4_removeRest` [future flag](/docs/api/shopify-app-remix/v3/guide-future-flags) is true, then the REST API will not be available.**' + + 'If the `removeRest` [future flag](/docs/api/shopify-app-remix/v3/guide-future-flags) is true, then the REST API will not be available.**' + '\n\nOnce a request is authenticated, `authenticate.admin` will return an `admin` object that contains a REST client that can interact with the [REST Admin API](/docs/api/admin-rest).' + '\n\nYou can also import a set of resource classes from the `@shopify/shopify-api` package, which is included in `@shopify/shopify-app-remix`.' + '\n\nThese classes map to the individual REST endpoints, and will be returned under `admin.rest.resources`.', diff --git a/packages/apps/shopify-app-remix/docs/staticPages/future-flags.doc.ts b/packages/apps/shopify-app-remix/docs/staticPages/future-flags.doc.ts index ac8a45f9c5..dbb99b82b3 100644 --- a/packages/apps/shopify-app-remix/docs/staticPages/future-flags.doc.ts +++ b/packages/apps/shopify-app-remix/docs/staticPages/future-flags.doc.ts @@ -51,7 +51,7 @@ const data: LandingTemplateSchema = { anchorLink: 'breaking-changes', title: 'Breaking changes', sectionContent: - 'Similarly to unstable APIs, breaking changes will be introduced behind a future flag, but the prefix will be the next major version (e.g. `v4_`).' + + 'Similarly to unstable APIs, breaking changes will be introduced behind a future flag, but the prefix will be the next major version (e.g. ``).' + '\n\nThis allows apps to prepare for the next major version ahead of time, and to gradually adopt the new APIs.' + '\n\nWhen the next major version is released, the future flag will be removed, and the old code it changes will be removed. Apps that adopted the flag before then will continue to work the same way with no new changes.', }, @@ -71,7 +71,7 @@ const data: LandingTemplateSchema = { isOptional: true, }, { - name: 'v4_removeRest', + name: 'removeRest', value: '', description: 'Methods for interacting with the admin REST API will not be returned\n\n' + diff --git a/packages/apps/shopify-app-remix/docs/upcoming_changes.md b/packages/apps/shopify-app-remix/docs/upcoming_changes.md index 0a65175652..756b8b4a8d 100644 --- a/packages/apps/shopify-app-remix/docs/upcoming_changes.md +++ b/packages/apps/shopify-app-remix/docs/upcoming_changes.md @@ -27,7 +27,7 @@ For more details on how this works, please see the [new embedded authorization s ## Removing the REST API > [!NOTE] -> The `v4_removeRest` future flag removed the REST API. +> The `removeRest` future flag removed the REST API. > If you've already enabled the flag, you don't need to follow these instructions. -In the next major release, the REST API will be removed from the `@shopify/shopify-app-remix` package. Please use the GraphQL API instead. See [Shopify is all-in on graphql](https://www.shopify.com/ca/partners/blog/all-in-on-graphql) for more information. +The REST API will be removed from this package. Please use the GraphQL API instead. See [Shopify is all-in on graphql](https://www.shopify.com/ca/partners/blog/all-in-on-graphql) for more information. diff --git a/packages/apps/shopify-app-remix/src/server/__test-helpers/expect-admin-api-client.ts b/packages/apps/shopify-app-remix/src/server/__test-helpers/expect-admin-api-client.ts index 020b0f4bfd..6223912bb8 100644 --- a/packages/apps/shopify-app-remix/src/server/__test-helpers/expect-admin-api-client.ts +++ b/packages/apps/shopify-app-remix/src/server/__test-helpers/expect-admin-api-client.ts @@ -19,7 +19,7 @@ export function expectAdminApiClient( actualSession: Session; }>, ) { - describe('when future.v4_removeRest is falsey there is a REST client', () => { + describe('when future.removeRest is falsey there is a REST client', () => { it('can perform GET requests', async () => { // GIVEN const {admin} = await factory(); @@ -91,7 +91,7 @@ export function expectAdminApiClient( }); }); - describe('when future.v4_removeRest is truthy', () => { + describe('when future.removeRest is truthy', () => { it('does not include a rest property on the admin object', async () => { // GIVEN const {adminWithoutRest} = await factory(); diff --git a/packages/apps/shopify-app-remix/src/server/__test-helpers/setup-embedded-flow.ts b/packages/apps/shopify-app-remix/src/server/__test-helpers/setup-embedded-flow.ts index 2228ca87f7..9faf240154 100644 --- a/packages/apps/shopify-app-remix/src/server/__test-helpers/setup-embedded-flow.ts +++ b/packages/apps/shopify-app-remix/src/server/__test-helpers/setup-embedded-flow.ts @@ -13,7 +13,7 @@ export async function setUpEmbeddedFlow() { restResources, }), future: { - v4_removeRest: false, + removeRest: false, unstable_newEmbeddedAuthStrategy: false, wip_optionalScopesApi: true, }, @@ -41,7 +41,7 @@ export async function setUpEmbeddedFlowWithRemoveRestFlag() { restResources, }), future: { - v4_removeRest: true, + removeRest: true, unstable_newEmbeddedAuthStrategy: false, wip_optionalScopesApi: true, }, diff --git a/packages/apps/shopify-app-remix/src/server/__test-helpers/setup-fetch-flow.ts b/packages/apps/shopify-app-remix/src/server/__test-helpers/setup-fetch-flow.ts index be0757069f..92598a8c61 100644 --- a/packages/apps/shopify-app-remix/src/server/__test-helpers/setup-fetch-flow.ts +++ b/packages/apps/shopify-app-remix/src/server/__test-helpers/setup-fetch-flow.ts @@ -15,7 +15,7 @@ export async function setUpFetchFlow(flags?: { ...testConfig({ restResources, }), - future: {...flags, v4_removeRest: false, wip_optionalScopesApi: true}, + future: {...flags, removeRest: false, wip_optionalScopesApi: true}, }); await setUpValidSession(shopify.sessionStorage); diff --git a/packages/apps/shopify-app-remix/src/server/__test-helpers/setup-non-embedded-flow.ts b/packages/apps/shopify-app-remix/src/server/__test-helpers/setup-non-embedded-flow.ts index 506d99802d..7f50d7dfef 100644 --- a/packages/apps/shopify-app-remix/src/server/__test-helpers/setup-non-embedded-flow.ts +++ b/packages/apps/shopify-app-remix/src/server/__test-helpers/setup-non-embedded-flow.ts @@ -11,7 +11,7 @@ import {testConfig} from './test-config'; export async function setUpNonEmbeddedFlow() { const shopify = shopifyApp({ ...testConfig({restResources, isEmbeddedApp: false}), - future: {v4_removeRest: false, wip_optionalScopesApi: true}, + future: {removeRest: false, wip_optionalScopesApi: true}, }); const session = await setUpValidSession(shopify.sessionStorage); diff --git a/packages/apps/shopify-app-remix/src/server/__test-helpers/test-config.ts b/packages/apps/shopify-app-remix/src/server/__test-helpers/test-config.ts index 2737ba7dd0..8cff2850db 100644 --- a/packages/apps/shopify-app-remix/src/server/__test-helpers/test-config.ts +++ b/packages/apps/shopify-app-remix/src/server/__test-helpers/test-config.ts @@ -15,7 +15,7 @@ import {testConfig as testConfigImport} from '../test-helpers/test-config'; const TEST_FUTURE_FLAGS: Required<{[key in keyof FutureFlags]: true}> = { unstable_newEmbeddedAuthStrategy: true, wip_optionalScopesApi: true, - v4_removeRest: true, + removeRest: true, } as const; // Override the helper's future flags and logger settings for our purposes diff --git a/packages/apps/shopify-app-remix/src/server/__tests__/shopify-app.test.ts b/packages/apps/shopify-app-remix/src/server/__tests__/shopify-app.test.ts index d9288ba239..27b5e8e26d 100644 --- a/packages/apps/shopify-app-remix/src/server/__tests__/shopify-app.test.ts +++ b/packages/apps/shopify-app-remix/src/server/__tests__/shopify-app.test.ts @@ -29,7 +29,7 @@ describe('shopifyApp', () => { // GIVEN const shopify = shopifyApp({ ...testConfig(), - future: {v4_removeRest: false}, + future: {removeRest: false}, }); // THEN diff --git a/packages/apps/shopify-app-remix/src/server/authenticate/admin/__tests__/session-token-header-path.test.ts b/packages/apps/shopify-app-remix/src/server/authenticate/admin/__tests__/session-token-header-path.test.ts index 0b2f88a4b6..29ad79ae03 100644 --- a/packages/apps/shopify-app-remix/src/server/authenticate/admin/__tests__/session-token-header-path.test.ts +++ b/packages/apps/shopify-app-remix/src/server/authenticate/admin/__tests__/session-token-header-path.test.ts @@ -38,7 +38,7 @@ describe('authorize.session token header path', () => { // GIVEN const shopify = shopifyApp({ ...testConfig({useOnlineTokens: isOnline}), - future: {v4_removeRest: false}, + future: {removeRest: false}, }); const testSession = await setUpValidSession(shopify.sessionStorage, { @@ -63,7 +63,7 @@ describe('authorize.session token header path', () => { // GIVEN const shopify = shopifyApp({ ...testConfig({isEmbeddedApp: false, useOnlineTokens: isOnline}), - future: {v4_removeRest: false}, + future: {removeRest: false}, }); let testSession: Session; diff --git a/packages/apps/shopify-app-remix/src/server/authenticate/admin/strategies/__tests__/auth-code-flow/authenticate.test.ts b/packages/apps/shopify-app-remix/src/server/authenticate/admin/strategies/__tests__/auth-code-flow/authenticate.test.ts index 3351d73674..73a6527063 100644 --- a/packages/apps/shopify-app-remix/src/server/authenticate/admin/strategies/__tests__/auth-code-flow/authenticate.test.ts +++ b/packages/apps/shopify-app-remix/src/server/authenticate/admin/strategies/__tests__/auth-code-flow/authenticate.test.ts @@ -103,7 +103,7 @@ describe('authenticate', () => { future: {unstable_newEmbeddedAuthStrategy: false}, isEmbeddedApp: true, }), - future: {v4_removeRest: false}, + future: {removeRest: false}, }); let testSession: Session; @@ -133,7 +133,7 @@ describe('authenticate', () => { ...testConfig({ isEmbeddedApp: false, }), - future: {v4_removeRest: false}, + future: {removeRest: false}, }); let testSession: Session; diff --git a/packages/apps/shopify-app-remix/src/server/authenticate/admin/strategies/__tests__/merchant-custom/admin-client.test.ts b/packages/apps/shopify-app-remix/src/server/authenticate/admin/strategies/__tests__/merchant-custom/admin-client.test.ts index 2722aae538..47d341fee1 100644 --- a/packages/apps/shopify-app-remix/src/server/authenticate/admin/strategies/__tests__/merchant-custom/admin-client.test.ts +++ b/packages/apps/shopify-app-remix/src/server/authenticate/admin/strategies/__tests__/merchant-custom/admin-client.test.ts @@ -95,7 +95,7 @@ async function setUpMerchantCustomFlow() { distribution: AppDistribution.ShopifyAdmin, adminApiAccessToken: 'test-token', }), - future: {v4_removeRest: false}, + future: {removeRest: false}, }); const expectedSession = setupValidCustomAppSession(TEST_SHOP); @@ -117,7 +117,7 @@ async function setUpMerchantCustomFlowWithRemoveRestFlag() { distribution: AppDistribution.ShopifyAdmin, adminApiAccessToken: 'test-token', }), - future: {v4_removeRest: true}, + future: {removeRest: true}, }); const request = new Request(`${APP_URL}?shop=${TEST_SHOP}`); diff --git a/packages/apps/shopify-app-remix/src/server/authenticate/admin/strategies/__tests__/token-exchange/admin-client.test.ts b/packages/apps/shopify-app-remix/src/server/authenticate/admin/strategies/__tests__/token-exchange/admin-client.test.ts index 8a6126e988..a5e1bce6a7 100644 --- a/packages/apps/shopify-app-remix/src/server/authenticate/admin/strategies/__tests__/token-exchange/admin-client.test.ts +++ b/packages/apps/shopify-app-remix/src/server/authenticate/admin/strategies/__tests__/token-exchange/admin-client.test.ts @@ -131,7 +131,7 @@ async function setUpDocumentFlow() { future: { ...config.future, unstable_newEmbeddedAuthStrategy: true, - v4_removeRest: false, + removeRest: false, }, }); const expectedSession = await setUpValidSession(shopify.sessionStorage); @@ -158,7 +158,7 @@ async function setUpDocumentFlowWithRemoveRestFlag() { future: { ...config.future, unstable_newEmbeddedAuthStrategy: true, - v4_removeRest: true, + removeRest: true, }, }); diff --git a/packages/apps/shopify-app-remix/src/server/authenticate/admin/strategies/__tests__/token-exchange/authenticate.test.ts b/packages/apps/shopify-app-remix/src/server/authenticate/admin/strategies/__tests__/token-exchange/authenticate.test.ts index 05b06f46c1..9fc4765e34 100644 --- a/packages/apps/shopify-app-remix/src/server/authenticate/admin/strategies/__tests__/token-exchange/authenticate.test.ts +++ b/packages/apps/shopify-app-remix/src/server/authenticate/admin/strategies/__tests__/token-exchange/authenticate.test.ts @@ -130,7 +130,7 @@ describe('authenticate', () => { // GIVEN const shopify = shopifyApp({ ...testConfig({useOnlineTokens: isOnline}), - future: {v4_removeRest: false}, + future: {removeRest: false}, }); let testSession: Session; diff --git a/packages/apps/shopify-app-remix/src/server/authenticate/flow/__tests__/authenticate.test.ts b/packages/apps/shopify-app-remix/src/server/authenticate/flow/__tests__/authenticate.test.ts index 8382a78dab..acb419d470 100644 --- a/packages/apps/shopify-app-remix/src/server/authenticate/flow/__tests__/authenticate.test.ts +++ b/packages/apps/shopify-app-remix/src/server/authenticate/flow/__tests__/authenticate.test.ts @@ -109,7 +109,7 @@ describe('authenticating flow requests', () => { const sessionStorage = new MemorySessionStorage(); const shopify = shopifyApp({ ...testConfig({sessionStorage}), - future: {v4_removeRest: false}, + future: {removeRest: false}, }); const {request, session: expectedSession} = @@ -120,7 +120,7 @@ describe('authenticating flow requests', () => { const shopifyWithoutRest = shopifyApp({ ...testConfig({sessionStorage}), - future: {v4_removeRest: true}, + future: {removeRest: true}, }); const {request: requestForWithoutRest} = diff --git a/packages/apps/shopify-app-remix/src/server/authenticate/fulfillment-service/__tests__/authenticate.test.ts b/packages/apps/shopify-app-remix/src/server/authenticate/fulfillment-service/__tests__/authenticate.test.ts index e9ce7ce1d9..b3ef310c64 100644 --- a/packages/apps/shopify-app-remix/src/server/authenticate/fulfillment-service/__tests__/authenticate.test.ts +++ b/packages/apps/shopify-app-remix/src/server/authenticate/fulfillment-service/__tests__/authenticate.test.ts @@ -122,7 +122,7 @@ const MERCHANT_CUSTOM_APP_CONFIG = { const sessionStorage = new MemorySessionStorage(); const shopify = shopifyApp({ ...testConfig({sessionStorage}), - future: {v4_removeRest: false}, + future: {removeRest: false}, }); const {request, session: expectedSession} = @@ -133,7 +133,7 @@ const MERCHANT_CUSTOM_APP_CONFIG = { const shopifyWithoutRest = shopifyApp({ ...testConfig({sessionStorage}), - future: {v4_removeRest: true}, + future: {removeRest: true}, }); const {request: requestForWithoutRest} = diff --git a/packages/apps/shopify-app-remix/src/server/authenticate/public/appProxy/__tests__/authenticate.test.ts b/packages/apps/shopify-app-remix/src/server/authenticate/public/appProxy/__tests__/authenticate.test.ts index 5b86e3632e..c1db30ecd5 100644 --- a/packages/apps/shopify-app-remix/src/server/authenticate/public/appProxy/__tests__/authenticate.test.ts +++ b/packages/apps/shopify-app-remix/src/server/authenticate/public/appProxy/__tests__/authenticate.test.ts @@ -308,7 +308,7 @@ describe('authenticating app proxy requests', () => { expectAdminApiClient(async () => { const shopify = shopifyApp({ ...testConfig(), - future: {v4_removeRest: false}, + future: {removeRest: false}, }); const expectedSession = await setUpValidSession(shopify.sessionStorage, { isOnline: false, @@ -323,7 +323,7 @@ describe('authenticating app proxy requests', () => { const shopifyWithoutRest = shopifyApp({ ...testConfig(), - future: {v4_removeRest: true}, + future: {removeRest: true}, }); const {admin: adminWithoutRest} = diff --git a/packages/apps/shopify-app-remix/src/server/authenticate/webhooks/__tests__/authenticate.test.ts b/packages/apps/shopify-app-remix/src/server/authenticate/webhooks/__tests__/authenticate.test.ts index 1e4f75a3d8..ce71012f46 100644 --- a/packages/apps/shopify-app-remix/src/server/authenticate/webhooks/__tests__/authenticate.test.ts +++ b/packages/apps/shopify-app-remix/src/server/authenticate/webhooks/__tests__/authenticate.test.ts @@ -62,7 +62,7 @@ describe('Webhook validation', () => { const sessionStorage = new MemorySessionStorage(); const shopify = shopifyApp({ ...testConfig({sessionStorage, restResources}), - future: {v4_removeRest: false}, + future: {removeRest: false}, }); const body = {some: 'data'}; @@ -108,7 +108,7 @@ describe('Webhook validation', () => { const shopifyWithoutRest = shopifyApp({ ...testConfig({sessionStorage, restResources}), - future: {v4_removeRest: true}, + future: {removeRest: true}, }); const {admin: adminWithoutRest} = diff --git a/packages/apps/shopify-app-remix/src/server/clients/admin/factory.ts b/packages/apps/shopify-app-remix/src/server/clients/admin/factory.ts index 5e04899e89..5aa7b0aaa5 100644 --- a/packages/apps/shopify-app-remix/src/server/clients/admin/factory.ts +++ b/packages/apps/shopify-app-remix/src/server/clients/admin/factory.ts @@ -21,7 +21,7 @@ export function adminClientFactory< handleClientError, session, }: RestClientOptions): AdminApiContext { - if (params.config.future.v4_removeRest) { + if (params.config.future.removeRest) { return { graphql: graphqlClientFactory({params, session, handleClientError}), } as AdminApiContext; diff --git a/packages/apps/shopify-app-remix/src/server/clients/admin/types.ts b/packages/apps/shopify-app-remix/src/server/clients/admin/types.ts index 628f5c5cbf..6a57ee0faf 100644 --- a/packages/apps/shopify-app-remix/src/server/clients/admin/types.ts +++ b/packages/apps/shopify-app-remix/src/server/clients/admin/types.ts @@ -24,7 +24,7 @@ export type AdminApiContext< ConfigArg extends AppConfigArg, Resources extends ShopifyRestResources = ShopifyRestResources, > = - FeatureEnabled extends true + FeatureEnabled extends true ? AdminApiContextWithoutRest : AdminApiContextWithRest; diff --git a/packages/apps/shopify-app-remix/src/server/future/flags.ts b/packages/apps/shopify-app-remix/src/server/future/flags.ts index d658da391b..54d938ef25 100644 --- a/packages/apps/shopify-app-remix/src/server/future/flags.ts +++ b/packages/apps/shopify-app-remix/src/server/future/flags.ts @@ -44,7 +44,7 @@ export interface FutureFlags { * * @default false */ - v4_removeRest?: boolean; + removeRest?: boolean; } // When adding new flags, use this format: diff --git a/packages/apps/shopify-app-remix/src/server/unauthenticated/admin/__tests__/factory.test.ts b/packages/apps/shopify-app-remix/src/server/unauthenticated/admin/__tests__/factory.test.ts index 39e2ce1699..b02e112a32 100644 --- a/packages/apps/shopify-app-remix/src/server/unauthenticated/admin/__tests__/factory.test.ts +++ b/packages/apps/shopify-app-remix/src/server/unauthenticated/admin/__tests__/factory.test.ts @@ -16,7 +16,7 @@ describe('unauthenticated admin context', () => { // GIVEN const shopify = shopifyApp({ ...testConfig(), - future: {v4_removeRest: false}, + future: {removeRest: false}, }); // EXPECT @@ -28,7 +28,7 @@ describe('unauthenticated admin context', () => { expectAdminApiClient(async () => { const shopify = shopifyApp({ ...testConfig(), - future: {v4_removeRest: false}, + future: {removeRest: false}, }); const expectedSession = await setUpValidSession(shopify.sessionStorage, { isOnline: false, @@ -38,7 +38,7 @@ describe('unauthenticated admin context', () => { const shopifyWithoutRest = shopifyApp({ ...testConfig(), - future: {v4_removeRest: true}, + future: {removeRest: true}, }); const {admin: adminWithoutRest} = @@ -58,7 +58,7 @@ describe('unauthenticated admin context for merchant custom apps', () => { const shopify = shopifyApp({ ...config, - future: {v4_removeRest: false}, + future: {removeRest: false}, }); const expectedSession = setupValidCustomAppSession(TEST_SHOP); const {admin, session: actualSession} = @@ -66,7 +66,7 @@ describe('unauthenticated admin context for merchant custom apps', () => { const shopifyWithoutRest = shopifyApp({ ...config, - future: {v4_removeRest: true}, + future: {removeRest: true}, }); const {admin: adminWithoutRest} = await shopifyWithoutRest.unauthenticated.admin(TEST_SHOP); From a21e1cc60a73561c672cc2c290b74ff64c91b1ab Mon Sep 17 00:00:00 2001 From: Richard Powell Date: Mon, 18 Nov 2024 13:41:20 -0500 Subject: [PATCH 10/13] Don't commit the removal of REST to the next major release. --- .../shopify-app-remix/src/server/clients/admin/rest.ts | 8 ++++---- .../shopify-app-remix/src/server/clients/admin/types.ts | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/packages/apps/shopify-app-remix/src/server/clients/admin/rest.ts b/packages/apps/shopify-app-remix/src/server/clients/admin/rest.ts index eea484e8d4..513d3828da 100644 --- a/packages/apps/shopify-app-remix/src/server/clients/admin/rest.ts +++ b/packages/apps/shopify-app-remix/src/server/clients/admin/rest.ts @@ -67,7 +67,7 @@ class RemixRestClient { /** * Performs a GET request on the given path. * - * @deprecated In the next major release REST will be removed from this package. Please see [all-in on graphql](https://www.shopify.com/ca/partners/blog/all-in-on-graphql). + * @deprecated In a future major release REST will be removed from this package. Please see [all-in on graphql](https://www.shopify.com/ca/partners/blog/all-in-on-graphql). */ public async get(params: GetRequestParams) { return this.makeRequest({ @@ -79,7 +79,7 @@ class RemixRestClient { /** * Performs a POST request on the given path. * - * @deprecated In the next major release REST will be removed from this package. Please see [all-in on graphql](https://www.shopify.com/ca/partners/blog/all-in-on-graphql). + * @deprecated In a future major release REST will be removed from this package. Please see [all-in on graphql](https://www.shopify.com/ca/partners/blog/all-in-on-graphql). */ public async post(params: PostRequestParams) { return this.makeRequest({ @@ -91,7 +91,7 @@ class RemixRestClient { /** * Performs a PUT request on the given path. * - * @deprecated In the next major release REST will be removed from this package. Please see [all-in on graphql](https://www.shopify.com/ca/partners/blog/all-in-on-graphql). + * @deprecated In a future major release REST will be removed from this package. Please see [all-in on graphql](https://www.shopify.com/ca/partners/blog/all-in-on-graphql). */ public async put(params: PutRequestParams) { return this.makeRequest({ @@ -103,7 +103,7 @@ class RemixRestClient { /** * Performs a DELETE request on the given path. * - * @deprecated In the next major release REST will be removed from this package. Please see [all-in on graphql](https://www.shopify.com/ca/partners/blog/all-in-on-graphql). + * @deprecated In a future major release REST will be removed from this package. Please see [all-in on graphql](https://www.shopify.com/ca/partners/blog/all-in-on-graphql). */ public async delete(params: DeleteRequestParams) { return this.makeRequest({ diff --git a/packages/apps/shopify-app-remix/src/server/clients/admin/types.ts b/packages/apps/shopify-app-remix/src/server/clients/admin/types.ts index 6a57ee0faf..aa326b00d9 100644 --- a/packages/apps/shopify-app-remix/src/server/clients/admin/types.ts +++ b/packages/apps/shopify-app-remix/src/server/clients/admin/types.ts @@ -137,7 +137,7 @@ export interface AdminApiContextWithRest< /** * Methods for interacting with the Shopify Admin REST API * - * @deprecated In the next major release REST will be removed from this package. Please see [all-in on graphql](https://www.shopify.com/ca/partners/blog/all-in-on-graphql). + * @deprecated In a future major release REST will be removed from this package. Please see [all-in on graphql](https://www.shopify.com/ca/partners/blog/all-in-on-graphql). * * There are methods for interacting with individual REST resources. You can also make `GET`, `POST`, `PUT` and `DELETE` requests should the REST resources not meet your needs. * From c89060d99e4219170933b7cdda9338aa27fc69cb Mon Sep 17 00:00:00 2001 From: Richard Powell Date: Mon, 18 Nov 2024 15:50:57 -0500 Subject: [PATCH 11/13] Import using relative paths, rather than absolute from src/. Using src/ breaks the typescript types when built. Ouch --- .../authenticate/admin/__tests__/admin-client.test.ts | 2 +- .../admin/helpers/create-admin-api-context.ts | 2 +- .../admin/helpers/trigger-after-auth-hook.ts | 4 ++-- .../admin/scope/client/fetch-scopes-details.ts | 3 +-- .../authenticate/admin/scope/client/revoke-scopes.ts | 3 +-- .../src/server/authenticate/admin/scope/factory.ts | 2 +- .../src/server/authenticate/admin/scope/query.ts | 2 +- .../src/server/authenticate/admin/scope/request.ts | 2 +- .../src/server/authenticate/admin/scope/revoke.ts | 2 +- .../__tests__/auth-code-flow/admin-client.test.ts | 2 +- .../__tests__/merchant-custom/admin-client.test.ts | 2 +- .../__tests__/token-exchange/admin-client.test.ts | 2 +- .../admin/strategies/merchant-custom-app.ts | 11 ++++------- .../src/server/authenticate/flow/authenticate.ts | 2 +- .../src/server/authenticate/flow/types.ts | 2 +- .../authenticate/fulfillment-service/authenticate.ts | 2 +- .../server/authenticate/fulfillment-service/types.ts | 2 +- .../helpers/respond-to-invalid-session-token.ts | 3 +-- .../authenticate/public/appProxy/authenticate.ts | 2 +- .../src/server/authenticate/public/appProxy/types.ts | 2 +- .../public/customer-account/authenticate.ts | 3 +-- .../authenticate/public/extension/authenticate.ts | 3 +-- .../src/server/authenticate/public/factory.ts | 2 +- .../src/server/authenticate/public/types.ts | 4 ++-- .../src/server/authenticate/webhooks/authenticate.ts | 2 +- .../src/server/authenticate/webhooks/types.ts | 2 +- .../src/server/clients/admin/factory.ts | 2 +- .../src/server/clients/admin/types.ts | 4 ++-- .../src/server/unauthenticated/admin/factory.ts | 2 +- .../src/server/unauthenticated/admin/types.ts | 2 +- 30 files changed, 36 insertions(+), 44 deletions(-) diff --git a/packages/apps/shopify-app-remix/src/server/authenticate/admin/__tests__/admin-client.test.ts b/packages/apps/shopify-app-remix/src/server/authenticate/admin/__tests__/admin-client.test.ts index bd0531ae64..f70904d3ff 100644 --- a/packages/apps/shopify-app-remix/src/server/authenticate/admin/__tests__/admin-client.test.ts +++ b/packages/apps/shopify-app-remix/src/server/authenticate/admin/__tests__/admin-client.test.ts @@ -4,7 +4,6 @@ import { LATEST_API_VERSION, Session, } from '@shopify/shopify-api'; -import {AdminApiContextWithRest} from 'src/server/clients'; import { TEST_SHOP, @@ -16,6 +15,7 @@ import { setUpEmbeddedFlowWithRemoveRestFlag, setUpNonEmbeddedFlow, } from '../../../__test-helpers'; +import {AdminApiContextWithRest} from '../../../clients'; describe('admin.authenticate context', () => { expectAdminApiClient(async () => { diff --git a/packages/apps/shopify-app-remix/src/server/authenticate/admin/helpers/create-admin-api-context.ts b/packages/apps/shopify-app-remix/src/server/authenticate/admin/helpers/create-admin-api-context.ts index 1a62f3ffbb..b84cd5b625 100644 --- a/packages/apps/shopify-app-remix/src/server/authenticate/admin/helpers/create-admin-api-context.ts +++ b/packages/apps/shopify-app-remix/src/server/authenticate/admin/helpers/create-admin-api-context.ts @@ -1,6 +1,6 @@ import {Session, ShopifyRestResources} from '@shopify/shopify-api'; -import type {AppConfigArg} from 'src/server/config-types'; +import {AppConfigArg} from '../../../config-types'; import type {BasicParams} from '../../../types'; import { AdminApiContext, diff --git a/packages/apps/shopify-app-remix/src/server/authenticate/admin/helpers/trigger-after-auth-hook.ts b/packages/apps/shopify-app-remix/src/server/authenticate/admin/helpers/trigger-after-auth-hook.ts index 2d7fbcca3d..95f0b62a73 100644 --- a/packages/apps/shopify-app-remix/src/server/authenticate/admin/helpers/trigger-after-auth-hook.ts +++ b/packages/apps/shopify-app-remix/src/server/authenticate/admin/helpers/trigger-after-auth-hook.ts @@ -1,7 +1,7 @@ import {Session, ShopifyRestResources} from '@shopify/shopify-api'; -import type {AppConfigArg} from 'src/server/config-types'; -import {AdminApiContextWithRest} from 'src/server/clients'; +import type {AppConfigArg} from '../../../config-types'; +import {AdminApiContextWithRest} from '../../../clients'; import type {BasicParams} from '../../../types'; import {AuthorizationStrategy} from '../strategies/types'; diff --git a/packages/apps/shopify-app-remix/src/server/authenticate/admin/scope/client/fetch-scopes-details.ts b/packages/apps/shopify-app-remix/src/server/authenticate/admin/scope/client/fetch-scopes-details.ts index 0ce158c68d..54782d3043 100644 --- a/packages/apps/shopify-app-remix/src/server/authenticate/admin/scope/client/fetch-scopes-details.ts +++ b/packages/apps/shopify-app-remix/src/server/authenticate/admin/scope/client/fetch-scopes-details.ts @@ -1,5 +1,4 @@ -import {AppConfigArg} from 'src/server/config-types'; - +import {AppConfigArg} from '../../../../config-types'; import {AdminApiContext} from '../../../../clients'; export interface FetchScopesDetailResponse { 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 3e07242fe6..d7d592b40e 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 @@ -1,5 +1,4 @@ -import {AppConfigArg} from 'src/server/config-types'; - +import {AppConfigArg} from '../../../../config-types'; import {AdminApiContext} from '../../../../clients'; export interface RevokeScopesResponse { diff --git a/packages/apps/shopify-app-remix/src/server/authenticate/admin/scope/factory.ts b/packages/apps/shopify-app-remix/src/server/authenticate/admin/scope/factory.ts index 6305f31c08..c421ae281c 100644 --- a/packages/apps/shopify-app-remix/src/server/authenticate/admin/scope/factory.ts +++ b/packages/apps/shopify-app-remix/src/server/authenticate/admin/scope/factory.ts @@ -1,6 +1,6 @@ import {Session} from '@shopify/shopify-api'; -import {AppConfigArg} from 'src/server/config-types'; +import {AppConfigArg} from '../../../config-types'; import {BasicParams} from '../../../types'; import {AdminApiContext} from '../../../clients'; diff --git a/packages/apps/shopify-app-remix/src/server/authenticate/admin/scope/query.ts b/packages/apps/shopify-app-remix/src/server/authenticate/admin/scope/query.ts index 63cd540993..1e8cb9a695 100644 --- a/packages/apps/shopify-app-remix/src/server/authenticate/admin/scope/query.ts +++ b/packages/apps/shopify-app-remix/src/server/authenticate/admin/scope/query.ts @@ -1,6 +1,6 @@ import {AuthScopes, Session} from '@shopify/shopify-api'; -import {AppConfigArg} from 'src/server/config-types'; +import {AppConfigArg} from '../../../config-types'; import {AdminApiContext} from '../../../clients'; import type {BasicParams} from '../../../types'; diff --git a/packages/apps/shopify-app-remix/src/server/authenticate/admin/scope/request.ts b/packages/apps/shopify-app-remix/src/server/authenticate/admin/scope/request.ts index d7cb3d54dd..58ead6e35c 100644 --- a/packages/apps/shopify-app-remix/src/server/authenticate/admin/scope/request.ts +++ b/packages/apps/shopify-app-remix/src/server/authenticate/admin/scope/request.ts @@ -1,6 +1,6 @@ import {AuthScopes, Session} from '@shopify/shopify-api'; -import {AppConfigArg} from 'src/server/config-types'; +import {AppConfigArg} from '../../../config-types'; import type {BasicParams} from '../../../types'; import {redirectToInstallPage} from '../helpers/redirect-to-install-page'; import {AdminApiContext} from '../../../clients'; 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 b4c2b90cd6..0b13389d5b 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 @@ -1,6 +1,6 @@ import {Session} from '@shopify/shopify-api'; -import {AppConfigArg} from 'src/server/config-types'; +import {AppConfigArg} from '../../../config-types'; import {AdminApiContext} from '../../../clients'; import type {BasicParams} from '../../../types'; diff --git a/packages/apps/shopify-app-remix/src/server/authenticate/admin/strategies/__tests__/auth-code-flow/admin-client.test.ts b/packages/apps/shopify-app-remix/src/server/authenticate/admin/strategies/__tests__/auth-code-flow/admin-client.test.ts index 0623bb2dfb..b215fbf50c 100644 --- a/packages/apps/shopify-app-remix/src/server/authenticate/admin/strategies/__tests__/auth-code-flow/admin-client.test.ts +++ b/packages/apps/shopify-app-remix/src/server/authenticate/admin/strategies/__tests__/auth-code-flow/admin-client.test.ts @@ -1,6 +1,6 @@ import {ApiVersion, LATEST_API_VERSION, Session} from '@shopify/shopify-api'; -import {AdminApiContextWithRest} from 'src/server/clients'; +import {AdminApiContextWithRest} from '../../../../../clients'; import { APP_URL, TEST_SHOP, diff --git a/packages/apps/shopify-app-remix/src/server/authenticate/admin/strategies/__tests__/merchant-custom/admin-client.test.ts b/packages/apps/shopify-app-remix/src/server/authenticate/admin/strategies/__tests__/merchant-custom/admin-client.test.ts index 47d341fee1..0445578a88 100644 --- a/packages/apps/shopify-app-remix/src/server/authenticate/admin/strategies/__tests__/merchant-custom/admin-client.test.ts +++ b/packages/apps/shopify-app-remix/src/server/authenticate/admin/strategies/__tests__/merchant-custom/admin-client.test.ts @@ -5,8 +5,8 @@ import { ShopifyError, } from '@shopify/shopify-api'; import {restResources} from '@shopify/shopify-api/rest/admin/2023-04'; -import {AdminApiContextWithRest} from 'src/server/clients'; +import {AdminApiContextWithRest} from '../../../../../clients'; import {shopifyApp} from '../../../../..'; import { APP_URL, diff --git a/packages/apps/shopify-app-remix/src/server/authenticate/admin/strategies/__tests__/token-exchange/admin-client.test.ts b/packages/apps/shopify-app-remix/src/server/authenticate/admin/strategies/__tests__/token-exchange/admin-client.test.ts index a5e1bce6a7..72987e5074 100644 --- a/packages/apps/shopify-app-remix/src/server/authenticate/admin/strategies/__tests__/token-exchange/admin-client.test.ts +++ b/packages/apps/shopify-app-remix/src/server/authenticate/admin/strategies/__tests__/token-exchange/admin-client.test.ts @@ -1,7 +1,7 @@ import {ApiVersion, LATEST_API_VERSION, Session} from '@shopify/shopify-api'; import {restResources} from '@shopify/shopify-api/rest/admin/2023-04'; -import {AdminApiContextWithRest} from 'src/server/clients'; +import {AdminApiContextWithRest} from '../../../../../clients'; import {shopifyApp} from '../../../../..'; import { APP_URL, diff --git a/packages/apps/shopify-app-remix/src/server/authenticate/admin/strategies/merchant-custom-app.ts b/packages/apps/shopify-app-remix/src/server/authenticate/admin/strategies/merchant-custom-app.ts index 8c914c84d0..9d0785ccab 100644 --- a/packages/apps/shopify-app-remix/src/server/authenticate/admin/strategies/merchant-custom-app.ts +++ b/packages/apps/shopify-app-remix/src/server/authenticate/admin/strategies/merchant-custom-app.ts @@ -1,17 +1,14 @@ -import {AppConfig, AppConfigArg} from 'src/server/config-types'; import { Session, Shopify, ShopifyError, ShopifyRestResources, } from '@shopify/shopify-api'; -import {BasicParams} from 'src/server/types'; -import { - ApiConfigWithFutureFlags, - ApiFutureFlags, -} from 'src/server/future/flags'; -import {HandleAdminClientError} from 'src/server/clients'; +import {AppConfig, AppConfigArg} from '../../../config-types'; +import {BasicParams} from '../../../types'; +import {ApiConfigWithFutureFlags, ApiFutureFlags} from '../../../future/flags'; +import {HandleAdminClientError} from '../../../clients'; import {handleClientErrorFactory} from '../helpers'; import {AuthorizationStrategy, OnErrorOptions, SessionContext} from './types'; diff --git a/packages/apps/shopify-app-remix/src/server/authenticate/flow/authenticate.ts b/packages/apps/shopify-app-remix/src/server/authenticate/flow/authenticate.ts index 8262ccdd3b..d0cd663623 100644 --- a/packages/apps/shopify-app-remix/src/server/authenticate/flow/authenticate.ts +++ b/packages/apps/shopify-app-remix/src/server/authenticate/flow/authenticate.ts @@ -1,6 +1,6 @@ import {ShopifyRestResources} from '@shopify/shopify-api'; -import {AppConfigArg} from 'src/server/config-types'; +import {AppConfigArg} from '../../config-types'; import {adminClientFactory} from '../../clients/admin'; import {BasicParams} from '../../types'; diff --git a/packages/apps/shopify-app-remix/src/server/authenticate/flow/types.ts b/packages/apps/shopify-app-remix/src/server/authenticate/flow/types.ts index f08d389b87..28d06a800d 100644 --- a/packages/apps/shopify-app-remix/src/server/authenticate/flow/types.ts +++ b/packages/apps/shopify-app-remix/src/server/authenticate/flow/types.ts @@ -1,6 +1,6 @@ import {Session, ShopifyRestResources} from '@shopify/shopify-api'; -import {AppConfigArg} from 'src/server/config-types'; +import {AppConfigArg} from '../../config-types'; import type {AdminApiContext} from '../../clients'; export interface FlowContext< diff --git a/packages/apps/shopify-app-remix/src/server/authenticate/fulfillment-service/authenticate.ts b/packages/apps/shopify-app-remix/src/server/authenticate/fulfillment-service/authenticate.ts index 3d61d19318..be4ff0e509 100644 --- a/packages/apps/shopify-app-remix/src/server/authenticate/fulfillment-service/authenticate.ts +++ b/packages/apps/shopify-app-remix/src/server/authenticate/fulfillment-service/authenticate.ts @@ -1,6 +1,6 @@ import {ShopifyRestResources, ShopifyHeader} from '@shopify/shopify-api'; -import {AppConfigArg} from 'src/server/config-types'; +import {AppConfigArg} from '../../config-types'; import {adminClientFactory} from '../../clients/admin'; import {BasicParams} from '../../types'; import {createOrLoadOfflineSession} from '../helpers'; diff --git a/packages/apps/shopify-app-remix/src/server/authenticate/fulfillment-service/types.ts b/packages/apps/shopify-app-remix/src/server/authenticate/fulfillment-service/types.ts index bc4995333d..2f1895b050 100644 --- a/packages/apps/shopify-app-remix/src/server/authenticate/fulfillment-service/types.ts +++ b/packages/apps/shopify-app-remix/src/server/authenticate/fulfillment-service/types.ts @@ -1,6 +1,6 @@ import {Session, ShopifyRestResources} from '@shopify/shopify-api'; -import {AppConfigArg} from 'src/server/config-types'; +import {AppConfigArg} from '../../config-types'; import type {AdminApiContext} from '../../clients'; export type FulfillmentServicePayload = Record & { diff --git a/packages/apps/shopify-app-remix/src/server/authenticate/helpers/respond-to-invalid-session-token.ts b/packages/apps/shopify-app-remix/src/server/authenticate/helpers/respond-to-invalid-session-token.ts index 8b215d781a..e81e67b088 100644 --- a/packages/apps/shopify-app-remix/src/server/authenticate/helpers/respond-to-invalid-session-token.ts +++ b/packages/apps/shopify-app-remix/src/server/authenticate/helpers/respond-to-invalid-session-token.ts @@ -1,7 +1,6 @@ -import {BasicParams} from 'src/server/types'; - import {redirectToBouncePage} from '../admin/helpers/redirect-to-bounce-page'; import {RETRY_INVALID_SESSION_HEADER} from '../const'; +import {BasicParams} from '../../types'; interface RespondToInvalidSessionTokenParams { params: BasicParams; diff --git a/packages/apps/shopify-app-remix/src/server/authenticate/public/appProxy/authenticate.ts b/packages/apps/shopify-app-remix/src/server/authenticate/public/appProxy/authenticate.ts index a01035b30d..6c24435e4e 100644 --- a/packages/apps/shopify-app-remix/src/server/authenticate/public/appProxy/authenticate.ts +++ b/packages/apps/shopify-app-remix/src/server/authenticate/public/appProxy/authenticate.ts @@ -1,6 +1,6 @@ import {ShopifyRestResources} from '@shopify/shopify-api'; -import {AppConfigArg} from 'src/server/config-types'; +import {AppConfigArg} from '../../../config-types'; import {adminClientFactory, storefrontClientFactory} from '../../../clients'; import {BasicParams} from '../../../types'; diff --git a/packages/apps/shopify-app-remix/src/server/authenticate/public/appProxy/types.ts b/packages/apps/shopify-app-remix/src/server/authenticate/public/appProxy/types.ts index 550fc129da..4fab4a035f 100644 --- a/packages/apps/shopify-app-remix/src/server/authenticate/public/appProxy/types.ts +++ b/packages/apps/shopify-app-remix/src/server/authenticate/public/appProxy/types.ts @@ -1,6 +1,6 @@ import {Session, ShopifyRestResources} from '@shopify/shopify-api'; -import {AppConfigArg} from 'src/server/config-types'; +import {AppConfigArg} from '../../../config-types'; import {AdminApiContext, StorefrontContext} from '../../../clients'; export type AuthenticateAppProxy< diff --git a/packages/apps/shopify-app-remix/src/server/authenticate/public/customer-account/authenticate.ts b/packages/apps/shopify-app-remix/src/server/authenticate/public/customer-account/authenticate.ts index ba82a211ff..696ccfe3c8 100644 --- a/packages/apps/shopify-app-remix/src/server/authenticate/public/customer-account/authenticate.ts +++ b/packages/apps/shopify-app-remix/src/server/authenticate/public/customer-account/authenticate.ts @@ -1,5 +1,4 @@ -import {BasicParams} from 'src/server/types'; - +import {BasicParams} from '../../../types'; import {authenticateExtensionFactory} from '../extension/authenticate'; import {AuthenticateCustomerAccount} from './types'; diff --git a/packages/apps/shopify-app-remix/src/server/authenticate/public/extension/authenticate.ts b/packages/apps/shopify-app-remix/src/server/authenticate/public/extension/authenticate.ts index 3e67124c7a..8d7d174b1a 100644 --- a/packages/apps/shopify-app-remix/src/server/authenticate/public/extension/authenticate.ts +++ b/packages/apps/shopify-app-remix/src/server/authenticate/public/extension/authenticate.ts @@ -1,5 +1,4 @@ -import {BasicParams} from 'src/server/types'; - +import {BasicParams} from '../../../types'; import { respondToBotRequest, respondToOptionsRequest, diff --git a/packages/apps/shopify-app-remix/src/server/authenticate/public/factory.ts b/packages/apps/shopify-app-remix/src/server/authenticate/public/factory.ts index ef4e3967b2..a00b2a72ce 100644 --- a/packages/apps/shopify-app-remix/src/server/authenticate/public/factory.ts +++ b/packages/apps/shopify-app-remix/src/server/authenticate/public/factory.ts @@ -1,6 +1,6 @@ import {ShopifyRestResources} from '@shopify/shopify-api'; -import {AppConfigArg} from 'src/server/config-types'; +import {AppConfigArg} from '../../config-types'; import {BasicParams} from '../../types'; import {authenticateCheckoutFactory} from './checkout/authenticate'; diff --git a/packages/apps/shopify-app-remix/src/server/authenticate/public/types.ts b/packages/apps/shopify-app-remix/src/server/authenticate/public/types.ts index 93db6da257..cd9e92d2ca 100644 --- a/packages/apps/shopify-app-remix/src/server/authenticate/public/types.ts +++ b/packages/apps/shopify-app-remix/src/server/authenticate/public/types.ts @@ -1,4 +1,4 @@ -import type {AppConfigArg} from 'src/server/config-types'; +import type {AppConfigArg} from '../../config-types'; import type {AuthenticateCheckout} from './checkout/types'; import type {AuthenticateAppProxy} from './appProxy/types'; @@ -6,7 +6,7 @@ import type {AuthenticateCustomerAccount} from './customer-account/types'; // Eventually this will be just the `{}` bit without `AuthenticateCheckout &` // We have this is because in v1 public WAS the only public authenticate method -// But it became tightly coupled to authentictaing Checkout requests. +// But it became tightly coupled to authenticating Checkout requests. // In V2 you will have only public.checkout() and public.appProxy(), no public() export interface AuthenticatePublic { diff --git a/packages/apps/shopify-app-remix/src/server/authenticate/webhooks/authenticate.ts b/packages/apps/shopify-app-remix/src/server/authenticate/webhooks/authenticate.ts index 4303613eba..e1118d4cba 100644 --- a/packages/apps/shopify-app-remix/src/server/authenticate/webhooks/authenticate.ts +++ b/packages/apps/shopify-app-remix/src/server/authenticate/webhooks/authenticate.ts @@ -2,8 +2,8 @@ import { ShopifyRestResources, WebhookValidationErrorReason, } from '@shopify/shopify-api'; -import {AppConfigArg} from 'src/server/config-types'; +import {AppConfigArg} from '../../config-types'; import type {BasicParams} from '../../types'; import {adminClientFactory} from '../../clients'; import {handleClientErrorFactory} from '../admin/helpers'; diff --git a/packages/apps/shopify-app-remix/src/server/authenticate/webhooks/types.ts b/packages/apps/shopify-app-remix/src/server/authenticate/webhooks/types.ts index 094727f7a6..b5ccc566ab 100644 --- a/packages/apps/shopify-app-remix/src/server/authenticate/webhooks/types.ts +++ b/packages/apps/shopify-app-remix/src/server/authenticate/webhooks/types.ts @@ -1,6 +1,6 @@ import {Session, ShopifyRestResources} from '@shopify/shopify-api'; -import {AppConfigArg} from 'src/server/config-types'; +import {AppConfigArg} from '../../config-types'; import type {AdminApiContext} from '../../clients'; export interface RegisterWebhooksOptions { diff --git a/packages/apps/shopify-app-remix/src/server/clients/admin/factory.ts b/packages/apps/shopify-app-remix/src/server/clients/admin/factory.ts index 5aa7b0aaa5..9b1b42ba9a 100644 --- a/packages/apps/shopify-app-remix/src/server/clients/admin/factory.ts +++ b/packages/apps/shopify-app-remix/src/server/clients/admin/factory.ts @@ -1,6 +1,6 @@ import {Session, ShopifyRestResources} from '@shopify/shopify-api'; -import type {AppConfigArg} from 'src/server/config-types'; +import type {AppConfigArg} from '../../config-types'; import {BasicParams} from '../../types'; import {graphqlClientFactory} from './graphql'; diff --git a/packages/apps/shopify-app-remix/src/server/clients/admin/types.ts b/packages/apps/shopify-app-remix/src/server/clients/admin/types.ts index aa326b00d9..b9f83f3e49 100644 --- a/packages/apps/shopify-app-remix/src/server/clients/admin/types.ts +++ b/packages/apps/shopify-app-remix/src/server/clients/admin/types.ts @@ -1,8 +1,8 @@ import {AdminOperations} from '@shopify/admin-api-client'; import {Session, ShopifyRestResources} from '@shopify/shopify-api'; -import type {AppConfigArg} from 'src/server/config-types'; -import type {FeatureEnabled} from 'src/server/future/flags'; +import type {AppConfigArg} from '../../config-types'; +import type {FeatureEnabled} from '../../future/flags'; import {BasicParams} from '../../types'; import {GraphQLClient} from '../types'; diff --git a/packages/apps/shopify-app-remix/src/server/unauthenticated/admin/factory.ts b/packages/apps/shopify-app-remix/src/server/unauthenticated/admin/factory.ts index 10d1f81333..73c12ad1dc 100644 --- a/packages/apps/shopify-app-remix/src/server/unauthenticated/admin/factory.ts +++ b/packages/apps/shopify-app-remix/src/server/unauthenticated/admin/factory.ts @@ -1,6 +1,6 @@ import {ShopifyRestResources} from '@shopify/shopify-api'; -import {AppConfigArg} from 'src/server/config-types'; +import {AppConfigArg} from '../../config-types'; import {createOrLoadOfflineSession} from '../../authenticate/helpers/create-or-load-offline-session'; import {SessionNotFoundError} from '../../errors'; import {BasicParams} from '../../types'; diff --git a/packages/apps/shopify-app-remix/src/server/unauthenticated/admin/types.ts b/packages/apps/shopify-app-remix/src/server/unauthenticated/admin/types.ts index 73f3fbe8fb..ebde4db521 100644 --- a/packages/apps/shopify-app-remix/src/server/unauthenticated/admin/types.ts +++ b/packages/apps/shopify-app-remix/src/server/unauthenticated/admin/types.ts @@ -1,6 +1,6 @@ import {Session, ShopifyRestResources} from '@shopify/shopify-api'; -import {AppConfigArg} from 'src/server/config-types'; +import {AppConfigArg} from '../../config-types'; import {AdminApiContext} from '../../clients'; export interface UnauthenticatedAdminContext< From 8632d03c7c7f90adae7fef16d997dc919da74acf Mon Sep 17 00:00:00 2001 From: Richard Powell Date: Mon, 18 Nov 2024 16:01:22 -0500 Subject: [PATCH 12/13] Add vscode config to tell typescript autocomplete to import using relative paths. This avoids an issuewhere doning cmd + . will suggest an import path like 'src/...', which will then get ignored when built, which will then break Types (big oops) --- .gitignore | 1 - .vscode/settings.json | 3 +++ 2 files changed, 3 insertions(+), 1 deletion(-) create mode 100644 .vscode/settings.json diff --git a/.gitignore b/.gitignore index 28cbf3c271..e0ba222be5 100644 --- a/.gitignore +++ b/.gitignore @@ -10,7 +10,6 @@ database.sqlite packages/**/build packages/**/*.tgz tmp/ -.vscode/ bundle/ .turbo .rollup.cache diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 0000000000..11f21f5218 --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,3 @@ +{ + "typescript.preferences.importModuleSpecifier": "relative", +} From 5dddeaedfec46605b0faa00c67e6b18771718323 Mon Sep 17 00:00:00 2001 From: Richard Powell Date: Tue, 19 Nov 2024 10:51:29 -0500 Subject: [PATCH 13/13] PR Feedback: * Remove v4_ * Remove documentation about breaking changes being behingd a future flag with the next major version in their name. --- .changeset/eight-mails-hammer.md | 2 +- .../apps/shopify-app-remix/docs/staticPages/future-flags.doc.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.changeset/eight-mails-hammer.md b/.changeset/eight-mails-hammer.md index 54ad79a0ef..63036599c7 100644 --- a/.changeset/eight-mails-hammer.md +++ b/.changeset/eight-mails-hammer.md @@ -6,4 +6,4 @@ Added `removeRest` future flag. When `removeRest` is `true`, the REST API will no longer be available. Please use the GraphQL API instead. See [Shopify is all-in on graphql](https://www.shopify.com/ca/partners/blog/all-in-on-graphql) for more information. -If your app doesn't use the REST API, you can safely set `v4_removeRest` to `true` and be ready for a future major release. If your app does use the REST API, you should migrate to the GraphQL API and then set `removeRest` to `true`. +If your app doesn't use the REST API, you can safely set `removeRest` to `true` and be ready for a future major release. If your app does use the REST API, you should migrate to the GraphQL API and then set `removeRest` to `true`. diff --git a/packages/apps/shopify-app-remix/docs/staticPages/future-flags.doc.ts b/packages/apps/shopify-app-remix/docs/staticPages/future-flags.doc.ts index dbb99b82b3..4bb5e24e06 100644 --- a/packages/apps/shopify-app-remix/docs/staticPages/future-flags.doc.ts +++ b/packages/apps/shopify-app-remix/docs/staticPages/future-flags.doc.ts @@ -51,7 +51,7 @@ const data: LandingTemplateSchema = { anchorLink: 'breaking-changes', title: 'Breaking changes', sectionContent: - 'Similarly to unstable APIs, breaking changes will be introduced behind a future flag, but the prefix will be the next major version (e.g. ``).' + + 'Similarly to unstable APIs, breaking changes will be introduced behind a future flag.' + '\n\nThis allows apps to prepare for the next major version ahead of time, and to gradually adopt the new APIs.' + '\n\nWhen the next major version is released, the future flag will be removed, and the old code it changes will be removed. Apps that adopted the flag before then will continue to work the same way with no new changes.', },