feat(medusa): Convert GiftCardService to Typescript (#1664)
This commit is contained in:
@@ -77,10 +77,9 @@ export class AdminPostGiftCardsReq {
|
||||
@IsBoolean()
|
||||
is_disabled?: boolean
|
||||
|
||||
@IsOptional()
|
||||
@IsString()
|
||||
region_id?: string
|
||||
region_id: string
|
||||
|
||||
@IsOptional()
|
||||
metadata?: object
|
||||
metadata?: Record<string, unknown>
|
||||
}
|
||||
|
||||
@@ -2,14 +2,23 @@ import { Router } from "express"
|
||||
import "reflect-metadata"
|
||||
import { GiftCard } from "../../../.."
|
||||
import { DeleteResponse, PaginatedResponse } from "../../../../types/common"
|
||||
import middlewares from "../../../middlewares"
|
||||
import middlewares, { transformQuery } from "../../../middlewares"
|
||||
import { AdminGetGiftCardsParams } from "./list-gift-cards"
|
||||
|
||||
const route = Router()
|
||||
|
||||
export default (app) => {
|
||||
app.use("/gift-cards", route)
|
||||
|
||||
route.get("/", middlewares.wrap(require("./list-gift-cards").default))
|
||||
route.get(
|
||||
"/",
|
||||
transformQuery(AdminGetGiftCardsParams, {
|
||||
defaultFields: defaultAdminGiftCardFields,
|
||||
defaultRelations: defaultAdminGiftCardRelations,
|
||||
isList: true,
|
||||
}),
|
||||
middlewares.wrap(require("./list-gift-cards").default)
|
||||
)
|
||||
|
||||
route.post("/", middlewares.wrap(require("./create-gift-card").default))
|
||||
|
||||
@@ -22,7 +31,7 @@ export default (app) => {
|
||||
return app
|
||||
}
|
||||
|
||||
export const defaultAdminGiftCardFields = [
|
||||
export const defaultAdminGiftCardFields: (keyof GiftCard)[] = [
|
||||
"id",
|
||||
"code",
|
||||
"value",
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import { Type } from "class-transformer"
|
||||
import { IsInt, IsOptional, IsString } from "class-validator"
|
||||
import { pickBy } from "lodash"
|
||||
import { defaultAdminGiftCardFields, defaultAdminGiftCardRelations } from "."
|
||||
import { GiftCardService } from "../../../../services"
|
||||
import { validator } from "../../../../utils/validator"
|
||||
@@ -27,21 +28,12 @@ import { validator } from "../../../../utils/validator"
|
||||
export default async (req, res) => {
|
||||
const validated = await validator(AdminGetGiftCardsParams, req.query)
|
||||
|
||||
const selector = {}
|
||||
|
||||
if (validated.q && typeof validated.q !== "undefined") {
|
||||
selector["q"] = validated.q
|
||||
}
|
||||
|
||||
const giftCardService: GiftCardService = req.scope.resolve("giftCardService")
|
||||
|
||||
const giftCards = await giftCardService.list(selector, {
|
||||
select: defaultAdminGiftCardFields,
|
||||
relations: defaultAdminGiftCardRelations,
|
||||
order: { created_at: "DESC" },
|
||||
limit: validated.limit,
|
||||
skip: validated.offset,
|
||||
})
|
||||
const giftCards = await giftCardService.list(
|
||||
pickBy(req.filterableFields, (val) => typeof val !== "undefined"),
|
||||
req.listConfig
|
||||
)
|
||||
|
||||
res.status(200).json({
|
||||
gift_cards: giftCards,
|
||||
|
||||
@@ -83,5 +83,5 @@ export class AdminPostGiftCardsGiftCardReq {
|
||||
region_id?: string
|
||||
|
||||
@IsOptional()
|
||||
metadata?: object
|
||||
metadata?: Record<string, unknown>
|
||||
}
|
||||
|
||||
@@ -14,7 +14,12 @@ export default (app) => {
|
||||
|
||||
export const defaultStoreGiftCardRelations = ["region"]
|
||||
|
||||
export const defaultStoreGiftCardFields = ["id", "code", "value", "balance"]
|
||||
export const defaultStoreGiftCardFields: (keyof GiftCard)[] = [
|
||||
"id",
|
||||
"code",
|
||||
"value",
|
||||
"balance",
|
||||
]
|
||||
|
||||
export const allowedStoreGiftCardRelations = ["region"]
|
||||
|
||||
|
||||
@@ -1,15 +1,20 @@
|
||||
import { flatten, groupBy, map, merge } from "lodash"
|
||||
import { EntityRepository, FindManyOptions, Repository } from "typeorm"
|
||||
import { flatten, groupBy, merge } from "lodash"
|
||||
import {
|
||||
Brackets,
|
||||
EntityRepository,
|
||||
FindManyOptions,
|
||||
Repository,
|
||||
} from "typeorm"
|
||||
import { GiftCard } from "../models/gift-card"
|
||||
import { ExtendedFindConfig, QuerySelector, Writable } from "../types/common"
|
||||
|
||||
@EntityRepository(GiftCard)
|
||||
export class GiftCardRepository extends Repository<GiftCard> {
|
||||
public async findWithRelations(
|
||||
relations: Array<keyof GiftCard> = [],
|
||||
idsOrOptionsWithoutRelations: Omit<
|
||||
FindManyOptions<GiftCard>,
|
||||
"relations"
|
||||
> = {}
|
||||
relations: (keyof GiftCard | string)[] = [],
|
||||
idsOrOptionsWithoutRelations:
|
||||
| Omit<FindManyOptions<GiftCard>, "relations">
|
||||
| string[] = {}
|
||||
): Promise<GiftCard[]> {
|
||||
let entities
|
||||
if (Array.isArray(idsOrOptionsWithoutRelations)) {
|
||||
@@ -40,11 +45,47 @@ export class GiftCardRepository extends Repository<GiftCard> {
|
||||
const entitiesAndRelations = entitiesIdsWithRelations.concat(entities)
|
||||
|
||||
const entitiesAndRelationsById = groupBy(entitiesAndRelations, "id")
|
||||
return map(entitiesAndRelationsById, entityAndRelations =>
|
||||
merge({}, ...entityAndRelations)
|
||||
return Object.values(entitiesAndRelationsById).map((v) => merge({}, ...v))
|
||||
}
|
||||
|
||||
protected async queryGiftCards(
|
||||
q: string,
|
||||
where: Partial<Writable<QuerySelector<GiftCard>>>,
|
||||
rels: (keyof GiftCard | string)[]
|
||||
): Promise<GiftCard[]> {
|
||||
const raw = await this.createQueryBuilder("gift_card")
|
||||
.leftJoinAndSelect("gift_card.order", "order")
|
||||
.select(["gift_card.id"])
|
||||
.where(where)
|
||||
.andWhere(
|
||||
new Brackets((qb) => {
|
||||
return qb
|
||||
.where(`gift_card.code ILIKE :q`, { q: `%${q}%` })
|
||||
.orWhere(`display_id::varchar(255) ILIKE :dId`, { dId: `${q}` })
|
||||
})
|
||||
)
|
||||
.getMany()
|
||||
|
||||
return this.findWithRelations(
|
||||
rels,
|
||||
raw.map((i) => i.id)
|
||||
)
|
||||
}
|
||||
|
||||
public async listGiftCards(
|
||||
query: ExtendedFindConfig<GiftCard, QuerySelector<GiftCard>>,
|
||||
rels: (keyof GiftCard | string)[] = [],
|
||||
q?: string
|
||||
): Promise<GiftCard[]> {
|
||||
if (q) {
|
||||
const where = query.where
|
||||
delete where.id
|
||||
|
||||
return await this.queryGiftCards(q, where, rels)
|
||||
}
|
||||
return this.findWithRelations(rels, query)
|
||||
}
|
||||
|
||||
public async findOneWithRelations(
|
||||
relations: Array<keyof GiftCard> = [],
|
||||
optionsWithoutRelations: Omit<FindManyOptions<GiftCard>, "relations"> = {}
|
||||
|
||||
@@ -5,23 +5,23 @@ import GiftCardService from "../gift-card"
|
||||
describe("GiftCardService", () => {
|
||||
const eventBusService = {
|
||||
emit: jest.fn(),
|
||||
withTransaction: function() {
|
||||
withTransaction: function () {
|
||||
return this
|
||||
},
|
||||
}
|
||||
|
||||
describe("create", () => {
|
||||
const giftCardRepo = MockRepository({
|
||||
create: s => {
|
||||
create: (s) => {
|
||||
return Promise.resolve(s)
|
||||
},
|
||||
save: s => {
|
||||
save: (s) => {
|
||||
return Promise.resolve(s)
|
||||
},
|
||||
})
|
||||
|
||||
const regionService = {
|
||||
withTransaction: function() {
|
||||
withTransaction: function () {
|
||||
return this
|
||||
},
|
||||
retrieve: () => {
|
||||
@@ -59,18 +59,6 @@ describe("GiftCardService", () => {
|
||||
code: expect.any(String),
|
||||
})
|
||||
})
|
||||
|
||||
it("fails to create giftcard if no region is provided", async () => {
|
||||
const card = {
|
||||
...giftCard,
|
||||
}
|
||||
|
||||
card.region_id = undefined
|
||||
|
||||
await expect(giftCardService.create(card)).rejects.toThrow(
|
||||
"Gift card is missing region_id"
|
||||
)
|
||||
})
|
||||
})
|
||||
|
||||
describe("retrieve", () => {
|
||||
@@ -152,16 +140,16 @@ describe("GiftCardService", () => {
|
||||
}
|
||||
|
||||
const giftCardRepo = MockRepository({
|
||||
findOneWithRelations: s => {
|
||||
findOneWithRelations: (s) => {
|
||||
return Promise.resolve(giftCard)
|
||||
},
|
||||
save: s => {
|
||||
save: (s) => {
|
||||
return Promise.resolve(s)
|
||||
},
|
||||
})
|
||||
|
||||
const regionService = {
|
||||
withTransaction: function() {
|
||||
withTransaction: function () {
|
||||
return this
|
||||
},
|
||||
retrieve: () => {
|
||||
@@ -198,7 +186,7 @@ describe("GiftCardService", () => {
|
||||
|
||||
it.each([[-100], [6000]])(
|
||||
"fails to update balance with illegal input '%s'",
|
||||
async input => {
|
||||
async (input) => {
|
||||
await expect(
|
||||
giftCardService.update(IdMap.getId("giftcard-id"), {
|
||||
balance: input,
|
||||
@@ -215,7 +203,7 @@ describe("GiftCardService", () => {
|
||||
}
|
||||
|
||||
const giftCardRepo = MockRepository({
|
||||
findOne: s => {
|
||||
findOne: (s) => {
|
||||
switch (s.where.id) {
|
||||
case IdMap.getId("gift-card"):
|
||||
return Promise.resolve(giftCard)
|
||||
@@ -223,7 +211,7 @@ describe("GiftCardService", () => {
|
||||
return Promise.resolve()
|
||||
}
|
||||
},
|
||||
softRemove: s => {
|
||||
softRemove: (s) => {
|
||||
return Promise.resolve()
|
||||
},
|
||||
})
|
||||
|
||||
@@ -1,305 +0,0 @@
|
||||
import { MedusaError } from "medusa-core-utils"
|
||||
import { BaseService } from "medusa-interfaces"
|
||||
import randomize from "randomatic"
|
||||
import { Brackets } from "typeorm"
|
||||
|
||||
/**
|
||||
* Provides layer to manipulate gift cards.
|
||||
* @extends BaseService
|
||||
*/
|
||||
class GiftCardService extends BaseService {
|
||||
static Events = {
|
||||
CREATED: "gift_card.created",
|
||||
}
|
||||
|
||||
constructor({
|
||||
manager,
|
||||
giftCardRepository,
|
||||
giftCardTransactionRepository,
|
||||
regionService,
|
||||
eventBusService,
|
||||
}) {
|
||||
super()
|
||||
|
||||
/** @private @const {EntityManager} */
|
||||
this.manager_ = manager
|
||||
|
||||
/** @private @const {GiftCardRepository} */
|
||||
this.giftCardRepository_ = giftCardRepository
|
||||
|
||||
/** @private @const {GiftCardRepository} */
|
||||
this.giftCardTransactionRepo_ = giftCardTransactionRepository
|
||||
|
||||
/** @private @const {RegionService} */
|
||||
this.regionService_ = regionService
|
||||
|
||||
/** @private @const {EventBus} */
|
||||
this.eventBus_ = eventBusService
|
||||
}
|
||||
|
||||
withTransaction(transactionManager) {
|
||||
if (!transactionManager) {
|
||||
return this
|
||||
}
|
||||
|
||||
const cloned = new GiftCardService({
|
||||
manager: transactionManager,
|
||||
giftCardRepository: this.giftCardRepository_,
|
||||
giftCardTransactionRepository: this.giftCardTransactionRepo_,
|
||||
regionService: this.regionService_,
|
||||
eventBusService: this.eventBus_,
|
||||
})
|
||||
|
||||
cloned.transactionManager_ = transactionManager
|
||||
|
||||
return cloned
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates a 16 character gift card code
|
||||
* @return {string} the generated gift card code
|
||||
*/
|
||||
generateCode_() {
|
||||
const code = [
|
||||
randomize("A0", 4),
|
||||
randomize("A0", 4),
|
||||
randomize("A0", 4),
|
||||
randomize("A0", 4),
|
||||
].join("-")
|
||||
|
||||
return code
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {Object} selector - the query object for find
|
||||
* @param {Object} config - the configuration used to find the objects. contains relations, skip, and take.
|
||||
* @return {Promise} the result of the find operation
|
||||
*/
|
||||
async list(selector = {}, config = { relations: [], skip: 0, take: 10 }) {
|
||||
const giftCardRepo = this.manager_.getCustomRepository(
|
||||
this.giftCardRepository_
|
||||
)
|
||||
|
||||
let q
|
||||
if ("q" in selector) {
|
||||
q = selector.q
|
||||
delete selector.q
|
||||
}
|
||||
|
||||
const query = this.buildQuery_(selector, config)
|
||||
|
||||
const rels = query.relations
|
||||
delete query.relations
|
||||
|
||||
if (q) {
|
||||
const where = query.where
|
||||
delete where.id
|
||||
|
||||
const raw = await giftCardRepo
|
||||
.createQueryBuilder("gift_card")
|
||||
.leftJoinAndSelect("gift_card.order", "order")
|
||||
.select(["gift_card.id"])
|
||||
.where(where)
|
||||
.andWhere(
|
||||
new Brackets((qb) => {
|
||||
return qb
|
||||
.where(`gift_card.code ILIKE :q`, { q: `%${q}%` })
|
||||
.orWhere(`display_id::varchar(255) ILIKE :dId`, { dId: `${q}` })
|
||||
})
|
||||
)
|
||||
.getMany()
|
||||
|
||||
return giftCardRepo.findWithRelations(
|
||||
rels,
|
||||
raw.map((i) => i.id)
|
||||
)
|
||||
}
|
||||
return giftCardRepo.findWithRelations(rels, query)
|
||||
}
|
||||
|
||||
async createTransaction(data) {
|
||||
return this.atomicPhase_(async (manager) => {
|
||||
const gctRepo = manager.getCustomRepository(this.giftCardTransactionRepo_)
|
||||
const created = gctRepo.create(data)
|
||||
const saved = await gctRepo.save(created)
|
||||
return saved.id
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a gift card with provided data given that the data is validated.
|
||||
* @param {GiftCard} giftCard - the gift card data to create
|
||||
* @return {Promise<GiftCard>} the result of the create operation
|
||||
*/
|
||||
async create(giftCard) {
|
||||
return this.atomicPhase_(async (manager) => {
|
||||
const giftCardRepo = manager.getCustomRepository(this.giftCardRepository_)
|
||||
|
||||
if (!giftCard.region_id) {
|
||||
throw new MedusaError(
|
||||
MedusaError.Types.NOT_FOUND,
|
||||
`Gift card is missing region_id`
|
||||
)
|
||||
}
|
||||
|
||||
// Will throw if region does not exist
|
||||
const region = await this.regionService_.retrieve(giftCard.region_id)
|
||||
|
||||
const code = this.generateCode_()
|
||||
|
||||
const toCreate = {
|
||||
code,
|
||||
region_id: region.id,
|
||||
...giftCard,
|
||||
}
|
||||
|
||||
const created = await giftCardRepo.create(toCreate)
|
||||
const result = await giftCardRepo.save(created)
|
||||
|
||||
await this.eventBus_
|
||||
.withTransaction(manager)
|
||||
.emit(GiftCardService.Events.CREATED, {
|
||||
id: result.id,
|
||||
})
|
||||
|
||||
return result
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a gift card by id.
|
||||
* @param {string} giftCardId - id of gift card to retrieve
|
||||
* @param {object} config - optional values to include with gift card query
|
||||
* @return {Promise<GiftCard>} the gift card
|
||||
*/
|
||||
async retrieve(giftCardId, config = {}) {
|
||||
const giftCardRepo = this.manager_.getCustomRepository(
|
||||
this.giftCardRepository_
|
||||
)
|
||||
|
||||
const validatedId = this.validateId_(giftCardId)
|
||||
|
||||
const query = {
|
||||
where: { id: validatedId },
|
||||
}
|
||||
|
||||
if (config.select) {
|
||||
query.select = config.select
|
||||
}
|
||||
|
||||
if (config.relations) {
|
||||
query.relations = config.relations
|
||||
}
|
||||
|
||||
const rels = query.relations
|
||||
delete query.relations
|
||||
|
||||
const giftCard = await giftCardRepo.findOneWithRelations(rels, query)
|
||||
|
||||
if (!giftCard) {
|
||||
throw new MedusaError(
|
||||
MedusaError.Types.NOT_FOUND,
|
||||
`Gift card with ${giftCardId} was not found`
|
||||
)
|
||||
}
|
||||
|
||||
return giftCard
|
||||
}
|
||||
|
||||
async retrieveByCode(code, config = {}) {
|
||||
const giftCardRepo = this.manager_.getCustomRepository(
|
||||
this.giftCardRepository_
|
||||
)
|
||||
|
||||
const query = {
|
||||
where: { code },
|
||||
}
|
||||
|
||||
if (config.select) {
|
||||
query.select = config.select
|
||||
}
|
||||
|
||||
if (config.relations) {
|
||||
query.relations = config.relations
|
||||
}
|
||||
|
||||
const rels = query.relations
|
||||
delete query.relations
|
||||
|
||||
const giftCard = await giftCardRepo.findOneWithRelations(rels, query)
|
||||
|
||||
if (!giftCard) {
|
||||
throw new MedusaError(
|
||||
MedusaError.Types.NOT_FOUND,
|
||||
`Gift card with ${code} was not found`
|
||||
)
|
||||
}
|
||||
|
||||
return giftCard
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates a giftCard.
|
||||
* @param {string} giftCardId - giftCard id of giftCard to update
|
||||
* @param {GiftCard} update - the data to update the giftCard with
|
||||
* @return {Promise} the result of the update operation
|
||||
*/
|
||||
async update(giftCardId, update) {
|
||||
return this.atomicPhase_(async (manager) => {
|
||||
const giftCardRepo = manager.getCustomRepository(this.giftCardRepository_)
|
||||
|
||||
const giftCard = await this.retrieve(giftCardId)
|
||||
|
||||
const { region_id, metadata, balance, ...rest } = update
|
||||
|
||||
if (region_id && region_id !== giftCard.region_id) {
|
||||
const region = await this.regionService_.retrieve(region_id)
|
||||
giftCard.region_id = region.id
|
||||
}
|
||||
|
||||
if (metadata) {
|
||||
giftCard.metadata = await this.setMetadata_(giftCard.id, metadata)
|
||||
}
|
||||
|
||||
if (typeof balance !== "undefined") {
|
||||
if (balance < 0 || giftCard.value < balance) {
|
||||
throw new MedusaError(
|
||||
MedusaError.Types.INVALID_ARGUMENT,
|
||||
"new balance is invalid"
|
||||
)
|
||||
}
|
||||
giftCard.balance = balance
|
||||
}
|
||||
|
||||
for (const [key, value] of Object.entries(rest)) {
|
||||
giftCard[key] = value
|
||||
}
|
||||
|
||||
const updated = await giftCardRepo.save(giftCard)
|
||||
return updated
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* Deletes a gift card idempotently
|
||||
* @param {string} giftCardId - id of gift card to delete
|
||||
* @return {Promise} the result of the delete operation
|
||||
*/
|
||||
async delete(giftCardId) {
|
||||
return this.atomicPhase_(async (manager) => {
|
||||
const giftCardRepo = manager.getCustomRepository(this.giftCardRepository_)
|
||||
|
||||
const giftCard = await giftCardRepo.findOne({ where: { id: giftCardId } })
|
||||
|
||||
if (!giftCard) {
|
||||
return Promise.resolve()
|
||||
}
|
||||
|
||||
await giftCardRepo.softRemove(giftCard)
|
||||
|
||||
return Promise.resolve()
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
export default GiftCardService
|
||||
273
packages/medusa/src/services/gift-card.ts
Normal file
273
packages/medusa/src/services/gift-card.ts
Normal file
@@ -0,0 +1,273 @@
|
||||
import { MedusaError } from "medusa-core-utils"
|
||||
import randomize from "randomatic"
|
||||
import { Brackets, EntityManager, FindOneOptions } from "typeorm"
|
||||
import { EventBusService } from "."
|
||||
import { TransactionBaseService } from "../interfaces"
|
||||
import { GiftCard } from "../models"
|
||||
import { GiftCardRepository } from "../repositories/gift-card"
|
||||
import { GiftCardTransactionRepository } from "../repositories/gift-card-transaction"
|
||||
import {
|
||||
ExtendedFindConfig,
|
||||
FindConfig,
|
||||
QuerySelector,
|
||||
Selector,
|
||||
} from "../types/common"
|
||||
import {
|
||||
CreateGiftCardInput,
|
||||
CreateGiftCardTransactionInput,
|
||||
UpdateGiftCardInput,
|
||||
} from "../types/gift-card"
|
||||
import { buildQuery, setMetadata } from "../utils"
|
||||
import RegionService from "./region"
|
||||
|
||||
type InjectedDependencies = {
|
||||
manager: EntityManager
|
||||
giftCardRepository: typeof GiftCardRepository
|
||||
giftCardTransactionRepository: typeof GiftCardTransactionRepository
|
||||
regionService: RegionService
|
||||
eventBusService: EventBusService
|
||||
}
|
||||
/**
|
||||
* Provides layer to manipulate gift cards.
|
||||
*/
|
||||
class GiftCardService extends TransactionBaseService<GiftCardService> {
|
||||
protected readonly giftCardRepository_: typeof GiftCardRepository
|
||||
protected readonly giftCardTransactionRepo_: typeof GiftCardTransactionRepository
|
||||
protected readonly regionService_: RegionService
|
||||
protected readonly eventBus_: EventBusService
|
||||
|
||||
protected manager_: EntityManager
|
||||
protected transactionManager_: EntityManager | undefined
|
||||
|
||||
static Events = {
|
||||
CREATED: "gift_card.created",
|
||||
}
|
||||
|
||||
constructor({
|
||||
manager,
|
||||
giftCardRepository,
|
||||
giftCardTransactionRepository,
|
||||
regionService,
|
||||
eventBusService,
|
||||
}: InjectedDependencies) {
|
||||
// eslint-disable-next-line prefer-rest-params
|
||||
super(arguments[0])
|
||||
|
||||
this.manager_ = manager
|
||||
|
||||
this.giftCardRepository_ = giftCardRepository
|
||||
this.giftCardTransactionRepo_ = giftCardTransactionRepository
|
||||
this.regionService_ = regionService
|
||||
this.eventBus_ = eventBusService
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates a 16 character gift card code
|
||||
* @return the generated gift card code
|
||||
*/
|
||||
static generateCode(): string {
|
||||
const code = [
|
||||
randomize("A0", 4),
|
||||
randomize("A0", 4),
|
||||
randomize("A0", 4),
|
||||
randomize("A0", 4),
|
||||
].join("-")
|
||||
|
||||
return code
|
||||
}
|
||||
|
||||
/**
|
||||
* @param selector - the query object for find
|
||||
* @param config - the configuration used to find the objects. contains relations, skip, and take.
|
||||
* @return the result of the find operation
|
||||
*/
|
||||
async list(
|
||||
selector: QuerySelector<GiftCard> = {},
|
||||
config: FindConfig<GiftCard> = { relations: [], skip: 0, take: 10 }
|
||||
): Promise<GiftCard[]> {
|
||||
return await this.atomicPhase_(async (manager) => {
|
||||
const giftCardRepo = manager.getCustomRepository(this.giftCardRepository_)
|
||||
|
||||
let q
|
||||
if ("q" in selector) {
|
||||
q = selector.q
|
||||
delete selector.q
|
||||
}
|
||||
|
||||
const query: ExtendedFindConfig<
|
||||
GiftCard,
|
||||
QuerySelector<GiftCard>
|
||||
> = buildQuery<QuerySelector<GiftCard>, GiftCard>(selector, config)
|
||||
|
||||
const rels = query.relations
|
||||
delete query.relations
|
||||
|
||||
return await giftCardRepo.listGiftCards(query, rels, q)
|
||||
})
|
||||
}
|
||||
|
||||
async createTransaction(
|
||||
data: CreateGiftCardTransactionInput
|
||||
): Promise<string> {
|
||||
return await this.atomicPhase_(async (manager) => {
|
||||
const gctRepo = manager.getCustomRepository(this.giftCardTransactionRepo_)
|
||||
const created = gctRepo.create(data)
|
||||
const saved = await gctRepo.save(created)
|
||||
return saved.id
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a gift card with provided data given that the data is validated.
|
||||
* @param giftCard - the gift card data to create
|
||||
* @return the result of the create operation
|
||||
*/
|
||||
async create(giftCard: CreateGiftCardInput): Promise<GiftCard> {
|
||||
return await this.atomicPhase_(async (manager) => {
|
||||
const giftCardRepo = manager.getCustomRepository(this.giftCardRepository_)
|
||||
|
||||
// Will throw if region does not exist
|
||||
const region = await this.regionService_
|
||||
.withTransaction(manager)
|
||||
.retrieve(giftCard.region_id)
|
||||
|
||||
const code = GiftCardService.generateCode()
|
||||
|
||||
const toCreate = {
|
||||
code,
|
||||
...giftCard,
|
||||
region_id: region.id,
|
||||
}
|
||||
|
||||
const created = giftCardRepo.create(toCreate)
|
||||
const result = await giftCardRepo.save(created)
|
||||
|
||||
await this.eventBus_
|
||||
.withTransaction(manager)
|
||||
.emit(GiftCardService.Events.CREATED, {
|
||||
id: result.id,
|
||||
})
|
||||
|
||||
return result
|
||||
})
|
||||
}
|
||||
|
||||
protected async retrieve_(
|
||||
selector: Selector<GiftCard>,
|
||||
config: FindConfig<GiftCard> = {}
|
||||
): Promise<GiftCard> {
|
||||
return await this.atomicPhase_(async (manager) => {
|
||||
const giftCardRepo = manager.getCustomRepository(this.giftCardRepository_)
|
||||
|
||||
const { relations, ...query } = buildQuery(selector, config)
|
||||
|
||||
const giftCard = await giftCardRepo.findOneWithRelations(
|
||||
relations as (keyof GiftCard)[],
|
||||
query
|
||||
)
|
||||
|
||||
if (!giftCard) {
|
||||
const selectorConstraints = Object.entries(selector)
|
||||
.map((key, value) => `${key}: ${value}`)
|
||||
.join(", ")
|
||||
|
||||
throw new MedusaError(
|
||||
MedusaError.Types.NOT_FOUND,
|
||||
`Gift card with ${selectorConstraints} was not found`
|
||||
)
|
||||
}
|
||||
|
||||
return giftCard
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a gift card by id.
|
||||
* @param giftCardId - id of gift card to retrieve
|
||||
* @param config - optional values to include with gift card query
|
||||
* @return the gift card
|
||||
*/
|
||||
async retrieve(
|
||||
giftCardId: string,
|
||||
config: FindConfig<GiftCard> = {}
|
||||
): Promise<GiftCard> {
|
||||
return await this.atomicPhase_(async () => {
|
||||
return await this.retrieve_({ id: giftCardId }, config)
|
||||
})
|
||||
}
|
||||
|
||||
async retrieveByCode(
|
||||
code: string,
|
||||
config: FindConfig<GiftCard> = {}
|
||||
): Promise<GiftCard> {
|
||||
return await this.atomicPhase_(async () => {
|
||||
return await this.retrieve_({ code }, config)
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates a giftCard.
|
||||
* @param giftCardId - giftCard id of giftCard to update
|
||||
* @param update - the data to update the giftCard with
|
||||
* @return the result of the update operation
|
||||
*/
|
||||
async update(
|
||||
giftCardId: string,
|
||||
update: UpdateGiftCardInput
|
||||
): Promise<GiftCard> {
|
||||
return await this.atomicPhase_(async (manager) => {
|
||||
const giftCardRepo = manager.getCustomRepository(this.giftCardRepository_)
|
||||
|
||||
const giftCard = await this.retrieve(giftCardId)
|
||||
|
||||
const { region_id, metadata, balance, ...rest } = update
|
||||
|
||||
if (region_id && region_id !== giftCard.region_id) {
|
||||
const region = await this.regionService_.retrieve(region_id)
|
||||
giftCard.region_id = region.id
|
||||
}
|
||||
|
||||
if (metadata) {
|
||||
giftCard.metadata = setMetadata(giftCard, metadata)
|
||||
}
|
||||
|
||||
if (typeof balance !== "undefined") {
|
||||
if (balance < 0 || giftCard.value < balance) {
|
||||
throw new MedusaError(
|
||||
MedusaError.Types.INVALID_ARGUMENT,
|
||||
"new balance is invalid"
|
||||
)
|
||||
}
|
||||
|
||||
giftCard.balance = balance
|
||||
}
|
||||
|
||||
for (const [key, value] of Object.entries(rest)) {
|
||||
giftCard[key] = value
|
||||
}
|
||||
|
||||
return await giftCardRepo.save(giftCard)
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* Deletes a gift card idempotently
|
||||
* @param giftCardId - id of gift card to delete
|
||||
* @return the result of the delete operation
|
||||
*/
|
||||
async delete(giftCardId: string): Promise<GiftCard | void> {
|
||||
return await this.atomicPhase_(async (manager) => {
|
||||
const giftCardRepo = manager.getCustomRepository(this.giftCardRepository_)
|
||||
|
||||
const giftCard = await giftCardRepo.findOne({ where: { id: giftCardId } })
|
||||
|
||||
if (!giftCard) {
|
||||
return
|
||||
}
|
||||
|
||||
return await giftCardRepo.softRemove(giftCard)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
export default GiftCardService
|
||||
@@ -46,6 +46,8 @@ export type ExtendedFindConfig<
|
||||
relations?: string[]
|
||||
}
|
||||
|
||||
export type QuerySelector<TEntity> = Selector<TEntity> & { q?: string }
|
||||
|
||||
export type Selector<TEntity> = {
|
||||
[key in keyof TEntity]?:
|
||||
| TEntity[key]
|
||||
|
||||
23
packages/medusa/src/types/gift-card.ts
Normal file
23
packages/medusa/src/types/gift-card.ts
Normal file
@@ -0,0 +1,23 @@
|
||||
export type CreateGiftCardInput = {
|
||||
value?: number
|
||||
balance?: number
|
||||
ends_at?: Date
|
||||
is_disabled?: boolean
|
||||
region_id: string
|
||||
metadata?: Record<string, unknown>
|
||||
}
|
||||
|
||||
export type UpdateGiftCardInput = {
|
||||
balance?: number
|
||||
ends_at?: Date
|
||||
is_disabled?: boolean
|
||||
region_id?: string
|
||||
metadata?: Record<string, unknown>
|
||||
}
|
||||
|
||||
export type CreateGiftCardTransactionInput = {
|
||||
gift_card_id: string
|
||||
order_id: string
|
||||
amount: number
|
||||
created_at: Date
|
||||
}
|
||||
Reference in New Issue
Block a user