chore: merge release
This commit is contained in:
@@ -1,7 +1,7 @@
|
||||
import { Router } from "express"
|
||||
import errorHandler from "./middlewares/error-handler"
|
||||
import admin from "./routes/admin"
|
||||
import store from "./routes/store"
|
||||
import errorHandler from "./middlewares/error-handler"
|
||||
|
||||
// guaranteed to get dependencies
|
||||
export default (container, config) => {
|
||||
@@ -14,3 +14,20 @@ export default (container, config) => {
|
||||
|
||||
return app
|
||||
}
|
||||
|
||||
export * from "./routes/admin/notifications"
|
||||
export * from "./routes/admin/store"
|
||||
export * from "./routes/admin/variants"
|
||||
export * from "./routes/store/auth"
|
||||
export * from "./routes/store/carts"
|
||||
export * from "./routes/store/collections"
|
||||
export * from "./routes/store/customers"
|
||||
export * from "./routes/store/gift-cards"
|
||||
export * from "./routes/store/orders"
|
||||
export * from "./routes/store/products"
|
||||
export * from "./routes/store/regions"
|
||||
export * from "./routes/store/return-reasons"
|
||||
export * from "./routes/store/returns"
|
||||
export * from "./routes/store/shipping-options"
|
||||
export * from "./routes/store/swaps"
|
||||
export * from "./routes/store/variants"
|
||||
|
||||
@@ -1 +1,3 @@
|
||||
export default fn => (...args) => fn(...args).catch(args[2])
|
||||
export default (fn) =>
|
||||
(...args) =>
|
||||
fn(...args).catch(args[2])
|
||||
|
||||
@@ -1,25 +0,0 @@
|
||||
import { MedusaError, Validator } from "medusa-core-utils"
|
||||
|
||||
export default async (req, res) => {
|
||||
const schema = Validator.object().keys({
|
||||
application_name: Validator.string().required(),
|
||||
state: Validator.string().required(),
|
||||
code: Validator.string().required(),
|
||||
})
|
||||
|
||||
const { value, error } = schema.validate(req.body)
|
||||
if (error) {
|
||||
throw new MedusaError(MedusaError.Types.INVALID_DATA, error.details)
|
||||
}
|
||||
try {
|
||||
const oauthService = req.scope.resolve("oauthService")
|
||||
const data = await oauthService.generateToken(
|
||||
value.application_name,
|
||||
value.code,
|
||||
value.state
|
||||
)
|
||||
res.status(200).json({ apps: data })
|
||||
} catch (err) {
|
||||
throw err
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,63 @@
|
||||
import { IsNotEmpty, IsString } from "class-validator"
|
||||
import { OauthService } from "../../../../services"
|
||||
import { validator } from "../../../../utils/validator"
|
||||
/**
|
||||
* @oas [post] /apps
|
||||
* operationId: "PostApps"
|
||||
* summary: "Generates a token for an application."
|
||||
* description: "Generates a token for an application."
|
||||
* x-authenticated: true
|
||||
* requestBody:
|
||||
* content:
|
||||
* application/json:
|
||||
* schema:
|
||||
* required:
|
||||
* - application_name
|
||||
* - state
|
||||
* - code
|
||||
* properties:
|
||||
* application_name:
|
||||
* type: string
|
||||
* description: Name of the application for the token to be generated for.
|
||||
* state:
|
||||
* type: string
|
||||
* description: State of the application.
|
||||
* code:
|
||||
* type: string
|
||||
* description: The code for the generated token.
|
||||
* tags:
|
||||
* - Apps
|
||||
* responses:
|
||||
* "200":
|
||||
* description: OK
|
||||
* content:
|
||||
* application/json:
|
||||
* schema:
|
||||
* properties:
|
||||
* apps:
|
||||
* $ref: "#/components/schemas/OAuth"
|
||||
*/
|
||||
export default async (req, res) => {
|
||||
const validated = await validator(AdminPostAppsReq, req.body)
|
||||
const oauthService: OauthService = req.scope.resolve("oauthService")
|
||||
const data = await oauthService.generateToken(
|
||||
validated.application_name,
|
||||
validated.code,
|
||||
validated.state
|
||||
)
|
||||
res.status(200).json({ apps: data })
|
||||
}
|
||||
|
||||
export class AdminPostAppsReq {
|
||||
@IsString()
|
||||
@IsNotEmpty()
|
||||
application_name: string
|
||||
|
||||
@IsString()
|
||||
@IsNotEmpty()
|
||||
state: string
|
||||
|
||||
@IsString()
|
||||
@IsNotEmpty()
|
||||
code: string
|
||||
}
|
||||
+10
-1
@@ -1,9 +1,10 @@
|
||||
import { Router } from "express"
|
||||
import { Oauth } from "../../../.."
|
||||
import middlewares from "../../../middlewares"
|
||||
|
||||
const route = Router()
|
||||
|
||||
export default app => {
|
||||
export default (app) => {
|
||||
app.use("/apps", route)
|
||||
|
||||
route.get("/", middlewares.wrap(require("./list").default))
|
||||
@@ -14,3 +15,11 @@ export default app => {
|
||||
|
||||
return app
|
||||
}
|
||||
|
||||
export type AdminAppsRes = {
|
||||
apps: Oauth
|
||||
}
|
||||
|
||||
export type AdminAppsListRes = {
|
||||
apps: Oauth[]
|
||||
}
|
||||
@@ -1,12 +0,0 @@
|
||||
import { MedusaError, Validator } from "medusa-core-utils"
|
||||
|
||||
export default async (req, res) => {
|
||||
try {
|
||||
const oauthService = req.scope.resolve("oauthService")
|
||||
const data = await oauthService.list({})
|
||||
|
||||
res.status(200).json({ apps: data })
|
||||
} catch (err) {
|
||||
throw err
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
import { OauthService } from "../../../../services"
|
||||
|
||||
/**
|
||||
* @oas [get] /apps
|
||||
* operationId: "GetApps"
|
||||
* summary: "List applications"
|
||||
* description: "Retrieve a list of applications."
|
||||
* x-authenticated: true
|
||||
* tags:
|
||||
* - Apps
|
||||
* responses:
|
||||
* "200":
|
||||
* description: OK
|
||||
* content:
|
||||
* application/json:
|
||||
* schema:
|
||||
* properties:
|
||||
* collection:
|
||||
* $ref: "#/components/schemas/OAuth"
|
||||
*/
|
||||
export default async (req, res) => {
|
||||
const oauthService: OauthService = req.scope.resolve("oauthService")
|
||||
const data = await oauthService.list({})
|
||||
|
||||
res.status(200).json({ apps: data })
|
||||
}
|
||||
@@ -1,53 +0,0 @@
|
||||
import _ from "lodash"
|
||||
import jwt from "jsonwebtoken"
|
||||
import { Validator } from "medusa-core-utils"
|
||||
import config from "../../../../config"
|
||||
|
||||
/**
|
||||
* @oas [post] /auth
|
||||
* operationId: "PostAuth"
|
||||
* summary: "Authenticate a User"
|
||||
* description: "Logs a User in and authorizes them to manage Store settings."
|
||||
* parameters:
|
||||
* - (body) email=* {string} The User's email.
|
||||
* - (body) password=* {string} The User's password.
|
||||
* tags:
|
||||
* - Auth
|
||||
* responses:
|
||||
* "200":
|
||||
* description: OK
|
||||
* content:
|
||||
* application/json:
|
||||
* schema:
|
||||
* properties:
|
||||
* user:
|
||||
* $ref: "#/components/schemas/user"
|
||||
*/
|
||||
export default async (req, res) => {
|
||||
const { body } = req
|
||||
const schema = Validator.object().keys({
|
||||
email: Validator.string().required(),
|
||||
password: Validator.string().required(),
|
||||
})
|
||||
const { value, error } = schema.validate(body)
|
||||
|
||||
if (error) {
|
||||
throw error
|
||||
}
|
||||
|
||||
const authService = req.scope.resolve("authService")
|
||||
const result = await authService.authenticate(value.email, value.password)
|
||||
if (!result.success) {
|
||||
res.sendStatus(401)
|
||||
return
|
||||
}
|
||||
|
||||
// Add JWT to cookie
|
||||
req.session.jwt = jwt.sign({ userId: result.user.id }, config.jwtSecret, {
|
||||
expiresIn: "24h",
|
||||
})
|
||||
|
||||
const cleanRes = _.omit(result.user, ["password_hash"])
|
||||
|
||||
res.json({ user: cleanRes })
|
||||
}
|
||||
@@ -0,0 +1,67 @@
|
||||
import _ from "lodash"
|
||||
import jwt from "jsonwebtoken"
|
||||
import config from "../../../../config"
|
||||
import { validator } from "../../../../utils/validator"
|
||||
import { IsEmail, IsNotEmpty, IsString } from "class-validator"
|
||||
import AuthService from "../../../../services/auth"
|
||||
import { MedusaError } from "medusa-core-utils"
|
||||
|
||||
/**
|
||||
* @oas [post] /auth
|
||||
* operationId: "PostAuth"
|
||||
* summary: "Authenticate a User"
|
||||
* x-authenticated: false
|
||||
* description: "Logs a User in and authorizes them to manage Store settings."
|
||||
* parameters:
|
||||
* - (body) email=* {string} The User's email.
|
||||
* - (body) password=* {string} The User's password.
|
||||
* tags:
|
||||
* - Auth
|
||||
* responses:
|
||||
* "200":
|
||||
* description: OK
|
||||
* content:
|
||||
* application/json:
|
||||
* schema:
|
||||
* properties:
|
||||
* user:
|
||||
* $ref: "#/components/schemas/user"
|
||||
*/
|
||||
export default async (req, res) => {
|
||||
if (!config.jwtSecret) {
|
||||
throw new MedusaError(
|
||||
MedusaError.Types.NOT_FOUND,
|
||||
"Please configure jwtSecret in your environment"
|
||||
)
|
||||
}
|
||||
const validated = await validator(AdminPostAuthReq, req.body)
|
||||
|
||||
const authService: AuthService = req.scope.resolve("authService")
|
||||
const result = await authService.authenticate(
|
||||
validated.email,
|
||||
validated.password
|
||||
)
|
||||
|
||||
if (result.success && result.user) {
|
||||
// Add JWT to cookie
|
||||
req.session.jwt = jwt.sign({ userId: result.user.id }, config.jwtSecret, {
|
||||
expiresIn: "24h",
|
||||
})
|
||||
|
||||
const cleanRes = _.omit(result.user, ["password_hash"])
|
||||
|
||||
res.json({ user: cleanRes })
|
||||
} else {
|
||||
res.sendStatus(401)
|
||||
}
|
||||
}
|
||||
|
||||
export class AdminPostAuthReq {
|
||||
@IsEmail()
|
||||
@IsNotEmpty()
|
||||
email: string
|
||||
|
||||
@IsString()
|
||||
@IsNotEmpty()
|
||||
password: string
|
||||
}
|
||||
+1
-2
@@ -1,9 +1,8 @@
|
||||
import _ from "lodash"
|
||||
|
||||
/**
|
||||
* @oas [get] /auth
|
||||
* operationId: "DeleteAuth"
|
||||
* summary: "Delete Session"
|
||||
* x-authenticated: true
|
||||
* description: "Deletes the current session for the logged in user."
|
||||
* tags:
|
||||
* - Auth
|
||||
+10
-4
@@ -1,9 +1,11 @@
|
||||
import _ from "lodash"
|
||||
import UserService from "../../../../services/user"
|
||||
|
||||
/**
|
||||
* @oas [get] /auth
|
||||
* operationId: "GetAuth"
|
||||
* summary: "Get Session"
|
||||
* x-authenticated: true
|
||||
* description: "Gets the currently logged in User."
|
||||
* tags:
|
||||
* - Auth
|
||||
@@ -18,9 +20,13 @@ import _ from "lodash"
|
||||
* $ref: "#/components/schemas/user"
|
||||
*/
|
||||
export default async (req, res) => {
|
||||
const userService = req.scope.resolve("userService")
|
||||
const user = await userService.retrieve(req.user.userId)
|
||||
try {
|
||||
const userService: UserService = req.scope.resolve("userService")
|
||||
const user = await userService.retrieve(req.user.userId)
|
||||
|
||||
const cleanRes = _.omit(user, ["password_hash"])
|
||||
res.status(200).json({ user: cleanRes })
|
||||
const cleanRes = _.omit(user, ["password_hash"])
|
||||
res.status(200).json({ user: cleanRes })
|
||||
} catch (err) {
|
||||
res.sendStatus(400)
|
||||
}
|
||||
}
|
||||
+10
-1
@@ -1,9 +1,10 @@
|
||||
import { Router } from "express"
|
||||
import { User } from "../../../.."
|
||||
import middlewares from "../../../middlewares"
|
||||
|
||||
const route = Router()
|
||||
|
||||
export default app => {
|
||||
export default (app) => {
|
||||
app.use("/auth", route)
|
||||
|
||||
route.get(
|
||||
@@ -21,3 +22,11 @@ export default app => {
|
||||
|
||||
return app
|
||||
}
|
||||
|
||||
export type AdminAuthRes = {
|
||||
user: Omit<User, "password_hash">
|
||||
}
|
||||
|
||||
export * from "./create-session"
|
||||
export * from "./delete-session"
|
||||
export * from "./get-session"
|
||||
@@ -59,7 +59,9 @@ describe("POST /admin/collections", () => {
|
||||
|
||||
it("returns error details", () => {
|
||||
expect(subject.body.type).toEqual("invalid_data")
|
||||
expect(subject.body.message[0].message).toEqual(`"title" is required`)
|
||||
expect(subject.body.message).toEqual(
|
||||
"title should not be empty, title must be a string"
|
||||
)
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
@@ -22,7 +22,7 @@ describe("GET /admin/collections", () => {
|
||||
})
|
||||
|
||||
it("calls product collection service list", () => {
|
||||
expect(ProductCollectionServiceMock.list).toHaveBeenCalledTimes(1)
|
||||
expect(ProductCollectionServiceMock.listAndCount).toHaveBeenCalledTimes(1)
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
+24
-23
@@ -1,10 +1,12 @@
|
||||
import { MedusaError, Validator } from "medusa-core-utils"
|
||||
|
||||
import { IsNotEmpty, IsObject, IsOptional, IsString } from "class-validator"
|
||||
import ProductCollectionService from "../../../../services/product-collection"
|
||||
import { validator } from "../../../../utils/validator"
|
||||
/**
|
||||
* @oas [post] /collections
|
||||
* operationId: "PostCollections"
|
||||
* summary: "Create a Product Collection"
|
||||
* description: "Creates a Product Collection."
|
||||
* x-authenticated: true
|
||||
* requestBody:
|
||||
* content:
|
||||
* application/json:
|
||||
@@ -34,29 +36,28 @@ import { MedusaError, Validator } from "medusa-core-utils"
|
||||
* $ref: "#/components/schemas/product_collection"
|
||||
*/
|
||||
export default async (req, res) => {
|
||||
const schema = Validator.object().keys({
|
||||
title: Validator.string().required(),
|
||||
handle: Validator.string()
|
||||
.optional()
|
||||
.allow(""),
|
||||
metadata: Validator.object().optional(),
|
||||
})
|
||||
const validated = await validator(AdminPostCollectionsReq, req.body)
|
||||
|
||||
const { value, error } = schema.validate(req.body)
|
||||
if (error) {
|
||||
throw new MedusaError(MedusaError.Types.INVALID_DATA, error.details)
|
||||
}
|
||||
const productCollectionService: ProductCollectionService = req.scope.resolve(
|
||||
"productCollectionService"
|
||||
)
|
||||
|
||||
try {
|
||||
const productCollectionService = req.scope.resolve(
|
||||
"productCollectionService"
|
||||
)
|
||||
const created = await productCollectionService.create(validated)
|
||||
const collection = await productCollectionService.retrieve(created.id)
|
||||
|
||||
const created = await productCollectionService.create(value)
|
||||
const collection = await productCollectionService.retrieve(created.id)
|
||||
res.status(200).json({ collection })
|
||||
}
|
||||
|
||||
res.status(200).json({ collection })
|
||||
} catch (err) {
|
||||
throw err
|
||||
}
|
||||
export class AdminPostCollectionsReq {
|
||||
@IsString()
|
||||
@IsNotEmpty()
|
||||
title: string
|
||||
|
||||
@IsString()
|
||||
@IsOptional()
|
||||
handle?: string
|
||||
|
||||
@IsObject()
|
||||
@IsOptional()
|
||||
metadata?: object
|
||||
}
|
||||
+12
-13
@@ -1,8 +1,11 @@
|
||||
import ProductCollectionService from "../../../../services/product-collection"
|
||||
|
||||
/**
|
||||
* @oas [delete] /collections/{id}
|
||||
* operationId: "DeleteCollectionsCollection"
|
||||
* summary: "Delete a Product Collection"
|
||||
* description: "Deletes a Product Collection."
|
||||
* x-authenticated: true
|
||||
* parameters:
|
||||
* - (path) id=* {string} The id of the Collection.
|
||||
* tags:
|
||||
@@ -26,18 +29,14 @@
|
||||
export default async (req, res) => {
|
||||
const { id } = req.params
|
||||
|
||||
try {
|
||||
const productCollectionService = req.scope.resolve(
|
||||
"productCollectionService"
|
||||
)
|
||||
await productCollectionService.delete(id)
|
||||
const productCollectionService: ProductCollectionService = req.scope.resolve(
|
||||
"productCollectionService"
|
||||
)
|
||||
await productCollectionService.delete(id)
|
||||
|
||||
res.json({
|
||||
id,
|
||||
object: "product-collection",
|
||||
deleted: true,
|
||||
})
|
||||
} catch (err) {
|
||||
throw err
|
||||
}
|
||||
res.json({
|
||||
id,
|
||||
object: "product-collection",
|
||||
deleted: true,
|
||||
})
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
import ProductCollectionService from "../../../../services/product-collection"
|
||||
/**
|
||||
* @oas [get] /collections/{id}
|
||||
* operationId: "GetCollectionsCollection"
|
||||
* summary: "Retrieve a Product Collection"
|
||||
* description: "Retrieves a Product Collection."
|
||||
* x-authenticated: true
|
||||
* parameters:
|
||||
* - (path) id=* {string} The id of the Product Collection
|
||||
* tags:
|
||||
* - Collection
|
||||
* responses:
|
||||
* "200":
|
||||
* description: OK
|
||||
* content:
|
||||
* application/json:
|
||||
* schema:
|
||||
* properties:
|
||||
* collection:
|
||||
* $ref: "#/components/schemas/product_collection"
|
||||
*/
|
||||
export default async (req, res) => {
|
||||
const { id } = req.params
|
||||
|
||||
const productCollectionService: ProductCollectionService = req.scope.resolve(
|
||||
"productCollectionService"
|
||||
)
|
||||
|
||||
const collection = await productCollectionService.retrieve(id)
|
||||
res.status(200).json({ collection })
|
||||
}
|
||||
@@ -1,21 +0,0 @@
|
||||
import { Router } from "express"
|
||||
import middlewares from "../../../middlewares"
|
||||
|
||||
const route = Router()
|
||||
|
||||
export default app => {
|
||||
app.use("/collections", route)
|
||||
|
||||
route.post("/", middlewares.wrap(require("./create-collection").default))
|
||||
route.post("/:id", middlewares.wrap(require("./update-collection").default))
|
||||
|
||||
route.delete("/:id", middlewares.wrap(require("./delete-collection").default))
|
||||
|
||||
route.get("/:id", middlewares.wrap(require("./get-collection").default))
|
||||
route.get("/", middlewares.wrap(require("./list-collections").default))
|
||||
|
||||
return app
|
||||
}
|
||||
|
||||
export const defaultFields = ["id", "title", "handle"]
|
||||
export const defaultRelations = ["products"]
|
||||
@@ -0,0 +1,40 @@
|
||||
import { Router } from "express"
|
||||
import { ProductCollection } from "../../../.."
|
||||
import { DeleteResponse, PaginatedResponse } from "../../../../types/common"
|
||||
import middlewares from "../../../middlewares"
|
||||
import "reflect-metadata"
|
||||
|
||||
const route = Router()
|
||||
|
||||
export default (app) => {
|
||||
app.use("/collections", route)
|
||||
|
||||
route.post("/", middlewares.wrap(require("./create-collection").default))
|
||||
route.post("/:id", middlewares.wrap(require("./update-collection").default))
|
||||
|
||||
route.delete("/:id", middlewares.wrap(require("./delete-collection").default))
|
||||
|
||||
route.get("/:id", middlewares.wrap(require("./get-collection").default))
|
||||
route.get("/", middlewares.wrap(require("./list-collections").default))
|
||||
|
||||
return app
|
||||
}
|
||||
|
||||
export const defaultAdminCollectionsFields = ["id", "title", "handle"]
|
||||
export const defaultAdminCollectionsRelations = ["products"]
|
||||
|
||||
export type AdminCollectionsListRes = PaginatedResponse & {
|
||||
collections: ProductCollection[]
|
||||
}
|
||||
|
||||
export type AdminCollectionsDeleteRes = DeleteResponse
|
||||
|
||||
export type AdminCollectionsRes = {
|
||||
collection: ProductCollection
|
||||
}
|
||||
|
||||
export * from "./create-collection"
|
||||
export * from "./delete-collection"
|
||||
export * from "./get-collection"
|
||||
export * from "./list-collections"
|
||||
export * from "./update-collection"
|
||||
@@ -1,47 +0,0 @@
|
||||
import { defaultFields, defaultRelations } from "."
|
||||
|
||||
/**
|
||||
* @oas [get] /collections
|
||||
* operationId: "GetCollections"
|
||||
* summary: "List Product Collections"
|
||||
* description: "Retrieve a list of Product Collection."
|
||||
* tags:
|
||||
* - Collection
|
||||
* responses:
|
||||
* "200":
|
||||
* description: OK
|
||||
* content:
|
||||
* application/json:
|
||||
* schema:
|
||||
* properties:
|
||||
* collection:
|
||||
* $ref: "#/components/schemas/product_collection"
|
||||
*/
|
||||
export default async (req, res) => {
|
||||
try {
|
||||
const selector = {}
|
||||
|
||||
const limit = parseInt(req.query.limit) || 10
|
||||
const offset = parseInt(req.query.offset) || 0
|
||||
|
||||
const productCollectionService = req.scope.resolve(
|
||||
"productCollectionService"
|
||||
)
|
||||
|
||||
const listConfig = {
|
||||
select: defaultFields,
|
||||
relations: defaultRelations,
|
||||
skip: offset,
|
||||
take: limit,
|
||||
}
|
||||
|
||||
const collections = await productCollectionService.list(
|
||||
selector,
|
||||
listConfig
|
||||
)
|
||||
|
||||
res.status(200).json({ collections })
|
||||
} catch (err) {
|
||||
throw err
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,67 @@
|
||||
import { Type } from "class-transformer"
|
||||
import { IsNumber, IsOptional } from "class-validator"
|
||||
import {
|
||||
defaultAdminCollectionsFields,
|
||||
defaultAdminCollectionsRelations,
|
||||
} from "."
|
||||
import ProductCollectionService from "../../../../services/product-collection"
|
||||
import { validator } from "../../../../utils/validator"
|
||||
/**
|
||||
* @oas [get] /collections
|
||||
* operationId: "GetCollections"
|
||||
* summary: "List Product Collections"
|
||||
* description: "Retrieve a list of Product Collection."
|
||||
* x-authenticated: true
|
||||
* parameters:
|
||||
* - (path) limit {string} The number of collections to return.
|
||||
* - (path) offset {string} The offset of collections to return.
|
||||
* tags:
|
||||
* - Collection
|
||||
* responses:
|
||||
* "200":
|
||||
* description: OK
|
||||
* content:
|
||||
* application/json:
|
||||
* schema:
|
||||
* properties:
|
||||
* collection:
|
||||
* $ref: "#/components/schemas/product_collection"
|
||||
*/
|
||||
export default async (req, res) => {
|
||||
const validated = await validator(AdminGetCollectionsParams, req.query)
|
||||
|
||||
const productCollectionService: ProductCollectionService = req.scope.resolve(
|
||||
"productCollectionService"
|
||||
)
|
||||
|
||||
const listConfig = {
|
||||
select: defaultAdminCollectionsFields,
|
||||
relations: defaultAdminCollectionsRelations,
|
||||
skip: validated.offset,
|
||||
take: validated.limit,
|
||||
}
|
||||
|
||||
const [collections, count] = await productCollectionService.listAndCount(
|
||||
{},
|
||||
listConfig
|
||||
)
|
||||
|
||||
res.status(200).json({
|
||||
collections,
|
||||
count,
|
||||
offset: validated.offset,
|
||||
limit: validated.limit,
|
||||
})
|
||||
}
|
||||
|
||||
export class AdminGetCollectionsParams {
|
||||
@IsNumber()
|
||||
@IsOptional()
|
||||
@Type(() => Number)
|
||||
limit = 10
|
||||
|
||||
@IsNumber()
|
||||
@IsOptional()
|
||||
@Type(() => Number)
|
||||
offset = 0
|
||||
}
|
||||
+23
-21
@@ -1,10 +1,12 @@
|
||||
import { MedusaError, Validator } from "medusa-core-utils"
|
||||
|
||||
import { IsObject, IsOptional, IsString } from "class-validator"
|
||||
import ProductCollectionService from "../../../../services/product-collection"
|
||||
import { validator } from "../../../../utils/validator"
|
||||
/**
|
||||
* @oas [post] /collections/{id}
|
||||
* operationId: "PostCollectionsCollection"
|
||||
* summary: "Update a Product Collection"
|
||||
* description: "Updates a Product Collection."
|
||||
* x-authenticated: true
|
||||
* parameters:
|
||||
* - (path) id=* {string} The id of the Collection.
|
||||
* requestBody:
|
||||
@@ -36,27 +38,27 @@ import { MedusaError, Validator } from "medusa-core-utils"
|
||||
export default async (req, res) => {
|
||||
const { id } = req.params
|
||||
|
||||
const schema = Validator.object().keys({
|
||||
title: Validator.string().optional(),
|
||||
handle: Validator.string().optional(),
|
||||
metadata: Validator.object().optional(),
|
||||
})
|
||||
const validated = await validator(AdminPostCollectionsCollectionReq, req.body)
|
||||
const productCollectionService: ProductCollectionService = req.scope.resolve(
|
||||
"productCollectionService"
|
||||
)
|
||||
|
||||
const { value, error } = schema.validate(req.body)
|
||||
if (error) {
|
||||
throw new MedusaError(MedusaError.Types.INVALID_DATA, error.details)
|
||||
}
|
||||
const updated = await productCollectionService.update(id, validated)
|
||||
const collection = await productCollectionService.retrieve(updated.id)
|
||||
|
||||
try {
|
||||
const productCollectionService = req.scope.resolve(
|
||||
"productCollectionService"
|
||||
)
|
||||
res.status(200).json({ collection })
|
||||
}
|
||||
|
||||
const updated = await productCollectionService.update(id, value)
|
||||
const collection = await productCollectionService.retrieve(updated.id)
|
||||
export class AdminPostCollectionsCollectionReq {
|
||||
@IsString()
|
||||
@IsOptional()
|
||||
title?: string
|
||||
|
||||
res.status(200).json({ collection })
|
||||
} catch (err) {
|
||||
throw err
|
||||
}
|
||||
@IsString()
|
||||
@IsOptional()
|
||||
handle?: string
|
||||
|
||||
@IsObject()
|
||||
@IsOptional()
|
||||
metadata?: object
|
||||
}
|
||||
@@ -1,47 +0,0 @@
|
||||
import { Validator, MedusaError } from "medusa-core-utils"
|
||||
|
||||
/**
|
||||
* @oas [post] /customers
|
||||
* operationId: "PostCustomers"
|
||||
* summary: "Create a Customer"
|
||||
* description: "Creates a Customer."
|
||||
* parameters:
|
||||
* - (body) email=* {string} The Customer's email address.
|
||||
* - (body) first_name=* {string} The Customer's first name.
|
||||
* - (body) last_name=* {string} The Customer's last name.
|
||||
* - (body) phone {string} The Customer's phone number.
|
||||
* tags:
|
||||
* - Customer
|
||||
* responses:
|
||||
* 200:
|
||||
* description: OK
|
||||
* content:
|
||||
* application/json:
|
||||
* schema:
|
||||
* properties:
|
||||
* customer:
|
||||
* $ref: "#/components/schemas/customer"
|
||||
*/
|
||||
export default async (req, res) => {
|
||||
const schema = Validator.object().keys({
|
||||
email: Validator.string()
|
||||
.email()
|
||||
.required(),
|
||||
first_name: Validator.string().required(),
|
||||
last_name: Validator.string().required(),
|
||||
password: Validator.string().required(),
|
||||
phone: Validator.string().optional(),
|
||||
})
|
||||
|
||||
const { value, error } = schema.validate(req.body)
|
||||
if (error) {
|
||||
throw new MedusaError(MedusaError.Types.INVALID_DATA, error.details)
|
||||
}
|
||||
try {
|
||||
const customerService = req.scope.resolve("customerService")
|
||||
const customer = await customerService.create(value)
|
||||
res.status(201).json({ customer })
|
||||
} catch (err) {
|
||||
throw err
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,57 @@
|
||||
import { IsEmail, IsObject, IsOptional, IsString } from "class-validator"
|
||||
import { CustomerService } from "../../../../services"
|
||||
import { validator } from "../../../../utils/validator"
|
||||
|
||||
/**
|
||||
* @oas [post] /customers
|
||||
* operationId: "PostCustomers"
|
||||
* summary: "Create a Customer"
|
||||
* description: "Creates a Customer."
|
||||
* x-authenticated: true
|
||||
* parameters:
|
||||
* - (body) email=* {string} The Customer's email address.
|
||||
* - (body) first_name=* {string} The Customer's first name.
|
||||
* - (body) last_name=* {string} The Customer's last name.
|
||||
* - (body) phone {string} The Customer's phone number.
|
||||
* - (body) metadata {object} Metadata for the customer.
|
||||
* tags:
|
||||
* - Customer
|
||||
* responses:
|
||||
* 200:
|
||||
* description: OK
|
||||
* content:
|
||||
* application/json:
|
||||
* schema:
|
||||
* properties:
|
||||
* customer:
|
||||
* $ref: "#/components/schemas/customer"
|
||||
*/
|
||||
export default async (req, res) => {
|
||||
const validated = await validator(AdminPostCustomersReq, req.bodyn)
|
||||
|
||||
const customerService: CustomerService = req.scope.resolve("customerService")
|
||||
const customer = await customerService.create(validated)
|
||||
res.status(201).json({ customer })
|
||||
}
|
||||
|
||||
export class AdminPostCustomersReq {
|
||||
@IsEmail()
|
||||
email: string
|
||||
|
||||
@IsString()
|
||||
first_name: string
|
||||
|
||||
@IsString()
|
||||
last_name: string
|
||||
|
||||
@IsString()
|
||||
password: string
|
||||
|
||||
@IsString()
|
||||
@IsOptional()
|
||||
phone?: string
|
||||
|
||||
@IsObject()
|
||||
@IsOptional()
|
||||
metadata?: object
|
||||
}
|
||||
+8
-9
@@ -1,8 +1,11 @@
|
||||
import CustomerService from "../../../../services/customer"
|
||||
|
||||
/**
|
||||
* @oas [get] /customers/{id}
|
||||
* operationId: "GetCustomersCustomer"
|
||||
* summary: "Retrieve a Customer"
|
||||
* description: "Retrieves a Customer."
|
||||
* x-authenticated: true
|
||||
* parameters:
|
||||
* - (path) id=* {string} The id of the Customer.
|
||||
* tags:
|
||||
@@ -19,14 +22,10 @@
|
||||
*/
|
||||
export default async (req, res) => {
|
||||
const { id } = req.params
|
||||
try {
|
||||
const customerService = req.scope.resolve("customerService")
|
||||
const customer = await customerService.retrieve(id, {
|
||||
relations: ["orders", "shipping_addresses"],
|
||||
})
|
||||
const customerService: CustomerService = req.scope.resolve("customerService")
|
||||
const customer = await customerService.retrieve(id, {
|
||||
relations: ["orders", "shipping_addresses"],
|
||||
})
|
||||
|
||||
res.json({ customer })
|
||||
} catch (err) {
|
||||
throw err
|
||||
}
|
||||
res.json({ customer })
|
||||
}
|
||||
@@ -1,15 +0,0 @@
|
||||
import { Router } from "express"
|
||||
import middlewares from "../../../middlewares"
|
||||
|
||||
const route = Router()
|
||||
|
||||
export default app => {
|
||||
app.use("/customers", route)
|
||||
|
||||
route.get("/", middlewares.wrap(require("./list-customers").default))
|
||||
route.get("/:id", middlewares.wrap(require("./get-customer").default))
|
||||
|
||||
route.post("/", middlewares.wrap(require("./create-customer").default))
|
||||
route.post("/:id", middlewares.wrap(require("./update-customer").default))
|
||||
return app
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
import { Router } from "express"
|
||||
import { Customer } from "../../../.."
|
||||
import { DeleteResponse, PaginatedResponse } from "../../../../types/common"
|
||||
import middlewares from "../../../middlewares"
|
||||
|
||||
const route = Router()
|
||||
|
||||
export default (app) => {
|
||||
app.use("/customers", route)
|
||||
|
||||
route.get("/", middlewares.wrap(require("./list-customers").default))
|
||||
route.get("/:id", middlewares.wrap(require("./get-customer").default))
|
||||
|
||||
route.post("/", middlewares.wrap(require("./create-customer").default))
|
||||
route.post("/:id", middlewares.wrap(require("./update-customer").default))
|
||||
return app
|
||||
}
|
||||
|
||||
export type AdminCustomersRes = {
|
||||
customer: Customer
|
||||
}
|
||||
|
||||
export type AdminCustomersDeleteRes = DeleteResponse
|
||||
|
||||
export type AdminCustomersListRes = PaginatedResponse & {
|
||||
customers: Customer[]
|
||||
}
|
||||
|
||||
export * from "./create-customer"
|
||||
export * from "./get-customer"
|
||||
export * from "./list-customers"
|
||||
export * from "./update-customer"
|
||||
@@ -1,51 +0,0 @@
|
||||
/**
|
||||
* @oas [get] /customers
|
||||
* operationId: "GetCustomers"
|
||||
* summary: "List Customers"
|
||||
* description: "Retrieves a list of Customers."
|
||||
* tags:
|
||||
* - Customer
|
||||
* responses:
|
||||
* 200:
|
||||
* description: OK
|
||||
* content:
|
||||
* application/json:
|
||||
* schema:
|
||||
* properties:
|
||||
* customer:
|
||||
* $ref: "#/components/schemas/customer"
|
||||
*/
|
||||
export default async (req, res) => {
|
||||
try {
|
||||
const customerService = req.scope.resolve("customerService")
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
let expandFields = []
|
||||
if ("expand" in req.query) {
|
||||
expandFields = req.query.expand.split(",")
|
||||
}
|
||||
|
||||
const listConfig = {
|
||||
relations: expandFields.length ? expandFields : [],
|
||||
skip: offset,
|
||||
take: limit,
|
||||
}
|
||||
|
||||
const [customers, count] = await customerService.listAndCount(
|
||||
selector,
|
||||
listConfig
|
||||
)
|
||||
|
||||
res.json({ customers, count, offset, limit })
|
||||
} catch (error) {
|
||||
throw error
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,75 @@
|
||||
import { Type } from "class-transformer"
|
||||
import { IsNumber, IsOptional, IsString } from "class-validator"
|
||||
import { Customer } from "../../../.."
|
||||
import CustomerService from "../../../../services/customer"
|
||||
import { FindConfig } from "../../../../types/common"
|
||||
import { AdminListCustomerSelector } from "../../../../types/customers"
|
||||
import { validator } from "../../../../utils/validator"
|
||||
/**
|
||||
* @oas [get] /customers
|
||||
* operationId: "GetCustomers"
|
||||
* summary: "List Customers"
|
||||
* description: "Retrieves a list of Customers."
|
||||
* x-authenticated: true
|
||||
* tags:
|
||||
* - Customer
|
||||
* responses:
|
||||
* 200:
|
||||
* description: OK
|
||||
* content:
|
||||
* application/json:
|
||||
* schema:
|
||||
* properties:
|
||||
* customer:
|
||||
* $ref: "#/components/schemas/customer"
|
||||
*/
|
||||
export default async (req, res) => {
|
||||
const validated = await validator(AdminGetCustomersParams, req.query)
|
||||
|
||||
const customerService: CustomerService = req.scope.resolve("customerService")
|
||||
|
||||
const selector: AdminListCustomerSelector = {}
|
||||
|
||||
if (validated.q) {
|
||||
selector.q = validated.q
|
||||
}
|
||||
|
||||
let expandFields: string[] = []
|
||||
if (validated.expand) {
|
||||
expandFields = validated.expand.split(",")
|
||||
}
|
||||
|
||||
const listConfig: FindConfig<Customer> = {
|
||||
relations: expandFields,
|
||||
skip: validated.offset,
|
||||
take: validated.limit,
|
||||
}
|
||||
|
||||
const [customers, count] = await customerService.listAndCount(
|
||||
selector,
|
||||
listConfig
|
||||
)
|
||||
|
||||
res.json({
|
||||
customers,
|
||||
count,
|
||||
offset: validated.offset,
|
||||
limit: validated.limit,
|
||||
})
|
||||
}
|
||||
|
||||
export class AdminGetCustomersParams extends AdminListCustomerSelector {
|
||||
@IsNumber()
|
||||
@IsOptional()
|
||||
@Type(() => Number)
|
||||
limit = 50
|
||||
|
||||
@IsNumber()
|
||||
@IsOptional()
|
||||
@Type(() => Number)
|
||||
offset = 0
|
||||
|
||||
@IsString()
|
||||
@IsOptional()
|
||||
expand?: string
|
||||
}
|
||||
@@ -1,76 +0,0 @@
|
||||
import { Validator, MedusaError } from "medusa-core-utils"
|
||||
|
||||
/**
|
||||
* @oas [post] /customers/{id}
|
||||
* operationId: "PostCustomersCustomer"
|
||||
* summary: "Update a Customer"
|
||||
* description: "Updates a Customer."
|
||||
* parameters:
|
||||
* - (path) id=* {string} The id of the Customer.
|
||||
* requestBody:
|
||||
* content:
|
||||
* application/json:
|
||||
* schema:
|
||||
* properties:
|
||||
* email:
|
||||
* type: string
|
||||
* description: The Customer's email. Only providable if user not registered.
|
||||
* first_name:
|
||||
* type: string
|
||||
* description: The Customer's first name.
|
||||
* last_name:
|
||||
* type: string
|
||||
* description: The Customer's last name.
|
||||
* phone:
|
||||
* description: The Customer's phone number.
|
||||
* type: object
|
||||
* tags:
|
||||
* - Customer
|
||||
* responses:
|
||||
* 200:
|
||||
* description: OK
|
||||
* content:
|
||||
* application/json:
|
||||
* schema:
|
||||
* properties:
|
||||
* customer:
|
||||
* $ref: "#/components/schemas/customer"
|
||||
*/
|
||||
export default async (req, res) => {
|
||||
const { id } = req.params
|
||||
|
||||
const schema = Validator.object().keys({
|
||||
email: Validator.string().optional(),
|
||||
first_name: Validator.string().optional(),
|
||||
last_name: Validator.string().optional(),
|
||||
password: Validator.string().optional(),
|
||||
phone: Validator.string().optional(),
|
||||
})
|
||||
|
||||
const { value, error } = schema.validate(req.body)
|
||||
if (error) {
|
||||
throw new MedusaError(MedusaError.Types.INVALID_DATA, error.details)
|
||||
}
|
||||
|
||||
try {
|
||||
const customerService = req.scope.resolve("customerService")
|
||||
|
||||
let customer = await customerService.retrieve(id)
|
||||
|
||||
if (value.email && customer.has_account) {
|
||||
throw new MedusaError(
|
||||
MedusaError.Types.INVALID_DATA,
|
||||
"Email cannot be changed when the user has registered their account"
|
||||
)
|
||||
}
|
||||
|
||||
await customerService.update(id, value)
|
||||
|
||||
customer = await customerService.retrieve(id, {
|
||||
relations: ["orders"],
|
||||
})
|
||||
res.status(200).json({ customer })
|
||||
} catch (err) {
|
||||
throw err
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,97 @@
|
||||
import { IsEmail, IsObject, IsOptional, IsString } from "class-validator"
|
||||
import { MedusaError } from "medusa-core-utils"
|
||||
import CustomerService from "../../../../services/customer"
|
||||
import { validator } from "../../../../utils/validator"
|
||||
|
||||
/**
|
||||
* @oas [post] /customers/{id}
|
||||
* operationId: "PostCustomersCustomer"
|
||||
* summary: "Update a Customer"
|
||||
* description: "Updates a Customer."
|
||||
* x-authenticated: true
|
||||
* parameters:
|
||||
* - (path) id=* {string} The id of the Customer.
|
||||
* requestBody:
|
||||
* content:
|
||||
* application/json:
|
||||
* schema:
|
||||
* properties:
|
||||
* email:
|
||||
* type: string
|
||||
* description: The Customer's email. Only providable if user not registered.
|
||||
* first_name:
|
||||
* type: string
|
||||
* description: The Customer's first name.
|
||||
* last_name:
|
||||
* type: string
|
||||
* description: The Customer's last name.
|
||||
* phone:
|
||||
* type: string
|
||||
* description: The Customer's phone number.
|
||||
* password:
|
||||
* type: string
|
||||
* description: The Customer's password.
|
||||
* metadata:
|
||||
* type: object
|
||||
* description: Metadata for the customer.
|
||||
* tags:
|
||||
* - Customer
|
||||
* responses:
|
||||
* 200:
|
||||
* description: OK
|
||||
* content:
|
||||
* application/json:
|
||||
* schema:
|
||||
* properties:
|
||||
* customer:
|
||||
* $ref: "#/components/schemas/customer"
|
||||
*/
|
||||
export default async (req, res) => {
|
||||
const { id } = req.params
|
||||
|
||||
const validated = await validator(AdminPostCustomersCustomerReq, req.body)
|
||||
|
||||
const customerService: CustomerService = req.scope.resolve("customerService")
|
||||
|
||||
let customer = await customerService.retrieve(id)
|
||||
|
||||
if (validated.email && customer.has_account) {
|
||||
throw new MedusaError(
|
||||
MedusaError.Types.INVALID_DATA,
|
||||
"Email cannot be changed when the user has registered their account"
|
||||
)
|
||||
}
|
||||
|
||||
await customerService.update(id, validated)
|
||||
|
||||
customer = await customerService.retrieve(id, {
|
||||
relations: ["orders"],
|
||||
})
|
||||
res.status(200).json({ customer })
|
||||
}
|
||||
|
||||
export class AdminPostCustomersCustomerReq {
|
||||
@IsEmail()
|
||||
@IsOptional()
|
||||
email?: string
|
||||
|
||||
@IsString()
|
||||
@IsOptional()
|
||||
first_name?: string
|
||||
|
||||
@IsString()
|
||||
@IsOptional()
|
||||
last_name?: string
|
||||
|
||||
@IsString()
|
||||
@IsOptional()
|
||||
password?: string
|
||||
|
||||
@IsString()
|
||||
@IsOptional()
|
||||
phone?: string
|
||||
|
||||
@IsObject()
|
||||
@IsOptional()
|
||||
metadata?: object
|
||||
}
|
||||
@@ -80,7 +80,7 @@ describe("POST /admin/discounts", () => {
|
||||
})
|
||||
|
||||
it("returns error", () => {
|
||||
expect(subject.body.message[0].message).toEqual(
|
||||
expect(subject.body.message).toEqual(
|
||||
`"valid_duration" must be a valid ISO 8601 duration`
|
||||
)
|
||||
})
|
||||
@@ -160,7 +160,9 @@ describe("POST /admin/discounts", () => {
|
||||
})
|
||||
|
||||
it("returns error", () => {
|
||||
expect(subject.body.message[0].message).toEqual(`"rule.type" is required`)
|
||||
expect(subject.body.message).toEqual(
|
||||
`type should not be empty, type must be a string`
|
||||
)
|
||||
})
|
||||
})
|
||||
|
||||
@@ -193,8 +195,8 @@ describe("POST /admin/discounts", () => {
|
||||
})
|
||||
|
||||
it("returns error", () => {
|
||||
expect(subject.body.message[0].message).toEqual(
|
||||
`"ends_at" must be greater than "ref:starts_at"`
|
||||
expect(subject.body.message).toEqual(
|
||||
`"ends_at" must be greater than "starts_at"`
|
||||
)
|
||||
})
|
||||
})
|
||||
@@ -231,8 +233,8 @@ describe("POST /admin/discounts", () => {
|
||||
it("returns error", () => {
|
||||
expect(DiscountServiceMock.create).toHaveBeenCalledWith({
|
||||
code: "TEST",
|
||||
is_dynamic: true,
|
||||
is_disabled: false,
|
||||
is_dynamic: true,
|
||||
is_disabled: false,
|
||||
rule: {
|
||||
description: "Test",
|
||||
type: "fixed",
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { IdMap } from "medusa-test-utils"
|
||||
import { defaultFields, defaultRelations } from ".."
|
||||
import { defaultAdminDiscountsFields, defaultAdminDiscountsRelations } from ".."
|
||||
import { request } from "../../../../../helpers/test-request"
|
||||
import { DiscountServiceMock } from "../../../../../services/__mocks__/discount"
|
||||
|
||||
@@ -27,8 +27,8 @@ describe("GET /admin/discounts", () => {
|
||||
expect(DiscountServiceMock.listAndCount).toHaveBeenCalledWith(
|
||||
{},
|
||||
{
|
||||
select: defaultFields,
|
||||
relations: defaultRelations,
|
||||
select: defaultAdminDiscountsFields,
|
||||
relations: defaultAdminDiscountsRelations,
|
||||
skip: 0,
|
||||
take: 20,
|
||||
order: { created_at: "DESC" },
|
||||
@@ -64,8 +64,8 @@ describe("GET /admin/discounts", () => {
|
||||
expect(DiscountServiceMock.listAndCount).toHaveBeenCalledWith(
|
||||
{ q: "OLI", is_dynamic: false, is_disabled: false },
|
||||
{
|
||||
select: defaultFields,
|
||||
relations: defaultRelations,
|
||||
select: defaultAdminDiscountsFields,
|
||||
relations: defaultAdminDiscountsRelations,
|
||||
skip: 20,
|
||||
take: 40,
|
||||
order: { created_at: "DESC" },
|
||||
@@ -97,8 +97,8 @@ describe("GET /admin/discounts", () => {
|
||||
expect(DiscountServiceMock.listAndCount).toHaveBeenCalledWith(
|
||||
{ is_dynamic: true },
|
||||
{
|
||||
select: defaultFields,
|
||||
relations: defaultRelations,
|
||||
select: defaultAdminDiscountsFields,
|
||||
relations: defaultAdminDiscountsRelations,
|
||||
skip: 0,
|
||||
take: 20,
|
||||
order: { created_at: "DESC" },
|
||||
@@ -130,8 +130,8 @@ describe("GET /admin/discounts", () => {
|
||||
expect(DiscountServiceMock.listAndCount).toHaveBeenCalledWith(
|
||||
{ is_disabled: true },
|
||||
{
|
||||
select: defaultFields,
|
||||
relations: defaultRelations,
|
||||
select: defaultAdminDiscountsFields,
|
||||
relations: defaultAdminDiscountsRelations,
|
||||
skip: 0,
|
||||
take: 20,
|
||||
order: { created_at: "DESC" },
|
||||
|
||||
@@ -46,7 +46,6 @@ describe("POST /admin/discounts", () => {
|
||||
value: 10,
|
||||
allocation: "total",
|
||||
},
|
||||
is_dynamic: false,
|
||||
}
|
||||
)
|
||||
})
|
||||
@@ -87,7 +86,7 @@ describe("POST /admin/discounts", () => {
|
||||
})
|
||||
|
||||
it("returns error", () => {
|
||||
expect(subject.body.message[0].message).toEqual(
|
||||
expect(subject.body.message).toEqual(
|
||||
`"valid_duration" must be a valid ISO 8601 duration`
|
||||
)
|
||||
})
|
||||
@@ -181,8 +180,8 @@ describe("POST /admin/discounts", () => {
|
||||
})
|
||||
|
||||
it("returns error", () => {
|
||||
expect(subject.body.message[0].message).toEqual(
|
||||
`"ends_at" must be greater than "ref:starts_at"`
|
||||
expect(subject.body.message).toEqual(
|
||||
`"ends_at" must be greater than "starts_at"`
|
||||
)
|
||||
})
|
||||
})
|
||||
|
||||
+11
-13
@@ -1,10 +1,12 @@
|
||||
import { defaultFields, defaultRelations } from "./"
|
||||
|
||||
import { defaultAdminDiscountsFields, defaultAdminDiscountsRelations } from "."
|
||||
import { Discount } from "../../../.."
|
||||
import DiscountService from "../../../../services/discount"
|
||||
/**
|
||||
* @oas [post] /discounts/{id}/regions/{region_id}
|
||||
* operationId: "PostDiscountsDiscountRegionsRegion"
|
||||
* summary: "Adds Region availability"
|
||||
* description: "Adds a Region to the list of Regions that a Discount can be used in."
|
||||
* x-authenticated: true
|
||||
* parameters:
|
||||
* - (path) id=* {string} The id of the Discount.
|
||||
* - (path) region_id=* {string} The id of the Region.
|
||||
@@ -22,18 +24,14 @@ import { defaultFields, defaultRelations } from "./"
|
||||
*/
|
||||
export default async (req, res) => {
|
||||
const { discount_id, region_id } = req.params
|
||||
try {
|
||||
const discountService = req.scope.resolve("discountService")
|
||||
|
||||
await discountService.addRegion(discount_id, region_id)
|
||||
const discountService: DiscountService = req.scope.resolve("discountService")
|
||||
await discountService.addRegion(discount_id, region_id)
|
||||
|
||||
const discount = await discountService.retrieve(discount_id, {
|
||||
select: defaultFields,
|
||||
relations: defaultRelations,
|
||||
})
|
||||
const discount: Discount = await discountService.retrieve(discount_id, {
|
||||
select: defaultAdminDiscountsFields,
|
||||
relations: defaultAdminDiscountsRelations,
|
||||
})
|
||||
|
||||
res.status(200).json({ discount })
|
||||
} catch (err) {
|
||||
throw err
|
||||
}
|
||||
res.status(200).json({ discount })
|
||||
}
|
||||
@@ -1,40 +0,0 @@
|
||||
import { defaultFields, defaultRelations } from "./"
|
||||
|
||||
/**
|
||||
* @oas [post] /discounts/{id}/products/{product_id}
|
||||
* operationId: "PostDiscountsDiscountProductsProduct"
|
||||
* summary: "Adds Product availability"
|
||||
* description: "Adds a Product to the list of Products that a Discount can be used for."
|
||||
* parameters:
|
||||
* - (path) id=* {string} The id of the Discount.
|
||||
* - (path) product_id=* {string} The id of the Product.
|
||||
* tags:
|
||||
* - Discount
|
||||
* responses:
|
||||
* 200:
|
||||
* description: OK
|
||||
* content:
|
||||
* application/json:
|
||||
* schema:
|
||||
* properties:
|
||||
* discount:
|
||||
* $ref: "#/components/schemas/discount"
|
||||
*/
|
||||
export default async (req, res) => {
|
||||
const { discount_id, variant_id } = req.params
|
||||
|
||||
try {
|
||||
const discountService = req.scope.resolve("discountService")
|
||||
|
||||
await discountService.addValidProduct(discount_id, variant_id)
|
||||
|
||||
const discount = await discountService.retrieve(discount_id, {
|
||||
select: defaultFields,
|
||||
relations: defaultRelations,
|
||||
})
|
||||
|
||||
res.status(200).json({ discount })
|
||||
} catch (err) {
|
||||
throw err
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
import { defaultAdminDiscountsFields, defaultAdminDiscountsRelations } from "."
|
||||
import { Discount } from "../../../.."
|
||||
import DiscountService from "../../../../services/discount"
|
||||
/**
|
||||
* @oas [post] /discounts/{id}/products/{variant_id}
|
||||
* operationId: "PostDiscountsDiscountProductsProduct"
|
||||
* summary: "Adds Product availability"
|
||||
* description: "Adds a Product to the list of Products that a Discount can be used for."
|
||||
* x-authenticated: true
|
||||
* parameters:
|
||||
* - (path) id=* {string} The id of the Discount.
|
||||
* - (path) variant_id=* {string} The id of the Product.
|
||||
* tags:
|
||||
* - Discount
|
||||
* responses:
|
||||
* 200:
|
||||
* description: OK
|
||||
* content:
|
||||
* application/json:
|
||||
* schema:
|
||||
* properties:
|
||||
* discount:
|
||||
* $ref: "#/components/schemas/discount"
|
||||
*/
|
||||
export default async (req, res) => {
|
||||
const { discount_id, variant_id } = req.params
|
||||
|
||||
const discountService: DiscountService = req.scope.resolve("discountService")
|
||||
await discountService.addValidProduct(discount_id, variant_id)
|
||||
|
||||
const discount: Discount = await discountService.retrieve(discount_id, {
|
||||
select: defaultAdminDiscountsFields,
|
||||
relations: defaultAdminDiscountsRelations,
|
||||
})
|
||||
|
||||
res.status(200).json({ discount })
|
||||
}
|
||||
+100
-51
@@ -1,14 +1,33 @@
|
||||
import { MedusaError, Validator } from "medusa-core-utils"
|
||||
import { defaultRelations } from "."
|
||||
|
||||
import { Type } from "class-transformer"
|
||||
import {
|
||||
IsArray,
|
||||
IsBoolean,
|
||||
IsDate,
|
||||
IsNotEmpty,
|
||||
IsNumber,
|
||||
IsObject,
|
||||
IsOptional,
|
||||
IsPositive,
|
||||
IsString,
|
||||
ValidateNested,
|
||||
} from "class-validator"
|
||||
import { defaultAdminDiscountsRelations } from "."
|
||||
import DiscountService from "../../../../services/discount"
|
||||
import { IsGreaterThan } from "../../../../utils/validators/greater-than"
|
||||
import { validator } from "../../../../utils/validator"
|
||||
import { IsISO8601Duration } from "../../../../utils/validators/iso8601-duration"
|
||||
/**
|
||||
* @oas [post] /discounts
|
||||
* operationId: "PostDiscounts"
|
||||
* summary: "Creates a Discount"
|
||||
* x-authenticated: true
|
||||
* description: "Creates a Discount with a given set of rules that define how the Discount behaves."
|
||||
* requestBody:
|
||||
* content:
|
||||
* application/json:
|
||||
* required:
|
||||
* - code
|
||||
* - rule
|
||||
* schema:
|
||||
* properties:
|
||||
* code:
|
||||
@@ -56,54 +75,84 @@ import { defaultRelations } from "."
|
||||
* $ref: "#/components/schemas/discount"
|
||||
*/
|
||||
export default async (req, res) => {
|
||||
const schema = Validator.object().keys({
|
||||
code: Validator.string().required(),
|
||||
is_dynamic: Validator.boolean().default(false),
|
||||
rule: Validator.object()
|
||||
.keys({
|
||||
description: Validator.string().optional(),
|
||||
type: Validator.string().required(),
|
||||
value: Validator.number()
|
||||
.positive()
|
||||
.required(),
|
||||
allocation: Validator.string().required(),
|
||||
valid_for: Validator.array().items(Validator.string()),
|
||||
})
|
||||
.required(),
|
||||
is_disabled: Validator.boolean().default(false),
|
||||
starts_at: Validator.date().optional(),
|
||||
ends_at: Validator.date()
|
||||
.greater(Validator.ref("starts_at"))
|
||||
.optional(),
|
||||
valid_duration: Validator.string()
|
||||
.isoDuration()
|
||||
.allow(null)
|
||||
.optional(),
|
||||
usage_limit: Validator.number()
|
||||
.positive()
|
||||
.optional(),
|
||||
regions: Validator.array()
|
||||
.items(Validator.string())
|
||||
.optional(),
|
||||
metadata: Validator.object().optional(),
|
||||
})
|
||||
const validated = await validator(AdminPostDiscountsReq, req.body)
|
||||
|
||||
const { value, error } = schema.validate(req.body)
|
||||
if (error) {
|
||||
throw new MedusaError(MedusaError.Types.INVALID_DATA, error.details)
|
||||
}
|
||||
const discountService: DiscountService = req.scope.resolve("discountService")
|
||||
const created = await discountService.create(validated)
|
||||
const discount = await discountService.retrieve(
|
||||
created.id,
|
||||
defaultAdminDiscountsRelations
|
||||
)
|
||||
|
||||
try {
|
||||
const discountService = req.scope.resolve("discountService")
|
||||
|
||||
const created = await discountService.create(value)
|
||||
const discount = await discountService.retrieve(
|
||||
created.id,
|
||||
defaultRelations
|
||||
)
|
||||
|
||||
res.status(200).json({ discount })
|
||||
} catch (err) {
|
||||
throw err
|
||||
}
|
||||
res.status(200).json({ discount })
|
||||
}
|
||||
|
||||
export class AdminPostDiscountsReq {
|
||||
@IsString()
|
||||
@IsNotEmpty()
|
||||
code: string
|
||||
|
||||
@IsNotEmpty()
|
||||
@ValidateNested()
|
||||
@Type(() => AdminPostDiscountsDiscountRule)
|
||||
rule: AdminPostDiscountsDiscountRule
|
||||
|
||||
@IsBoolean()
|
||||
@IsOptional()
|
||||
is_dynamic = false
|
||||
|
||||
@IsBoolean()
|
||||
@IsOptional()
|
||||
is_disabled = false
|
||||
|
||||
@IsDate()
|
||||
@IsOptional()
|
||||
@Type(() => Date)
|
||||
starts_at?: Date
|
||||
|
||||
@IsDate()
|
||||
@IsOptional()
|
||||
@IsGreaterThan("starts_at")
|
||||
@Type(() => Date)
|
||||
ends_at?: Date
|
||||
|
||||
@IsISO8601Duration()
|
||||
@IsOptional()
|
||||
valid_duration?: string
|
||||
|
||||
@IsNumber()
|
||||
@IsOptional()
|
||||
@IsPositive()
|
||||
usage_limit?: number
|
||||
|
||||
@IsArray()
|
||||
@IsOptional()
|
||||
@IsString({ each: true })
|
||||
regions?: string[]
|
||||
|
||||
@IsObject()
|
||||
@IsOptional()
|
||||
metadata?: object
|
||||
}
|
||||
|
||||
export class AdminPostDiscountsDiscountRule {
|
||||
@IsString()
|
||||
@IsOptional()
|
||||
description?: string
|
||||
|
||||
@IsString()
|
||||
@IsNotEmpty()
|
||||
type: string
|
||||
|
||||
@IsNumber()
|
||||
value: number
|
||||
|
||||
@IsString()
|
||||
@IsNotEmpty()
|
||||
allocation: string
|
||||
|
||||
@IsOptional()
|
||||
@IsArray()
|
||||
@IsString({ each: true })
|
||||
valid_for?: string[]
|
||||
}
|
||||
+35
-20
@@ -1,10 +1,18 @@
|
||||
import { MedusaError, Validator } from "medusa-core-utils"
|
||||
|
||||
import {
|
||||
IsNotEmpty,
|
||||
IsNumber,
|
||||
IsObject,
|
||||
IsOptional,
|
||||
IsString,
|
||||
} from "class-validator"
|
||||
import DiscountService from "../../../../services/discount"
|
||||
import { validator } from "../../../../utils/validator"
|
||||
/**
|
||||
* @oas [post] /discounts/{id}/dynamic-codes
|
||||
* operationId: "PostDiscountsDiscountDynamicCodes"
|
||||
* summary: "Create a dynamic Discount code"
|
||||
* description: "Creates a unique code that can map to a parent Discount. This is useful if you want to automatically generate codes with the same behaviour."
|
||||
* x-authenticated: true
|
||||
* parameters:
|
||||
* - (path) id=* {string} The id of the Discount to create the dynamic code from."
|
||||
* - (body) code=* {string} The unique code that will be used to redeem the Discount.
|
||||
@@ -24,27 +32,34 @@ import { MedusaError, Validator } from "medusa-core-utils"
|
||||
export default async (req, res) => {
|
||||
const { discount_id } = req.params
|
||||
|
||||
const schema = Validator.object().keys({
|
||||
code: Validator.string().required(),
|
||||
usage_limit: Validator.number().default(1),
|
||||
metadata: Validator.object().optional(),
|
||||
const validated = await validator(
|
||||
AdminPostDiscountsDiscountDynamicCodesReq,
|
||||
req.body
|
||||
)
|
||||
|
||||
const discountService: DiscountService = req.scope.resolve("discountService")
|
||||
const created = await discountService.createDynamicCode(
|
||||
discount_id,
|
||||
validated
|
||||
)
|
||||
|
||||
const discount = await discountService.retrieve(created.id, {
|
||||
relations: ["rule", "rule.valid_for", "regions"],
|
||||
})
|
||||
|
||||
const { value, error } = schema.validate(req.body)
|
||||
if (error) {
|
||||
throw new MedusaError(MedusaError.Types.INVALID_DATA, error.details)
|
||||
}
|
||||
res.status(200).json({ discount })
|
||||
}
|
||||
|
||||
try {
|
||||
const discountService = req.scope.resolve("discountService")
|
||||
const created = await discountService.createDynamicCode(discount_id, value)
|
||||
export class AdminPostDiscountsDiscountDynamicCodesReq {
|
||||
@IsString()
|
||||
@IsNotEmpty()
|
||||
code: string
|
||||
|
||||
const discount = await discountService.retrieve(created.id, {
|
||||
relations: ["rule", "rule.valid_for", "regions"],
|
||||
})
|
||||
@IsNumber()
|
||||
@IsOptional()
|
||||
usage_limit = 1
|
||||
|
||||
res.status(200).json({ discount })
|
||||
} catch (err) {
|
||||
throw err
|
||||
}
|
||||
@IsObject()
|
||||
@IsOptional()
|
||||
metadata?: object
|
||||
}
|
||||
+10
-11
@@ -1,8 +1,11 @@
|
||||
import DiscountService from "../../../../services/discount"
|
||||
|
||||
/**
|
||||
* @oas [delete] /discounts/{id}
|
||||
* operationId: "DeleteDiscountsDiscount"
|
||||
* summary: "Delete a Discount"
|
||||
* description: "Deletes a Discount."
|
||||
* x-authenticated: true
|
||||
* parameters:
|
||||
* - (path) id=* {string} The id of the Discount
|
||||
* tags:
|
||||
@@ -26,16 +29,12 @@
|
||||
export default async (req, res) => {
|
||||
const { discount_id } = req.params
|
||||
|
||||
try {
|
||||
const discountService = req.scope.resolve("discountService")
|
||||
await discountService.delete(discount_id)
|
||||
const discountService: DiscountService = req.scope.resolve("discountService")
|
||||
await discountService.delete(discount_id)
|
||||
|
||||
res.json({
|
||||
id: discount_id,
|
||||
object: "discount",
|
||||
deleted: true,
|
||||
})
|
||||
} catch (err) {
|
||||
throw err
|
||||
}
|
||||
res.json({
|
||||
id: discount_id,
|
||||
object: "discount",
|
||||
deleted: true,
|
||||
})
|
||||
}
|
||||
+9
-10
@@ -1,8 +1,11 @@
|
||||
import DiscountService from "../../../../services/discount"
|
||||
|
||||
/**
|
||||
* @oas [delete] /discounts/{id}/dynamic-codes/{code}
|
||||
* operationId: "DeleteDiscountsDiscountDynamicCodesCode"
|
||||
* summary: "Delete a dynamic code"
|
||||
* description: "Deletes a dynamic code from a Discount."
|
||||
* x-authenticated: true
|
||||
* parameters:
|
||||
* - (path) id=* {string} The id of the Discount
|
||||
* - (path) code=* {string} The id of the Discount
|
||||
@@ -21,16 +24,12 @@
|
||||
export default async (req, res) => {
|
||||
const { discount_id, code } = req.params
|
||||
|
||||
try {
|
||||
const discountService = req.scope.resolve("discountService")
|
||||
await discountService.deleteDynamicCode(discount_id, code)
|
||||
const discountService: DiscountService = req.scope.resolve("discountService")
|
||||
await discountService.deleteDynamicCode(discount_id, code)
|
||||
|
||||
const discount = await discountService.retrieve(discount_id, {
|
||||
relations: ["rule", "rule.valid_for", "regions"],
|
||||
})
|
||||
const discount = await discountService.retrieve(discount_id, {
|
||||
relations: ["rule", "rule.valid_for", "regions"],
|
||||
})
|
||||
|
||||
res.status(200).json({ discount })
|
||||
} catch (err) {
|
||||
throw err
|
||||
}
|
||||
res.status(200).json({ discount })
|
||||
}
|
||||
+10
-12
@@ -1,10 +1,11 @@
|
||||
import { defaultFields, defaultRelations } from "./"
|
||||
|
||||
import { defaultAdminDiscountsRelations } from "."
|
||||
import DiscountService from "../../../../services/discount"
|
||||
/**
|
||||
* @oas [get] /discounts/code/{code}
|
||||
* operationId: "GetDiscountsDiscountCode"
|
||||
* summary: "Retrieve a Discount by code"
|
||||
* description: "Retrieves a Discount by its discount code"
|
||||
* x-authenticated: true
|
||||
* parameters:
|
||||
* - (path) code=* {string} The code of the Discount
|
||||
* tags:
|
||||
@@ -21,15 +22,12 @@ import { defaultFields, defaultRelations } from "./"
|
||||
*/
|
||||
export default async (req, res) => {
|
||||
const { code } = req.params
|
||||
try {
|
||||
const discountService = req.scope.resolve("discountService")
|
||||
const discount = await discountService.retrieveByCode(
|
||||
code,
|
||||
defaultRelations
|
||||
)
|
||||
|
||||
res.status(200).json({ discount })
|
||||
} catch (err) {
|
||||
throw err
|
||||
}
|
||||
const discountService: DiscountService = req.scope.resolve("discountService")
|
||||
const discount = await discountService.retrieveByCode(
|
||||
code,
|
||||
defaultAdminDiscountsRelations
|
||||
)
|
||||
|
||||
res.status(200).json({ discount })
|
||||
}
|
||||
+10
-12
@@ -1,10 +1,11 @@
|
||||
import { defaultFields, defaultRelations } from "./"
|
||||
|
||||
import { defaultAdminDiscountsFields, defaultAdminDiscountsRelations } from "."
|
||||
import DiscountService from "../../../../services/discount"
|
||||
/**
|
||||
* @oas [get] /discounts/{id}
|
||||
* operationId: "GetDiscountsDiscount"
|
||||
* summary: "Retrieve a Discount"
|
||||
* description: "Retrieves a Discount"
|
||||
* x-authenticated: true
|
||||
* parameters:
|
||||
* - (path) id=* {string} The id of the Discount
|
||||
* tags:
|
||||
@@ -21,15 +22,12 @@ import { defaultFields, defaultRelations } from "./"
|
||||
*/
|
||||
export default async (req, res) => {
|
||||
const { discount_id } = req.params
|
||||
try {
|
||||
const discountService = req.scope.resolve("discountService")
|
||||
const data = await discountService.retrieve(discount_id, {
|
||||
select: defaultFields,
|
||||
relations: defaultRelations,
|
||||
})
|
||||
|
||||
res.status(200).json({ discount: data })
|
||||
} catch (err) {
|
||||
throw err
|
||||
}
|
||||
const discountService: DiscountService = req.scope.resolve("discountService")
|
||||
const data = await discountService.retrieve(discount_id, {
|
||||
select: defaultAdminDiscountsFields,
|
||||
relations: defaultAdminDiscountsRelations,
|
||||
})
|
||||
|
||||
res.status(200).json({ discount: data })
|
||||
}
|
||||
+29
-3
@@ -1,9 +1,12 @@
|
||||
import { Router } from "express"
|
||||
import { Discount } from "../../../.."
|
||||
import middlewares from "../../../middlewares"
|
||||
import "reflect-metadata"
|
||||
import { DeleteResponse, PaginatedResponse } from "../../../../types/common"
|
||||
|
||||
const route = Router()
|
||||
|
||||
export default app => {
|
||||
export default (app) => {
|
||||
app.use("/discounts", route)
|
||||
|
||||
route.get("/", middlewares.wrap(require("./list-discounts").default))
|
||||
@@ -59,7 +62,7 @@ export default app => {
|
||||
return app
|
||||
}
|
||||
|
||||
export const defaultFields = [
|
||||
export const defaultAdminDiscountsFields = [
|
||||
"id",
|
||||
"code",
|
||||
"is_dynamic",
|
||||
@@ -77,9 +80,32 @@ export const defaultFields = [
|
||||
"valid_duration",
|
||||
]
|
||||
|
||||
export const defaultRelations = [
|
||||
export const defaultAdminDiscountsRelations = [
|
||||
"rule",
|
||||
"parent_discount",
|
||||
"regions",
|
||||
"rule.valid_for",
|
||||
]
|
||||
|
||||
export type AdminDiscountsRes = {
|
||||
discount: Discount
|
||||
}
|
||||
|
||||
export type AdminDiscountsDeleteRes = DeleteResponse
|
||||
|
||||
export type AdminDiscountsListRes = PaginatedResponse & {
|
||||
discounts: Discount[]
|
||||
}
|
||||
|
||||
export * from "./add-region"
|
||||
export * from "./add-valid-product"
|
||||
export * from "./create-discount"
|
||||
export * from "./create-dynamic-code"
|
||||
export * from "./delete-discount"
|
||||
export * from "./delete-dynamic-code"
|
||||
export * from "./get-discount"
|
||||
export * from "./get-discount-by-code"
|
||||
export * from "./list-discounts"
|
||||
export * from "./remove-region"
|
||||
export * from "./remove-valid-product"
|
||||
export * from "./update-discount"
|
||||
@@ -1,58 +0,0 @@
|
||||
import { defaultFields, defaultRelations } from "./"
|
||||
|
||||
/**
|
||||
* @oas [get] /discounts
|
||||
* operationId: "GetDiscounts"
|
||||
* summary: "List Discounts"
|
||||
* description: "Retrieves a list of Discounts"
|
||||
* tags:
|
||||
* - Discount
|
||||
* responses:
|
||||
* 200:
|
||||
* description: OK
|
||||
* content:
|
||||
* application/json:
|
||||
* schema:
|
||||
* properties:
|
||||
* discount:
|
||||
* $ref: "#/components/schemas/discount"
|
||||
*/
|
||||
export default async (req, res) => {
|
||||
try {
|
||||
const discountService = req.scope.resolve("discountService")
|
||||
|
||||
const limit = parseInt(req.query.limit) || 20
|
||||
const offset = parseInt(req.query.offset) || 0
|
||||
|
||||
let selector = {}
|
||||
|
||||
if ("q" in req.query) {
|
||||
selector.q = req.query.q
|
||||
}
|
||||
|
||||
if ("is_dynamic" in req.query) {
|
||||
selector.is_dynamic = req.query.is_dynamic === "true"
|
||||
}
|
||||
|
||||
if ("is_disabled" in req.query) {
|
||||
selector.is_disabled = req.query.is_disabled === "true"
|
||||
}
|
||||
|
||||
const listConfig = {
|
||||
select: defaultFields,
|
||||
relations: defaultRelations,
|
||||
skip: offset,
|
||||
take: limit,
|
||||
order: { created_at: "DESC" },
|
||||
}
|
||||
|
||||
const [discounts, count] = await discountService.listAndCount(
|
||||
selector,
|
||||
listConfig
|
||||
)
|
||||
|
||||
res.status(200).json({ discounts, count, offset, limit })
|
||||
} catch (err) {
|
||||
throw err
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,93 @@
|
||||
import { Type, Transform } from "class-transformer"
|
||||
import { IsBoolean, IsInt, IsOptional, IsString } from "class-validator"
|
||||
import { defaultAdminDiscountsFields, defaultAdminDiscountsRelations } from "."
|
||||
import DiscountService from "../../../../services/discount"
|
||||
import { ListSelector } from "../../../../types/discount"
|
||||
import { validator } from "../../../../utils/validator"
|
||||
/**
|
||||
* @oas [get] /discounts
|
||||
* operationId: "GetDiscounts"
|
||||
* summary: "List Discounts"
|
||||
* x-authenticated: true
|
||||
* description: "Retrieves a list of Discounts"
|
||||
* parameters:
|
||||
* - (query) q {string} Search query applied on results.
|
||||
* - (query) is_dynamic {boolean} Return only dynamic discounts.
|
||||
* - (query) is_disabled {boolean} Return only disabled discounts.
|
||||
* - (query) limit {number} The number of items in the response
|
||||
* - (query) offset {number} The offset of items in response
|
||||
* - (query) expand {string} Comma separated list of relations to include in the results.
|
||||
* tags:
|
||||
* - Discount
|
||||
* responses:
|
||||
* 200:
|
||||
* description: OK
|
||||
* content:
|
||||
* application/json:
|
||||
* schema:
|
||||
* properties:
|
||||
* discount:
|
||||
* $ref: "#/components/schemas/discount"
|
||||
*/
|
||||
export default async (req, res) => {
|
||||
const validated = await validator(AdminGetDiscountsParams, req.query)
|
||||
|
||||
const discountService: DiscountService = req.scope.resolve("discountService")
|
||||
const selector: ListSelector = {}
|
||||
|
||||
if (validated.q) {
|
||||
selector.q = validated.q
|
||||
}
|
||||
|
||||
selector.is_disabled = validated.is_disabled
|
||||
selector.is_dynamic = validated.is_dynamic
|
||||
|
||||
const listConfig = {
|
||||
select: defaultAdminDiscountsFields,
|
||||
relations: defaultAdminDiscountsRelations,
|
||||
skip: validated.offset,
|
||||
take: validated.limit,
|
||||
order: { created_at: "DESC" },
|
||||
}
|
||||
const [discounts, count] = await discountService.listAndCount(
|
||||
selector,
|
||||
listConfig
|
||||
)
|
||||
|
||||
res.status(200).json({
|
||||
discounts,
|
||||
count,
|
||||
offset: validated.offset,
|
||||
limit: validated.limit,
|
||||
})
|
||||
}
|
||||
|
||||
export class AdminGetDiscountsParams {
|
||||
@IsString()
|
||||
@IsOptional()
|
||||
q?: string
|
||||
|
||||
@IsBoolean()
|
||||
@IsOptional()
|
||||
@Transform(({ value }) => value === "true")
|
||||
is_dynamic?: boolean
|
||||
|
||||
@IsBoolean()
|
||||
@IsOptional()
|
||||
@Transform(({ value }) => value === "true")
|
||||
is_disabled?: boolean
|
||||
|
||||
@IsInt()
|
||||
@IsOptional()
|
||||
@Type(() => Number)
|
||||
limit = 20
|
||||
|
||||
@IsInt()
|
||||
@IsOptional()
|
||||
@Type(() => Number)
|
||||
offset = 0
|
||||
|
||||
@IsString()
|
||||
@IsOptional()
|
||||
expand?: string
|
||||
}
|
||||
+10
-13
@@ -1,9 +1,10 @@
|
||||
import { defaultFields, defaultRelations } from "./"
|
||||
|
||||
import DiscountService from "../../../../services/discount"
|
||||
import { defaultAdminDiscountsFields, defaultAdminDiscountsRelations } from "."
|
||||
/**
|
||||
* @oas [delete] /discounts/{id}/regions/{region_id}
|
||||
* operationId: "DeleteDiscountsDiscountRegionsRegion"
|
||||
* summary: "Remove Region availability"
|
||||
* x-authenticated: true
|
||||
* description: "Removes a Region from the list of Regions that a Discount can be used in."
|
||||
* parameters:
|
||||
* - (path) id=* {string} The id of the Discount.
|
||||
@@ -23,17 +24,13 @@ import { defaultFields, defaultRelations } from "./"
|
||||
export default async (req, res) => {
|
||||
const { discount_id, region_id } = req.params
|
||||
|
||||
try {
|
||||
const discountService = req.scope.resolve("discountService")
|
||||
const discountService: DiscountService = req.scope.resolve("discountService")
|
||||
await discountService.removeRegion(discount_id, region_id)
|
||||
|
||||
await discountService.removeRegion(discount_id, region_id)
|
||||
const discount = await discountService.retrieve(discount_id, {
|
||||
select: defaultFields,
|
||||
relations: defaultRelations,
|
||||
})
|
||||
const discount = await discountService.retrieve(discount_id, {
|
||||
select: defaultAdminDiscountsFields,
|
||||
relations: defaultAdminDiscountsRelations,
|
||||
})
|
||||
|
||||
res.status(200).json({ discount })
|
||||
} catch (err) {
|
||||
throw err
|
||||
}
|
||||
res.status(200).json({ discount })
|
||||
}
|
||||
+10
-14
@@ -1,10 +1,11 @@
|
||||
import { defaultFields, defaultRelations } from "./"
|
||||
|
||||
import DiscountService from "../../../../services/discount"
|
||||
import { defaultAdminDiscountsFields, defaultAdminDiscountsRelations } from "."
|
||||
/**
|
||||
* @oas [post] /discounts/{id}/products/{product_id}
|
||||
* operationId: "DeleteDiscountsDiscountProductsProduct"
|
||||
* summary: "Remove Product availability"
|
||||
* description: "Removes a Product from the list of Products that a Discount can be used for."
|
||||
* x-authenticated: true
|
||||
* parameters:
|
||||
* - (path) id=* {string} The id of the Discount.
|
||||
* - (path) product_id=* {string} The id of the Product.
|
||||
@@ -23,18 +24,13 @@ import { defaultFields, defaultRelations } from "./"
|
||||
export default async (req, res) => {
|
||||
const { discount_id, variant_id } = req.params
|
||||
|
||||
try {
|
||||
const discountService = req.scope.resolve("discountService")
|
||||
const discountService: DiscountService = req.scope.resolve("discountService")
|
||||
await discountService.removeValidProduct(discount_id, variant_id)
|
||||
|
||||
await discountService.removeValidProduct(discount_id, variant_id)
|
||||
const discount = await discountService.retrieve(discount_id, {
|
||||
select: defaultAdminDiscountsFields,
|
||||
relations: defaultAdminDiscountsRelations,
|
||||
})
|
||||
|
||||
const discount = await discountService.retrieve(discount_id, {
|
||||
select: defaultFields,
|
||||
relations: defaultRelations,
|
||||
})
|
||||
|
||||
res.status(200).json({ discount })
|
||||
} catch (err) {
|
||||
throw err
|
||||
}
|
||||
res.status(200).json({ discount })
|
||||
}
|
||||
@@ -1,109 +0,0 @@
|
||||
import { MedusaError, Validator } from "medusa-core-utils"
|
||||
import { defaultFields, defaultRelations } from "./"
|
||||
|
||||
/**
|
||||
* @oas [post] /discounts/{id}
|
||||
* operationId: "PostDiscountsDiscount"
|
||||
* summary: "Update a Discount"
|
||||
* description: "Updates a Discount with a given set of rules that define how the Discount behaves."
|
||||
* parameters:
|
||||
* - (path) id=* {string} The id of the Discount.
|
||||
* requestBody:
|
||||
* content:
|
||||
* application/json:
|
||||
* schema:
|
||||
* properties:
|
||||
* code:
|
||||
* type: string
|
||||
* description: A unique code that will be used to redeem the Discount
|
||||
* is_dynamic:
|
||||
* type: string
|
||||
* description: Whether the Discount should have multiple instances of itself, each with a different code. This can be useful for automatically generated codes that all have to follow a common set of rules.
|
||||
* rule:
|
||||
* description: The Discount Rule that defines how Discounts are calculated
|
||||
* oneOf:
|
||||
* - $ref: "#/components/schemas/discount_rule"
|
||||
* is_disabled:
|
||||
* type: boolean
|
||||
* description: Whether the Discount code is disabled on creation. You will have to enable it later to make it available to Customers.
|
||||
* starts_at:
|
||||
* type: string
|
||||
* format: date-time
|
||||
* description: The time at which the Discount should be available.
|
||||
* ends_at:
|
||||
* type: string
|
||||
* format: date-time
|
||||
* description: The time at which the Discount should no longer be available.
|
||||
* regions:
|
||||
* description: A list of Region ids representing the Regions in which the Discount can be used.
|
||||
* type: array
|
||||
* items:
|
||||
* type: string
|
||||
* tags:
|
||||
* - Discount
|
||||
* responses:
|
||||
* 200:
|
||||
* description: OK
|
||||
* content:
|
||||
* application/json:
|
||||
* schema:
|
||||
* properties:
|
||||
* discount:
|
||||
* $ref: "#/components/schemas/discount"
|
||||
*/
|
||||
export default async (req, res) => {
|
||||
const { discount_id } = req.params
|
||||
const schema = Validator.object().keys({
|
||||
code: Validator.string().optional(),
|
||||
is_dynamic: Validator.boolean().default(false),
|
||||
rule: Validator.object()
|
||||
.keys({
|
||||
id: Validator.string().required(),
|
||||
description: Validator.string().optional(),
|
||||
type: Validator.string().required(),
|
||||
value: Validator.number().required(),
|
||||
allocation: Validator.string().required(),
|
||||
valid_for: Validator.array().items(Validator.string()),
|
||||
})
|
||||
.optional(),
|
||||
is_disabled: Validator.boolean().optional(),
|
||||
starts_at: Validator.date().optional(),
|
||||
ends_at: Validator.when("starts_at", {
|
||||
not: undefined,
|
||||
then: Validator.date()
|
||||
.greater(Validator.ref("starts_at"))
|
||||
.optional(),
|
||||
otherwise: Validator.date().optional(),
|
||||
}),
|
||||
valid_duration: Validator.string()
|
||||
.isoDuration().allow(null)
|
||||
.optional(),
|
||||
usage_limit: Validator.number()
|
||||
.positive()
|
||||
.optional(),
|
||||
regions: Validator.array()
|
||||
.items(Validator.string())
|
||||
.optional(),
|
||||
})
|
||||
|
||||
const { value, error } = schema.validate(req.body)
|
||||
|
||||
if (error) {
|
||||
throw new MedusaError(MedusaError.Types.INVALID_DATA, error.details)
|
||||
}
|
||||
|
||||
try {
|
||||
const discountService = req.scope.resolve("discountService")
|
||||
|
||||
await discountService.update(discount_id, value)
|
||||
|
||||
const discount = await discountService.retrieve(discount_id, {
|
||||
select: defaultFields,
|
||||
relations: defaultRelations,
|
||||
})
|
||||
|
||||
res.status(200).json({ discount })
|
||||
} catch (err) {
|
||||
throw err
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,158 @@
|
||||
import { Type } from "class-transformer"
|
||||
import {
|
||||
IsArray,
|
||||
IsBoolean,
|
||||
IsDate,
|
||||
IsNotEmpty,
|
||||
IsNumber,
|
||||
IsObject,
|
||||
IsOptional,
|
||||
IsPositive,
|
||||
IsString,
|
||||
ValidateNested,
|
||||
} from "class-validator"
|
||||
import { defaultAdminDiscountsFields, defaultAdminDiscountsRelations } from "."
|
||||
import DiscountService from "../../../../services/discount"
|
||||
import { IsGreaterThan } from "../../../../utils/validators/greater-than"
|
||||
import { validator } from "../../../../utils/validator"
|
||||
import { IsISO8601Duration } from "../../../../utils/validators/iso8601-duration"
|
||||
|
||||
/**
|
||||
* @oas [post] /discounts/{id}
|
||||
* operationId: "PostDiscountsDiscount"
|
||||
* summary: "Update a Discount"
|
||||
* description: "Updates a Discount with a given set of rules that define how the Discount behaves."
|
||||
* x-authenticated: true
|
||||
* parameters:
|
||||
* - (path) id=* {string} The id of the Discount.
|
||||
* requestBody:
|
||||
* content:
|
||||
* application/json:
|
||||
* schema:
|
||||
* properties:
|
||||
* code:
|
||||
* type: string
|
||||
* description: A unique code that will be used to redeem the Discount
|
||||
* is_dynamic:
|
||||
* type: string
|
||||
* description: Whether the Discount should have multiple instances of itself, each with a different code. This can be useful for automatically generated codes that all have to follow a common set of rules.
|
||||
* rule:
|
||||
* description: The Discount Rule that defines how Discounts are calculated
|
||||
* oneOf:
|
||||
* - $ref: "#/components/schemas/discount_rule"
|
||||
* is_disabled:
|
||||
* type: boolean
|
||||
* description: Whether the Discount code is disabled on creation. You will have to enable it later to make it available to Customers.
|
||||
* starts_at:
|
||||
* type: Date
|
||||
* description: The time at which the Discount should be available.
|
||||
* ends_at:
|
||||
* type: Date
|
||||
* description: The time at which the Discount should no longer be available.
|
||||
* regions:
|
||||
* description: A list of Region ids representing the Regions in which the Discount can be used.
|
||||
* type: array
|
||||
* items:
|
||||
* type: string
|
||||
* metadata:
|
||||
* description: An object containing metadata of the discount
|
||||
* type: object
|
||||
* tags:
|
||||
* - Discount
|
||||
* responses:
|
||||
* 200:
|
||||
* description: OK
|
||||
* content:
|
||||
* application/json:
|
||||
* schema:
|
||||
* properties:
|
||||
* discount:
|
||||
* $ref: "#/components/schemas/discount"
|
||||
*/
|
||||
export default async (req, res) => {
|
||||
const { discount_id } = req.params
|
||||
|
||||
const validated = await validator(AdminPostDiscountsDiscountReq, req.body)
|
||||
const discountService: DiscountService = req.scope.resolve("discountService")
|
||||
await discountService.update(discount_id, validated)
|
||||
const discount = await discountService.retrieve(discount_id, {
|
||||
select: defaultAdminDiscountsFields,
|
||||
relations: defaultAdminDiscountsRelations,
|
||||
})
|
||||
|
||||
res.status(200).json({ discount })
|
||||
}
|
||||
|
||||
export class AdminPostDiscountsDiscountReq {
|
||||
@IsString()
|
||||
@IsOptional()
|
||||
code?: string
|
||||
|
||||
@IsOptional()
|
||||
@ValidateNested()
|
||||
@Type(() => AdminUpdateDiscountRule)
|
||||
rule?: AdminUpdateDiscountRule
|
||||
|
||||
@IsBoolean()
|
||||
@IsOptional()
|
||||
is_dynamic?: boolean
|
||||
|
||||
@IsBoolean()
|
||||
@IsOptional()
|
||||
is_disabled?: boolean
|
||||
|
||||
@IsDate()
|
||||
@IsOptional()
|
||||
@Type(() => Date)
|
||||
starts_at?: Date
|
||||
|
||||
@IsDate()
|
||||
@IsOptional()
|
||||
@IsGreaterThan("starts_at")
|
||||
@Type(() => Date)
|
||||
ends_at?: Date
|
||||
|
||||
@IsISO8601Duration()
|
||||
@IsOptional()
|
||||
valid_duration?: string
|
||||
|
||||
@IsNumber()
|
||||
@IsOptional()
|
||||
@IsPositive()
|
||||
usage_limit?: number
|
||||
|
||||
@IsArray()
|
||||
@IsOptional()
|
||||
@IsString({ each: true })
|
||||
regions?: string[]
|
||||
|
||||
@IsObject()
|
||||
@IsOptional()
|
||||
metadata?: object
|
||||
}
|
||||
|
||||
export class AdminUpdateDiscountRule {
|
||||
@IsString()
|
||||
@IsNotEmpty()
|
||||
id: string
|
||||
|
||||
@IsString()
|
||||
@IsOptional()
|
||||
description?: string
|
||||
|
||||
@IsString()
|
||||
@IsNotEmpty()
|
||||
type: string
|
||||
|
||||
@IsNumber()
|
||||
value: string
|
||||
|
||||
@IsString()
|
||||
@IsNotEmpty()
|
||||
allocation: string
|
||||
|
||||
@IsArray()
|
||||
@IsOptional()
|
||||
@IsString({ each: true })
|
||||
valid_for?: string
|
||||
}
|
||||
+136
-68
@@ -1,19 +1,40 @@
|
||||
import { Type } from "class-transformer"
|
||||
import {
|
||||
MedusaError,
|
||||
Validator,
|
||||
transformIdableFields,
|
||||
} from "medusa-core-utils"
|
||||
import { defaultFields, defaultRelations } from "."
|
||||
|
||||
IsArray,
|
||||
IsBoolean,
|
||||
IsEmail,
|
||||
IsEnum,
|
||||
IsNotEmpty,
|
||||
IsNumber,
|
||||
IsObject,
|
||||
IsOptional,
|
||||
IsString,
|
||||
ValidateNested,
|
||||
} from "class-validator"
|
||||
import { transformIdableFields } from "medusa-core-utils"
|
||||
import {
|
||||
defaultAdminDraftOrdersFields,
|
||||
defaultAdminDraftOrdersRelations,
|
||||
} from "."
|
||||
import { DraftOrder } from "../../../.."
|
||||
import { DraftOrderService } from "../../../../services"
|
||||
import { AddressPayload } from "../../../../types/common"
|
||||
import { validator } from "../../../../utils/validator"
|
||||
/**
|
||||
* @oas [post] /draft-orders
|
||||
* operationId: "PostDraftOrders"
|
||||
* summary: "Create a Draft Order"
|
||||
* description: "Creates a Draft Order"
|
||||
* x-authenticated: true
|
||||
* requestBody:
|
||||
* content:
|
||||
* application/json:
|
||||
* schema:
|
||||
* required:
|
||||
* - email
|
||||
* - items
|
||||
* - region_id
|
||||
* - shipping_methods
|
||||
* properties:
|
||||
* status:
|
||||
* description: "The status of the draft order"
|
||||
@@ -97,68 +118,115 @@ import { defaultFields, defaultRelations } from "."
|
||||
*/
|
||||
|
||||
export default async (req, res) => {
|
||||
const schema = Validator.object().keys({
|
||||
status: Validator.string()
|
||||
.valid("open", "completed")
|
||||
.optional(),
|
||||
email: Validator.string()
|
||||
.email()
|
||||
.required(),
|
||||
billing_address: Validator.address().optional(),
|
||||
shipping_address: Validator.address().optional(),
|
||||
items: Validator.array()
|
||||
.items({
|
||||
variant_id: Validator.string()
|
||||
.optional()
|
||||
.allow(""),
|
||||
unit_price: Validator.number().optional(),
|
||||
title: Validator.string()
|
||||
.optional()
|
||||
.allow(""),
|
||||
quantity: Validator.number().required(),
|
||||
metadata: Validator.object().default({}),
|
||||
})
|
||||
.required(),
|
||||
region_id: Validator.string().required(),
|
||||
discounts: Validator.array()
|
||||
.items({
|
||||
code: Validator.string().required(),
|
||||
})
|
||||
.optional(),
|
||||
customer_id: Validator.string().optional(),
|
||||
no_notification_order: Validator.boolean().optional(),
|
||||
shipping_methods: Validator.array()
|
||||
.items({
|
||||
option_id: Validator.string().required(),
|
||||
data: Validator.object().optional(),
|
||||
price: Validator.number()
|
||||
.integer()
|
||||
.integer()
|
||||
.allow(0)
|
||||
.optional(),
|
||||
})
|
||||
.required(),
|
||||
metadata: Validator.object().optional(),
|
||||
const validated = await validator(AdminPostDraftOrdersReq, req.body)
|
||||
|
||||
const value = transformIdableFields(validated, [
|
||||
"shipping_address",
|
||||
"billing_address",
|
||||
])
|
||||
|
||||
const draftOrderService: DraftOrderService =
|
||||
req.scope.resolve("draftOrderService")
|
||||
let draftOrder: DraftOrder = await draftOrderService.create(value)
|
||||
|
||||
draftOrder = await draftOrderService.retrieve(draftOrder.id, {
|
||||
relations: defaultAdminDraftOrdersRelations,
|
||||
select: defaultAdminDraftOrdersFields,
|
||||
})
|
||||
|
||||
let { value, error } = schema.validate(req.body)
|
||||
if (error) {
|
||||
throw new MedusaError(MedusaError.Types.INVALID_DATA, error.details)
|
||||
}
|
||||
|
||||
value = transformIdableFields(value, ["shipping_address", "billing_address"])
|
||||
|
||||
try {
|
||||
const draftOrderService = req.scope.resolve("draftOrderService")
|
||||
let draftOrder = await draftOrderService.create(value)
|
||||
|
||||
draftOrder = await draftOrderService.retrieve(draftOrder.id, {
|
||||
relations: defaultRelations,
|
||||
select: defaultFields,
|
||||
})
|
||||
|
||||
res.status(200).json({ draft_order: draftOrder })
|
||||
} catch (err) {
|
||||
throw err
|
||||
}
|
||||
res.status(200).json({ draft_order: draftOrder })
|
||||
}
|
||||
|
||||
enum Status {
|
||||
open = "open",
|
||||
completed = "completed",
|
||||
}
|
||||
|
||||
export class AdminPostDraftOrdersReq {
|
||||
@IsEnum(Status)
|
||||
@IsOptional()
|
||||
status?: string
|
||||
|
||||
@IsEmail()
|
||||
email: string
|
||||
|
||||
@IsOptional()
|
||||
@Type(() => AddressPayload)
|
||||
billing_address?: AddressPayload
|
||||
|
||||
@IsOptional()
|
||||
@Type(() => AddressPayload)
|
||||
shipping_address?: AddressPayload
|
||||
|
||||
@IsArray()
|
||||
@Type(() => Item)
|
||||
@IsNotEmpty()
|
||||
@ValidateNested({ each: true })
|
||||
items: Item[]
|
||||
|
||||
@IsString()
|
||||
region_id: string
|
||||
|
||||
@IsArray()
|
||||
@IsOptional()
|
||||
@Type(() => Discount)
|
||||
@ValidateNested({ each: true })
|
||||
discounts?: Discount[]
|
||||
|
||||
@IsString()
|
||||
@IsOptional()
|
||||
customer_id?: string
|
||||
|
||||
@IsBoolean()
|
||||
@IsOptional()
|
||||
no_notification_order?: boolean
|
||||
|
||||
@IsArray()
|
||||
@Type(() => ShippingMethod)
|
||||
@IsNotEmpty()
|
||||
@ValidateNested({ each: true })
|
||||
shipping_methods: ShippingMethod[]
|
||||
|
||||
@IsObject()
|
||||
@IsOptional()
|
||||
metadata?: object = {}
|
||||
}
|
||||
|
||||
class ShippingMethod {
|
||||
@IsString()
|
||||
option_id: string
|
||||
|
||||
@IsObject()
|
||||
@IsOptional()
|
||||
data?: object = {}
|
||||
|
||||
@IsNumber()
|
||||
@IsOptional()
|
||||
price?: number
|
||||
}
|
||||
|
||||
class Discount {
|
||||
@IsString()
|
||||
code: string
|
||||
}
|
||||
|
||||
class Item {
|
||||
@IsString()
|
||||
@IsOptional()
|
||||
title?: string
|
||||
|
||||
@IsNumber()
|
||||
@IsOptional()
|
||||
unit_price?: number
|
||||
|
||||
@IsString()
|
||||
@IsOptional()
|
||||
variant_id?: string
|
||||
|
||||
@IsNumber()
|
||||
quantity: number
|
||||
|
||||
@IsObject()
|
||||
@IsOptional()
|
||||
metadata?: object = {}
|
||||
}
|
||||
@@ -1,111 +0,0 @@
|
||||
import { MedusaError, Validator } from "medusa-core-utils"
|
||||
import { defaultCartFields, defaultCartRelations, defaultFields } from "."
|
||||
|
||||
/**
|
||||
* @oas [post] /draft-orders/{id}/line-items
|
||||
* operationId: "PostDraftOrdersDraftOrderLineItems"
|
||||
* summary: "Create a Line Item for Draft Order"
|
||||
* description: "Creates a Line Item for the Draft Order"
|
||||
* requestBody:
|
||||
* content:
|
||||
* application/json:
|
||||
* schema:
|
||||
* properties:
|
||||
* variant_id:
|
||||
* description: The id of the Product Variant to generate the Line Item from.
|
||||
* type: string
|
||||
* unit_price:
|
||||
* description: The potential custom price of the item.
|
||||
* type: integer
|
||||
* title:
|
||||
* description: The potential custom title of the item.
|
||||
* type: string
|
||||
* quantity:
|
||||
* description: The quantity of the Line Item.
|
||||
* type: integer
|
||||
* metadata:
|
||||
* description: The optional key-value map with additional details about the Line Item.
|
||||
* type: object
|
||||
* tags:
|
||||
* - Draft Order
|
||||
* responses:
|
||||
* 200:
|
||||
* description: OK
|
||||
* content:
|
||||
* application/json:
|
||||
* schema:
|
||||
* properties:
|
||||
* draft_order:
|
||||
* $ref: "#/components/schemas/draft-order"
|
||||
*/
|
||||
|
||||
export default async (req, res) => {
|
||||
const { id } = req.params
|
||||
|
||||
const schema = Validator.object().keys({
|
||||
title: Validator.string().optional(),
|
||||
unit_price: Validator.number().optional(),
|
||||
variant_id: Validator.string().optional(),
|
||||
quantity: Validator.number().required(),
|
||||
metadata: Validator.object().optional(),
|
||||
})
|
||||
|
||||
const { value, error } = schema.validate(req.body)
|
||||
if (error) {
|
||||
throw new MedusaError(MedusaError.Types.INVALID_DATA, error.details)
|
||||
}
|
||||
|
||||
try {
|
||||
const draftOrderService = req.scope.resolve("draftOrderService")
|
||||
const cartService = req.scope.resolve("cartService")
|
||||
const lineItemService = req.scope.resolve("lineItemService")
|
||||
const entityManager = req.scope.resolve("manager")
|
||||
|
||||
await entityManager.transaction(async manager => {
|
||||
const draftOrder = await draftOrderService
|
||||
.withTransaction(manager)
|
||||
.retrieve(id, { select: defaultFields, relations: ["cart"] })
|
||||
|
||||
if (draftOrder.status === "completed") {
|
||||
throw new MedusaError(
|
||||
MedusaError.Types.NOT_ALLOWED,
|
||||
"You are only allowed to update open draft orders"
|
||||
)
|
||||
}
|
||||
|
||||
if (value.variant_id) {
|
||||
const line = await lineItemService.generate(
|
||||
value.variant_id,
|
||||
draftOrder.cart.region_id,
|
||||
value.quantity,
|
||||
{ metadata: value.metadata, unit_price: value.unit_price }
|
||||
)
|
||||
|
||||
await cartService
|
||||
.withTransaction(manager)
|
||||
.addLineItem(draftOrder.cart_id, line)
|
||||
} else {
|
||||
// custom line items can be added to a draft order
|
||||
await lineItemService.withTransaction(manager).create({
|
||||
cart_id: draftOrder.cart_id,
|
||||
has_shipping: true,
|
||||
title: value.title || "Custom item",
|
||||
allow_discounts: false,
|
||||
unit_price: value.unit_price || 0,
|
||||
quantity: value.quantity,
|
||||
})
|
||||
}
|
||||
|
||||
draftOrder.cart = await cartService
|
||||
.withTransaction(manager)
|
||||
.retrieve(draftOrder.cart_id, {
|
||||
relations: defaultCartRelations,
|
||||
select: defaultCartFields,
|
||||
})
|
||||
|
||||
res.status(200).json({ draft_order: draftOrder })
|
||||
})
|
||||
} catch (err) {
|
||||
throw err
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,138 @@
|
||||
import { IsInt, IsObject, IsOptional, IsString } from "class-validator"
|
||||
import { MedusaError } from "medusa-core-utils"
|
||||
import { EntityManager } from "typeorm"
|
||||
import {
|
||||
defaultAdminDraftOrdersCartFields,
|
||||
defaultAdminDraftOrdersCartRelations,
|
||||
defaultAdminDraftOrdersFields,
|
||||
} from "."
|
||||
import {
|
||||
CartService,
|
||||
DraftOrderService,
|
||||
LineItemService,
|
||||
} from "../../../../services"
|
||||
import { validator } from "../../../../utils/validator"
|
||||
/**
|
||||
* @oas [post] /draft-orders/{id}/line-items
|
||||
* operationId: "PostDraftOrdersDraftOrderLineItems"
|
||||
* summary: "Create a Line Item for Draft Order"
|
||||
* description: "Creates a Line Item for the Draft Order"
|
||||
* x-authenticated: true
|
||||
* requestBody:
|
||||
* content:
|
||||
* application/json:
|
||||
* required:
|
||||
* - quantity
|
||||
* schema:
|
||||
* properties:
|
||||
* variant_id:
|
||||
* description: The id of the Product Variant to generate the Line Item from.
|
||||
* type: string
|
||||
* unit_price:
|
||||
* description: The potential custom price of the item.
|
||||
* type: integer
|
||||
* title:
|
||||
* description: The potential custom title of the item.
|
||||
* type: string
|
||||
* quantity:
|
||||
* description: The quantity of the Line Item.
|
||||
* type: integer
|
||||
* metadata:
|
||||
* description: The optional key-value map with additional details about the Line Item.
|
||||
* type: object
|
||||
* tags:
|
||||
* - Draft Order
|
||||
* responses:
|
||||
* 200:
|
||||
* description: OK
|
||||
* content:
|
||||
* application/json:
|
||||
* schema:
|
||||
* properties:
|
||||
* draft_order:
|
||||
* $ref: "#/components/schemas/draft-order"
|
||||
*/
|
||||
|
||||
export default async (req, res) => {
|
||||
const { id } = req.params
|
||||
|
||||
const validated = await validator(
|
||||
AdminPostDraftOrdersDraftOrderLineItemsReq,
|
||||
req.body
|
||||
)
|
||||
|
||||
const draftOrderService: DraftOrderService =
|
||||
req.scope.resolve("draftOrderService")
|
||||
const cartService: CartService = req.scope.resolve("cartService")
|
||||
const lineItemService: LineItemService = req.scope.resolve("lineItemService")
|
||||
const entityManager: EntityManager = req.scope.resolve("manager")
|
||||
|
||||
await entityManager.transaction(async (manager) => {
|
||||
const draftOrder = await draftOrderService
|
||||
.withTransaction(manager)
|
||||
.retrieve(id, {
|
||||
select: defaultAdminDraftOrdersFields,
|
||||
relations: ["cart"],
|
||||
})
|
||||
|
||||
if (draftOrder.status === "completed") {
|
||||
throw new MedusaError(
|
||||
MedusaError.Types.NOT_ALLOWED,
|
||||
"You are only allowed to update open draft orders"
|
||||
)
|
||||
}
|
||||
|
||||
if (validated.variant_id) {
|
||||
const line = await lineItemService.generate(
|
||||
validated.variant_id,
|
||||
draftOrder.cart.region_id,
|
||||
validated.quantity,
|
||||
{ metadata: validated.metadata, unit_price: validated.unit_price }
|
||||
)
|
||||
|
||||
await cartService
|
||||
.withTransaction(manager)
|
||||
.addLineItem(draftOrder.cart_id, line)
|
||||
} else {
|
||||
// custom line items can be added to a draft order
|
||||
await lineItemService.withTransaction(manager).create({
|
||||
cart_id: draftOrder.cart_id,
|
||||
has_shipping: true,
|
||||
title: validated.title,
|
||||
allow_discounts: false,
|
||||
unit_price: validated.unit_price || 0,
|
||||
quantity: validated.quantity,
|
||||
})
|
||||
}
|
||||
|
||||
draftOrder.cart = await cartService
|
||||
.withTransaction(manager)
|
||||
.retrieve(draftOrder.cart_id, {
|
||||
relations: defaultAdminDraftOrdersCartRelations,
|
||||
select: defaultAdminDraftOrdersCartFields,
|
||||
})
|
||||
|
||||
res.status(200).json({ draft_order: draftOrder })
|
||||
})
|
||||
}
|
||||
|
||||
export class AdminPostDraftOrdersDraftOrderLineItemsReq {
|
||||
@IsString()
|
||||
@IsOptional()
|
||||
title?: string = "Custom item"
|
||||
|
||||
@IsInt()
|
||||
@IsOptional()
|
||||
unit_price?: number
|
||||
|
||||
@IsString()
|
||||
@IsOptional()
|
||||
variant_id?: string
|
||||
|
||||
@IsInt()
|
||||
quantity: number
|
||||
|
||||
@IsObject()
|
||||
@IsOptional()
|
||||
metadata?: object = {}
|
||||
}
|
||||
+11
-12
@@ -1,8 +1,10 @@
|
||||
import { DraftOrderService } from "../../../../services"
|
||||
/**
|
||||
* @oas [delete] /draft-orders/{id}
|
||||
* operationId: DeleteDraftOrdersDraftOrder
|
||||
* summary: Delete a Draft Order
|
||||
* description: "Deletes a Draft Order"
|
||||
* x-authenticated: true
|
||||
* parameters:
|
||||
* - (path) id=* {string} The id of the Draft Order.
|
||||
* tags:
|
||||
@@ -23,19 +25,16 @@
|
||||
* deleted:
|
||||
* type: boolean
|
||||
*/
|
||||
|
||||
export default async (req, res) => {
|
||||
const { id } = req.params
|
||||
|
||||
try {
|
||||
const draftOrderService = req.scope.resolve("draftOrderService")
|
||||
await draftOrderService.delete(id)
|
||||
res.json({
|
||||
id,
|
||||
object: "draft-order",
|
||||
deleted: true,
|
||||
})
|
||||
} catch (err) {
|
||||
throw err
|
||||
}
|
||||
const draftOrderService: DraftOrderService =
|
||||
req.scope.resolve("draftOrderService")
|
||||
await draftOrderService.delete(id)
|
||||
|
||||
res.json({
|
||||
id,
|
||||
object: "draft-order",
|
||||
deleted: true,
|
||||
})
|
||||
}
|
||||
@@ -1,61 +0,0 @@
|
||||
import { MedusaError, Validator } from "medusa-core-utils"
|
||||
import { defaultCartFields, defaultCartRelations, defaultFields } from "."
|
||||
|
||||
/**
|
||||
* @oas [delete] /draft-orders/{id}/line-items/{line_id}
|
||||
* operationId: DeleteDraftOrdersDraftOrderLineItemsItem
|
||||
* summary: Delete a Line Item
|
||||
* description: "Removes a Line Item from a Draft Order."
|
||||
* parameters:
|
||||
* - (path) id=* {string} The id of the Draft Order.
|
||||
* - (path) line_id=* {string} The id of the Draft Order.
|
||||
* tags:
|
||||
* - Draft Order
|
||||
* responses:
|
||||
* 200:
|
||||
* description: OK
|
||||
* content:
|
||||
* application/json:
|
||||
* schema:
|
||||
* properties:
|
||||
* draft_order:
|
||||
* $ref: "#/components/schemas/draft-order"
|
||||
*/
|
||||
|
||||
export default async (req, res) => {
|
||||
const { id, line_id } = req.params
|
||||
|
||||
try {
|
||||
const draftOrderService = req.scope.resolve("draftOrderService")
|
||||
const cartService = req.scope.resolve("cartService")
|
||||
const entityManager = req.scope.resolve("manager")
|
||||
|
||||
await entityManager.transaction(async manager => {
|
||||
const draftOrder = await draftOrderService
|
||||
.withTransaction(manager)
|
||||
.retrieve(id, { select: defaultFields })
|
||||
|
||||
if (draftOrder.status === "completed") {
|
||||
throw new MedusaError(
|
||||
MedusaError.Types.NOT_ALLOWED,
|
||||
"You are only allowed to update open draft orders"
|
||||
)
|
||||
}
|
||||
|
||||
await cartService
|
||||
.withTransaction(manager)
|
||||
.removeLineItem(draftOrder.cart_id, line_id)
|
||||
|
||||
draftOrder.cart = await cartService
|
||||
.withTransaction(manager)
|
||||
.retrieve(draftOrder.cart_id, {
|
||||
relations: defaultCartRelations,
|
||||
select: defaultCartFields,
|
||||
})
|
||||
|
||||
res.status(200).json({ draft_order: draftOrder })
|
||||
})
|
||||
} catch (err) {
|
||||
throw err
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,65 @@
|
||||
import { MedusaError } from "medusa-core-utils"
|
||||
import { EntityManager } from "typeorm"
|
||||
import {
|
||||
defaultAdminDraftOrdersCartFields,
|
||||
defaultAdminDraftOrdersCartRelations,
|
||||
defaultAdminDraftOrdersFields,
|
||||
} from "."
|
||||
import { DraftOrder } from "../../../.."
|
||||
import { CartService, DraftOrderService } from "../../../../services"
|
||||
/**
|
||||
* @oas [delete] /draft-orders/{id}/line-items/{line_id}
|
||||
* operationId: DeleteDraftOrdersDraftOrderLineItemsItem
|
||||
* summary: Delete a Line Item
|
||||
* description: "Removes a Line Item from a Draft Order."
|
||||
* x-authenticated: true
|
||||
* parameters:
|
||||
* - (path) id=* {string} The id of the Draft Order.
|
||||
* - (path) line_id=* {string} The id of the Draft Order.
|
||||
* tags:
|
||||
* - Draft Order
|
||||
* responses:
|
||||
* 200:
|
||||
* description: OK
|
||||
* content:
|
||||
* application/json:
|
||||
* schema:
|
||||
* properties:
|
||||
* draft_order:
|
||||
* $ref: "#/components/schemas/draft-order"
|
||||
*/
|
||||
|
||||
export default async (req, res) => {
|
||||
const { id, line_id } = req.params
|
||||
|
||||
const draftOrderService: DraftOrderService =
|
||||
req.scope.resolve("draftOrderService")
|
||||
const cartService: CartService = req.scope.resolve("cartService")
|
||||
const entityManager: EntityManager = req.scope.resolve("manager")
|
||||
|
||||
await entityManager.transaction(async (manager) => {
|
||||
const draftOrder: DraftOrder = await draftOrderService
|
||||
.withTransaction(manager)
|
||||
.retrieve(id, { select: defaultAdminDraftOrdersFields })
|
||||
|
||||
if (draftOrder.status === "completed") {
|
||||
throw new MedusaError(
|
||||
MedusaError.Types.NOT_ALLOWED,
|
||||
"You are only allowed to update open draft orders"
|
||||
)
|
||||
}
|
||||
|
||||
await cartService
|
||||
.withTransaction(manager)
|
||||
.removeLineItem(draftOrder.cart_id, line_id)
|
||||
|
||||
draftOrder.cart = await cartService
|
||||
.withTransaction(manager)
|
||||
.retrieve(draftOrder.cart_id, {
|
||||
relations: defaultAdminDraftOrdersCartRelations,
|
||||
select: defaultAdminDraftOrdersCartFields,
|
||||
})
|
||||
|
||||
res.status(200).json({ draft_order: draftOrder })
|
||||
})
|
||||
}
|
||||
@@ -1,49 +0,0 @@
|
||||
import {
|
||||
defaultRelations,
|
||||
defaultFields,
|
||||
defaultCartRelations,
|
||||
defaultCartFields,
|
||||
} from "."
|
||||
|
||||
/**
|
||||
* @oas [get] /draft-orders/{id}
|
||||
* operationId: "GetDraftOrdersDraftOrder"
|
||||
* summary: "Retrieve a Draft Order"
|
||||
* description: "Retrieves a Draft Order."
|
||||
* parameters:
|
||||
* - (path) id=* {string} The id of the Draft Order.
|
||||
* tags:
|
||||
* - Draft Order
|
||||
* responses:
|
||||
* 200:
|
||||
* description: OK
|
||||
* content:
|
||||
* application/json:
|
||||
* schema:
|
||||
* properties:
|
||||
* draft_order:
|
||||
* $ref: "#/components/schemas/draft-order"
|
||||
*/
|
||||
|
||||
export default async (req, res) => {
|
||||
const { id } = req.params
|
||||
|
||||
try {
|
||||
const draftOrderService = req.scope.resolve("draftOrderService")
|
||||
const cartService = req.scope.resolve("cartService")
|
||||
|
||||
const draftOrder = await draftOrderService.retrieve(id, {
|
||||
select: defaultFields,
|
||||
relations: defaultRelations,
|
||||
})
|
||||
|
||||
draftOrder.cart = await cartService.retrieve(draftOrder.cart_id, {
|
||||
relations: defaultCartRelations,
|
||||
select: defaultCartFields,
|
||||
})
|
||||
|
||||
res.json({ draft_order: draftOrder })
|
||||
} catch (error) {
|
||||
throw error
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,49 @@
|
||||
import {
|
||||
defaultAdminDraftOrdersRelations,
|
||||
defaultAdminDraftOrdersFields,
|
||||
defaultAdminDraftOrdersCartRelations,
|
||||
defaultAdminDraftOrdersCartFields,
|
||||
} from "."
|
||||
import { DraftOrder } from "../../../.."
|
||||
import { CartService, DraftOrderService } from "../../../../services"
|
||||
|
||||
/**
|
||||
* @oas [get] /draft-orders/{id}
|
||||
* operationId: "GetDraftOrdersDraftOrder"
|
||||
* summary: "Retrieve a Draft Order"
|
||||
* description: "Retrieves a Draft Order."
|
||||
* x-authenticated: true
|
||||
* parameters:
|
||||
* - (path) id=* {string} The id of the Draft Order.
|
||||
* tags:
|
||||
* - Draft Order
|
||||
* responses:
|
||||
* 200:
|
||||
* description: OK
|
||||
* content:
|
||||
* application/json:
|
||||
* schema:
|
||||
* properties:
|
||||
* draft_order:
|
||||
* $ref: "#/components/schemas/draft-order"
|
||||
*/
|
||||
|
||||
export default async (req, res) => {
|
||||
const { id } = req.params
|
||||
|
||||
const draftOrderService: DraftOrderService =
|
||||
req.scope.resolve("draftOrderService")
|
||||
const cartService: CartService = req.scope.resolve("cartService")
|
||||
|
||||
const draftOrder: DraftOrder = await draftOrderService.retrieve(id, {
|
||||
select: defaultAdminDraftOrdersFields,
|
||||
relations: defaultAdminDraftOrdersRelations,
|
||||
})
|
||||
|
||||
draftOrder.cart = await cartService.retrieve(draftOrder.cart_id, {
|
||||
relations: defaultAdminDraftOrdersCartRelations,
|
||||
select: defaultAdminDraftOrdersCartFields,
|
||||
})
|
||||
|
||||
res.json({ draft_order: draftOrder })
|
||||
}
|
||||
+33
-7
@@ -1,9 +1,11 @@
|
||||
import { Router } from "express"
|
||||
import { DraftOrder, Order } from "../../../.."
|
||||
import middlewares from "../../../middlewares"
|
||||
import { DeleteResponse, PaginatedResponse } from "../../../../types/common"
|
||||
|
||||
const route = Router()
|
||||
|
||||
export default app => {
|
||||
export default (app) => {
|
||||
app.use("/draft-orders", route)
|
||||
|
||||
route.get("/", middlewares.wrap(require("./list-draft-orders").default))
|
||||
@@ -44,9 +46,9 @@ export default app => {
|
||||
return app
|
||||
}
|
||||
|
||||
export const defaultRelations = ["order", "cart"]
|
||||
export const defaultAdminDraftOrdersRelations = ["order", "cart"]
|
||||
|
||||
export const defaultCartRelations = [
|
||||
export const defaultAdminDraftOrdersCartRelations = [
|
||||
"region",
|
||||
"items",
|
||||
"payment",
|
||||
@@ -60,7 +62,7 @@ export const defaultCartRelations = [
|
||||
"discounts.rule",
|
||||
]
|
||||
|
||||
export const defaultCartFields = [
|
||||
export const defaultAdminDraftOrdersCartFields = [
|
||||
"subtotal",
|
||||
"tax_total",
|
||||
"shipping_total",
|
||||
@@ -69,7 +71,7 @@ export const defaultCartFields = [
|
||||
"total",
|
||||
]
|
||||
|
||||
export const defaultFields = [
|
||||
export const defaultAdminDraftOrdersFields = [
|
||||
"id",
|
||||
"status",
|
||||
"display_id",
|
||||
@@ -82,7 +84,7 @@ export const defaultFields = [
|
||||
"no_notification_order",
|
||||
]
|
||||
|
||||
export const allowedFields = [
|
||||
export const allowedAdminDraftOrdersFields = [
|
||||
"id",
|
||||
"status",
|
||||
"display_id",
|
||||
@@ -94,4 +96,28 @@ export const allowedFields = [
|
||||
"no_notification_order",
|
||||
]
|
||||
|
||||
export const allowedRelations = ["cart"]
|
||||
export const allowedAdminDraftOrdersRelations = ["cart"]
|
||||
|
||||
export type AdminPostDraftOrdersDraftOrderRegisterPaymentRes = {
|
||||
order: Order
|
||||
}
|
||||
|
||||
export type AdminDraftOrdersRes = {
|
||||
draft_order: DraftOrder
|
||||
}
|
||||
|
||||
export type AdminDraftOrdersDeleteRes = DeleteResponse
|
||||
|
||||
export type AdminDraftOrdersListRes = PaginatedResponse & {
|
||||
draft_orders: DraftOrder[]
|
||||
}
|
||||
|
||||
export * from "./create-draft-order"
|
||||
export * from "./create-line-item"
|
||||
export * from "./delete-draft-order"
|
||||
export * from "./delete-line-item"
|
||||
export * from "./get-draft-order"
|
||||
export * from "./list-draft-orders"
|
||||
export * from "./register-payment"
|
||||
export * from "./update-draft-order"
|
||||
export * from "./update-line-item"
|
||||
@@ -1,52 +0,0 @@
|
||||
import _ from "lodash"
|
||||
import { defaultFields, defaultRelations } from "./"
|
||||
|
||||
/**
|
||||
* @oas [get] /draft-orders
|
||||
* operationId: "GetDraftOrders"
|
||||
* summary: "List Draft Orders"
|
||||
* description: "Retrieves an list of Draft Orders"
|
||||
* tags:
|
||||
* - Draft Order
|
||||
* responses:
|
||||
* 200:
|
||||
* description: OK
|
||||
* content:
|
||||
* application/json:
|
||||
* schema:
|
||||
* properties:
|
||||
* draft_order:
|
||||
* $ref: "#/components/schemas/draft-order"
|
||||
*/
|
||||
|
||||
export default async (req, res) => {
|
||||
try {
|
||||
const draftOrderService = req.scope.resolve("draftOrderService")
|
||||
|
||||
const limit = parseInt(req.query.limit) || 50
|
||||
const offset = parseInt(req.query.offset) || 0
|
||||
|
||||
let selector = {}
|
||||
|
||||
if ("q" in req.query) {
|
||||
selector.q = req.query.q
|
||||
}
|
||||
|
||||
const listConfig = {
|
||||
select: defaultFields,
|
||||
relations: defaultRelations,
|
||||
skip: offset,
|
||||
take: limit,
|
||||
order: { created_at: "DESC" },
|
||||
}
|
||||
|
||||
const [draftOrders, count] = await draftOrderService.listAndCount(
|
||||
selector,
|
||||
listConfig
|
||||
)
|
||||
|
||||
res.json({ draft_orders: draftOrders, count, offset, limit })
|
||||
} catch (error) {
|
||||
throw error
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,76 @@
|
||||
import {
|
||||
defaultAdminDraftOrdersFields,
|
||||
defaultAdminDraftOrdersRelations,
|
||||
} from "."
|
||||
import { DraftOrderService } from "../../../../services"
|
||||
import { IsNumber, IsOptional, IsString } from "class-validator"
|
||||
import { validator } from "../../../../utils/validator"
|
||||
import { Type } from "class-transformer"
|
||||
import { DraftOrderListSelector } from "../../../../types/draft-orders"
|
||||
/**
|
||||
* @oas [get] /draft-orders
|
||||
* operationId: "GetDraftOrders"
|
||||
* summary: "List Draft Orders"
|
||||
* description: "Retrieves an list of Draft Orders"
|
||||
* x-authenticated: true
|
||||
* tags:
|
||||
* - Draft Order
|
||||
* responses:
|
||||
* 200:
|
||||
* description: OK
|
||||
* content:
|
||||
* application/json:
|
||||
* schema:
|
||||
* properties:
|
||||
* draft_order:
|
||||
* $ref: "#/components/schemas/draft-order"
|
||||
*/
|
||||
|
||||
export default async (req, res) => {
|
||||
const draftOrderService: DraftOrderService =
|
||||
req.scope.resolve("draftOrderService")
|
||||
|
||||
const validated = await validator(AdminGetDraftOrdersParams, req.query)
|
||||
|
||||
const selector: DraftOrderListSelector = {}
|
||||
|
||||
if (validated.q) {
|
||||
selector.q = validated.q
|
||||
}
|
||||
|
||||
const listConfig = {
|
||||
select: defaultAdminDraftOrdersFields,
|
||||
relations: defaultAdminDraftOrdersRelations,
|
||||
skip: validated.offset,
|
||||
take: validated.limit,
|
||||
order: { created_at: "DESC" },
|
||||
}
|
||||
|
||||
const [draftOrders, count] = await draftOrderService.listAndCount(
|
||||
selector,
|
||||
listConfig
|
||||
)
|
||||
|
||||
res.json({
|
||||
draft_orders: draftOrders,
|
||||
count,
|
||||
offset: validated.offset,
|
||||
limit: validated.limit,
|
||||
})
|
||||
}
|
||||
|
||||
export class AdminGetDraftOrdersParams {
|
||||
@IsString()
|
||||
@IsOptional()
|
||||
q?: string
|
||||
|
||||
@IsNumber()
|
||||
@IsOptional()
|
||||
@Type(() => Number)
|
||||
limit?: number = 50
|
||||
|
||||
@IsNumber()
|
||||
@IsOptional()
|
||||
@Type(() => Number)
|
||||
offset?: number = 0
|
||||
}
|
||||
@@ -1,84 +0,0 @@
|
||||
import {
|
||||
defaultFields as defaultOrderFields,
|
||||
defaultRelations as defaultOrderRelations,
|
||||
} from "../orders/index"
|
||||
|
||||
/**
|
||||
* @oas [post] /draft-orders/{id}/register-payment
|
||||
* summary: "Registers a payment for a Draft Order"
|
||||
* operationId: "PostDraftOrdersDraftOrderRegisterPayment"
|
||||
* description: "Registers a payment for a Draft Order."
|
||||
* parameters:
|
||||
* - (path) id=* {String} The Draft Order id.
|
||||
* tags:
|
||||
* - Draft Order
|
||||
* responses:
|
||||
* 200:
|
||||
* description: OK
|
||||
* content:
|
||||
* application/json:
|
||||
* schema:
|
||||
* properties:
|
||||
* draft_order:
|
||||
* $ref: "#/components/schemas/draft-order"
|
||||
*/
|
||||
|
||||
export default async (req, res) => {
|
||||
const { id } = req.params
|
||||
|
||||
try {
|
||||
const draftOrderService = req.scope.resolve("draftOrderService")
|
||||
const paymentProviderService = req.scope.resolve("paymentProviderService")
|
||||
const orderService = req.scope.resolve("orderService")
|
||||
const cartService = req.scope.resolve("cartService")
|
||||
const entityManager = req.scope.resolve("manager")
|
||||
|
||||
let result
|
||||
await entityManager.transaction(async manager => {
|
||||
const draftOrder = await draftOrderService
|
||||
.withTransaction(manager)
|
||||
.retrieve(id)
|
||||
|
||||
const cart = await cartService
|
||||
.withTransaction(manager)
|
||||
.retrieve(draftOrder.cart_id, {
|
||||
select: ["total"],
|
||||
relations: [
|
||||
"discounts",
|
||||
"discounts.rule",
|
||||
"discounts.rule.valid_for",
|
||||
"shipping_methods",
|
||||
"region",
|
||||
"items",
|
||||
],
|
||||
})
|
||||
|
||||
await paymentProviderService
|
||||
.withTransaction(manager)
|
||||
.createSession("system", cart)
|
||||
|
||||
await cartService
|
||||
.withTransaction(manager)
|
||||
.setPaymentSession(cart.id, "system")
|
||||
|
||||
await cartService.withTransaction(manager).authorizePayment(cart.id)
|
||||
|
||||
result = await orderService
|
||||
.withTransaction(manager)
|
||||
.createFromCart(cart.id)
|
||||
|
||||
await draftOrderService
|
||||
.withTransaction(manager)
|
||||
.registerCartCompletion(draftOrder.id, result.id)
|
||||
})
|
||||
|
||||
const order = await orderService.retrieve(result.id, {
|
||||
relations: defaultOrderRelations,
|
||||
select: defaultOrderFields,
|
||||
})
|
||||
|
||||
res.status(200).json({ order })
|
||||
} catch (err) {
|
||||
throw err
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,89 @@
|
||||
import { EntityManager } from "typeorm"
|
||||
import {
|
||||
CartService,
|
||||
DraftOrderService,
|
||||
OrderService,
|
||||
PaymentProviderService,
|
||||
} from "../../../../services"
|
||||
import {
|
||||
defaultAdminOrdersFields as defaultOrderFields,
|
||||
defaultAdminOrdersRelations as defaultOrderRelations,
|
||||
} from "../orders/index"
|
||||
|
||||
/**
|
||||
* @oas [post] /draft-orders/{id}/register-payment
|
||||
* summary: "Registers a payment for a Draft Order"
|
||||
* operationId: "PostDraftOrdersDraftOrderRegisterPayment"
|
||||
* description: "Registers a payment for a Draft Order."
|
||||
* x-authenticated: true
|
||||
* parameters:
|
||||
* - (path) id=* {String} The Draft Order id.
|
||||
* tags:
|
||||
* - Draft Order
|
||||
* responses:
|
||||
* 200:
|
||||
* description: OK
|
||||
* content:
|
||||
* application/json:
|
||||
* schema:
|
||||
* properties:
|
||||
* draft_order:
|
||||
* $ref: "#/components/schemas/draft-order"
|
||||
*/
|
||||
|
||||
export default async (req, res) => {
|
||||
const { id } = req.params
|
||||
|
||||
const draftOrderService: DraftOrderService =
|
||||
req.scope.resolve("draftOrderService")
|
||||
const paymentProviderService: PaymentProviderService = req.scope.resolve(
|
||||
"paymentProviderService"
|
||||
)
|
||||
const orderService: OrderService = req.scope.resolve("orderService")
|
||||
const cartService: CartService = req.scope.resolve("cartService")
|
||||
const entityManager: EntityManager = req.scope.resolve("manager")
|
||||
|
||||
let result
|
||||
await entityManager.transaction(async (manager) => {
|
||||
const draftOrder = await draftOrderService
|
||||
.withTransaction(manager)
|
||||
.retrieve(id)
|
||||
|
||||
const cart = await cartService
|
||||
.withTransaction(manager)
|
||||
.retrieve(draftOrder.cart_id, {
|
||||
select: ["total"],
|
||||
relations: [
|
||||
"discounts",
|
||||
"discounts.rule",
|
||||
"discounts.rule.valid_for",
|
||||
"shipping_methods",
|
||||
"region",
|
||||
"items",
|
||||
],
|
||||
})
|
||||
|
||||
await paymentProviderService
|
||||
.withTransaction(manager)
|
||||
.createSession("system", cart)
|
||||
|
||||
await cartService
|
||||
.withTransaction(manager)
|
||||
.setPaymentSession(cart.id, "system")
|
||||
|
||||
await cartService.withTransaction(manager).authorizePayment(cart.id)
|
||||
|
||||
result = await orderService.withTransaction(manager).createFromCart(cart.id)
|
||||
|
||||
await draftOrderService
|
||||
.withTransaction(manager)
|
||||
.registerCartCompletion(draftOrder.id, result.id)
|
||||
})
|
||||
|
||||
const order = await orderService.retrieve(result.id, {
|
||||
relations: defaultOrderRelations,
|
||||
select: defaultOrderFields,
|
||||
})
|
||||
|
||||
res.status(200).json({ order })
|
||||
}
|
||||
@@ -1,113 +0,0 @@
|
||||
import { MedusaError, Validator } from "medusa-core-utils"
|
||||
import { defaultCartFields, defaultCartRelations, defaultFields } from "."
|
||||
|
||||
/**
|
||||
* @oas [post] /admin/draft-orders/{id}
|
||||
* operationId: PostDraftOrdersDraftOrder
|
||||
* summary: Update a Draft Order"
|
||||
* description: "Updates a Draft Order."
|
||||
* parameters:
|
||||
* - (path) id=* {string} The id of the Draft Order.
|
||||
* requestBody:
|
||||
* content:
|
||||
* application/json:
|
||||
* schema:
|
||||
* properties:
|
||||
* region_id:
|
||||
* type: string
|
||||
* description: The id of the Region to create the Draft Order in.
|
||||
* email:
|
||||
* type: string
|
||||
* description: "An email to be used on the Draft Order."
|
||||
* billing_address:
|
||||
* description: "The Address to be used for billing purposes."
|
||||
* anyOf:
|
||||
* - $ref: "#/components/schemas/address"
|
||||
* shipping_address:
|
||||
* description: "The Address to be used for shipping."
|
||||
* anyOf:
|
||||
* - $ref: "#/components/schemas/address"
|
||||
* discounts:
|
||||
* description: "An array of Discount codes to add to the Draft Order."
|
||||
* type: array
|
||||
* items:
|
||||
* properties:
|
||||
* code:
|
||||
* description: "The code that a Discount is identifed by."
|
||||
* type: string
|
||||
* no_notification_order:
|
||||
* description: "An optional flag passed to the resulting order to determine use of notifications."
|
||||
* type: boolean
|
||||
* customer_id:
|
||||
* description: "The id of the Customer to associate the Draft Order with."
|
||||
* type: string
|
||||
* tags:
|
||||
* - Draft Order
|
||||
* responses:
|
||||
* 200:
|
||||
* description: OK
|
||||
* content:
|
||||
* application/json:
|
||||
* schema:
|
||||
* properties:
|
||||
* draft_order:
|
||||
* $ref: "#/components/schemas/draft-order"
|
||||
*/
|
||||
|
||||
export default async (req, res) => {
|
||||
const { id } = req.params
|
||||
|
||||
const schema = Validator.object().keys({
|
||||
region_id: Validator.string().optional(),
|
||||
country_code: Validator.string().optional(),
|
||||
email: Validator.string()
|
||||
.email()
|
||||
.optional(),
|
||||
billing_address: Validator.object().optional(),
|
||||
shipping_address: Validator.object().optional(),
|
||||
discounts: Validator.array()
|
||||
.items({
|
||||
code: Validator.string(),
|
||||
})
|
||||
.optional(),
|
||||
customer_id: Validator.string().optional(),
|
||||
no_notification_order: Validator.boolean().optional(),
|
||||
})
|
||||
|
||||
const { value, error } = schema.validate(req.body)
|
||||
if (error) {
|
||||
throw new MedusaError(MedusaError.Types.INVALID_DATA, error.details)
|
||||
}
|
||||
|
||||
try {
|
||||
const draftOrderService = req.scope.resolve("draftOrderService")
|
||||
const cartService = req.scope.resolve("cartService")
|
||||
|
||||
const draftOrder = await draftOrderService.retrieve(id)
|
||||
|
||||
if (draftOrder.status === "completed") {
|
||||
throw new MedusaError(
|
||||
MedusaError.Types.NOT_ALLOWED,
|
||||
"You are only allowed to update open draft orders"
|
||||
)
|
||||
}
|
||||
|
||||
if ("no_notification_order" in value) {
|
||||
await draftOrderService.update(draftOrder.id, {
|
||||
no_notification_order: value.no_notification_order,
|
||||
})
|
||||
delete value.no_notification_order
|
||||
}
|
||||
|
||||
await cartService.update(draftOrder.cart_id, value)
|
||||
|
||||
draftOrder.cart = await cartService.retrieve(draftOrder.cart_id, {
|
||||
relations: defaultCartRelations,
|
||||
select: defaultCartFields,
|
||||
})
|
||||
|
||||
res.status(200).json({ draft_order: draftOrder })
|
||||
} catch (err) {
|
||||
throw err
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,146 @@
|
||||
import { MedusaError } from "medusa-core-utils"
|
||||
import {
|
||||
defaultAdminDraftOrdersCartFields,
|
||||
defaultAdminDraftOrdersCartRelations,
|
||||
} from "."
|
||||
import {
|
||||
IsArray,
|
||||
IsBoolean,
|
||||
IsEmail,
|
||||
IsOptional,
|
||||
IsString,
|
||||
ValidateNested,
|
||||
} from "class-validator"
|
||||
import { CartService, DraftOrderService } from "../../../../services"
|
||||
import { Type } from "class-transformer"
|
||||
import { AddressPayload } from "../../../../types/common"
|
||||
import { validator } from "../../../../utils/validator"
|
||||
/**
|
||||
* @oas [post] /admin/draft-orders/{id}
|
||||
* operationId: PostDraftOrdersDraftOrder
|
||||
* summary: Update a Draft Order"
|
||||
* description: "Updates a Draft Order."
|
||||
* x-authenticated: true
|
||||
* parameters:
|
||||
* - (path) id=* {string} The id of the Draft Order.
|
||||
* requestBody:
|
||||
* content:
|
||||
* application/json:
|
||||
* schema:
|
||||
* properties:
|
||||
* region_id:
|
||||
* type: string
|
||||
* description: The id of the Region to create the Draft Order in.
|
||||
* email:
|
||||
* type: string
|
||||
* description: "An email to be used on the Draft Order."
|
||||
* billing_address:
|
||||
* description: "The Address to be used for billing purposes."
|
||||
* anyOf:
|
||||
* - $ref: "#/components/schemas/address"
|
||||
* shipping_address:
|
||||
* description: "The Address to be used for shipping."
|
||||
* anyOf:
|
||||
* - $ref: "#/components/schemas/address"
|
||||
* discounts:
|
||||
* description: "An array of Discount codes to add to the Draft Order."
|
||||
* type: array
|
||||
* items:
|
||||
* properties:
|
||||
* code:
|
||||
* description: "The code that a Discount is identifed by."
|
||||
* type: string
|
||||
* no_notification_order:
|
||||
* description: "An optional flag passed to the resulting order to determine use of notifications."
|
||||
* type: boolean
|
||||
* customer_id:
|
||||
* description: "The id of the Customer to associate the Draft Order with."
|
||||
* type: string
|
||||
* tags:
|
||||
* - Draft Order
|
||||
* responses:
|
||||
* 200:
|
||||
* description: OK
|
||||
* content:
|
||||
* application/json:
|
||||
* schema:
|
||||
* properties:
|
||||
* draft_order:
|
||||
* $ref: "#/components/schemas/draft-order"
|
||||
*/
|
||||
|
||||
export default async (req, res) => {
|
||||
const { id } = req.params
|
||||
|
||||
const validated = await validator(AdminPostDraftOrdersDraftOrderReq, req.body)
|
||||
|
||||
const draftOrderService: DraftOrderService =
|
||||
req.scope.resolve("draftOrderService")
|
||||
const cartService: CartService = req.scope.resolve("cartService")
|
||||
|
||||
const draftOrder = await draftOrderService.retrieve(id)
|
||||
|
||||
if (draftOrder.status === "completed") {
|
||||
throw new MedusaError(
|
||||
MedusaError.Types.NOT_ALLOWED,
|
||||
"You are only allowed to update open draft orders"
|
||||
)
|
||||
}
|
||||
|
||||
if (validated.no_notification_order !== undefined) {
|
||||
await draftOrderService.update(draftOrder.id, {
|
||||
no_notification_order: validated.no_notification_order,
|
||||
})
|
||||
delete validated.no_notification_order
|
||||
}
|
||||
|
||||
await cartService.update(draftOrder.cart_id, validated)
|
||||
|
||||
draftOrder.cart = await cartService.retrieve(draftOrder.cart_id, {
|
||||
relations: defaultAdminDraftOrdersCartRelations,
|
||||
select: defaultAdminDraftOrdersCartFields,
|
||||
})
|
||||
|
||||
res.status(200).json({ draft_order: draftOrder })
|
||||
}
|
||||
|
||||
export class AdminPostDraftOrdersDraftOrderReq {
|
||||
@IsString()
|
||||
@IsOptional()
|
||||
region_id?: string
|
||||
|
||||
@IsString()
|
||||
@IsOptional()
|
||||
country_code?: string
|
||||
|
||||
@IsEmail()
|
||||
@IsOptional()
|
||||
email?: string
|
||||
|
||||
@IsOptional()
|
||||
@Type(() => AddressPayload)
|
||||
billing_address?: AddressPayload
|
||||
|
||||
@IsOptional()
|
||||
@Type(() => AddressPayload)
|
||||
shipping_address?: AddressPayload
|
||||
|
||||
@IsArray()
|
||||
@IsOptional()
|
||||
@Type(() => Discount)
|
||||
@ValidateNested({ each: true })
|
||||
discounts?: Discount[]
|
||||
|
||||
@IsString()
|
||||
@IsOptional()
|
||||
customer_id?: string
|
||||
|
||||
@IsBoolean()
|
||||
@IsOptional()
|
||||
no_notification_order?: boolean
|
||||
}
|
||||
|
||||
class Discount {
|
||||
@IsString()
|
||||
code: string
|
||||
}
|
||||
@@ -1,114 +0,0 @@
|
||||
import { MedusaError, Validator } from "medusa-core-utils"
|
||||
import { defaultCartFields, defaultCartRelations, defaultFields } from "."
|
||||
|
||||
/**
|
||||
* @oas [post] /draft-orders/{id}/line-items/{line_id}
|
||||
* operationId: "PostDraftOrdersDraftOrderLineItemsItem"
|
||||
* summary: "Update a Line Item for a Draft Order"
|
||||
* description: "Updates a Line Item for a Draft Order"
|
||||
* requestBody:
|
||||
* content:
|
||||
* application/json:
|
||||
* schema:
|
||||
* properties:
|
||||
* unit_price:
|
||||
* description: The potential custom price of the item.
|
||||
* type: integer
|
||||
* title:
|
||||
* description: The potential custom title of the item.
|
||||
* type: string
|
||||
* quantity:
|
||||
* description: The quantity of the Line Item.
|
||||
* type: integer
|
||||
* metadata:
|
||||
* description: The optional key-value map with additional details about the Line Item.
|
||||
* type: object
|
||||
* tags:
|
||||
* - Draft Order
|
||||
* responses:
|
||||
* 200:
|
||||
* description: OK
|
||||
* content:
|
||||
* application/json:
|
||||
* schema:
|
||||
* properties:
|
||||
* draft_order:
|
||||
* $ref: "#/components/schemas/draft-order"
|
||||
*/
|
||||
|
||||
export default async (req, res) => {
|
||||
const { id, line_id } = req.params
|
||||
|
||||
const schema = Validator.object().keys({
|
||||
title: Validator.string().optional(),
|
||||
unit_price: Validator.number().optional(),
|
||||
quantity: Validator.number().optional(),
|
||||
metadata: Validator.object().optional(),
|
||||
})
|
||||
|
||||
const { value, error } = schema.validate(req.body)
|
||||
if (error) {
|
||||
throw new MedusaError(MedusaError.Types.INVALID_DATA, error.details)
|
||||
}
|
||||
|
||||
try {
|
||||
const draftOrderService = req.scope.resolve("draftOrderService")
|
||||
const cartService = req.scope.resolve("cartService")
|
||||
const entityManager = req.scope.resolve("manager")
|
||||
|
||||
await entityManager.transaction(async manager => {
|
||||
const draftOrder = await draftOrderService
|
||||
.withTransaction(manager)
|
||||
.retrieve(id, {
|
||||
select: defaultFields,
|
||||
relations: ["cart", "cart.items"],
|
||||
})
|
||||
|
||||
if (draftOrder.status === "completed") {
|
||||
throw new MedusaError(
|
||||
MedusaError.Types.NOT_ALLOWED,
|
||||
"You are only allowed to update open draft orders"
|
||||
)
|
||||
}
|
||||
|
||||
if (value.quantity === 0) {
|
||||
await cartService
|
||||
.withTransaction(manager)
|
||||
.removeLineItem(draftOrder.cart.id, line_id)
|
||||
} else {
|
||||
const existing = draftOrder.cart.items.find(i => i.id === line_id)
|
||||
|
||||
if (!existing) {
|
||||
throw new MedusaError(
|
||||
MedusaError.Types.INVALID_DATA,
|
||||
"Could not find the line item"
|
||||
)
|
||||
}
|
||||
|
||||
const lineItemUpdate = {
|
||||
...value,
|
||||
region_id: draftOrder.cart.region_id,
|
||||
}
|
||||
|
||||
if (existing.variant_id) {
|
||||
lineItemUpdate.variant_id = existing.variant_id
|
||||
}
|
||||
|
||||
await cartService
|
||||
.withTransaction(manager)
|
||||
.updateLineItem(draftOrder.cart_id, line_id, lineItemUpdate)
|
||||
}
|
||||
|
||||
draftOrder.cart = await cartService
|
||||
.withTransaction(manager)
|
||||
.retrieve(draftOrder.cart_id, {
|
||||
relations: defaultCartRelations,
|
||||
select: defaultCartFields,
|
||||
})
|
||||
|
||||
res.status(200).json({ draft_order: draftOrder })
|
||||
})
|
||||
} catch (err) {
|
||||
throw err
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,140 @@
|
||||
import { IsInt, IsObject, IsOptional, IsString } from "class-validator"
|
||||
import { MedusaError } from "medusa-core-utils"
|
||||
import { EntityManager } from "typeorm"
|
||||
import {
|
||||
defaultAdminDraftOrdersCartFields,
|
||||
defaultAdminDraftOrdersCartRelations,
|
||||
defaultAdminDraftOrdersFields,
|
||||
} from "."
|
||||
import { DraftOrder } from "../../../.."
|
||||
import { CartService, DraftOrderService } from "../../../../services"
|
||||
import { validator } from "../../../../utils/validator"
|
||||
/**
|
||||
* @oas [post] /draft-orders/{id}/line-items/{line_id}
|
||||
* operationId: "PostDraftOrdersDraftOrderLineItemsItem"
|
||||
* summary: "Update a Line Item for a Draft Order"
|
||||
* description: "Updates a Line Item for a Draft Order"
|
||||
* x-authenticated: true
|
||||
* requestBody:
|
||||
* content:
|
||||
* application/json:
|
||||
* schema:
|
||||
* properties:
|
||||
* unit_price:
|
||||
* description: The potential custom price of the item.
|
||||
* type: integer
|
||||
* title:
|
||||
* description: The potential custom title of the item.
|
||||
* type: string
|
||||
* quantity:
|
||||
* description: The quantity of the Line Item.
|
||||
* type: integer
|
||||
* metadata:
|
||||
* description: The optional key-value map with additional details about the Line Item.
|
||||
* type: object
|
||||
* tags:
|
||||
* - Draft Order
|
||||
* responses:
|
||||
* 200:
|
||||
* description: OK
|
||||
* content:
|
||||
* application/json:
|
||||
* schema:
|
||||
* properties:
|
||||
* draft_order:
|
||||
* $ref: "#/components/schemas/draft-order"
|
||||
*/
|
||||
|
||||
export default async (req, res) => {
|
||||
const { id, line_id } = req.params
|
||||
|
||||
const validated = await validator(
|
||||
AdminPostDraftOrdersDraftOrderLineItemsItemReq,
|
||||
req.body
|
||||
)
|
||||
|
||||
const draftOrderService: DraftOrderService =
|
||||
req.scope.resolve("draftOrderService")
|
||||
const cartService: CartService = req.scope.resolve("cartService")
|
||||
const entityManager: EntityManager = req.scope.resolve("manager")
|
||||
|
||||
await entityManager.transaction(async (manager) => {
|
||||
const draftOrder: DraftOrder = await draftOrderService
|
||||
.withTransaction(manager)
|
||||
.retrieve(id, {
|
||||
select: defaultAdminDraftOrdersFields,
|
||||
relations: ["cart", "cart.items"],
|
||||
})
|
||||
|
||||
if (draftOrder.status === "completed") {
|
||||
throw new MedusaError(
|
||||
MedusaError.Types.NOT_ALLOWED,
|
||||
"You are only allowed to update open draft orders"
|
||||
)
|
||||
}
|
||||
|
||||
if (validated.quantity === 0) {
|
||||
await cartService
|
||||
.withTransaction(manager)
|
||||
.removeLineItem(draftOrder.cart.id, line_id)
|
||||
} else {
|
||||
const existing = draftOrder.cart.items.find((i) => i.id === line_id)
|
||||
|
||||
if (!existing) {
|
||||
throw new MedusaError(
|
||||
MedusaError.Types.INVALID_DATA,
|
||||
"Could not find the line item"
|
||||
)
|
||||
}
|
||||
|
||||
const lineItemUpdate: LineItemUpdate = {
|
||||
...validated,
|
||||
region_id: draftOrder.cart.region_id,
|
||||
}
|
||||
|
||||
if (existing.variant_id) {
|
||||
lineItemUpdate.variant_id = existing.variant_id
|
||||
}
|
||||
|
||||
await cartService
|
||||
.withTransaction(manager)
|
||||
.updateLineItem(draftOrder.cart_id, line_id, lineItemUpdate)
|
||||
}
|
||||
|
||||
draftOrder.cart = await cartService
|
||||
.withTransaction(manager)
|
||||
.retrieve(draftOrder.cart_id, {
|
||||
relations: defaultAdminDraftOrdersCartRelations,
|
||||
select: defaultAdminDraftOrdersCartFields,
|
||||
})
|
||||
|
||||
res.status(200).json({ draft_order: draftOrder })
|
||||
})
|
||||
}
|
||||
|
||||
class LineItemUpdate {
|
||||
title?: string
|
||||
unit_price?: number
|
||||
quantity?: number
|
||||
metadata?: object = {}
|
||||
region_id?: string
|
||||
variant_id?: string
|
||||
}
|
||||
|
||||
export class AdminPostDraftOrdersDraftOrderLineItemsItemReq {
|
||||
@IsString()
|
||||
@IsOptional()
|
||||
title?: string
|
||||
|
||||
@IsInt()
|
||||
@IsOptional()
|
||||
unit_price?: number
|
||||
|
||||
@IsInt()
|
||||
@IsOptional()
|
||||
quantity?: number
|
||||
|
||||
@IsObject()
|
||||
@IsOptional()
|
||||
metadata?: object = {}
|
||||
}
|
||||
+37
-28
@@ -1,11 +1,15 @@
|
||||
import { MedusaError, Validator } from "medusa-core-utils"
|
||||
import { defaultFields, defaultRelations } from "./"
|
||||
import { Type } from "class-transformer"
|
||||
import { IsBoolean, IsDate, IsInt, IsOptional, IsString } from "class-validator"
|
||||
import { defaultAdminGiftCardFields, defaultAdminGiftCardRelations } from "."
|
||||
import { GiftCardService } from "../../../../services"
|
||||
import { validator } from "../../../../utils/validator"
|
||||
|
||||
/**
|
||||
* @oas [post] /gift-cards
|
||||
* operationId: "PostGiftCards"
|
||||
* summary: "Create a Gift Card"
|
||||
* description: "Creates a Gift Card that can redeemed by its unique code. The Gift Card is only valid within 1 region."
|
||||
* x-authenticated: true
|
||||
* requestBody:
|
||||
* content:
|
||||
* application/json:
|
||||
@@ -42,36 +46,41 @@ import { defaultFields, defaultRelations } from "./"
|
||||
* $ref: "#/components/schemas/gift_card"
|
||||
*/
|
||||
export default async (req, res) => {
|
||||
const schema = Validator.object().keys({
|
||||
value: Validator.number()
|
||||
.integer()
|
||||
.optional(),
|
||||
ends_at: Validator.date().optional(),
|
||||
is_disabled: Validator.boolean().optional(),
|
||||
region_id: Validator.string().optional(),
|
||||
metadata: Validator.object().optional(),
|
||||
const validated = await validator(AdminPostGiftCardsReq, req.body)
|
||||
|
||||
const giftCardService: GiftCardService = req.scope.resolve("giftCardService")
|
||||
|
||||
const newly = await giftCardService.create({
|
||||
...validated,
|
||||
balance: validated.value,
|
||||
})
|
||||
|
||||
const { value, error } = schema.validate(req.body)
|
||||
if (error) {
|
||||
throw new MedusaError(MedusaError.Types.INVALID_DATA, error.details)
|
||||
}
|
||||
const giftCard = await giftCardService.retrieve(newly.id, {
|
||||
select: defaultAdminGiftCardFields,
|
||||
relations: defaultAdminGiftCardRelations,
|
||||
})
|
||||
|
||||
try {
|
||||
const giftCardService = req.scope.resolve("giftCardService")
|
||||
res.status(200).json({ gift_card: giftCard })
|
||||
}
|
||||
|
||||
const newly = await giftCardService.create({
|
||||
...value,
|
||||
balance: value.value,
|
||||
})
|
||||
export class AdminPostGiftCardsReq {
|
||||
@IsOptional()
|
||||
@IsInt()
|
||||
value?: number
|
||||
|
||||
const giftCard = await giftCardService.retrieve(newly.id, {
|
||||
select: defaultFields,
|
||||
relations: defaultRelations,
|
||||
})
|
||||
@IsOptional()
|
||||
@IsDate()
|
||||
@Type(() => Date)
|
||||
ends_at?: Date
|
||||
|
||||
res.status(200).json({ gift_card: giftCard })
|
||||
} catch (err) {
|
||||
throw err
|
||||
}
|
||||
@IsOptional()
|
||||
@IsBoolean()
|
||||
is_disabled?: boolean
|
||||
|
||||
@IsOptional()
|
||||
@IsString()
|
||||
region_id?: string
|
||||
|
||||
@IsOptional()
|
||||
metadata?: object
|
||||
}
|
||||
+8
-11
@@ -3,6 +3,7 @@
|
||||
* operationId: "DeleteGiftCardsGiftCard"
|
||||
* summary: "Delete a Gift Card"
|
||||
* description: "Deletes a Gift Card"
|
||||
* x-authenticated: true
|
||||
* parameters:
|
||||
* - (path) id=* {string} The id of the Gift Card to delete.
|
||||
* tags:
|
||||
@@ -26,16 +27,12 @@
|
||||
export default async (req, res) => {
|
||||
const { id } = req.params
|
||||
|
||||
try {
|
||||
const giftCardService = req.scope.resolve("giftCardService")
|
||||
await giftCardService.delete(id)
|
||||
const giftCardService = req.scope.resolve("giftCardService")
|
||||
await giftCardService.delete(id)
|
||||
|
||||
res.json({
|
||||
id,
|
||||
object: "gift-card",
|
||||
deleted: true,
|
||||
})
|
||||
} catch (err) {
|
||||
throw err
|
||||
}
|
||||
res.json({
|
||||
id,
|
||||
object: "gift-card",
|
||||
deleted: true,
|
||||
})
|
||||
}
|
||||
+8
-11
@@ -1,10 +1,11 @@
|
||||
import { defaultFields, defaultRelations } from "./"
|
||||
import { defaultAdminGiftCardFields, defaultAdminGiftCardRelations } from "./"
|
||||
|
||||
/**
|
||||
* @oas [get] /gift-cards/{id}
|
||||
* operationId: "GetGiftCardsGiftCard"
|
||||
* summary: "Retrieve a Gift Card"
|
||||
* description: "Retrieves a Gift Card."
|
||||
* x-authenticated: true
|
||||
* parameters:
|
||||
* - (path) id=* {string} The id of the Gift Card.
|
||||
* tags:
|
||||
@@ -22,15 +23,11 @@ import { defaultFields, defaultRelations } from "./"
|
||||
export default async (req, res) => {
|
||||
const { id } = req.params
|
||||
|
||||
try {
|
||||
const giftCardService = req.scope.resolve("giftCardService")
|
||||
const giftCard = await giftCardService.retrieve(id, {
|
||||
select: defaultFields,
|
||||
relations: defaultRelations,
|
||||
})
|
||||
const giftCardService = req.scope.resolve("giftCardService")
|
||||
const giftCard = await giftCardService.retrieve(id, {
|
||||
select: defaultAdminGiftCardFields,
|
||||
relations: defaultAdminGiftCardRelations,
|
||||
})
|
||||
|
||||
res.status(200).json({ gift_card: giftCard })
|
||||
} catch (err) {
|
||||
throw err
|
||||
}
|
||||
res.status(200).json({ gift_card: giftCard })
|
||||
}
|
||||
+22
-8
@@ -1,9 +1,12 @@
|
||||
import { Router } from "express"
|
||||
import "reflect-metadata"
|
||||
import { GiftCard } from "../../../.."
|
||||
import { DeleteResponse, PaginatedResponse } from "../../../../types/common"
|
||||
import middlewares from "../../../middlewares"
|
||||
|
||||
const route = Router()
|
||||
|
||||
export default app => {
|
||||
export default (app) => {
|
||||
app.use("/gift-cards", route)
|
||||
|
||||
route.get("/", middlewares.wrap(require("./list-gift-cards").default))
|
||||
@@ -19,7 +22,7 @@ export default app => {
|
||||
return app
|
||||
}
|
||||
|
||||
export const defaultFields = [
|
||||
export const defaultAdminGiftCardFields = [
|
||||
"id",
|
||||
"code",
|
||||
"value",
|
||||
@@ -33,12 +36,9 @@ export const defaultFields = [
|
||||
"metadata",
|
||||
]
|
||||
|
||||
export const defaultRelations = [
|
||||
"region",
|
||||
"order",
|
||||
]
|
||||
export const defaultAdminGiftCardRelations = ["region", "order"]
|
||||
|
||||
export const allowedFields = [
|
||||
export const allowedAdminGiftCardFields = [
|
||||
"id",
|
||||
"code",
|
||||
"value",
|
||||
@@ -52,4 +52,18 @@ export const allowedFields = [
|
||||
"metadata",
|
||||
]
|
||||
|
||||
export const allowedRelations = ["region"]
|
||||
export const allowedAdminGiftCardRelations = ["region"]
|
||||
|
||||
export type AdminGiftCardsRes = {
|
||||
gift_card: GiftCard
|
||||
}
|
||||
|
||||
export type AdminGiftCardsDeleteRes = DeleteResponse
|
||||
|
||||
export type AdminGiftCardsListRes = PaginatedResponse & {
|
||||
gift_cards: GiftCard[]
|
||||
}
|
||||
|
||||
export * from "./create-gift-card"
|
||||
export * from "./list-gift-cards"
|
||||
export * from "./update-gift-card"
|
||||
@@ -1,48 +0,0 @@
|
||||
import { MedusaError, Validator } from "medusa-core-utils"
|
||||
import { defaultFields, defaultRelations } from "./"
|
||||
|
||||
/**
|
||||
* @oas [get] /gift-cards
|
||||
* operationId: "GetGiftCards"
|
||||
* summary: "List Gift Cards"
|
||||
* description: "Retrieves a list of Gift Cards."
|
||||
* tags:
|
||||
* - Gift Card
|
||||
* responses:
|
||||
* 200:
|
||||
* description: OK
|
||||
* content:
|
||||
* application/json:
|
||||
* schema:
|
||||
* properties:
|
||||
* gift_cards:
|
||||
* type: array
|
||||
* items:
|
||||
* $ref: "#/components/schemas/gift_card"
|
||||
*/
|
||||
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 })
|
||||
} catch (err) {
|
||||
throw err
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,68 @@
|
||||
import { Type } from "class-transformer"
|
||||
import { IsInt, IsOptional, IsString } from "class-validator"
|
||||
import { defaultAdminGiftCardFields, defaultAdminGiftCardRelations } from "."
|
||||
import { GiftCardService } from "../../../../services"
|
||||
import { validator } from "../../../../utils/validator"
|
||||
|
||||
/**
|
||||
* @oas [get] /gift-cards
|
||||
* operationId: "GetGiftCards"
|
||||
* summary: "List Gift Cards"
|
||||
* description: "Retrieves a list of Gift Cards."
|
||||
* x-authenticated: true
|
||||
* tags:
|
||||
* - Gift Card
|
||||
* responses:
|
||||
* 200:
|
||||
* description: OK
|
||||
* content:
|
||||
* application/json:
|
||||
* schema:
|
||||
* properties:
|
||||
* gift_cards:
|
||||
* type: array
|
||||
* items:
|
||||
* $ref: "#/components/schemas/gift_card"
|
||||
*/
|
||||
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,
|
||||
})
|
||||
|
||||
res.status(200).json({
|
||||
gift_cards: giftCards,
|
||||
count: giftCards.length,
|
||||
offset: validated.offset,
|
||||
limit: validated.limit,
|
||||
})
|
||||
}
|
||||
|
||||
export class AdminGetGiftCardsParams {
|
||||
@IsOptional()
|
||||
@IsInt()
|
||||
@Type(() => Number)
|
||||
limit = 50
|
||||
|
||||
@IsOptional()
|
||||
@IsInt()
|
||||
@Type(() => Number)
|
||||
offset = 0
|
||||
|
||||
@IsOptional()
|
||||
@IsString()
|
||||
q?: string
|
||||
}
|
||||
+34
-25
@@ -1,11 +1,15 @@
|
||||
import { MedusaError, Validator } from "medusa-core-utils"
|
||||
import { defaultFields, defaultRelations } from "./"
|
||||
import { Type } from "class-transformer"
|
||||
import { IsBoolean, IsDate, IsInt, IsOptional, IsString } from "class-validator"
|
||||
import { defaultAdminGiftCardFields, defaultAdminGiftCardRelations } from "."
|
||||
import { GiftCardService } from "../../../../services"
|
||||
import { validator } from "../../../../utils/validator"
|
||||
|
||||
/**
|
||||
* @oas [post] /gift-cards/{id}
|
||||
* operationId: "PostGiftCardsGiftCard"
|
||||
* summary: "Create a Gift Card"
|
||||
* description: "Creates a Gift Card that can redeemed by its unique code. The Gift Card is only valid within 1 region."
|
||||
* x-authenticated: true
|
||||
* parameters:
|
||||
* - (path) id=* {string} The id of the Gift Card.
|
||||
* requestBody:
|
||||
@@ -46,33 +50,38 @@ import { defaultFields, defaultRelations } from "./"
|
||||
export default async (req, res) => {
|
||||
const { id } = req.params
|
||||
|
||||
const schema = Validator.object().keys({
|
||||
balance: Validator.number()
|
||||
.precision(0)
|
||||
.optional(),
|
||||
ends_at: Validator.date().optional(),
|
||||
is_disabled: Validator.boolean().optional(),
|
||||
region_id: Validator.string().optional(),
|
||||
metadata: Validator.object().optional(),
|
||||
const validated = await validator(AdminPostGiftCardsGiftCardReq, req.body)
|
||||
|
||||
const giftCardService: GiftCardService = req.scope.resolve("giftCardService")
|
||||
|
||||
await giftCardService.update(id, validated)
|
||||
|
||||
const giftCard = await giftCardService.retrieve(id, {
|
||||
select: defaultAdminGiftCardFields,
|
||||
relations: defaultAdminGiftCardRelations,
|
||||
})
|
||||
|
||||
const { value, error } = schema.validate(req.body)
|
||||
if (error) {
|
||||
throw new MedusaError(MedusaError.Types.INVALID_DATA, error.details)
|
||||
}
|
||||
res.status(200).json({ gift_card: giftCard })
|
||||
}
|
||||
|
||||
try {
|
||||
const giftCardService = req.scope.resolve("giftCardService")
|
||||
export class AdminPostGiftCardsGiftCardReq {
|
||||
@IsOptional()
|
||||
@IsInt()
|
||||
balance?: number
|
||||
|
||||
await giftCardService.update(id, value)
|
||||
@IsOptional()
|
||||
@IsBoolean()
|
||||
is_disabled?: boolean
|
||||
|
||||
const giftCard = await giftCardService.retrieve(id, {
|
||||
select: defaultFields,
|
||||
relations: defaultRelations,
|
||||
})
|
||||
@IsOptional()
|
||||
@IsDate()
|
||||
@Type(() => Date)
|
||||
ends_at?: Date
|
||||
|
||||
res.status(200).json({ gift_card: giftCard })
|
||||
} catch (err) {
|
||||
throw err
|
||||
}
|
||||
@IsOptional()
|
||||
@IsString()
|
||||
region_id?: string
|
||||
|
||||
@IsOptional()
|
||||
metadata?: object
|
||||
}
|
||||
+27
-23
@@ -1,10 +1,13 @@
|
||||
import { MedusaError, Validator } from "medusa-core-utils"
|
||||
import { IsNotEmpty, IsString } from "class-validator"
|
||||
import NoteService from "../../../../services/note"
|
||||
import { validator } from "../../../../utils/validator"
|
||||
|
||||
/**
|
||||
* @oas [post] /notes
|
||||
* operationId: "PostNotes"
|
||||
* summary: "Creates a Note"
|
||||
* description: "Creates a Note which can be associated with any resource as required."
|
||||
* x-authenticated: true
|
||||
* requestBody:
|
||||
* content:
|
||||
* application/json:
|
||||
@@ -33,31 +36,32 @@ import { MedusaError, Validator } from "medusa-core-utils"
|
||||
*
|
||||
*/
|
||||
export default async (req, res) => {
|
||||
const schema = Validator.object().keys({
|
||||
resource_id: Validator.string(),
|
||||
resource_type: Validator.string(),
|
||||
value: Validator.string(),
|
||||
const validated = await validator(AdminPostNotesReq, req.body)
|
||||
|
||||
const userId: string = req.user.id || req.user.userId
|
||||
|
||||
const noteService: NoteService = req.scope.resolve("noteService")
|
||||
|
||||
const result = await noteService.create({
|
||||
resource_id: validated.resource_id,
|
||||
resource_type: validated.resource_type,
|
||||
value: validated.value,
|
||||
author_id: userId,
|
||||
})
|
||||
|
||||
const userId = req.user.id || req.user.userId
|
||||
res.status(200).json({ note: result })
|
||||
}
|
||||
|
||||
const { value, error } = schema.validate(req.body)
|
||||
if (error) {
|
||||
throw new MedusaError(MedusaError.Types.INVALID_DATA, error.details)
|
||||
}
|
||||
export class AdminPostNotesReq {
|
||||
@IsString()
|
||||
@IsNotEmpty()
|
||||
resource_id: string
|
||||
|
||||
try {
|
||||
const noteService = req.scope.resolve("noteService")
|
||||
@IsString()
|
||||
@IsNotEmpty()
|
||||
resource_type: string
|
||||
|
||||
const result = await noteService.create({
|
||||
resource_id: value.resource_id,
|
||||
resource_type: value.resource_type,
|
||||
value: value.value,
|
||||
author_id: userId,
|
||||
})
|
||||
|
||||
res.status(200).json({ note: result })
|
||||
} catch (err) {
|
||||
throw err
|
||||
}
|
||||
@IsString()
|
||||
@IsNotEmpty()
|
||||
value: string
|
||||
}
|
||||
+6
-7
@@ -1,8 +1,11 @@
|
||||
import NoteService from "../../../../services/note"
|
||||
|
||||
/**
|
||||
* @oas [delete] /notes/{id}
|
||||
* operationId: "DeleteNotesNote"
|
||||
* summary: "Deletes a Note"
|
||||
* description: "Deletes a Note."
|
||||
* x-authenticated: true
|
||||
* parameters:
|
||||
* - (path) id=* {string} The id of the Note to delete.
|
||||
* tags:
|
||||
@@ -24,12 +27,8 @@
|
||||
export default async (req, res) => {
|
||||
const { id } = req.params
|
||||
|
||||
try {
|
||||
const noteService = req.scope.resolve("noteService")
|
||||
await noteService.delete(id)
|
||||
const noteService: NoteService = req.scope.resolve("noteService")
|
||||
await noteService.delete(id)
|
||||
|
||||
res.status(200).json({ id, deleted: true })
|
||||
} catch (err) {
|
||||
throw err
|
||||
}
|
||||
res.status(200).json({ id, object: "note", deleted: true })
|
||||
}
|
||||
+7
-8
@@ -1,8 +1,11 @@
|
||||
import NoteService from "../../../../services/note"
|
||||
|
||||
/**
|
||||
* @oas [get] /notes/{id}
|
||||
* operationId: "GetNoteNote"
|
||||
* operationId: "GetNotesNote"
|
||||
* summary: "Get Note"
|
||||
* description: "Retrieves a single note using its id"
|
||||
* x-authenticated: true
|
||||
* parameters:
|
||||
* - (path) id=* {string} The id of the note to retrieve.
|
||||
* tags:
|
||||
@@ -20,12 +23,8 @@
|
||||
export default async (req, res) => {
|
||||
const { id } = req.params
|
||||
|
||||
try {
|
||||
const noteService = req.scope.resolve("noteService")
|
||||
const note = await noteService.retrieve(id, { relations: ["author"] })
|
||||
const noteService: NoteService = req.scope.resolve("noteService")
|
||||
const note = await noteService.retrieve(id, { relations: ["author"] })
|
||||
|
||||
res.status(200).json({ note })
|
||||
} catch (err) {
|
||||
throw err
|
||||
}
|
||||
res.status(200).json({ note })
|
||||
}
|
||||
+19
-1
@@ -1,9 +1,12 @@
|
||||
import { Router } from "express"
|
||||
import { Note } from "../../../.."
|
||||
import { DeleteResponse, PaginatedResponse } from "../../../../types/common"
|
||||
import middlewares from "../../../middlewares"
|
||||
import "reflect-metadata"
|
||||
|
||||
const route = Router()
|
||||
|
||||
export default app => {
|
||||
export default (app) => {
|
||||
app.use("/notes", route)
|
||||
|
||||
route.get("/:id", middlewares.wrap(require("./get-note").default))
|
||||
@@ -18,3 +21,18 @@ export default app => {
|
||||
|
||||
return app
|
||||
}
|
||||
|
||||
export type AdminNotesRes = {
|
||||
note: Note
|
||||
}
|
||||
|
||||
export type AdminNotesDeleteRes = DeleteResponse
|
||||
|
||||
export type AdminNotesListRes = PaginatedResponse & {
|
||||
notes: Note[]
|
||||
}
|
||||
export * from "./create-note"
|
||||
export * from "./delete-note"
|
||||
export * from "./get-note"
|
||||
export * from "./list-notes"
|
||||
export * from "./update-note"
|
||||
@@ -1,42 +0,0 @@
|
||||
/**
|
||||
* @oas [get] /notes
|
||||
* operationId: "GetNotes"
|
||||
* summary: "List Notes"
|
||||
* description: "Retrieves a list of notes"
|
||||
* tags:
|
||||
* - Note
|
||||
* responses:
|
||||
* 200:
|
||||
* description: OK
|
||||
* content:
|
||||
* application/json:
|
||||
* schema:
|
||||
* properties:
|
||||
* notes:
|
||||
* type: array
|
||||
* items:
|
||||
* $ref: "#/components/schemas/note"
|
||||
*/
|
||||
export default async (req, res) => {
|
||||
try {
|
||||
const limit = parseInt(req.query.limit) || 50
|
||||
const offset = parseInt(req.query.offset) || 0
|
||||
|
||||
const selector = {}
|
||||
|
||||
if ("resource_id" in req.query) {
|
||||
selector.resource_id = req.query.resource_id
|
||||
}
|
||||
|
||||
const noteService = req.scope.resolve("noteService")
|
||||
const notes = await noteService.list(selector, {
|
||||
take: limit,
|
||||
skip: offset,
|
||||
relations: ["author"],
|
||||
})
|
||||
|
||||
res.status(200).json({ notes })
|
||||
} catch (err) {
|
||||
throw err
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,68 @@
|
||||
import { IsNumber, IsOptional, IsString } from "class-validator"
|
||||
import NoteService from "../../../../services/note"
|
||||
import { validator } from "../../../../utils/validator"
|
||||
import { selector } from "../../../../types/note"
|
||||
import { Type } from "class-transformer"
|
||||
/**
|
||||
* @oas [get] /notes
|
||||
* operationId: "GetNotes"
|
||||
* summary: "List Notes"
|
||||
* x-authenticated: true
|
||||
* description: "Retrieves a list of notes"
|
||||
* * parameters:
|
||||
* - (path) limit= {number} The number of notes to get
|
||||
* - (path) offset= {number} The offset at which to get notes
|
||||
* - (path) resource_id= {string} The id which the notes belongs to
|
||||
* tags:
|
||||
* - Note
|
||||
* responses:
|
||||
* 200:
|
||||
* description: OK
|
||||
* content:
|
||||
* application/json:
|
||||
* schema:
|
||||
* properties:
|
||||
* notes:
|
||||
* type: array
|
||||
* items:
|
||||
* $ref: "#/components/schemas/note"
|
||||
*/
|
||||
export default async (req, res) => {
|
||||
const validated = await validator(AdminGetNotesParams, req.query)
|
||||
|
||||
const selector: selector = {}
|
||||
|
||||
if (validated.resource_id) {
|
||||
selector.resource_id = validated.resource_id
|
||||
}
|
||||
|
||||
const noteService: NoteService = req.scope.resolve("noteService")
|
||||
const notes = await noteService.list(selector, {
|
||||
take: validated.limit,
|
||||
skip: validated.offset,
|
||||
relations: ["author"],
|
||||
})
|
||||
|
||||
res.status(200).json({
|
||||
notes,
|
||||
count: notes.length,
|
||||
offset: validated.offset,
|
||||
limit: validated.limit,
|
||||
})
|
||||
}
|
||||
|
||||
export class AdminGetNotesParams {
|
||||
@IsString()
|
||||
@IsOptional()
|
||||
resource_id?: string
|
||||
|
||||
@IsNumber()
|
||||
@IsOptional()
|
||||
@Type(() => Number)
|
||||
limit = 50
|
||||
|
||||
@IsNumber()
|
||||
@IsOptional()
|
||||
@Type(() => Number)
|
||||
offset = 0
|
||||
}
|
||||
+14
-15
@@ -1,9 +1,12 @@
|
||||
import { MedusaError, Validator } from "medusa-core-utils"
|
||||
import { IsString } from "class-validator"
|
||||
import NoteService from "../../../../services/note"
|
||||
import { validator } from "../../../../utils/validator"
|
||||
|
||||
/**
|
||||
* @oas [post] /notes/{id}
|
||||
* operationId: "PostNotesNote"
|
||||
* summary: "Updates a Note"
|
||||
* x-authenticated: true
|
||||
* description: "Updates a Note associated with some resource"
|
||||
* parameters:
|
||||
* - (path) id=* {string} The id of the Note to update
|
||||
@@ -11,6 +14,8 @@ import { MedusaError, Validator } from "medusa-core-utils"
|
||||
* content:
|
||||
* application/json:
|
||||
* schema:
|
||||
* required:
|
||||
* - value
|
||||
* properties:
|
||||
* value:
|
||||
* type: string
|
||||
@@ -31,21 +36,15 @@ import { MedusaError, Validator } from "medusa-core-utils"
|
||||
export default async (req, res) => {
|
||||
const { id } = req.params
|
||||
|
||||
const schema = Validator.object().keys({
|
||||
value: Validator.string(),
|
||||
})
|
||||
const validated = await validator(AdminPostNotesNoteReq, req.body)
|
||||
|
||||
const { value, error } = schema.validate(req.body)
|
||||
if (error) {
|
||||
throw new MedusaError(MedusaError.Types.INVALID_DATA, error.details)
|
||||
}
|
||||
const noteService: NoteService = req.scope.resolve("noteService")
|
||||
const note = await noteService.update(id, validated.value)
|
||||
|
||||
try {
|
||||
const noteService = req.scope.resolve("noteService")
|
||||
const result = await noteService.update(id, value.value)
|
||||
res.status(200).json({ note })
|
||||
}
|
||||
|
||||
res.status(200).json({ note: result })
|
||||
} catch (err) {
|
||||
throw err
|
||||
}
|
||||
export class AdminPostNotesNoteReq {
|
||||
@IsString()
|
||||
value: string
|
||||
}
|
||||
+15
-5
@@ -1,9 +1,11 @@
|
||||
import { Router } from "express"
|
||||
import { Notification } from "./../../../../"
|
||||
import { PaginatedResponse } from "./../../../../types/common"
|
||||
import middlewares from "../../../middlewares"
|
||||
|
||||
const route = Router()
|
||||
|
||||
export default app => {
|
||||
export default (app) => {
|
||||
app.use("/notifications", route)
|
||||
|
||||
/**
|
||||
@@ -22,10 +24,10 @@ export default app => {
|
||||
return app
|
||||
}
|
||||
|
||||
export const defaultRelations = ["resends"]
|
||||
export const allowedRelations = ["resends"]
|
||||
export const defaultAdminNotificationsRelations = ["resends"]
|
||||
export const allowedAdminNotificationsRelations = ["resends"]
|
||||
|
||||
export const defaultFields = [
|
||||
export const defaultAdminNotificationsFields = [
|
||||
"id",
|
||||
"resource_type",
|
||||
"resource_id",
|
||||
@@ -36,7 +38,7 @@ export const defaultFields = [
|
||||
"updated_at",
|
||||
]
|
||||
|
||||
export const allowedFields = [
|
||||
export const allowedAdminNotificationsFields = [
|
||||
"id",
|
||||
"resource_type",
|
||||
"resource_id",
|
||||
@@ -46,3 +48,11 @@ export const allowedFields = [
|
||||
"created_at",
|
||||
"updated_at",
|
||||
]
|
||||
|
||||
export type AdminNotificationsListRes = {
|
||||
notifications: Notification[]
|
||||
}
|
||||
|
||||
export type AdminNotificationsRes = PaginatedResponse & {
|
||||
notification: Notification
|
||||
}
|
||||
@@ -1,83 +0,0 @@
|
||||
import _ from "lodash"
|
||||
import { defaultRelations, defaultFields } from "./"
|
||||
|
||||
/**
|
||||
* @oas [get] /notifications
|
||||
* operationId: "GetNotifications"
|
||||
* summary: "List Notifications"
|
||||
* description: "Retrieves a list of Notifications."
|
||||
* tags:
|
||||
* - Notification
|
||||
* responses:
|
||||
* 200:
|
||||
* description: OK
|
||||
* content:
|
||||
* application/json:
|
||||
* schema:
|
||||
* properties:
|
||||
* notifications:
|
||||
* type: array
|
||||
* items:
|
||||
* $ref: "#/components/schemas/notification"
|
||||
*/
|
||||
export default async (req, res) => {
|
||||
try {
|
||||
const notificationService = req.scope.resolve("notificationService")
|
||||
|
||||
const limit = parseInt(req.query.limit) || 50
|
||||
const offset = parseInt(req.query.offset) || 0
|
||||
|
||||
let selector = {}
|
||||
|
||||
let includeFields = []
|
||||
if ("fields" in req.query) {
|
||||
includeFields = req.query.fields.split(",")
|
||||
}
|
||||
|
||||
let expandFields = []
|
||||
if ("expand" in req.query) {
|
||||
expandFields = req.query.expand.split(",")
|
||||
}
|
||||
|
||||
if ("event_name" in req.query) {
|
||||
const values = req.query.event_name.split(",")
|
||||
selector.event_name = values.length > 1 ? values : values[0]
|
||||
}
|
||||
|
||||
if ("resource_type" in req.query) {
|
||||
const values = req.query.resource_type.split(",")
|
||||
selector.resource_type = values.length > 1 ? values : values[0]
|
||||
}
|
||||
|
||||
if ("resource_id" in req.query) {
|
||||
const values = req.query.resource_id.split(",")
|
||||
selector.resource_id = values.length > 1 ? values : values[0]
|
||||
}
|
||||
|
||||
if ("to" in req.query) {
|
||||
const values = req.query.to.split(",")
|
||||
selector.to = values.length > 1 ? values : values[0]
|
||||
}
|
||||
|
||||
if (!("include_resends" in req.query)) {
|
||||
selector.parent_id = null
|
||||
}
|
||||
|
||||
const listConfig = {
|
||||
select: includeFields.length ? includeFields : defaultFields,
|
||||
relations: expandFields.length ? expandFields : defaultRelations,
|
||||
skip: offset,
|
||||
take: limit,
|
||||
order: { created_at: "DESC" },
|
||||
}
|
||||
|
||||
const notifications = await notificationService.list(selector, listConfig)
|
||||
|
||||
const fields = [...listConfig.select, ...listConfig.relations]
|
||||
const data = notifications.map(o => _.pick(o, fields))
|
||||
|
||||
res.json({ notifications: data, offset, limit })
|
||||
} catch (error) {
|
||||
throw error
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,151 @@
|
||||
import { Type } from "class-transformer"
|
||||
import { IsBooleanString, IsInt, IsOptional, IsString } from "class-validator"
|
||||
import { pick } from "lodash"
|
||||
import { NotificationService } from "../../../../services"
|
||||
import { validator } from "../../../../utils/validator"
|
||||
import {
|
||||
defaultAdminNotificationsFields,
|
||||
defaultAdminNotificationsRelations,
|
||||
} from "./"
|
||||
|
||||
/**
|
||||
* @oas [get] /notifications
|
||||
* operationId: "GetNotifications"
|
||||
* summary: "List Notifications"
|
||||
* description: "Retrieves a list of Notifications."
|
||||
* x-authenticated: true
|
||||
* parameters:
|
||||
* - (query) offset=0 {integer} The number of notifications to skip before starting to collect the notifications set
|
||||
* - (query) limit=50 {integer} The number of notifications to return
|
||||
* - (query) fields {string} The fields to include in the result set
|
||||
* - (query) expand {string} The fields to populate
|
||||
* - (query) event_name {string}
|
||||
* - (query) resource_type {string}
|
||||
* - (query) resource_id {string}
|
||||
* - (query) to {string}
|
||||
* - (query) include_resends {boolean} Whether the result set should include resent notifications or not
|
||||
* tags:
|
||||
* - Notification
|
||||
* responses:
|
||||
* 200:
|
||||
* description: OK
|
||||
* content:
|
||||
* application/json:
|
||||
* schema:
|
||||
* properties:
|
||||
* notifications:
|
||||
* type: array
|
||||
* items:
|
||||
* $ref: "#/components/schemas/notification"
|
||||
*/
|
||||
export default async (req, res) => {
|
||||
const notificationService: NotificationService = req.scope.resolve(
|
||||
"notificationService"
|
||||
)
|
||||
const {
|
||||
limit,
|
||||
offset,
|
||||
fields,
|
||||
expand,
|
||||
event_name,
|
||||
resource_id,
|
||||
resource_type,
|
||||
to,
|
||||
include_resends,
|
||||
} = await validator(AdminGetNotificationsParams, req.query)
|
||||
|
||||
const selector: any = {}
|
||||
|
||||
let includeFields: string[] = []
|
||||
if (fields) {
|
||||
includeFields = fields.split(",")
|
||||
}
|
||||
|
||||
let expandFields: string[] = []
|
||||
if (expand) {
|
||||
expandFields = expand.split(",")
|
||||
}
|
||||
|
||||
if (event_name) {
|
||||
const values = event_name.split(",")
|
||||
selector.event_name = values.length > 1 ? values : values[0]
|
||||
}
|
||||
|
||||
if (resource_type) {
|
||||
const values = resource_type.split(",")
|
||||
selector.resource_type = values.length > 1 ? values : values[0]
|
||||
}
|
||||
|
||||
if (resource_id) {
|
||||
const values = resource_id.split(",")
|
||||
selector.resource_id = values.length > 1 ? values : values[0]
|
||||
}
|
||||
|
||||
if (to) {
|
||||
const values = to.split(",")
|
||||
selector.to = values.length > 1 ? values : values[0]
|
||||
}
|
||||
|
||||
if (!include_resends || include_resends === "false") {
|
||||
selector.parent_id = null
|
||||
}
|
||||
|
||||
const listConfig = {
|
||||
select: includeFields.length
|
||||
? includeFields
|
||||
: defaultAdminNotificationsFields,
|
||||
relations: expandFields.length
|
||||
? expandFields
|
||||
: defaultAdminNotificationsRelations,
|
||||
skip: offset,
|
||||
take: limit,
|
||||
order: { created_at: "DESC" },
|
||||
}
|
||||
|
||||
const notifications = await notificationService.list(selector, listConfig)
|
||||
|
||||
const resultFields = [...listConfig.select, ...listConfig.relations]
|
||||
const data = notifications.map((o) => pick(o, resultFields))
|
||||
|
||||
res.json({ notifications: data })
|
||||
}
|
||||
|
||||
export class AdminGetNotificationsParams {
|
||||
@IsOptional()
|
||||
@IsInt()
|
||||
@Type(() => Number)
|
||||
limit?: number = 50
|
||||
|
||||
@IsOptional()
|
||||
@IsInt()
|
||||
@Type(() => Number)
|
||||
offset?: number = 0
|
||||
|
||||
@IsOptional()
|
||||
@IsString()
|
||||
fields?: string
|
||||
|
||||
@IsOptional()
|
||||
@IsString()
|
||||
expand?: string
|
||||
|
||||
@IsOptional()
|
||||
@IsString()
|
||||
event_name?: string
|
||||
|
||||
@IsOptional()
|
||||
@IsString()
|
||||
resource_type?: string
|
||||
|
||||
@IsOptional()
|
||||
@IsString()
|
||||
resource_id?: string
|
||||
|
||||
@IsOptional()
|
||||
@IsString()
|
||||
to?: string
|
||||
|
||||
@IsOptional()
|
||||
@IsBooleanString()
|
||||
include_resends?: string
|
||||
}
|
||||
@@ -1,55 +0,0 @@
|
||||
import { MedusaError, Validator } from "medusa-core-utils"
|
||||
import { defaultFields, defaultRelations } from "./"
|
||||
|
||||
/**
|
||||
* @oas [post] /notifications/{id}/resend
|
||||
* operationId: "PostNotificationsNotificationResend"
|
||||
* summary: "Resend Notification"
|
||||
* description: "Resends a previously sent notifications, with the same data but optionally to a different address"
|
||||
* parameters:
|
||||
* - (path) id=* {string} The id of the Notification
|
||||
* tags:
|
||||
* - Notification
|
||||
* responses:
|
||||
* 200:
|
||||
* description: OK
|
||||
* content:
|
||||
* application/json:
|
||||
* schema:
|
||||
* properties:
|
||||
* notification:
|
||||
* $ref: "#/components/schemas/notification"
|
||||
*/
|
||||
export default async (req, res) => {
|
||||
const { id } = req.params
|
||||
|
||||
const schema = Validator.object().keys({
|
||||
to: Validator.string().optional(),
|
||||
})
|
||||
|
||||
const { value, error } = schema.validate(req.body)
|
||||
if (error) {
|
||||
throw new MedusaError(MedusaError.Types.INVALID_DATA, error.details)
|
||||
}
|
||||
|
||||
try {
|
||||
const notificationService = req.scope.resolve("notificationService")
|
||||
|
||||
const config = {}
|
||||
|
||||
if (value.to) {
|
||||
config.to = value.to
|
||||
}
|
||||
|
||||
await notificationService.resend(id, config)
|
||||
|
||||
const notification = await notificationService.retrieve(id, {
|
||||
select: defaultFields,
|
||||
relations: defaultRelations,
|
||||
})
|
||||
|
||||
res.json({ notification })
|
||||
} catch (error) {
|
||||
throw error
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,70 @@
|
||||
import { IsString } from "class-validator"
|
||||
import { IsOptional } from "class-validator"
|
||||
import {
|
||||
defaultAdminNotificationsFields,
|
||||
defaultAdminNotificationsRelations,
|
||||
} from "."
|
||||
import { validator } from "../../../../utils/validator"
|
||||
import { NotificationService } from "../../../../services"
|
||||
|
||||
/**
|
||||
* @oas [post] /notifications/{id}/resend
|
||||
* operationId: "PostNotificationsNotificationResend"
|
||||
* summary: "Resend Notification"
|
||||
* description: "Resends a previously sent notifications, with the same data but optionally to a different address"
|
||||
* x-authenticated: true
|
||||
* parameters:
|
||||
* - (path) id=* {string} The id of the Notification
|
||||
* requestBody:
|
||||
* content:
|
||||
* application/json:
|
||||
* schema:
|
||||
* properties:
|
||||
* to:
|
||||
* description: "The address or user identifier that the Notification was sent to"
|
||||
* type: string
|
||||
* tags:
|
||||
* - Notification
|
||||
* responses:
|
||||
* 200:
|
||||
* description: OK
|
||||
* content:
|
||||
* application/json:
|
||||
* schema:
|
||||
* properties:
|
||||
* notification:
|
||||
* $ref: "#/components/schemas/notification"
|
||||
*/
|
||||
export default async (req, res) => {
|
||||
const { id } = req.params
|
||||
|
||||
const validatedBody = await validator(
|
||||
AdminPostNotificationsNotificationResendReq,
|
||||
req.body
|
||||
)
|
||||
|
||||
const notificationService: NotificationService = req.scope.resolve(
|
||||
"notificationService"
|
||||
)
|
||||
|
||||
const config: any = {}
|
||||
|
||||
if (validatedBody.to) {
|
||||
config.to = validatedBody.to
|
||||
}
|
||||
|
||||
await notificationService.resend(id, config)
|
||||
|
||||
const notification = await notificationService.retrieve(id, {
|
||||
select: defaultAdminNotificationsFields,
|
||||
relations: defaultAdminNotificationsRelations,
|
||||
})
|
||||
|
||||
res.json({ notification })
|
||||
}
|
||||
|
||||
export class AdminPostNotificationsNotificationResendReq {
|
||||
@IsOptional()
|
||||
@IsString()
|
||||
to?: string
|
||||
}
|
||||
+42
-31
@@ -1,12 +1,20 @@
|
||||
import _ from "lodash"
|
||||
import { Validator, MedusaError } from "medusa-core-utils"
|
||||
import { defaultFields, defaultRelations } from "./"
|
||||
import {
|
||||
IsInt,
|
||||
IsNotEmpty,
|
||||
IsObject,
|
||||
IsOptional,
|
||||
IsString,
|
||||
} from "class-validator"
|
||||
import { defaultAdminOrdersFields, defaultAdminOrdersRelations } from "."
|
||||
import { OrderService } from "../../../../services"
|
||||
import { validator } from "../../../../utils/validator"
|
||||
|
||||
/**
|
||||
* @oas [post] /orders/{id}/shipping-methods
|
||||
* operationId: "PostOrdersOrderShippingMethods"
|
||||
* summary: "Add a Shipping Method"
|
||||
* description: "Adds a Shipping Method to an Order. If another Shipping Method exists with the same Shipping Profile, the previous Shipping Method will be replaced."
|
||||
* x-authenticated: true
|
||||
* parameters:
|
||||
* - (path) id=* {string} The id of the Order.
|
||||
* - (body) price=* {integer} The price (excluding VAT) that should be charged for the Shipping Method
|
||||
@@ -27,37 +35,40 @@ import { defaultFields, defaultRelations } from "./"
|
||||
export default async (req, res) => {
|
||||
const { id } = req.params
|
||||
|
||||
const schema = Validator.object().keys({
|
||||
price: Validator.number()
|
||||
.integer()
|
||||
.integer()
|
||||
.allow(0)
|
||||
.required(),
|
||||
option_id: Validator.string().required(),
|
||||
data: Validator.object()
|
||||
.optional()
|
||||
.default({}),
|
||||
const validated = await validator(
|
||||
AdminPostOrdersOrderShippingMethodsReq,
|
||||
req.body
|
||||
)
|
||||
|
||||
const orderService: OrderService = req.scope.resolve("orderService")
|
||||
|
||||
await orderService.addShippingMethod(
|
||||
id,
|
||||
validated.option_id,
|
||||
validated.data,
|
||||
{
|
||||
price: validated.price,
|
||||
}
|
||||
)
|
||||
|
||||
const order = await orderService.retrieve(id, {
|
||||
select: defaultAdminOrdersFields,
|
||||
relations: defaultAdminOrdersRelations,
|
||||
})
|
||||
|
||||
const { value, error } = schema.validate(req.body)
|
||||
if (error) {
|
||||
throw new MedusaError(MedusaError.Types.INVALID_DATA, error.details)
|
||||
}
|
||||
res.status(200).json({ order })
|
||||
}
|
||||
|
||||
try {
|
||||
const orderService = req.scope.resolve("orderService")
|
||||
export class AdminPostOrdersOrderShippingMethodsReq {
|
||||
@IsInt()
|
||||
@IsNotEmpty()
|
||||
price: number
|
||||
|
||||
await orderService.addShippingMethod(id, value.option_id, value.data, {
|
||||
price: value.price,
|
||||
})
|
||||
@IsString()
|
||||
@IsNotEmpty()
|
||||
option_id: string
|
||||
|
||||
const order = await orderService.retrieve(id, {
|
||||
select: defaultFields,
|
||||
relations: defaultRelations,
|
||||
})
|
||||
|
||||
res.status(200).json({ order })
|
||||
} catch (err) {
|
||||
throw err
|
||||
}
|
||||
@IsObject()
|
||||
@IsOptional()
|
||||
data?: object = {}
|
||||
}
|
||||
@@ -1,17 +0,0 @@
|
||||
export default async (req, res) => {
|
||||
const { id } = req.params
|
||||
|
||||
try {
|
||||
const orderService = req.scope.resolve("orderService")
|
||||
|
||||
await orderService.archive(id)
|
||||
|
||||
const order = await orderService.retrieve(id, {
|
||||
relations: ["region", "customer", "swaps"],
|
||||
})
|
||||
|
||||
res.json({ order })
|
||||
} catch (error) {
|
||||
throw error
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
import { OrderService } from "../../../../services"
|
||||
|
||||
/**
|
||||
* @oas [post] /orders/{id}/archive
|
||||
* operationId: "PostOrdersOrderArchive"
|
||||
* summary: "Archive order"
|
||||
* description: "Archives the order with the given id."
|
||||
* x-authenticated: true
|
||||
* parameters:
|
||||
* - (path) id=* {string} The id of the Order.
|
||||
* tags:
|
||||
* - Order
|
||||
* responses:
|
||||
* 200:
|
||||
* description: OK
|
||||
* content:
|
||||
* application/json:
|
||||
* schema:
|
||||
* properties:
|
||||
* order:
|
||||
* $ref: "#/components/schemas/order"
|
||||
*/
|
||||
export default async (req, res) => {
|
||||
const { id } = req.params
|
||||
|
||||
const orderService: OrderService = req.scope.resolve("orderService")
|
||||
|
||||
await orderService.archive(id)
|
||||
|
||||
const order = await orderService.retrieve(id, {
|
||||
relations: ["region", "customer", "swaps"],
|
||||
})
|
||||
|
||||
res.json({ order })
|
||||
}
|
||||
@@ -1,51 +0,0 @@
|
||||
import { MedusaError } from "medusa-core-utils"
|
||||
import { defaultRelations, defaultFields } from "."
|
||||
|
||||
/**
|
||||
* @oas [post] /orders/{id}/claims/{claim_id}/cancel
|
||||
* operationId: "PostOrdersClaimCancel"
|
||||
* summary: "Cancels a Claim"
|
||||
* description: "Cancels a Claim"
|
||||
* parameters:
|
||||
* - (path) id=* {string} The id of the Order.
|
||||
* . (path) claim_id=* {string} The id of the Claim.
|
||||
* tags:
|
||||
* - Claim
|
||||
* responses:
|
||||
* 200:
|
||||
* description: OK
|
||||
* content:
|
||||
* application/json:
|
||||
* schema:
|
||||
* properties:
|
||||
* order:
|
||||
* $ref: "#/components/schemas/claim"
|
||||
*/
|
||||
export default async (req, res) => {
|
||||
const { id, claim_id } = req.params
|
||||
|
||||
try {
|
||||
const claimService = req.scope.resolve("claimService")
|
||||
const orderService = req.scope.resolve("orderService")
|
||||
|
||||
const claim = await claimService.retrieve(claim_id)
|
||||
|
||||
if (claim.order_id !== id) {
|
||||
throw new MedusaError(
|
||||
MedusaError.Types.NOT_FOUND,
|
||||
`no claim was found with the id: ${claim_id} related to order: ${id}`
|
||||
)
|
||||
}
|
||||
|
||||
await claimService.cancel(claim_id)
|
||||
|
||||
const order = await orderService.retrieve(id, {
|
||||
select: defaultFields,
|
||||
relations: defaultRelations,
|
||||
})
|
||||
|
||||
res.json({ order })
|
||||
} catch (error) {
|
||||
throw error
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,49 @@
|
||||
import { MedusaError } from "medusa-core-utils"
|
||||
import { defaultAdminOrdersRelations, defaultAdminOrdersFields } from "."
|
||||
import { ClaimService, OrderService } from "../../../../services"
|
||||
|
||||
/**
|
||||
* @oas [post] /orders/{id}/claims/{claim_id}/cancel
|
||||
* operationId: "PostOrdersClaimCancel"
|
||||
* summary: "Cancels a Claim"
|
||||
* description: "Cancels a Claim"
|
||||
* x-authenticated: true
|
||||
* parameters:
|
||||
* - (path) id=* {string} The id of the Order.
|
||||
* - (path) claim_id=* {string} The id of the Claim.
|
||||
* tags:
|
||||
* - Claim
|
||||
* responses:
|
||||
* 200:
|
||||
* description: OK
|
||||
* content:
|
||||
* application/json:
|
||||
* schema:
|
||||
* properties:
|
||||
* order:
|
||||
* $ref: "#/components/schemas/claim_order"
|
||||
*/
|
||||
export default async (req, res) => {
|
||||
const { id, claim_id } = req.params
|
||||
|
||||
const claimService: ClaimService = req.scope.resolve("claimService")
|
||||
const orderService: OrderService = req.scope.resolve("orderService")
|
||||
|
||||
const claim = await claimService.retrieve(claim_id)
|
||||
|
||||
if (claim.order_id !== id) {
|
||||
throw new MedusaError(
|
||||
MedusaError.Types.NOT_FOUND,
|
||||
`no claim was found with the id: ${claim_id} related to order: ${id}`
|
||||
)
|
||||
}
|
||||
|
||||
await claimService.cancel(claim_id)
|
||||
|
||||
const order = await orderService.retrieve(id, {
|
||||
select: defaultAdminOrdersFields,
|
||||
relations: defaultAdminOrdersRelations,
|
||||
})
|
||||
|
||||
res.json({ order })
|
||||
}
|
||||
@@ -1,61 +0,0 @@
|
||||
import { MedusaError } from "medusa-core-utils"
|
||||
import { defaultRelations, defaultFields } from "."
|
||||
|
||||
/**
|
||||
* @oas [post] orders//{id}/claims/{claim_id}/fulfillments/{fulfillment_id}/cancel
|
||||
* operationId: "PostOrdersClaimFulfillmentsCancel"
|
||||
* summary: "Cancels a fulfilmment related to a Claim"
|
||||
* description: "Registers a Fulfillment as canceled."
|
||||
* parameters:
|
||||
* - (path) id=* {string} The id of the Order which the Claim relates to.
|
||||
* - (path) claim_id=* {string} The id of the Claim which the Fulfillment relates to.
|
||||
* - (path) fulfillment_id=* {string} The id of the Fulfillment.
|
||||
* tags:
|
||||
* - Fulfillment
|
||||
* responses:
|
||||
* 200:
|
||||
* description: OK
|
||||
* content:
|
||||
* application/json:
|
||||
* schema:
|
||||
* properties:
|
||||
* fulfillment:
|
||||
* $ref: "#/components/schemas/fulfillment"
|
||||
*/
|
||||
export default async (req, res) => {
|
||||
const { id, claim_id, fulfillment_id } = req.params
|
||||
|
||||
try {
|
||||
const fulfillmentService = req.scope.resolve("fulfillmentService")
|
||||
const claimService = req.scope.resolve("claimService")
|
||||
const orderService = req.scope.resolve("orderService")
|
||||
|
||||
const fulfillment = await fulfillmentService.retrieve(fulfillment_id)
|
||||
|
||||
if (fulfillment.claim_order_id !== claim_id) {
|
||||
throw new MedusaError(
|
||||
MedusaError.Types.NOT_FOUND,
|
||||
`no fulfillment was found with the id: ${fulfillment_id} related to claim: ${claim_id}`
|
||||
)
|
||||
}
|
||||
|
||||
const claim = await claimService.retrieve(claim_id)
|
||||
|
||||
if (claim.order_id !== id) {
|
||||
throw new MedusaError(
|
||||
MedusaError.Types.NOT_FOUND,
|
||||
`no claim was found with the id: ${claim_id} related to order: ${id}`
|
||||
)
|
||||
}
|
||||
|
||||
await claimService.cancelFulfillment(fulfillment_id)
|
||||
|
||||
const order = await orderService.retrieve(id, {
|
||||
select: defaultFields,
|
||||
relations: defaultRelations,
|
||||
})
|
||||
res.json({ order })
|
||||
} catch (error) {
|
||||
throw error
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,64 @@
|
||||
import { MedusaError } from "medusa-core-utils"
|
||||
import { defaultAdminOrdersRelations, defaultAdminOrdersFields } from "."
|
||||
import {
|
||||
ClaimService,
|
||||
FulfillmentService,
|
||||
OrderService,
|
||||
} from "../../../../services"
|
||||
|
||||
/**
|
||||
* @oas [post] /orders/{id}/claims/{claim_id}/fulfillments/{fulfillment_id}/cancel
|
||||
* operationId: "PostOrdersClaimFulfillmentsCancel"
|
||||
* summary: "Cancels a fulfilmment related to a Claim"
|
||||
* description: "Registers a Fulfillment as canceled."
|
||||
* x-authenticated: true
|
||||
* parameters:
|
||||
* - (path) id=* {string} The id of the Order which the Claim relates to.
|
||||
* - (path) claim_id=* {string} The id of the Claim which the Fulfillment relates to.
|
||||
* - (path) fulfillment_id=* {string} The id of the Fulfillment.
|
||||
* tags:
|
||||
* - Fulfillment
|
||||
* responses:
|
||||
* 200:
|
||||
* description: OK
|
||||
* content:
|
||||
* application/json:
|
||||
* schema:
|
||||
* properties:
|
||||
* fulfillment:
|
||||
* $ref: "#/components/schemas/fulfillment"
|
||||
*/
|
||||
export default async (req, res) => {
|
||||
const { id, claim_id, fulfillment_id } = req.params
|
||||
|
||||
const fulfillmentService: FulfillmentService =
|
||||
req.scope.resolve("fulfillmentService")
|
||||
const claimService: ClaimService = req.scope.resolve("claimService")
|
||||
const orderService: OrderService = req.scope.resolve("orderService")
|
||||
|
||||
const fulfillment = await fulfillmentService.retrieve(fulfillment_id)
|
||||
|
||||
if (fulfillment.claim_order_id !== claim_id) {
|
||||
throw new MedusaError(
|
||||
MedusaError.Types.NOT_FOUND,
|
||||
`no fulfillment was found with the id: ${fulfillment_id} related to claim: ${claim_id}`
|
||||
)
|
||||
}
|
||||
|
||||
const claim = await claimService.retrieve(claim_id)
|
||||
|
||||
if (claim.order_id !== id) {
|
||||
throw new MedusaError(
|
||||
MedusaError.Types.NOT_FOUND,
|
||||
`no claim was found with the id: ${claim_id} related to order: ${id}`
|
||||
)
|
||||
}
|
||||
|
||||
await claimService.cancelFulfillment(fulfillment_id)
|
||||
|
||||
const order = await orderService.retrieve(id, {
|
||||
select: defaultAdminOrdersFields,
|
||||
relations: defaultAdminOrdersRelations,
|
||||
})
|
||||
res.json({ order })
|
||||
}
|
||||
@@ -1,62 +0,0 @@
|
||||
import { MedusaError } from "medusa-core-utils"
|
||||
import { defaultRelations, defaultFields } from "."
|
||||
|
||||
/**
|
||||
* @oas [post] /orders/{id}/swaps/{swap_id}/fulfillments/{fulfillment_id}/cancel
|
||||
* operationId: "PostOrdersSwapFulfillmentsCancel"
|
||||
* summary: "Cancels a fulfilmment related to a Swap"
|
||||
* description: "Registers a Fulfillment as canceled."
|
||||
* parameters:
|
||||
* - (path) id=* {string} The id of the Order which the Swap relates to.
|
||||
* - (path) swap_id=* {string} The id of the Swap which the Fulfillment relates to.
|
||||
* - (path) fulfillment_id=* {string} The id of the Fulfillment.
|
||||
* tags:
|
||||
* - Fulfillment
|
||||
* responses:
|
||||
* 200:
|
||||
* description: OK
|
||||
* content:
|
||||
* application/json:
|
||||
* schema:
|
||||
* properties:
|
||||
* fulfillment:
|
||||
* $ref: "#/components/schemas/fulfillment"
|
||||
*/
|
||||
export default async (req, res) => {
|
||||
const { id, swap_id, fulfillment_id } = req.params
|
||||
|
||||
try {
|
||||
const fulfillmentService = req.scope.resolve("fulfillmentService")
|
||||
const swapService = req.scope.resolve("swapService")
|
||||
const orderService = req.scope.resolve("orderService")
|
||||
|
||||
const fulfillment = await fulfillmentService.retrieve(fulfillment_id)
|
||||
|
||||
if (fulfillment.swap_id !== swap_id) {
|
||||
throw new MedusaError(
|
||||
MedusaError.Types.NOT_FOUND,
|
||||
`no fulfillment was found with the id: ${fulfillment_id} related to swap: ${id}`
|
||||
)
|
||||
}
|
||||
|
||||
const swap = await swapService.retrieve(swap_id)
|
||||
|
||||
if (swap.order_id !== id) {
|
||||
throw new MedusaError(
|
||||
MedusaError.Types.NOT_FOUND,
|
||||
`no swap was found with the id: ${swap_id} related to order: ${id}`
|
||||
)
|
||||
}
|
||||
|
||||
await swapService.cancelFulfillment(fulfillment_id)
|
||||
|
||||
const order = await orderService.retrieve(id, {
|
||||
select: defaultFields,
|
||||
relations: defaultRelations,
|
||||
})
|
||||
|
||||
res.json({ order })
|
||||
} catch (error) {
|
||||
throw error
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,65 @@
|
||||
import { MedusaError } from "medusa-core-utils"
|
||||
import { defaultAdminOrdersRelations, defaultAdminOrdersFields } from "."
|
||||
import {
|
||||
FulfillmentService,
|
||||
OrderService,
|
||||
SwapService,
|
||||
} from "../../../../services"
|
||||
|
||||
/**
|
||||
* @oas [post] /orders/{id}/swaps/{swap_id}/fulfillments/{fulfillment_id}/cancel
|
||||
* operationId: "PostOrdersSwapFulfillmentsCancel"
|
||||
* summary: "Cancels a fulfilmment related to a Swap"
|
||||
* description: "Registers a Fulfillment as canceled."
|
||||
* x-authenticated: true
|
||||
* parameters:
|
||||
* - (path) id=* {string} The id of the Order which the Swap relates to.
|
||||
* - (path) swap_id=* {string} The id of the Swap which the Fulfillment relates to.
|
||||
* - (path) fulfillment_id=* {string} The id of the Fulfillment.
|
||||
* tags:
|
||||
* - Fulfillment
|
||||
* responses:
|
||||
* 200:
|
||||
* description: OK
|
||||
* content:
|
||||
* application/json:
|
||||
* schema:
|
||||
* properties:
|
||||
* fulfillment:
|
||||
* $ref: "#/components/schemas/fulfillment"
|
||||
*/
|
||||
export default async (req, res) => {
|
||||
const { id, swap_id, fulfillment_id } = req.params
|
||||
|
||||
const swapService: SwapService = req.scope.resolve("swapService")
|
||||
const orderService: OrderService = req.scope.resolve("orderService")
|
||||
const fulfillmentService: FulfillmentService =
|
||||
req.scope.resolve("fulfillmentService")
|
||||
|
||||
const fulfillment = await fulfillmentService.retrieve(fulfillment_id)
|
||||
|
||||
if (fulfillment.swap_id !== swap_id) {
|
||||
throw new MedusaError(
|
||||
MedusaError.Types.NOT_FOUND,
|
||||
`no fulfillment was found with the id: ${fulfillment_id} related to swap: ${id}`
|
||||
)
|
||||
}
|
||||
|
||||
const swap = await swapService.retrieve(swap_id)
|
||||
|
||||
if (swap.order_id !== id) {
|
||||
throw new MedusaError(
|
||||
MedusaError.Types.NOT_FOUND,
|
||||
`no swap was found with the id: ${swap_id} related to order: ${id}`
|
||||
)
|
||||
}
|
||||
|
||||
await swapService.cancelFulfillment(fulfillment_id)
|
||||
|
||||
const order = await orderService.retrieve(id, {
|
||||
select: defaultAdminOrdersFields,
|
||||
relations: defaultAdminOrdersRelations,
|
||||
})
|
||||
|
||||
res.json({ order })
|
||||
}
|
||||
@@ -1,51 +0,0 @@
|
||||
import { MedusaError } from "medusa-core-utils"
|
||||
import { defaultRelations, defaultFields } from "."
|
||||
|
||||
/**
|
||||
* @oas [post] /orders/{id}/fulfillments/{fulfillment_id}/cancel
|
||||
* operationId: "PostOrdersOrderFulfillmentsCancel"
|
||||
* summary: "Cancels a fulfilmment"
|
||||
* description: "Registers a Fulfillment as canceled."
|
||||
* parameters:
|
||||
* - (path) id=* {string} The id of the Order which the Fulfillment relates to.
|
||||
* - (path) fulfillment_id=* {string} The id of the Fulfillment
|
||||
* tags:
|
||||
* - Fulfillment
|
||||
* responses:
|
||||
* 200:
|
||||
* description: OK
|
||||
* content:
|
||||
* application/json:
|
||||
* schema:
|
||||
* properties:
|
||||
* fulfillment:
|
||||
* $ref: "#/components/schemas/fulfillment"
|
||||
*/
|
||||
export default async (req, res) => {
|
||||
const { id, fulfillment_id } = req.params
|
||||
|
||||
try {
|
||||
const fulfillmentService = req.scope.resolve("fulfillmentService")
|
||||
const orderService = req.scope.resolve("orderService")
|
||||
|
||||
const fulfillment = await fulfillmentService.retrieve(fulfillment_id)
|
||||
|
||||
if (fulfillment.order_id !== id) {
|
||||
throw new MedusaError(
|
||||
MedusaError.Types.NOT_FOUND,
|
||||
`no fulfillment was found with the id: ${fulfillment_id} related to order: ${id}`
|
||||
)
|
||||
}
|
||||
|
||||
await orderService.cancelFulfillment(fulfillment_id)
|
||||
|
||||
const order = await orderService.retrieve(id, {
|
||||
select: defaultFields,
|
||||
relations: defaultRelations,
|
||||
})
|
||||
|
||||
res.json({ order })
|
||||
} catch (error) {
|
||||
throw error
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,50 @@
|
||||
import { MedusaError } from "medusa-core-utils"
|
||||
import { defaultAdminOrdersRelations, defaultAdminOrdersFields } from "."
|
||||
import { FulfillmentService, OrderService } from "../../../../services"
|
||||
|
||||
/**
|
||||
* @oas [post] /orders/{id}/fulfillments/{fulfillment_id}/cancel
|
||||
* operationId: "PostOrdersOrderFulfillmentsCancel"
|
||||
* summary: "Cancels a fulfilmment"
|
||||
* description: "Registers a Fulfillment as canceled."
|
||||
* x-authenticated: true
|
||||
* parameters:
|
||||
* - (path) id=* {string} The id of the Order which the Fulfillment relates to.
|
||||
* - (path) fulfillment_id=* {string} The id of the Fulfillment
|
||||
* tags:
|
||||
* - Fulfillment
|
||||
* responses:
|
||||
* 200:
|
||||
* description: OK
|
||||
* content:
|
||||
* application/json:
|
||||
* schema:
|
||||
* properties:
|
||||
* fulfillment:
|
||||
* $ref: "#/components/schemas/fulfillment"
|
||||
*/
|
||||
export default async (req, res) => {
|
||||
const { id, fulfillment_id } = req.params
|
||||
|
||||
const orderService: OrderService = req.scope.resolve("orderService")
|
||||
const fulfillmentService: FulfillmentService =
|
||||
req.scope.resolve("fulfillmentService")
|
||||
|
||||
const fulfillment = await fulfillmentService.retrieve(fulfillment_id)
|
||||
|
||||
if (fulfillment.order_id !== id) {
|
||||
throw new MedusaError(
|
||||
MedusaError.Types.NOT_FOUND,
|
||||
`no fulfillment was found with the id: ${fulfillment_id} related to order: ${id}`
|
||||
)
|
||||
}
|
||||
|
||||
await orderService.cancelFulfillment(fulfillment_id)
|
||||
|
||||
const order = await orderService.retrieve(id, {
|
||||
select: defaultAdminOrdersFields,
|
||||
relations: defaultAdminOrdersRelations,
|
||||
})
|
||||
|
||||
res.json({ order })
|
||||
}
|
||||
+10
-12
@@ -1,10 +1,12 @@
|
||||
import { defaultFields, defaultRelations } from "."
|
||||
import { defaultAdminOrdersFields, defaultAdminOrdersRelations } from "."
|
||||
import { OrderService } from "../../../../services"
|
||||
|
||||
/**
|
||||
* @oas [post] /orders/{id}/cancel
|
||||
* operationId: "PostOrdersOrderCancel"
|
||||
* summary: "Cancel an Order"
|
||||
* description: "Registers an Order as canceled. This triggers a flow that will cancel any created Fulfillments and Payments, may fail if the Payment or Fulfillment Provider is unable to cancel the Payment/Fulfillment."
|
||||
* x-authenticated: true
|
||||
* parameters:
|
||||
* - (path) id=* {string} The id of the Order.
|
||||
* tags:
|
||||
@@ -22,17 +24,13 @@ import { defaultFields, defaultRelations } from "."
|
||||
export default async (req, res) => {
|
||||
const { id } = req.params
|
||||
|
||||
try {
|
||||
const orderService = req.scope.resolve("orderService")
|
||||
await orderService.cancel(id)
|
||||
const orderService: OrderService = req.scope.resolve("orderService")
|
||||
await orderService.cancel(id)
|
||||
|
||||
const order = await orderService.retrieve(id, {
|
||||
select: defaultFields,
|
||||
relations: defaultRelations,
|
||||
})
|
||||
const order = await orderService.retrieve(id, {
|
||||
select: defaultAdminOrdersFields,
|
||||
relations: defaultAdminOrdersRelations,
|
||||
})
|
||||
|
||||
res.json({ order })
|
||||
} catch (error) {
|
||||
throw error
|
||||
}
|
||||
res.json({ order })
|
||||
}
|
||||
@@ -1,51 +0,0 @@
|
||||
import { MedusaError } from "medusa-core-utils"
|
||||
import { defaultRelations, defaultFields } from "."
|
||||
|
||||
/**
|
||||
* @oas [post] /orders/{id}/swaps/{swap_id}/cancel
|
||||
* operationId: "PostOrdersSwapCancel"
|
||||
* summary: "Cancels a Swap"
|
||||
* description: "Cancels a Swap"
|
||||
* parameters:
|
||||
* - (path) id=* {string} The id of the Order.
|
||||
* . (path) swap_id=* {string} The id of the Swap.
|
||||
* tags:
|
||||
* - Swap
|
||||
* responses:
|
||||
* 200:
|
||||
* description: OK
|
||||
* content:
|
||||
* application/json:
|
||||
* schema:
|
||||
* properties:
|
||||
* order:
|
||||
* $ref: "#/components/schemas/swap"
|
||||
*/
|
||||
export default async (req, res) => {
|
||||
const { id, swap_id } = req.params
|
||||
|
||||
try {
|
||||
const swapService = req.scope.resolve("swapService")
|
||||
const orderService = req.scope.resolve("orderService")
|
||||
|
||||
const swap = await swapService.retrieve(swap_id)
|
||||
|
||||
if (swap.order_id !== id) {
|
||||
throw new MedusaError(
|
||||
MedusaError.Types.NOT_FOUND,
|
||||
`no swap was found with the id: ${swap_id} related to order: ${id}`
|
||||
)
|
||||
}
|
||||
|
||||
await swapService.cancel(swap_id)
|
||||
|
||||
const order = await orderService.retrieve(id, {
|
||||
select: defaultFields,
|
||||
relations: defaultRelations,
|
||||
})
|
||||
|
||||
res.json({ order })
|
||||
} catch (error) {
|
||||
throw error
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,49 @@
|
||||
import { MedusaError } from "medusa-core-utils"
|
||||
import { defaultAdminOrdersRelations, defaultAdminOrdersFields } from "."
|
||||
import { OrderService, SwapService } from "../../../../services"
|
||||
|
||||
/**
|
||||
* @oas [post] /orders/{id}/swaps/{swap_id}/cancel
|
||||
* operationId: "PostOrdersSwapCancel"
|
||||
* summary: "Cancels a Swap"
|
||||
* description: "Cancels a Swap"
|
||||
* x-authenticated: true
|
||||
* parameters:
|
||||
* - (path) id=* {string} The id of the Order.
|
||||
* - (path) swap_id=* {string} The id of the Swap.
|
||||
* tags:
|
||||
* - Swap
|
||||
* responses:
|
||||
* 200:
|
||||
* description: OK
|
||||
* content:
|
||||
* application/json:
|
||||
* schema:
|
||||
* properties:
|
||||
* order:
|
||||
* $ref: "#/components/schemas/swap"
|
||||
*/
|
||||
export default async (req, res) => {
|
||||
const { id, swap_id } = req.params
|
||||
|
||||
const swapService: SwapService = req.scope.resolve("swapService")
|
||||
const orderService: OrderService = req.scope.resolve("orderService")
|
||||
|
||||
const swap = await swapService.retrieve(swap_id)
|
||||
|
||||
if (swap.order_id !== id) {
|
||||
throw new MedusaError(
|
||||
MedusaError.Types.NOT_FOUND,
|
||||
`no swap was found with the id: ${swap_id} related to order: ${id}`
|
||||
)
|
||||
}
|
||||
|
||||
await swapService.cancel(swap_id)
|
||||
|
||||
const order = await orderService.retrieve(id, {
|
||||
select: defaultAdminOrdersFields,
|
||||
relations: defaultAdminOrdersRelations,
|
||||
})
|
||||
|
||||
res.json({ order })
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user