From ce81cade887659cefe9638e3c1c2807378191c62 Mon Sep 17 00:00:00 2001 From: Oli Juhl <59018053+olivermrbl@users.noreply.github.com> Date: Thu, 11 Jan 2024 15:40:13 +0100 Subject: [PATCH 1/2] feat(cart): Cart + Address services (#6055) Duplicate of #6008, but I messed up my git history big time :'( **What** - CartService and CartRepository + tests - AddressService and AddressRepository + tests - CartModuleService skeleton. Full implementation + tests will be added in a separate PR --- .changeset/red-mirrors-tap.md | 5 + .../__fixtures__/cart/data.ts | 23 +++ .../__fixtures__/cart/index.ts | 21 +++ .../__tests__/services/address/index.spec.ts | 127 ++++++++++++++ .../__tests__/services/cart/index.spec.ts | 124 ++++++++++++++ packages/cart/src/models/cart.ts | 5 +- packages/cart/src/repositories/address.ts | 27 +++ packages/cart/src/repositories/cart.ts | 27 +++ packages/cart/src/repositories/index.ts | 3 + packages/cart/src/services/address.ts | 128 ++++++++++++++ packages/cart/src/services/cart-module.ts | 157 +++++++++++++++++- packages/cart/src/services/cart.ts | 128 ++++++++++++++ packages/cart/src/services/index.ts | 4 +- packages/cart/src/types/address.ts | 20 +++ packages/cart/src/types/cart.ts | 18 ++ packages/cart/src/types/index.ts | 2 + packages/cart/tsconfig.json | 3 +- packages/types/src/cart/common.ts | 7 +- packages/types/src/cart/service.ts | 33 ++-- .../src/dal/mikro-orm/mikro-orm-repository.ts | 18 +- 20 files changed, 847 insertions(+), 33 deletions(-) create mode 100644 .changeset/red-mirrors-tap.md create mode 100644 packages/cart/integration-tests/__fixtures__/cart/data.ts create mode 100644 packages/cart/integration-tests/__fixtures__/cart/index.ts create mode 100644 packages/cart/integration-tests/__tests__/services/address/index.spec.ts create mode 100644 packages/cart/integration-tests/__tests__/services/cart/index.spec.ts create mode 100644 packages/cart/src/repositories/address.ts create mode 100644 packages/cart/src/repositories/cart.ts create mode 100644 packages/cart/src/services/address.ts create mode 100644 packages/cart/src/services/cart.ts create mode 100644 packages/cart/src/types/address.ts create mode 100644 packages/cart/src/types/cart.ts diff --git a/.changeset/red-mirrors-tap.md b/.changeset/red-mirrors-tap.md new file mode 100644 index 0000000000..a249585ce7 --- /dev/null +++ b/.changeset/red-mirrors-tap.md @@ -0,0 +1,5 @@ +--- +"@medusajs/types": patch +--- + +feat(cart): Add CartService and AddressService diff --git a/packages/cart/integration-tests/__fixtures__/cart/data.ts b/packages/cart/integration-tests/__fixtures__/cart/data.ts new file mode 100644 index 0000000000..ba3eb43849 --- /dev/null +++ b/packages/cart/integration-tests/__fixtures__/cart/data.ts @@ -0,0 +1,23 @@ +export const defaultCartsData = [ + { + id: "cart-id-1", + region_id: "region-id-1", + customer_id: "customer-id-1", + email: "test@email.com", + currency_code: "usd", + }, + { + id: "cart-id-2", + region_id: "region-id-1", + customer_id: "customer-id-1", + shipping_address: { + first_name: "Tony", + last_name: "Stark", + }, + billing_address_id: { + address_1: "Stark Industries", + city: "New York", + }, + currency_code: "usd", + }, +] diff --git a/packages/cart/integration-tests/__fixtures__/cart/index.ts b/packages/cart/integration-tests/__fixtures__/cart/index.ts new file mode 100644 index 0000000000..f355d7fc4c --- /dev/null +++ b/packages/cart/integration-tests/__fixtures__/cart/index.ts @@ -0,0 +1,21 @@ +import { CreateCartDTO } from "@medusajs/types" +import { SqlEntityManager } from "@mikro-orm/postgresql" +import { Cart } from "../../../src/models" +import { defaultCartsData } from "./data" + +export * from "./data" + +export async function createCarts( + manager: SqlEntityManager, + cartsData: CreateCartDTO[] = defaultCartsData +): Promise { + const carts: Cart[] = [] + + for (let cartData of cartsData) { + let cart = manager.create(Cart, cartData) + + await manager.persistAndFlush(cart) + } + + return carts +} diff --git a/packages/cart/integration-tests/__tests__/services/address/index.spec.ts b/packages/cart/integration-tests/__tests__/services/address/index.spec.ts new file mode 100644 index 0000000000..54b1395499 --- /dev/null +++ b/packages/cart/integration-tests/__tests__/services/address/index.spec.ts @@ -0,0 +1,127 @@ +import { SqlEntityManager } from "@mikro-orm/postgresql" +import { AddressRepository } from "../../../../src/repositories" +import { AddressService } from "../../../../src/services" +import { MikroOrmWrapper } from "../../../utils" + +jest.setTimeout(30000) + +describe("Address Service", () => { + let service: AddressService + let testManager: SqlEntityManager + let repositoryManager: SqlEntityManager + + beforeEach(async () => { + await MikroOrmWrapper.setupDatabase() + repositoryManager = await MikroOrmWrapper.forkManager() + testManager = await MikroOrmWrapper.forkManager() + + const addressRepository = new AddressRepository({ + manager: repositoryManager, + }) + + service = new AddressService({ + addressRepository: addressRepository, + }) + }) + + afterEach(async () => { + await MikroOrmWrapper.clearDatabase() + }) + + describe("create", () => { + it("should create an address successfully", async () => { + const [createdAddress] = await service.create([ + { + first_name: "John", + last_name: "Doe", + address_1: "Test street 1", + city: "Test city", + country_code: "US", + postal_code: "12345", + }, + ]) + + const [address] = await service.list({ id: [createdAddress.id] }) + + expect(address).toEqual( + expect.objectContaining({ + id: createdAddress.id, + first_name: "John", + last_name: "Doe", + address_1: "Test street 1", + city: "Test city", + country_code: "US", + postal_code: "12345", + }) + ) + }) + }) + + describe("update", () => { + it("should throw an error if address does not exist", async () => { + const error = await service + .update([ + { + id: "none-existing", + }, + ]) + .catch((e) => e) + + expect(error.message).toContain( + "Address with id \"none-existing\" not found" + ) + }) + + it("should update an address successfully", async () => { + const [createdAddress] = await service.create([ + { + first_name: "Jane", + last_name: "Doe", + address_1: "Test street 1", + city: "Test city", + country_code: "US", + postal_code: "12345", + }, + ]) + + await service.update([ + { + id: createdAddress.id, + address_1: "Test street 2", + city: "Test city 2", + }, + ]) + + const [address] = await service.list({ id: [createdAddress.id] }) + + expect(address).toEqual( + expect.objectContaining({ + id: createdAddress.id, + first_name: "Jane", + last_name: "Doe", + address_1: "Test street 2", + city: "Test city 2", + country_code: "US", + postal_code: "12345", + }) + ) + }) + }) + + describe("delete", () => { + it("should delete a cart successfully", async () => { + const [createdAddress] = await service.create([ + { + first_name: "Jane", + last_name: "Doe", + }, + ]) + + await service.delete([createdAddress.id]) + + const carts = await service.list({ id: [createdAddress.id] }) + + expect(carts.length).toEqual(0) + }) + }) +}) diff --git a/packages/cart/integration-tests/__tests__/services/cart/index.spec.ts b/packages/cart/integration-tests/__tests__/services/cart/index.spec.ts new file mode 100644 index 0000000000..b1dfeb1826 --- /dev/null +++ b/packages/cart/integration-tests/__tests__/services/cart/index.spec.ts @@ -0,0 +1,124 @@ +import { SqlEntityManager } from "@mikro-orm/postgresql" +import { CartRepository } from "../../../../src/repositories" +import { CartService } from "../../../../src/services" +import { createCarts } from "../../../__fixtures__/cart" +import { MikroOrmWrapper } from "../../../utils" + +jest.setTimeout(30000) + +describe("Cart Service", () => { + let service: CartService + let testManager: SqlEntityManager + let repositoryManager: SqlEntityManager + + beforeEach(async () => { + await MikroOrmWrapper.setupDatabase() + repositoryManager = await MikroOrmWrapper.forkManager() + testManager = await MikroOrmWrapper.forkManager() + + const cartRepository = new CartRepository({ + manager: repositoryManager, + }) + + service = new CartService({ + cartRepository: cartRepository, + }) + + await createCarts(testManager) + }) + + afterEach(async () => { + await MikroOrmWrapper.clearDatabase() + }) + + describe("create", () => { + it("should throw an error when required params are not passed", async () => { + const error = await service + .create([ + { + email: "test@email.com", + } as any, + ]) + .catch((e) => e) + + expect(error.message).toContain( + "Value for Cart.currency_code is required, 'undefined' found" + ) + }) + + it("should create a cart successfully", async () => { + const [createdCart] = await service.create([ + { + currency_code: "eur", + }, + ]) + + const [cart] = await service.list({ id: [createdCart.id] }) + + expect(cart).toEqual( + expect.objectContaining({ + id: createdCart.id, + currency_code: "eur", + }) + ) + }) + }) + + describe("update", () => { + it("should throw an error if cart does not exist", async () => { + const error = await service + .update([ + { + id: "none-existing", + }, + ]) + .catch((e) => e) + + expect(error.message).toContain( + "Cart with id \"none-existing\" not found" + ) + }) + + it("should update a cart successfully", async () => { + const [createdCart] = await service.create([ + { + currency_code: "eur", + }, + ]) + + const [updatedCart] = await service.update([ + + { + id: createdCart.id, + email: "test@email.com", + }, + ]) + + const [cart] = await service.list({ id: [createdCart.id] }) + + expect(cart).toEqual( + expect.objectContaining({ + id: createdCart.id, + currency_code: "eur", + email: updatedCart.email, + }) + ) + }) + }) + + describe("delete", () => { + it("should delete a cart successfully", async () => { + const [createdCart] = await service.create([ + { + currency_code: "eur", + }, + ]) + + await service.delete([createdCart.id]) + + const carts = await service.list({ id: [createdCart.id] }) + + expect(carts.length).toEqual(0) + }) + }) +}) diff --git a/packages/cart/src/models/cart.ts b/packages/cart/src/models/cart.ts index c44963d6a0..9526e3054e 100644 --- a/packages/cart/src/models/cart.ts +++ b/packages/cart/src/models/cart.ts @@ -67,12 +67,13 @@ export default class Cart { metadata?: Record | null @OneToMany(() => LineItem, (lineItem) => lineItem.cart, { - orphanRemoval: true, + cascade: [Cascade.REMOVE], }) items = new Collection(this) @OneToMany(() => ShippingMethod, (shippingMethod) => shippingMethod.cart, { - orphanRemoval: true, + cascade: [Cascade.REMOVE], + }) shipping_methods = new Collection(this) diff --git a/packages/cart/src/repositories/address.ts b/packages/cart/src/repositories/address.ts new file mode 100644 index 0000000000..994ff44221 --- /dev/null +++ b/packages/cart/src/repositories/address.ts @@ -0,0 +1,27 @@ +import { Context } from "@medusajs/types" +import { DALUtils } from "@medusajs/utils" +import { SqlEntityManager } from "@mikro-orm/postgresql" +import { Address } from "@models" +import { CreateAddressDTO, UpdateAddressDTO } from "../types" + +export class AddressRepository extends DALUtils.mikroOrmBaseRepositoryFactory< + Address, + { + create: CreateAddressDTO + } +>(Address) { + async update( + data: { address: Address; update: UpdateAddressDTO }[], + context: Context = {} + ): Promise { + const manager = this.getActiveManager(context) + + const entities = data.map(({ address, update }) => { + return manager.assign(address, update) + }) + + manager.persist(entities) + + return entities + } +} diff --git a/packages/cart/src/repositories/cart.ts b/packages/cart/src/repositories/cart.ts new file mode 100644 index 0000000000..4aeddee7f3 --- /dev/null +++ b/packages/cart/src/repositories/cart.ts @@ -0,0 +1,27 @@ +import { Context } from "@medusajs/types" +import { DALUtils } from "@medusajs/utils" +import { SqlEntityManager } from "@mikro-orm/postgresql" +import { Cart } from "@models" +import { CreateCartDTO, UpdateCartDTO } from "../types" + +export class CartRepository extends DALUtils.mikroOrmBaseRepositoryFactory< + Cart, + { + create: CreateCartDTO + } +>(Cart) { + async update( + data: { cart: Cart; update: UpdateCartDTO }[], + context: Context = {} + ): Promise { + const manager = this.getActiveManager(context) + + const entities = data.map(({ cart, update }) => { + return manager.assign(cart, update) + }) + + manager.persist(entities) + + return entities + } +} diff --git a/packages/cart/src/repositories/index.ts b/packages/cart/src/repositories/index.ts index 147c9cc259..d527e6f73c 100644 --- a/packages/cart/src/repositories/index.ts +++ b/packages/cart/src/repositories/index.ts @@ -1 +1,4 @@ export { MikroOrmBaseRepository as BaseRepository } from "@medusajs/utils" +export * from "./address" +export * from "./cart" + diff --git a/packages/cart/src/services/address.ts b/packages/cart/src/services/address.ts new file mode 100644 index 0000000000..24484662fe --- /dev/null +++ b/packages/cart/src/services/address.ts @@ -0,0 +1,128 @@ +import { + AddressDTO, + Context, + DAL, + FilterableAddressProps, + FindConfig, +} from "@medusajs/types" +import { + InjectManager, + InjectTransactionManager, + MedusaContext, + MedusaError, + ModulesSdkUtils, + retrieveEntity, +} from "@medusajs/utils" +import { Address } from "@models" +import { AddressRepository } from "../repositories/address" +import { CreateAddressDTO, UpdateAddressDTO } from "../types" + +type InjectedDependencies = { + addressRepository: DAL.RepositoryService +} + +export default class AddressService { + protected readonly addressRepository_: DAL.RepositoryService + + constructor({ addressRepository }: InjectedDependencies) { + this.addressRepository_ = addressRepository + } + + @InjectManager("addressRepository_") + async retrieve( + id: string, + config: FindConfig = {}, + @MedusaContext() sharedContext: Context = {} + ): Promise { + return (await retrieveEntity({ + id: id, + entityName: Address.name, + repository: this.addressRepository_, + config, + sharedContext, + })) as TEntity + } + + @InjectManager("addressRepository_") + async list( + filters: FilterableAddressProps = {}, + config: FindConfig = {}, + @MedusaContext() sharedContext: Context = {} + ): Promise { + const queryOptions = ModulesSdkUtils.buildQuery
(filters, config) + + return (await this.addressRepository_.find( + queryOptions, + sharedContext + )) as TEntity[] + } + + @InjectManager("addressRepository_") + async listAndCount( + filters: FilterableAddressProps = {}, + config: FindConfig = {}, + @MedusaContext() sharedContext: Context = {} + ): Promise<[TEntity[], number]> { + const queryOptions = ModulesSdkUtils.buildQuery
(filters, config) + + return (await this.addressRepository_.findAndCount( + queryOptions, + sharedContext + )) as [TEntity[], number] + } + + @InjectTransactionManager("addressRepository_") + async create( + data: CreateAddressDTO[], + @MedusaContext() sharedContext: Context = {} + ): Promise { + return (await (this.addressRepository_ as AddressRepository).create( + data, + sharedContext + )) as TEntity[] + } + + @InjectTransactionManager("addressRepository_") + async update( + data: UpdateAddressDTO[], + @MedusaContext() sharedContext: Context = {} + ): Promise { + const existingAddresses = await this.list( + { id: data.map(({ id }) => id) }, + {}, + sharedContext + ) + + const existingAddressesMap = new Map( + existingAddresses.map<[string, Address]>((addr) => [addr.id, addr]) + ) + + const updates: { address: Address; update: UpdateAddressDTO }[] = [] + + for (const update of data) { + const address = existingAddressesMap.get(update.id) + + if (!address) { + throw new MedusaError( + MedusaError.Types.NOT_FOUND, + `Address with id "${update.id}" not found` + ) + } + + updates.push({ address, update }) + } + + return (await (this.addressRepository_ as AddressRepository).update( + updates, + sharedContext + )) as TEntity[] + } + + @InjectTransactionManager("addressRepository_") + async delete( + ids: string[], + @MedusaContext() sharedContext: Context = {} + ): Promise { + await this.addressRepository_.delete(ids, sharedContext) + } +} diff --git a/packages/cart/src/services/cart-module.ts b/packages/cart/src/services/cart-module.ts index ab136b060d..b951b660b7 100644 --- a/packages/cart/src/services/cart-module.ts +++ b/packages/cart/src/services/cart-module.ts @@ -1,20 +1,35 @@ import { + CartDTO, + Context, + CreateCartDTO, DAL, + FilterableCartProps, + FindConfig, + ICartModuleService, InternalModuleDeclaration, ModuleJoinerConfig, + UpdateCartDTO, } from "@medusajs/types" -import { Cart } from "@models" - +import { + InjectManager, + InjectTransactionManager, + MedusaContext, +} from "@medusajs/utils" import { joinerConfig } from "../joiner-config" +import AddressService from "./address" +import CartService from "./cart" type InjectedDependencies = { baseRepository: DAL.RepositoryService + cartService: CartService + addressService: AddressService } -// TODO: implement ICartModuleService from @medusajs/types -export default class CartModuleService { +export default class CartModuleService implements ICartModuleService { protected baseRepository_: DAL.RepositoryService + protected cartService_: CartService + protected addressService_: AddressService constructor( { baseRepository }: InjectedDependencies, @@ -26,4 +41,138 @@ export default class CartModuleService { __joinerConfig(): ModuleJoinerConfig { return joinerConfig } + + @InjectManager("baseRepository_") + async retrieve( + id: string, + config: FindConfig = {}, + @MedusaContext() sharedContext: Context = {} + ): Promise { + const cart = await this.cartService_.retrieve(id, config, sharedContext) + + return await this.baseRepository_.serialize(cart, { + populate: true, + }) + } + + @InjectManager("baseRepository_") + async list( + filters: FilterableCartProps = {}, + config: FindConfig = {}, + @MedusaContext() sharedContext: Context = {} + ): Promise { + const carts = await this.cartService_.list(filters, config, sharedContext) + + return await this.baseRepository_.serialize(carts, { + populate: true, + }) + } + + @InjectManager("baseRepository_") + async listAndCount( + filters: FilterableCartProps = {}, + config: FindConfig = {}, + @MedusaContext() sharedContext: Context = {} + ): Promise<[CartDTO[], number]> { + const [carts, count] = await this.cartService_.listAndCount( + filters, + config, + sharedContext + ) + + return [ + await this.baseRepository_.serialize(carts, { + populate: true, + }), + count, + ] + } + + async create( + data: CreateCartDTO[], + sharedContext?: Context + ): Promise + + async create(data: CreateCartDTO, sharedContext?: Context): Promise + + @InjectManager("baseRepository_") + async create( + data: CreateCartDTO[] | CreateCartDTO, + @MedusaContext() sharedContext: Context = {} + ): Promise { + const input = Array.isArray(data) ? data : [data] + + const carts = await this.create_(input, sharedContext) + + const result = await this.list( + { id: carts.map((p) => p!.id) }, + { + relations: ["shipping_address", "billing_address"], + }, + sharedContext + ) + + return (Array.isArray(data) ? result : result[0]) as CartDTO | CartDTO[] + } + + @InjectTransactionManager("baseRepository_") + protected async create_( + data: CreateCartDTO[], + @MedusaContext() sharedContext: Context = {} + ) { + return await this.cartService_.create(data, sharedContext) + } + + async update( + data: UpdateCartDTO[], + sharedContext?: Context + ): Promise + + async update(data: UpdateCartDTO, sharedContext?: Context): Promise + + @InjectManager("baseRepository_") + async update( + data: UpdateCartDTO[] | UpdateCartDTO, + @MedusaContext() sharedContext: Context = {} + ): Promise { + const input = Array.isArray(data) ? data : [data] + const carts = await this.update_(input, sharedContext) + + const result = await this.list( + { id: carts.map((p) => p!.id) }, + { + relations: ["shipping_address", "billing_address"], + }, + sharedContext + ) + + return (Array.isArray(data) ? result : result[0]) as CartDTO | CartDTO[] + } + + @InjectTransactionManager("baseRepository_") + protected async update_( + data: UpdateCartDTO[], + @MedusaContext() sharedContext: Context = {} + ) { + return await this.cartService_.update(data, sharedContext) + } + + async delete( + ids: string[], + sharedContext?: Context + ): Promise + + async delete( + ids: string, + sharedContext?: Context + ): Promise + + @InjectTransactionManager("baseRepository_") + async delete( + ids: string[] | string, + @MedusaContext() sharedContext: Context = {} + ): Promise { + const cartIds = Array.isArray(ids) ? ids : [ids] + await this.cartService_.delete(cartIds, sharedContext) + } } diff --git a/packages/cart/src/services/cart.ts b/packages/cart/src/services/cart.ts new file mode 100644 index 0000000000..0cae7e1dee --- /dev/null +++ b/packages/cart/src/services/cart.ts @@ -0,0 +1,128 @@ +import { + CartDTO, + Context, + DAL, + FilterableCartProps, + FindConfig, +} from "@medusajs/types" +import { + InjectManager, + InjectTransactionManager, + MedusaContext, + MedusaError, + ModulesSdkUtils, + retrieveEntity, +} from "@medusajs/utils" +import { Cart } from "@models" +import { CartRepository } from "@repositories" +import { CreateCartDTO, UpdateCartDTO } from "@types" + +type InjectedDependencies = { + cartRepository: DAL.RepositoryService +} + +export default class CartService { + protected readonly cartRepository_: DAL.RepositoryService + + constructor({ cartRepository }: InjectedDependencies) { + this.cartRepository_ = cartRepository + } + + @InjectManager("cartRepository_") + async retrieve( + id: string, + config: FindConfig = {}, + @MedusaContext() sharedContext: Context = {} + ): Promise { + return (await retrieveEntity({ + id: id, + entityName: Cart.name, + repository: this.cartRepository_, + config, + sharedContext, + })) as TEntity + } + + @InjectManager("cartRepository_") + async list( + filters: FilterableCartProps = {}, + config: FindConfig = {}, + @MedusaContext() sharedContext: Context = {} + ): Promise { + const queryOptions = ModulesSdkUtils.buildQuery(filters, config) + + return (await this.cartRepository_.find( + queryOptions, + sharedContext + )) as TEntity[] + } + + @InjectManager("cartRepository_") + async listAndCount( + filters: FilterableCartProps = {}, + config: FindConfig = {}, + @MedusaContext() sharedContext: Context = {} + ): Promise<[TEntity[], number]> { + const queryOptions = ModulesSdkUtils.buildQuery(filters, config) + + return (await this.cartRepository_.findAndCount( + queryOptions, + sharedContext + )) as [TEntity[], number] + } + + @InjectTransactionManager("cartRepository_") + async create( + data: CreateCartDTO[], + @MedusaContext() sharedContext: Context = {} + ): Promise { + return (await (this.cartRepository_ as CartRepository).create( + data, + sharedContext + )) as TEntity[] + } + + @InjectTransactionManager("cartRepository_") + async update( + data: UpdateCartDTO[], + @MedusaContext() sharedContext: Context = {} + ): Promise { + const existingCarts = await this.list( + { id: data.map(({ id }) => id) }, + {}, + sharedContext + ) + + const existingCartsMap = new Map( + existingCarts.map<[string, Cart]>((cart) => [cart.id, cart]) + ) + + const updates: { cart: Cart; update: UpdateCartDTO }[] = [] + + for (const update of data) { + const cart = existingCartsMap.get(update.id) + + if (!cart) { + throw new MedusaError( + MedusaError.Types.NOT_FOUND, + `Cart with id "${update.id}" not found` + ) + } + + updates.push({ cart, update }) + } + + return (await (this.cartRepository_ as CartRepository).update( + updates, + sharedContext + )) as TEntity[] + } + + @InjectTransactionManager("cartRepository_") + async delete( + ids: string[], + @MedusaContext() sharedContext: Context = {} + ): Promise { + await this.cartRepository_.delete(ids, sharedContext) + } +} diff --git a/packages/cart/src/services/index.ts b/packages/cart/src/services/index.ts index 2191dc84cf..1f70a17a34 100644 --- a/packages/cart/src/services/index.ts +++ b/packages/cart/src/services/index.ts @@ -1,2 +1,4 @@ -export { default as CartModuleService } from "./cart-module"; +export { default as AddressService } from "./address" +export { default as CartService } from "./cart" +export { default as CartModuleService } from "./cart-module" diff --git a/packages/cart/src/types/address.ts b/packages/cart/src/types/address.ts new file mode 100644 index 0000000000..07d8aecd92 --- /dev/null +++ b/packages/cart/src/types/address.ts @@ -0,0 +1,20 @@ +export interface UpsertAddressDTO { + customer_id?: string + company?: string + first_name?: string + last_name?: string + address_1?: string + address_2?: string + city?: string + country_code?: string + province?: string + postal_code?: string + phone?: string + metadata?: Record +} + +export interface UpdateAddressDTO extends UpsertAddressDTO { + id: string +} + +export interface CreateAddressDTO extends UpsertAddressDTO {} diff --git a/packages/cart/src/types/cart.ts b/packages/cart/src/types/cart.ts new file mode 100644 index 0000000000..852159840a --- /dev/null +++ b/packages/cart/src/types/cart.ts @@ -0,0 +1,18 @@ +export interface CreateCartDTO { + region_id?: string + customer_id?: string + sales_channel_id?: string + email?: string + currency_code: string + metadata?: Record +} + +export interface UpdateCartDTO { + id: string + region_id?: string + customer_id?: string + sales_channel_id?: string + email?: string + currency_code?: string + metadata?: Record +} diff --git a/packages/cart/src/types/index.ts b/packages/cart/src/types/index.ts index 0f252977b0..8b97276103 100644 --- a/packages/cart/src/types/index.ts +++ b/packages/cart/src/types/index.ts @@ -1,4 +1,6 @@ import { Logger } from "@medusajs/types" +export * from "./address" +export * from "./cart" export type InitializeModuleInjectableDependencies = { logger?: Logger diff --git a/packages/cart/tsconfig.json b/packages/cart/tsconfig.json index 213e38fc55..6143fb1ef3 100644 --- a/packages/cart/tsconfig.json +++ b/packages/cart/tsconfig.json @@ -22,7 +22,8 @@ "paths": { "@models": ["./src/models"], "@services": ["./src/services"], - "@repositories": ["./src/repositories"] + "@repositories": ["./src/repositories"], + "@types": ["./src/types"], } }, "include": ["src"], diff --git a/packages/types/src/cart/common.ts b/packages/types/src/cart/common.ts index 7a552618c7..1e17abf889 100644 --- a/packages/types/src/cart/common.ts +++ b/packages/types/src/cart/common.ts @@ -465,8 +465,13 @@ export interface FilterableCartProps updated_at?: OperatorMap } +export interface FilterableAddressProps + extends BaseFilterable { + id?: string | string[] +} + /** - * TODO: Remove this in favor of new__CartDTO, when module is released + * TODO: Remove this in favor of CartDTO, when module is released * @deprecated Use CartDTO instead */ diff --git a/packages/types/src/cart/service.ts b/packages/types/src/cart/service.ts index c94bf82f73..800ede55a3 100644 --- a/packages/types/src/cart/service.ts +++ b/packages/types/src/cart/service.ts @@ -2,7 +2,7 @@ import { FindConfig } from "../common" import { IModuleService } from "../modules-sdk" import { Context } from "../shared-context" import { CartDTO, FilterableCartProps } from "./common" -import { AddLineItemsDTO, CreateCartDTO, UpdateCartDTO, UpdateLineItemsDTO } from "./mutations" +import { CreateCartDTO, UpdateCartDTO } from "./mutations" export interface ICartModuleService extends IModuleService { retrieve( @@ -24,25 +24,28 @@ export interface ICartModuleService extends IModuleService { ): Promise<[CartDTO[], number]> create(data: CreateCartDTO[], sharedContext?: Context): Promise + create(data: CreateCartDTO, sharedContext?: Context): Promise update(data: UpdateCartDTO[], sharedContext?: Context): Promise + update(data: UpdateCartDTO, sharedContext?: Context): Promise delete(cartIds: string[], sharedContext?: Context): Promise + delete(cartId: string, sharedContext?: Context): Promise - addLineItems(data: AddLineItemsDTO, sharedContext?: Context): Promise - addLineItems( - data: AddLineItemsDTO[], - sharedContext?: Context - ): Promise + // addLineItems(data: AddLineItemsDTO, sharedContext?: Context): Promise + // addLineItems( + // data: AddLineItemsDTO[], + // sharedContext?: Context + // ): Promise - updateLineItems( - data: UpdateLineItemsDTO, - sharedContext?: Context - ): Promise - updateLineItems( - data: UpdateLineItemsDTO[], - sharedContext?: Context - ): Promise + // updateLineItems( + // data: UpdateLineItemsDTO, + // sharedContext?: Context + // ): Promise + // updateLineItems( + // data: UpdateLineItemsDTO[], + // sharedContext?: Context + // ): Promise - removeLineItems(lineItemIds: string[], sharedContext?: Context): Promise + // removeLineItems(lineItemIds: string[], sharedContext?: Context): Promise } diff --git a/packages/utils/src/dal/mikro-orm/mikro-orm-repository.ts b/packages/utils/src/dal/mikro-orm/mikro-orm-repository.ts index df24e190be..84c8b437f2 100644 --- a/packages/utils/src/dal/mikro-orm/mikro-orm-repository.ts +++ b/packages/utils/src/dal/mikro-orm/mikro-orm-repository.ts @@ -4,14 +4,6 @@ import { FilterQuery as InternalFilerQuery, RepositoryTransformOptions, } from "@medusajs/types" -import { arrayDifference, isString, MedusaError } from "../../common" -import { MedusaContext } from "../../decorators" -import { buildQuery, InjectTransactionManager } from "../../modules-sdk" -import { - getSoftDeletedCascadedEntitiesIdsMappedBy, - transactionWrapper, -} from "../utils" -import { mikroOrmSerializer, mikroOrmUpdateDeletedAtRecursively } from "./utils" import { EntityManager, EntitySchema, @@ -19,12 +11,20 @@ import { LoadStrategy, RequiredEntityData, } from "@mikro-orm/core" +import { FindOptions as MikroOptions } from "@mikro-orm/core/drivers/IDatabaseDriver" import { EntityClass, EntityName, FilterQuery as MikroFilterQuery, } from "@mikro-orm/core/typings" -import { FindOptions as MikroOptions } from "@mikro-orm/core/drivers/IDatabaseDriver" +import { MedusaError, arrayDifference, isString } from "../../common" +import { MedusaContext } from "../../decorators" +import { InjectTransactionManager, buildQuery } from "../../modules-sdk" +import { + getSoftDeletedCascadedEntitiesIdsMappedBy, + transactionWrapper, +} from "../utils" +import { mikroOrmSerializer, mikroOrmUpdateDeletedAtRecursively } from "./utils" export class MikroOrmBase { readonly manager_: any From 8472460f533322cc4535199aa768ac163021bc79 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Frane=20Poli=C4=87?= <16856471+fPolic@users.noreply.github.com> Date: Thu, 11 Jan 2024 16:41:40 +0100 Subject: [PATCH 2/2] feat: Payment module package setup (#6059) --- .changeset/wicked-months-greet.md | 6 ++ packages/modules-sdk/src/definitions.ts | 17 ++++++ packages/payment/.gitignore | 6 ++ packages/payment/CHANGELOG.md | 0 packages/payment/README.md | 3 + .../integration-tests/__tests__/index.spec.ts | 5 ++ .../payment/integration-tests/setup-env.js | 6 ++ packages/payment/integration-tests/setup.js | 3 + .../payment/integration-tests/utils/config.ts | 6 ++ .../integration-tests/utils/database.ts | 18 ++++++ .../payment/integration-tests/utils/index.ts | 2 + packages/payment/jest.config.js | 21 +++++++ packages/payment/mikro-orm.config.dev.ts | 8 +++ packages/payment/package.json | 61 +++++++++++++++++++ packages/payment/src/index.ts | 23 +++++++ packages/payment/src/initialize/index.ts | 27 ++++++++ packages/payment/src/joiner-config.ts | 31 ++++++++++ packages/payment/src/loaders/connection.ts | 34 +++++++++++ packages/payment/src/loaders/container.ts | 39 ++++++++++++ packages/payment/src/loaders/index.ts | 2 + packages/payment/src/models/index.ts | 1 + packages/payment/src/models/payment.ts | 40 ++++++++++++ packages/payment/src/module-definition.ts | 14 +++++ packages/payment/src/repositories/index.ts | 1 + packages/payment/src/scripts/bin/run-seed.ts | 33 ++++++++++ packages/payment/src/scripts/seed-utils.ts | 8 +++ .../src/services/__tests__/index.spec.ts | 5 ++ packages/payment/src/services/index.ts | 1 + .../payment/src/services/payment-module.ts | 29 +++++++++ packages/payment/src/types/index.ts | 5 ++ packages/payment/tsconfig.json | 36 +++++++++++ packages/payment/tsconfig.spec.json | 8 +++ packages/types/src/index.ts | 2 +- packages/types/src/payment/index.ts | 1 + packages/types/src/payment/service.ts | 3 + yarn.lock | 27 ++++++++ 36 files changed, 531 insertions(+), 1 deletion(-) create mode 100644 .changeset/wicked-months-greet.md create mode 100644 packages/payment/.gitignore create mode 100644 packages/payment/CHANGELOG.md create mode 100644 packages/payment/README.md create mode 100644 packages/payment/integration-tests/__tests__/index.spec.ts create mode 100644 packages/payment/integration-tests/setup-env.js create mode 100644 packages/payment/integration-tests/setup.js create mode 100644 packages/payment/integration-tests/utils/config.ts create mode 100644 packages/payment/integration-tests/utils/database.ts create mode 100644 packages/payment/integration-tests/utils/index.ts create mode 100644 packages/payment/jest.config.js create mode 100644 packages/payment/mikro-orm.config.dev.ts create mode 100644 packages/payment/package.json create mode 100644 packages/payment/src/index.ts create mode 100644 packages/payment/src/initialize/index.ts create mode 100644 packages/payment/src/joiner-config.ts create mode 100644 packages/payment/src/loaders/connection.ts create mode 100644 packages/payment/src/loaders/container.ts create mode 100644 packages/payment/src/loaders/index.ts create mode 100644 packages/payment/src/models/index.ts create mode 100644 packages/payment/src/models/payment.ts create mode 100644 packages/payment/src/module-definition.ts create mode 100644 packages/payment/src/repositories/index.ts create mode 100644 packages/payment/src/scripts/bin/run-seed.ts create mode 100644 packages/payment/src/scripts/seed-utils.ts create mode 100644 packages/payment/src/services/__tests__/index.spec.ts create mode 100644 packages/payment/src/services/index.ts create mode 100644 packages/payment/src/services/payment-module.ts create mode 100644 packages/payment/src/types/index.ts create mode 100644 packages/payment/tsconfig.json create mode 100644 packages/payment/tsconfig.spec.json create mode 100644 packages/types/src/payment/index.ts create mode 100644 packages/types/src/payment/service.ts diff --git a/.changeset/wicked-months-greet.md b/.changeset/wicked-months-greet.md new file mode 100644 index 0000000000..e0a8e026af --- /dev/null +++ b/.changeset/wicked-months-greet.md @@ -0,0 +1,6 @@ +--- +"@medusajs/modules-sdk": patch +"@medusajs/types": patch +--- + +feat: add Payment module package diff --git a/packages/modules-sdk/src/definitions.ts b/packages/modules-sdk/src/definitions.ts index 48cbe2eb16..a70b6f1b93 100644 --- a/packages/modules-sdk/src/definitions.ts +++ b/packages/modules-sdk/src/definitions.ts @@ -16,6 +16,7 @@ export enum Modules { PROMOTION = "promotion", AUTHENTICATION = "authentication", CART = "cart", + PAYMENT = "payment", } export enum ModuleRegistrationName { @@ -28,6 +29,7 @@ export enum ModuleRegistrationName { PROMOTION = "promotionModuleService", AUTHENTICATION = "authenticationModuleService", CART = "cartModuleService", + PAYMENT = "paymentModuleService", } export const MODULE_PACKAGE_NAMES = { @@ -40,6 +42,7 @@ export const MODULE_PACKAGE_NAMES = { [Modules.PROMOTION]: "@medusajs/promotion", [Modules.AUTHENTICATION]: "@medusajs/authentication", [Modules.CART]: "@medusajs/cart", + [Modules.PAYMENT]: "@medusajs/payment", } export const ModulesDefinition: { [key: string | Modules]: ModuleDefinition } = @@ -171,6 +174,20 @@ export const ModulesDefinition: { [key: string | Modules]: ModuleDefinition } = resources: MODULE_RESOURCE_TYPE.SHARED, }, }, + [Modules.PAYMENT]: { + key: Modules.PAYMENT, + registrationName: ModuleRegistrationName.PAYMENT, + defaultPackage: false, + label: upperCaseFirst(ModuleRegistrationName.PAYMENT), + isRequired: false, + canOverride: true, + isQueryable: true, + dependencies: ["logger"], + defaultModuleDeclaration: { + scope: MODULE_SCOPE.INTERNAL, + resources: MODULE_RESOURCE_TYPE.SHARED, + }, + }, } export const MODULE_DEFINITIONS: ModuleDefinition[] = diff --git a/packages/payment/.gitignore b/packages/payment/.gitignore new file mode 100644 index 0000000000..874c6c69d3 --- /dev/null +++ b/packages/payment/.gitignore @@ -0,0 +1,6 @@ +/dist +node_modules +.DS_store +.env* +.env +*.sql diff --git a/packages/payment/CHANGELOG.md b/packages/payment/CHANGELOG.md new file mode 100644 index 0000000000..e69de29bb2 diff --git a/packages/payment/README.md b/packages/payment/README.md new file mode 100644 index 0000000000..5f233bc704 --- /dev/null +++ b/packages/payment/README.md @@ -0,0 +1,3 @@ +# Payment Module + +A Payment is the result of a series of financial transactions performed as part of collecting a payment of a purchase. The Payment holds information about the payment provider as well as the state of it. You can associate a Payment with a Cart, an Order, an OrderEdit, or any other resource you’d like to have your customers pay for. diff --git a/packages/payment/integration-tests/__tests__/index.spec.ts b/packages/payment/integration-tests/__tests__/index.spec.ts new file mode 100644 index 0000000000..728f6245c6 --- /dev/null +++ b/packages/payment/integration-tests/__tests__/index.spec.ts @@ -0,0 +1,5 @@ +describe("Noop test", () => { + it("noop check", async () => { + expect(true).toBe(true) + }) +}) diff --git a/packages/payment/integration-tests/setup-env.js b/packages/payment/integration-tests/setup-env.js new file mode 100644 index 0000000000..ca80a7dace --- /dev/null +++ b/packages/payment/integration-tests/setup-env.js @@ -0,0 +1,6 @@ +if (typeof process.env.DB_TEMP_NAME === "undefined") { + const tempName = parseInt(process.env.JEST_WORKER_ID || "1") + process.env.DB_TEMP_NAME = `medusa-payment-integration-${tempName}` +} + +process.env.MEDUSA_PAYMENT_DB_SCHEMA = "public" diff --git a/packages/payment/integration-tests/setup.js b/packages/payment/integration-tests/setup.js new file mode 100644 index 0000000000..43f99aab4a --- /dev/null +++ b/packages/payment/integration-tests/setup.js @@ -0,0 +1,3 @@ +import { JestUtils } from "medusa-test-utils" + +JestUtils.afterAllHookDropDatabase() diff --git a/packages/payment/integration-tests/utils/config.ts b/packages/payment/integration-tests/utils/config.ts new file mode 100644 index 0000000000..5017bc79f0 --- /dev/null +++ b/packages/payment/integration-tests/utils/config.ts @@ -0,0 +1,6 @@ +import { ModuleServiceInitializeOptions } from "@medusajs/types" + +export const databaseOptions: ModuleServiceInitializeOptions["database"] = { + schema: "public", + clientUrl: "medusa-payment-test", +} diff --git a/packages/payment/integration-tests/utils/database.ts b/packages/payment/integration-tests/utils/database.ts new file mode 100644 index 0000000000..0fbf6bdd1c --- /dev/null +++ b/packages/payment/integration-tests/utils/database.ts @@ -0,0 +1,18 @@ +import { TestDatabaseUtils } from "medusa-test-utils" + +import * as PaymentModules from "@models" + +const pathToMigrations = "../../src/migrations" +const mikroOrmEntities = PaymentModules as unknown as any[] + +export const MikroOrmWrapper = TestDatabaseUtils.getMikroOrmWrapper( + mikroOrmEntities, + pathToMigrations +) + +export const MikroOrmConfig = TestDatabaseUtils.getMikroOrmConfig( + mikroOrmEntities, + pathToMigrations +) + +export const DB_URL = TestDatabaseUtils.getDatabaseURL() diff --git a/packages/payment/integration-tests/utils/index.ts b/packages/payment/integration-tests/utils/index.ts new file mode 100644 index 0000000000..5ca5d1bdc0 --- /dev/null +++ b/packages/payment/integration-tests/utils/index.ts @@ -0,0 +1,2 @@ +export * from "./config" +export * from "./database" diff --git a/packages/payment/jest.config.js b/packages/payment/jest.config.js new file mode 100644 index 0000000000..860ba90a49 --- /dev/null +++ b/packages/payment/jest.config.js @@ -0,0 +1,21 @@ +module.exports = { + moduleNameMapper: { + "^@models": "/src/models", + "^@services": "/src/services", + "^@repositories": "/src/repositories", + }, + transform: { + "^.+\\.[jt]s?$": [ + "ts-jest", + { + tsConfig: "tsconfig.spec.json", + isolatedModules: true, + }, + ], + }, + testEnvironment: `node`, + moduleFileExtensions: [`js`, `ts`], + modulePathIgnorePatterns: ["dist/"], + setupFiles: ["/integration-tests/setup-env.js"], + setupFilesAfterEnv: ["/integration-tests/setup.js"], +} diff --git a/packages/payment/mikro-orm.config.dev.ts b/packages/payment/mikro-orm.config.dev.ts new file mode 100644 index 0000000000..9e03481768 --- /dev/null +++ b/packages/payment/mikro-orm.config.dev.ts @@ -0,0 +1,8 @@ +import * as entities from "./src/models" + +module.exports = { + entities: Object.values(entities), + schema: "public", + clientUrl: "postgres://postgres@localhost/medusa-payment", + type: "postgresql", +} diff --git a/packages/payment/package.json b/packages/payment/package.json new file mode 100644 index 0000000000..cfc0e8a564 --- /dev/null +++ b/packages/payment/package.json @@ -0,0 +1,61 @@ +{ + "name": "@medusajs/payment", + "version": "0.0.1", + "description": "Medusa Payment module", + "main": "dist/index.js", + "types": "dist/index.d.ts", + "files": [ + "dist" + ], + "engines": { + "node": ">=16" + }, + "bin": { + "medusa-payment-seed": "dist/scripts/bin/run-seed.js" + }, + "repository": { + "type": "git", + "url": "https://github.com/medusajs/medusa", + "directory": "packages/payment" + }, + "publishConfig": { + "access": "public" + }, + "author": "Medusa", + "license": "MIT", + "scripts": { + "watch": "tsc --build --watch", + "watch:test": "tsc --build tsconfig.spec.json --watch", + "prepublishOnly": "cross-env NODE_ENV=production tsc --build && tsc-alias -p tsconfig.json", + "build": "rimraf dist && tsc --build && tsc-alias -p tsconfig.json", + "test": "jest --runInBand --bail --forceExit -- src/**/__tests__/**/*.ts", + "test:integration": "jest --runInBand --forceExit -- integration-tests/**/__tests__/**/*.ts", + "migration:generate": " MIKRO_ORM_CLI=./mikro-orm.config.dev.ts mikro-orm migration:generate", + "migration:initial": " MIKRO_ORM_CLI=./mikro-orm.config.dev.ts mikro-orm migration:create --initial", + "migration:create": " MIKRO_ORM_CLI=./mikro-orm.config.dev.ts mikro-orm migration:create", + "migration:up": " MIKRO_ORM_CLI=./mikro-orm.config.dev.ts mikro-orm migration:up", + "orm:cache:clear": " MIKRO_ORM_CLI=./mikro-orm.config.dev.ts mikro-orm cache:clear" + }, + "devDependencies": { + "@mikro-orm/cli": "5.9.7", + "cross-env": "^5.2.1", + "jest": "^29.6.3", + "medusa-test-utils": "^1.1.40", + "rimraf": "^3.0.2", + "ts-jest": "^29.1.1", + "ts-node": "^10.9.1", + "tsc-alias": "^1.8.6", + "typescript": "^5.1.6" + }, + "dependencies": { + "@medusajs/modules-sdk": "^1.12.5", + "@medusajs/types": "^1.11.9", + "@medusajs/utils": "^1.11.2", + "@mikro-orm/core": "5.9.7", + "@mikro-orm/migrations": "5.9.7", + "@mikro-orm/postgresql": "5.9.7", + "awilix": "^8.0.0", + "dotenv": "^16.1.4", + "knex": "2.4.2" + } +} diff --git a/packages/payment/src/index.ts b/packages/payment/src/index.ts new file mode 100644 index 0000000000..5ec1f1b289 --- /dev/null +++ b/packages/payment/src/index.ts @@ -0,0 +1,23 @@ +import { moduleDefinition } from "./module-definition" +import { Modules } from "@medusajs/modules-sdk" +import { ModulesSdkUtils } from "@medusajs/utils" + +import * as PaymentModels from "@models" + +export default moduleDefinition + +const migrationScriptOptions = { + moduleName: Modules.PAYMENT, + models: PaymentModels, + pathToMigrations: __dirname + "/migrations", +} + +export const runMigrations = ModulesSdkUtils.buildMigrationScript( + migrationScriptOptions +) +export const revertMigration = ModulesSdkUtils.buildRevertMigrationScript( + migrationScriptOptions +) + +export * from "./initialize" +export * from "./loaders" diff --git a/packages/payment/src/initialize/index.ts b/packages/payment/src/initialize/index.ts new file mode 100644 index 0000000000..37830dcf8a --- /dev/null +++ b/packages/payment/src/initialize/index.ts @@ -0,0 +1,27 @@ +import { + ExternalModuleDeclaration, + InternalModuleDeclaration, + MedusaModule, + MODULE_PACKAGE_NAMES, + Modules, +} from "@medusajs/modules-sdk" +import { IPaymentModuleService, ModulesSdkTypes } from "@medusajs/types" +import { moduleDefinition } from "../module-definition" +import { InitializeModuleInjectableDependencies } from "../types" + +export const initialize = async ( + options?: ModulesSdkTypes.ModuleBootstrapDeclaration, + injectedDependencies?: InitializeModuleInjectableDependencies +): Promise => { + const loaded = await MedusaModule.bootstrap({ + moduleKey: Modules.PAYMENT, + defaultPath: MODULE_PACKAGE_NAMES[Modules.PAYMENT], + declaration: options as + | InternalModuleDeclaration + | ExternalModuleDeclaration, + injectedDependencies, + moduleExports: moduleDefinition, + }) + + return loaded[Modules.PAYMENT] +} diff --git a/packages/payment/src/joiner-config.ts b/packages/payment/src/joiner-config.ts new file mode 100644 index 0000000000..a04381a6ef --- /dev/null +++ b/packages/payment/src/joiner-config.ts @@ -0,0 +1,31 @@ +import { Modules } from "@medusajs/modules-sdk" +import { ModuleJoinerConfig } from "@medusajs/types" +import { MapToConfig } from "@medusajs/utils" +import { Payment } from "@models" + +export const LinkableKeys = { + payment_id: Payment.name, +} + +const entityLinkableKeysMap: MapToConfig = {} +Object.entries(LinkableKeys).forEach(([key, value]) => { + entityLinkableKeysMap[value] ??= [] + entityLinkableKeysMap[value].push({ + mapTo: key, + valueFrom: key.split("_").pop()!, + }) +}) + +export const entityNameToLinkableKeysMap: MapToConfig = entityLinkableKeysMap + +export const joinerConfig: ModuleJoinerConfig = { + serviceName: Modules.PAYMENT, + primaryKeys: ["id"], + linkableKeys: LinkableKeys, + alias: { + name: ["payment", "payments"], + args: { + entity: Payment.name, + }, + }, +} diff --git a/packages/payment/src/loaders/connection.ts b/packages/payment/src/loaders/connection.ts new file mode 100644 index 0000000000..97eec362af --- /dev/null +++ b/packages/payment/src/loaders/connection.ts @@ -0,0 +1,34 @@ +import { + InternalModuleDeclaration, + LoaderOptions, + Modules, +} from "@medusajs/modules-sdk" +import { ModulesSdkTypes } from "@medusajs/types" +import { ModulesSdkUtils } from "@medusajs/utils" +import { EntitySchema } from "@mikro-orm/core" +import * as PaymentModels from "../models" + +export default async ( + { + options, + container, + logger, + }: LoaderOptions< + | ModulesSdkTypes.ModuleServiceInitializeOptions + | ModulesSdkTypes.ModuleServiceInitializeCustomDataLayerOptions + >, + moduleDeclaration?: InternalModuleDeclaration +): Promise => { + const entities = Object.values(PaymentModels) as unknown as EntitySchema[] + const pathToMigrations = __dirname + "/../migrations" + + await ModulesSdkUtils.mikroOrmConnectionLoader({ + moduleName: Modules.PAYMENT, + entities, + container, + options, + moduleDeclaration, + logger, + pathToMigrations, + }) +} diff --git a/packages/payment/src/loaders/container.ts b/packages/payment/src/loaders/container.ts new file mode 100644 index 0000000000..ba1b855344 --- /dev/null +++ b/packages/payment/src/loaders/container.ts @@ -0,0 +1,39 @@ +import { LoaderOptions } from "@medusajs/modules-sdk" +import { ModulesSdkTypes } from "@medusajs/types" +import { loadCustomRepositories } from "@medusajs/utils" +import { asClass } from "awilix" + +import * as defaultRepositories from "@repositories" +import * as defaultServices from "@services" + +export default async ({ + container, + options, +}: LoaderOptions< + | ModulesSdkTypes.ModuleServiceInitializeOptions + | ModulesSdkTypes.ModuleServiceInitializeCustomDataLayerOptions +>): Promise => { + const customRepositories = ( + options as ModulesSdkTypes.ModuleServiceInitializeCustomDataLayerOptions + )?.repositories + + container.register({ + // paymentService: asClass(defaultServices.PaymentService).singleton(), + }) + + if (customRepositories) { + loadCustomRepositories({ + defaultRepositories, + customRepositories, + container, + }) + } else { + loadDefaultRepositories({ container }) + } +} + +function loadDefaultRepositories({ container }) { + container.register({ + baseRepository: asClass(defaultRepositories.BaseRepository).singleton(), + }) +} diff --git a/packages/payment/src/loaders/index.ts b/packages/payment/src/loaders/index.ts new file mode 100644 index 0000000000..3614963d8c --- /dev/null +++ b/packages/payment/src/loaders/index.ts @@ -0,0 +1,2 @@ +export * from "./connection" +export * from "./container" diff --git a/packages/payment/src/models/index.ts b/packages/payment/src/models/index.ts new file mode 100644 index 0000000000..6810edbc6f --- /dev/null +++ b/packages/payment/src/models/index.ts @@ -0,0 +1 @@ +export { default as Payment } from "./payment" diff --git a/packages/payment/src/models/payment.ts b/packages/payment/src/models/payment.ts new file mode 100644 index 0000000000..f4fee2bd64 --- /dev/null +++ b/packages/payment/src/models/payment.ts @@ -0,0 +1,40 @@ +import { + BeforeCreate, + Entity, + OnInit, + PrimaryKey, + Property, +} from "@mikro-orm/core" + +import { generateEntityId } from "@medusajs/utils" + +@Entity({ tableName: "payment" }) +export default class Payment { + @PrimaryKey({ columnType: "text" }) + id: string + + @Property({ + onCreate: () => new Date(), + columnType: "timestamptz", + defaultRaw: "now()", + }) + created_at: Date + + @Property({ + onCreate: () => new Date(), + onUpdate: () => new Date(), + columnType: "timestamptz", + defaultRaw: "now()", + }) + updated_at: Date + + @BeforeCreate() + onCreate() { + this.id = generateEntityId(this.id, "pay") + } + + @OnInit() + onInit() { + this.id = generateEntityId(this.id, "pay") + } +} diff --git a/packages/payment/src/module-definition.ts b/packages/payment/src/module-definition.ts new file mode 100644 index 0000000000..543a6ef254 --- /dev/null +++ b/packages/payment/src/module-definition.ts @@ -0,0 +1,14 @@ +import { ModuleExports } from "@medusajs/types" + +import { PaymentModuleService } from "@services" + +import loadConnection from "./loaders/connection" +import loadContainer from "./loaders/container" + +const service = PaymentModuleService +const loaders = [loadContainer, loadConnection] as any + +export const moduleDefinition: ModuleExports = { + service, + loaders, +} diff --git a/packages/payment/src/repositories/index.ts b/packages/payment/src/repositories/index.ts new file mode 100644 index 0000000000..147c9cc259 --- /dev/null +++ b/packages/payment/src/repositories/index.ts @@ -0,0 +1 @@ +export { MikroOrmBaseRepository as BaseRepository } from "@medusajs/utils" diff --git a/packages/payment/src/scripts/bin/run-seed.ts b/packages/payment/src/scripts/bin/run-seed.ts new file mode 100644 index 0000000000..ab7eacb3f3 --- /dev/null +++ b/packages/payment/src/scripts/bin/run-seed.ts @@ -0,0 +1,33 @@ +#!/usr/bin/env node + +import { EOL } from "os" +import { ModulesSdkUtils } from "@medusajs/utils" +import { Modules } from "@medusajs/modules-sdk" + +import * as PaymentModels from "@models" + +import { createPayments } from "../seed-utils" + +const args = process.argv +const path = args.pop() as string + +export default (async () => { + const { config } = await import("dotenv") + config() + if (!path) { + throw new Error( + `filePath is required.${EOL}Example: medusa-payment-seed ` + ) + } + + const run = ModulesSdkUtils.buildSeedScript({ + moduleName: Modules.PAYMENT, + models: PaymentModels, + pathToMigrations: __dirname + "/../../migrations", + seedHandler: async ({ manager, data }) => { + const { paymentsData } = data + await createPayments(manager, paymentsData) + }, + }) + await run({ path }) +})() diff --git a/packages/payment/src/scripts/seed-utils.ts b/packages/payment/src/scripts/seed-utils.ts new file mode 100644 index 0000000000..a507d0b6bc --- /dev/null +++ b/packages/payment/src/scripts/seed-utils.ts @@ -0,0 +1,8 @@ +import { SqlEntityManager } from "@mikro-orm/postgresql" + +export async function createPayments( + manager: SqlEntityManager, + paymentsData: any[] +): Promise { + return [] +} diff --git a/packages/payment/src/services/__tests__/index.spec.ts b/packages/payment/src/services/__tests__/index.spec.ts new file mode 100644 index 0000000000..728f6245c6 --- /dev/null +++ b/packages/payment/src/services/__tests__/index.spec.ts @@ -0,0 +1,5 @@ +describe("Noop test", () => { + it("noop check", async () => { + expect(true).toBe(true) + }) +}) diff --git a/packages/payment/src/services/index.ts b/packages/payment/src/services/index.ts new file mode 100644 index 0000000000..a4f5ea37ae --- /dev/null +++ b/packages/payment/src/services/index.ts @@ -0,0 +1 @@ +export { default as PaymentModuleService } from "./payment-module" diff --git a/packages/payment/src/services/payment-module.ts b/packages/payment/src/services/payment-module.ts new file mode 100644 index 0000000000..9dc1a7a406 --- /dev/null +++ b/packages/payment/src/services/payment-module.ts @@ -0,0 +1,29 @@ +import { + DAL, + InternalModuleDeclaration, + ModuleJoinerConfig, +} from "@medusajs/types" + +import { Payment } from "@models" + +import { joinerConfig } from "../joiner-config" + +type InjectedDependencies = { + baseRepository: DAL.RepositoryService +} + +// TODO: implement IPaymentModule +export default class PaymentModule { + protected baseRepository_: DAL.RepositoryService + + constructor( + { baseRepository }: InjectedDependencies, + protected readonly moduleDeclaration: InternalModuleDeclaration + ) { + this.baseRepository_ = baseRepository + } + + __joinerConfig(): ModuleJoinerConfig { + return joinerConfig + } +} diff --git a/packages/payment/src/types/index.ts b/packages/payment/src/types/index.ts new file mode 100644 index 0000000000..0f252977b0 --- /dev/null +++ b/packages/payment/src/types/index.ts @@ -0,0 +1,5 @@ +import { Logger } from "@medusajs/types" + +export type InitializeModuleInjectableDependencies = { + logger?: Logger +} diff --git a/packages/payment/tsconfig.json b/packages/payment/tsconfig.json new file mode 100644 index 0000000000..213e38fc55 --- /dev/null +++ b/packages/payment/tsconfig.json @@ -0,0 +1,36 @@ +{ + "compilerOptions": { + "lib": ["es2020"], + "target": "es2020", + "outDir": "./dist", + "esModuleInterop": true, + "declaration": true, + "module": "commonjs", + "moduleResolution": "node", + "emitDecoratorMetadata": true, + "experimentalDecorators": true, + "sourceMap": false, + "noImplicitReturns": true, + "strictNullChecks": true, + "strictFunctionTypes": true, + "noImplicitThis": true, + "allowJs": true, + "skipLibCheck": true, + "downlevelIteration": true, // to use ES5 specific tooling + "baseUrl": ".", + "resolveJsonModule": true, + "paths": { + "@models": ["./src/models"], + "@services": ["./src/services"], + "@repositories": ["./src/repositories"] + } + }, + "include": ["src"], + "exclude": [ + "dist", + "./src/**/__tests__", + "./src/**/__mocks__", + "./src/**/__fixtures__", + "node_modules" + ] +} diff --git a/packages/payment/tsconfig.spec.json b/packages/payment/tsconfig.spec.json new file mode 100644 index 0000000000..48e47e8cbb --- /dev/null +++ b/packages/payment/tsconfig.spec.json @@ -0,0 +1,8 @@ +{ + "extends": "./tsconfig.json", + "include": ["src", "integration-tests"], + "exclude": ["node_modules", "dist"], + "compilerOptions": { + "sourceMap": true + } +} diff --git a/packages/types/src/index.ts b/packages/types/src/index.ts index e9ed92dcbd..823b2eada2 100644 --- a/packages/types/src/index.ts +++ b/packages/types/src/index.ts @@ -16,6 +16,7 @@ export * from "./logger" export * from "./modules-sdk" export * from "./pricing" export * from "./product" +export * from "./payment" export * from "./product-category" export * from "./promotion" export * from "./region" @@ -25,4 +26,3 @@ export * from "./shared-context" export * from "./stock-location" export * from "./transaction-base" export * from "./workflow" - diff --git a/packages/types/src/payment/index.ts b/packages/types/src/payment/index.ts new file mode 100644 index 0000000000..9376fea807 --- /dev/null +++ b/packages/types/src/payment/index.ts @@ -0,0 +1 @@ +export * from "./service" diff --git a/packages/types/src/payment/service.ts b/packages/types/src/payment/service.ts new file mode 100644 index 0000000000..80c70e30da --- /dev/null +++ b/packages/types/src/payment/service.ts @@ -0,0 +1,3 @@ +import { IModuleService } from "../modules-sdk" + +export interface IPaymentModuleService extends IModuleService {} diff --git a/yarn.lock b/yarn.lock index 1a9b7e1666..3edcaca246 100644 --- a/yarn.lock +++ b/yarn.lock @@ -8358,6 +8358,33 @@ __metadata: languageName: unknown linkType: soft +"@medusajs/payment@workspace:packages/payment": + version: 0.0.0-use.local + resolution: "@medusajs/payment@workspace:packages/payment" + dependencies: + "@medusajs/modules-sdk": ^1.12.5 + "@medusajs/types": ^1.11.9 + "@medusajs/utils": ^1.11.2 + "@mikro-orm/cli": 5.9.7 + "@mikro-orm/core": 5.9.7 + "@mikro-orm/migrations": 5.9.7 + "@mikro-orm/postgresql": 5.9.7 + awilix: ^8.0.0 + cross-env: ^5.2.1 + dotenv: ^16.1.4 + jest: ^29.6.3 + knex: 2.4.2 + medusa-test-utils: ^1.1.40 + rimraf: ^3.0.2 + ts-jest: ^29.1.1 + ts-node: ^10.9.1 + tsc-alias: ^1.8.6 + typescript: ^5.1.6 + bin: + medusa-payment-seed: dist/scripts/bin/run-seed.js + languageName: unknown + linkType: soft + "@medusajs/pricing@workspace:^, @medusajs/pricing@workspace:packages/pricing": version: 0.0.0-use.local resolution: "@medusajs/pricing@workspace:packages/pricing"