Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import {TypedDocumentNode as DocumentNode} from '@graphql-typed-document-node/co

export type FetchStoreByDomainQueryVariables = Types.Exact<{
domain?: Types.InputMaybe<Types.Scalars['String']['input']>
storeTypes: Types.Scalars['String']['input']
filters?: Types.InputMaybe<Types.ShopFilterInput[] | Types.ShopFilterInput>
}>

export type FetchStoreByDomainQuery = {
Expand Down Expand Up @@ -44,8 +44,11 @@ export const FetchStoreByDomain = {
},
{
kind: 'VariableDefinition',
variable: {kind: 'Variable', name: {kind: 'Name', value: 'storeTypes'}},
type: {kind: 'NonNullType', type: {kind: 'NamedType', name: {kind: 'Name', value: 'String'}}},
variable: {kind: 'Variable', name: {kind: 'Name', value: 'filters'}},
type: {
kind: 'ListType',
type: {kind: 'NonNullType', type: {kind: 'NamedType', name: {kind: 'Name', value: 'ShopFilterInput'}}},
},
},
],
selectionSet: {
Expand All @@ -66,26 +69,7 @@ export const FetchStoreByDomain = {
{
kind: 'Argument',
name: {kind: 'Name', value: 'filters'},
value: {
kind: 'ObjectValue',
fields: [
{
kind: 'ObjectField',
name: {kind: 'Name', value: 'field'},
value: {kind: 'EnumValue', value: 'STORE_TYPE'},
},
{
kind: 'ObjectField',
name: {kind: 'Name', value: 'operator'},
value: {kind: 'EnumValue', value: 'IN'},
},
{
kind: 'ObjectField',
name: {kind: 'Name', value: 'value'},
value: {kind: 'Variable', name: {kind: 'Name', value: 'storeTypes'}},
},
],
},
value: {kind: 'Variable', name: {kind: 'Name', value: 'filters'}},
},
{
kind: 'Argument',
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -78,11 +78,56 @@ export type Scalars = {
URL: { input: string; output: string; }
};

/** Operators for user filter queries. */
export type Operator =
/** Between operator. */
| 'BETWEEN'
/** Equals operator. */
| 'EQUALS'
/** In operator. */
| 'IN';

export type OrganizationUserProvisionShopAccessInput = {
/** The shop to provision the requester on. */
shopifyShopId: Scalars['PropertyPublicID']['input'];
};

/** Field options for filtering shop queries. */
export type ShopFilterField =
/**
* The phase of the client transfer process. Requires
* `store_type=client_transfer`. Values: `in_development`, `pending`, `completed`.
*/
| 'CLIENT_TRANSFER_PHASE'
/**
* The status of the collaborator relationship. Requires
* `store_type=collaborator`. Values: `active`, `access_pending`, `expired`.
*/
| 'COLLABORATOR_RELATIONSHIP_STATUS'
/** The GID of the counterpart organization. Requires `store_type=client_transfer` or `store_type=collaborator`. */
| 'COUNTERPART_ORGANIZATION_ID'
/**
* The plan of the shop. Values: `basic`, `grow`, `plus`, `frozen`, `advanced`,
* `inactive`, `cancelled`, `client_transfer`, `plus_client_transfer`,
* `development_legacy`, `custom`, `fraudulent`, `staff`, `trial`,
* `plus_development`, `retail`, `shop_pay_commerce_components`, `non_profit`.
*/
| 'SHOP_PLAN'
/** The active/inactive status of the shop. Values: `active`, `inactive`. */
| 'STORE_STATUS'
/**
* The type of the shop. Values: `development`, `production`, `app_development`,
* `development_superset`, `client_transfer`, `collaborator`.
*/
| 'STORE_TYPE';

/** Represents a single filter option for shop queries. */
export type ShopFilterInput = {
field: ShopFilterField;
operator: Operator;
value: Scalars['String']['input'];
};

export type Store =
| 'APP_DEVELOPMENT'
| 'CLIENT_TRANSFER'
Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
query FetchStoreByDomain($domain: String, $storeTypes: String!) {
query FetchStoreByDomain($domain: String, $filters: [ShopFilterInput!]) {
organization {
id
name
accessibleShops(filters: {field: STORE_TYPE, operator: IN, value: $storeTypes}, search: $domain) {
accessibleShops(filters: $filters, search: $domain) {
edges {
node {
id
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -89,10 +89,7 @@ import {
DevSessionUpdateMutationVariables,
} from '../../api/graphql/app-dev/generated/dev-session-update.js'
import {DevSessionDelete, DevSessionDeleteMutation} from '../../api/graphql/app-dev/generated/dev-session-delete.js'
import {
FetchStoreByDomain,
FetchStoreByDomainQueryVariables,
} from '../../api/graphql/business-platform-organizations/generated/fetch_store_by_domain.js'
import {FetchStoreByDomain} from '../../api/graphql/business-platform-organizations/generated/fetch_store_by_domain.js'
import {
ListAppDevStores,
ListAppDevStoresQuery,
Expand Down Expand Up @@ -840,26 +837,30 @@ export class AppManagementClient implements DeveloperPlatformClient {
}

async storeByDomain(orgId: string, shopDomain: string, storeTypes: Store[]): Promise<OrganizationStore | undefined> {
const queryVariables: FetchStoreByDomainQueryVariables = {
domain: shopDomain,
storeTypes: storeTypes.map((t) => t.toLowerCase()).join(','),
}
const storesResult = await this.businessPlatformOrganizationsRequest({
query: FetchStoreByDomain,
organizationId: String(numberFromGid(orgId)),
variables: queryVariables,
})

const organization = storesResult.organization
const results = await Promise.all(
storeTypes.map((storeType) =>
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

looks like pre-existing issue, but map is always synchronous, so this code is probably producing race conditions

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for flagging! The map here is intentional. It builds an array of Promises (through businessPlatformOrganizationsRequest) that are immediately passed to Promise.all, so all requests run in parallel and we wait for all of them to resolve. No fire-and-forget happening.

this.businessPlatformOrganizationsRequest({
query: FetchStoreByDomain,
organizationId: String(numberFromGid(orgId)),
variables: {
domain: shopDomain,
filters: [{field: 'STORE_TYPE' as const, operator: 'EQUALS' as const, value: storeType.toLowerCase()}],
},
}),
),
)

if (!organization) {
const organizations = results.map((result) => result.organization).filter((org) => org != null)
if (organizations.length === 0) {
throw new AbortError(`No organization found`)
}

const bpStoresArray = organization.accessibleShops?.edges.map((value) => value.node) ?? []
const provisionable = isStoreProvisionable(organization.currentUser?.organizationPermissions ?? [])
const storesArray = mapBusinessPlatformStoresToOrganizationStores(bpStoresArray, provisionable)
return storesArray[0]
const stores = organizations.flatMap((org) => {
const nodes = org.accessibleShops?.edges.map((edge) => edge.node) ?? []
const provisionable = isStoreProvisionable(org.currentUser?.organizationPermissions ?? [])
return mapBusinessPlatformStoresToOrganizationStores(nodes, provisionable)
})
return stores[0]
}

async ensureUserAccessToStore(orgId: string, store: OrganizationStore): Promise<void> {
Expand Down
Loading