@csbeker/medusa-product-attributes 2.2.0

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.
Files changed (237) hide show
  1. package/.medusa/server/src/admin/index.js +9012 -0
  2. package/.medusa/server/src/admin/index.mjs +9010 -0
  3. package/.medusa/server/src/api/admin/middlewares.js +10 -0
  4. package/.medusa/server/src/api/admin/plugin/attribute-set/[id]/attributes/route.js +17 -0
  5. package/.medusa/server/src/api/admin/plugin/attribute-set/[id]/route.js +33 -0
  6. package/.medusa/server/src/api/admin/plugin/attribute-set/middlewares.js +44 -0
  7. package/.medusa/server/src/api/admin/plugin/attribute-set/query-config.js +22 -0
  8. package/.medusa/server/src/api/admin/plugin/attribute-set/route.js +25 -0
  9. package/.medusa/server/src/api/admin/plugin/attribute-set/validators.js +37 -0
  10. package/.medusa/server/src/api/admin/plugin/attributes/[id]/route.js +68 -0
  11. package/.medusa/server/src/api/admin/plugin/attributes/[id]/values/[valueId]/route.js +37 -0
  12. package/.medusa/server/src/api/admin/plugin/attributes/[id]/values/route.js +31 -0
  13. package/.medusa/server/src/api/admin/plugin/attributes/middlewares.js +103 -0
  14. package/.medusa/server/src/api/admin/plugin/attributes/query-config.js +41 -0
  15. package/.medusa/server/src/api/admin/plugin/attributes/route.js +28 -0
  16. package/.medusa/server/src/api/admin/plugin/attributes/validators.js +69 -0
  17. package/.medusa/server/src/api/admin/plugin/route.js +7 -0
  18. package/.medusa/server/src/api/middlewares.js +12 -0
  19. package/.medusa/server/src/api/store/middlewares.js +8 -0
  20. package/.medusa/server/src/api/store/plugin/attributes/middlewares.js +64 -0
  21. package/.medusa/server/src/api/store/plugin/attributes/products/middlewares.js +100 -0
  22. package/.medusa/server/src/api/store/plugin/attributes/products/query-config.js +20 -0
  23. package/.medusa/server/src/api/store/plugin/attributes/products/route.js +48 -0
  24. package/.medusa/server/src/api/store/plugin/attributes/products/validators.js +39 -0
  25. package/.medusa/server/src/api/store/plugin/attributes/query-config.js +21 -0
  26. package/.medusa/server/src/api/store/plugin/attributes/route.js +15 -0
  27. package/.medusa/server/src/api/store/plugin/attributes/validators.js +14 -0
  28. package/.medusa/server/src/api/store/plugin/route.js +7 -0
  29. package/.medusa/server/src/api/utils/common-validators.js +23 -0
  30. package/.medusa/server/src/api/utils/constants.js +6 -0
  31. package/.medusa/server/src/api/utils/middlewares.js +34 -0
  32. package/.medusa/server/src/links/attribute-product-category.js +16 -0
  33. package/.medusa/server/src/links/attribute-value-product.js +16 -0
  34. package/.medusa/server/src/modules/attribute/events/index.js +8 -0
  35. package/.medusa/server/src/modules/attribute/index.js +13 -0
  36. package/.medusa/server/src/modules/attribute/migrations/Migration20250319161229.js +24 -0
  37. package/.medusa/server/src/modules/attribute/migrations/Migration20250320182643.js +16 -0
  38. package/.medusa/server/src/modules/attribute/migrations/Migration20250321162638.js +14 -0
  39. package/.medusa/server/src/modules/attribute/migrations/Migration20250505144933.js +23 -0
  40. package/.medusa/server/src/modules/attribute/migrations/Migration20250505201747.js +21 -0
  41. package/.medusa/server/src/modules/attribute/migrations/Migration20250506162300.js +14 -0
  42. package/.medusa/server/src/modules/attribute/migrations/Migration20250611160552.js +14 -0
  43. package/.medusa/server/src/modules/attribute/migrations/Migration20250611173345.js +16 -0
  44. package/.medusa/server/src/modules/attribute/migrations/Migration20250612192857.js +16 -0
  45. package/.medusa/server/src/modules/attribute/models/attribute-possible-value.js +24 -0
  46. package/.medusa/server/src/modules/attribute/models/attribute-set.js +22 -0
  47. package/.medusa/server/src/modules/attribute/models/attribute-value.js +17 -0
  48. package/.medusa/server/src/modules/attribute/models/attribute.js +27 -0
  49. package/.medusa/server/src/modules/attribute/service.js +84 -0
  50. package/.medusa/server/src/modules/attribute/types/attribute/common.js +13 -0
  51. package/.medusa/server/src/modules/attribute/types/attribute/index.js +18 -0
  52. package/.medusa/server/src/modules/attribute/types/attribute-set/index.js +18 -0
  53. package/.medusa/server/src/modules/attribute/types/attribute-set/mutations.js +3 -0
  54. package/.medusa/server/src/modules/attribute/types/attribute-value/index.js +18 -0
  55. package/.medusa/server/src/modules/attribute/types/attribute-value/mutations.js +3 -0
  56. package/.medusa/server/src/modules/attribute/types/index.js +20 -0
  57. package/.medusa/server/src/types/attribute/common.js +3 -0
  58. package/.medusa/server/src/types/attribute/http/attribute/admin/index.js +3 -0
  59. package/.medusa/server/src/types/attribute/http/attribute/index.js +18 -0
  60. package/.medusa/server/src/types/attribute/http/attribute-set/index.js +3 -0
  61. package/.medusa/server/src/types/attribute/http/index.js +19 -0
  62. package/.medusa/server/src/types/attribute/index.js +19 -0
  63. package/.medusa/server/src/utils/index.js +18 -0
  64. package/.medusa/server/src/utils/products-created-handler.js +22 -0
  65. package/.medusa/server/src/utils/products-updated-handler.js +58 -0
  66. package/.medusa/server/src/utils/validate-attribute-values-to-link.js +43 -0
  67. package/.medusa/server/src/workflows/attribute/index.js +19 -0
  68. package/.medusa/server/src/workflows/attribute/steps/create-attribute-possible-values.js +18 -0
  69. package/.medusa/server/src/workflows/attribute/steps/create-attributes.js +27 -0
  70. package/.medusa/server/src/workflows/attribute/steps/delete-attribute.js +31 -0
  71. package/.medusa/server/src/workflows/attribute/steps/index.js +21 -0
  72. package/.medusa/server/src/workflows/attribute/steps/update-attributes.js +34 -0
  73. package/.medusa/server/src/workflows/attribute/workflows/create-attribute-possible-values.js +11 -0
  74. package/.medusa/server/src/workflows/attribute/workflows/create-attributes.js +46 -0
  75. package/.medusa/server/src/workflows/attribute/workflows/delete-attribute.js +10 -0
  76. package/.medusa/server/src/workflows/attribute/workflows/index.js +20 -0
  77. package/.medusa/server/src/workflows/attribute/workflows/update-attributes.js +73 -0
  78. package/.medusa/server/src/workflows/attribute-set/steps/batch-link-attribute-set-attributes.js +66 -0
  79. package/.medusa/server/src/workflows/attribute-set/steps/create-attribute-set.js +24 -0
  80. package/.medusa/server/src/workflows/attribute-set/steps/index.js +20 -0
  81. package/.medusa/server/src/workflows/attribute-set/steps/update-attribute-set.js +22 -0
  82. package/.medusa/server/src/workflows/attribute-set/workflows/batch-link-attribute-set-attributes.js +10 -0
  83. package/.medusa/server/src/workflows/attribute-set/workflows/create-attribute-set.js +11 -0
  84. package/.medusa/server/src/workflows/attribute-set/workflows/index.js +20 -0
  85. package/.medusa/server/src/workflows/attribute-set/workflows/update-attribute-set.js +10 -0
  86. package/.medusa/server/src/workflows/attribute-value/steps/create-attribute-value.js +18 -0
  87. package/.medusa/server/src/workflows/attribute-value/steps/delete-attribute-value.js +18 -0
  88. package/.medusa/server/src/workflows/attribute-value/steps/index.js +20 -0
  89. package/.medusa/server/src/workflows/attribute-value/steps/validate-attribute-value.js +53 -0
  90. package/.medusa/server/src/workflows/attribute-value/workflow/create-attribute-value.js +28 -0
  91. package/.medusa/server/src/workflows/attribute-value/workflow/delete-attribute-value.js +38 -0
  92. package/.medusa/server/src/workflows/attribute-value/workflow/index.js +19 -0
  93. package/.medusa/server/src/workflows/attribute_possible_value/index.js +19 -0
  94. package/.medusa/server/src/workflows/attribute_possible_value/steps/index.js +18 -0
  95. package/.medusa/server/src/workflows/attribute_possible_value/steps/update-attribute-possible-value.js +18 -0
  96. package/.medusa/server/src/workflows/attribute_possible_value/workflows/index.js +18 -0
  97. package/.medusa/server/src/workflows/attribute_possible_value/workflows/update-attribute-possible-value.js +11 -0
  98. package/.medusa/server/src/workflows/index.js +18 -0
  99. package/CHANGELOG.md +104 -0
  100. package/README.md +86 -0
  101. package/package.json +90 -0
  102. package/src/admin/README.md +31 -0
  103. package/src/admin/components/metadata-editor/index.tsx +101 -0
  104. package/src/admin/components/section-row.tsx +41 -0
  105. package/src/admin/hooks/api/attribute-set.ts +122 -0
  106. package/src/admin/hooks/api/attributes.ts +126 -0
  107. package/src/admin/hooks/table/columns/index.ts +1 -0
  108. package/src/admin/hooks/table/columns/use-attribute-table-columns.tsx +280 -0
  109. package/src/admin/layouts/single-column.tsx +11 -0
  110. package/src/admin/lib/config.ts +8 -0
  111. package/src/admin/lib/query-key-factory.ts +53 -0
  112. package/src/admin/routes/attributes/[id]/edit/page.tsx +133 -0
  113. package/src/admin/routes/attributes/[id]/edit-possible-value/page.tsx +174 -0
  114. package/src/admin/routes/attributes/[id]/page.tsx +127 -0
  115. package/src/admin/routes/attributes/components/AttributeForm.tsx +301 -0
  116. package/src/admin/routes/attributes/components/AttributeSetTable.tsx +108 -0
  117. package/src/admin/routes/attributes/components/category-selection-modal.tsx +82 -0
  118. package/src/admin/routes/attributes/components/possible-values-table.tsx +119 -0
  119. package/src/admin/routes/attributes/create/components/MultiSelectCategory.tsx +148 -0
  120. package/src/admin/routes/attributes/create/components/PossibleValuesList.tsx +151 -0
  121. package/src/admin/routes/attributes/create/page.tsx +123 -0
  122. package/src/admin/routes/attributes/create-set/page.tsx +110 -0
  123. package/src/admin/routes/attributes/page.tsx +346 -0
  124. package/src/admin/routes/attributes/set/[id]/attributes/page.tsx +35 -0
  125. package/src/admin/routes/attributes/set/[id]/components/AttributeSetAttributesSection.tsx +114 -0
  126. package/src/admin/routes/attributes/set/[id]/components/AttributeSetGeneralSection.tsx +42 -0
  127. package/src/admin/routes/attributes/set/[id]/components/attribute-set-attributes-form.tsx +143 -0
  128. package/src/admin/routes/attributes/set/[id]/components/index.ts +2 -0
  129. package/src/admin/routes/attributes/set/[id]/edit/page.tsx +119 -0
  130. package/src/admin/routes/attributes/set/[id]/page.tsx +45 -0
  131. package/src/admin/tsconfig.json +27 -0
  132. package/src/admin/types/global.d.ts +3 -0
  133. package/src/admin/vite-env.d.ts +1 -0
  134. package/src/api/README.md +133 -0
  135. package/src/api/admin/middlewares.ts +8 -0
  136. package/src/api/admin/plugin/attribute-set/[id]/attributes/route.ts +17 -0
  137. package/src/api/admin/plugin/attribute-set/[id]/route.ts +41 -0
  138. package/src/api/admin/plugin/attribute-set/middlewares.ts +42 -0
  139. package/src/api/admin/plugin/attribute-set/query-config.ts +20 -0
  140. package/src/api/admin/plugin/attribute-set/route.ts +34 -0
  141. package/src/api/admin/plugin/attribute-set/validators.ts +45 -0
  142. package/src/api/admin/plugin/attributes/[id]/route.ts +85 -0
  143. package/src/api/admin/plugin/attributes/[id]/values/[valueId]/route.ts +41 -0
  144. package/src/api/admin/plugin/attributes/[id]/values/route.ts +39 -0
  145. package/src/api/admin/plugin/attributes/middlewares.ts +91 -0
  146. package/src/api/admin/plugin/attributes/query-config.ts +42 -0
  147. package/src/api/admin/plugin/attributes/route.ts +33 -0
  148. package/src/api/admin/plugin/attributes/validators.ts +91 -0
  149. package/src/api/admin/plugin/route.ts +8 -0
  150. package/src/api/middlewares.ts +10 -0
  151. package/src/api/store/middlewares.ts +6 -0
  152. package/src/api/store/plugin/attributes/middlewares.ts +33 -0
  153. package/src/api/store/plugin/attributes/products/middlewares.ts +73 -0
  154. package/src/api/store/plugin/attributes/products/query-config.ts +19 -0
  155. package/src/api/store/plugin/attributes/products/route.ts +68 -0
  156. package/src/api/store/plugin/attributes/products/validators.ts +55 -0
  157. package/src/api/store/plugin/attributes/query-config.ts +19 -0
  158. package/src/api/store/plugin/attributes/route.ts +13 -0
  159. package/src/api/store/plugin/attributes/validators.ts +14 -0
  160. package/src/api/store/plugin/route.ts +8 -0
  161. package/src/api/utils/common-validators.ts +24 -0
  162. package/src/api/utils/constants.ts +2 -0
  163. package/src/api/utils/middlewares.ts +31 -0
  164. package/src/jobs/README.md +36 -0
  165. package/src/links/README.md +26 -0
  166. package/src/links/attribute-product-category.ts +14 -0
  167. package/src/links/attribute-value-product.ts +14 -0
  168. package/src/modules/README.md +116 -0
  169. package/src/modules/attribute/events/index.ts +4 -0
  170. package/src/modules/attribute/index.ts +8 -0
  171. package/src/modules/attribute/migrations/.snapshot-medusa-attribute.json +624 -0
  172. package/src/modules/attribute/migrations/Migration20250319161229.ts +27 -0
  173. package/src/modules/attribute/migrations/Migration20250320182643.ts +15 -0
  174. package/src/modules/attribute/migrations/Migration20250321162638.ts +13 -0
  175. package/src/modules/attribute/migrations/Migration20250505144933.ts +26 -0
  176. package/src/modules/attribute/migrations/Migration20250505201747.ts +23 -0
  177. package/src/modules/attribute/migrations/Migration20250506162300.ts +13 -0
  178. package/src/modules/attribute/migrations/Migration20250611160552.ts +13 -0
  179. package/src/modules/attribute/migrations/Migration20250611173345.ts +17 -0
  180. package/src/modules/attribute/migrations/Migration20250612192857.ts +17 -0
  181. package/src/modules/attribute/models/attribute-possible-value.ts +20 -0
  182. package/src/modules/attribute/models/attribute-set.ts +18 -0
  183. package/src/modules/attribute/models/attribute-value.ts +13 -0
  184. package/src/modules/attribute/models/attribute.ts +23 -0
  185. package/src/modules/attribute/service.ts +102 -0
  186. package/src/modules/attribute/types/attribute/common.ts +94 -0
  187. package/src/modules/attribute/types/attribute/index.ts +1 -0
  188. package/src/modules/attribute/types/attribute-set/index.ts +1 -0
  189. package/src/modules/attribute/types/attribute-set/mutations.ts +7 -0
  190. package/src/modules/attribute/types/attribute-value/index.ts +1 -0
  191. package/src/modules/attribute/types/attribute-value/mutations.ts +5 -0
  192. package/src/modules/attribute/types/index.ts +3 -0
  193. package/src/providers/README.md +30 -0
  194. package/src/subscribers/README.md +59 -0
  195. package/src/types/attribute/common.ts +173 -0
  196. package/src/types/attribute/http/attribute/admin/index.ts +0 -0
  197. package/src/types/attribute/http/attribute/index.ts +42 -0
  198. package/src/types/attribute/http/attribute-set/index.ts +10 -0
  199. package/src/types/attribute/http/index.ts +2 -0
  200. package/src/types/attribute/index.ts +2 -0
  201. package/src/utils/index.ts +1 -0
  202. package/src/utils/products-created-handler.ts +35 -0
  203. package/src/utils/products-updated-handler.ts +74 -0
  204. package/src/utils/validate-attribute-values-to-link.ts +67 -0
  205. package/src/workflows/README.md +79 -0
  206. package/src/workflows/attribute/index.ts +2 -0
  207. package/src/workflows/attribute/steps/create-attribute-possible-values.ts +29 -0
  208. package/src/workflows/attribute/steps/create-attributes.ts +35 -0
  209. package/src/workflows/attribute/steps/delete-attribute.ts +41 -0
  210. package/src/workflows/attribute/steps/index.ts +4 -0
  211. package/src/workflows/attribute/steps/update-attributes.ts +45 -0
  212. package/src/workflows/attribute/workflows/create-attribute-possible-values.ts +17 -0
  213. package/src/workflows/attribute/workflows/create-attributes.ts +56 -0
  214. package/src/workflows/attribute/workflows/delete-attribute.ts +15 -0
  215. package/src/workflows/attribute/workflows/index.ts +3 -0
  216. package/src/workflows/attribute/workflows/update-attributes.ts +103 -0
  217. package/src/workflows/attribute-set/steps/batch-link-attribute-set-attributes.ts +82 -0
  218. package/src/workflows/attribute-set/steps/create-attribute-set.ts +34 -0
  219. package/src/workflows/attribute-set/steps/index.ts +3 -0
  220. package/src/workflows/attribute-set/steps/update-attribute-set.ts +32 -0
  221. package/src/workflows/attribute-set/workflows/batch-link-attribute-set-attributes.ts +12 -0
  222. package/src/workflows/attribute-set/workflows/create-attribute-set.ts +17 -0
  223. package/src/workflows/attribute-set/workflows/index.ts +3 -0
  224. package/src/workflows/attribute-set/workflows/update-attribute-set.ts +14 -0
  225. package/src/workflows/attribute-value/steps/create-attribute-value.ts +26 -0
  226. package/src/workflows/attribute-value/steps/delete-attribute-value.ts +26 -0
  227. package/src/workflows/attribute-value/steps/index.ts +3 -0
  228. package/src/workflows/attribute-value/steps/validate-attribute-value.ts +95 -0
  229. package/src/workflows/attribute-value/workflow/create-attribute-value.ts +36 -0
  230. package/src/workflows/attribute-value/workflow/delete-attribute-value.ts +46 -0
  231. package/src/workflows/attribute-value/workflow/index.ts +2 -0
  232. package/src/workflows/attribute_possible_value/index.ts +2 -0
  233. package/src/workflows/attribute_possible_value/steps/index.ts +1 -0
  234. package/src/workflows/attribute_possible_value/steps/update-attribute-possible-value.ts +25 -0
  235. package/src/workflows/attribute_possible_value/workflows/index.ts +1 -0
  236. package/src/workflows/attribute_possible_value/workflows/update-attribute-possible-value.ts +15 -0
  237. package/src/workflows/index.ts +1 -0
@@ -0,0 +1,26 @@
1
+ import { Migration } from '@mikro-orm/migrations';
2
+
3
+ export class Migration20250505144933 extends Migration {
4
+
5
+ override async up(): Promise<void> {
6
+ this.addSql(`alter table if exists "attribute_set" drop constraint if exists "attribute_set_handle_unique";`);
7
+ this.addSql(`create table if not exists "attribute_set" ("id" text not null, "name" text not null, "description" text null, "handle" text not null, "metadata" jsonb null, "created_at" timestamptz not null default now(), "updated_at" timestamptz not null default now(), "deleted_at" timestamptz null, constraint "attribute_set_pkey" primary key ("id"));`);
8
+ this.addSql(`CREATE INDEX IF NOT EXISTS "IDX_ATTRIBUTE_SET_NAME" ON "attribute_set" (name) WHERE deleted_at IS NULL;`);
9
+ this.addSql(`CREATE UNIQUE INDEX IF NOT EXISTS "IDX_attribute_set_handle_unique" ON "attribute_set" (handle) WHERE deleted_at IS NULL;`);
10
+ this.addSql(`CREATE INDEX IF NOT EXISTS "IDX_attribute_set_deleted_at" ON "attribute_set" (deleted_at) WHERE deleted_at IS NULL;`);
11
+
12
+ this.addSql(`create table if not exists "attribute_set_attributes" ("attribute_set_id" text not null, "attribute_id" text not null, constraint "attribute_set_attributes_pkey" primary key ("attribute_set_id", "attribute_id"));`);
13
+
14
+ this.addSql(`alter table if exists "attribute_set_attributes" add constraint "attribute_set_attributes_attribute_set_id_foreign" foreign key ("attribute_set_id") references "attribute_set" ("id") on update cascade on delete cascade;`);
15
+ this.addSql(`alter table if exists "attribute_set_attributes" add constraint "attribute_set_attributes_attribute_id_foreign" foreign key ("attribute_id") references "attribute" ("id") on update cascade on delete cascade;`);
16
+ }
17
+
18
+ override async down(): Promise<void> {
19
+ this.addSql(`alter table if exists "attribute_set_attributes" drop constraint if exists "attribute_set_attributes_attribute_set_id_foreign";`);
20
+
21
+ this.addSql(`drop table if exists "attribute_set" cascade;`);
22
+
23
+ this.addSql(`drop table if exists "attribute_set_attributes" cascade;`);
24
+ }
25
+
26
+ }
@@ -0,0 +1,23 @@
1
+ import { Migration } from '@mikro-orm/migrations';
2
+
3
+ export class Migration20250505201747 extends Migration {
4
+
5
+ override async up(): Promise<void> {
6
+ this.addSql(`create table if not exists "attribute_possible_value" ("id" text not null, "value" text not null, "rank" integer not null, "metadata" jsonb null, "attribute_id" text not null, "created_at" timestamptz not null default now(), "updated_at" timestamptz not null default now(), "deleted_at" timestamptz null, constraint "attribute_possible_value_pkey" primary key ("id"));`);
7
+ this.addSql(`CREATE INDEX IF NOT EXISTS "IDX_attribute_possible_value_attribute_id" ON "attribute_possible_value" (attribute_id) WHERE deleted_at IS NULL;`);
8
+ this.addSql(`CREATE INDEX IF NOT EXISTS "IDX_attribute_possible_value_deleted_at" ON "attribute_possible_value" (deleted_at) WHERE deleted_at IS NULL;`);
9
+ this.addSql(`CREATE UNIQUE INDEX IF NOT EXISTS "UQ_attribute_id_value" ON "attribute_possible_value" (attribute_id, value) WHERE deleted_at IS NULL;`);
10
+
11
+ this.addSql(`alter table if exists "attribute_possible_value" drop constraint if exists "attribute_possible_value_attribute_id_foreign";`);
12
+ this.addSql(`alter table if exists "attribute_possible_value" add constraint "attribute_possible_value_attribute_id_foreign" foreign key ("attribute_id") references "attribute" ("id") on update cascade;`);
13
+
14
+ this.addSql(`drop index if exists "UQ_attribute_id_value";`);
15
+ }
16
+
17
+ override async down(): Promise<void> {
18
+ this.addSql(`drop table if exists "attribute_possible_value" cascade;`);
19
+
20
+ this.addSql(`CREATE UNIQUE INDEX IF NOT EXISTS "UQ_attribute_id_value" ON "attribute_value" (attribute_id, value) WHERE deleted_at IS NULL;`);
21
+ }
22
+
23
+ }
@@ -0,0 +1,13 @@
1
+ import { Migration } from '@mikro-orm/migrations';
2
+
3
+ export class Migration20250506162300 extends Migration {
4
+
5
+ override async up(): Promise<void> {
6
+ this.addSql(`alter table if exists "attribute_value" drop column if exists "rank";`);
7
+ }
8
+
9
+ override async down(): Promise<void> {
10
+ this.addSql(`alter table if exists "attribute_value" add column if not exists "rank" integer not null;`);
11
+ }
12
+
13
+ }
@@ -0,0 +1,13 @@
1
+ import { Migration } from '@mikro-orm/migrations';
2
+
3
+ export class Migration20250611160552 extends Migration {
4
+
5
+ override async up(): Promise<void> {
6
+ this.addSql(`alter table if exists "attribute" add column if not exists "ui_component" text check ("ui_component" in ('select', 'multivalue', 'unit', 'toggle', 'text-area', 'color_picker')) not null default 'select';`);
7
+ }
8
+
9
+ override async down(): Promise<void> {
10
+ this.addSql(`alter table if exists "attribute" drop column if exists "ui_component";`);
11
+ }
12
+
13
+ }
@@ -0,0 +1,17 @@
1
+ import { Migration } from '@mikro-orm/migrations';
2
+
3
+ export class Migration20250611173345 extends Migration {
4
+
5
+ override async up(): Promise<void> {
6
+ this.addSql(`alter table if exists "attribute" drop constraint if exists "attribute_ui_component_check";`);
7
+
8
+ this.addSql(`alter table if exists "attribute" add constraint "attribute_ui_component_check" check("ui_component" in ('select', 'multivalue', 'unit', 'toggle', 'text_area', 'color_picker'));`);
9
+ }
10
+
11
+ override async down(): Promise<void> {
12
+ this.addSql(`alter table if exists "attribute" drop constraint if exists "attribute_ui_component_check";`);
13
+
14
+ this.addSql(`alter table if exists "attribute" add constraint "attribute_ui_component_check" check("ui_component" in ('select', 'multivalue', 'unit', 'toggle', 'text-area', 'color_picker'));`);
15
+ }
16
+
17
+ }
@@ -0,0 +1,17 @@
1
+ import { Migration } from '@mikro-orm/migrations';
2
+
3
+ export class Migration20250612192857 extends Migration {
4
+
5
+ override async up(): Promise<void> {
6
+ this.addSql(`alter table if exists "attribute_possible_value" drop constraint if exists "attribute_possible_value_attribute_id_foreign";`);
7
+
8
+ this.addSql(`alter table if exists "attribute_possible_value" add constraint "attribute_possible_value_attribute_id_foreign" foreign key ("attribute_id") references "attribute" ("id") on update cascade on delete cascade;`);
9
+ }
10
+
11
+ override async down(): Promise<void> {
12
+ this.addSql(`alter table if exists "attribute_possible_value" drop constraint if exists "attribute_possible_value_attribute_id_foreign";`);
13
+
14
+ this.addSql(`alter table if exists "attribute_possible_value" add constraint "attribute_possible_value_attribute_id_foreign" foreign key ("attribute_id") references "attribute" ("id") on update cascade;`);
15
+ }
16
+
17
+ }
@@ -0,0 +1,20 @@
1
+ import Attribute from "./attribute";
2
+ import { model } from "@medusajs/framework/utils";
3
+
4
+ const AttributePossibleValue = model.define('attribute_possible_value', {
5
+ id: model.id({ prefix: 'attrposval' }).primaryKey(),
6
+ value: model.text(),
7
+ rank: model.number(),
8
+ metadata: model.json().nullable(),
9
+ attribute: model.belongsTo(() => Attribute, {
10
+ mappedBy: 'possible_values',
11
+ })
12
+ }).indexes([
13
+ {
14
+ on: ['attribute_id', 'value'],
15
+ name: 'UQ_attribute_id_value',
16
+ unique: true,
17
+ }
18
+ ])
19
+
20
+ export default AttributePossibleValue
@@ -0,0 +1,18 @@
1
+ import Attribute from "./attribute"
2
+ import { model } from "@medusajs/framework/utils"
3
+
4
+ const AttributeSet = model.define('attribute_set', {
5
+ id: model.id({ prefix: 'attrset' }).primaryKey(),
6
+ name: model.text().index('IDX_ATTRIBUTE_SET_NAME').searchable(),
7
+ description: model.text().nullable(),
8
+ handle: model.text().unique(),
9
+ metadata: model.json().nullable(),
10
+ attributes: model.manyToMany(() => Attribute, {
11
+ mappedBy: 'sets',
12
+ pivotTable: 'attribute_set_attributes',
13
+ join_column: 'attribute_set_id',
14
+ inverseJoinColumn: 'attribute_id',
15
+ }),
16
+ })
17
+
18
+ export default AttributeSet
@@ -0,0 +1,13 @@
1
+ import Attribute from "./attribute";
2
+ import { model } from "@medusajs/framework/utils";
3
+
4
+ const AttributeValue = model.define('attribute_value', {
5
+ id: model.id({ prefix: 'attrval' }).primaryKey(),
6
+ value: model.text(),
7
+ metadata: model.json().nullable(),
8
+ attribute: model.belongsTo(() => Attribute, {
9
+ mappedBy: 'values',
10
+ })
11
+ })
12
+
13
+ export default AttributeValue
@@ -0,0 +1,23 @@
1
+ import { AttributeUIComponent } from "../types"
2
+ import AttributePossibleValue from "./attribute-possible-value"
3
+ import AttributeSet from "./attribute-set"
4
+ import AttributeValue from "./attribute-value"
5
+ import { model } from "@medusajs/framework/utils"
6
+
7
+ const Attribute = model.define('attribute', {
8
+ id: model.id({ prefix: 'attr' }).primaryKey(),
9
+ name: model.text().index('IDX_ATTRIBUTE_NAME').searchable(),
10
+ description: model.text().nullable(),
11
+ is_variant_defining: model.boolean().default(true),
12
+ is_filterable: model.boolean().default(true),
13
+ handle: model.text().unique(),
14
+ ui_component: model.enum(Object.values(AttributeUIComponent)).default(AttributeUIComponent.SELECT),
15
+ metadata: model.json().nullable(),
16
+ possible_values: model.hasMany(() => AttributePossibleValue),
17
+ values: model.hasMany(() => AttributeValue),
18
+ sets: model.manyToMany(() => AttributeSet, { mappedBy: 'attributes' }),
19
+ }).cascades({
20
+ delete: ['values', 'possible_values']
21
+ })
22
+
23
+ export default Attribute
@@ -0,0 +1,102 @@
1
+ import {
2
+ arrayDifference,
3
+ InjectManager,
4
+ InjectTransactionManager,
5
+ MedusaContext,
6
+ MedusaService,
7
+ } from "@medusajs/framework/utils";
8
+ import Attribute from "./models/attribute";
9
+ import { Context, DAL, InferTypeOf } from "@medusajs/framework/types";
10
+ import { EntityManager } from "@mikro-orm/knex";
11
+ import {
12
+ CreateAttributeValueDTO,
13
+ UpdateAttributeDTO,
14
+ UpsertAttributeValueDTO,
15
+ UpdateAttributeValueDTO,
16
+ } from "../../types/attribute";
17
+ import AttributeValue from "./models/attribute-value";
18
+ import AttributeSet from "./models/attribute-set";
19
+ import AttributePossibleValue from "./models/attribute-possible-value";
20
+
21
+ type Attribute = InferTypeOf<typeof Attribute>
22
+ type AttributePossibleValue = InferTypeOf<typeof AttributePossibleValue>
23
+
24
+ type InjectedDependencies = {
25
+ attributeRepository: DAL.RepositoryService<Attribute>;
26
+ attributePossibleValueRepository: DAL.RepositoryService<AttributePossibleValue>;
27
+ };
28
+
29
+ class AttributeModuleService extends MedusaService({
30
+ Attribute,
31
+ AttributeValue,
32
+ AttributeSet,
33
+ AttributePossibleValue,
34
+ }) {
35
+ protected attributeRepository_: DAL.RepositoryService<Attribute>;
36
+ protected attributePossibleValueRepository_: DAL.RepositoryService<AttributePossibleValue>;
37
+
38
+ constructor({
39
+ attributeRepository,
40
+ attributePossibleValueRepository,
41
+ }: InjectedDependencies) {
42
+ super(...arguments);
43
+ this.attributeRepository_ = attributeRepository;
44
+ this.attributePossibleValueRepository_ = attributePossibleValueRepository;
45
+ }
46
+
47
+ /**
48
+ *
49
+ * @param input
50
+ * @param sharedContext
51
+ *
52
+ * Useful to update attribute, allowing to upsert possible_values in the same operation. If "id"
53
+ * is not provided for "possible_values" entries, it will lookup the DB by attributePossibleValue.value,
54
+ * to update or create accordingly.
55
+ *
56
+ * Assumes caller will eventually refetch entities, for now, to reduce complexity of this
57
+ * method and concentrate on upserting like ProductOption - ProductOptionValue from Medusa
58
+ */
59
+ @InjectManager()
60
+ async updateAttributeWithUpsertOrReplacePossibleValues(
61
+ input: UpdateAttributeDTO | UpdateAttributeDTO[],
62
+ @MedusaContext() sharedContext?: Context<EntityManager>
63
+ ) {
64
+ const normalizedInput = Array.isArray(input) ? input : [input];
65
+
66
+ return this.updateAttributeWithUpsertOrReplacePossibleValues_(
67
+ normalizedInput,
68
+ sharedContext
69
+ );
70
+ }
71
+
72
+ @InjectTransactionManager()
73
+ protected async updateAttributeWithUpsertOrReplacePossibleValues_(
74
+ input: UpdateAttributeDTO[],
75
+ @MedusaContext() sharedContext?: Context<EntityManager>
76
+ ) {
77
+ // When debugging this, it only seems to have the id proprty returned
78
+ // so i refetch the entities
79
+ const upsertedValues = await this.attributePossibleValueRepository_.upsert(
80
+ input.flatMap((element) => element.possible_values),
81
+ sharedContext
82
+ );
83
+
84
+ // const upsertedValues = await this.listAttributePossibleValues({
85
+ // id: upsertedValuesWithoutFields.map(val => val.id)
86
+ // }, undefined, sharedContext)
87
+
88
+ const attributesInput = input.map(toUpdate => {
89
+ const { possible_values, product_category_ids, ...attribute } = toUpdate;
90
+ return {
91
+ ...attribute,
92
+ possible_values: upsertedValues
93
+ .filter(val => val.attribute_id === attribute.id)
94
+ .map(upserted => ({ id: upserted.id }))
95
+ }
96
+ });
97
+
98
+ return this.attributeRepository_.upsertWithReplace(attributesInput, { relations: ['possible_values'] }, sharedContext)
99
+ }
100
+ }
101
+
102
+ export default AttributeModuleService;
@@ -0,0 +1,94 @@
1
+ import { AdminCreateAttributeType } from "../../../../api/admin/plugin/attributes/validators"
2
+
3
+ export enum AttributeUIComponent {
4
+ SELECT = 'select', // ui component with list of values which are represented in possible value table
5
+ MULTIVALUE = 'multivalue',
6
+ UNIT = 'unit', // cantimeters, grams etc which are represented in scale table
7
+ TOGGLE = 'toggle',
8
+ TEXTAREA = 'text_area',
9
+ COLOR_PICKER = 'color_picker'
10
+ }
11
+
12
+ export type CreateAttributesWorkflowInput = {
13
+ attributes: AdminCreateAttributeType[]
14
+ }
15
+
16
+ export type CreateAttributeStepInput = Omit<AdminCreateAttributeType, 'product_category_ids'>[]
17
+
18
+ // Attributes
19
+
20
+ /**
21
+ * @interface
22
+ *
23
+ * The data to update the attribute.
24
+ */
25
+ export interface UpdateAttributeDTO {
26
+ /**
27
+ * The id of the attribute to update.
28
+ */
29
+ id: string
30
+
31
+ /**
32
+ * The name of the attribute.
33
+ */
34
+ name?: string
35
+
36
+ /**
37
+ * The description of the attribute.
38
+ */
39
+ description?: string
40
+
41
+ /**
42
+ * Whether the attribute should be used as a Blueprint for options
43
+ * when creating them a product variant.
44
+ */
45
+ is_variant_defining?: boolean
46
+
47
+ /**
48
+ * Whether the attribute can be used to filter products.
49
+ */
50
+ is_filterable?: boolean
51
+
52
+ /**
53
+ * The handle of the attribute. The handle can be used to create slug URL paths.
54
+ * If not supplied, the value of the `handle` attribute of the attribute is set to the slug version of the `name` property.
55
+ */
56
+ handle?: string
57
+
58
+ /**
59
+ * Holds custom data in key-value pairs.
60
+ */
61
+ metadata?: Record<string, unknown>
62
+
63
+ /**
64
+ * The associated product categories
65
+ */
66
+ product_category_ids?: string[]
67
+ }
68
+
69
+ /**
70
+ * @interface
71
+ *
72
+ * The data to update an attribute value.
73
+ */
74
+ export interface UpdateAttributeValueDTO {
75
+ /**
76
+ * The id of the attribute value to update.
77
+ */
78
+ id: string
79
+
80
+ /**
81
+ * The value of the attribute value.
82
+ */
83
+ value?: string
84
+
85
+ /**
86
+ * The rank of the attribute value. Useful to visually order it on dropdowns.
87
+ */
88
+ rank?: number
89
+
90
+ /**
91
+ * Holds custom data in key-value pairs.
92
+ */
93
+ metadata?: Record<string, unknown> | null
94
+ }
@@ -0,0 +1 @@
1
+ export * from './common'
@@ -0,0 +1 @@
1
+ export * from './mutations'
@@ -0,0 +1,7 @@
1
+ export type CreateAttributeSetDTO = {
2
+ name: string
3
+ handle?: string
4
+ description?: string
5
+ metadata?: Record<string, unknown> | null
6
+ attributes?: string[]
7
+ }
@@ -0,0 +1 @@
1
+ export * from './mutations'
@@ -0,0 +1,5 @@
1
+ export type CreateProductAttributeValueDTO = {
2
+ attribute_id: string;
3
+ product_id: string;
4
+ value: string;
5
+ }
@@ -0,0 +1,3 @@
1
+ export * from './attribute'
2
+ export * from './attribute-set'
3
+ export * from './attribute-value'
@@ -0,0 +1,30 @@
1
+ ## Module Providers
2
+
3
+ You can create module providers, such as Notification or File Module Providers under a sub-directory of this directory. For example, `src/providers/my-notification`.
4
+
5
+ Then, you register them in the Medusa application as `plugin-name/providers/my-notification`:
6
+
7
+ ```ts
8
+ module.exports = defineConfig({
9
+ // ...
10
+ modules: [
11
+ {
12
+ resolve: "@medusajs/medusa/notification",
13
+ options: {
14
+ providers: [
15
+ {
16
+ resolve: "@myorg/plugin-name/providers/my-notification",
17
+ id: "my-notification",
18
+ options: {
19
+ channels: ["email"],
20
+ // provider options...
21
+ },
22
+ },
23
+ ],
24
+ },
25
+ },
26
+ ],
27
+ })
28
+ ```
29
+
30
+ Learn more in [this documentation](https://docs.medusajs.com/learn/fundamentals/plugins/create).
@@ -0,0 +1,59 @@
1
+ # Custom subscribers
2
+
3
+ Subscribers handle events emitted in the Medusa application.
4
+
5
+ The subscriber is created in a TypeScript or JavaScript file under the `src/subscribers` directory.
6
+
7
+ For example, create the file `src/subscribers/product-created.ts` with the following content:
8
+
9
+ ```ts
10
+ import {
11
+ type SubscriberConfig,
12
+ } from "@medusajs/framework"
13
+
14
+ // subscriber function
15
+ export default async function productCreateHandler() {
16
+ console.log("A product was created")
17
+ }
18
+
19
+ // subscriber config
20
+ export const config: SubscriberConfig = {
21
+ event: "product.created",
22
+ }
23
+ ```
24
+
25
+ A subscriber file must export:
26
+
27
+ - The subscriber function that is an asynchronous function executed whenever the associated event is triggered.
28
+ - A configuration object defining the event this subscriber is listening to.
29
+
30
+ ## Subscriber Parameters
31
+
32
+ A subscriber receives an object having the following properties:
33
+
34
+ - `event`: An object holding the event's details. It has a `data` property, which is the event's data payload.
35
+ - `container`: The Medusa container. Use it to resolve modules' main services and other registered resources.
36
+
37
+ ```ts
38
+ import type {
39
+ SubscriberArgs,
40
+ SubscriberConfig,
41
+ } from "@medusajs/framework"
42
+
43
+ export default async function productCreateHandler({
44
+ event: { data },
45
+ container,
46
+ }: SubscriberArgs<{ id: string }>) {
47
+ const productId = data.id
48
+
49
+ const productModuleService = container.resolve("product")
50
+
51
+ const product = await productModuleService.retrieveProduct(productId)
52
+
53
+ console.log(`The product ${product.title} was created`)
54
+ }
55
+
56
+ export const config: SubscriberConfig = {
57
+ event: "product.created",
58
+ }
59
+ ```