PhoenixKitEcommerce.Shopify.AdminClient (PhoenixKitEcommerce v0.5.6)

Copy Markdown View Source

Thin REST client for the Shopify Admin API's products.json endpoint.

Resolves the shop domain and access token from the PhoenixKit.Integrations connection identified by integration_uuid — never from application env (see PhoenixKitEcommerce.Shopify.Provider for why). Authenticates with the X-Shopify-Access-Token header, NOT Authorization: Bearer — that is what the REST Admin API expects for a Custom App's static token.

Summary

Functions

Fetches the product ids of collection_id, in the order Shopify returns them — Shopify applies the collection's own sort order to this endpoint, so no client-side sorting happens here; CollectionSync reads this order directly as item.position within the category. Paginated like fetch_products/2.

Fetches every collection from the connected store — custom_collections and smart_collections concatenated, each paginated like fetch_products/2. Each returned collection carries "kind" ("custom" or "smart", which endpoint it came from) and "position" — a running index across BOTH lists, in API order (custom first, then smart) — this is the order CollectionSync writes as category.position.

Fetches a single product from the Shopify store connected via integration_uuid, given its Shopify product_id — a single, unpaginated GET /admin/api/<version>/products/{id}.json request. This is the point lookup a per-product panel needs instead of pulling the whole catalog through fetch_products/2 to check one item.

Fetches every product from the Shopify store connected via integration_uuid, following Link: rel="next" pagination.

Fetches the connected store's own shop.json — its name, domain, and crucially its currency. Per the per-domain-currency design (§7.5), a Shopify sync must be able to check the store's own currency against the base currency and refuse price updates on a mismatch rather than silently reimporting numbers that no longer mean what they used to.

Functions

fetch_collection_product_ids(collection_id, opts \\ [])

@spec fetch_collection_product_ids(String.t() | integer(), keyword()) ::
  {:ok, [term()]} | {:error, :invalid_collection_id | term()}

Fetches the product ids of collection_id, in the order Shopify returns them — Shopify applies the collection's own sort order to this endpoint, so no client-side sorting happens here; CollectionSync reads this order directly as item.position within the category. Paginated like fetch_products/2.

Options

Same as fetch_collections/1.

fetch_collections(opts \\ [])

@spec fetch_collections(keyword()) :: {:ok, [map()]} | {:error, term()}

Fetches every collection from the connected store — custom_collections and smart_collections concatenated, each paginated like fetch_products/2. Each returned collection carries "kind" ("custom" or "smart", which endpoint it came from) and "position" — a running index across BOTH lists, in API order (custom first, then smart) — this is the order CollectionSync writes as category.position.

Options

fetch_product(integration_uuid, product_id, opts \\ [])

@spec fetch_product(String.t(), String.t() | integer(), keyword()) ::
  {:ok, map()} | {:error, :invalid_product_id | term()}

Fetches a single product from the Shopify store connected via integration_uuid, given its Shopify product_id — a single, unpaginated GET /admin/api/<version>/products/{id}.json request. This is the point lookup a per-product panel needs instead of pulling the whole catalog through fetch_products/2 to check one item.

Reuses resolve_client/2 (credential resolution/auth), the same @product_fields field set, and the same 429/401/403 handling as fetch_products/2 — but is NOT built on top of fetch_all/5: that helper is shaped around a paginated LIST response (Link: rel="next", an accumulator), while this endpoint returns exactly one "product" map and never paginates. The two diverge on the 404 case too — see below — so a shared status-handling core was not worth the added indirection for what is otherwise a short, linear match.

A 404 here means the given product_id doesn't exist in this store and maps to :not_found — deliberately NOT :shop_not_found (fetch_products/2's 404, meaning the shop domain itself doesn't resolve): the shop answered fine, it just has no such product.

Options

fetch_products(integration_uuid, opts \\ [])

@spec fetch_products(String.t(), keyword()) :: {:ok, [map()]} | {:error, term()}

Fetches every product from the Shopify store connected via integration_uuid, following Link: rel="next" pagination.

Options

  • :req_options — keyword list merged into Req.new/1 (e.g. plug: to stub the transport in tests).

fetch_shop(integration_uuid, opts \\ [])

@spec fetch_shop(String.t(), keyword()) :: {:ok, map()} | {:error, term()}

Fetches the connected store's own shop.json — its name, domain, and crucially its currency. Per the per-domain-currency design (§7.5), a Shopify sync must be able to check the store's own currency against the base currency and refuse price updates on a mismatch rather than silently reimporting numbers that no longer mean what they used to.

A single, unpaginated request — unlike fetch_products/2 and its siblings, there is only ever one shop.

Options