feat: Typescript for API layer (#817)
Co-authored-by: Philip Korsholm <88927411+pKorsholm@users.noreply.github.com> Co-authored-by: Zakaria El Asri <33696020+zakariaelas@users.noreply.github.com> Co-authored-by: Kasper Fabricius Kristensen <45367945+kasperkristensen@users.noreply.github.com> Co-authored-by: Philip Korsholm <philip.korsholm@hotmail.com> Co-authored-by: Sebastian Rindom <seb@medusa-commerce.com>
This commit is contained in:
413 files changed
+20957
-10349
No files matched your search
@@ -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,21 +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)
|
||||
}
|
||||
const oauthService = req.scope.resolve("oauthService")
|
||||
const data = await oauthService.generateToken(
|
||||
value.application_name,
|
||||
value.code,
|
||||
value.state
|
||||
)
|
||||
res.status(200).json({ apps: data })
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
+9
@@ -1,4 +1,5 @@
|
||||
import { Router } from "express"
|
||||
import { Oauth } from "../../../.."
|
||||
import middlewares from "../../../middlewares"
|
||||
|
||||
const route = Router()
|
||||
@@ -14,3 +15,11 @@ export default (app) => {
|
||||
|
||||
return app
|
||||
}
|
||||
|
||||
export type AdminAppsRes = {
|
||||
apps: Oauth
|
||||
}
|
||||
|
||||
export type AdminAppsListRes = {
|
||||
apps: Oauth[]
|
||||
}
|
||||
@@ -1,6 +0,0 @@
|
||||
export default async (req, res) => {
|
||||
const oauthService = req.scope.resolve("oauthService")
|
||||
const data = await oauthService.list({})
|
||||
|
||||
res.status(200).json({ apps: data })
|
||||
}
|
||||
@@ -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"
|
||||
)
|
||||
})
|
||||
})
|
||||
})
|
||||
+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,
|
||||
})
|
||||
}
|
||||
+8
-10
@@ -1,8 +1,10 @@
|
||||
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:
|
||||
@@ -17,17 +19,13 @@
|
||||
* collection:
|
||||
* $ref: "#/components/schemas/product_collection"
|
||||
*/
|
||||
|
||||
export default async (req, res) => {
|
||||
const { id } = req.params
|
||||
try {
|
||||
const productCollectionService = req.scope.resolve(
|
||||
"productCollectionService"
|
||||
)
|
||||
|
||||
const collection = await productCollectionService.retrieve(id)
|
||||
res.status(200).json({ collection })
|
||||
} catch (err) {
|
||||
throw err
|
||||
}
|
||||
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,41 +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) => {
|
||||
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, count] = await productCollectionService.listAndCount(
|
||||
selector,
|
||||
listConfig
|
||||
)
|
||||
|
||||
res.status(200).json({ collections, count })
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
+25
-16
@@ -1,10 +1,12 @@
|
||||
import { Validator, MedusaError } from "medusa-core-utils"
|
||||
|
||||
import { IsEmail, 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.
|
||||
@@ -23,20 +25,27 @@ import { Validator, MedusaError } from "medusa-core-utils"
|
||||
* $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 validated = await validator(AdminPostCustomersReq, req.bodyn)
|
||||
|
||||
const { value, error } = schema.validate(req.body)
|
||||
if (error) {
|
||||
throw new MedusaError(MedusaError.Types.INVALID_DATA, error.details)
|
||||
}
|
||||
|
||||
const customerService = req.scope.resolve("customerService")
|
||||
const customer = await customerService.create(value)
|
||||
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
|
||||
}
|
||||
+4
-1
@@ -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,7 +22,7 @@
|
||||
*/
|
||||
export default async (req, res) => {
|
||||
const { id } = req.params
|
||||
const customerService = req.scope.resolve("customerService")
|
||||
const customerService: CustomerService = req.scope.resolve("customerService")
|
||||
const customer = await customerService.retrieve(id, {
|
||||
relations: ["orders", "shipping_addresses"],
|
||||
})
|
||||
+17
@@ -1,4 +1,6 @@
|
||||
import { Router } from "express"
|
||||
import { Customer } from "../../../.."
|
||||
import { DeleteResponse, PaginatedResponse } from "../../../../types/common"
|
||||
import middlewares from "../../../middlewares"
|
||||
|
||||
const route = Router()
|
||||
@@ -13,3 +15,18 @@ export default (app) => {
|
||||
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,47 +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) => {
|
||||
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 })
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
+35
-18
@@ -1,10 +1,13 @@
|
||||
import { Validator, MedusaError } from "medusa-core-utils"
|
||||
|
||||
import { IsEmail, IsOptional, IsString } from "class-validator"
|
||||
import CustomerService from "../../../../services/customer"
|
||||
import { MedusaError } from "medusa-core-utils"
|
||||
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:
|
||||
@@ -22,8 +25,11 @@ import { Validator, MedusaError } from "medusa-core-utils"
|
||||
* type: string
|
||||
* description: The Customer's last name.
|
||||
* phone:
|
||||
* type: string
|
||||
* description: The Customer's phone number.
|
||||
* type: object
|
||||
* password:
|
||||
* type: string
|
||||
* description: The Customer's password.
|
||||
* tags:
|
||||
* - Customer
|
||||
* responses:
|
||||
@@ -39,34 +45,45 @@ import { Validator, MedusaError } from "medusa-core-utils"
|
||||
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 validated = await validator(AdminPostCustomersCustomerReq, req.body)
|
||||
|
||||
const { value, error } = schema.validate(req.body)
|
||||
if (error) {
|
||||
throw new MedusaError(MedusaError.Types.INVALID_DATA, error.details)
|
||||
}
|
||||
|
||||
const customerService = req.scope.resolve("customerService")
|
||||
const customerService: CustomerService = req.scope.resolve("customerService")
|
||||
|
||||
let customer = await customerService.retrieve(id)
|
||||
|
||||
if (value.email && customer.has_account) {
|
||||
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, value)
|
||||
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
|
||||
}
|
||||
@@ -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" },
|
||||
|
||||
@@ -86,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`
|
||||
)
|
||||
})
|
||||
@@ -180,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"`
|
||||
)
|
||||
})
|
||||
})
|
||||
|
||||
+9
-6
@@ -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,12 +24,13 @@ import { defaultFields, defaultRelations } from "./"
|
||||
*/
|
||||
export default async (req, res) => {
|
||||
const { discount_id, region_id } = req.params
|
||||
const discountService = req.scope.resolve("discountService")
|
||||
|
||||
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 })
|
||||
+11
-8
@@ -1,13 +1,15 @@
|
||||
import { defaultFields, defaultRelations } from "./"
|
||||
|
||||
import { defaultAdminDiscountsFields, defaultAdminDiscountsRelations } from "."
|
||||
import { Discount } from "../../../.."
|
||||
import DiscountService from "../../../../services/discount"
|
||||
/**
|
||||
* @oas [post] /discounts/{id}/products/{product_id}
|
||||
* @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) product_id=* {string} The id of the Product.
|
||||
* - (path) variant_id=* {string} The id of the Product.
|
||||
* tags:
|
||||
* - Discount
|
||||
* responses:
|
||||
@@ -22,12 +24,13 @@ import { defaultFields, defaultRelations } from "./"
|
||||
*/
|
||||
export default async (req, res) => {
|
||||
const { discount_id, variant_id } = req.params
|
||||
const discountService = req.scope.resolve("discountService")
|
||||
|
||||
const discountService: DiscountService = req.scope.resolve("discountService")
|
||||
await discountService.addValidProduct(discount_id, variant_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 })
|
||||
+99
-32
@@ -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,36 +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 = req.scope.resolve("discountService")
|
||||
const created = await discountService.create(value)
|
||||
const discount = await discountService.retrieve(created.id, defaultRelations)
|
||||
const discountService: DiscountService = req.scope.resolve("discountService")
|
||||
const created = await discountService.create(validated)
|
||||
const discount = await discountService.retrieve(
|
||||
created.id,
|
||||
defaultAdminDiscountsRelations
|
||||
)
|
||||
|
||||
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[]
|
||||
}
|
||||
+33
-15
@@ -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,20 +32,16 @@ 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 { value, error } = schema.validate(req.body)
|
||||
|
||||
if (error) {
|
||||
throw new MedusaError(MedusaError.Types.INVALID_DATA, error.details)
|
||||
}
|
||||
|
||||
const discountService = req.scope.resolve("discountService")
|
||||
const created = await discountService.createDynamicCode(discount_id, value)
|
||||
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"],
|
||||
@@ -45,3 +49,17 @@ export default async (req, res) => {
|
||||
|
||||
res.status(200).json({ discount })
|
||||
}
|
||||
|
||||
export class AdminPostDiscountsDiscountDynamicCodesReq {
|
||||
@IsString()
|
||||
@IsNotEmpty()
|
||||
code: string
|
||||
|
||||
@IsNumber()
|
||||
@IsOptional()
|
||||
usage_limit = 1
|
||||
|
||||
@IsObject()
|
||||
@IsOptional()
|
||||
metadata?: object
|
||||
}
|
||||
+5
-1
@@ -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:
|
||||
@@ -25,7 +28,8 @@
|
||||
*/
|
||||
export default async (req, res) => {
|
||||
const { discount_id } = req.params
|
||||
const discountService = req.scope.resolve("discountService")
|
||||
|
||||
const discountService: DiscountService = req.scope.resolve("discountService")
|
||||
await discountService.delete(discount_id)
|
||||
|
||||
res.json({
|
||||
+5
-1
@@ -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
|
||||
@@ -20,7 +23,8 @@
|
||||
*/
|
||||
export default async (req, res) => {
|
||||
const { discount_id, code } = req.params
|
||||
const discountService = req.scope.resolve("discountService")
|
||||
|
||||
const discountService: DiscountService = req.scope.resolve("discountService")
|
||||
await discountService.deleteDynamicCode(discount_id, code)
|
||||
|
||||
const discount = await discountService.retrieve(discount_id, {
|
||||
+9
-4
@@ -1,10 +1,11 @@
|
||||
import { 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,8 +22,12 @@ import { defaultRelations } from "./"
|
||||
*/
|
||||
export default async (req, res) => {
|
||||
const { code } = req.params
|
||||
const discountService = req.scope.resolve("discountService")
|
||||
const discount = await discountService.retrieveByCode(code, defaultRelations)
|
||||
|
||||
const discountService: DiscountService = req.scope.resolve("discountService")
|
||||
const discount = await discountService.retrieveByCode(
|
||||
code,
|
||||
defaultAdminDiscountsRelations
|
||||
)
|
||||
|
||||
res.status(200).json({ discount })
|
||||
}
|
||||
+7
-5
@@ -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,10 +22,11 @@ import { defaultFields, defaultRelations } from "./"
|
||||
*/
|
||||
export default async (req, res) => {
|
||||
const { discount_id } = req.params
|
||||
const discountService = req.scope.resolve("discountService")
|
||||
|
||||
const discountService: DiscountService = req.scope.resolve("discountService")
|
||||
const data = await discountService.retrieve(discount_id, {
|
||||
select: defaultFields,
|
||||
relations: defaultRelations,
|
||||
select: defaultAdminDiscountsFields,
|
||||
relations: defaultAdminDiscountsRelations,
|
||||
})
|
||||
|
||||
res.status(200).json({ discount: data })
|
||||
+28
-2
@@ -1,5 +1,8 @@
|
||||
import { Router } from "express"
|
||||
import { Discount } from "../../../.."
|
||||
import middlewares from "../../../middlewares"
|
||||
import "reflect-metadata"
|
||||
import { DeleteResponse, PaginatedResponse } from "../../../../types/common"
|
||||
|
||||
const route = Router()
|
||||
|
||||
@@ -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,52 +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) => {
|
||||
const discountService = req.scope.resolve("discountService")
|
||||
const limit = parseInt(req.query.limit) || 20
|
||||
const offset = parseInt(req.query.offset) || 0
|
||||
const 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 })
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
+7
-5
@@ -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.
|
||||
@@ -22,12 +23,13 @@ import { defaultFields, defaultRelations } from "./"
|
||||
*/
|
||||
export default async (req, res) => {
|
||||
const { discount_id, region_id } = req.params
|
||||
const discountService = req.scope.resolve("discountService")
|
||||
|
||||
const discountService: DiscountService = req.scope.resolve("discountService")
|
||||
await discountService.removeRegion(discount_id, region_id)
|
||||
|
||||
const discount = await discountService.retrieve(discount_id, {
|
||||
select: defaultFields,
|
||||
relations: defaultRelations,
|
||||
select: defaultAdminDiscountsFields,
|
||||
relations: defaultAdminDiscountsRelations,
|
||||
})
|
||||
|
||||
res.status(200).json({ discount })
|
||||
+7
-5
@@ -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.
|
||||
@@ -22,12 +23,13 @@ import { defaultFields, defaultRelations } from "./"
|
||||
*/
|
||||
export default async (req, res) => {
|
||||
const { discount_id, variant_id } = req.params
|
||||
const discountService = req.scope.resolve("discountService")
|
||||
|
||||
const discountService: DiscountService = req.scope.resolve("discountService")
|
||||
await discountService.removeValidProduct(discount_id, variant_id)
|
||||
|
||||
const discount = await discountService.retrieve(discount_id, {
|
||||
select: defaultFields,
|
||||
relations: defaultRelations,
|
||||
select: defaultAdminDiscountsFields,
|
||||
relations: defaultAdminDiscountsRelations,
|
||||
})
|
||||
|
||||
res.status(200).json({ discount })
|
||||
@@ -1,95 +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().optional(),
|
||||
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)
|
||||
}
|
||||
|
||||
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 })
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
+131
-47
@@ -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,52 +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)
|
||||
|
||||
let { value, error } = schema.validate(req.body)
|
||||
if (error) {
|
||||
throw new MedusaError(MedusaError.Types.INVALID_DATA, error.details)
|
||||
}
|
||||
const value = transformIdableFields(validated, [
|
||||
"shipping_address",
|
||||
"billing_address",
|
||||
])
|
||||
|
||||
value = transformIdableFields(value, ["shipping_address", "billing_address"])
|
||||
|
||||
const draftOrderService = req.scope.resolve("draftOrderService")
|
||||
let draftOrder = await draftOrderService.create(value)
|
||||
const draftOrderService: DraftOrderService =
|
||||
req.scope.resolve("draftOrderService")
|
||||
let draftOrder: DraftOrder = await draftOrderService.create(value)
|
||||
|
||||
draftOrder = await draftOrderService.retrieve(draftOrder.id, {
|
||||
relations: defaultRelations,
|
||||
select: defaultFields,
|
||||
relations: defaultAdminDraftOrdersRelations,
|
||||
select: defaultAdminDraftOrdersFields,
|
||||
})
|
||||
|
||||
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 = {}
|
||||
}
|
||||
+60
-29
@@ -1,14 +1,28 @@
|
||||
import { MedusaError, Validator } from "medusa-core-utils"
|
||||
import { defaultCartFields, defaultCartRelations, defaultFields } from "."
|
||||
|
||||
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:
|
||||
@@ -42,28 +56,24 @@ import { defaultCartFields, defaultCartRelations, defaultFields } from "."
|
||||
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 validated = await validator(
|
||||
AdminPostDraftOrdersDraftOrderLineItemsReq,
|
||||
req.body
|
||||
)
|
||||
|
||||
const { value, error } = schema.validate(req.body)
|
||||
if (error) {
|
||||
throw new MedusaError(MedusaError.Types.INVALID_DATA, error.details)
|
||||
}
|
||||
|
||||
const draftOrderService = req.scope.resolve("draftOrderService")
|
||||
const cartService = req.scope.resolve("cartService")
|
||||
const lineItemService = req.scope.resolve("lineItemService")
|
||||
const entityManager = req.scope.resolve("manager")
|
||||
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: defaultFields, relations: ["cart"] })
|
||||
.retrieve(id, {
|
||||
select: defaultAdminDraftOrdersFields,
|
||||
relations: ["cart"],
|
||||
})
|
||||
|
||||
if (draftOrder.status === "completed") {
|
||||
throw new MedusaError(
|
||||
@@ -72,12 +82,12 @@ export default async (req, res) => {
|
||||
)
|
||||
}
|
||||
|
||||
if (value.variant_id) {
|
||||
if (validated.variant_id) {
|
||||
const line = await lineItemService.generate(
|
||||
value.variant_id,
|
||||
validated.variant_id,
|
||||
draftOrder.cart.region_id,
|
||||
value.quantity,
|
||||
{ metadata: value.metadata, unit_price: value.unit_price }
|
||||
validated.quantity,
|
||||
{ metadata: validated.metadata, unit_price: validated.unit_price }
|
||||
)
|
||||
|
||||
await cartService
|
||||
@@ -88,20 +98,41 @@ export default async (req, res) => {
|
||||
await lineItemService.withTransaction(manager).create({
|
||||
cart_id: draftOrder.cart_id,
|
||||
has_shipping: true,
|
||||
title: value.title || "Custom item",
|
||||
title: validated.title,
|
||||
allow_discounts: false,
|
||||
unit_price: value.unit_price || 0,
|
||||
quantity: value.quantity,
|
||||
unit_price: validated.unit_price || 0,
|
||||
quantity: validated.quantity,
|
||||
})
|
||||
}
|
||||
|
||||
draftOrder.cart = await cartService
|
||||
.withTransaction(manager)
|
||||
.retrieve(draftOrder.cart_id, {
|
||||
relations: defaultCartRelations,
|
||||
select: defaultCartFields,
|
||||
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 = {}
|
||||
}
|
||||
+4
-2
@@ -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,11 +25,11 @@
|
||||
* deleted:
|
||||
* type: boolean
|
||||
*/
|
||||
|
||||
export default async (req, res) => {
|
||||
const { id } = req.params
|
||||
|
||||
const draftOrderService = req.scope.resolve("draftOrderService")
|
||||
const draftOrderService: DraftOrderService =
|
||||
req.scope.resolve("draftOrderService")
|
||||
await draftOrderService.delete(id)
|
||||
|
||||
res.json({
|
||||
+17
-9
@@ -1,11 +1,18 @@
|
||||
import { MedusaError } from "medusa-core-utils"
|
||||
import { defaultCartFields, defaultCartRelations, defaultFields } from "."
|
||||
|
||||
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.
|
||||
@@ -25,14 +32,15 @@ import { defaultCartFields, defaultCartRelations, defaultFields } from "."
|
||||
export default async (req, res) => {
|
||||
const { id, line_id } = req.params
|
||||
|
||||
const draftOrderService = req.scope.resolve("draftOrderService")
|
||||
const cartService = req.scope.resolve("cartService")
|
||||
const entityManager = req.scope.resolve("manager")
|
||||
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 = await draftOrderService
|
||||
const draftOrder: DraftOrder = await draftOrderService
|
||||
.withTransaction(manager)
|
||||
.retrieve(id, { select: defaultFields })
|
||||
.retrieve(id, { select: defaultAdminDraftOrdersFields })
|
||||
|
||||
if (draftOrder.status === "completed") {
|
||||
throw new MedusaError(
|
||||
@@ -48,8 +56,8 @@ export default async (req, res) => {
|
||||
draftOrder.cart = await cartService
|
||||
.withTransaction(manager)
|
||||
.retrieve(draftOrder.cart_id, {
|
||||
relations: defaultCartRelations,
|
||||
select: defaultCartFields,
|
||||
relations: defaultAdminDraftOrdersCartRelations,
|
||||
select: defaultAdminDraftOrdersCartFields,
|
||||
})
|
||||
|
||||
res.status(200).json({ draft_order: draftOrder })
|
||||
@@ -1,45 +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
|
||||
|
||||
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 })
|
||||
}
|
||||
@@ -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 })
|
||||
}
|
||||
+32
-6
@@ -1,5 +1,7 @@
|
||||
import { Router } from "express"
|
||||
import { DraftOrder, Order } from "../../../.."
|
||||
import middlewares from "../../../middlewares"
|
||||
import { DeleteResponse, PaginatedResponse } from "../../../../types/common"
|
||||
|
||||
const route = Router()
|
||||
|
||||
@@ -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,47 +0,0 @@
|
||||
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) => {
|
||||
const draftOrderService = req.scope.resolve("draftOrderService")
|
||||
|
||||
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 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 })
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
+18
-7
@@ -1,6 +1,13 @@
|
||||
import { EntityManager } from "typeorm"
|
||||
import {
|
||||
defaultFields as defaultOrderFields,
|
||||
defaultRelations as defaultOrderRelations,
|
||||
CartService,
|
||||
DraftOrderService,
|
||||
OrderService,
|
||||
PaymentProviderService,
|
||||
} from "../../../../services"
|
||||
import {
|
||||
defaultAdminOrdersFields as defaultOrderFields,
|
||||
defaultAdminOrdersRelations as defaultOrderRelations,
|
||||
} from "../orders/index"
|
||||
|
||||
/**
|
||||
@@ -8,6 +15,7 @@ import {
|
||||
* 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:
|
||||
@@ -26,11 +34,14 @@ import {
|
||||
export default async (req, res) => {
|
||||
const { id } = req.params
|
||||
|
||||
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")
|
||||
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) => {
|
||||
+69
-30
@@ -1,11 +1,26 @@
|
||||
import { MedusaError, Validator } from "medusa-core-utils"
|
||||
import { defaultCartFields, defaultCartRelations } from "."
|
||||
|
||||
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:
|
||||
@@ -57,28 +72,11 @@ import { defaultCartFields, defaultCartRelations } from "."
|
||||
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 validated = await validator(AdminPostDraftOrdersDraftOrderReq, req.body)
|
||||
|
||||
const { value, error } = schema.validate(req.body)
|
||||
if (error) {
|
||||
throw new MedusaError(MedusaError.Types.INVALID_DATA, error.details)
|
||||
}
|
||||
|
||||
const draftOrderService = req.scope.resolve("draftOrderService")
|
||||
const cartService = req.scope.resolve("cartService")
|
||||
const draftOrderService: DraftOrderService =
|
||||
req.scope.resolve("draftOrderService")
|
||||
const cartService: CartService = req.scope.resolve("cartService")
|
||||
|
||||
const draftOrder = await draftOrderService.retrieve(id)
|
||||
|
||||
@@ -89,19 +87,60 @@ export default async (req, res) => {
|
||||
)
|
||||
}
|
||||
|
||||
if ("no_notification_order" in value) {
|
||||
if (validated.no_notification_order !== undefined) {
|
||||
await draftOrderService.update(draftOrder.id, {
|
||||
no_notification_order: value.no_notification_order,
|
||||
no_notification_order: validated.no_notification_order,
|
||||
})
|
||||
delete value.no_notification_order
|
||||
delete validated.no_notification_order
|
||||
}
|
||||
|
||||
await cartService.update(draftOrder.cart_id, value)
|
||||
await cartService.update(draftOrder.cart_id, validated)
|
||||
|
||||
draftOrder.cart = await cartService.retrieve(draftOrder.cart_id, {
|
||||
relations: defaultCartRelations,
|
||||
select: defaultCartFields,
|
||||
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
|
||||
}
|
||||
+54
-24
@@ -1,11 +1,20 @@
|
||||
import { MedusaError, Validator } from "medusa-core-utils"
|
||||
import { defaultCartFields, defaultCartRelations, defaultFields } from "."
|
||||
|
||||
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:
|
||||
@@ -39,27 +48,21 @@ import { defaultCartFields, defaultCartRelations, defaultFields } from "."
|
||||
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 validated = await validator(
|
||||
AdminPostDraftOrdersDraftOrderLineItemsItemReq,
|
||||
req.body
|
||||
)
|
||||
|
||||
const { value, error } = schema.validate(req.body)
|
||||
if (error) {
|
||||
throw new MedusaError(MedusaError.Types.INVALID_DATA, error.details)
|
||||
}
|
||||
|
||||
const draftOrderService = req.scope.resolve("draftOrderService")
|
||||
const cartService = req.scope.resolve("cartService")
|
||||
const entityManager = req.scope.resolve("manager")
|
||||
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 = await draftOrderService
|
||||
const draftOrder: DraftOrder = await draftOrderService
|
||||
.withTransaction(manager)
|
||||
.retrieve(id, {
|
||||
select: defaultFields,
|
||||
select: defaultAdminDraftOrdersFields,
|
||||
relations: ["cart", "cart.items"],
|
||||
})
|
||||
|
||||
@@ -70,7 +73,7 @@ export default async (req, res) => {
|
||||
)
|
||||
}
|
||||
|
||||
if (value.quantity === 0) {
|
||||
if (validated.quantity === 0) {
|
||||
await cartService
|
||||
.withTransaction(manager)
|
||||
.removeLineItem(draftOrder.cart.id, line_id)
|
||||
@@ -84,8 +87,8 @@ export default async (req, res) => {
|
||||
)
|
||||
}
|
||||
|
||||
const lineItemUpdate = {
|
||||
...value,
|
||||
const lineItemUpdate: LineItemUpdate = {
|
||||
...validated,
|
||||
region_id: draftOrder.cart.region_id,
|
||||
}
|
||||
|
||||
@@ -101,10 +104,37 @@ export default async (req, res) => {
|
||||
draftOrder.cart = await cartService
|
||||
.withTransaction(manager)
|
||||
.retrieve(draftOrder.cart_id, {
|
||||
relations: defaultCartRelations,
|
||||
select: defaultCartFields,
|
||||
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 = {}
|
||||
}
|
||||
+34
-19
@@ -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,30 +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 { value, error } = schema.validate(req.body)
|
||||
if (error) {
|
||||
throw new MedusaError(MedusaError.Types.INVALID_DATA, error.details)
|
||||
}
|
||||
|
||||
const giftCardService = req.scope.resolve("giftCardService")
|
||||
const giftCardService: GiftCardService = req.scope.resolve("giftCardService")
|
||||
|
||||
const newly = await giftCardService.create({
|
||||
...value,
|
||||
balance: value.value,
|
||||
...validated,
|
||||
balance: validated.value,
|
||||
})
|
||||
|
||||
const giftCard = await giftCardService.retrieve(newly.id, {
|
||||
select: defaultFields,
|
||||
relations: defaultRelations,
|
||||
select: defaultAdminGiftCardFields,
|
||||
relations: defaultAdminGiftCardRelations,
|
||||
})
|
||||
|
||||
res.status(200).json({ gift_card: giftCard })
|
||||
}
|
||||
|
||||
export class AdminPostGiftCardsReq {
|
||||
@IsOptional()
|
||||
@IsInt()
|
||||
value?: number
|
||||
|
||||
@IsOptional()
|
||||
@IsDate()
|
||||
@Type(() => Date)
|
||||
ends_at?: Date
|
||||
|
||||
@IsOptional()
|
||||
@IsBoolean()
|
||||
is_disabled?: boolean
|
||||
|
||||
@IsOptional()
|
||||
@IsString()
|
||||
region_id?: string
|
||||
|
||||
@IsOptional()
|
||||
metadata?: object
|
||||
}
|
||||
+1
@@ -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:
|
||||
+4
-3
@@ -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:
|
||||
@@ -24,8 +25,8 @@ export default async (req, res) => {
|
||||
|
||||
const giftCardService = req.scope.resolve("giftCardService")
|
||||
const giftCard = await giftCardService.retrieve(id, {
|
||||
select: defaultFields,
|
||||
relations: defaultRelations,
|
||||
select: defaultAdminGiftCardFields,
|
||||
relations: defaultAdminGiftCardRelations,
|
||||
})
|
||||
|
||||
res.status(200).json({ gift_card: giftCard })
|
||||
+21
-4
@@ -1,4 +1,7 @@
|
||||
import { Router } from "express"
|
||||
import "reflect-metadata"
|
||||
import { GiftCard } from "../../../.."
|
||||
import { DeleteResponse, PaginatedResponse } from "../../../../types/common"
|
||||
import middlewares from "../../../middlewares"
|
||||
|
||||
const route = Router()
|
||||
@@ -19,7 +22,7 @@ export default (app) => {
|
||||
return app
|
||||
}
|
||||
|
||||
export const defaultFields = [
|
||||
export const defaultAdminGiftCardFields = [
|
||||
"id",
|
||||
"code",
|
||||
"value",
|
||||
@@ -33,9 +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",
|
||||
@@ -49,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,43 +0,0 @@
|
||||
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) => {
|
||||
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 })
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
+33
-18
@@ -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,27 +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 { value, error } = schema.validate(req.body)
|
||||
if (error) {
|
||||
throw new MedusaError(MedusaError.Types.INVALID_DATA, error.details)
|
||||
}
|
||||
const giftCardService: GiftCardService = req.scope.resolve("giftCardService")
|
||||
|
||||
const giftCardService = req.scope.resolve("giftCardService")
|
||||
|
||||
await giftCardService.update(id, value)
|
||||
await giftCardService.update(id, validated)
|
||||
|
||||
const giftCard = await giftCardService.retrieve(id, {
|
||||
select: defaultFields,
|
||||
relations: defaultRelations,
|
||||
select: defaultAdminGiftCardFields,
|
||||
relations: defaultAdminGiftCardRelations,
|
||||
})
|
||||
|
||||
res.status(200).json({ gift_card: giftCard })
|
||||
}
|
||||
|
||||
export class AdminPostGiftCardsGiftCardReq {
|
||||
@IsOptional()
|
||||
@IsInt()
|
||||
balance?: number
|
||||
|
||||
@IsOptional()
|
||||
@IsBoolean()
|
||||
is_disabled?: boolean
|
||||
|
||||
@IsOptional()
|
||||
@IsDate()
|
||||
@Type(() => Date)
|
||||
ends_at?: Date
|
||||
|
||||
@IsOptional()
|
||||
@IsString()
|
||||
region_id?: string
|
||||
|
||||
@IsOptional()
|
||||
metadata?: object
|
||||
}
|
||||
+24
-16
@@ -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,27 +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 = req.user.id || req.user.userId
|
||||
const userId: string = req.user.id || req.user.userId
|
||||
|
||||
const { value, error } = schema.validate(req.body)
|
||||
if (error) {
|
||||
throw new MedusaError(MedusaError.Types.INVALID_DATA, error.details)
|
||||
}
|
||||
|
||||
const noteService = req.scope.resolve("noteService")
|
||||
const noteService: NoteService = req.scope.resolve("noteService")
|
||||
|
||||
const result = await noteService.create({
|
||||
resource_id: value.resource_id,
|
||||
resource_type: value.resource_type,
|
||||
value: value.value,
|
||||
resource_id: validated.resource_id,
|
||||
resource_type: validated.resource_type,
|
||||
value: validated.value,
|
||||
author_id: userId,
|
||||
})
|
||||
|
||||
res.status(200).json({ note: result })
|
||||
}
|
||||
|
||||
export class AdminPostNotesReq {
|
||||
@IsString()
|
||||
@IsNotEmpty()
|
||||
resource_id: string
|
||||
|
||||
@IsString()
|
||||
@IsNotEmpty()
|
||||
resource_type: string
|
||||
|
||||
@IsString()
|
||||
@IsNotEmpty()
|
||||
value: string
|
||||
}
|
||||
+5
-2
@@ -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,8 +27,8 @@
|
||||
export default async (req, res) => {
|
||||
const { id } = req.params
|
||||
|
||||
const noteService = req.scope.resolve("noteService")
|
||||
const noteService: NoteService = req.scope.resolve("noteService")
|
||||
await noteService.delete(id)
|
||||
|
||||
res.status(200).json({ id, deleted: true })
|
||||
res.status(200).json({ id, object: "note", deleted: true })
|
||||
}
|
||||
+5
-2
@@ -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,7 +23,7 @@
|
||||
export default async (req, res) => {
|
||||
const { id } = req.params
|
||||
|
||||
const noteService = req.scope.resolve("noteService")
|
||||
const noteService: NoteService = req.scope.resolve("noteService")
|
||||
const note = await noteService.retrieve(id, { relations: ["author"] })
|
||||
|
||||
res.status(200).json({ note })
|
||||
+18
@@ -1,5 +1,8 @@
|
||||
import { Router } from "express"
|
||||
import { Note } from "../../../.."
|
||||
import { DeleteResponse, PaginatedResponse } from "../../../../types/common"
|
||||
import middlewares from "../../../middlewares"
|
||||
import "reflect-metadata"
|
||||
|
||||
const route = Router()
|
||||
|
||||
@@ -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,38 +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) => {
|
||||
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 })
|
||||
}
|
||||
@@ -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
-11
@@ -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,17 +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)
|
||||
|
||||
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 })
|
||||
export class AdminPostNotesNoteReq {
|
||||
@IsString()
|
||||
value: string
|
||||
}
|
||||
+14
-4
@@ -1,4 +1,6 @@
|
||||
import { Router } from "express"
|
||||
import { Notification } from "./../../../../"
|
||||
import { PaginatedResponse } from "./../../../../types/common"
|
||||
import middlewares from "../../../middlewares"
|
||||
|
||||
const route = Router()
|
||||
@@ -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,79 +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) => {
|
||||
const notificationService = req.scope.resolve("notificationService")
|
||||
|
||||
const limit = parseInt(req.query.limit) || 50
|
||||
const offset = parseInt(req.query.offset) || 0
|
||||
|
||||
const 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 })
|
||||
}
|
||||
@@ -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,51 +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)
|
||||
}
|
||||
|
||||
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 })
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
+40
-18
@@ -1,11 +1,20 @@
|
||||
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
|
||||
@@ -26,27 +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 { value, error } = schema.validate(req.body)
|
||||
if (error) {
|
||||
throw new MedusaError(MedusaError.Types.INVALID_DATA, error.details)
|
||||
}
|
||||
const orderService: OrderService = req.scope.resolve("orderService")
|
||||
|
||||
const orderService = req.scope.resolve("orderService")
|
||||
|
||||
await orderService.addShippingMethod(id, value.option_id, value.data, {
|
||||
price: value.price,
|
||||
})
|
||||
await orderService.addShippingMethod(
|
||||
id,
|
||||
validated.option_id,
|
||||
validated.data,
|
||||
{
|
||||
price: validated.price,
|
||||
}
|
||||
)
|
||||
|
||||
const order = await orderService.retrieve(id, {
|
||||
select: defaultFields,
|
||||
relations: defaultRelations,
|
||||
select: defaultAdminOrdersFields,
|
||||
relations: defaultAdminOrdersRelations,
|
||||
})
|
||||
|
||||
res.status(200).json({ order })
|
||||
}
|
||||
|
||||
export class AdminPostOrdersOrderShippingMethodsReq {
|
||||
@IsInt()
|
||||
@IsNotEmpty()
|
||||
price: number
|
||||
|
||||
@IsString()
|
||||
@IsNotEmpty()
|
||||
option_id: string
|
||||
|
||||
@IsObject()
|
||||
@IsOptional()
|
||||
data?: object = {}
|
||||
}
|
||||
@@ -1,13 +0,0 @@
|
||||
export default async (req, res) => {
|
||||
const { id } = req.params
|
||||
|
||||
const orderService = req.scope.resolve("orderService")
|
||||
|
||||
await orderService.archive(id)
|
||||
|
||||
const order = await orderService.retrieve(id, {
|
||||
relations: ["region", "customer", "swaps"],
|
||||
})
|
||||
|
||||
res.json({ order })
|
||||
}
|
||||
@@ -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 })
|
||||
}
|
||||
+7
-5
@@ -1,11 +1,13 @@
|
||||
import { MedusaError } from "medusa-core-utils"
|
||||
import { defaultRelations, defaultFields } from "."
|
||||
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.
|
||||
@@ -24,8 +26,8 @@ import { defaultRelations, defaultFields } from "."
|
||||
export default async (req, res) => {
|
||||
const { id, claim_id } = req.params
|
||||
|
||||
const claimService = req.scope.resolve("claimService")
|
||||
const orderService = req.scope.resolve("orderService")
|
||||
const claimService: ClaimService = req.scope.resolve("claimService")
|
||||
const orderService: OrderService = req.scope.resolve("orderService")
|
||||
|
||||
const claim = await claimService.retrieve(claim_id)
|
||||
|
||||
@@ -39,8 +41,8 @@ export default async (req, res) => {
|
||||
await claimService.cancel(claim_id)
|
||||
|
||||
const order = await orderService.retrieve(id, {
|
||||
select: defaultFields,
|
||||
relations: defaultRelations,
|
||||
select: defaultAdminOrdersFields,
|
||||
relations: defaultAdminOrdersRelations,
|
||||
})
|
||||
|
||||
res.json({ order })
|
||||
+13
-6
@@ -1,11 +1,17 @@
|
||||
import { MedusaError } from "medusa-core-utils"
|
||||
import { defaultRelations, defaultFields } from "."
|
||||
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.
|
||||
@@ -25,9 +31,10 @@ import { defaultRelations, defaultFields } from "."
|
||||
export default async (req, res) => {
|
||||
const { id, claim_id, fulfillment_id } = req.params
|
||||
|
||||
const fulfillmentService = req.scope.resolve("fulfillmentService")
|
||||
const claimService = req.scope.resolve("claimService")
|
||||
const orderService = req.scope.resolve("orderService")
|
||||
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)
|
||||
|
||||
@@ -50,8 +57,8 @@ export default async (req, res) => {
|
||||
await claimService.cancelFulfillment(fulfillment_id)
|
||||
|
||||
const order = await orderService.retrieve(id, {
|
||||
select: defaultFields,
|
||||
relations: defaultRelations,
|
||||
select: defaultAdminOrdersFields,
|
||||
relations: defaultAdminOrdersRelations,
|
||||
})
|
||||
res.json({ order })
|
||||
}
|
||||
+13
-6
@@ -1,11 +1,17 @@
|
||||
import { MedusaError } from "medusa-core-utils"
|
||||
import { defaultRelations, defaultFields } from "."
|
||||
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.
|
||||
@@ -25,9 +31,10 @@ import { defaultRelations, defaultFields } from "."
|
||||
export default async (req, res) => {
|
||||
const { id, swap_id, fulfillment_id } = req.params
|
||||
|
||||
const fulfillmentService = req.scope.resolve("fulfillmentService")
|
||||
const swapService = req.scope.resolve("swapService")
|
||||
const orderService = req.scope.resolve("orderService")
|
||||
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)
|
||||
|
||||
@@ -50,8 +57,8 @@ export default async (req, res) => {
|
||||
await swapService.cancelFulfillment(fulfillment_id)
|
||||
|
||||
const order = await orderService.retrieve(id, {
|
||||
select: defaultFields,
|
||||
relations: defaultRelations,
|
||||
select: defaultAdminOrdersFields,
|
||||
relations: defaultAdminOrdersRelations,
|
||||
})
|
||||
|
||||
res.json({ order })
|
||||
+8
-5
@@ -1,11 +1,13 @@
|
||||
import { MedusaError } from "medusa-core-utils"
|
||||
import { defaultRelations, defaultFields } from "."
|
||||
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
|
||||
@@ -24,8 +26,9 @@ import { defaultRelations, defaultFields } from "."
|
||||
export default async (req, res) => {
|
||||
const { id, fulfillment_id } = req.params
|
||||
|
||||
const fulfillmentService = req.scope.resolve("fulfillmentService")
|
||||
const orderService = req.scope.resolve("orderService")
|
||||
const orderService: OrderService = req.scope.resolve("orderService")
|
||||
const fulfillmentService: FulfillmentService =
|
||||
req.scope.resolve("fulfillmentService")
|
||||
|
||||
const fulfillment = await fulfillmentService.retrieve(fulfillment_id)
|
||||
|
||||
@@ -39,8 +42,8 @@ export default async (req, res) => {
|
||||
await orderService.cancelFulfillment(fulfillment_id)
|
||||
|
||||
const order = await orderService.retrieve(id, {
|
||||
select: defaultFields,
|
||||
relations: defaultRelations,
|
||||
select: defaultAdminOrdersFields,
|
||||
relations: defaultAdminOrdersRelations,
|
||||
})
|
||||
|
||||
res.json({ order })
|
||||
+6
-4
@@ -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,12 +24,12 @@ import { defaultFields, defaultRelations } from "."
|
||||
export default async (req, res) => {
|
||||
const { id } = req.params
|
||||
|
||||
const orderService = req.scope.resolve("orderService")
|
||||
const orderService: OrderService = req.scope.resolve("orderService")
|
||||
await orderService.cancel(id)
|
||||
|
||||
const order = await orderService.retrieve(id, {
|
||||
select: defaultFields,
|
||||
relations: defaultRelations,
|
||||
select: defaultAdminOrdersFields,
|
||||
relations: defaultAdminOrdersRelations,
|
||||
})
|
||||
|
||||
res.json({ order })
|
||||
+7
-5
@@ -1,11 +1,13 @@
|
||||
import { MedusaError } from "medusa-core-utils"
|
||||
import { defaultRelations, defaultFields } from "."
|
||||
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.
|
||||
@@ -24,8 +26,8 @@ import { defaultRelations, defaultFields } from "."
|
||||
export default async (req, res) => {
|
||||
const { id, swap_id } = req.params
|
||||
|
||||
const swapService = req.scope.resolve("swapService")
|
||||
const orderService = req.scope.resolve("orderService")
|
||||
const swapService: SwapService = req.scope.resolve("swapService")
|
||||
const orderService: OrderService = req.scope.resolve("orderService")
|
||||
|
||||
const swap = await swapService.retrieve(swap_id)
|
||||
|
||||
@@ -39,8 +41,8 @@ export default async (req, res) => {
|
||||
await swapService.cancel(swap_id)
|
||||
|
||||
const order = await orderService.retrieve(id, {
|
||||
select: defaultFields,
|
||||
relations: defaultRelations,
|
||||
select: defaultAdminOrdersFields,
|
||||
relations: defaultAdminOrdersRelations,
|
||||
})
|
||||
|
||||
res.json({ order })
|
||||
+6
-4
@@ -1,10 +1,12 @@
|
||||
import { defaultRelations, defaultFields } from "./"
|
||||
import { defaultAdminOrdersRelations, defaultAdminOrdersFields } from "."
|
||||
import { OrderService } from "../../../../services"
|
||||
|
||||
/**
|
||||
* @oas [post] /orders/{id}/capture
|
||||
* operationId: "PostOrdersOrderCapture"
|
||||
* summary: "Capture an Order"
|
||||
* description: "Captures all the Payments associated with an Order."
|
||||
* x-authenticated: true
|
||||
* parameters:
|
||||
* - (path) id=* {string} The id of the Order.
|
||||
* tags:
|
||||
@@ -22,13 +24,13 @@ import { defaultRelations, defaultFields } from "./"
|
||||
export default async (req, res) => {
|
||||
const { id } = req.params
|
||||
|
||||
const orderService = req.scope.resolve("orderService")
|
||||
const orderService: OrderService = req.scope.resolve("orderService")
|
||||
|
||||
await orderService.capturePayment(id)
|
||||
|
||||
const order = await orderService.retrieve(id, {
|
||||
select: defaultFields,
|
||||
relations: defaultRelations,
|
||||
select: defaultAdminOrdersFields,
|
||||
relations: defaultAdminOrdersRelations,
|
||||
})
|
||||
|
||||
res.json({ order })
|
||||
@@ -1,13 +0,0 @@
|
||||
export default async (req, res) => {
|
||||
const { id } = req.params
|
||||
|
||||
const orderService = req.scope.resolve("orderService")
|
||||
|
||||
await orderService.completeOrder(id)
|
||||
|
||||
const order = await orderService.retrieve(id, {
|
||||
relations: ["region", "customer", "swaps"],
|
||||
})
|
||||
|
||||
res.json({ order })
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
import { OrderService } from "../../../../services"
|
||||
|
||||
/**
|
||||
* @oas [post] /orders/{id}/complete
|
||||
* operationId: "PostOrdersOrderComplete"
|
||||
* summary: "Complete an Order"
|
||||
* description: "Completes an Order"
|
||||
* 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.completeOrder(id)
|
||||
|
||||
const order = await orderService.retrieve(id, {
|
||||
relations: ["region", "customer", "swaps"],
|
||||
})
|
||||
|
||||
res.json({ order })
|
||||
}
|
||||
+28
-17
@@ -1,11 +1,14 @@
|
||||
import { MedusaError, Validator } from "medusa-core-utils"
|
||||
import { defaultFields, defaultRelations } from "./"
|
||||
import { IsArray, IsNotEmpty, IsOptional, IsString } from "class-validator"
|
||||
import { defaultAdminOrdersFields, defaultAdminOrdersRelations } from "."
|
||||
import { ClaimService, OrderService } from "../../../../services"
|
||||
import { validator } from "../../../../utils/validator"
|
||||
|
||||
/**
|
||||
* @oas [post] /orders/{id}/claims/{claim_id}/shipments
|
||||
* operationId: "PostOrdersOrderClaimsClaimShipments"
|
||||
* summary: "Create Claim Shipment"
|
||||
* description: "Registers a Claim Fulfillment as shipped."
|
||||
* x-authenticated: true
|
||||
* parameters:
|
||||
* - (path) id=* {string} The id of the Order.
|
||||
* - (path) claim_id=* {string} The id of the Claim.
|
||||
@@ -13,6 +16,8 @@ import { defaultFields, defaultRelations } from "./"
|
||||
* content:
|
||||
* application/json:
|
||||
* schema:
|
||||
* required:
|
||||
* - fulfillment_id
|
||||
* properties:
|
||||
* fulfillment_id:
|
||||
* description: The id of the Fulfillment.
|
||||
@@ -37,29 +42,35 @@ import { defaultFields, defaultRelations } from "./"
|
||||
export default async (req, res) => {
|
||||
const { id, claim_id } = req.params
|
||||
|
||||
const schema = Validator.object().keys({
|
||||
fulfillment_id: Validator.string().required(),
|
||||
tracking_numbers: Validator.array().items(Validator.string()).optional(),
|
||||
})
|
||||
const validated = await validator(
|
||||
AdminPostOrdersOrderClaimsClaimShipmentsReq,
|
||||
req.body
|
||||
)
|
||||
|
||||
const { value, error } = schema.validate(req.body)
|
||||
if (error) {
|
||||
throw new MedusaError(MedusaError.Types.INVALID_DATA, error.details)
|
||||
}
|
||||
|
||||
const orderService = req.scope.resolve("orderService")
|
||||
const claimService = req.scope.resolve("claimService")
|
||||
const orderService: OrderService = req.scope.resolve("orderService")
|
||||
const claimService: ClaimService = req.scope.resolve("claimService")
|
||||
|
||||
await claimService.createShipment(
|
||||
claim_id,
|
||||
value.fulfillment_id,
|
||||
value.tracking_numbers.map((n) => ({ tracking_number: n }))
|
||||
validated.fulfillment_id,
|
||||
validated.tracking_numbers?.map((n) => ({ tracking_number: n }))
|
||||
)
|
||||
|
||||
const order = await orderService.retrieve(id, {
|
||||
select: defaultFields,
|
||||
relations: defaultRelations,
|
||||
select: defaultAdminOrdersFields,
|
||||
relations: defaultAdminOrdersRelations,
|
||||
})
|
||||
|
||||
res.json({ order })
|
||||
}
|
||||
|
||||
export class AdminPostOrdersOrderClaimsClaimShipmentsReq {
|
||||
@IsString()
|
||||
@IsNotEmpty()
|
||||
fulfillment_id: string
|
||||
|
||||
@IsArray()
|
||||
@IsOptional()
|
||||
@IsString({ each: true })
|
||||
tracking_numbers?: string[]
|
||||
}
|
||||
+190
-93
@@ -1,17 +1,35 @@
|
||||
import { MedusaError, Validator } from "medusa-core-utils"
|
||||
import { defaultRelations, defaultFields } from "./"
|
||||
import { Type } from "class-transformer"
|
||||
import {
|
||||
IsArray,
|
||||
IsOptional,
|
||||
ValidateNested,
|
||||
IsBoolean,
|
||||
IsObject,
|
||||
IsString,
|
||||
IsInt,
|
||||
IsNotEmpty,
|
||||
IsEnum,
|
||||
} from "class-validator"
|
||||
import { MedusaError } from "medusa-core-utils"
|
||||
import { defaultAdminOrdersRelations, defaultAdminOrdersFields } from "."
|
||||
import { AddressPayload } from "../../../../types/common"
|
||||
import { validator } from "../../../../utils/validator"
|
||||
|
||||
/**
|
||||
* @oas [post] /order/{id}/claims
|
||||
* operationId: "PostOrdersOrderClaims"
|
||||
* summary: "Create a Claim"
|
||||
* description: "Creates a Claim."
|
||||
* x-authenticated: true
|
||||
* parameters:
|
||||
* - (path) id=* {string} The id of the Order.
|
||||
* requestBody:
|
||||
* content:
|
||||
* application/json:
|
||||
* schema:
|
||||
* required:
|
||||
* - type
|
||||
* - claim_items
|
||||
* properties:
|
||||
* type:
|
||||
* description: "The type of the Claim. This will determine how the Claim is treated: `replace` Claims will result in a Fulfillment with new items being created, while a `refund` Claim will refund the amount paid for the claimed items."
|
||||
@@ -50,53 +68,53 @@ import { defaultRelations, defaultFields } from "./"
|
||||
* description: A list of image URL's that will be associated with the Claim
|
||||
* items:
|
||||
* type: string
|
||||
* return_shipping:
|
||||
* description: Optional details for the Return Shipping Method, if the items are to be sent back.
|
||||
* type: object
|
||||
* properties:
|
||||
* option_id:
|
||||
* type: string
|
||||
* description: The id of the Shipping Option to create the Shipping Method from.
|
||||
* price:
|
||||
* type: integer
|
||||
* description: The price to charge for the Shipping Method.
|
||||
* additional_items:
|
||||
* description: The new items to send to the Customer when the Claim type is Replace.
|
||||
* type: array
|
||||
* items:
|
||||
* properties:
|
||||
* variant_id:
|
||||
* description: The id of the Product Variant to ship.
|
||||
* type: string
|
||||
* quantity:
|
||||
* description: The quantity of the Product Variant to ship.
|
||||
* type: integer
|
||||
* shipping_methods:
|
||||
* description: The Shipping Methods to send the additional Line Items with.
|
||||
* type: array
|
||||
* items:
|
||||
* return_shipping:
|
||||
* description: Optional details for the Return Shipping Method, if the items are to be sent back.
|
||||
* type: object
|
||||
* properties:
|
||||
* option_id:
|
||||
* type: string
|
||||
* description: The id of the Shipping Option to create the Shipping Method from.
|
||||
* price:
|
||||
* type: integer
|
||||
* description: The price to charge for the Shipping Method.
|
||||
* additional_items:
|
||||
* description: The new items to send to the Customer when the Claim type is Replace.
|
||||
* type: array
|
||||
* items:
|
||||
* properties:
|
||||
* id:
|
||||
* description: The id of an existing Shipping Method
|
||||
* variant_id:
|
||||
* description: The id of the Product Variant to ship.
|
||||
* type: string
|
||||
* option_id:
|
||||
* description: The id of the Shipping Option to create a Shipping Method from
|
||||
* type: string
|
||||
* price:
|
||||
* description: The price to charge for the Shipping Method
|
||||
* quantity:
|
||||
* description: The quantity of the Product Variant to ship.
|
||||
* type: integer
|
||||
* shipping_address:
|
||||
* type: object
|
||||
* description: "An optional shipping address to send the claim to. Defaults to the parent order's shipping address"
|
||||
* refund_amount:
|
||||
* description: The amount to refund the Customer when the Claim type is `refund`.
|
||||
* type: integer
|
||||
* no_notification:
|
||||
* description: If set to true no notification will be send related to this Claim.
|
||||
* type: boolean
|
||||
* metadata:
|
||||
* description: An optional set of key-value pairs to hold additional information.
|
||||
* type: object
|
||||
* shipping_methods:
|
||||
* description: The Shipping Methods to send the additional Line Items with.
|
||||
* type: array
|
||||
* items:
|
||||
* properties:
|
||||
* id:
|
||||
* description: The id of an existing Shipping Method
|
||||
* type: string
|
||||
* option_id:
|
||||
* description: The id of the Shipping Option to create a Shipping Method from
|
||||
* type: string
|
||||
* price:
|
||||
* description: The price to charge for the Shipping Method
|
||||
* type: integer
|
||||
* shipping_address:
|
||||
* type: object
|
||||
* description: "An optional shipping address to send the claim to. Defaults to the parent order's shipping address"
|
||||
* refund_amount:
|
||||
* description: The amount to refund the Customer when the Claim type is `refund`.
|
||||
* type: integer
|
||||
* no_notification:
|
||||
* description: If set to true no notification will be send related to this Claim.
|
||||
* type: boolean
|
||||
* metadata:
|
||||
* description: An optional set of key-value pairs to hold additional information.
|
||||
* type: object
|
||||
* tags:
|
||||
* - Order
|
||||
* responses:
|
||||
@@ -109,54 +127,12 @@ import { defaultRelations, defaultFields } from "./"
|
||||
* order:
|
||||
* $ref: "#/components/schemas/order"
|
||||
*/
|
||||
|
||||
export default async (req, res) => {
|
||||
const { id } = req.params
|
||||
const schema = Validator.object().keys({
|
||||
type: Validator.string().valid("replace", "refund").required(),
|
||||
claim_items: Validator.array()
|
||||
.items({
|
||||
item_id: Validator.string().required(),
|
||||
quantity: Validator.number().required(),
|
||||
note: Validator.string().optional(),
|
||||
reason: Validator.string().valid(
|
||||
"missing_item",
|
||||
"wrong_item",
|
||||
"production_failure",
|
||||
"other"
|
||||
),
|
||||
tags: Validator.array().items(Validator.string()),
|
||||
images: Validator.array().items(Validator.string()),
|
||||
})
|
||||
.required(),
|
||||
return_shipping: Validator.object()
|
||||
.keys({
|
||||
option_id: Validator.string().optional(),
|
||||
price: Validator.number().integer().optional(),
|
||||
})
|
||||
.optional(),
|
||||
additional_items: Validator.array()
|
||||
.items({
|
||||
variant_id: Validator.string().required(),
|
||||
quantity: Validator.number().required(),
|
||||
})
|
||||
.optional(),
|
||||
shipping_methods: Validator.array()
|
||||
.items({
|
||||
id: Validator.string().optional(),
|
||||
option_id: Validator.string().optional(),
|
||||
price: Validator.number().integer().optional(),
|
||||
})
|
||||
.optional(),
|
||||
refund_amount: Validator.number().integer().optional(),
|
||||
shipping_address: Validator.object().optional(),
|
||||
no_notification: Validator.boolean().optional(),
|
||||
metadata: Validator.object().optional(),
|
||||
})
|
||||
|
||||
const { value, error } = schema.validate(req.body)
|
||||
if (error) {
|
||||
throw new MedusaError(MedusaError.Types.INVALID_DATA, error.details)
|
||||
}
|
||||
const value = await validator(AdminPostOrdersOrderClaimsReq, req.body)
|
||||
|
||||
const idempotencyKeyService = req.scope.resolve("idempotencyKeyService")
|
||||
|
||||
const headerKey = req.get("Idempotency-Key") || ""
|
||||
@@ -302,8 +278,8 @@ export default async (req, res) => {
|
||||
}
|
||||
|
||||
order = await orderService.withTransaction(manager).retrieve(id, {
|
||||
select: defaultFields,
|
||||
relations: defaultRelations,
|
||||
select: defaultAdminOrdersFields,
|
||||
relations: defaultAdminOrdersRelations,
|
||||
})
|
||||
|
||||
return {
|
||||
@@ -346,3 +322,124 @@ export default async (req, res) => {
|
||||
|
||||
res.status(idempotencyKey.response_code).json(idempotencyKey.response_body)
|
||||
}
|
||||
|
||||
enum ClaimTypes {
|
||||
replace = "replace",
|
||||
refund = "refund",
|
||||
}
|
||||
|
||||
enum ClaimItemReason {
|
||||
missing_item = "missing_item",
|
||||
wrong_item = "wrong_item",
|
||||
production_failure = "production_failure",
|
||||
other = "other",
|
||||
}
|
||||
|
||||
export class AdminPostOrdersOrderClaimsReq {
|
||||
@IsEnum(ClaimTypes)
|
||||
@IsNotEmpty()
|
||||
type: ClaimTypes
|
||||
|
||||
@IsArray()
|
||||
@IsNotEmpty()
|
||||
@Type(() => Item)
|
||||
@ValidateNested({ each: true })
|
||||
claim_items: Item[]
|
||||
|
||||
@IsOptional()
|
||||
@ValidateNested({ each: true })
|
||||
@Type(() => ReturnShipping)
|
||||
return_shipping?: ReturnShipping
|
||||
|
||||
@IsArray()
|
||||
@IsOptional()
|
||||
@ValidateNested({ each: true })
|
||||
@Type(() => AdditionalItem)
|
||||
additional_items?: AdditionalItem[]
|
||||
|
||||
@IsArray()
|
||||
@IsOptional()
|
||||
@ValidateNested({ each: true })
|
||||
@Type(() => ShippingMethod)
|
||||
shipping_methods?: ShippingMethod[]
|
||||
|
||||
@IsInt()
|
||||
@IsOptional()
|
||||
refund_amount?: number
|
||||
|
||||
@IsObject()
|
||||
@IsOptional()
|
||||
@ValidateNested()
|
||||
@Type(() => AddressPayload)
|
||||
shipping_address?: AddressPayload
|
||||
|
||||
@IsBoolean()
|
||||
@IsOptional()
|
||||
no_notification?: boolean
|
||||
|
||||
@IsObject()
|
||||
@IsOptional()
|
||||
metadata?: object
|
||||
}
|
||||
|
||||
class ReturnShipping {
|
||||
@IsString()
|
||||
@IsOptional()
|
||||
option_id?: string
|
||||
|
||||
@IsInt()
|
||||
@IsOptional()
|
||||
price?: number
|
||||
}
|
||||
|
||||
class ShippingMethod {
|
||||
@IsString()
|
||||
@IsOptional()
|
||||
id?: string
|
||||
|
||||
@IsString()
|
||||
@IsOptional()
|
||||
option_id?: string
|
||||
|
||||
@IsInt()
|
||||
@IsOptional()
|
||||
price?: number
|
||||
}
|
||||
|
||||
class Item {
|
||||
@IsString()
|
||||
@IsNotEmpty()
|
||||
item_id: string
|
||||
|
||||
@IsInt()
|
||||
@IsNotEmpty()
|
||||
quantity: number
|
||||
|
||||
@IsString()
|
||||
@IsOptional()
|
||||
note?: string
|
||||
|
||||
@IsEnum(ClaimItemReason)
|
||||
@IsOptional()
|
||||
reason?: ClaimItemReason
|
||||
|
||||
@IsArray()
|
||||
@IsOptional()
|
||||
@IsString({ each: true })
|
||||
tags?: string[]
|
||||
|
||||
@IsArray()
|
||||
@IsOptional()
|
||||
@IsString({ each: true })
|
||||
images?: string[]
|
||||
}
|
||||
|
||||
class AdditionalItem {
|
||||
@IsString()
|
||||
@IsNotEmpty()
|
||||
variant_id: string
|
||||
|
||||
@IsInt()
|
||||
@IsNotEmpty()
|
||||
quantity: number
|
||||
}
|
||||
+53
-24
@@ -1,17 +1,31 @@
|
||||
import { MedusaError, Validator } from "medusa-core-utils"
|
||||
import { defaultRelations, defaultFields } from "./"
|
||||
|
||||
import { Transform, Type } from "class-transformer"
|
||||
import {
|
||||
IsArray,
|
||||
IsBoolean,
|
||||
IsInt,
|
||||
IsNotEmpty,
|
||||
IsObject,
|
||||
IsOptional,
|
||||
IsString,
|
||||
ValidateNested,
|
||||
} from "class-validator"
|
||||
import { defaultAdminOrdersRelations, defaultAdminOrdersFields } from "."
|
||||
import { OrderService } from "../../../../services"
|
||||
import { validator } from "../../../../utils/validator"
|
||||
/**
|
||||
* @oas [post] /orders/{id}/fulfillments
|
||||
* operationId: "PostOrdersOrderFulfillments"
|
||||
* summary: "Create a Fulfillment"
|
||||
* description: "Creates a Fulfillment of an Order - will notify Fulfillment Providers to prepare a shipment."
|
||||
* x-authenticated: true
|
||||
* parameters:
|
||||
* - (path) id=* {string} The id of the Order.
|
||||
* requestBody:
|
||||
* content:
|
||||
* application/json:
|
||||
* schema:
|
||||
* required:
|
||||
* - items
|
||||
* properties:
|
||||
* items:
|
||||
* description: The Line Items to include in the Fulfillment.
|
||||
@@ -45,33 +59,48 @@ import { defaultRelations, defaultFields } from "./"
|
||||
export default async (req, res) => {
|
||||
const { id } = req.params
|
||||
|
||||
const schema = Validator.object().keys({
|
||||
items: Validator.array()
|
||||
.items({
|
||||
item_id: Validator.string().required(),
|
||||
quantity: Validator.number().required(),
|
||||
})
|
||||
.required(),
|
||||
no_notification: Validator.boolean().optional(),
|
||||
metadata: Validator.object().optional(),
|
||||
})
|
||||
const validated = await validator(
|
||||
AdminPostOrdersOrderFulfillmentsReq,
|
||||
req.body
|
||||
)
|
||||
|
||||
const { value, error } = schema.validate(req.body)
|
||||
if (error) {
|
||||
throw new MedusaError(MedusaError.Types.INVALID_DATA, error.details)
|
||||
}
|
||||
const orderService: OrderService = req.scope.resolve("orderService")
|
||||
|
||||
const orderService = req.scope.resolve("orderService")
|
||||
|
||||
await orderService.createFulfillment(id, value.items, {
|
||||
metadata: value.metadata,
|
||||
no_notification: value.no_notification,
|
||||
await orderService.createFulfillment(id, validated.items, {
|
||||
metadata: validated.metadata,
|
||||
no_notification: validated.no_notification,
|
||||
})
|
||||
|
||||
const order = await orderService.retrieve(id, {
|
||||
select: defaultFields,
|
||||
relations: defaultRelations,
|
||||
select: defaultAdminOrdersFields,
|
||||
relations: defaultAdminOrdersRelations,
|
||||
})
|
||||
|
||||
res.json({ order })
|
||||
}
|
||||
|
||||
export class AdminPostOrdersOrderFulfillmentsReq {
|
||||
@IsArray()
|
||||
@ValidateNested({ each: true })
|
||||
@Type(() => Item)
|
||||
items: Item[]
|
||||
|
||||
@IsBoolean()
|
||||
@IsOptional()
|
||||
@Transform(({ value }) => value === "true")
|
||||
no_notification?: boolean
|
||||
|
||||
@IsObject()
|
||||
@IsOptional()
|
||||
metadata?: object
|
||||
}
|
||||
|
||||
class Item {
|
||||
@IsString()
|
||||
@IsNotEmpty()
|
||||
item_id: string
|
||||
|
||||
@IsInt()
|
||||
@IsNotEmpty()
|
||||
quantity: number
|
||||
}
|
||||
@@ -1,42 +0,0 @@
|
||||
import { MedusaError, Validator } from "medusa-core-utils"
|
||||
|
||||
export default async (req, res) => {
|
||||
const schema = Validator.object().keys({
|
||||
status: Validator.string().optional(),
|
||||
email: Validator.string().email().required(),
|
||||
billing_address: Validator.address().required(),
|
||||
shipping_address: Validator.address().required(),
|
||||
items: Validator.array().required(),
|
||||
region: Validator.string().required(),
|
||||
discounts: Validator.array().optional(),
|
||||
customer_id: Validator.string().required(),
|
||||
payment_method: Validator.object()
|
||||
.keys({
|
||||
provider_id: Validator.string().required(),
|
||||
data: Validator.object().optional(),
|
||||
})
|
||||
.required(),
|
||||
shipping_method: Validator.array()
|
||||
.items({
|
||||
provider_id: Validator.string().required(),
|
||||
profile_id: Validator.string().required(),
|
||||
price: Validator.number().required(),
|
||||
data: Validator.object().optional(),
|
||||
items: Validator.array().optional(),
|
||||
})
|
||||
.required(),
|
||||
no_notification: Validator.boolean().optional(),
|
||||
metadata: Validator.object().optional(),
|
||||
})
|
||||
|
||||
const { value, error } = schema.validate(req.body)
|
||||
if (error) {
|
||||
throw new MedusaError(MedusaError.Types.INVALID_DATA, error.details)
|
||||
}
|
||||
|
||||
const orderService = req.scope.resolve("orderService")
|
||||
let order = await orderService.create(value)
|
||||
order = await orderService.decorate(order, [], ["region"])
|
||||
|
||||
res.status(200).json({ order })
|
||||
}
|
||||
@@ -0,0 +1,203 @@
|
||||
import { Type, Transform } from "class-transformer"
|
||||
import {
|
||||
IsEmail,
|
||||
IsOptional,
|
||||
ValidateNested,
|
||||
IsArray,
|
||||
IsString,
|
||||
IsBoolean,
|
||||
IsObject,
|
||||
IsInt,
|
||||
IsNotEmpty,
|
||||
} from "class-validator"
|
||||
import { OrderService } from "../../../../services"
|
||||
import { AddressPayload } from "../../../../types/common"
|
||||
import { validator } from "../../../../utils/validator"
|
||||
/**
|
||||
* @oas [post] /orders
|
||||
* operationId: "PostOrders"
|
||||
* summary: "Create an order"
|
||||
* description: "Creates and order"
|
||||
* x-authenticated: true
|
||||
* requestBody:
|
||||
* content:
|
||||
* application/json:
|
||||
* required:
|
||||
* - email
|
||||
* - billing_address
|
||||
* - shipping_address
|
||||
* - items
|
||||
* - region
|
||||
* - customer_id
|
||||
* - payment_method
|
||||
* - shipping_method
|
||||
* schema:
|
||||
* properties:
|
||||
* status:
|
||||
* description: status of the order
|
||||
* type: string
|
||||
* email:
|
||||
* description: the email for the order
|
||||
* type: string
|
||||
* billing_address:
|
||||
* description: Billing address
|
||||
* anyOf:
|
||||
* - $ref: "#/components/schemas/address
|
||||
* shipping_address:
|
||||
* description: Shipping address
|
||||
* anyOf:
|
||||
* - $ref: "#/components/schemas/address
|
||||
* items:
|
||||
* description: The Line Items for the order
|
||||
* type: array
|
||||
* region:
|
||||
* description: Region where the order belongs
|
||||
* type: string
|
||||
* discounts:
|
||||
* description: Discounts applied to the order
|
||||
* type: array
|
||||
* customer_id:
|
||||
* description: id of the customer
|
||||
* type: string
|
||||
* payment_method:
|
||||
* description: payment method chosen for the order
|
||||
* type: object
|
||||
* required:
|
||||
* - provider_id
|
||||
* properties:
|
||||
* provider_id:
|
||||
* type: string
|
||||
* description: id of the payment provider
|
||||
* data:
|
||||
* description: Data relevant for the given payment method
|
||||
* type: object
|
||||
* shipping_method:
|
||||
* description: The Shipping Method used for shipping the order.
|
||||
* type: object
|
||||
* required:
|
||||
* - provider_id
|
||||
* - profile_id
|
||||
* - price
|
||||
* properties:
|
||||
* provider_id:
|
||||
* type: string
|
||||
* description: The id of the shipping provider.
|
||||
* profile_id:
|
||||
* type: string
|
||||
* description: The id of the shipping profile.
|
||||
* price:
|
||||
* type: integer
|
||||
* description: The price of the shipping.
|
||||
* data:
|
||||
* type: object
|
||||
* description: Data relevant to the specific shipping method.
|
||||
* items:
|
||||
* type: array
|
||||
* description: Items to ship
|
||||
* no_notification:
|
||||
* description: A flag to indicate if no notifications should be emitted related to the updated order.
|
||||
* type: boolean
|
||||
* tags:
|
||||
* - Order
|
||||
* responses:
|
||||
* 200:
|
||||
* description: OK
|
||||
* content:
|
||||
* application/json:
|
||||
* schema:
|
||||
* properties:
|
||||
* order:
|
||||
* $ref: "#/components/schemas/order"
|
||||
*/
|
||||
|
||||
export default async (req, res) => {
|
||||
const validated = await validator(AdminPostOrdersReq, req.body)
|
||||
|
||||
const orderService: OrderService = req.scope.resolve("orderService")
|
||||
let order = await orderService.create(validated)
|
||||
order = await orderService.decorate(order, [], ["region"])
|
||||
|
||||
res.status(200).json({ order })
|
||||
}
|
||||
|
||||
export class AdminPostOrdersReq {
|
||||
@IsString()
|
||||
@IsOptional()
|
||||
status?: string
|
||||
|
||||
@IsEmail()
|
||||
@IsNotEmpty()
|
||||
email: string
|
||||
|
||||
@IsNotEmpty()
|
||||
@ValidateNested()
|
||||
@Type(() => AddressPayload)
|
||||
billing_address: AddressPayload
|
||||
|
||||
@IsNotEmpty()
|
||||
@ValidateNested()
|
||||
@Type(() => AddressPayload)
|
||||
shipping_address: AddressPayload
|
||||
|
||||
@IsArray()
|
||||
@IsNotEmpty()
|
||||
items: object[]
|
||||
|
||||
@IsString()
|
||||
@IsNotEmpty()
|
||||
region: string
|
||||
|
||||
@IsArray()
|
||||
@IsOptional()
|
||||
discounts?: object[]
|
||||
|
||||
@IsString()
|
||||
@IsNotEmpty()
|
||||
customer_id: string
|
||||
|
||||
@IsNotEmpty()
|
||||
@ValidateNested()
|
||||
@Type(() => PaymentMethod)
|
||||
payment_method: PaymentMethod
|
||||
|
||||
@IsNotEmpty()
|
||||
@ValidateNested({ each: true })
|
||||
@Type(() => ShippingMethod)
|
||||
shipping_method?: ShippingMethod[]
|
||||
|
||||
@IsBoolean()
|
||||
@IsOptional()
|
||||
no_notification?: boolean
|
||||
}
|
||||
|
||||
class PaymentMethod {
|
||||
@IsString()
|
||||
@IsNotEmpty()
|
||||
provider_id: string
|
||||
|
||||
@IsObject()
|
||||
@IsOptional()
|
||||
data?: object
|
||||
}
|
||||
|
||||
class ShippingMethod {
|
||||
@IsString()
|
||||
@IsNotEmpty()
|
||||
provider_id: string
|
||||
|
||||
@IsString()
|
||||
@IsNotEmpty()
|
||||
profile_id: string
|
||||
|
||||
@IsInt()
|
||||
@IsNotEmpty()
|
||||
price: number
|
||||
|
||||
@IsObject()
|
||||
@IsOptional()
|
||||
data?: object
|
||||
|
||||
@IsArray()
|
||||
@IsOptional()
|
||||
items?: object[]
|
||||
}
|
||||
+35
-19
@@ -1,17 +1,27 @@
|
||||
import { MedusaError, Validator } from "medusa-core-utils"
|
||||
import { defaultRelations, defaultFields } from "./"
|
||||
|
||||
import {
|
||||
IsArray,
|
||||
IsBoolean,
|
||||
IsNotEmpty,
|
||||
IsOptional,
|
||||
IsString,
|
||||
} from "class-validator"
|
||||
import { defaultAdminOrdersRelations, defaultAdminOrdersFields } from "."
|
||||
import { OrderService } from "../../../../services"
|
||||
import { validator } from "../../../../utils/validator"
|
||||
/**
|
||||
* @oas [post] /orders/{id}/shipment
|
||||
* operationId: "PostOrdersOrderShipment"
|
||||
* summary: "Create a Shipment"
|
||||
* description: "Registers a Fulfillment as shipped."
|
||||
* x-authenticated: true
|
||||
* parameters:
|
||||
* - (path) id=* {string} The id of the Order.
|
||||
* requestBody:
|
||||
* content:
|
||||
* application/json:
|
||||
* schema:
|
||||
* required:
|
||||
* - fulfillment_id
|
||||
* properties:
|
||||
* fulfillment_id:
|
||||
* description: The id of the Fulfillment.
|
||||
@@ -39,30 +49,36 @@ import { defaultRelations, defaultFields } from "./"
|
||||
export default async (req, res) => {
|
||||
const { id } = req.params
|
||||
|
||||
const schema = Validator.object().keys({
|
||||
fulfillment_id: Validator.string().required(),
|
||||
tracking_numbers: Validator.array().items(Validator.string()).optional(),
|
||||
no_notification: Validator.boolean().optional(),
|
||||
})
|
||||
const validated = await validator(AdminPostOrdersOrderShipmentReq, req.body)
|
||||
|
||||
const { value, error } = schema.validate(req.body)
|
||||
if (error) {
|
||||
throw new MedusaError(MedusaError.Types.INVALID_DATA, error.details)
|
||||
}
|
||||
|
||||
const orderService = req.scope.resolve("orderService")
|
||||
const orderService: OrderService = req.scope.resolve("orderService")
|
||||
|
||||
await orderService.createShipment(
|
||||
id,
|
||||
value.fulfillment_id,
|
||||
value.tracking_numbers.map((n) => ({ tracking_number: n })),
|
||||
{ no_notification: value.no_notification }
|
||||
validated.fulfillment_id,
|
||||
validated.tracking_numbers?.map((n) => ({ tracking_number: n })),
|
||||
{ no_notification: validated.no_notification }
|
||||
)
|
||||
|
||||
const order = await orderService.retrieve(id, {
|
||||
select: defaultFields,
|
||||
relations: defaultRelations,
|
||||
select: defaultAdminOrdersFields,
|
||||
relations: defaultAdminOrdersRelations,
|
||||
})
|
||||
|
||||
res.json({ order })
|
||||
}
|
||||
|
||||
export class AdminPostOrdersOrderShipmentReq {
|
||||
@IsString()
|
||||
@IsNotEmpty()
|
||||
fulfillment_id: string
|
||||
|
||||
@IsArray()
|
||||
@IsOptional()
|
||||
@IsString({ each: true })
|
||||
tracking_numbers?: string[]
|
||||
|
||||
@IsBoolean()
|
||||
@IsOptional()
|
||||
no_notification?: boolean
|
||||
}
|
||||
+39
-20
@@ -1,11 +1,19 @@
|
||||
import { MedusaError, Validator } from "medusa-core-utils"
|
||||
import { defaultFields, defaultRelations } from "./"
|
||||
|
||||
import {
|
||||
IsArray,
|
||||
IsBoolean,
|
||||
IsNotEmpty,
|
||||
IsOptional,
|
||||
IsString,
|
||||
} from "class-validator"
|
||||
import { defaultAdminOrdersFields, defaultAdminOrdersRelations } from "."
|
||||
import { OrderService, SwapService } from "../../../../services"
|
||||
import { validator } from "../../../../utils/validator"
|
||||
/**
|
||||
* @oas [post] /orders/{id}/swaps/{swap_id}/shipments
|
||||
* operationId: "PostOrdersOrderSwapsSwapShipments"
|
||||
* summary: "Create Swap Shipment"
|
||||
* description: "Registers a Swap Fulfillment as shipped."
|
||||
* x-authenticated: true
|
||||
* parameters:
|
||||
* - (path) id=* {string} The id of the Order.
|
||||
* - (path) swap_id=* {string} The id of the Swap.
|
||||
@@ -13,6 +21,8 @@ import { defaultFields, defaultRelations } from "./"
|
||||
* content:
|
||||
* application/json:
|
||||
* schema:
|
||||
* required:
|
||||
* - fulfillment_id
|
||||
* properties:
|
||||
* fulfillment_id:
|
||||
* description: The id of the Fulfillment.
|
||||
@@ -40,31 +50,40 @@ import { defaultFields, defaultRelations } from "./"
|
||||
export default async (req, res) => {
|
||||
const { id, swap_id } = req.params
|
||||
|
||||
const schema = Validator.object().keys({
|
||||
fulfillment_id: Validator.string().required(),
|
||||
tracking_numbers: Validator.array().items(Validator.string()).optional(),
|
||||
no_notification: Validator.boolean().optional(),
|
||||
})
|
||||
const validated = await validator(
|
||||
AdminPostOrdersOrderSwapsSwapShipmentsReq,
|
||||
req.body
|
||||
)
|
||||
|
||||
const { value, error } = schema.validate(req.body)
|
||||
if (error) {
|
||||
throw new MedusaError(MedusaError.Types.INVALID_DATA, error.details)
|
||||
}
|
||||
|
||||
const orderService = req.scope.resolve("orderService")
|
||||
const swapService = req.scope.resolve("swapService")
|
||||
const orderService: OrderService = req.scope.resolve("orderService")
|
||||
const swapService: SwapService = req.scope.resolve("swapService")
|
||||
|
||||
await swapService.createShipment(
|
||||
swap_id,
|
||||
value.fulfillment_id,
|
||||
value.tracking_numbers.map((n) => ({ tracking_number: n })),
|
||||
{ no_notification: value.no_notification }
|
||||
validated.fulfillment_id,
|
||||
validated.tracking_numbers?.map((n) => ({ tracking_number: n })),
|
||||
{ no_notification: validated.no_notification }
|
||||
)
|
||||
|
||||
const order = await orderService.retrieve(id, {
|
||||
select: defaultFields,
|
||||
relations: defaultRelations,
|
||||
select: defaultAdminOrdersFields,
|
||||
relations: defaultAdminOrdersRelations,
|
||||
})
|
||||
|
||||
res.json({ order })
|
||||
}
|
||||
|
||||
export class AdminPostOrdersOrderSwapsSwapShipmentsReq {
|
||||
@IsString()
|
||||
@IsNotEmpty()
|
||||
fulfillment_id: string
|
||||
|
||||
@IsArray()
|
||||
@IsOptional()
|
||||
@IsString({ each: true })
|
||||
tracking_numbers?: string[]
|
||||
|
||||
@IsBoolean()
|
||||
@IsOptional()
|
||||
no_notification?: boolean
|
||||
}
|
||||
+122
-45
@@ -1,22 +1,47 @@
|
||||
import { MedusaError, Validator } from "medusa-core-utils"
|
||||
import { defaultFields, defaultRelations } from "./"
|
||||
import { Type } from "class-transformer"
|
||||
import {
|
||||
IsOptional,
|
||||
IsArray,
|
||||
IsString,
|
||||
IsBoolean,
|
||||
IsObject,
|
||||
IsInt,
|
||||
IsNotEmpty,
|
||||
IsNumber,
|
||||
ValidateNested,
|
||||
} from "class-validator"
|
||||
import { MedusaError } from "medusa-core-utils"
|
||||
import { defaultAdminOrdersFields, defaultAdminOrdersRelations } from "."
|
||||
import {
|
||||
IdempotencyKeyService,
|
||||
OrderService,
|
||||
ReturnService,
|
||||
SwapService,
|
||||
} from "../../../../services"
|
||||
import { validator } from "../../../../utils/validator"
|
||||
|
||||
/**
|
||||
* @oas [post] /order/{id}/swaps
|
||||
* operationId: "PostOrdersOrderSwaps"
|
||||
* summary: "Create a Swap"
|
||||
* description: "Creates a Swap. Swaps are used to handle Return of previously purchased goods and Fulfillment of replacements simultaneously."
|
||||
* x-authenticated: true
|
||||
* parameters:
|
||||
* - (path) id=* {string} The id of the Order.
|
||||
* requestBody:
|
||||
* content:
|
||||
* application/json:
|
||||
* schema:
|
||||
* required:
|
||||
* - return_items
|
||||
* properties:
|
||||
* return_items:
|
||||
* description: The Line Items to return as part of the Swap.
|
||||
* type: array
|
||||
* items:
|
||||
* required:
|
||||
* - item_id
|
||||
* - quantity
|
||||
* properties:
|
||||
* item_id:
|
||||
* description: The id of the Line Item that will be claimed.
|
||||
@@ -38,6 +63,9 @@ import { defaultFields, defaultRelations } from "./"
|
||||
* description: The new items to send to the Customer.
|
||||
* type: array
|
||||
* items:
|
||||
* required:
|
||||
* - variant_id
|
||||
* - quantity
|
||||
* properties:
|
||||
* variant_id:
|
||||
* description: The id of the Product Variant to ship.
|
||||
@@ -49,6 +77,9 @@ import { defaultFields, defaultRelations } from "./"
|
||||
* description: The custom shipping options to potentially create a Shipping Method from.
|
||||
* type: array
|
||||
* items:
|
||||
* required:
|
||||
* - option_id
|
||||
* - price
|
||||
* properties:
|
||||
* option_id:
|
||||
* description: The id of the Shipping Option to override with a custom price.
|
||||
@@ -77,39 +108,11 @@ import { defaultFields, defaultRelations } from "./"
|
||||
export default async (req, res) => {
|
||||
const { id } = req.params
|
||||
|
||||
const schema = Validator.object().keys({
|
||||
return_items: Validator.array()
|
||||
.items({
|
||||
item_id: Validator.string().required(),
|
||||
quantity: Validator.number().required(),
|
||||
})
|
||||
.required(),
|
||||
return_shipping: Validator.object()
|
||||
.keys({
|
||||
option_id: Validator.string().optional(),
|
||||
price: Validator.number().integer().optional(),
|
||||
})
|
||||
.optional(),
|
||||
additional_items: Validator.array().items({
|
||||
variant_id: Validator.string().required(),
|
||||
quantity: Validator.number().required(),
|
||||
}),
|
||||
custom_shipping_options: Validator.array()
|
||||
.items({
|
||||
option_id: Validator.string().required(),
|
||||
price: Validator.number().required(),
|
||||
})
|
||||
.default([]),
|
||||
no_notification: Validator.boolean().optional(),
|
||||
allow_backorder: Validator.boolean().default(true),
|
||||
})
|
||||
const validated = await validator(AdminPostOrdersOrderSwapsReq, req.body)
|
||||
|
||||
const { value, error } = schema.validate(req.body)
|
||||
if (error) {
|
||||
throw new MedusaError(MedusaError.Types.INVALID_DATA, error.details)
|
||||
}
|
||||
|
||||
const idempotencyKeyService = req.scope.resolve("idempotencyKeyService")
|
||||
const idempotencyKeyService: IdempotencyKeyService = req.scope.resolve(
|
||||
"idempotencyKeyService"
|
||||
)
|
||||
|
||||
const headerKey = req.get("Idempotency-Key") || ""
|
||||
|
||||
@@ -129,9 +132,9 @@ export default async (req, res) => {
|
||||
res.setHeader("Access-Control-Expose-Headers", "Idempotency-Key")
|
||||
res.setHeader("Idempotency-Key", idempotencyKey.idempotency_key)
|
||||
|
||||
const orderService = req.scope.resolve("orderService")
|
||||
const swapService = req.scope.resolve("swapService")
|
||||
const returnService = req.scope.resolve("returnService")
|
||||
const orderService: OrderService = req.scope.resolve("orderService")
|
||||
const swapService: SwapService = req.scope.resolve("swapService")
|
||||
const returnService: ReturnService = req.scope.resolve("returnService")
|
||||
|
||||
let inProgress = true
|
||||
let err = false
|
||||
@@ -153,19 +156,19 @@ export default async (req, res) => {
|
||||
.withTransaction(manager)
|
||||
.create(
|
||||
order,
|
||||
value.return_items,
|
||||
value.additional_items,
|
||||
value.return_shipping,
|
||||
validated.return_items,
|
||||
validated.additional_items,
|
||||
validated.return_shipping,
|
||||
{
|
||||
idempotency_key: idempotencyKey.idempotency_key,
|
||||
no_notification: value.no_notification,
|
||||
allow_backorder: value.allow_backorder,
|
||||
no_notification: validated.no_notification,
|
||||
allow_backorder: validated.allow_backorder,
|
||||
}
|
||||
)
|
||||
|
||||
await swapService
|
||||
.withTransaction(manager)
|
||||
.createCart(swap.id, value.custom_shipping_options)
|
||||
.createCart(swap.id, validated.custom_shipping_options)
|
||||
const returnOrder = await returnService
|
||||
.withTransaction(manager)
|
||||
.retrieveBySwap(swap.id)
|
||||
@@ -203,8 +206,8 @@ export default async (req, res) => {
|
||||
}
|
||||
|
||||
const order = await orderService.retrieve(id, {
|
||||
select: defaultFields,
|
||||
relations: defaultRelations,
|
||||
select: defaultAdminOrdersFields,
|
||||
relations: defaultAdminOrdersRelations,
|
||||
})
|
||||
|
||||
return {
|
||||
@@ -247,3 +250,77 @@ export default async (req, res) => {
|
||||
|
||||
res.status(idempotencyKey.response_code).json(idempotencyKey.response_body)
|
||||
}
|
||||
|
||||
export class AdminPostOrdersOrderSwapsReq {
|
||||
@IsArray()
|
||||
@IsNotEmpty()
|
||||
@ValidateNested({ each: true })
|
||||
@Type(() => ReturnItem)
|
||||
return_items: ReturnItem[]
|
||||
|
||||
@IsObject()
|
||||
@IsOptional()
|
||||
@ValidateNested()
|
||||
@Type(() => ReturnShipping)
|
||||
return_shipping?: ReturnShipping
|
||||
|
||||
@IsArray()
|
||||
@IsOptional()
|
||||
@ValidateNested({ each: true })
|
||||
@Type(() => AdditionalItem)
|
||||
additional_items?: AdditionalItem[]
|
||||
|
||||
@IsArray()
|
||||
@IsOptional()
|
||||
@ValidateNested({ each: true })
|
||||
@Type(() => CustomShippingOption)
|
||||
custom_shipping_options?: CustomShippingOption[] = []
|
||||
|
||||
@IsBoolean()
|
||||
@IsOptional()
|
||||
no_notification?: boolean
|
||||
|
||||
@IsBoolean()
|
||||
@IsOptional()
|
||||
allow_backorder?: boolean = true
|
||||
}
|
||||
|
||||
class ReturnItem {
|
||||
@IsString()
|
||||
@IsNotEmpty()
|
||||
item_id: string
|
||||
|
||||
@IsNumber()
|
||||
@IsNotEmpty()
|
||||
quantity: number
|
||||
}
|
||||
|
||||
class ReturnShipping {
|
||||
@IsString()
|
||||
@IsNotEmpty()
|
||||
option_id: string
|
||||
|
||||
@IsInt()
|
||||
@IsOptional()
|
||||
price?: number
|
||||
}
|
||||
|
||||
class CustomShippingOption {
|
||||
@IsString()
|
||||
@IsNotEmpty()
|
||||
option_id: string
|
||||
|
||||
@IsInt()
|
||||
@IsNotEmpty()
|
||||
price: number
|
||||
}
|
||||
|
||||
class AdditionalItem {
|
||||
@IsString()
|
||||
@IsNotEmpty()
|
||||
variant_id: string
|
||||
|
||||
@IsNumber()
|
||||
@IsNotEmpty()
|
||||
quantity: number
|
||||
}
|
||||
+4
-1
@@ -1,8 +1,11 @@
|
||||
import { OrderService } from "../../../../services"
|
||||
|
||||
/**
|
||||
* @oas [delete] /order/{id}/metadata/{key}
|
||||
* operationId: "DeleteOrdersOrderMetadataKey"
|
||||
* summary: "Delete Metadata"
|
||||
* description: "Deletes a metadata key."
|
||||
* x-authenticated: true
|
||||
* parameters:
|
||||
* - (path) id=* {string} The id of the Order.
|
||||
* - (path) key=* {string} The metadata key.
|
||||
@@ -21,7 +24,7 @@
|
||||
export default async (req, res) => {
|
||||
const { id, key } = req.params
|
||||
|
||||
const orderService = req.scope.resolve("orderService")
|
||||
const orderService: OrderService = req.scope.resolve("orderService")
|
||||
|
||||
await orderService.deleteMetadata(id, key)
|
||||
|
||||
+27
-19
@@ -1,11 +1,14 @@
|
||||
import { MedusaError, Validator } from "medusa-core-utils"
|
||||
import { defaultRelations, defaultFields } from "./"
|
||||
|
||||
import { IsBoolean, IsObject, IsOptional } from "class-validator"
|
||||
import { EntityManager } from "typeorm"
|
||||
import { defaultAdminOrdersRelations, defaultAdminOrdersFields } from "."
|
||||
import { ClaimService, OrderService } from "../../../../services"
|
||||
import { validator } from "../../../../utils/validator"
|
||||
/**
|
||||
* @oas [post] /orders/{id}/claims/{claim_id}/fulfillments
|
||||
* operationId: "PostOrdersOrderClaimsClaimFulfillments"
|
||||
* summary: "Create a Claim Fulfillment"
|
||||
* description: "Creates a Fulfillment for a Claim."
|
||||
* x-authenticated: true
|
||||
* parameters:
|
||||
* - (path) id=* {string} The id of the Order.
|
||||
* - (path) claim_id=* {string} The id of the Claim.
|
||||
@@ -35,31 +38,36 @@ import { defaultRelations, defaultFields } from "./"
|
||||
export default async (req, res) => {
|
||||
const { id, claim_id } = req.params
|
||||
|
||||
const schema = Validator.object().keys({
|
||||
metadata: Validator.object().optional(),
|
||||
no_notification: Validator.boolean().optional(),
|
||||
})
|
||||
const validated = await validator(
|
||||
AdminPostOrdersOrderClaimsClaimFulfillmentsReq,
|
||||
req.body
|
||||
)
|
||||
|
||||
const { value, error } = schema.validate(req.body)
|
||||
if (error) {
|
||||
throw new MedusaError(MedusaError.Types.INVALID_DATA, error.details)
|
||||
}
|
||||
|
||||
const orderService = req.scope.resolve("orderService")
|
||||
const claimService = req.scope.resolve("claimService")
|
||||
const entityManager = req.scope.resolve("manager")
|
||||
const orderService: OrderService = req.scope.resolve("orderService")
|
||||
const claimService: ClaimService = req.scope.resolve("claimService")
|
||||
const entityManager: EntityManager = req.scope.resolve("manager")
|
||||
|
||||
await entityManager.transaction(async (manager) => {
|
||||
await claimService.withTransaction(manager).createFulfillment(claim_id, {
|
||||
metadata: value.metadata,
|
||||
no_notification: value.no_notification,
|
||||
metadata: validated.metadata,
|
||||
no_notification: validated.no_notification,
|
||||
})
|
||||
})
|
||||
|
||||
const order = await orderService.retrieve(id, {
|
||||
select: defaultFields,
|
||||
relations: defaultRelations,
|
||||
select: defaultAdminOrdersFields,
|
||||
relations: defaultAdminOrdersRelations,
|
||||
})
|
||||
|
||||
res.status(200).json({ order })
|
||||
}
|
||||
|
||||
export class AdminPostOrdersOrderClaimsClaimFulfillmentsReq {
|
||||
@IsObject()
|
||||
@IsOptional()
|
||||
metadata?: object
|
||||
|
||||
@IsBoolean()
|
||||
@IsOptional()
|
||||
no_notification?: boolean
|
||||
}
|
||||
+27
-19
@@ -1,11 +1,14 @@
|
||||
import { MedusaError, Validator } from "medusa-core-utils"
|
||||
import { defaultRelations, defaultFields } from "./"
|
||||
|
||||
import { IsBoolean, IsObject, IsOptional } from "class-validator"
|
||||
import { EntityManager } from "typeorm"
|
||||
import { defaultAdminOrdersRelations, defaultAdminOrdersFields } from "."
|
||||
import { OrderService, SwapService } from "../../../../services"
|
||||
import { validator } from "../../../../utils/validator"
|
||||
/**
|
||||
* @oas [post] /orders/{id}/swaps/{swap_id}/fulfillments
|
||||
* operationId: "PostOrdersOrderSwapsSwapFulfillments"
|
||||
* summary: "Create a Swap Fulfillment"
|
||||
* description: "Creates a Fulfillment for a Swap."
|
||||
* x-authenticated: true
|
||||
* parameters:
|
||||
* - (path) id=* {string} The id of the Order.
|
||||
* - (path) swap_id=* {string} The id of the Swap.
|
||||
@@ -35,31 +38,36 @@ import { defaultRelations, defaultFields } from "./"
|
||||
export default async (req, res) => {
|
||||
const { id, swap_id } = req.params
|
||||
|
||||
const schema = Validator.object().keys({
|
||||
metadata: Validator.object().optional(),
|
||||
no_notification: Validator.boolean().optional,
|
||||
})
|
||||
const validated = await validator(
|
||||
AdminPostOrdersOrderSwapsSwapFulfillmentsReq,
|
||||
req.body
|
||||
)
|
||||
|
||||
const { value, error } = schema.validate(req.body)
|
||||
if (error) {
|
||||
throw new MedusaError(MedusaError.Types.INVALID_DATA, error.details)
|
||||
}
|
||||
|
||||
const orderService = req.scope.resolve("orderService")
|
||||
const swapService = req.scope.resolve("swapService")
|
||||
const entityManager = req.scope.resolve("manager")
|
||||
const orderService: OrderService = req.scope.resolve("orderService")
|
||||
const swapService: SwapService = req.scope.resolve("swapService")
|
||||
const entityManager: EntityManager = req.scope.resolve("manager")
|
||||
|
||||
await entityManager.transaction(async (manager) => {
|
||||
await swapService.withTransaction(manager).createFulfillment(swap_id, {
|
||||
metadata: value.metadata,
|
||||
no_notification: value.no_notification,
|
||||
metadata: validated.metadata,
|
||||
no_notification: validated.no_notification,
|
||||
})
|
||||
|
||||
const order = await orderService.withTransaction(manager).retrieve(id, {
|
||||
select: defaultFields,
|
||||
relations: defaultRelations,
|
||||
select: defaultAdminOrdersFields,
|
||||
relations: defaultAdminOrdersRelations,
|
||||
})
|
||||
|
||||
res.status(200).json({ order })
|
||||
})
|
||||
}
|
||||
|
||||
export class AdminPostOrdersOrderSwapsSwapFulfillmentsReq {
|
||||
@IsObject()
|
||||
@IsOptional()
|
||||
metadata?: object
|
||||
|
||||
@IsBoolean()
|
||||
@IsOptional()
|
||||
no_notification?: boolean
|
||||
}
|
||||
+6
-4
@@ -1,10 +1,12 @@
|
||||
import { defaultRelations, defaultFields } from "./"
|
||||
import { defaultAdminOrdersRelations, defaultAdminOrdersFields } from "."
|
||||
import { OrderService } from "../../../../services"
|
||||
|
||||
/**
|
||||
* @oas [get] /orders/{id}
|
||||
* operationId: "GetOrdersOrder"
|
||||
* summary: "Retrieve an Order"
|
||||
* description: "Retrieves an Order"
|
||||
* x-authenticated: true
|
||||
* parameters:
|
||||
* - (path) id=* {string} The id of the Order.
|
||||
* tags:
|
||||
@@ -22,11 +24,11 @@ import { defaultRelations, defaultFields } from "./"
|
||||
export default async (req, res) => {
|
||||
const { id } = req.params
|
||||
|
||||
const orderService = req.scope.resolve("orderService")
|
||||
const orderService: OrderService = req.scope.resolve("orderService")
|
||||
|
||||
const order = await orderService.retrieve(id, {
|
||||
select: defaultFields,
|
||||
relations: defaultRelations,
|
||||
select: defaultAdminOrdersFields,
|
||||
relations: defaultAdminOrdersRelations,
|
||||
})
|
||||
|
||||
res.json({ order })
|
||||
+52
-6
@@ -1,5 +1,8 @@
|
||||
import { Router } from "express"
|
||||
import { Order } from "../../../.."
|
||||
import middlewares from "../../../middlewares"
|
||||
import "reflect-metadata"
|
||||
import { DeleteResponse, PaginatedResponse } from "../../../../types/common"
|
||||
|
||||
const route = Router()
|
||||
|
||||
@@ -18,7 +21,11 @@ export default (app) => {
|
||||
/**
|
||||
* Get an order
|
||||
*/
|
||||
route.get("/:id", middlewares.wrap(require("./get-order").default))
|
||||
route.get(
|
||||
"/:id",
|
||||
middlewares.normalizeQuery(),
|
||||
middlewares.wrap(require("./get-order").default)
|
||||
)
|
||||
|
||||
/**
|
||||
* Create a new order
|
||||
@@ -216,7 +223,17 @@ export default (app) => {
|
||||
return app
|
||||
}
|
||||
|
||||
export const defaultRelations = [
|
||||
export type AdminOrdersRes = {
|
||||
order: Order
|
||||
}
|
||||
|
||||
export type AdminDeleteRes = DeleteResponse
|
||||
|
||||
export type AdminOrdersListRes = PaginatedResponse & {
|
||||
orders: Order[]
|
||||
}
|
||||
|
||||
export const defaultAdminOrdersRelations = [
|
||||
"customer",
|
||||
"billing_address",
|
||||
"shipping_address",
|
||||
@@ -253,7 +270,7 @@ export const defaultRelations = [
|
||||
"swaps.fulfillments",
|
||||
]
|
||||
|
||||
export const defaultFields = [
|
||||
export const defaultAdminOrdersFields = [
|
||||
"id",
|
||||
"status",
|
||||
"fulfillment_status",
|
||||
@@ -285,7 +302,7 @@ export const defaultFields = [
|
||||
"no_notification",
|
||||
]
|
||||
|
||||
export const allowedFields = [
|
||||
export const allowedAdminOrdersFields = [
|
||||
"id",
|
||||
"status",
|
||||
"fulfillment_status",
|
||||
@@ -314,7 +331,7 @@ export const allowedFields = [
|
||||
"no_notification",
|
||||
]
|
||||
|
||||
export const allowedRelations = [
|
||||
export const allowedAdminOrdersRelations = [
|
||||
"customer",
|
||||
"region",
|
||||
"billing_address",
|
||||
@@ -333,7 +350,7 @@ export const allowedRelations = [
|
||||
"swaps.additional_items",
|
||||
]
|
||||
|
||||
export const filterableFields = [
|
||||
export const filterableAdminOrdersFields = [
|
||||
"id",
|
||||
"status",
|
||||
"fulfillment_status",
|
||||
@@ -349,3 +366,32 @@ export const filterableFields = [
|
||||
"created_at",
|
||||
"updated_at",
|
||||
]
|
||||
|
||||
export * from "./add-shipping-method"
|
||||
export * from "./archive-order"
|
||||
export * from "./cancel-claim"
|
||||
export * from "./cancel-fulfillment"
|
||||
export * from "./cancel-fulfillment-claim"
|
||||
export * from "./cancel-fulfillment-swap"
|
||||
export * from "./cancel-order"
|
||||
export * from "./cancel-swap"
|
||||
export * from "./capture-payment"
|
||||
export * from "./complete-order"
|
||||
export * from "./create-claim"
|
||||
export * from "./create-claim-shipment"
|
||||
export * from "./create-fulfillment"
|
||||
export * from "./create-order"
|
||||
export * from "./create-shipment"
|
||||
export * from "./create-swap"
|
||||
export * from "./create-swap-shipment"
|
||||
export * from "./delete-metadata"
|
||||
export * from "./fulfill-claim"
|
||||
export * from "./fulfill-swap"
|
||||
export * from "./get-order"
|
||||
export * from "./list-orders"
|
||||
export * from "./process-swap-payment"
|
||||
export * from "./receive-swap"
|
||||
export * from "./refund-payment"
|
||||
export * from "./request-return"
|
||||
export * from "./update-claim"
|
||||
export * from "./update-order"
|
||||
Loaded 100 of 413 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user