Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
37 changes: 28 additions & 9 deletions mindsdb/integrations/handlers/shopify_handler/connection_args.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,27 +6,46 @@
connection_args = OrderedDict(
shop_url={
"type": ARG_TYPE.STR,
"description": "Shop url",
"description": "Shop url (e.g. shop-123456.myshopify.com)",
"required": True,
"label": "Shop url",
},
access_token={
"type": ARG_TYPE.PWD,
"description": "Permanent access token for the Shopify Admin API. Use this instead of client_id/client_secret when you already have a token from a custom app.",
"required": False,
"label": "Access token",
"secret": True,
},
client_id={
"type": ARG_TYPE.STR,
"description": "Client ID of the app",
"required": True,
"label": "client_id",
"description": "Client ID of the Shopify app (used to obtain an access token via OAuth client credentials flow).",
"required": False,
"label": "Client ID",
},
client_secret={
"type": ARG_TYPE.PWD,
"description": "Secret of the app",
"required": True,
"label": "Database",
"description": "Client secret of the Shopify app (used with client_id to obtain an access token).",
"required": False,
"label": "Client secret",
"secret": True,
},
refresh_token={
"type": ARG_TYPE.PWD,
"description": "Offline refresh token from Shopify OAuth. Used with client_id and client_secret to rotate expiring access tokens.",
"required": False,
"label": "Refresh token",
"secret": True,
},
expires_at={
"type": ARG_TYPE.STR,
"description": "ISO 8601 timestamp of when the current access token expires (e.g. 2026-04-09T12:00:00+00:00).",
"required": False,
"label": "Token expiry",
},
)

connection_args_example = OrderedDict(
shop_url="shop-123456.myshopify.com",
client_id="secret",
client_secret="shpss_secret",
access_token="shpat_xxxxxxxxxxxxxxxxxxxx",
)
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
from .common import AliasesEnum, MailingAddress
from .utils import Extract, Nodes


class AbandonedCheckoutLineItem(AliasesEnum):
"""Minimal representation of an abandoned checkout line item."""

id = "id"
title = "title"
quantity = "quantity"
variantTitle = "variantTitle"


class AbandonedCheckouts(AliasesEnum):
"""A class to represent a Shopify GraphQL abandoned checkout.
Reference: https://shopify.dev/docs/api/admin-graphql/latest/objects/AbandonedCheckout
Require `read_checkouts` permission.
"""

abandonedCheckoutUrl = "abandonedCheckoutUrl"
completedAt = "completedAt"
createdAt = "createdAt"
customerId = Extract("customer", "id")
id = "id"
lineItems = Nodes(AbandonedCheckoutLineItem)
shippingAddress = MailingAddress
updatedAt = "updatedAt"


columns = [
{
"TABLE_NAME": "abandoned_checkouts",
"COLUMN_NAME": "abandonedCheckoutUrl",
"DATA_TYPE": "TEXT",
"COLUMN_DESCRIPTION": "The URL for the abandoned checkout.",
"IS_NULLABLE": True,
},
{
"TABLE_NAME": "abandoned_checkouts",
"COLUMN_NAME": "completedAt",
"DATA_TYPE": "TEXT",
"COLUMN_DESCRIPTION": "The date and time when the checkout was completed.",
"IS_NULLABLE": True,
},
{
"TABLE_NAME": "abandoned_checkouts",
"COLUMN_NAME": "createdAt",
"DATA_TYPE": "TEXT",
"COLUMN_DESCRIPTION": "The date and time when the checkout was created.",
"IS_NULLABLE": False,
},
{
"TABLE_NAME": "abandoned_checkouts",
"COLUMN_NAME": "customerId",
"DATA_TYPE": "TEXT",
"COLUMN_DESCRIPTION": "The ID of the customer who started the checkout.",
"IS_NULLABLE": True,
},
{
"TABLE_NAME": "abandoned_checkouts",
"COLUMN_NAME": "id",
"DATA_TYPE": "TEXT",
"COLUMN_DESCRIPTION": "A globally-unique ID.",
"IS_NULLABLE": False,
},
{
"TABLE_NAME": "abandoned_checkouts",
"COLUMN_NAME": "lineItems",
"DATA_TYPE": "JSON",
"COLUMN_DESCRIPTION": "The line items in the checkout.",
"IS_NULLABLE": True,
},
{
"TABLE_NAME": "abandoned_checkouts",
"COLUMN_NAME": "shippingAddress",
"DATA_TYPE": "JSON",
"COLUMN_DESCRIPTION": "The shipping address of the checkout.",
"IS_NULLABLE": True,
},
{
"TABLE_NAME": "abandoned_checkouts",
"COLUMN_NAME": "updatedAt",
"DATA_TYPE": "TEXT",
"COLUMN_DESCRIPTION": "The date and time when the checkout was last updated.",
"IS_NULLABLE": False,
},
]
17 changes: 17 additions & 0 deletions mindsdb/integrations/handlers/shopify_handler/models/analytics.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
# Analytics uses a custom list() in shopify_tables.py that passes a raw ShopifyQL
# string to the shopifyqlQuery GraphQL root field. The returned columns are dynamic
# (determined by the query at runtime), so no AliasesEnum is defined here.
#
# Usage:
# SELECT * FROM shopify.analytics
# WHERE query = 'FROM sales SHOW total_sales, orders_count GROUP BY day SINCE -30d'

columns = [
{
"TABLE_NAME": "analytics",
"COLUMN_NAME": "query",
"DATA_TYPE": "TEXT",
"COLUMN_DESCRIPTION": "The ShopifyQL query string to execute (used as a WHERE condition). Result columns are dynamic.",
"IS_NULLABLE": False,
},
]
126 changes: 126 additions & 0 deletions mindsdb/integrations/handlers/shopify_handler/models/articles.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,126 @@
from .common import AliasesEnum
from .utils import Extract


class ArticleAuthor(AliasesEnum):
"""A class to represent a Shopify GraphQL article author.
Reference: https://shopify.dev/docs/api/admin-graphql/latest/objects/ArticleAuthor
"""

name = "name"


class Articles(AliasesEnum):
"""A class to represent a Shopify GraphQL article.
Reference: https://shopify.dev/docs/api/admin-graphql/latest/objects/Article
Require `read_content` permission.
"""

author = ArticleAuthor
blogId = Extract("blog", "id")
blogTitle = Extract("blog", "title")
body = "body"
createdAt = "createdAt"
handle = "handle"
id = "id"
isPublished = "isPublished"
publishedAt = "publishedAt"
tags = "tags"
templateSuffix = "templateSuffix"
title = "title"
updatedAt = "updatedAt"


columns = [
{
"TABLE_NAME": "articles",
"COLUMN_NAME": "author",
"DATA_TYPE": "JSON",
"COLUMN_DESCRIPTION": "The author of the article.",
"IS_NULLABLE": False,
},
{
"TABLE_NAME": "articles",
"COLUMN_NAME": "blogId",
"DATA_TYPE": "TEXT",
"COLUMN_DESCRIPTION": "The ID of the blog this article belongs to.",
"IS_NULLABLE": False,
},
{
"TABLE_NAME": "articles",
"COLUMN_NAME": "blogTitle",
"DATA_TYPE": "TEXT",
"COLUMN_DESCRIPTION": "The title of the blog this article belongs to.",
"IS_NULLABLE": False,
},
{
"TABLE_NAME": "articles",
"COLUMN_NAME": "body",
"DATA_TYPE": "TEXT",
"COLUMN_DESCRIPTION": "The content of the article in HTML format.",
"IS_NULLABLE": False,
},
{
"TABLE_NAME": "articles",
"COLUMN_NAME": "createdAt",
"DATA_TYPE": "TEXT",
"COLUMN_DESCRIPTION": "The date and time when the article was created.",
"IS_NULLABLE": False,
},
{
"TABLE_NAME": "articles",
"COLUMN_NAME": "handle",
"DATA_TYPE": "TEXT",
"COLUMN_DESCRIPTION": "A unique, human-readable string for the article.",
"IS_NULLABLE": False,
},
{
"TABLE_NAME": "articles",
"COLUMN_NAME": "id",
"DATA_TYPE": "TEXT",
"COLUMN_DESCRIPTION": "A globally-unique ID.",
"IS_NULLABLE": False,
},
{
"TABLE_NAME": "articles",
"COLUMN_NAME": "isPublished",
"DATA_TYPE": "BOOLEAN",
"COLUMN_DESCRIPTION": "Whether the article is published.",
"IS_NULLABLE": False,
},
{
"TABLE_NAME": "articles",
"COLUMN_NAME": "publishedAt",
"DATA_TYPE": "TEXT",
"COLUMN_DESCRIPTION": "The date and time when the article was published.",
"IS_NULLABLE": True,
},
{
"TABLE_NAME": "articles",
"COLUMN_NAME": "tags",
"DATA_TYPE": "JSON",
"COLUMN_DESCRIPTION": "A list of tags associated with the article.",
"IS_NULLABLE": False,
},
{
"TABLE_NAME": "articles",
"COLUMN_NAME": "templateSuffix",
"DATA_TYPE": "TEXT",
"COLUMN_DESCRIPTION": "The suffix of the Liquid template for the article.",
"IS_NULLABLE": True,
},
{
"TABLE_NAME": "articles",
"COLUMN_NAME": "title",
"DATA_TYPE": "TEXT",
"COLUMN_DESCRIPTION": "The title of the article.",
"IS_NULLABLE": False,
},
{
"TABLE_NAME": "articles",
"COLUMN_NAME": "updatedAt",
"DATA_TYPE": "TEXT",
"COLUMN_DESCRIPTION": "The date and time when the article was last updated.",
"IS_NULLABLE": False,
},
]
69 changes: 69 additions & 0 deletions mindsdb/integrations/handlers/shopify_handler/models/blogs.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
from .common import AliasesEnum, Count


class Blogs(AliasesEnum):
"""A class to represent a Shopify GraphQL blog.
Reference: https://shopify.dev/docs/api/admin-graphql/latest/objects/Blog
Require `read_content` permission.
"""

articlesCount = Count
createdAt = "createdAt"
handle = "handle"
id = "id"
templateSuffix = "templateSuffix"
title = "title"
updatedAt = "updatedAt"


columns = [
{
"TABLE_NAME": "blogs",
"COLUMN_NAME": "articlesCount",
"DATA_TYPE": "JSON",
"COLUMN_DESCRIPTION": "The number of articles in the blog.",
"IS_NULLABLE": False,
},
{
"TABLE_NAME": "blogs",
"COLUMN_NAME": "createdAt",
"DATA_TYPE": "TEXT",
"COLUMN_DESCRIPTION": "The date and time when the blog was created.",
"IS_NULLABLE": False,
},
{
"TABLE_NAME": "blogs",
"COLUMN_NAME": "handle",
"DATA_TYPE": "TEXT",
"COLUMN_DESCRIPTION": "A unique, human-readable string for the blog.",
"IS_NULLABLE": False,
},
{
"TABLE_NAME": "blogs",
"COLUMN_NAME": "id",
"DATA_TYPE": "TEXT",
"COLUMN_DESCRIPTION": "A globally-unique ID.",
"IS_NULLABLE": False,
},
{
"TABLE_NAME": "blogs",
"COLUMN_NAME": "templateSuffix",
"DATA_TYPE": "TEXT",
"COLUMN_DESCRIPTION": "The suffix of the Liquid template being used for the blog.",
"IS_NULLABLE": True,
},
{
"TABLE_NAME": "blogs",
"COLUMN_NAME": "title",
"DATA_TYPE": "TEXT",
"COLUMN_DESCRIPTION": "The title of the blog.",
"IS_NULLABLE": False,
},
{
"TABLE_NAME": "blogs",
"COLUMN_NAME": "updatedAt",
"DATA_TYPE": "TEXT",
"COLUMN_DESCRIPTION": "The date and time when the blog was last updated.",
"IS_NULLABLE": False,
},
]
Loading
Loading