Skip to content

Commit efe7cea

Browse files
committed
Create shopify theme metafields pull command
1 parent 4f35f37 commit efe7cea

File tree

21 files changed

+850
-15
lines changed

21 files changed

+850
-15
lines changed

.changeset/nervous-terms-invite.md

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
---
2+
'@shopify/theme': minor
3+
'@shopify/cli': minor
4+
---
5+
6+
New CLI command under `shopify theme` to pull metafield definitions from the shop
7+
8+
Run command by calling `shopify theme metafields pull`

.changeset/quick-eggs-end.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
'@shopify/cli-kit': patch
3+
---
4+
5+
Introduce method to fetch metafield definitions by ownerType from Admin API
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
shopify theme metafields pull [flags]
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
// This is an autogenerated file. Don't edit this file manually.
2+
export interface thememetafieldspull {
3+
/**
4+
* Disable color output.
5+
* @environment SHOPIFY_FLAG_NO_COLOR
6+
*/
7+
'--no-color'?: ''
8+
9+
/**
10+
* Password generated from the Theme Access app.
11+
* @environment SHOPIFY_CLI_THEME_TOKEN
12+
*/
13+
'--password <value>'?: string
14+
15+
/**
16+
* The path to your theme directory.
17+
* @environment SHOPIFY_FLAG_PATH
18+
*/
19+
'--path <value>'?: string
20+
21+
/**
22+
* Store URL. It can be the store prefix (example) or the full myshopify.com URL (example.myshopify.com, https://example.myshopify.com).
23+
* @environment SHOPIFY_FLAG_STORE
24+
*/
25+
'-s, --store <value>'?: string
26+
27+
/**
28+
* Increase the verbosity of the output.
29+
* @environment SHOPIFY_FLAG_VERBOSE
30+
*/
31+
'--verbose'?: ''
32+
}
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
// This is an autogenerated file. Don't edit this file manually.
2+
import {ReferenceEntityTemplateSchema} from '@shopify/generate-docs'
3+
4+
const data: ReferenceEntityTemplateSchema = {
5+
name: 'theme metafields pull',
6+
description: `Retrieves metafields from Shopify Admin.
7+
8+
If the metafields file already exists, it will be overwritten.`,
9+
overviewPreviewDescription: `Download metafields definitions from your shop into a local file.`,
10+
type: 'command',
11+
isVisualComponent: false,
12+
defaultExample: {
13+
codeblock: {
14+
tabs: [
15+
{
16+
title: 'theme metafields pull',
17+
code: './examples/theme-metafields-pull.example.sh',
18+
language: 'bash',
19+
},
20+
],
21+
title: 'theme metafields pull',
22+
},
23+
},
24+
definitions: [
25+
{
26+
title: 'Flags',
27+
description: 'The following flags are available for the `theme metafields pull` command:',
28+
type: 'thememetafieldspull',
29+
},
30+
],
31+
category: 'theme',
32+
related: [
33+
],
34+
}
35+
36+
export default data

docs-shopify.dev/generated/generated_docs_data.json

Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5332,6 +5332,89 @@
53325332
"category": "theme",
53335333
"related": []
53345334
},
5335+
{
5336+
"name": "theme metafields pull",
5337+
"description": "Retrieves metafields from Shopify Admin.\n\nIf the metafields file already exists, it will be overwritten.",
5338+
"overviewPreviewDescription": "Download metafields definitions from your shop into a local file.",
5339+
"type": "command",
5340+
"isVisualComponent": false,
5341+
"defaultExample": {
5342+
"codeblock": {
5343+
"tabs": [
5344+
{
5345+
"title": "theme metafields pull",
5346+
"code": "shopify theme metafields pull [flags]",
5347+
"language": "bash"
5348+
}
5349+
],
5350+
"title": "theme metafields pull"
5351+
}
5352+
},
5353+
"definitions": [
5354+
{
5355+
"title": "Flags",
5356+
"description": "The following flags are available for the `theme metafields pull` command:",
5357+
"type": "thememetafieldspull",
5358+
"typeDefinitions": {
5359+
"thememetafieldspull": {
5360+
"filePath": "docs-shopify.dev/commands/interfaces/theme-metafields-pull.interface.ts",
5361+
"name": "thememetafieldspull",
5362+
"description": "",
5363+
"members": [
5364+
{
5365+
"filePath": "docs-shopify.dev/commands/interfaces/theme-metafields-pull.interface.ts",
5366+
"syntaxKind": "PropertySignature",
5367+
"name": "--no-color",
5368+
"value": "\"\"",
5369+
"description": "Disable color output.",
5370+
"isOptional": true,
5371+
"environmentValue": "SHOPIFY_FLAG_NO_COLOR"
5372+
},
5373+
{
5374+
"filePath": "docs-shopify.dev/commands/interfaces/theme-metafields-pull.interface.ts",
5375+
"syntaxKind": "PropertySignature",
5376+
"name": "--password <value>",
5377+
"value": "string",
5378+
"description": "Password generated from the Theme Access app.",
5379+
"isOptional": true,
5380+
"environmentValue": "SHOPIFY_CLI_THEME_TOKEN"
5381+
},
5382+
{
5383+
"filePath": "docs-shopify.dev/commands/interfaces/theme-metafields-pull.interface.ts",
5384+
"syntaxKind": "PropertySignature",
5385+
"name": "--path <value>",
5386+
"value": "string",
5387+
"description": "The path to your theme directory.",
5388+
"isOptional": true,
5389+
"environmentValue": "SHOPIFY_FLAG_PATH"
5390+
},
5391+
{
5392+
"filePath": "docs-shopify.dev/commands/interfaces/theme-metafields-pull.interface.ts",
5393+
"syntaxKind": "PropertySignature",
5394+
"name": "--verbose",
5395+
"value": "\"\"",
5396+
"description": "Increase the verbosity of the output.",
5397+
"isOptional": true,
5398+
"environmentValue": "SHOPIFY_FLAG_VERBOSE"
5399+
},
5400+
{
5401+
"filePath": "docs-shopify.dev/commands/interfaces/theme-metafields-pull.interface.ts",
5402+
"syntaxKind": "PropertySignature",
5403+
"name": "-s, --store <value>",
5404+
"value": "string",
5405+
"description": "Store URL. It can be the store prefix (example) or the full myshopify.com URL (example.myshopify.com, https://example.myshopify.com).",
5406+
"isOptional": true,
5407+
"environmentValue": "SHOPIFY_FLAG_STORE"
5408+
}
5409+
],
5410+
"value": "export interface thememetafieldspull {\n /**\n * Disable color output.\n * @environment SHOPIFY_FLAG_NO_COLOR\n */\n '--no-color'?: ''\n\n /**\n * Password generated from the Theme Access app.\n * @environment SHOPIFY_CLI_THEME_TOKEN\n */\n '--password <value>'?: string\n\n /**\n * The path to your theme directory.\n * @environment SHOPIFY_FLAG_PATH\n */\n '--path <value>'?: string\n\n /**\n * Store URL. It can be the store prefix (example) or the full myshopify.com URL (example.myshopify.com, https://example.myshopify.com).\n * @environment SHOPIFY_FLAG_STORE\n */\n '-s, --store <value>'?: string\n\n /**\n * Increase the verbosity of the output.\n * @environment SHOPIFY_FLAG_VERBOSE\n */\n '--verbose'?: ''\n}"
5411+
}
5412+
}
5413+
}
5414+
],
5415+
"category": "theme",
5416+
"related": []
5417+
},
53355418
{
53365419
"name": "theme open",
53375420
"description": "Returns links that let you preview the specified theme. The following links are returned:\n\n - A link to the [editor](/docs/themes/tools/online-editor) for the theme in the Shopify admin.\n - A [preview link](https://help.shopify.com/manual/online-store/themes/adding-themes#share-a-theme-preview-with-others) that you can share with other developers.\n\n If you don't specify a theme, then you're prompted to select the theme to open from the list of the themes in your store.",
Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,87 @@
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 MetafieldDefinitionsByOwnerTypeQueryVariables = Types.Exact<{
7+
ownerType: Types.MetafieldOwnerType
8+
}>
9+
10+
export type MetafieldDefinitionsByOwnerTypeQuery = {
11+
metafieldDefinitions: {
12+
nodes: {
13+
key: string
14+
name: string
15+
namespace: string
16+
description?: string | null
17+
type: {category: string; name: string}
18+
}[]
19+
}
20+
}
21+
22+
export const MetafieldDefinitionsByOwnerType = {
23+
kind: 'Document',
24+
definitions: [
25+
{
26+
kind: 'OperationDefinition',
27+
operation: 'query',
28+
name: {kind: 'Name', value: 'metafieldDefinitionsByOwnerType'},
29+
variableDefinitions: [
30+
{
31+
kind: 'VariableDefinition',
32+
variable: {kind: 'Variable', name: {kind: 'Name', value: 'ownerType'}},
33+
type: {kind: 'NonNullType', type: {kind: 'NamedType', name: {kind: 'Name', value: 'MetafieldOwnerType'}}},
34+
},
35+
],
36+
selectionSet: {
37+
kind: 'SelectionSet',
38+
selections: [
39+
{
40+
kind: 'Field',
41+
name: {kind: 'Name', value: 'metafieldDefinitions'},
42+
arguments: [
43+
{
44+
kind: 'Argument',
45+
name: {kind: 'Name', value: 'ownerType'},
46+
value: {kind: 'Variable', name: {kind: 'Name', value: 'ownerType'}},
47+
},
48+
{kind: 'Argument', name: {kind: 'Name', value: 'first'}, value: {kind: 'IntValue', value: '250'}},
49+
],
50+
selectionSet: {
51+
kind: 'SelectionSet',
52+
selections: [
53+
{
54+
kind: 'Field',
55+
name: {kind: 'Name', value: 'nodes'},
56+
selectionSet: {
57+
kind: 'SelectionSet',
58+
selections: [
59+
{kind: 'Field', name: {kind: 'Name', value: 'key'}},
60+
{kind: 'Field', name: {kind: 'Name', value: 'name'}},
61+
{kind: 'Field', name: {kind: 'Name', value: 'namespace'}},
62+
{kind: 'Field', name: {kind: 'Name', value: 'description'}},
63+
{
64+
kind: 'Field',
65+
name: {kind: 'Name', value: 'type'},
66+
selectionSet: {
67+
kind: 'SelectionSet',
68+
selections: [
69+
{kind: 'Field', name: {kind: 'Name', value: 'category'}},
70+
{kind: 'Field', name: {kind: 'Name', value: 'name'}},
71+
{kind: 'Field', name: {kind: 'Name', value: '__typename'}},
72+
],
73+
},
74+
},
75+
{kind: 'Field', name: {kind: 'Name', value: '__typename'}},
76+
],
77+
},
78+
},
79+
{kind: 'Field', name: {kind: 'Name', value: '__typename'}},
80+
],
81+
},
82+
},
83+
],
84+
},
85+
},
86+
],
87+
} as unknown as DocumentNode<MetafieldDefinitionsByOwnerTypeQuery, MetafieldDefinitionsByOwnerTypeQueryVariables>

packages/cli-kit/src/cli/api/graphql/admin/generated/types.d.ts

Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -120,6 +120,67 @@ export type Scalars = {
120120
UtcOffset: {input: any; output: any}
121121
}
122122

123+
/** Possible types of a metafield's owner resource. */
124+
export type MetafieldOwnerType =
125+
/** The Api Permission metafield owner type. */
126+
| 'API_PERMISSION'
127+
/** The Article metafield owner type. */
128+
| 'ARTICLE'
129+
/** The Blog metafield owner type. */
130+
| 'BLOG'
131+
/** The Brand metafield owner type. */
132+
| 'BRAND'
133+
/** The Cart Transform metafield owner type. */
134+
| 'CARTTRANSFORM'
135+
/** The Collection metafield owner type. */
136+
| 'COLLECTION'
137+
/** The Company metafield owner type. */
138+
| 'COMPANY'
139+
/** The Company Location metafield owner type. */
140+
| 'COMPANY_LOCATION'
141+
/** The Customer metafield owner type. */
142+
| 'CUSTOMER'
143+
/** The Delivery Customization metafield owner type. */
144+
| 'DELIVERY_CUSTOMIZATION'
145+
/** The Delivery Method metafield owner type. */
146+
| 'DELIVERY_METHOD'
147+
/** The Delivery Option Generator metafield owner type. */
148+
| 'DELIVERY_OPTION_GENERATOR'
149+
/** The Discount metafield owner type. */
150+
| 'DISCOUNT'
151+
/** The draft order metafield owner type. */
152+
| 'DRAFTORDER'
153+
/** The Fulfillment Constraint Rule metafield owner type. */
154+
| 'FULFILLMENT_CONSTRAINT_RULE'
155+
/** The Gate Configuration metafield owner type. */
156+
| 'GATE_CONFIGURATION'
157+
/** The GiftCardTransaction metafield owner type. */
158+
| 'GIFT_CARD_TRANSACTION'
159+
/** The Location metafield owner type. */
160+
| 'LOCATION'
161+
/** The Market metafield owner type. */
162+
| 'MARKET'
163+
/** The Media Image metafield owner type. */
164+
| 'MEDIA_IMAGE'
165+
/** The Order metafield owner type. */
166+
| 'ORDER'
167+
/** The Order Routing Location Rule metafield owner type. */
168+
| 'ORDER_ROUTING_LOCATION_RULE'
169+
/** The Page metafield owner type. */
170+
| 'PAGE'
171+
/** The Payment Customization metafield owner type. */
172+
| 'PAYMENT_CUSTOMIZATION'
173+
/** The Product metafield owner type. */
174+
| 'PRODUCT'
175+
/** The Product Variant metafield owner type. */
176+
| 'PRODUCTVARIANT'
177+
/** The Selling Plan metafield owner type. */
178+
| 'SELLING_PLAN'
179+
/** The Shop metafield owner type. */
180+
| 'SHOP'
181+
/** The Validation metafield owner type. */
182+
| 'VALIDATION'
183+
123184
/** Type of a theme file operation result. */
124185
export type OnlineStoreThemeFileResultType =
125186
/** Operation was malformed or invalid. */
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
query metafieldDefinitionsByOwnerType($ownerType: MetafieldOwnerType!) {
2+
metafieldDefinitions(ownerType: $ownerType, first: 250) {
3+
nodes {
4+
key
5+
name
6+
namespace
7+
description
8+
type {
9+
category
10+
name
11+
}
12+
}
13+
}
14+
}

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

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,8 @@ import {ThemeDelete} from '../../../cli/api/graphql/admin/generated/theme_delete
66
import {ThemePublish} from '../../../cli/api/graphql/admin/generated/theme_publish.js'
77
import {GetThemeFileBodies} from '../../../cli/api/graphql/admin/generated/get_theme_file_bodies.js'
88
import {GetThemeFileChecksums} from '../../../cli/api/graphql/admin/generated/get_theme_file_checksums.js'
9+
import {MetafieldDefinitionsByOwnerType} from '../../../cli/api/graphql/admin/generated/metafield_definitions_by_owner_type.js'
10+
import {MetafieldOwnerType} from '../../../cli/api/graphql/admin/generated/types.js'
911
import {restRequest, RestResponse, adminRequestDoc} from '@shopify/cli-kit/node/api/admin'
1012
import {AdminSession} from '@shopify/cli-kit/node/session'
1113
import {AbortError} from '@shopify/cli-kit/node/error'
@@ -212,6 +214,23 @@ export async function themeDelete(id: number, session: AdminSession): Promise<bo
212214
return true
213215
}
214216

217+
export async function metafieldDefinitionsByOwnerType(type: MetafieldOwnerType, session: AdminSession) {
218+
const {metafieldDefinitions} = await adminRequestDoc(MetafieldDefinitionsByOwnerType, session, {
219+
ownerType: type,
220+
})
221+
222+
return metafieldDefinitions.nodes.map((definition) => ({
223+
key: definition.key,
224+
namespace: definition.namespace,
225+
name: definition.name,
226+
description: definition.description,
227+
type: {
228+
name: definition.type.name,
229+
category: definition.type.category,
230+
},
231+
}))
232+
}
233+
215234
async function request<T>(
216235
method: string,
217236
path: string,

0 commit comments

Comments
 (0)