Initial commit: backend, storefront, vendor-panel added
This commit is contained in:
45
backend/packages/modules/attribute/package.json
Normal file
45
backend/packages/modules/attribute/package.json
Normal file
@@ -0,0 +1,45 @@
|
||||
{
|
||||
"name": "@mercurjs/attribute",
|
||||
"version": "1.0.0",
|
||||
"main": "dist/index.js",
|
||||
"types": "dist/index.d.ts",
|
||||
"files": [
|
||||
"dist",
|
||||
"!dist/**/__tests__",
|
||||
"!dist/**/__mocks__",
|
||||
"!dist/**/__fixtures__"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=20"
|
||||
},
|
||||
"license": "MIT",
|
||||
"scripts": {
|
||||
"build": "rimraf dist && tsc --build",
|
||||
"migration:initial": " MIKRO_ORM_CLI_CONFIG=./mikro-orm.config.dev.ts medusa-mikro-orm migration:create --initial",
|
||||
"migration:create": " MIKRO_ORM_CLI_CONFIG=./mikro-orm.config.dev.ts medusa-mikro-orm migration:create",
|
||||
"migration:up": " MIKRO_ORM_CLI_CONFIG=./mikro-orm.config.dev.ts medusa-mikro-orm migration:up",
|
||||
"orm:cache:clear": " MIKRO_ORM_CLI_CONFIG=./mikro-orm.config.dev.ts medusa-mikro-orm cache:clear"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@medusajs/framework": "2.8.6",
|
||||
"@medusajs/test-utils": "2.8.6",
|
||||
"@mercurjs/framework": "*",
|
||||
"@mikro-orm/cli": "6.4.3",
|
||||
"@mikro-orm/core": "6.4.3",
|
||||
"@mikro-orm/migrations": "6.4.3",
|
||||
"@mikro-orm/postgresql": "6.4.3",
|
||||
"@swc/core": "^1.7.28",
|
||||
"@swc/jest": "^0.2.36",
|
||||
"jest": "^29.7.0",
|
||||
"rimraf": "^3.0.2",
|
||||
"tsc-alias": "^1.8.6",
|
||||
"typescript": "^5.6.2"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@medusajs/framework": "2.8.6",
|
||||
"@mikro-orm/core": "6.4.3",
|
||||
"@mikro-orm/migrations": "6.4.3",
|
||||
"@mikro-orm/postgresql": "6.4.3",
|
||||
"awilix": "^8.0.1"
|
||||
}
|
||||
}
|
||||
10
backend/packages/modules/attribute/src/index.ts
Normal file
10
backend/packages/modules/attribute/src/index.ts
Normal file
@@ -0,0 +1,10 @@
|
||||
import { Module } from "@medusajs/framework/utils";
|
||||
|
||||
import AttributeModuleService from "./service";
|
||||
|
||||
export const ATTRIBUTE_MODULE = "attribute";
|
||||
export { AttributeModuleService };
|
||||
|
||||
export default Module(ATTRIBUTE_MODULE, {
|
||||
service: AttributeModuleService,
|
||||
});
|
||||
@@ -0,0 +1,422 @@
|
||||
{
|
||||
"namespaces": [
|
||||
"public"
|
||||
],
|
||||
"name": "public",
|
||||
"tables": [
|
||||
{
|
||||
"columns": {
|
||||
"id": {
|
||||
"name": "id",
|
||||
"type": "text",
|
||||
"unsigned": false,
|
||||
"autoincrement": false,
|
||||
"primary": false,
|
||||
"nullable": false,
|
||||
"mappedType": "text"
|
||||
},
|
||||
"name": {
|
||||
"name": "name",
|
||||
"type": "text",
|
||||
"unsigned": false,
|
||||
"autoincrement": false,
|
||||
"primary": false,
|
||||
"nullable": false,
|
||||
"mappedType": "text"
|
||||
},
|
||||
"description": {
|
||||
"name": "description",
|
||||
"type": "text",
|
||||
"unsigned": false,
|
||||
"autoincrement": false,
|
||||
"primary": false,
|
||||
"nullable": true,
|
||||
"mappedType": "text"
|
||||
},
|
||||
"is_filterable": {
|
||||
"name": "is_filterable",
|
||||
"type": "boolean",
|
||||
"unsigned": false,
|
||||
"autoincrement": false,
|
||||
"primary": false,
|
||||
"nullable": false,
|
||||
"default": "true",
|
||||
"mappedType": "boolean"
|
||||
},
|
||||
"handle": {
|
||||
"name": "handle",
|
||||
"type": "text",
|
||||
"unsigned": false,
|
||||
"autoincrement": false,
|
||||
"primary": false,
|
||||
"nullable": false,
|
||||
"mappedType": "text"
|
||||
},
|
||||
"metadata": {
|
||||
"name": "metadata",
|
||||
"type": "jsonb",
|
||||
"unsigned": false,
|
||||
"autoincrement": false,
|
||||
"primary": false,
|
||||
"nullable": true,
|
||||
"mappedType": "json"
|
||||
},
|
||||
"ui_component": {
|
||||
"name": "ui_component",
|
||||
"type": "text",
|
||||
"unsigned": false,
|
||||
"autoincrement": false,
|
||||
"primary": false,
|
||||
"nullable": false,
|
||||
"default": "'select'",
|
||||
"enumItems": [
|
||||
"select",
|
||||
"multivalue",
|
||||
"unit",
|
||||
"toggle",
|
||||
"text_area",
|
||||
"color_picker"
|
||||
],
|
||||
"mappedType": "enum"
|
||||
},
|
||||
"created_at": {
|
||||
"name": "created_at",
|
||||
"type": "timestamptz",
|
||||
"unsigned": false,
|
||||
"autoincrement": false,
|
||||
"primary": false,
|
||||
"nullable": false,
|
||||
"length": 6,
|
||||
"default": "now()",
|
||||
"mappedType": "datetime"
|
||||
},
|
||||
"updated_at": {
|
||||
"name": "updated_at",
|
||||
"type": "timestamptz",
|
||||
"unsigned": false,
|
||||
"autoincrement": false,
|
||||
"primary": false,
|
||||
"nullable": false,
|
||||
"length": 6,
|
||||
"default": "now()",
|
||||
"mappedType": "datetime"
|
||||
},
|
||||
"deleted_at": {
|
||||
"name": "deleted_at",
|
||||
"type": "timestamptz",
|
||||
"unsigned": false,
|
||||
"autoincrement": false,
|
||||
"primary": false,
|
||||
"nullable": true,
|
||||
"length": 6,
|
||||
"mappedType": "datetime"
|
||||
}
|
||||
},
|
||||
"name": "attribute",
|
||||
"schema": "public",
|
||||
"indexes": [
|
||||
{
|
||||
"keyName": "IDX_attribute_handle_unique",
|
||||
"columnNames": [],
|
||||
"composite": false,
|
||||
"constraint": false,
|
||||
"primary": false,
|
||||
"unique": false,
|
||||
"expression": "CREATE UNIQUE INDEX IF NOT EXISTS \"IDX_attribute_handle_unique\" ON \"attribute\" (handle) WHERE deleted_at IS NULL"
|
||||
},
|
||||
{
|
||||
"keyName": "IDX_attribute_deleted_at",
|
||||
"columnNames": [],
|
||||
"composite": false,
|
||||
"constraint": false,
|
||||
"primary": false,
|
||||
"unique": false,
|
||||
"expression": "CREATE INDEX IF NOT EXISTS \"IDX_attribute_deleted_at\" ON \"attribute\" (deleted_at) WHERE deleted_at IS NULL"
|
||||
},
|
||||
{
|
||||
"keyName": "attribute_pkey",
|
||||
"columnNames": [
|
||||
"id"
|
||||
],
|
||||
"composite": false,
|
||||
"constraint": true,
|
||||
"primary": true,
|
||||
"unique": true
|
||||
}
|
||||
],
|
||||
"checks": [],
|
||||
"foreignKeys": {},
|
||||
"nativeEnums": {}
|
||||
},
|
||||
{
|
||||
"columns": {
|
||||
"id": {
|
||||
"name": "id",
|
||||
"type": "text",
|
||||
"unsigned": false,
|
||||
"autoincrement": false,
|
||||
"primary": false,
|
||||
"nullable": false,
|
||||
"mappedType": "text"
|
||||
},
|
||||
"value": {
|
||||
"name": "value",
|
||||
"type": "text",
|
||||
"unsigned": false,
|
||||
"autoincrement": false,
|
||||
"primary": false,
|
||||
"nullable": false,
|
||||
"mappedType": "text"
|
||||
},
|
||||
"rank": {
|
||||
"name": "rank",
|
||||
"type": "integer",
|
||||
"unsigned": false,
|
||||
"autoincrement": false,
|
||||
"primary": false,
|
||||
"nullable": false,
|
||||
"mappedType": "integer"
|
||||
},
|
||||
"metadata": {
|
||||
"name": "metadata",
|
||||
"type": "jsonb",
|
||||
"unsigned": false,
|
||||
"autoincrement": false,
|
||||
"primary": false,
|
||||
"nullable": true,
|
||||
"mappedType": "json"
|
||||
},
|
||||
"attribute_id": {
|
||||
"name": "attribute_id",
|
||||
"type": "text",
|
||||
"unsigned": false,
|
||||
"autoincrement": false,
|
||||
"primary": false,
|
||||
"nullable": false,
|
||||
"mappedType": "text"
|
||||
},
|
||||
"created_at": {
|
||||
"name": "created_at",
|
||||
"type": "timestamptz",
|
||||
"unsigned": false,
|
||||
"autoincrement": false,
|
||||
"primary": false,
|
||||
"nullable": false,
|
||||
"length": 6,
|
||||
"default": "now()",
|
||||
"mappedType": "datetime"
|
||||
},
|
||||
"updated_at": {
|
||||
"name": "updated_at",
|
||||
"type": "timestamptz",
|
||||
"unsigned": false,
|
||||
"autoincrement": false,
|
||||
"primary": false,
|
||||
"nullable": false,
|
||||
"length": 6,
|
||||
"default": "now()",
|
||||
"mappedType": "datetime"
|
||||
},
|
||||
"deleted_at": {
|
||||
"name": "deleted_at",
|
||||
"type": "timestamptz",
|
||||
"unsigned": false,
|
||||
"autoincrement": false,
|
||||
"primary": false,
|
||||
"nullable": true,
|
||||
"length": 6,
|
||||
"mappedType": "datetime"
|
||||
}
|
||||
},
|
||||
"name": "attribute_possible_value",
|
||||
"schema": "public",
|
||||
"indexes": [
|
||||
{
|
||||
"keyName": "IDX_attribute_possible_value_attribute_id",
|
||||
"columnNames": [],
|
||||
"composite": false,
|
||||
"constraint": false,
|
||||
"primary": false,
|
||||
"unique": false,
|
||||
"expression": "CREATE INDEX IF NOT EXISTS \"IDX_attribute_possible_value_attribute_id\" ON \"attribute_possible_value\" (attribute_id) WHERE deleted_at IS NULL"
|
||||
},
|
||||
{
|
||||
"keyName": "IDX_attribute_possible_value_deleted_at",
|
||||
"columnNames": [],
|
||||
"composite": false,
|
||||
"constraint": false,
|
||||
"primary": false,
|
||||
"unique": false,
|
||||
"expression": "CREATE INDEX IF NOT EXISTS \"IDX_attribute_possible_value_deleted_at\" ON \"attribute_possible_value\" (deleted_at) WHERE deleted_at IS NULL"
|
||||
},
|
||||
{
|
||||
"keyName": "UQ_attribute_id_value",
|
||||
"columnNames": [],
|
||||
"composite": false,
|
||||
"constraint": false,
|
||||
"primary": false,
|
||||
"unique": false,
|
||||
"expression": "CREATE UNIQUE INDEX IF NOT EXISTS \"UQ_attribute_id_value\" ON \"attribute_possible_value\" (attribute_id, value) WHERE deleted_at IS NULL"
|
||||
},
|
||||
{
|
||||
"keyName": "attribute_possible_value_pkey",
|
||||
"columnNames": [
|
||||
"id"
|
||||
],
|
||||
"composite": false,
|
||||
"constraint": true,
|
||||
"primary": true,
|
||||
"unique": true
|
||||
}
|
||||
],
|
||||
"checks": [],
|
||||
"foreignKeys": {
|
||||
"attribute_possible_value_attribute_id_foreign": {
|
||||
"constraintName": "attribute_possible_value_attribute_id_foreign",
|
||||
"columnNames": [
|
||||
"attribute_id"
|
||||
],
|
||||
"localTableName": "public.attribute_possible_value",
|
||||
"referencedColumnNames": [
|
||||
"id"
|
||||
],
|
||||
"referencedTableName": "public.attribute",
|
||||
"deleteRule": "cascade",
|
||||
"updateRule": "cascade"
|
||||
}
|
||||
},
|
||||
"nativeEnums": {}
|
||||
},
|
||||
{
|
||||
"columns": {
|
||||
"id": {
|
||||
"name": "id",
|
||||
"type": "text",
|
||||
"unsigned": false,
|
||||
"autoincrement": false,
|
||||
"primary": false,
|
||||
"nullable": false,
|
||||
"mappedType": "text"
|
||||
},
|
||||
"value": {
|
||||
"name": "value",
|
||||
"type": "text",
|
||||
"unsigned": false,
|
||||
"autoincrement": false,
|
||||
"primary": false,
|
||||
"nullable": false,
|
||||
"mappedType": "text"
|
||||
},
|
||||
"rank": {
|
||||
"name": "rank",
|
||||
"type": "integer",
|
||||
"unsigned": false,
|
||||
"autoincrement": false,
|
||||
"primary": false,
|
||||
"nullable": false,
|
||||
"mappedType": "integer"
|
||||
},
|
||||
"metadata": {
|
||||
"name": "metadata",
|
||||
"type": "jsonb",
|
||||
"unsigned": false,
|
||||
"autoincrement": false,
|
||||
"primary": false,
|
||||
"nullable": true,
|
||||
"mappedType": "json"
|
||||
},
|
||||
"attribute_id": {
|
||||
"name": "attribute_id",
|
||||
"type": "text",
|
||||
"unsigned": false,
|
||||
"autoincrement": false,
|
||||
"primary": false,
|
||||
"nullable": false,
|
||||
"mappedType": "text"
|
||||
},
|
||||
"created_at": {
|
||||
"name": "created_at",
|
||||
"type": "timestamptz",
|
||||
"unsigned": false,
|
||||
"autoincrement": false,
|
||||
"primary": false,
|
||||
"nullable": false,
|
||||
"length": 6,
|
||||
"default": "now()",
|
||||
"mappedType": "datetime"
|
||||
},
|
||||
"updated_at": {
|
||||
"name": "updated_at",
|
||||
"type": "timestamptz",
|
||||
"unsigned": false,
|
||||
"autoincrement": false,
|
||||
"primary": false,
|
||||
"nullable": false,
|
||||
"length": 6,
|
||||
"default": "now()",
|
||||
"mappedType": "datetime"
|
||||
},
|
||||
"deleted_at": {
|
||||
"name": "deleted_at",
|
||||
"type": "timestamptz",
|
||||
"unsigned": false,
|
||||
"autoincrement": false,
|
||||
"primary": false,
|
||||
"nullable": true,
|
||||
"length": 6,
|
||||
"mappedType": "datetime"
|
||||
}
|
||||
},
|
||||
"name": "attribute_value",
|
||||
"schema": "public",
|
||||
"indexes": [
|
||||
{
|
||||
"keyName": "IDX_attribute_value_attribute_id",
|
||||
"columnNames": [],
|
||||
"composite": false,
|
||||
"constraint": false,
|
||||
"primary": false,
|
||||
"unique": false,
|
||||
"expression": "CREATE INDEX IF NOT EXISTS \"IDX_attribute_value_attribute_id\" ON \"attribute_value\" (attribute_id) WHERE deleted_at IS NULL"
|
||||
},
|
||||
{
|
||||
"keyName": "IDX_attribute_value_deleted_at",
|
||||
"columnNames": [],
|
||||
"composite": false,
|
||||
"constraint": false,
|
||||
"primary": false,
|
||||
"unique": false,
|
||||
"expression": "CREATE INDEX IF NOT EXISTS \"IDX_attribute_value_deleted_at\" ON \"attribute_value\" (deleted_at) WHERE deleted_at IS NULL"
|
||||
},
|
||||
{
|
||||
"keyName": "attribute_value_pkey",
|
||||
"columnNames": [
|
||||
"id"
|
||||
],
|
||||
"composite": false,
|
||||
"constraint": true,
|
||||
"primary": true,
|
||||
"unique": true
|
||||
}
|
||||
],
|
||||
"checks": [],
|
||||
"foreignKeys": {
|
||||
"attribute_value_attribute_id_foreign": {
|
||||
"constraintName": "attribute_value_attribute_id_foreign",
|
||||
"columnNames": [
|
||||
"attribute_id"
|
||||
],
|
||||
"localTableName": "public.attribute_value",
|
||||
"referencedColumnNames": [
|
||||
"id"
|
||||
],
|
||||
"referencedTableName": "public.attribute",
|
||||
"deleteRule": "cascade",
|
||||
"updateRule": "cascade"
|
||||
}
|
||||
},
|
||||
"nativeEnums": {}
|
||||
}
|
||||
],
|
||||
"nativeEnums": {}
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
import { Migration } from '@mikro-orm/migrations';
|
||||
|
||||
export class Migration20250617080244 extends Migration {
|
||||
|
||||
override async up(): Promise<void> {
|
||||
this.addSql(`alter table if exists "attribute" drop constraint if exists "attribute_handle_unique";`);
|
||||
this.addSql(`create table if not exists "attribute" ("id" text not null, "name" text not null, "description" text null, "handle" text not null, "metadata" jsonb null, "ui_component" text check ("ui_component" in ('select', 'multivalue', 'unit', 'toggle', 'text_area', 'color_picker')) not null default 'select', "created_at" timestamptz not null default now(), "updated_at" timestamptz not null default now(), "deleted_at" timestamptz null, constraint "attribute_pkey" primary key ("id"));`);
|
||||
this.addSql(`CREATE UNIQUE INDEX IF NOT EXISTS "IDX_attribute_handle_unique" ON "attribute" (handle) WHERE deleted_at IS NULL;`);
|
||||
this.addSql(`CREATE INDEX IF NOT EXISTS "IDX_attribute_deleted_at" ON "attribute" (deleted_at) WHERE deleted_at IS NULL;`);
|
||||
|
||||
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"));`);
|
||||
this.addSql(`CREATE INDEX IF NOT EXISTS "IDX_attribute_possible_value_attribute_id" ON "attribute_possible_value" (attribute_id) WHERE deleted_at IS NULL;`);
|
||||
this.addSql(`CREATE INDEX IF NOT EXISTS "IDX_attribute_possible_value_deleted_at" ON "attribute_possible_value" (deleted_at) WHERE deleted_at IS NULL;`);
|
||||
this.addSql(`CREATE UNIQUE INDEX IF NOT EXISTS "UQ_attribute_id_value" ON "attribute_possible_value" (attribute_id, value) WHERE deleted_at IS NULL;`);
|
||||
|
||||
this.addSql(`create table if not exists "attribute_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_value_pkey" primary key ("id"));`);
|
||||
this.addSql(`CREATE INDEX IF NOT EXISTS "IDX_attribute_value_attribute_id" ON "attribute_value" (attribute_id) WHERE deleted_at IS NULL;`);
|
||||
this.addSql(`CREATE INDEX IF NOT EXISTS "IDX_attribute_value_deleted_at" ON "attribute_value" (deleted_at) WHERE deleted_at IS NULL;`);
|
||||
|
||||
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;`);
|
||||
|
||||
this.addSql(`alter table if exists "attribute_value" add constraint "attribute_value_attribute_id_foreign" foreign key ("attribute_id") references "attribute" ("id") on update cascade on delete cascade;`);
|
||||
}
|
||||
|
||||
override async down(): Promise<void> {
|
||||
this.addSql(`alter table if exists "attribute_possible_value" drop constraint if exists "attribute_possible_value_attribute_id_foreign";`);
|
||||
|
||||
this.addSql(`alter table if exists "attribute_value" drop constraint if exists "attribute_value_attribute_id_foreign";`);
|
||||
|
||||
this.addSql(`drop table if exists "attribute" cascade;`);
|
||||
|
||||
this.addSql(`drop table if exists "attribute_possible_value" cascade;`);
|
||||
|
||||
this.addSql(`drop table if exists "attribute_value" cascade;`);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
import { Migration } from '@mikro-orm/migrations';
|
||||
|
||||
export class Migration20250620110849 extends Migration {
|
||||
|
||||
override async up(): Promise<void> {
|
||||
this.addSql(`alter table if exists "attribute" add column if not exists "is_filterable" boolean not null default true;`);
|
||||
}
|
||||
|
||||
override async down(): Promise<void> {
|
||||
this.addSql(`alter table if exists "attribute" drop column if exists "is_filterable";`);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
import { model } from '@medusajs/framework/utils'
|
||||
|
||||
import Attribute from './attribute'
|
||||
|
||||
const AttributePossibleValue = model
|
||||
.define('attribute_possible_value', {
|
||||
id: model.id({ prefix: 'attr_pos_val' }).primaryKey(),
|
||||
value: model.text(),
|
||||
rank: model.number(),
|
||||
metadata: model.json().nullable(),
|
||||
attribute: model.belongsTo(() => Attribute, {
|
||||
mappedBy: 'possible_values'
|
||||
})
|
||||
})
|
||||
.indexes([
|
||||
{
|
||||
on: ['attribute_id', 'value'],
|
||||
name: 'UQ_attribute_id_value',
|
||||
unique: true
|
||||
}
|
||||
])
|
||||
|
||||
export default AttributePossibleValue
|
||||
@@ -0,0 +1,15 @@
|
||||
import { model } from '@medusajs/framework/utils'
|
||||
|
||||
import Attribute from './attribute'
|
||||
|
||||
const AttributeValue = model.define('attribute_value', {
|
||||
id: model.id({ prefix: 'attr_val' }).primaryKey(),
|
||||
value: model.text(),
|
||||
rank: model.number(),
|
||||
metadata: model.json().nullable(),
|
||||
attribute: model.belongsTo(() => Attribute, {
|
||||
mappedBy: 'values'
|
||||
})
|
||||
})
|
||||
|
||||
export default AttributeValue
|
||||
25
backend/packages/modules/attribute/src/models/attribute.ts
Normal file
25
backend/packages/modules/attribute/src/models/attribute.ts
Normal file
@@ -0,0 +1,25 @@
|
||||
import { model } from "@medusajs/framework/utils";
|
||||
|
||||
import { AttributeUIComponent } from "@mercurjs/framework";
|
||||
import AttributePossibleValue from "./attribute-possible-value";
|
||||
import AttributeValue from "./attribute-value";
|
||||
|
||||
const Attribute = model
|
||||
.define("attribute", {
|
||||
id: model.id({ prefix: "attr" }).primaryKey(),
|
||||
name: model.text().searchable(),
|
||||
description: model.text().nullable(),
|
||||
is_filterable: model.boolean().default(true),
|
||||
handle: model.text().unique(),
|
||||
metadata: model.json().nullable(),
|
||||
ui_component: model
|
||||
.enum(Object.values(AttributeUIComponent))
|
||||
.default(AttributeUIComponent.SELECT),
|
||||
values: model.hasMany(() => AttributeValue),
|
||||
possible_values: model.hasMany(() => AttributePossibleValue),
|
||||
})
|
||||
.cascades({
|
||||
delete: ["values", "possible_values"],
|
||||
});
|
||||
|
||||
export default Attribute;
|
||||
93
backend/packages/modules/attribute/src/service.ts
Normal file
93
backend/packages/modules/attribute/src/service.ts
Normal file
@@ -0,0 +1,93 @@
|
||||
import { EntityManager } from "@mikro-orm/knex";
|
||||
import { UpdateAttributeDTO } from "@mercurjs/framework";
|
||||
import { Context, DAL, InferTypeOf } from "@medusajs/framework/types";
|
||||
import {
|
||||
InjectManager,
|
||||
InjectTransactionManager,
|
||||
MedusaContext,
|
||||
MedusaService,
|
||||
} from "@medusajs/framework/utils";
|
||||
|
||||
import Attribute from "./models/attribute";
|
||||
import AttributePossibleValue from "./models/attribute-possible-value";
|
||||
import AttributeValue from "./models/attribute-value";
|
||||
|
||||
type Attribute = InferTypeOf<typeof Attribute>;
|
||||
type AttributePossibleValue = InferTypeOf<typeof AttributePossibleValue>;
|
||||
|
||||
type InjectedDependencies = {
|
||||
attributeRepository: DAL.RepositoryService<Attribute>;
|
||||
attributePossibleValueRepository: DAL.RepositoryService<AttributePossibleValue>;
|
||||
};
|
||||
|
||||
class AttributeModuleService extends MedusaService({
|
||||
Attribute,
|
||||
AttributeValue,
|
||||
AttributePossibleValue,
|
||||
}) {
|
||||
protected attributeRepository_: DAL.RepositoryService<Attribute>;
|
||||
protected attributePossibleValueRepository_: DAL.RepositoryService<AttributePossibleValue>;
|
||||
|
||||
constructor({
|
||||
attributeRepository,
|
||||
attributePossibleValueRepository,
|
||||
}: InjectedDependencies) {
|
||||
super(...arguments);
|
||||
this.attributeRepository_ = attributeRepository;
|
||||
this.attributePossibleValueRepository_ = attributePossibleValueRepository;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param input
|
||||
* @param sharedContext
|
||||
*
|
||||
* Useful to update attribute, allowing to upsert possible_values in the same operation. If "id"
|
||||
* is not provided for "possible_values" entries, it will lookup the DB by attributePossibleValue.value,
|
||||
* to update or create accordingly.
|
||||
*
|
||||
* Assumes caller will eventually refetch entities, for now, to reduce complexity of this
|
||||
* method and concentrate on upserting like ProductOption - ProductOptionValue from Medusa
|
||||
*/
|
||||
@InjectManager()
|
||||
async updateAttributeWithUpsertOrReplacePossibleValues(
|
||||
input: UpdateAttributeDTO | UpdateAttributeDTO[],
|
||||
@MedusaContext() sharedContext?: Context<EntityManager>
|
||||
) {
|
||||
const normalizedInput = Array.isArray(input) ? input : [input];
|
||||
|
||||
return this.updateAttributeWithUpsertOrReplacePossibleValues_(
|
||||
normalizedInput,
|
||||
sharedContext
|
||||
);
|
||||
}
|
||||
|
||||
@InjectTransactionManager()
|
||||
protected async updateAttributeWithUpsertOrReplacePossibleValues_(
|
||||
input: UpdateAttributeDTO[],
|
||||
@MedusaContext() sharedContext?: Context<EntityManager>
|
||||
) {
|
||||
const upsertedValues = await this.attributePossibleValueRepository_.upsert(
|
||||
input.flatMap((element) => element.possible_values),
|
||||
sharedContext
|
||||
);
|
||||
|
||||
const attributesInput = input.map((toUpdate) => {
|
||||
const { ...attribute } = toUpdate;
|
||||
return {
|
||||
...attribute,
|
||||
possible_values: upsertedValues
|
||||
.filter((val) => val.attribute_id === attribute.id)
|
||||
.map((upserted) => ({ id: upserted.id })),
|
||||
};
|
||||
});
|
||||
|
||||
return this.attributeRepository_.upsertWithReplace(
|
||||
attributesInput,
|
||||
{ relations: ["possible_values"] },
|
||||
sharedContext
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export default AttributeModuleService;
|
||||
27
backend/packages/modules/attribute/tsconfig.json
Normal file
27
backend/packages/modules/attribute/tsconfig.json
Normal file
@@ -0,0 +1,27 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"lib": ["ES2021"],
|
||||
"target": "ES2021",
|
||||
"outDir": "${configDir}/dist",
|
||||
"esModuleInterop": true,
|
||||
"declaration": true,
|
||||
"declarationMap": true,
|
||||
"noUnusedLocals": true,
|
||||
"module": "node16",
|
||||
"moduleResolution": "node16",
|
||||
"emitDecoratorMetadata": true,
|
||||
"experimentalDecorators": true,
|
||||
"sourceMap": true,
|
||||
"noImplicitReturns": true,
|
||||
"resolveJsonModule": true,
|
||||
"forceConsistentCasingInFileNames": true,
|
||||
"strictNullChecks": true,
|
||||
"strictFunctionTypes": true,
|
||||
"noImplicitThis": true,
|
||||
"allowJs": true,
|
||||
"skipLibCheck": true,
|
||||
"incremental": false
|
||||
},
|
||||
"include": ["${configDir}/src"],
|
||||
"exclude": ["${configDir}/dist", "${configDir}/node_modules"]
|
||||
}
|
||||
Reference in New Issue
Block a user