@weareseeed/medusa-athos-plugin 0.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.medusa/server/src/admin/index.js +259 -0
- package/.medusa/server/src/admin/index.mjs +258 -0
- package/.medusa/server/src/api/admin/athos/config/route.d.ts +4 -0
- package/.medusa/server/src/api/admin/athos/config/route.js +21 -0
- package/.medusa/server/src/api/admin/athos/middlewares.d.ts +11 -0
- package/.medusa/server/src/api/admin/athos/middlewares.js +20 -0
- package/.medusa/server/src/api/admin/plugin/route.d.ts +2 -0
- package/.medusa/server/src/api/admin/plugin/route.js +7 -0
- package/.medusa/server/src/api/athos/feed/route.d.ts +2 -0
- package/.medusa/server/src/api/athos/feed/route.js +249 -0
- package/.medusa/server/src/api/athos/middlewares.d.ts +2 -0
- package/.medusa/server/src/api/athos/middlewares.js +26 -0
- package/.medusa/server/src/api/middlewares.d.ts +2 -0
- package/.medusa/server/src/api/middlewares.js +9 -0
- package/.medusa/server/src/api/store/plugin/route.d.ts +2 -0
- package/.medusa/server/src/api/store/plugin/route.js +7 -0
- package/.medusa/server/src/modules/athosConfig/index.d.ts +21 -0
- package/.medusa/server/src/modules/athosConfig/index.js +13 -0
- package/.medusa/server/src/modules/athosConfig/migrations/Migration20260518000000.d.ts +5 -0
- package/.medusa/server/src/modules/athosConfig/migrations/Migration20260518000000.js +26 -0
- package/.medusa/server/src/modules/athosConfig/migrations/Migration20260518000001.d.ts +5 -0
- package/.medusa/server/src/modules/athosConfig/migrations/Migration20260518000001.js +14 -0
- package/.medusa/server/src/modules/athosConfig/models/athos-config.d.ts +9 -0
- package/.medusa/server/src/modules/athosConfig/models/athos-config.js +13 -0
- package/.medusa/server/src/modules/athosConfig/service.d.ts +17 -0
- package/.medusa/server/src/modules/athosConfig/service.js +32 -0
- package/.medusa/server/src/modules/athosConfig/types.d.ts +15 -0
- package/.medusa/server/src/modules/athosConfig/types.js +3 -0
- package/.medusa/server/src/workflows/steps/upsert-athos-config-step.d.ts +9 -0
- package/.medusa/server/src/workflows/steps/upsert-athos-config-step.js +31 -0
- package/.medusa/server/src/workflows/upsert-athos-config.d.ts +9 -0
- package/.medusa/server/src/workflows/upsert-athos-config.js +10 -0
- package/README.md +198 -0
- package/package.json +73 -0
package/README.md
ADDED
|
@@ -0,0 +1,198 @@
|
|
|
1
|
+
# @weareseeed/medusa-athos-plugin
|
|
2
|
+
|
|
3
|
+
Medusa v2 plugin that generates a streaming [NDJSON](https://ndjson.org/) product feed for [Athos Commerce](https://athos.com) integration.
|
|
4
|
+
|
|
5
|
+
## Features
|
|
6
|
+
|
|
7
|
+
- Streaming NDJSON product feed (one line per variant + one product summary line)
|
|
8
|
+
- Token-based feed authentication
|
|
9
|
+
- Price filtering by region
|
|
10
|
+
- Product filtering by sales channel
|
|
11
|
+
- Configurable swatch options
|
|
12
|
+
- Extensible via `extraProductFields` and `transformLine` hooks
|
|
13
|
+
- Admin API to manage plugin configuration
|
|
14
|
+
|
|
15
|
+
## Requirements
|
|
16
|
+
|
|
17
|
+
- Medusa v2
|
|
18
|
+
- Node.js >= 20
|
|
19
|
+
|
|
20
|
+
## Installation
|
|
21
|
+
|
|
22
|
+
```bash
|
|
23
|
+
yarn add @weareseeed/medusa-athos-plugin
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
## Setup
|
|
27
|
+
|
|
28
|
+
### 1. Register the plugin
|
|
29
|
+
|
|
30
|
+
```ts
|
|
31
|
+
// medusa-config.ts
|
|
32
|
+
import { defineConfig } from "@medusajs/framework/config"
|
|
33
|
+
|
|
34
|
+
export default defineConfig({
|
|
35
|
+
plugins: [
|
|
36
|
+
{
|
|
37
|
+
resolve: "@weareseeed/medusa-athos-plugin",
|
|
38
|
+
options: {},
|
|
39
|
+
},
|
|
40
|
+
],
|
|
41
|
+
})
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
### 2. Run migrations
|
|
45
|
+
|
|
46
|
+
```bash
|
|
47
|
+
npx medusa db:migrate
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
### 3. Configure via Admin API
|
|
51
|
+
|
|
52
|
+
Set the plugin configuration through the authenticated admin endpoint:
|
|
53
|
+
|
|
54
|
+
```bash
|
|
55
|
+
curl -X POST http://localhost:9000/admin/athos/config \
|
|
56
|
+
-H "Authorization: Bearer <admin_token>" \
|
|
57
|
+
-H "Content-Type: application/json" \
|
|
58
|
+
-d '{
|
|
59
|
+
"storefront_url": "https://mystore.com",
|
|
60
|
+
"feed_token": "a-secret-token",
|
|
61
|
+
"region_id": "reg_01...",
|
|
62
|
+
"sales_channel_ids": ["sc_01..."],
|
|
63
|
+
"swatch_option_titles": ["color", "colour"]
|
|
64
|
+
}'
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
| Field | Type | Required | Description |
|
|
68
|
+
|---|---|---|---|
|
|
69
|
+
| `storefront_url` | `string` | Yes | Base URL prepended to `/products/<handle>` for each product URL |
|
|
70
|
+
| `feed_token` | `string` | Yes | Secret token required to access the feed endpoint |
|
|
71
|
+
| `region_id` | `string` | No | When set, prices are filtered to the region's currency |
|
|
72
|
+
| `sales_channel_ids` | `string[]` | No | When set, only products in these sales channels are included |
|
|
73
|
+
| `swatch_option_titles` | `string[]` | No | Option titles treated as swatches (default: `["color", "colour"]`) |
|
|
74
|
+
|
|
75
|
+
## Feed Endpoint
|
|
76
|
+
|
|
77
|
+
```
|
|
78
|
+
GET /athos/feed?token=<feed_token>
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
Returns an `application/x-ndjson` stream. Each product produces:
|
|
82
|
+
|
|
83
|
+
1. One **variant line** per variant
|
|
84
|
+
2. One **product summary line**
|
|
85
|
+
|
|
86
|
+
### Variant line fields
|
|
87
|
+
|
|
88
|
+
| Field | Description |
|
|
89
|
+
|---|---|
|
|
90
|
+
| `Product ID` | Variant ID |
|
|
91
|
+
| `SKU` | Variant SKU |
|
|
92
|
+
| `Name` | Variant title |
|
|
93
|
+
| `Product URL` | `<storefront_url>/products/<handle>` |
|
|
94
|
+
| `Price` | Lowest price for the variant (filtered by region if configured) |
|
|
95
|
+
| `Retail Price` | `compare_at_price` |
|
|
96
|
+
| `Thumbnail URL` | Product thumbnail |
|
|
97
|
+
| `Description` | Product description |
|
|
98
|
+
| `Category` | Array of category path strings (e.g. `"Parent>Child"`) |
|
|
99
|
+
| `Category ID` | Array of category IDs |
|
|
100
|
+
| `Search Keywords` | Comma-separated product tags |
|
|
101
|
+
| `__parent_id` | Parent product ID |
|
|
102
|
+
| `__parent_title` | Parent product title |
|
|
103
|
+
| `__parent_image` | Parent product thumbnail |
|
|
104
|
+
| `__variant_position` | 1-based position within the product |
|
|
105
|
+
| `__in_stock` | `true` if the variant is available |
|
|
106
|
+
| `__in_stock_pct` | % of variants in stock across the product |
|
|
107
|
+
| `__standard_options` | All product options with positions and values |
|
|
108
|
+
| `__selected_options` | This variant's chosen option values |
|
|
109
|
+
| `__swatch_options` | Values of configured swatch options (product-level) |
|
|
110
|
+
|
|
111
|
+
### Product summary line fields
|
|
112
|
+
|
|
113
|
+
Same as variant line minus all `__` private fields, with `Product ID` set to the product ID and `Price` set to the lowest price across all variants.
|
|
114
|
+
|
|
115
|
+
### Example
|
|
116
|
+
|
|
117
|
+
```json
|
|
118
|
+
{"Product ID":"variant_01...","SKU":"1","Name":"blue, m","Product URL":"https://mystore.com/products/example","Price":10,"Thumbnail URL":"https://...","Description":"...","Category":["Tops"],"Category ID":["pcat_01..."],"__parent_id":"prod_01...","__parent_title":"Example Product","__parent_image":"https://...","__variant_position":1,"__in_stock":true,"__in_stock_pct":100,"__standard_options":{"color":{"position":0,"values":["red","blue"]},"size":{"position":1,"values":["s","m"]}},"__selected_options":{"color":{"value":"blue"},"size":{"value":"m"}},"__swatch_options":{"red":{"value":"red"},"blue":{"value":"blue"}}}
|
|
119
|
+
{"Product ID":"prod_01...","Name":"Example Product","Product URL":"https://mystore.com/products/example","Price":10,"Thumbnail URL":"https://...","Description":"...","Category":["Tops"],"Category ID":["pcat_01..."]}
|
|
120
|
+
```
|
|
121
|
+
|
|
122
|
+
## Plugin Options
|
|
123
|
+
|
|
124
|
+
Pass options when registering the plugin in `medusa-config.ts` to extend the feed.
|
|
125
|
+
|
|
126
|
+
```ts
|
|
127
|
+
import { defineConfig } from "@medusajs/framework/config"
|
|
128
|
+
import type { FeedLineContext } from "@weareseeed/medusa-athos-plugin/types"
|
|
129
|
+
|
|
130
|
+
export default defineConfig({
|
|
131
|
+
plugins: [
|
|
132
|
+
{
|
|
133
|
+
resolve: "@weareseeed/medusa-athos-plugin",
|
|
134
|
+
options: {
|
|
135
|
+
feed: {
|
|
136
|
+
extraProductFields: ["metadata", "variants.metadata"],
|
|
137
|
+
transformLine: (line, ctx: FeedLineContext) => {
|
|
138
|
+
line["Brand"] = (ctx.product as any).metadata?.brand ?? undefined
|
|
139
|
+
|
|
140
|
+
if (ctx.type === "variant") {
|
|
141
|
+
line["Custom Variant Field"] = (ctx.variant as any).metadata?.custom ?? undefined
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
if (ctx.type === "product") {
|
|
145
|
+
line["Custom Product Field"] = (ctx.product as any).metadata?.custom ?? undefined
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
return line
|
|
149
|
+
},
|
|
150
|
+
},
|
|
151
|
+
},
|
|
152
|
+
},
|
|
153
|
+
],
|
|
154
|
+
})
|
|
155
|
+
```
|
|
156
|
+
|
|
157
|
+
### `feed.extraProductFields`
|
|
158
|
+
|
|
159
|
+
`string[]` — Additional Medusa product fields to fetch and make available inside `transformLine`. Uses the same dot-notation field paths as Medusa's `query.graph`.
|
|
160
|
+
|
|
161
|
+
```ts
|
|
162
|
+
extraProductFields: [
|
|
163
|
+
"metadata",
|
|
164
|
+
"variants.metadata",
|
|
165
|
+
"images.url",
|
|
166
|
+
"collection.title",
|
|
167
|
+
]
|
|
168
|
+
```
|
|
169
|
+
|
|
170
|
+
### `feed.transformLine`
|
|
171
|
+
|
|
172
|
+
```ts
|
|
173
|
+
(line: Record<string, unknown>, ctx: FeedLineContext) =>
|
|
174
|
+
Record<string, unknown> | Promise<Record<string, unknown>>
|
|
175
|
+
```
|
|
176
|
+
|
|
177
|
+
Called for every line written to the feed. Use it to add, remove, or transform fields. The `ctx` argument provides typed access to the raw product and variant data:
|
|
178
|
+
|
|
179
|
+
```ts
|
|
180
|
+
type FeedLineContext =
|
|
181
|
+
| { type: "variant"; product: Record<string, unknown>; variant: Record<string, unknown>; variantIndex: number }
|
|
182
|
+
| { type: "product"; product: Record<string, unknown> }
|
|
183
|
+
```
|
|
184
|
+
|
|
185
|
+
> Fields that resolve to `undefined` are automatically stripped from the output.
|
|
186
|
+
|
|
187
|
+
## Admin API
|
|
188
|
+
|
|
189
|
+
Both endpoints require an authenticated admin JWT.
|
|
190
|
+
|
|
191
|
+
| Method | Path | Description |
|
|
192
|
+
|---|---|---|
|
|
193
|
+
| `GET` | `/admin/athos/config` | Retrieve the current configuration |
|
|
194
|
+
| `POST` | `/admin/athos/config` | Create or update the configuration |
|
|
195
|
+
|
|
196
|
+
## License
|
|
197
|
+
|
|
198
|
+
MIT — [Seeed](https://seeed.us/)
|
package/package.json
ADDED
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@weareseeed/medusa-athos-plugin",
|
|
3
|
+
"version": "0.0.1",
|
|
4
|
+
"description": "Medusa Athos Integration Plugin.",
|
|
5
|
+
"author": "Seeed (https://seeed.us/)",
|
|
6
|
+
"license": "MIT",
|
|
7
|
+
"files": [
|
|
8
|
+
".medusa/server"
|
|
9
|
+
],
|
|
10
|
+
"exports": {
|
|
11
|
+
"./package.json": "./package.json",
|
|
12
|
+
"./types": {
|
|
13
|
+
"types": "./.medusa/server/src/modules/athosConfig/types.d.ts",
|
|
14
|
+
"default": "./.medusa/server/src/modules/athosConfig/types.js"
|
|
15
|
+
},
|
|
16
|
+
"./workflows": "./.medusa/server/src/workflows/index.js",
|
|
17
|
+
"./.medusa/server/src/modules/*": "./.medusa/server/src/modules/*/index.js",
|
|
18
|
+
"./modules/*": "./.medusa/server/src/modules/*/index.js",
|
|
19
|
+
"./providers/*": "./.medusa/server/src/providers/*/index.js",
|
|
20
|
+
"./*": "./.medusa/server/src/*.js",
|
|
21
|
+
"./admin": {
|
|
22
|
+
"import": "./.medusa/server/src/admin/index.mjs",
|
|
23
|
+
"require": "./.medusa/server/src/admin/index.js",
|
|
24
|
+
"default": "./.medusa/server/src/admin/index.js"
|
|
25
|
+
}
|
|
26
|
+
},
|
|
27
|
+
"keywords": [
|
|
28
|
+
"medusa",
|
|
29
|
+
"plugin",
|
|
30
|
+
"medusa-plugin",
|
|
31
|
+
"medusa-plugin-integration",
|
|
32
|
+
"medusa-v2",
|
|
33
|
+
"medusa-plugin-search"
|
|
34
|
+
],
|
|
35
|
+
"scripts": {
|
|
36
|
+
"build": "medusa plugin:build && tsc --emitDeclarationOnly --noEmit false",
|
|
37
|
+
"dev": "medusa plugin:develop",
|
|
38
|
+
"prepublishOnly": "medusa plugin:build"
|
|
39
|
+
},
|
|
40
|
+
"devDependencies": {
|
|
41
|
+
"@medusajs/admin-sdk": "2.14.2",
|
|
42
|
+
"@medusajs/cli": "2.14.2",
|
|
43
|
+
"@medusajs/framework": "2.14.2",
|
|
44
|
+
"@medusajs/icons": "2.14.2",
|
|
45
|
+
"@medusajs/medusa": "2.14.2",
|
|
46
|
+
"@medusajs/test-utils": "2.14.2",
|
|
47
|
+
"@medusajs/ui": "4.1.9",
|
|
48
|
+
"@swc/core": "^1.7.28",
|
|
49
|
+
"@types/node": "^20.0.0",
|
|
50
|
+
"@types/react": "^18.3.2",
|
|
51
|
+
"@types/react-dom": "^18.2.25",
|
|
52
|
+
"prop-types": "^15.8.1",
|
|
53
|
+
"react": "^18.2.0",
|
|
54
|
+
"react-dom": "^18.2.0",
|
|
55
|
+
"ts-node": "^10.9.2",
|
|
56
|
+
"typescript": "^5.6.2",
|
|
57
|
+
"vite": "^5.2.11",
|
|
58
|
+
"yalc": "^1.0.0-pre.53"
|
|
59
|
+
},
|
|
60
|
+
"peerDependencies": {
|
|
61
|
+
"@medusajs/admin-sdk": "2.14.2",
|
|
62
|
+
"@medusajs/cli": "2.14.2",
|
|
63
|
+
"@medusajs/framework": "2.14.2",
|
|
64
|
+
"@medusajs/icons": "2.14.2",
|
|
65
|
+
"@medusajs/medusa": "2.14.2",
|
|
66
|
+
"@medusajs/test-utils": "2.14.2",
|
|
67
|
+
"@medusajs/ui": "4.1.9"
|
|
68
|
+
},
|
|
69
|
+
"engines": {
|
|
70
|
+
"node": ">=20"
|
|
71
|
+
},
|
|
72
|
+
"packageManager": "yarn@4.9.2"
|
|
73
|
+
}
|