Feat/improve overview of gift cards (#297)
* modified list-gift-card * added search with relations to gift cards to allow for proper lookup * removed new line * created skeleton for testing * began impl. tests * added more test * finished tests * reverted failed changes * updated tests to accomodate re-introduced additions * removed VSCode settings file * updated according to comments * corrected use of findOneWithRelations for gift-card * corrected parsing to be postgresql instead of an if-statement for gift-card * changed bits and pieces
This commit is contained in:
committed by
GitHub
parent
0c96813e2f
commit
92caff845e
@@ -33,7 +33,10 @@ export const defaultFields = [
|
||||
"metadata",
|
||||
]
|
||||
|
||||
export const defaultRelations = ["region"]
|
||||
export const defaultRelations = [
|
||||
"region",
|
||||
"order",
|
||||
]
|
||||
|
||||
export const allowedFields = [
|
||||
"id",
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import { MedusaError, Validator } from "medusa-core-utils"
|
||||
import { defaultFields, defaultRelations } from "./"
|
||||
|
||||
/**
|
||||
@@ -21,14 +22,23 @@ import { defaultFields, defaultRelations } from "./"
|
||||
*/
|
||||
export default async (req, res) => {
|
||||
try {
|
||||
const limit = parseInt(req.query.limit) || 50
|
||||
const offset = parseInt(req.query.offset) || 0
|
||||
|
||||
const selector = {}
|
||||
|
||||
if ("q" in req.query) {
|
||||
selector.q = req.query.q
|
||||
}
|
||||
|
||||
const giftCardService = req.scope.resolve("giftCardService")
|
||||
|
||||
const giftCards = await giftCardService.list(selector, {
|
||||
select: defaultFields,
|
||||
relations: defaultRelations,
|
||||
order: { created_at: "DESC" },
|
||||
limit: limit,
|
||||
skip: offset,
|
||||
})
|
||||
|
||||
res.status(200).json({ gift_cards: giftCards })
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import { MedusaError, Validator } from "medusa-core-utils"
|
||||
import { defaultFields, defaultRelations } from "./"
|
||||
|
||||
/**
|
||||
* @oas [post] /gift-cards/{id}
|
||||
|
||||
@@ -1,5 +1,61 @@
|
||||
import { EntityRepository, Repository } from "typeorm"
|
||||
import { flatten, groupBy, map, merge } from "lodash"
|
||||
import { EntityRepository, FindManyOptions, Repository } from "typeorm"
|
||||
import { GiftCard } from "../models/gift-card"
|
||||
|
||||
@EntityRepository(GiftCard)
|
||||
export class GiftCardRepository extends Repository<GiftCard> {}
|
||||
export class GiftCardRepository extends Repository<GiftCard> {
|
||||
public async findWithRelations(
|
||||
relations: Array<keyof GiftCard> = [],
|
||||
idsOrOptionsWithoutRelations: Omit<
|
||||
FindManyOptions<GiftCard>,
|
||||
"relations"
|
||||
> = {}
|
||||
): Promise<GiftCard[]> {
|
||||
let entities
|
||||
if (Array.isArray(idsOrOptionsWithoutRelations)) {
|
||||
entities = await this.findByIds(idsOrOptionsWithoutRelations)
|
||||
} else {
|
||||
entities = await this.find(idsOrOptionsWithoutRelations)
|
||||
}
|
||||
const entitiesIds = entities.map(({ id }) => id)
|
||||
|
||||
const groupedRelations = {}
|
||||
for (const rel of relations) {
|
||||
const [topLevel] = rel.split(".")
|
||||
if (groupedRelations[topLevel]) {
|
||||
groupedRelations[topLevel].push(rel)
|
||||
} else {
|
||||
groupedRelations[topLevel] = [rel]
|
||||
}
|
||||
}
|
||||
|
||||
const entitiesIdsWithRelations = await Promise.all(
|
||||
Object.entries(groupedRelations).map(([_, rels]) => {
|
||||
return this.findByIds(entitiesIds, {
|
||||
select: ["id"],
|
||||
relations: rels as string[],
|
||||
})
|
||||
})
|
||||
).then(flatten)
|
||||
const entitiesAndRelations = entitiesIdsWithRelations.concat(entities)
|
||||
|
||||
const entitiesAndRelationsById = groupBy(entitiesAndRelations, "id")
|
||||
return map(entitiesAndRelationsById, entityAndRelations =>
|
||||
merge({}, ...entityAndRelations)
|
||||
)
|
||||
}
|
||||
|
||||
public async findOneWithRelations(
|
||||
relations: Array<keyof GiftCard> = [],
|
||||
optionsWithoutRelations: Omit<FindManyOptions<GiftCard>, "relations"> = {}
|
||||
): Promise<GiftCard> {
|
||||
// Limit 1
|
||||
optionsWithoutRelations.take = 1
|
||||
|
||||
const result = await this.findWithRelations(
|
||||
relations,
|
||||
optionsWithoutRelations
|
||||
)
|
||||
return result[0]
|
||||
}
|
||||
}
|
||||
|
||||
256
packages/medusa/src/services/__tests__/gift-card.js
Normal file
256
packages/medusa/src/services/__tests__/gift-card.js
Normal file
@@ -0,0 +1,256 @@
|
||||
import { IdMap, MockManager, MockRepository } from "medusa-test-utils"
|
||||
|
||||
import GiftCardService from "../gift-card"
|
||||
|
||||
describe("GiftCardService", () => {
|
||||
const eventBusService = {
|
||||
emit: jest.fn(),
|
||||
withTransaction: function() {
|
||||
return this
|
||||
},
|
||||
}
|
||||
|
||||
describe("create", () => {
|
||||
const giftCardRepo = MockRepository({
|
||||
create: s => {
|
||||
return Promise.resolve(s)
|
||||
},
|
||||
save: s => {
|
||||
return Promise.resolve(s)
|
||||
},
|
||||
})
|
||||
|
||||
const regionService = {
|
||||
withTransaction: function() {
|
||||
return this
|
||||
},
|
||||
retrieve: () => {
|
||||
return Promise.resolve({
|
||||
id: IdMap.getId("region-id"),
|
||||
})
|
||||
},
|
||||
}
|
||||
|
||||
const giftCardService = new GiftCardService({
|
||||
manager: MockManager,
|
||||
giftCardRepository: giftCardRepo,
|
||||
regionService: regionService,
|
||||
eventBusService: eventBusService,
|
||||
})
|
||||
|
||||
const giftCard = {
|
||||
region_id: IdMap.getId("region-id"),
|
||||
order_id: IdMap.getId("order-id"),
|
||||
is_disabled: true,
|
||||
}
|
||||
|
||||
beforeEach(async () => {
|
||||
jest.clearAllMocks()
|
||||
})
|
||||
|
||||
it("correctly creates a giftcard", async () => {
|
||||
await giftCardService.create(giftCard)
|
||||
|
||||
expect(giftCardRepo.create).toHaveBeenCalledTimes(1)
|
||||
expect(giftCardRepo.create).toHaveBeenCalledWith({
|
||||
region_id: IdMap.getId("region-id"),
|
||||
order_id: IdMap.getId("order-id"),
|
||||
is_disabled: true,
|
||||
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", () => {
|
||||
const giftCardRepo = MockRepository({
|
||||
findOneWithRelations: () => {
|
||||
return Promise.resolve({})
|
||||
},
|
||||
})
|
||||
|
||||
beforeEach(async () => {
|
||||
jest.clearAllMocks()
|
||||
})
|
||||
|
||||
const giftCardService = new GiftCardService({
|
||||
manager: MockManager,
|
||||
giftCardRepository: giftCardRepo,
|
||||
})
|
||||
|
||||
it("it calls order model functions", async () => {
|
||||
await giftCardService.retrieve(IdMap.getId("gift-card"), {
|
||||
relations: ["region"],
|
||||
select: ["id"],
|
||||
})
|
||||
|
||||
expect(giftCardRepo.findOneWithRelations).toHaveBeenCalledTimes(1)
|
||||
expect(giftCardRepo.findOneWithRelations).toHaveBeenCalledWith(
|
||||
["region"],
|
||||
{
|
||||
where: {
|
||||
id: IdMap.getId("gift-card"),
|
||||
},
|
||||
select: ["id"],
|
||||
}
|
||||
)
|
||||
})
|
||||
})
|
||||
|
||||
describe("retrieveByCode", () => {
|
||||
const giftCardRepo = MockRepository({
|
||||
findOneWithRelations: () => {
|
||||
return Promise.resolve({})
|
||||
},
|
||||
})
|
||||
|
||||
beforeEach(async () => {
|
||||
jest.clearAllMocks()
|
||||
})
|
||||
|
||||
const giftCardService = new GiftCardService({
|
||||
manager: MockManager,
|
||||
giftCardRepository: giftCardRepo,
|
||||
})
|
||||
|
||||
it("it calls order model functions", async () => {
|
||||
await giftCardService.retrieveByCode("1234-1234-1234-1234", {
|
||||
relations: ["region"],
|
||||
select: ["id"],
|
||||
})
|
||||
|
||||
expect(giftCardRepo.findOneWithRelations).toHaveBeenCalledTimes(1)
|
||||
expect(giftCardRepo.findOneWithRelations).toHaveBeenCalledWith(
|
||||
["region"],
|
||||
{
|
||||
where: {
|
||||
code: "1234-1234-1234-1234",
|
||||
},
|
||||
select: ["id"],
|
||||
}
|
||||
)
|
||||
})
|
||||
})
|
||||
|
||||
describe("update", () => {
|
||||
const giftCard = {
|
||||
region_id: IdMap.getId("region-id"),
|
||||
order_id: IdMap.getId("order-id"),
|
||||
is_disabled: true,
|
||||
value: 5000,
|
||||
}
|
||||
|
||||
const giftCardRepo = MockRepository({
|
||||
findOneWithRelations: s => {
|
||||
return Promise.resolve(giftCard)
|
||||
},
|
||||
save: s => {
|
||||
return Promise.resolve(s)
|
||||
},
|
||||
})
|
||||
|
||||
const regionService = {
|
||||
withTransaction: function() {
|
||||
return this
|
||||
},
|
||||
retrieve: () => {
|
||||
return Promise.resolve({
|
||||
id: IdMap.getId("other-region"),
|
||||
})
|
||||
},
|
||||
}
|
||||
|
||||
const giftCardService = new GiftCardService({
|
||||
manager: MockManager,
|
||||
giftCardRepository: giftCardRepo,
|
||||
regionService: regionService,
|
||||
})
|
||||
|
||||
beforeEach(async () => {
|
||||
jest.clearAllMocks()
|
||||
})
|
||||
|
||||
it("calls order model functions", async () => {
|
||||
await giftCardService.update(IdMap.getId("giftcard-id"), {
|
||||
is_disabled: false,
|
||||
region_id: IdMap.getId("other-region"),
|
||||
})
|
||||
|
||||
expect(giftCardRepo.save).toHaveBeenCalledTimes(1)
|
||||
expect(giftCardRepo.save).toHaveBeenCalledWith({
|
||||
region_id: IdMap.getId("other-region"),
|
||||
order_id: IdMap.getId("order-id"),
|
||||
is_disabled: false,
|
||||
value: 5000,
|
||||
})
|
||||
})
|
||||
|
||||
it.each([[-100], [6000]])(
|
||||
"fails to update balance with illegal input '%s'",
|
||||
async input => {
|
||||
await expect(
|
||||
giftCardService.update(IdMap.getId("giftcard-id"), {
|
||||
balance: input,
|
||||
})
|
||||
).rejects.toThrow("new balance is invalid")
|
||||
}
|
||||
)
|
||||
})
|
||||
|
||||
describe("delete", () => {
|
||||
const giftCard = {
|
||||
region_id: IdMap.getId("region-id"),
|
||||
order_id: IdMap.getId("order-id"),
|
||||
}
|
||||
|
||||
const giftCardRepo = MockRepository({
|
||||
findOne: s => {
|
||||
switch (s.where.id) {
|
||||
case IdMap.getId("gift-card"):
|
||||
return Promise.resolve(giftCard)
|
||||
default:
|
||||
return Promise.resolve()
|
||||
}
|
||||
},
|
||||
softRemove: s => {
|
||||
return Promise.resolve()
|
||||
},
|
||||
})
|
||||
|
||||
const giftCardService = new GiftCardService({
|
||||
manager: MockManager,
|
||||
giftCardRepository: giftCardRepo,
|
||||
})
|
||||
|
||||
beforeEach(async () => {
|
||||
jest.clearAllMocks()
|
||||
})
|
||||
|
||||
it("successfully deletes existing gift-card", async () => {
|
||||
await giftCardService.delete(IdMap.getId("gift-card"))
|
||||
|
||||
expect(giftCardRepo.softRemove).toHaveBeenCalledTimes(1)
|
||||
expect(giftCardRepo.softRemove).toHaveBeenCalledWith({
|
||||
region_id: IdMap.getId("region-id"),
|
||||
order_id: IdMap.getId("order-id"),
|
||||
})
|
||||
})
|
||||
|
||||
it("returns if no gift-card found", async () => {
|
||||
await giftCardService.delete(IdMap.getId("other"))
|
||||
|
||||
expect(giftCardRepo.softRemove).toHaveBeenCalledTimes(0)
|
||||
})
|
||||
})
|
||||
})
|
||||
@@ -1,7 +1,8 @@
|
||||
import _ from "lodash"
|
||||
import randomize from "randomatic"
|
||||
import { BaseService } from "medusa-interfaces"
|
||||
import { Validator, MedusaError } from "medusa-core-utils"
|
||||
import { Brackets } from "typeorm"
|
||||
import { MedusaError } from "medusa-core-utils"
|
||||
|
||||
/**
|
||||
* Provides layer to manipulate gift cards.
|
||||
@@ -72,6 +73,7 @@ class GiftCardService extends BaseService {
|
||||
|
||||
/**
|
||||
* @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 }) {
|
||||
@@ -79,8 +81,41 @@ class GiftCardService extends BaseService {
|
||||
this.giftCardRepository_
|
||||
)
|
||||
|
||||
let q
|
||||
if ("q" in selector) {
|
||||
q = selector.q
|
||||
delete selector.q
|
||||
}
|
||||
|
||||
const query = this.buildQuery_(selector, config)
|
||||
return giftCardRepo.find(query)
|
||||
|
||||
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) {
|
||||
@@ -156,7 +191,10 @@ class GiftCardService extends BaseService {
|
||||
query.relations = config.relations
|
||||
}
|
||||
|
||||
const giftCard = await giftCardRepo.findOne(query)
|
||||
const rels = query.relations
|
||||
delete query.relations
|
||||
|
||||
const giftCard = await giftCardRepo.findOneWithRelations(rels, query)
|
||||
|
||||
if (!giftCard) {
|
||||
throw new MedusaError(
|
||||
@@ -185,7 +223,10 @@ class GiftCardService extends BaseService {
|
||||
query.relations = config.relations
|
||||
}
|
||||
|
||||
const giftCard = await giftCardRepo.findOne(query)
|
||||
const rels = query.relations
|
||||
delete query.relations
|
||||
|
||||
const giftCard = await giftCardRepo.findOneWithRelations(rels, query)
|
||||
|
||||
if (!giftCard) {
|
||||
throw new MedusaError(
|
||||
@@ -209,7 +250,7 @@ class GiftCardService extends BaseService {
|
||||
|
||||
const giftCard = await this.retrieve(giftCardId)
|
||||
|
||||
const { region_id, metadata, ...rest } = update
|
||||
const { region_id, metadata, balance, ...rest } = update
|
||||
|
||||
if (region_id && region_id !== giftCard.region_id) {
|
||||
const region = await this.regionService_.retrieve(region_id)
|
||||
@@ -220,6 +261,16 @@ class GiftCardService extends BaseService {
|
||||
giftCard.metadata = await this.setMetadata_(giftCard.id, metadata)
|
||||
}
|
||||
|
||||
if (balance) {
|
||||
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
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user