Skip to content

Commit

Permalink
Port fetchTheme / fetchThemes to graphQL
Browse files Browse the repository at this point in the history
  • Loading branch information
catlee committed Dec 18, 2024
1 parent 4e3ffcc commit 16892f3
Show file tree
Hide file tree
Showing 6 changed files with 209 additions and 14 deletions.
54 changes: 54 additions & 0 deletions packages/cli-kit/src/cli/api/graphql/admin/generated/get_theme.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
/* eslint-disable @typescript-eslint/consistent-type-definitions */
import * as Types from './types.js'

import {TypedDocumentNode as DocumentNode} from '@graphql-typed-document-node/core'

export type GetThemeQueryVariables = Types.Exact<{
id: Types.Scalars['ID']['input']
}>

export type GetThemeQuery = {theme?: {id: string; name: string; role: Types.ThemeRole; processing: boolean} | null}

export const GetTheme = {
kind: 'Document',
definitions: [
{
kind: 'OperationDefinition',
operation: 'query',
name: {kind: 'Name', value: 'getTheme'},
variableDefinitions: [
{
kind: 'VariableDefinition',
variable: {kind: 'Variable', name: {kind: 'Name', value: 'id'}},
type: {kind: 'NonNullType', type: {kind: 'NamedType', name: {kind: 'Name', value: 'ID'}}},
},
],
selectionSet: {
kind: 'SelectionSet',
selections: [
{
kind: 'Field',
name: {kind: 'Name', value: 'theme'},
arguments: [
{
kind: 'Argument',
name: {kind: 'Name', value: 'id'},
value: {kind: 'Variable', name: {kind: 'Name', value: 'id'}},
},
],
selectionSet: {
kind: 'SelectionSet',
selections: [
{kind: 'Field', name: {kind: 'Name', value: 'id'}},
{kind: 'Field', name: {kind: 'Name', value: 'name'}},
{kind: 'Field', name: {kind: 'Name', value: 'role'}},
{kind: 'Field', name: {kind: 'Name', value: 'processing'}},
{kind: 'Field', name: {kind: 'Name', value: '__typename'}},
],
},
},
],
},
},
],
} as unknown as DocumentNode<GetThemeQuery, GetThemeQueryVariables>
82 changes: 82 additions & 0 deletions packages/cli-kit/src/cli/api/graphql/admin/generated/get_themes.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
/* eslint-disable @typescript-eslint/consistent-type-definitions */
import * as Types from './types.js'

import {TypedDocumentNode as DocumentNode} from '@graphql-typed-document-node/core'

export type GetThemesQueryVariables = Types.Exact<{
after?: Types.InputMaybe<Types.Scalars['String']['input']>
}>

export type GetThemesQuery = {
themes?: {
nodes: {id: string; name: string; role: Types.ThemeRole; processing: boolean}[]
pageInfo: {hasNextPage: boolean; endCursor?: string | null}
} | null
}

export const GetThemes = {
kind: 'Document',
definitions: [
{
kind: 'OperationDefinition',
operation: 'query',
name: {kind: 'Name', value: 'getThemes'},
variableDefinitions: [
{
kind: 'VariableDefinition',
variable: {kind: 'Variable', name: {kind: 'Name', value: 'after'}},
type: {kind: 'NamedType', name: {kind: 'Name', value: 'String'}},
},
],
selectionSet: {
kind: 'SelectionSet',
selections: [
{
kind: 'Field',
name: {kind: 'Name', value: 'themes'},
arguments: [
{kind: 'Argument', name: {kind: 'Name', value: 'first'}, value: {kind: 'IntValue', value: '50'}},
{
kind: 'Argument',
name: {kind: 'Name', value: 'after'},
value: {kind: 'Variable', name: {kind: 'Name', value: 'after'}},
},
],
selectionSet: {
kind: 'SelectionSet',
selections: [
{
kind: 'Field',
name: {kind: 'Name', value: 'nodes'},
selectionSet: {
kind: 'SelectionSet',
selections: [
{kind: 'Field', name: {kind: 'Name', value: 'id'}},
{kind: 'Field', name: {kind: 'Name', value: 'name'}},
{kind: 'Field', name: {kind: 'Name', value: 'role'}},
{kind: 'Field', name: {kind: 'Name', value: 'processing'}},
{kind: 'Field', name: {kind: 'Name', value: '__typename'}},
],
},
},
{
kind: 'Field',
name: {kind: 'Name', value: 'pageInfo'},
selectionSet: {
kind: 'SelectionSet',
selections: [
{kind: 'Field', name: {kind: 'Name', value: 'hasNextPage'}},
{kind: 'Field', name: {kind: 'Name', value: 'endCursor'}},
{kind: 'Field', name: {kind: 'Name', value: '__typename'}},
],
},
},
{kind: 'Field', name: {kind: 'Name', value: '__typename'}},
],
},
},
],
},
},
],
} as unknown as DocumentNode<GetThemesQuery, GetThemesQueryVariables>
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@

query getTheme($id: ID!) {
theme(id: $id) {
id
name
role
processing
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@

query getThemes($after: String) {
themes(first: 50, after: $after) {
nodes {
id
name
role
processing
}
pageInfo {
hasNextPage
endCursor
}
}
}
16 changes: 8 additions & 8 deletions packages/cli-kit/src/public/node/themes/api.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ import {ThemePublish} from '../../../cli/api/graphql/admin/generated/theme_publi
import {GetThemeFileChecksums} from '../../../cli/api/graphql/admin/generated/get_theme_file_checksums.js'
import {ThemeFilesUpsert} from '../../../cli/api/graphql/admin/generated/theme_files_upsert.js'
import {OnlineStoreThemeFileBodyInputType} from '../../../cli/api/graphql/admin/generated/types.js'
import {GetThemes} from '../../../cli/api/graphql/admin/generated/get_themes.js'
import {test, vi, expect, describe} from 'vitest'
import {adminRequestDoc, restRequest, supportedApiVersions} from '@shopify/cli-kit/node/api/admin'
import {AbortError} from '@shopify/cli-kit/node/error'
Expand All @@ -31,22 +32,21 @@ const sessions = {CLI: session, 'Theme Access': themeAccessSession}
describe('fetchThemes', () => {
test('returns store themes', async () => {
// Given
vi.mocked(restRequest).mockResolvedValue({
json: {
themes: [
{id: 123, name: 'store theme 1', processing: false},
{id: 456, name: 'store theme 2', processing: true},
vi.mocked(adminRequestDoc).mockResolvedValue({
themes: {
nodes: [
{id: 'gid://shopify/OnlineStoreTheme/123', name: 'store theme 1', role: 'UNPUBLISHED', processing: false},
{id: 'gid://shopify/OnlineStoreTheme/456', name: 'store theme 2', role: 'MAIN', processing: true},
],
pageInfo: {hasNextPage: false, endCursor: null},
},
status: 200,
headers: {},
})

// When
const themes = await fetchThemes(session)

// Then
expect(restRequest).toHaveBeenCalledWith('GET', '/themes', session, undefined, {fields: 'id,name,role,processing'})
expect(adminRequestDoc).toHaveBeenCalledWith(GetThemes, session, {after: null})
expect(themes).toHaveLength(2)

expect(themes[0]!.id).toEqual(123)
Expand Down
47 changes: 41 additions & 6 deletions packages/cli-kit/src/public/node/themes/api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@ import {
MetafieldOwnerType,
} from '../../../cli/api/graphql/admin/generated/types.js'
import {MetafieldDefinitionsByOwnerType} from '../../../cli/api/graphql/admin/generated/metafield_definitions_by_owner_type.js'
import {GetThemes} from '../../../cli/api/graphql/admin/generated/get_themes.js'
import {GetTheme} from '../../../cli/api/graphql/admin/generated/get_theme.js'
import {restRequest, RestResponse, adminRequestDoc} from '@shopify/cli-kit/node/api/admin'
import {AdminSession} from '@shopify/cli-kit/node/session'
import {AbortError} from '@shopify/cli-kit/node/error'
Expand All @@ -28,15 +30,48 @@ export type ThemeParams = Partial<Pick<Theme, 'name' | 'role' | 'processing' | '
export type AssetParams = Pick<ThemeAsset, 'key'> & Partial<Pick<ThemeAsset, 'value' | 'attachment'>>

export async function fetchTheme(id: number, session: AdminSession): Promise<Theme | undefined> {
const response = await request('GET', `/themes/${id}`, session, undefined, {fields: 'id,name,role,processing'})
return buildTheme(response.json.theme)
const response = await adminRequestDoc(GetTheme, session, {id: composeThemeGid(id)})

const {theme} = response
if (!theme) {
return undefined
}
return buildTheme({
id: parseGid(theme.id),
processing: theme.processing,
role: theme.role.toLowerCase(),
name: theme.name,
})
}

export async function fetchThemes(session: AdminSession): Promise<Theme[]> {
const response = await request('GET', '/themes', session, undefined, {fields: 'id,name,role,processing'})
const themes = response.json?.themes
if (themes?.length > 0) return themes.map(buildTheme)
return []
const themes: Theme[] = []
let after: string | null = null

while (true) {
// eslint-disable-next-line no-await-in-loop
const response = await adminRequestDoc(GetThemes, session, {after})
if (!response.themes) {
unexpectedGraphQLError('Failed to fetch themes')
}
const {nodes, pageInfo} = response.themes
nodes.forEach((theme) => {
const t = buildTheme({
id: parseGid(theme.id),
processing: theme.processing,
role: theme.role.toLowerCase(),
name: theme.name,
})
if (t !== undefined) {
themes.push(t)
}
})
if (!pageInfo.hasNextPage) {
return themes
}

after = pageInfo.endCursor as string
}
}

export async function createTheme(params: ThemeParams, session: AdminSession): Promise<Theme | undefined> {
Expand Down

0 comments on commit 16892f3

Please sign in to comment.