feat(medusa,medusa-js,medusa-react): Add BatchJob API support in medusa-js + medusa-react (#1704)

* Add BatchJob API to medusa-js

* Adds BatchJob API hooks

* Fix tests in medusa-react
This commit is contained in:
Oliver Windall Juhl
2022-06-21 11:08:44 +02:00
committed by GitHub
parent 5f2744eb9f
commit 7302d76e12
13 changed files with 439 additions and 68 deletions

View File

@@ -0,0 +1,59 @@
import {
AdminBatchJobListRes,
AdminBatchJobRes,
AdminGetBatchParams,
AdminPostBatchesReq,
} from "@medusajs/medusa"
import qs from "qs"
import { ResponsePromise } from "../../typings"
import BaseResource from "../base"
class AdminBatchJobsResource extends BaseResource {
create(
payload: AdminPostBatchesReq,
customHeaders: Record<string, any> = {}
): ResponsePromise<AdminBatchJobRes> {
const path = `/admin/batch-jobs`
return this.client.request("POST", path, payload, {}, customHeaders)
}
list(
query?: AdminGetBatchParams,
customHeaders: Record<string, any> = {}
): ResponsePromise<AdminBatchJobListRes> {
let path = `/admin/batch-jobs`
if (query) {
const queryString = qs.stringify(query)
path = `/admin/batch-jobs?${queryString}`
}
return this.client.request("GET", path, {}, {}, customHeaders)
}
cancel(
batchJobId: string,
customHeaders: Record<string, any> = {}
): ResponsePromise<AdminBatchJobRes> {
const path = `/admin/batch-jobs/${batchJobId}/cancel`
return this.client.request("POST", path, {}, {}, customHeaders)
}
confirm(
batchJobId: string,
customHeaders: Record<string, any> = {}
): ResponsePromise<AdminBatchJobRes> {
const path = `/admin/batch-jobs/${batchJobId}/confirm`
return this.client.request("POST", path, {}, {}, customHeaders)
}
retrieve(
batchJobId: string,
customHeaders: Record<string, any> = {}
): ResponsePromise<AdminBatchJobRes> {
const path = `/admin/batch-jobs/${batchJobId}`
return this.client.request("GET", path, {}, {}, customHeaders)
}
}
export default AdminBatchJobsResource

View File

@@ -1,33 +1,35 @@
import BaseResource from "../base"
import AdminAuthResource from "./auth"
import AdminCustomersResource from "./customers"
import AdminCustomerGroupsResource from "./customer-groups"
import AdminDiscountsResource from "./discounts"
import AdminBatchJobsResource from "./batch-jobs"
import CollectionsResource from "./collections"
import AdminCustomerGroupsResource from "./customer-groups"
import AdminCustomersResource from "./customers"
import AdminDiscountsResource from "./discounts"
import AdminDraftOrdersResource from "./draft-orders"
import AdminGiftCardsResource from "./gift-cards"
import AdminInvitesResource from "./invites"
import AdminNotesResource from "./notes"
import AdminProductsResource from "./products"
import AdminProductTypesResource from "./product-types"
import AdminUsersResource from "./users"
import AdminReturnsResource from "./returns"
import AdminNotificationsResource from "./notifications"
import AdminOrdersResource from "./orders"
import AdminPriceListResource from "./price-lists"
import AdminProductTagsResource from "./product-tags"
import AdminProductTypesResource from "./product-types"
import AdminProductsResource from "./products"
import AdminRegionsResource from "./regions"
import AdminReturnReasonsResource from "./return-reasons"
import AdminVariantsResource from "./variants"
import AdminSwapsResource from "./swaps"
import AdminTaxRatesResource from "./tax-rates"
import AdminReturnsResource from "./returns"
import AdminShippingOptionsResource from "./shipping-options"
import AdminShippingProfilesResource from "./shipping-profiles"
import AdminStoresResource from "./store"
import AdminShippingOptionsResource from "./shipping-options"
import AdminRegionsResource from "./regions"
import AdminNotificationsResource from "./notifications"
import AdminSwapsResource from "./swaps"
import AdminTaxRatesResource from "./tax-rates"
import AdminUploadsResource from "./uploads"
import AdminProductTagsResource from "./product-tags"
import AdminPriceListResource from "./price-lists"
import AdminUsersResource from "./users"
import AdminVariantsResource from "./variants"
class Admin extends BaseResource {
public auth = new AdminAuthResource(this.client)
public batchJobs = new AdminBatchJobsResource(this.client)
public customers = new AdminCustomersResource(this.client)
public customerGroups = new AdminCustomerGroupsResource(this.client)
public discounts = new AdminDiscountsResource(this.client)

View File

@@ -1247,6 +1247,17 @@
"fulfillment_option": {
"provider_id": "test-ful",
"options": []
},
"batch_job": {
"id": "batch_01F0YES4R67TXXC1QBQ8P54A8Y",
"type": "product_export",
"created_by": "usr_123412341234",
"context": null,
"result": null,
"dry_run": false,
"updated_at": "2021-03-16T21:24:00.389Z",
"created_at": "2021-03-16T21:24:00.389Z",
"deleted_at": null
}
}
}

View File

@@ -2,6 +2,55 @@ import { rest } from "msw"
import { fixtures } from "../data"
export const adminHandlers = [
rest.post("/admin/batch-jobs/", (req, res, ctx) => {
const body = req.body as Record<string, any>
return res(
ctx.status(200),
ctx.json({
batch_job: {
...fixtures.get("batch_job"),
...body,
},
})
)
}),
rest.get("/admin/batch-jobs/", (req, res, ctx) => {
return res(
ctx.status(200),
ctx.json({
batch_jobs: fixtures.list("batch_job"),
})
)
}),
rest.get("/admin/batch-jobs/:id", (req, res, ctx) => {
return res(
ctx.status(200),
ctx.json({
batch_job: fixtures.get("batch_job"),
})
)
}),
rest.post("/admin/batch-jobs/:id/confirm", (req, res, ctx) => {
return res(
ctx.status(200),
ctx.json({
batch_job: fixtures.get("batch_job"),
})
)
}),
rest.post("/admin/batch-jobs/:id/cancel", (req, res, ctx) => {
return res(
ctx.status(200),
ctx.json({
batch_job: fixtures.get("batch_job"),
})
)
}),
rest.post("/admin/collections/", (req, res, ctx) => {
const body = req.body as Record<string, any>
return res(
@@ -253,27 +302,33 @@ export const adminHandlers = [
)
}),
rest.delete("/admin/price-lists/:id/products/:product_id/prices", (req, res, ctx) => {
return res(
ctx.status(200),
ctx.json({
ids: [],
object: "money-amount",
deleted: true,
})
)
}),
rest.delete(
"/admin/price-lists/:id/products/:product_id/prices",
(req, res, ctx) => {
return res(
ctx.status(200),
ctx.json({
ids: [],
object: "money-amount",
deleted: true,
})
)
}
),
rest.delete("/admin/price-lists/:id/variants/:variant_id/prices", (req, res, ctx) => {
return res(
ctx.status(200),
ctx.json({
ids: [],
object: "money-amount",
deleted: true,
})
)
}),
rest.delete(
"/admin/price-lists/:id/variants/:variant_id/prices",
(req, res, ctx) => {
return res(
ctx.status(200),
ctx.json({
ids: [],
object: "money-amount",
deleted: true,
})
)
}
),
rest.post("/admin/return-reasons/", (req, res, ctx) => {
const body = req.body as Record<string, any>

View File

@@ -0,0 +1,2 @@
export * from "./queries"
export * from "./mutations"

View File

@@ -0,0 +1,74 @@
import { AdminBatchJobRes, AdminPostBatchesReq } from "@medusajs/medusa"
import { Response } from "@medusajs/medusa-js"
import { useMutation, UseMutationOptions, useQueryClient } from "react-query"
import { useMedusa } from "../../../contexts"
import { buildOptions } from "../../utils/buildOptions"
import { adminBatchJobsKeys } from "./queries"
/**
* Hook returns functions for creating batch jobs.
*
* @param options
*/
export const useAdminCreateBatchJob = (
options?: UseMutationOptions<
Response<AdminBatchJobRes>,
Error,
AdminPostBatchesReq
>
) => {
const { client } = useMedusa()
const queryClient = useQueryClient()
return useMutation(
(payload: AdminPostBatchesReq) => client.admin.batchJobs.create(payload),
buildOptions(queryClient, adminBatchJobsKeys.lists(), options)
)
}
/**
* Hook return functions for canceling a batch job
*
* @param id - id of the batch job
* @param options
*/
export const useAdminCancelBatchJob = (
id: string,
options?: UseMutationOptions<Response<AdminBatchJobRes>, Error>
) => {
const { client } = useMedusa()
const queryClient = useQueryClient()
return useMutation(
() => client.admin.batchJobs.cancel(id),
buildOptions(
queryClient,
[adminBatchJobsKeys.lists(), adminBatchJobsKeys.detail(id)],
options
)
)
}
/**
* Hook return functions for confirming a batch job
*
* @param id - id of the batch job
* @param options
*/
export const useAdminConfirmBatchJob = (
id: string,
options?: UseMutationOptions<Response<AdminBatchJobRes>, Error>
) => {
const { client } = useMedusa()
const queryClient = useQueryClient()
return useMutation(
() => client.admin.batchJobs.confirm(id),
buildOptions(
queryClient,
[adminBatchJobsKeys.lists(), adminBatchJobsKeys.detail(id)],
options
)
)
}

View File

@@ -0,0 +1,50 @@
import {
AdminBatchJobListRes,
AdminBatchJobRes,
AdminGetBatchParams,
} from "@medusajs/medusa"
import { Response } from "@medusajs/medusa-js"
import { useQuery } from "react-query"
import { useMedusa } from "../../../contexts"
import { UseQueryOptionsWrapper } from "../../../types"
import { queryKeysFactory } from "../../utils/index"
const ADMIN_COLLECTIONS_QUERY_KEY = `admin_batches` as const
export const adminBatchJobsKeys = queryKeysFactory(ADMIN_COLLECTIONS_QUERY_KEY)
type BatchJobsQueryKey = typeof adminBatchJobsKeys
export const useAdminBatchJobs = (
query?: AdminGetBatchParams,
options?: UseQueryOptionsWrapper<
Response<AdminBatchJobListRes>,
Error,
ReturnType<BatchJobsQueryKey["list"]>
>
) => {
const { client } = useMedusa()
const { data, ...rest } = useQuery(
adminBatchJobsKeys.list(query),
() => client.admin.batchJobs.list(query),
options
)
return { ...data, ...rest } as const
}
export const useAdminBatchJob = (
id: string,
options?: UseQueryOptionsWrapper<
Response<AdminBatchJobRes>,
Error,
ReturnType<BatchJobsQueryKey["detail"]>
>
) => {
const { client } = useMedusa()
const { data, ...rest } = useQuery(
adminBatchJobsKeys.detail(id),
() => client.admin.batchJobs.retrieve(id),
options
)
return { ...data, ...rest } as const
}

View File

@@ -1,26 +1,27 @@
export * from "./auth"
export * from "./collections"
export * from "./batch-jobs"
export * from "./claims"
export * from "./customers"
export * from "./collections"
export * from "./customer-groups"
export * from "./customers"
export * from "./discounts"
export * from "./draft-orders"
export * from "./gift-cards"
export * from "./invites"
export * from "./notes"
export * from "./notifications"
export * from "./orders"
export * from "./products"
export * from "./price-lists"
export * from "./product-tags"
export * from "./product-types"
export * from "./price-lists"
export * from "./return-reasons"
export * from "./products"
export * from "./regions"
export * from "./return-reasons"
export * from "./returns"
export * from "./shipping-options"
export * from "./shipping-profiles"
export * from "./notes"
export * from "./invites"
export * from "./notifications"
export * from "./returns"
export * from "./store"
export * from "./swaps"
export * from "./tax-rates"
export * from "./users"
export * from "./variants"
export * from "./tax-rates"

View File

@@ -0,0 +1,78 @@
import { renderHook } from "@testing-library/react-hooks"
import { fixtures } from "../../../../mocks/data"
import {
useAdminCancelBatchJob,
useAdminConfirmBatchJob,
useAdminCreateBatchJob,
} from "../../../../src"
import { createWrapper } from "../../../utils"
describe("useAdminCreateBatchJob hook", () => {
test("creates a batch job and returns it", async () => {
const batch = {
type: "product_export",
dry_run: false,
context: {},
}
const { result, waitFor } = renderHook(() => useAdminCreateBatchJob(), {
wrapper: createWrapper(),
})
result.current.mutate(batch)
await waitFor(() => result.current.isSuccess)
expect(result.current.data?.response.status).toEqual(200)
expect(result.current.data?.batch_job).toEqual(
expect.objectContaining({
...fixtures.get("batch_job"),
...batch,
})
)
})
})
describe("useAdminCancelBatchJob hook", () => {
test("cancels a batch job and returns it", async () => {
const { result, waitFor } = renderHook(
() => useAdminCancelBatchJob(fixtures.get("batch_job").id),
{
wrapper: createWrapper(),
}
)
result.current.mutate()
await waitFor(() => result.current.isSuccess)
expect(result.current.data?.response.status).toEqual(200)
expect(result.current.data?.batch_job).toEqual(
expect.objectContaining({
...fixtures.get("batch_job"),
})
)
})
})
describe("useAdminConfirmBatchJob hook", () => {
test("confirms a batch job and returns it", async () => {
const { result, waitFor } = renderHook(
() => useAdminConfirmBatchJob(fixtures.get("batch_job").id),
{
wrapper: createWrapper(),
}
)
result.current.mutate()
await waitFor(() => result.current.isSuccess)
expect(result.current.data?.response.status).toEqual(200)
expect(result.current.data?.batch_job).toEqual(
expect.objectContaining({
...fixtures.get("batch_job"),
})
)
})
})

View File

@@ -0,0 +1,35 @@
import { renderHook } from "@testing-library/react-hooks"
import { fixtures } from "../../../../mocks/data"
import { useAdminBatchJob, useAdminBatchJobs } from "../../../../src"
import { createWrapper } from "../../../utils"
describe("useAdminBatchJobs hook", () => {
test("returns a list of batch job", async () => {
const batchJobs = fixtures.list("batch_job")
const { result, waitFor } = renderHook(() => useAdminBatchJobs(), {
wrapper: createWrapper(),
})
await waitFor(() => result.current.isSuccess)
expect(result.current.response?.status).toEqual(200)
expect(result.current.batch_jobs).toEqual(batchJobs)
})
})
describe("useAdminBatchJob hook", () => {
test("returns a batch job", async () => {
const batchJob = fixtures.get("batch_job")
const { result, waitFor } = renderHook(
() => useAdminBatchJob(batchJob.id),
{
wrapper: createWrapper(),
}
)
await waitFor(() => result.current.isSuccess)
expect(result.current.response?.status).toEqual(200)
expect(result.current.batch_job).toEqual(batchJob)
})
})

View File

@@ -16,33 +16,33 @@ export default (container, config) => {
}
// Admin
export * from "./routes/admin/collections"
export * from "./routes/admin/auth"
export * from "./routes/admin/customers"
export * from "./routes/admin/batch"
export * from "./routes/admin/collections"
export * from "./routes/admin/customer-groups"
export * from "./routes/admin/customers"
export * from "./routes/admin/discounts"
export * from "./routes/admin/draft-orders"
export * from "./routes/admin/gift-cards"
export * from "./routes/admin/invites"
export * from "./routes/admin/notes"
export * from "./routes/admin/notifications"
export * from "./routes/admin/shipping-profiles"
export * from "./routes/admin/store"
export * from "./routes/admin/products"
export * from "./routes/admin/users"
export * from "./routes/admin/orders"
export * from "./routes/admin/variants"
export * from "./routes/admin/return-reasons"
export * from "./routes/admin/swaps"
export * from "./routes/admin/uploads"
export * from "./routes/admin/returns"
export * from "./routes/admin/tax-rates"
export * from "./routes/admin/shipping-options"
export * from "./routes/admin/regions"
export * from "./routes/admin/price-lists"
export * from "./routes/admin/product-tags"
export * from "./routes/admin/product-types"
export * from "./routes/admin/price-lists"
export * from "./routes/admin/products"
export * from "./routes/admin/regions"
export * from "./routes/admin/return-reasons"
export * from "./routes/admin/returns"
export * from "./routes/admin/shipping-options"
export * from "./routes/admin/shipping-profiles"
export * from "./routes/admin/store"
export * from "./routes/admin/swaps"
export * from "./routes/admin/tax-rates"
export * from "./routes/admin/uploads"
export * from "./routes/admin/users"
export * from "./routes/admin/variants"
// Store
export * from "./routes/store/auth"
export * from "./routes/store/carts"

View File

@@ -1,12 +1,12 @@
import { Router } from "express"
import { BatchJob } from "../../../.."
import { DeleteResponse, PaginatedResponse } from "../../../../types/common"
import { AdminGetBatchParams } from "./list-batch-jobs"
import middlewares, {
transformQuery,
getRequestedBatchJob,
canAccessBatchJob,
getRequestedBatchJob,
transformQuery,
} from "../../../middlewares"
import { AdminGetBatchParams } from "./list-batch-jobs"
export default (app) => {
const route = Router()
@@ -59,4 +59,8 @@ export const defaultAdminBatchFields = [
"deleted_at",
]
export * from "./cancel-batch-job"
export * from "./confirm-batch-job"
export * from "./create-batch-job"
export * from "./get-batch-job"
export * from "./list-batch-jobs"

View File

@@ -27673,10 +27673,10 @@ typeorm@^0.2.29, typeorm@^0.2.31:
yargs "^17.0.1"
zen-observable-ts "^1.0.0"
typescript@^3.7.3:
version "3.9.10"
resolved "https://registry.yarnpkg.com/typescript/-/typescript-3.9.10.tgz#70f3910ac7a51ed6bef79da7800690b19bf778b8"
integrity sha512-w6fIxVE/H1PkLKcCPsFqKE7Kv7QUwhU8qQY2MueZXWx5cPZdwFupLgKK3vntcK98BtNHZtAF4LA/yl2a7k8R6Q==
typescript@^3.7.3, typescript@^4.5.0:
version "4.7.4"
resolved "https://registry.yarnpkg.com/typescript/-/typescript-4.7.4.tgz#1a88596d1cf47d59507a1bcdfb5b9dfe4d488235"
integrity sha512-C0WQT0gezHuw6AdY1M2jxUO83Rjf0HP7Sk1DtXj6j1EwkQNZrHAg2XPWlq62oqEhYvONq5pkC2Y9oPljWToLmQ==
typescript@^4.1.3:
version "4.4.2"