Skip to content

Commit b37740f

Browse files
Add dev store deletion though core dev store API
1 parent 59bbf90 commit b37740f

35 files changed

+1850
-8
lines changed

bin/get-graphql-schemas.js

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -91,6 +91,12 @@ const schemas = [
9191
pathToFile: 'areas/platforms/organizations/db/graphql/organizations_schema.graphql',
9292
localPath: './packages/store/src/cli/api/graphql/business-platform-organizations/organizations_schema.graphql',
9393
},
94+
{
95+
owner: 'shop',
96+
repo: 'world',
97+
pathToFile: 'areas/core/shopify/db/graphql/dev_stores_schema_unstable_public.graphql',
98+
localPath: './packages/store/src/cli/api/graphql/dev-stores/dev_stores_schema.graphql',
99+
},
94100
]
95101

96102

graphql.config.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -86,5 +86,7 @@ export default {
8686
// Store package projects
8787
storeAdmin: projectFactory('admin', 'admin_schema.graphql', 'store'),
8888
storeBusinessPlatformDestinations: projectFactory('business-platform-destinations', 'destinations_schema.graphql', 'store'),
89+
storeBusinessPlatformOrganizations: projectFactory('business-platform-organizations', 'organizations_schema.graphql', 'store'),
90+
devStores: projectFactory('dev-stores', 'dev_stores_schema.graphql', 'store'),
8991
},
9092
}

packages/cli-kit/src/private/node/session/scopes.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,7 @@ function defaultApiScopes(api: API): string[] {
5252
case 'partners':
5353
return ['cli']
5454
case 'business-platform':
55-
return ['destinations', 'store-management', 'on-demand-user-access']
55+
return ['destinations', 'store-management', 'on-demand-user-access', 'shop-create']
5656
case 'app-management':
5757
return ['app-management']
5858
default:
@@ -80,6 +80,8 @@ function scopeTransform(scope: string): string {
8080
return 'https://api.shopify.com/auth/organization.on-demand-user-access'
8181
case 'app-management':
8282
return 'https://api.shopify.com/auth/organization.apps.manage'
83+
case 'shop-create':
84+
return 'https://api.shopify.com/auth/shop.create'
8385
default:
8486
return scope
8587
}
Lines changed: 121 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,121 @@
1+
import {CacheOptions, GraphQLResponse, UnauthorizedHandler, graphqlRequestDoc} from './graphql.js'
2+
import {setNextDeprecationDate} from '../../../private/node/context/deprecations-store.js'
3+
import {buildHeaders} from '../../../private/node/api/headers.js'
4+
import {RequestModeInput} from '../http.js'
5+
import {devStoreFqdn} from '../context/fqdn.js'
6+
import Bottleneck from 'bottleneck'
7+
import {TypedDocumentNode} from '@graphql-typed-document-node/core'
8+
import {Variables} from 'graphql-request'
9+
10+
// API Rate limiter for partners API (Limit is 10 requests per second)
11+
// Jobs are launched every 150ms to add an extra 50ms margin per request.
12+
// Only 10 requests can be executed concurrently.
13+
const limiter = new Bottleneck({
14+
minTime: 150,
15+
maxConcurrent: 10,
16+
})
17+
18+
async function setupRequest(token: string) {
19+
const api = 'Dev Store'
20+
const fqdn = await devStoreFqdn()
21+
const url = `https://${fqdn}/dev_stores/unstable/graphql.json`
22+
return {
23+
token,
24+
api,
25+
url,
26+
responseOptions: {onResponse: handleDeprecations},
27+
}
28+
}
29+
30+
export const devStoresHeaders = (token: string): {[key: string]: string} => {
31+
return buildHeaders(token)
32+
}
33+
34+
// export const devStoresAppLogsUrl = async (
35+
// organizationId: string,
36+
// cursor?: string,
37+
// filters?: {
38+
// status?: string
39+
// source?: string
40+
// },
41+
// ): Promise<string> => {
42+
// const fqdn = await devStoreFqdn()
43+
// const url = `https://${fqdn}/dev_stores/unstable/organizations/${organizationId}/app_logs/poll`
44+
// return addCursorAndFiltersToAppLogsUrl(url, cursor, filters)
45+
// }
46+
47+
export interface RequestOptions {
48+
requestMode: RequestModeInput
49+
}
50+
51+
/**
52+
* @param organizationId - The organization ID.
53+
* @param query - GraphQL query to execute.
54+
* @param token - Partners token.
55+
* @param variables - GraphQL variables to pass to the query.
56+
* @param cacheOptions - Cache options for the request. If not present, the request will not be cached.
57+
* @param requestOptions - Preferred behaviour for the request.
58+
* @param unauthorizedHandler - Optional handler for unauthorized requests.
59+
*/
60+
export interface DevStoresRequestOptions<TResult, TVariables extends Variables> {
61+
query: TypedDocumentNode<TResult, TVariables>
62+
organizationId: string
63+
token: string
64+
variables?: TVariables
65+
cacheOptions?: CacheOptions
66+
requestOptions?: RequestOptions
67+
unauthorizedHandler: UnauthorizedHandler
68+
}
69+
70+
/**
71+
* Executes an org-scoped GraphQL query against the App Management API. Uses typed documents.
72+
*
73+
* @param options - The options for the request.
74+
* @returns The response of the query of generic type <T>.
75+
*/
76+
export async function devStoresRequestDoc<TResult, TVariables extends Variables>(
77+
options: DevStoresRequestOptions<TResult, TVariables>,
78+
): Promise<TResult> {
79+
const cacheExtraKey = options.cacheOptions?.cacheExtraKey ?? ''
80+
const newCacheOptions = options.cacheOptions ? {...options.cacheOptions, cacheExtraKey} : undefined
81+
82+
const result = limiter.schedule<TResult>(async () =>
83+
graphqlRequestDoc<TResult, TVariables>({
84+
...(await setupRequest(options.token)),
85+
query: options.query,
86+
variables: options.variables,
87+
cacheOptions: newCacheOptions,
88+
preferredBehaviour: options.requestOptions?.requestMode,
89+
unauthorizedHandler: options.unauthorizedHandler,
90+
}),
91+
)
92+
93+
return result
94+
}
95+
96+
interface Deprecation {
97+
supportedUntilDate?: string
98+
}
99+
100+
interface WithDeprecations {
101+
deprecations: Deprecation[]
102+
}
103+
104+
/**
105+
* Sets the next deprecation date from [GraphQL response extensions](https://www.apollographql.com/docs/resources/graphql-glossary/#extensions)
106+
* if `response.extensions.deprecations` objects contain a `supportedUntilDate` (ISO 8601-formatted string).
107+
*
108+
* @param response - The response of the query.
109+
*/
110+
export function handleDeprecations<T>(response: GraphQLResponse<T>): void {
111+
if (!response.extensions) return
112+
113+
const deprecationDates: Date[] = []
114+
for (const deprecation of (response.extensions as WithDeprecations).deprecations) {
115+
if (deprecation.supportedUntilDate) {
116+
deprecationDates.push(new Date(deprecation.supportedUntilDate))
117+
}
118+
}
119+
120+
setNextDeprecationDate(deprecationDates)
121+
}

packages/cli-kit/src/public/node/context/fqdn.ts

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,21 @@ export async function appManagementFqdn(): Promise<string> {
5858
}
5959
}
6060

61+
/**
62+
* It returns the Dev Store API service we should interact with.
63+
*
64+
* @returns Fully-qualified domain of the Dev Store service we should interact with.
65+
*/
66+
export async function devStoreFqdn(): Promise<string> {
67+
const environment = serviceEnvironment()
68+
const productionFqdn = 'app.shopify.com'
69+
switch (environment) {
70+
case 'local':
71+
return new DevServerCore().host('app')
72+
default:
73+
return productionFqdn
74+
}
75+
}
6176
/**
6277
* It returns the App Dev API service we should interact with.
6378
*

packages/cli/README.md

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,9 @@
6464
* [`shopify plugins unlink [PLUGIN]`](#shopify-plugins-unlink-plugin)
6565
* [`shopify plugins update`](#shopify-plugins-update)
6666
* [`shopify search [query]`](#shopify-search-query)
67+
* [`shopify store create`](#shopify-store-create)
68+
* [`shopify store delete`](#shopify-store-delete)
69+
* [`shopify store list`](#shopify-store-list)
6770
* [`shopify theme check`](#shopify-theme-check)
6871
* [`shopify theme console`](#shopify-theme-console)
6972
* [`shopify theme delete`](#shopify-theme-delete)
@@ -1698,6 +1701,48 @@ EXAMPLES
16981701
shopify search "<a search query separated by spaces>"
16991702
```
17001703

1704+
## `shopify store create`
1705+
1706+
List your development stores.
1707+
1708+
```
1709+
USAGE
1710+
$ shopify store create
1711+
1712+
DESCRIPTION
1713+
List your development stores.
1714+
1715+
Display a list of development stores in your organization.
1716+
```
1717+
1718+
## `shopify store delete`
1719+
1720+
Delete a dev store.
1721+
1722+
```
1723+
USAGE
1724+
$ shopify store delete
1725+
1726+
DESCRIPTION
1727+
Delete a dev store.
1728+
1729+
Delete a dev store from your organization.
1730+
```
1731+
1732+
## `shopify store list`
1733+
1734+
List your development stores.
1735+
1736+
```
1737+
USAGE
1738+
$ shopify store list
1739+
1740+
DESCRIPTION
1741+
List your development stores.
1742+
1743+
Display a list of development stores in your organization.
1744+
```
1745+
17011746
## `shopify theme check`
17021747

17031748
Validate the theme.

packages/cli/oclif.manifest.json

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4988,6 +4988,63 @@
49884988
"strict": true,
49894989
"summary": "Copy, export, or import store data"
49904990
},
4991+
"store:create": {
4992+
"aliases": [
4993+
],
4994+
"args": {
4995+
},
4996+
"customPluginName": "@shopify/store",
4997+
"description": "Display a list of development stores in your organization.",
4998+
"flags": {
4999+
},
5000+
"hasDynamicHelp": false,
5001+
"hiddenAliases": [
5002+
],
5003+
"id": "store:create",
5004+
"pluginAlias": "@shopify/cli",
5005+
"pluginName": "@shopify/cli",
5006+
"pluginType": "core",
5007+
"strict": true,
5008+
"summary": "List your development stores."
5009+
},
5010+
"store:delete": {
5011+
"aliases": [
5012+
],
5013+
"args": {
5014+
},
5015+
"customPluginName": "@shopify/store",
5016+
"description": "Delete a dev store from your organization.",
5017+
"flags": {
5018+
},
5019+
"hasDynamicHelp": false,
5020+
"hiddenAliases": [
5021+
],
5022+
"id": "store:delete",
5023+
"pluginAlias": "@shopify/cli",
5024+
"pluginName": "@shopify/cli",
5025+
"pluginType": "core",
5026+
"strict": true,
5027+
"summary": "Delete a dev store."
5028+
},
5029+
"store:list": {
5030+
"aliases": [
5031+
],
5032+
"args": {
5033+
},
5034+
"customPluginName": "@shopify/store",
5035+
"description": "Display a list of development stores in your organization.",
5036+
"flags": {
5037+
},
5038+
"hasDynamicHelp": false,
5039+
"hiddenAliases": [
5040+
],
5041+
"id": "store:list",
5042+
"pluginAlias": "@shopify/cli",
5043+
"pluginName": "@shopify/cli",
5044+
"pluginType": "core",
5045+
"strict": true,
5046+
"summary": "List your development stores."
5047+
},
49915048
"theme:check": {
49925049
"aliases": [
49935050
],

packages/store/project.json

Lines changed: 37 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -75,13 +75,16 @@
7575
"inputs": [{ "dependentTasksOutputFiles": "**/*.ts" }],
7676
"outputs": [
7777
"{projectRoot}/src/cli/api/graphql/admin/generated/**/*.ts",
78-
"{projectRoot}/src/cli/api/graphql/business-platform-destinations/generated/**/*.ts"
78+
"{projectRoot}/src/cli/api/graphql/business-platform-destinations/generated/**/*.ts",
79+
"{projectRoot}/src/cli/api/graphql/business-platform-organizations/generated/**/*.ts",
80+
"{projectRoot}/src/cli/api/graphql/dev-stores/generated/**/*.ts"
7981
],
8082
"options": {
8183
"commands": [
8284
"pnpm eslint 'src/cli/api/graphql/admin/generated/**/*.ts' --fix --rule '@typescript-eslint/ban-ts-comment: off' --rule 'eslint-comments/no-unused-disable: off'",
83-
"pnpm eslint 'src/cli/api/graphql/business-platform-destinations/generated/**/*.ts' --fix --rule '@typescript-eslint/ban-ts-comment: off' --rule 'eslint-comments/no-unused-disable: off'"
84-
],
85+
"pnpm eslint 'src/cli/api/graphql/business-platform-destinations/generated/**/*.ts' --fix --rule '@typescript-eslint/ban-ts-comment: off' --rule 'eslint-comments/no-unused-disable: off'",
86+
"pnpm eslint 'src/cli/api/graphql/business-platform-organizations/generated/**/*.{ts,tsx}' --fix --rule '@typescript-eslint/ban-ts-comment: off' --rule 'eslint-comments/no-unused-disable: off'",
87+
"pnpm eslint 'src/cli/api/graphql/dev-stores/generated/**/*.{ts,tsx}' --fix --rule '@typescript-eslint/ban-ts-comment: off' --rule 'eslint-comments/no-unused-disable: off'"],
8588
"cwd": "packages/store"
8689
}
8790
},
@@ -107,21 +110,49 @@
107110
"cwd": "{workspaceRoot}"
108111
}
109112
},
113+
"graphql-codegen:generate:business-platform-organizations": {
114+
"executor": "nx:run-commands",
115+
"inputs": ["{workspaceRoot}/graphql.config.ts", "{projectRoot}/src/cli/api/graphql/business-platform-organizations/**/*.graphql"],
116+
"outputs": ["{projectRoot}/src/cli/api/graphql/business-platform-organizations/generated/**/*.ts"],
117+
"options": {
118+
"commands": [
119+
"pnpm exec graphql-codegen --project=storeBusinessPlatformOrganizations"
120+
],
121+
"cwd": "{workspaceRoot}"
122+
}
123+
},
124+
"graphql-codegen:generate:dev-stores": {
125+
"executor": "nx:run-commands",
126+
"inputs": ["{workspaceRoot}/graphql.config.ts", "{projectRoot}/src/cli/api/graphql/dev-stores/**/*.graphql"],
127+
"outputs": ["{projectRoot}/src/cli/api/graphql/dev-stores/generated/**/*.ts"],
128+
"options": {
129+
"commands": [
130+
"pnpm exec graphql-codegen --project=devStores"
131+
],
132+
"cwd": "{workspaceRoot}"
133+
}
134+
},
110135
"graphql-codegen:postfix": {
111136
"executor": "nx:run-commands",
112137
"dependsOn": [
113138
"graphql-codegen:generate:admin",
114-
"graphql-codegen:generate:business-platform-destinations"
139+
"graphql-codegen:generate:business-platform-destinations",
140+
"graphql-codegen:generate:business-platform-organizations",
141+
"graphql-codegen:generate:dev-stores"
115142
],
116143
"inputs": [{ "dependentTasksOutputFiles": "**/*.ts" }],
117144
"outputs": [
118145
"{projectRoot}/src/cli/api/graphql/admin/generated/**/*.ts",
119-
"{projectRoot}/src/cli/api/graphql/business-platform-destinations/generated/**/*.ts"
146+
"{projectRoot}/src/cli/api/graphql/business-platform-destinations/generated/**/*.ts",
147+
"{projectRoot}/src/cli/api/graphql/business-platform-organizations/generated/**/*.ts",
148+
"{projectRoot}/src/cli/api/graphql/dev-stores/generated/**/*.ts"
120149
],
121150
"options": {
122151
"commands": [
123152
"find ./packages/store/src/cli/api/graphql/admin/generated/ -type f -name '*.ts' -exec sh -c 'sed -i \"\" \"s|import \\* as Types from '\\''./types'\\'';|import \\* as Types from '\\''./types.js'\\'';|g; s|export const \\([A-Za-z0-9_]*\\)Document =|export const \\1 =|g\" \"$0\"' {} \\;",
124-
"find ./packages/store/src/cli/api/graphql/business-platform-destinations/generated/ -type f -name '*.ts' -exec sh -c 'sed -i \"\" \"s|import \\* as Types from '\\''./types'\\'';|import \\* as Types from '\\''./types.js'\\'';|g; s|export const \\([A-Za-z0-9_]*\\)Document =|export const \\1 =|g\" \"$0\"' {} \\;"
153+
"find ./packages/store/src/cli/api/graphql/business-platform-destinations/generated/ -type f -name '*.ts' -exec sh -c 'sed -i \"\" \"s|import \\* as Types from '\\''./types'\\'';|import \\* as Types from '\\''./types.js'\\'';|g; s|export const \\([A-Za-z0-9_]*\\)Document =|export const \\1 =|g\" \"$0\"' {} \\;",
154+
"find ./packages/store/src/cli/api/graphql/business-platform-organizations/generated/ -type f -name '*.ts' -exec sh -c 'sed -i \"\" \"s|import \\* as Types from '\\''./types'\\'';|import \\* as Types from '\\''./types.js'\\'';|g; s|export const \\([A-Za-z0-9_]*\\)Document =|export const \\1 =|g\" \"$0\"' {} \\;",
155+
"find ./packages/store/src/cli/api/graphql/dev-stores/generated/ -type f -name '*.ts' -exec sh -c 'sed -i \"\" \"s|import \\* as Types from '\\''./types'\\'';|import \\* as Types from '\\''./types.js'\\'';|g; s|export const \\([A-Za-z0-9_]*\\)Document =|export const \\1 =|g\" \"$0\"' {} \\;"
125156
],
126157
"cwd": "{workspaceRoot}"
127158
}

0 commit comments

Comments
 (0)