Skip to content

Commit 7abf580

Browse files
committed
Add ability to fetch development themes by name
1 parent b014978 commit 7abf580

4 files changed

Lines changed: 179 additions & 0 deletions

File tree

Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
/* eslint-disable @typescript-eslint/consistent-type-definitions */
2+
import * as Types from './types.js'
3+
4+
import {TypedDocumentNode as DocumentNode} from '@graphql-typed-document-node/core'
5+
6+
export type FindDevelopmentThemeByNameQueryVariables = Types.Exact<{
7+
name: Types.Scalars['String']['input']
8+
}>
9+
10+
export type FindDevelopmentThemeByNameQuery = {
11+
themes?: {nodes: {id: string; name: string; role: Types.ThemeRole; processing: boolean}[]} | null
12+
}
13+
14+
export const FindDevelopmentThemeByName = {
15+
kind: 'Document',
16+
definitions: [
17+
{
18+
kind: 'OperationDefinition',
19+
operation: 'query',
20+
name: {kind: 'Name', value: 'findDevelopmentThemeByName'},
21+
variableDefinitions: [
22+
{
23+
kind: 'VariableDefinition',
24+
variable: {kind: 'Variable', name: {kind: 'Name', value: 'name'}},
25+
type: {kind: 'NonNullType', type: {kind: 'NamedType', name: {kind: 'Name', value: 'String'}}},
26+
},
27+
],
28+
selectionSet: {
29+
kind: 'SelectionSet',
30+
selections: [
31+
{
32+
kind: 'Field',
33+
name: {kind: 'Name', value: 'themes'},
34+
arguments: [
35+
{kind: 'Argument', name: {kind: 'Name', value: 'first'}, value: {kind: 'IntValue', value: '2'}},
36+
{
37+
kind: 'Argument',
38+
name: {kind: 'Name', value: 'names'},
39+
value: {kind: 'ListValue', values: [{kind: 'Variable', name: {kind: 'Name', value: 'name'}}]},
40+
},
41+
{
42+
kind: 'Argument',
43+
name: {kind: 'Name', value: 'roles'},
44+
value: {kind: 'ListValue', values: [{kind: 'EnumValue', value: 'DEVELOPMENT'}]},
45+
},
46+
],
47+
selectionSet: {
48+
kind: 'SelectionSet',
49+
selections: [
50+
{
51+
kind: 'Field',
52+
name: {kind: 'Name', value: 'nodes'},
53+
selectionSet: {
54+
kind: 'SelectionSet',
55+
selections: [
56+
{kind: 'Field', name: {kind: 'Name', value: 'id'}},
57+
{kind: 'Field', name: {kind: 'Name', value: 'name'}},
58+
{kind: 'Field', name: {kind: 'Name', value: 'role'}},
59+
{kind: 'Field', name: {kind: 'Name', value: 'processing'}},
60+
{kind: 'Field', name: {kind: 'Name', value: '__typename'}},
61+
],
62+
},
63+
},
64+
{kind: 'Field', name: {kind: 'Name', value: '__typename'}},
65+
],
66+
},
67+
},
68+
],
69+
},
70+
},
71+
],
72+
} as unknown as DocumentNode<FindDevelopmentThemeByNameQuery, FindDevelopmentThemeByNameQueryVariables>
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
query findDevelopmentThemeByName($name: String!) {
2+
themes(first: 2, names: [$name], roles: [DEVELOPMENT]) {
3+
nodes {
4+
id
5+
name
6+
role
7+
processing
8+
}
9+
}
10+
}

packages/cli-kit/src/public/node/themes/api.test.ts

Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import {
44
themeDuplicate,
55
fetchTheme,
66
fetchThemes,
7+
findDevelopmentThemeByName,
78
ThemeParams,
89
themeUpdate,
910
themePublish,
@@ -24,6 +25,7 @@ import {ThemeFilesUpsert} from '../../../cli/api/graphql/admin/generated/theme_f
2425
import {ThemeFilesDelete} from '../../../cli/api/graphql/admin/generated/theme_files_delete.js'
2526
import {GetThemes} from '../../../cli/api/graphql/admin/generated/get_themes.js'
2627
import {GetTheme} from '../../../cli/api/graphql/admin/generated/get_theme.js'
28+
import {FindDevelopmentThemeByName} from '../../../cli/api/graphql/admin/generated/find_development_theme_by_name.js'
2729
import {adminRequestDoc, supportedApiVersions} from '../api/admin.js'
2830
import {AbortError} from '../error.js'
2931
import {test, vi, expect, describe, beforeEach} from 'vitest'
@@ -88,6 +90,68 @@ describe('fetchTheme', () => {
8890
})
8991
})
9092

93+
describe('findDevelopmentThemeByName', () => {
94+
test('returns a development theme with a specific name', async () => {
95+
vi.mocked(adminRequestDoc).mockResolvedValue({
96+
themes: {
97+
nodes: [{id: 'gid://shopify/OnlineStoreTheme/1', name: 'PR-123', role: 'DEVELOPMENT', processing: false}],
98+
},
99+
})
100+
101+
// When
102+
const theme = await findDevelopmentThemeByName('PR-123', session)
103+
104+
// Then
105+
expect(adminRequestDoc).toHaveBeenCalledWith({
106+
query: FindDevelopmentThemeByName,
107+
session,
108+
variables: {name: 'PR-123'},
109+
responseOptions: {handleErrors: false},
110+
preferredBehaviour: expectedApiOptions,
111+
})
112+
113+
expect(theme).not.toBeNull()
114+
expect(theme!.id).toEqual(1)
115+
expect(theme!.name).toEqual('PR-123')
116+
expect(theme!.processing).toBeFalsy()
117+
})
118+
119+
test('returns undefined when a theme is not found', async () => {
120+
vi.mocked(adminRequestDoc).mockResolvedValue({
121+
themes: {
122+
nodes: [],
123+
},
124+
})
125+
126+
const theme = await findDevelopmentThemeByName('PR-123', session)
127+
128+
expect(theme).toBeUndefined()
129+
})
130+
131+
test('aborts if there is more than one development theme with the same name', async () => {
132+
vi.mocked(adminRequestDoc).mockResolvedValue({
133+
themes: {
134+
nodes: [
135+
{id: 'gid://shopify/OnlineStoreTheme/1', name: 'PR-123', role: 'DEVELOPMENT', processing: true},
136+
{id: 'gid://shopify/OnlineStoreTheme/2', name: 'PR-123', role: 'DEVELOPMENT', processing: true},
137+
],
138+
},
139+
})
140+
141+
await expect(findDevelopmentThemeByName('PR-123', session)).rejects.toThrow(AbortError)
142+
})
143+
144+
test('returns undefined when the query errors', async () => {
145+
const errorResponse = {
146+
status: 200,
147+
errors: [{message: 'Theme not found'} as any],
148+
}
149+
vi.mocked(adminRequestDoc).mockRejectedValue(new ClientError(errorResponse, {query: ''}))
150+
151+
await expect(findDevelopmentThemeByName('PR-123', session)).rejects.toThrow()
152+
})
153+
})
154+
91155
describe('fetchThemes', () => {
92156
test('returns store themes', async () => {
93157
// Given

packages/cli-kit/src/public/node/themes/api.ts

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ import {
2222
import {MetafieldDefinitionsByOwnerType} from '../../../cli/api/graphql/admin/generated/metafield_definitions_by_owner_type.js'
2323
import {GetThemes} from '../../../cli/api/graphql/admin/generated/get_themes.js'
2424
import {GetTheme} from '../../../cli/api/graphql/admin/generated/get_theme.js'
25+
import {FindDevelopmentThemeByName} from '../../../cli/api/graphql/admin/generated/find_development_theme_by_name.js'
2526
import {OnlineStorePasswordProtection} from '../../../cli/api/graphql/admin/generated/online_store_password_protection.js'
2627
import {RequestModeInput} from '../http.js'
2728
import {adminRequestDoc} from '../api/admin.js'
@@ -113,6 +114,38 @@ export async function fetchThemes(session: AdminSession): Promise<Theme[]> {
113114
}
114115
}
115116

117+
export async function findDevelopmentThemeByName(name: string, session: AdminSession): Promise<Theme | undefined> {
118+
recordEvent('theme-api:find-development-theme-by-name')
119+
120+
const {themes} = await adminRequestDoc({
121+
query: FindDevelopmentThemeByName,
122+
session,
123+
variables: {name},
124+
responseOptions: {handleErrors: false},
125+
preferredBehaviour: THEME_API_NETWORK_BEHAVIOUR,
126+
})
127+
128+
if (!themes) {
129+
unexpectedGraphQLError('Failed to fetch themes')
130+
}
131+
132+
if (themes.nodes.length > 1) {
133+
throw recordError(new AbortError(`More than one development theme is named "${name}"`))
134+
}
135+
136+
if (themes.nodes.length === 1) {
137+
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
138+
const {id, processing, role, name} = themes.nodes[0]!
139+
140+
return buildTheme({
141+
id: parseGid(id),
142+
processing,
143+
role: role.toLowerCase(),
144+
name,
145+
})
146+
}
147+
}
148+
116149
export async function themeCreate(params: ThemeParams, session: AdminSession): Promise<Theme | undefined> {
117150
const themeSource = params.src ?? SkeletonThemeCdn
118151
recordEvent('theme-api:create-theme')

0 commit comments

Comments
 (0)