Skip to content

Commit

Permalink
Create shopify theme metafields pull command
Browse files Browse the repository at this point in the history
  • Loading branch information
aswamy committed Nov 4, 2024
1 parent 31e46ef commit 5acbdac
Show file tree
Hide file tree
Showing 12 changed files with 662 additions and 0 deletions.
8 changes: 8 additions & 0 deletions .changeset/nervous-terms-invite.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
---
'@shopify/theme': minor
'@shopify/cli': minor
---

New CLI command under `shopify theme` to pull metafield definitions from the shop

Run command by calling `shopify theme metafields pull`
5 changes: 5 additions & 0 deletions .changeset/quick-eggs-end.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@shopify/cli-kit': patch
---

Introduce method to fetch metafield definitions by ownerType from Admin API
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
/* 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 MetafieldDefinitionsByOwnerTypeQueryVariables = Types.Exact<{
ownerType: Types.MetafieldOwnerType
}>

export type MetafieldDefinitionsByOwnerTypeQuery = {
metafieldDefinitions: {
nodes: {name: string; namespace: string; description?: string | null; type: {category: string; name: string}}[]
}
}

export const MetafieldDefinitionsByOwnerType = {
kind: 'Document',
definitions: [
{
kind: 'OperationDefinition',
operation: 'query',
name: {kind: 'Name', value: 'metafieldDefinitionsByOwnerType'},
variableDefinitions: [
{
kind: 'VariableDefinition',
variable: {kind: 'Variable', name: {kind: 'Name', value: 'ownerType'}},
type: {kind: 'NonNullType', type: {kind: 'NamedType', name: {kind: 'Name', value: 'MetafieldOwnerType'}}},
},
],
selectionSet: {
kind: 'SelectionSet',
selections: [
{
kind: 'Field',
name: {kind: 'Name', value: 'metafieldDefinitions'},
arguments: [
{
kind: 'Argument',
name: {kind: 'Name', value: 'ownerType'},
value: {kind: 'Variable', name: {kind: 'Name', value: 'ownerType'}},
},
{kind: 'Argument', name: {kind: 'Name', value: 'first'}, value: {kind: 'IntValue', value: '250'}},
],
selectionSet: {
kind: 'SelectionSet',
selections: [
{
kind: 'Field',
name: {kind: 'Name', value: 'nodes'},
selectionSet: {
kind: 'SelectionSet',
selections: [
{kind: 'Field', name: {kind: 'Name', value: 'name'}},
{kind: 'Field', name: {kind: 'Name', value: 'namespace'}},
{kind: 'Field', name: {kind: 'Name', value: 'description'}},
{
kind: 'Field',
name: {kind: 'Name', value: 'type'},
selectionSet: {
kind: 'SelectionSet',
selections: [
{kind: 'Field', name: {kind: 'Name', value: 'category'}},
{kind: 'Field', name: {kind: 'Name', value: 'name'}},
{kind: 'Field', name: {kind: 'Name', value: '__typename'}},
],
},
},
{kind: 'Field', name: {kind: 'Name', value: '__typename'}},
],
},
},
{kind: 'Field', name: {kind: 'Name', value: '__typename'}},
],
},
},
],
},
},
],
} as unknown as DocumentNode<MetafieldDefinitionsByOwnerTypeQuery, MetafieldDefinitionsByOwnerTypeQueryVariables>
61 changes: 61 additions & 0 deletions packages/cli-kit/src/cli/api/graphql/admin/generated/types.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -120,6 +120,67 @@ export type Scalars = {
UtcOffset: {input: any; output: any}
}

/** Possible types of a metafield's owner resource. */
export type MetafieldOwnerType =
/** The Api Permission metafield owner type. */
| 'API_PERMISSION'
/** The Article metafield owner type. */
| 'ARTICLE'
/** The Blog metafield owner type. */
| 'BLOG'
/** The Brand metafield owner type. */
| 'BRAND'
/** The Cart Transform metafield owner type. */
| 'CARTTRANSFORM'
/** The Collection metafield owner type. */
| 'COLLECTION'
/** The Company metafield owner type. */
| 'COMPANY'
/** The Company Location metafield owner type. */
| 'COMPANY_LOCATION'
/** The Customer metafield owner type. */
| 'CUSTOMER'
/** The Delivery Customization metafield owner type. */
| 'DELIVERY_CUSTOMIZATION'
/** The Delivery Method metafield owner type. */
| 'DELIVERY_METHOD'
/** The Delivery Option Generator metafield owner type. */
| 'DELIVERY_OPTION_GENERATOR'
/** The Discount metafield owner type. */
| 'DISCOUNT'
/** The draft order metafield owner type. */
| 'DRAFTORDER'
/** The Fulfillment Constraint Rule metafield owner type. */
| 'FULFILLMENT_CONSTRAINT_RULE'
/** The Gate Configuration metafield owner type. */
| 'GATE_CONFIGURATION'
/** The GiftCardTransaction metafield owner type. */
| 'GIFT_CARD_TRANSACTION'
/** The Location metafield owner type. */
| 'LOCATION'
/** The Market metafield owner type. */
| 'MARKET'
/** The Media Image metafield owner type. */
| 'MEDIA_IMAGE'
/** The Order metafield owner type. */
| 'ORDER'
/** The Order Routing Location Rule metafield owner type. */
| 'ORDER_ROUTING_LOCATION_RULE'
/** The Page metafield owner type. */
| 'PAGE'
/** The Payment Customization metafield owner type. */
| 'PAYMENT_CUSTOMIZATION'
/** The Product metafield owner type. */
| 'PRODUCT'
/** The Product Variant metafield owner type. */
| 'PRODUCTVARIANT'
/** The Selling Plan metafield owner type. */
| 'SELLING_PLAN'
/** The Shop metafield owner type. */
| 'SHOP'
/** The Validation metafield owner type. */
| 'VALIDATION'

/** Type of a theme file operation result. */
export type OnlineStoreThemeFileResultType =
/** Operation was malformed or invalid. */
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
query metafieldDefinitionsByOwnerType($ownerType: MetafieldOwnerType!) {
metafieldDefinitions(ownerType: $ownerType, first: 250) {
nodes {
name
namespace
description
type {
category
name
}
}
}
}
19 changes: 19 additions & 0 deletions packages/cli-kit/src/public/node/themes/api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@ import {ThemeDelete} from '../../../cli/api/graphql/admin/generated/theme_delete
import {ThemePublish} from '../../../cli/api/graphql/admin/generated/theme_publish.js'
import {GetThemeFileBodies} from '../../../cli/api/graphql/admin/generated/get_theme_file_bodies.js'
import {GetThemeFileChecksums} from '../../../cli/api/graphql/admin/generated/get_theme_file_checksums.js'
import {MetafieldDefinitionsByOwnerType} from '../../../cli/api/graphql/admin/generated/metafield_definitions_by_owner_type.js'
import {MetafieldOwnerType} from '../../../cli/api/graphql/admin/generated/types.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 Down Expand Up @@ -209,6 +211,23 @@ export async function themeDelete(id: number, session: AdminSession): Promise<bo
return true
}

export async function metafieldDefinitionsByOwnerType(type: MetafieldOwnerType, session: AdminSession) {
return adminRequestDoc(MetafieldDefinitionsByOwnerType, session, {
ownerType: type,
}).then((result) =>
// stripping away the __typename field
result.metafieldDefinitions.nodes.map((node) => ({
name: node.name,
namespace: node.namespace,
description: node.description,
type: {
category: node.type.category,
name: node.type.name,
},
})),
)
}

async function request<T>(
method: string,
path: string,
Expand Down
25 changes: 25 additions & 0 deletions packages/cli/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,7 @@
* [`shopify theme init [name]`](#shopify-theme-init-name)
* [`shopify theme language-server`](#shopify-theme-language-server)
* [`shopify theme list`](#shopify-theme-list)
* [`shopify theme metafields pull`](#shopify-theme-metafields-pull)
* [`shopify theme open`](#shopify-theme-open)
* [`shopify theme package`](#shopify-theme-package)
* [`shopify theme publish`](#shopify-theme-publish)
Expand Down Expand Up @@ -1970,6 +1971,30 @@ DESCRIPTION
Lists the themes in your store, along with their IDs and statuses.
```

## `shopify theme metafields pull`

Download metafields defined on Shopify Admin into a local file.

```
USAGE
$ shopify theme metafields pull [--no-color] [--password <value>] [--path <value>] [-s <value>] [--verbose]
FLAGS
-s, --store=<value> Store URL. It can be the store prefix (example) or the full myshopify.com URL
(example.myshopify.com, https://example.myshopify.com).
--no-color Disable color output.
--password=<value> Password generated from the Theme Access app.
--path=<value> The path to your theme directory.
--verbose Increase the verbosity of the output.
DESCRIPTION
Download metafields defined on Shopify Admin into a local file.
Retrieves metafields from Shopify Admin.
If the metafields file already exists, it will be overwritten.
```

## `shopify theme open`

Opens the preview of your remote theme.
Expand Down
71 changes: 71 additions & 0 deletions packages/cli/oclif.manifest.json
Original file line number Diff line number Diff line change
Expand Up @@ -5365,6 +5365,77 @@
"pluginType": "core",
"strict": true
},
"theme:metafields:pull": {
"aliases": [
],
"args": {
},
"customPluginName": "@shopify/theme",
"description": "Retrieves metafields from Shopify Admin.\n\nIf the metafields file already exists, it will be overwritten.",
"descriptionWithMarkdown": "Retrieves metafields from Shopify Admin.\n\nIf the metafields file already exists, it will be overwritten.",
"flags": {
"force": {
"allowNo": false,
"char": "f",
"description": "Proceed without confirmation, if current directory does not seem to be theme directory.",
"env": "SHOPIFY_FLAG_FORCE",
"hidden": true,
"name": "force",
"type": "boolean"
},
"no-color": {
"allowNo": false,
"description": "Disable color output.",
"env": "SHOPIFY_FLAG_NO_COLOR",
"hidden": false,
"name": "no-color",
"type": "boolean"
},
"password": {
"description": "Password generated from the Theme Access app.",
"env": "SHOPIFY_CLI_THEME_TOKEN",
"hasDynamicHelp": false,
"multiple": false,
"name": "password",
"type": "option"
},
"path": {
"description": "The path to your theme directory.",
"env": "SHOPIFY_FLAG_PATH",
"hasDynamicHelp": false,
"multiple": false,
"name": "path",
"noCacheDefault": true,
"type": "option"
},
"store": {
"char": "s",
"description": "Store URL. It can be the store prefix (example) or the full myshopify.com URL (example.myshopify.com, https://example.myshopify.com).",
"env": "SHOPIFY_FLAG_STORE",
"hasDynamicHelp": false,
"multiple": false,
"name": "store",
"type": "option"
},
"verbose": {
"allowNo": false,
"description": "Increase the verbosity of the output.",
"env": "SHOPIFY_FLAG_VERBOSE",
"hidden": false,
"name": "verbose",
"type": "boolean"
}
},
"hasDynamicHelp": false,
"hiddenAliases": [
],
"id": "theme:metafields:pull",
"pluginAlias": "@shopify/cli",
"pluginName": "@shopify/cli",
"pluginType": "core",
"strict": true,
"summary": "Download metafields defined on Shopify Admin into a local file."
},
"theme:open": {
"aliases": [
],
Expand Down
44 changes: 44 additions & 0 deletions packages/theme/src/cli/commands/theme/metafields/pull.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
import {themeFlags} from '../../../flags.js'
import {metafieldsPull, MetafieldsPullFlags} from '../../../services/metafields-pull.js'
import ThemeCommand from '../../../utilities/theme-command.js'
import {globalFlags} from '@shopify/cli-kit/node/cli'
import {Flags} from '@oclif/core'

export default class MetafieldsPull extends ThemeCommand {
static summary = 'Download metafields defined on Shopify Admin into a local file.'

static descriptionWithMarkdown = `Retrieves metafields from Shopify Admin.
If the metafields file already exists, it will be overwritten.`

static description = this.descriptionWithoutMarkdown()

static flags = {
...globalFlags,
...{
path: themeFlags.path,
password: themeFlags.password,
store: themeFlags.store,
},
force: Flags.boolean({
hidden: true,
char: 'f',
description: 'Proceed without confirmation, if current directory does not seem to be theme directory.',
env: 'SHOPIFY_FLAG_FORCE',
}),
}

async run(): Promise<void> {
const {flags} = await this.parse(MetafieldsPull)
const args: MetafieldsPullFlags = {
path: flags.path,
password: flags.password,
store: flags.store,
force: flags.force,
verbose: flags.verbose,
noColor: flags['no-color'],
}

await metafieldsPull(args)
}
}
Loading

0 comments on commit 5acbdac

Please sign in to comment.