Skip to content

Commit

Permalink
feat(user): Init user module (#6293)
Browse files Browse the repository at this point in the history
* init user module

* add migration

* update module with latest utils

* pr ready

* make interface types interfaces
  • Loading branch information
pKorsholm authored Feb 6, 2024
1 parent 823b98a commit b2eaac8
Show file tree
Hide file tree
Showing 43 changed files with 1,005 additions and 26 deletions.
68 changes: 42 additions & 26 deletions packages/modules-sdk/src/definitions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,55 +7,58 @@ import {
import { upperCaseFirst } from "@medusajs/utils"

export enum Modules {
LINK = "linkModules",
AUTH = "auth",
CACHE = "cacheService",
CART = "cart",
CUSTOMER = "customer",
EVENT_BUS = "eventBus",
STOCK_LOCATION = "stockLocationService",
INVENTORY = "inventoryService",
CACHE = "cacheService",
PRODUCT = "productService",
LINK = "linkModules",
PAYMENT = "payment",
PRICING = "pricingService",
PRODUCT = "productService",
PROMOTION = "promotion",
AUTH = "auth",
WORKFLOW_ENGINE = "workflows",
SALES_CHANNEL = "salesChannel",
CART = "cart",
CUSTOMER = "customer",
PAYMENT = "payment",
STOCK_LOCATION = "stockLocationService",
USER = "user",
WORKFLOW_ENGINE = "workflows",
REGION = "region",
}

export enum ModuleRegistrationName {
AUTH = "authModuleService",
CACHE = "cacheService",
CART = "cartModuleService",
CUSTOMER = "customerModuleService",
EVENT_BUS = "eventBusModuleService",
STOCK_LOCATION = "stockLocationService",
INVENTORY = "inventoryService",
CACHE = "cacheService",
PRODUCT = "productModuleService",
PAYMENT = "paymentModuleService",
PRICING = "pricingModuleService",
PRODUCT = "productModuleService",
PROMOTION = "promotionModuleService",
AUTH = "authModuleService",
WORKFLOW_ENGINE = "workflowsModuleService",
SALES_CHANNEL = "salesChannelModuleService",
CART = "cartModuleService",
CUSTOMER = "customerModuleService",
PAYMENT = "paymentModuleService",
STOCK_LOCATION = "stockLocationService",
USER = "userModuleService",
WORKFLOW_ENGINE = "workflowsModuleService",
REGION = "regionModuleService",
}

export const MODULE_PACKAGE_NAMES = {
[Modules.LINK]: "@medusajs/link-modules",
[Modules.PRODUCT]: "@medusajs/product",
[Modules.AUTH]: "@medusajs/auth",
[Modules.CACHE]: "@medusajs/cache-inmemory",
[Modules.CART]: "@medusajs/cart",
[Modules.CUSTOMER]: "@medusajs/customer",
[Modules.EVENT_BUS]: "@medusajs/event-bus-local",
[Modules.STOCK_LOCATION]: "@medusajs/stock-location",
[Modules.INVENTORY]: "@medusajs/inventory",
[Modules.CACHE]: "@medusajs/cache-inmemory",
[Modules.LINK]: "@medusajs/link-modules",
[Modules.PAYMENT]: "@medusajs/payment",
[Modules.PRICING]: "@medusajs/pricing",
[Modules.PRODUCT]: "@medusajs/product",
[Modules.PROMOTION]: "@medusajs/promotion",
[Modules.AUTH]: "@medusajs/auth",
[Modules.WORKFLOW_ENGINE]: "@medusajs/workflow-engine-inmemory",
[Modules.SALES_CHANNEL]: "@medusajs/sales-channel",
[Modules.CART]: "@medusajs/cart",
[Modules.CUSTOMER]: "@medusajs/customer",
[Modules.PAYMENT]: "@medusajs/payment",
[Modules.STOCK_LOCATION]: "@medusajs/stock-location",
[Modules.USER]: "@medusajs/user",
[Modules.WORKFLOW_ENGINE]: "@medusajs/workflow-engine-inmemory",
[Modules.REGION]: "@medusajs/region",
}

Expand Down Expand Up @@ -231,6 +234,19 @@ export const ModulesDefinition: { [key: string | Modules]: ModuleDefinition } =
resources: MODULE_RESOURCE_TYPE.SHARED,
},
},
[Modules.USER]: {
key: Modules.USER,
registrationName: ModuleRegistrationName.USER,
defaultPackage: false,
label: upperCaseFirst(ModuleRegistrationName.USER),
isRequired: false,
isQueryable: true,
dependencies: ["logger"],
defaultModuleDeclaration: {
scope: MODULE_SCOPE.INTERNAL,
resources: MODULE_RESOURCE_TYPE.SHARED,
},
},
[Modules.REGION]: {
key: Modules.REGION,
registrationName: ModuleRegistrationName.REGION,
Expand Down
1 change: 1 addition & 0 deletions packages/types/src/bundles.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,5 +17,6 @@ export * as SalesChannelTypes from "./sales-channel"
export * as SearchTypes from "./search"
export * as StockLocationTypes from "./stock-location"
export * as TransactionBaseTypes from "./transaction-base"
export * as UserTypes from "./user"
export * as WorkflowTypes from "./workflow"

1 change: 1 addition & 0 deletions packages/types/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,5 +26,6 @@ export * from "./search"
export * from "./shared-context"
export * from "./stock-location"
export * from "./transaction-base"
export * from "./user"
export * from "./workflow"

5 changes: 5 additions & 0 deletions packages/types/src/user/common.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
export type UserDTO = {
id: string
}

export type FilterableUserProps = {}
3 changes: 3 additions & 0 deletions packages/types/src/user/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
export * from "./common"
export * from "./mutations"
export * from "./service"
6 changes: 6 additions & 0 deletions packages/types/src/user/mutations.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
export interface CreateUserDTO {
id?: string
}
export interface UpdateUserDTO {
id: string
}
36 changes: 36 additions & 0 deletions packages/types/src/user/service.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
import { CreateUserDTO, UpdateUserDTO } from "./mutations"
import { FilterableUserProps, UserDTO } from "./common"

import { Context } from "../shared-context"
import { FindConfig } from "../common"
import { IModuleService } from "../modules-sdk"

export interface IUserModuleService extends IModuleService {
retrieve(
id: string,
config?: FindConfig<UserDTO>,
sharedContext?: Context
): Promise<UserDTO>

list(
filters?: FilterableUserProps,
config?: FindConfig<UserDTO>,
sharedContext?: Context
): Promise<UserDTO[]>

listAndCount(
filters?: FilterableUserProps,
config?: FindConfig<UserDTO>,
sharedContext?: Context
): Promise<[UserDTO[], number]>

create(data: CreateUserDTO[], sharedContext?: Context): Promise<UserDTO[]>

create(data: CreateUserDTO, sharedContext?: Context): Promise<UserDTO>

update(data: UpdateUserDTO[], sharedContext?: Context): Promise<UserDTO[]>

update(data: UpdateUserDTO, sharedContext?: Context): Promise<UserDTO>

delete(ids: string[], sharedContext?: Context): Promise<void>
}
6 changes: 6 additions & 0 deletions packages/user/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
/dist
node_modules
.DS_store
.env*
.env
*.sql
3 changes: 3 additions & 0 deletions packages/user/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
# User Module

The User Module is Medusa’s user engine.
16 changes: 16 additions & 0 deletions packages/user/integration-tests/__fixtures__/user.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import { SqlEntityManager } from "@mikro-orm/postgresql"
import { User } from "@models"

export const createUsers = async (
manager: SqlEntityManager,
userData = [{ id: "1" }]
) => {
const users: User[] = []

for (const user of userData) {
const usr = manager.create(User, user)
users.push(usr)
}

await manager.persistAndFlush(users)
}
197 changes: 197 additions & 0 deletions packages/user/integration-tests/__tests__/services/module/user.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,197 @@
import { IUserModuleService } from "@medusajs/types/dist/user"
import { MikroOrmWrapper } from "../../../utils"
import { Modules } from "@medusajs/modules-sdk"
import { SqlEntityManager } from "@mikro-orm/postgresql"
import { createUsers } from "../../../__fixtures__/user"
import { getInitModuleConfig } from "../../../utils/get-init-module-config"
import { initModules } from "medusa-test-utils"

jest.setTimeout(30000)

describe("UserModuleService - User", () => {
let service: IUserModuleService
let testManager: SqlEntityManager
let shutdownFunc: () => Promise<void>

beforeAll(async () => {
const initModulesConfig = getInitModuleConfig()

const { medusaApp, shutdown } = await initModules(initModulesConfig)

service = medusaApp.modules[Modules.USER]

shutdownFunc = shutdown
})

beforeEach(async () => {
await MikroOrmWrapper.setupDatabase()
testManager = MikroOrmWrapper.forkManager()

await createUsers(testManager)
})

afterEach(async () => {
await MikroOrmWrapper.clearDatabase()
})

afterAll(async () => {
await shutdownFunc()
})

describe("listUsers", () => {
it("should list users", async () => {
const users = await service.list()
const serialized = JSON.parse(JSON.stringify(users))

expect(serialized).toEqual([
expect.objectContaining({
id: "1",
}),
])
})

it("should list users by id", async () => {
const users = await service.list({
id: ["1"],
})

expect(users).toEqual([
expect.objectContaining({
id: "1",
}),
])
})
})

describe("listAndCountUsers", () => {
it("should list and count users", async () => {
const [users, count] = await service.listAndCount()
const serialized = JSON.parse(JSON.stringify(users))

expect(count).toEqual(1)
expect(serialized).toEqual([
expect.objectContaining({
id: "1",
}),
])
})

it("should listAndCount Users by id", async () => {
const [Users, count] = await service.listAndCount({
id: "1",
})

expect(count).toEqual(1)
expect(Users).toEqual([
expect.objectContaining({
id: "1",
}),
])
})
})

describe("retrieveUser", () => {
const id = "1"

it("should return an user for the given id", async () => {
const user = await service.retrieve(id)

expect(user).toEqual(
expect.objectContaining({
id,
})
)
})

it("should throw an error when an user with the given id does not exist", async () => {
let error

try {
await service.retrieve("does-not-exist")
} catch (e) {
error = e
}

expect(error.message).toEqual(
"User with id: does-not-exist was not found"
)
})

it("should throw an error when a userId is not provided", async () => {
let error

try {
await service.retrieve(undefined as unknown as string)
} catch (e) {
error = e
}

expect(error.message).toEqual("user - id must be defined")
})

it("should return user based on config select param", async () => {
const User = await service.retrieve(id, {
select: ["id"],
})

const serialized = JSON.parse(JSON.stringify(User))

expect(serialized).toEqual({
id,
})
})
})

describe("deleteUser", () => {
const id = "1"

it("should delete the Users given an id successfully", async () => {
await service.delete([id])

const users = await service.list({
id: [id],
})

expect(users).toHaveLength(0)
})
})

describe("updateUser", () => {
it("should throw an error when a id does not exist", async () => {
let error

try {
await service.update([
{
id: "does-not-exist",
},
])
} catch (e) {
error = e
}

expect(error.message).toEqual('User with id "does-not-exist" not found')
})
})

describe("createUser", () => {
it("should create a User successfully", async () => {
await service.create([
{
id: "2",
},
])

const [User, count] = await service.listAndCount({
id: ["2"],
})

expect(count).toEqual(1)
expect(User[0]).toEqual(
expect.objectContaining({
id: "2",
})
)
})
})
})
Loading

0 comments on commit b2eaac8

Please sign in to comment.