feat(medusa, medusa-js, medusa-react): PublishableApiKey "update" endpoint & add "title" property (#2609)
**What** - update PK endpoint - medusa-js/react implementation - add a title property to the entity - update the migration file - pass a title on create - list PKs by title - update the client libs with new param signatures - change id prefix to: "pk_"
This commit is contained in:
@@ -82,9 +82,15 @@ describe("[MEDUSA_FF_PUBLISHABLE_API_KEYS] Publishable API keys", () => {
|
|||||||
beforeEach(async () => {
|
beforeEach(async () => {
|
||||||
await adminSeeder(dbConnection)
|
await adminSeeder(dbConnection)
|
||||||
|
|
||||||
await simplePublishableApiKeyFactory(dbConnection, {})
|
await simplePublishableApiKeyFactory(dbConnection, {
|
||||||
await simplePublishableApiKeyFactory(dbConnection, {})
|
title: "just a title",
|
||||||
await simplePublishableApiKeyFactory(dbConnection, {})
|
})
|
||||||
|
await simplePublishableApiKeyFactory(dbConnection, {
|
||||||
|
title: "special title 1",
|
||||||
|
})
|
||||||
|
await simplePublishableApiKeyFactory(dbConnection, {
|
||||||
|
title: "special title 2",
|
||||||
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
afterEach(async () => {
|
afterEach(async () => {
|
||||||
@@ -105,6 +111,30 @@ describe("[MEDUSA_FF_PUBLISHABLE_API_KEYS] Publishable API keys", () => {
|
|||||||
expect(response.data.offset).toBe(0)
|
expect(response.data.offset).toBe(0)
|
||||||
expect(response.data.publishable_api_keys).toHaveLength(2)
|
expect(response.data.publishable_api_keys).toHaveLength(2)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
it("list publishable keys with query search", async () => {
|
||||||
|
const api = useApi()
|
||||||
|
|
||||||
|
const response = await api.get(
|
||||||
|
`/admin/publishable-api-keys?q=special`,
|
||||||
|
adminHeaders
|
||||||
|
)
|
||||||
|
|
||||||
|
expect(response.data.count).toBe(2)
|
||||||
|
expect(response.data.limit).toBe(20)
|
||||||
|
expect(response.data.offset).toBe(0)
|
||||||
|
expect(response.data.publishable_api_keys).toHaveLength(2)
|
||||||
|
expect(response.data.publishable_api_keys).toEqual(
|
||||||
|
expect.arrayContaining([
|
||||||
|
expect.objectContaining({
|
||||||
|
title: "special title 1",
|
||||||
|
}),
|
||||||
|
expect.objectContaining({
|
||||||
|
title: "special title 2",
|
||||||
|
}),
|
||||||
|
])
|
||||||
|
)
|
||||||
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
describe("POST /admin/publishable-api-keys", () => {
|
describe("POST /admin/publishable-api-keys", () => {
|
||||||
@@ -122,7 +152,7 @@ describe("[MEDUSA_FF_PUBLISHABLE_API_KEYS] Publishable API keys", () => {
|
|||||||
|
|
||||||
const response = await api.post(
|
const response = await api.post(
|
||||||
`/admin/publishable-api-keys`,
|
`/admin/publishable-api-keys`,
|
||||||
{},
|
{ title: "Store api key" },
|
||||||
adminHeaders
|
adminHeaders
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -130,6 +160,45 @@ describe("[MEDUSA_FF_PUBLISHABLE_API_KEYS] Publishable API keys", () => {
|
|||||||
expect(response.data.publishable_api_key).toMatchObject({
|
expect(response.data.publishable_api_key).toMatchObject({
|
||||||
created_by: "admin_user",
|
created_by: "admin_user",
|
||||||
id: expect.any(String),
|
id: expect.any(String),
|
||||||
|
title: "Store api key",
|
||||||
|
revoked_by: null,
|
||||||
|
revoked_at: null,
|
||||||
|
created_at: expect.any(String),
|
||||||
|
updated_at: expect.any(String),
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
describe("POST /admin/publishable-api-keys/:id", () => {
|
||||||
|
const pubKeyId = IdMap.getId("pubkey-get-id-update")
|
||||||
|
|
||||||
|
beforeEach(async () => {
|
||||||
|
await adminSeeder(dbConnection)
|
||||||
|
|
||||||
|
await simplePublishableApiKeyFactory(dbConnection, {
|
||||||
|
id: pubKeyId,
|
||||||
|
title: "Initial key title",
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
afterEach(async () => {
|
||||||
|
const db = useDb()
|
||||||
|
return await db.teardown()
|
||||||
|
})
|
||||||
|
|
||||||
|
it("update a publishable key", async () => {
|
||||||
|
const api = useApi()
|
||||||
|
|
||||||
|
const response = await api.post(
|
||||||
|
`/admin/publishable-api-keys/${pubKeyId}`,
|
||||||
|
{ title: "Changed title" },
|
||||||
|
adminHeaders
|
||||||
|
)
|
||||||
|
|
||||||
|
expect(response.status).toBe(200)
|
||||||
|
expect(response.data.publishable_api_key).toMatchObject({
|
||||||
|
id: pubKeyId,
|
||||||
|
title: "Changed title",
|
||||||
revoked_by: null,
|
revoked_by: null,
|
||||||
revoked_at: null,
|
revoked_at: null,
|
||||||
created_at: expect.any(String),
|
created_at: expect.any(String),
|
||||||
|
|||||||
@@ -9,7 +9,6 @@ const adminSeeder = require("../../helpers/admin-seeder")
|
|||||||
const {
|
const {
|
||||||
simpleSalesChannelFactory,
|
simpleSalesChannelFactory,
|
||||||
simpleProductFactory,
|
simpleProductFactory,
|
||||||
simpleCartFactory,
|
|
||||||
} = require("../../factories")
|
} = require("../../factories")
|
||||||
const { simpleOrderFactory } = require("../../factories")
|
const { simpleOrderFactory } = require("../../factories")
|
||||||
const orderSeeder = require("../../helpers/order-seeder")
|
const orderSeeder = require("../../helpers/order-seeder")
|
||||||
|
|||||||
@@ -3,7 +3,6 @@ import {
|
|||||||
MoneyAmount,
|
MoneyAmount,
|
||||||
PriceListType,
|
PriceListType,
|
||||||
PriceListStatus,
|
PriceListStatus,
|
||||||
CustomerGroup,
|
|
||||||
} from "@medusajs/medusa"
|
} from "@medusajs/medusa"
|
||||||
import faker from "faker"
|
import faker from "faker"
|
||||||
import { Connection } from "typeorm"
|
import { Connection } from "typeorm"
|
||||||
|
|||||||
@@ -1,4 +1,6 @@
|
|||||||
|
import faker from "faker"
|
||||||
import { Connection } from "typeorm"
|
import { Connection } from "typeorm"
|
||||||
|
|
||||||
import { PublishableApiKey } from "@medusajs/medusa"
|
import { PublishableApiKey } from "@medusajs/medusa"
|
||||||
|
|
||||||
export type PublishableApiKeyData = {
|
export type PublishableApiKeyData = {
|
||||||
@@ -6,6 +8,7 @@ export type PublishableApiKeyData = {
|
|||||||
revoked_at?: Date
|
revoked_at?: Date
|
||||||
revoked_by?: string
|
revoked_by?: string
|
||||||
created_by?: string
|
created_by?: string
|
||||||
|
title?: string
|
||||||
}
|
}
|
||||||
|
|
||||||
export const simplePublishableApiKeyFactory = async (
|
export const simplePublishableApiKeyFactory = async (
|
||||||
@@ -14,7 +17,10 @@ export const simplePublishableApiKeyFactory = async (
|
|||||||
): Promise<PublishableApiKey> => {
|
): Promise<PublishableApiKey> => {
|
||||||
const manager = connection.manager
|
const manager = connection.manager
|
||||||
|
|
||||||
const pubKey = manager.create(PublishableApiKey, data)
|
const pubKey = manager.create(PublishableApiKey, {
|
||||||
|
...data,
|
||||||
|
title: data.title || `${faker.commerce.department()} API key`,
|
||||||
|
})
|
||||||
|
|
||||||
return await manager.save(pubKey)
|
return await manager.save(pubKey)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,6 +5,8 @@ import {
|
|||||||
AdminPublishableApiKeysRes,
|
AdminPublishableApiKeysRes,
|
||||||
GetPublishableApiKeysParams,
|
GetPublishableApiKeysParams,
|
||||||
AdminPublishableApiKeysListRes,
|
AdminPublishableApiKeysListRes,
|
||||||
|
AdminPostPublishableApiKeysReq,
|
||||||
|
AdminPostPublishableApiKeysPublishableApiKeyReq,
|
||||||
} from "@medusajs/medusa"
|
} from "@medusajs/medusa"
|
||||||
|
|
||||||
import { ResponsePromise } from "../../typings"
|
import { ResponsePromise } from "../../typings"
|
||||||
@@ -41,13 +43,22 @@ class AdminPublishableApiKeyResource extends BaseResource {
|
|||||||
}
|
}
|
||||||
|
|
||||||
create(
|
create(
|
||||||
payload: {},
|
payload: AdminPostPublishableApiKeysReq,
|
||||||
customHeaders: Record<string, any> = {}
|
customHeaders: Record<string, any> = {}
|
||||||
): ResponsePromise<AdminPublishableApiKeysRes> {
|
): ResponsePromise<AdminPublishableApiKeysRes> {
|
||||||
const path = `/admin/publishable-api-keys`
|
const path = `/admin/publishable-api-keys`
|
||||||
return this.client.request("POST", path, payload, {}, customHeaders)
|
return this.client.request("POST", path, payload, {}, customHeaders)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
update(
|
||||||
|
id: string,
|
||||||
|
payload: AdminPostPublishableApiKeysPublishableApiKeyReq,
|
||||||
|
customHeaders: Record<string, any> = {}
|
||||||
|
) {
|
||||||
|
const path = `/admin/publishable-api-keys/${id}`
|
||||||
|
return this.client.request("POST", path, payload, {}, customHeaders)
|
||||||
|
}
|
||||||
|
|
||||||
delete(
|
delete(
|
||||||
id: string,
|
id: string,
|
||||||
customHeaders: Record<string, any> = {}
|
customHeaders: Record<string, any> = {}
|
||||||
|
|||||||
@@ -980,6 +980,18 @@ export const adminHandlers = [
|
|||||||
)
|
)
|
||||||
}),
|
}),
|
||||||
|
|
||||||
|
rest.post("/admin/publishable-api-keys/:id", (req, res, ctx) => {
|
||||||
|
return res(
|
||||||
|
ctx.status(200),
|
||||||
|
ctx.json({
|
||||||
|
publishable_api_key: {
|
||||||
|
...fixtures.get("publishable_api_key"),
|
||||||
|
...(req.body as any),
|
||||||
|
},
|
||||||
|
})
|
||||||
|
)
|
||||||
|
}),
|
||||||
|
|
||||||
rest.post("/admin/publishable-api-keys/", (req, res, ctx) => {
|
rest.post("/admin/publishable-api-keys/", (req, res, ctx) => {
|
||||||
return res(
|
return res(
|
||||||
ctx.status(200),
|
ctx.status(200),
|
||||||
|
|||||||
@@ -4,6 +4,8 @@ import { Response } from "@medusajs/medusa-js"
|
|||||||
import {
|
import {
|
||||||
AdminPublishableApiKeyDeleteRes,
|
AdminPublishableApiKeyDeleteRes,
|
||||||
AdminPublishableApiKeysRes,
|
AdminPublishableApiKeysRes,
|
||||||
|
AdminPostPublishableApiKeysPublishableApiKeyReq,
|
||||||
|
AdminPostPublishableApiKeysReq,
|
||||||
} from "@medusajs/medusa"
|
} from "@medusajs/medusa"
|
||||||
|
|
||||||
import { buildOptions } from "../../utils/buildOptions"
|
import { buildOptions } from "../../utils/buildOptions"
|
||||||
@@ -11,16 +13,47 @@ import { useMedusa } from "../../../contexts"
|
|||||||
import { adminPublishableApiKeysKeys } from "."
|
import { adminPublishableApiKeysKeys } from "."
|
||||||
|
|
||||||
export const useAdminCreatePublishableApiKey = (
|
export const useAdminCreatePublishableApiKey = (
|
||||||
options?: UseMutationOptions<Response<AdminPublishableApiKeysRes>, Error, {}>
|
options?: UseMutationOptions<
|
||||||
|
Response<AdminPublishableApiKeysRes>,
|
||||||
|
Error,
|
||||||
|
AdminPostPublishableApiKeysReq
|
||||||
|
>
|
||||||
) => {
|
) => {
|
||||||
const { client } = useMedusa()
|
const { client } = useMedusa()
|
||||||
const queryClient = useQueryClient()
|
const queryClient = useQueryClient()
|
||||||
return useMutation(
|
return useMutation(
|
||||||
(payload: {}) => client.admin.publishableApiKeys.create(payload),
|
(payload: AdminPostPublishableApiKeysReq) =>
|
||||||
|
client.admin.publishableApiKeys.create(payload),
|
||||||
buildOptions(queryClient, [adminPublishableApiKeysKeys.lists()], options)
|
buildOptions(queryClient, [adminPublishableApiKeysKeys.lists()], options)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export const useAdminUpdatePublishableApiKey = (
|
||||||
|
id: string,
|
||||||
|
options?: UseMutationOptions<
|
||||||
|
Response<AdminPublishableApiKeysRes>,
|
||||||
|
Error,
|
||||||
|
AdminPostPublishableApiKeysPublishableApiKeyReq
|
||||||
|
>
|
||||||
|
) => {
|
||||||
|
const { client } = useMedusa()
|
||||||
|
const queryClient = useQueryClient()
|
||||||
|
|
||||||
|
return useMutation(
|
||||||
|
(payload: AdminPostPublishableApiKeysPublishableApiKeyReq) =>
|
||||||
|
client.admin.publishableApiKeys.update(id, payload),
|
||||||
|
buildOptions(
|
||||||
|
queryClient,
|
||||||
|
[
|
||||||
|
adminPublishableApiKeysKeys.lists(),
|
||||||
|
adminPublishableApiKeysKeys.detail(id),
|
||||||
|
adminPublishableApiKeysKeys.details(),
|
||||||
|
],
|
||||||
|
options
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
export const useAdminDeletePublishableApiKey = (
|
export const useAdminDeletePublishableApiKey = (
|
||||||
id: string,
|
id: string,
|
||||||
options?: UseMutationOptions<
|
options?: UseMutationOptions<
|
||||||
|
|||||||
@@ -1,12 +1,13 @@
|
|||||||
import { renderHook } from "@testing-library/react-hooks"
|
import { renderHook } from "@testing-library/react-hooks"
|
||||||
|
|
||||||
import { createWrapper } from "../../../utils"
|
|
||||||
import {
|
import {
|
||||||
useAdminDeletePublishableApiKey,
|
useAdminDeletePublishableApiKey,
|
||||||
useAdminRevokePublishableApiKey,
|
useAdminRevokePublishableApiKey,
|
||||||
|
useAdminUpdatePublishableApiKey,
|
||||||
|
useAdminCreatePublishableApiKey,
|
||||||
} from "../../../../src"
|
} from "../../../../src"
|
||||||
|
import { createWrapper } from "../../../utils"
|
||||||
import { fixtures } from "../../../../mocks/data"
|
import { fixtures } from "../../../../mocks/data"
|
||||||
import { useAdminCreatePublishableApiKey } from "../../../../src"
|
|
||||||
|
|
||||||
describe("useAdminCreatePublishableApiKey hook", () => {
|
describe("useAdminCreatePublishableApiKey hook", () => {
|
||||||
test("Created a publishable api key", async () => {
|
test("Created a publishable api key", async () => {
|
||||||
@@ -17,7 +18,7 @@ describe("useAdminCreatePublishableApiKey hook", () => {
|
|||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
result.current.mutate({})
|
result.current.mutate({ title: "Mandatory title" })
|
||||||
|
|
||||||
await waitFor(() => result.current.isSuccess)
|
await waitFor(() => result.current.isSuccess)
|
||||||
|
|
||||||
@@ -25,6 +26,7 @@ describe("useAdminCreatePublishableApiKey hook", () => {
|
|||||||
expect(result.current.data).toEqual(
|
expect(result.current.data).toEqual(
|
||||||
expect.objectContaining({
|
expect.objectContaining({
|
||||||
publishable_api_key: {
|
publishable_api_key: {
|
||||||
|
title: "Mandatory title",
|
||||||
...fixtures.get("publishable_api_key"),
|
...fixtures.get("publishable_api_key"),
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
@@ -32,6 +34,34 @@ describe("useAdminCreatePublishableApiKey hook", () => {
|
|||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
|
describe("useAdminUpdatePublishableApiKey hook", () => {
|
||||||
|
test("updates an publishable key and returns it", async () => {
|
||||||
|
const pubKey = {
|
||||||
|
title: "changed title",
|
||||||
|
}
|
||||||
|
|
||||||
|
const { result, waitFor } = renderHook(
|
||||||
|
() =>
|
||||||
|
useAdminUpdatePublishableApiKey(fixtures.get("publishable_api_key").id),
|
||||||
|
{
|
||||||
|
wrapper: createWrapper(),
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
result.current.mutate(pubKey)
|
||||||
|
|
||||||
|
await waitFor(() => result.current.isSuccess)
|
||||||
|
|
||||||
|
expect(result.current.data.response.status).toEqual(200)
|
||||||
|
expect(result.current.data.publishable_api_key).toEqual(
|
||||||
|
expect.objectContaining({
|
||||||
|
...fixtures.get("publishable_api_key"),
|
||||||
|
...pubKey,
|
||||||
|
})
|
||||||
|
)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
describe("useAdminRevokePublishableApiKey hook", () => {
|
describe("useAdminRevokePublishableApiKey hook", () => {
|
||||||
test("Revoke a publishable api key", async () => {
|
test("Revoke a publishable api key", async () => {
|
||||||
const id = "pubkey_1234"
|
const id = "pubkey_1234"
|
||||||
|
|||||||
+18
-1
@@ -1,5 +1,6 @@
|
|||||||
import { Request, Response } from "express"
|
import { Request, Response } from "express"
|
||||||
import { EntityManager } from "typeorm"
|
import { EntityManager } from "typeorm"
|
||||||
|
import { IsString } from "class-validator"
|
||||||
|
|
||||||
import PublishableApiKeyService from "../../../../services/publishable-api-key"
|
import PublishableApiKeyService from "../../../../services/publishable-api-key"
|
||||||
|
|
||||||
@@ -8,6 +9,16 @@ import PublishableApiKeyService from "../../../../services/publishable-api-key"
|
|||||||
* operationId: "PostPublishableApiKeys"
|
* operationId: "PostPublishableApiKeys"
|
||||||
* summary: "Create a PublishableApiKey"
|
* summary: "Create a PublishableApiKey"
|
||||||
* description: "Creates a PublishableApiKey."
|
* description: "Creates a PublishableApiKey."
|
||||||
|
* requestBody:
|
||||||
|
* content:
|
||||||
|
* application/json:
|
||||||
|
* schema:
|
||||||
|
* required:
|
||||||
|
* - title
|
||||||
|
* properties:
|
||||||
|
* title:
|
||||||
|
* description: A title for the publishable api key
|
||||||
|
* type: string
|
||||||
* x-authenticated: true
|
* x-authenticated: true
|
||||||
* x-codeSamples:
|
* x-codeSamples:
|
||||||
* - lang: JavaScript
|
* - lang: JavaScript
|
||||||
@@ -59,14 +70,20 @@ export default async (req: Request, res: Response) => {
|
|||||||
) as PublishableApiKeyService
|
) as PublishableApiKeyService
|
||||||
|
|
||||||
const manager = req.scope.resolve("manager") as EntityManager
|
const manager = req.scope.resolve("manager") as EntityManager
|
||||||
|
const data = req.validatedBody as AdminPostPublishableApiKeysReq
|
||||||
|
|
||||||
const loggedInUserId = (req.user?.id ?? req.user?.userId) as string
|
const loggedInUserId = (req.user?.id ?? req.user?.userId) as string
|
||||||
|
|
||||||
const pubKey = await manager.transaction(async (transactionManager) => {
|
const pubKey = await manager.transaction(async (transactionManager) => {
|
||||||
return await publishableApiKeyService
|
return await publishableApiKeyService
|
||||||
.withTransaction(transactionManager)
|
.withTransaction(transactionManager)
|
||||||
.create({ loggedInUserId })
|
.create(data, { loggedInUserId })
|
||||||
})
|
})
|
||||||
|
|
||||||
return res.status(200).json({ publishable_api_key: pubKey })
|
return res.status(200).json({ publishable_api_key: pubKey })
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export class AdminPostPublishableApiKeysReq {
|
||||||
|
@IsString()
|
||||||
|
title: string
|
||||||
|
}
|
||||||
|
|||||||
@@ -2,10 +2,15 @@ import { Router } from "express"
|
|||||||
|
|
||||||
import { isFeatureFlagEnabled } from "../../../middlewares/feature-flag-enabled"
|
import { isFeatureFlagEnabled } from "../../../middlewares/feature-flag-enabled"
|
||||||
import PublishableAPIKeysFeatureFlag from "../../../../loaders/feature-flags/publishable-api-keys"
|
import PublishableAPIKeysFeatureFlag from "../../../../loaders/feature-flags/publishable-api-keys"
|
||||||
import middlewares, { transformQuery } from "../../../middlewares"
|
import middlewares, {
|
||||||
|
transformBody,
|
||||||
|
transformQuery,
|
||||||
|
} from "../../../middlewares"
|
||||||
import { GetPublishableApiKeysParams } from "./list-publishable-api-keys"
|
import { GetPublishableApiKeysParams } from "./list-publishable-api-keys"
|
||||||
import { PublishableApiKey } from "../../../../models"
|
import { PublishableApiKey } from "../../../../models"
|
||||||
import { DeleteResponse, PaginatedResponse } from "../../../../types/common"
|
import { DeleteResponse, PaginatedResponse } from "../../../../types/common"
|
||||||
|
import { AdminPostPublishableApiKeysReq } from "./create-publishable-api-key"
|
||||||
|
import { AdminPostPublishableApiKeysPublishableApiKeyReq } from "./update-publishable-api-key"
|
||||||
|
|
||||||
const route = Router()
|
const route = Router()
|
||||||
|
|
||||||
@@ -18,6 +23,7 @@ export default (app) => {
|
|||||||
|
|
||||||
route.post(
|
route.post(
|
||||||
"/",
|
"/",
|
||||||
|
transformBody(AdminPostPublishableApiKeysReq),
|
||||||
middlewares.wrap(require("./create-publishable-api-key").default)
|
middlewares.wrap(require("./create-publishable-api-key").default)
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -26,6 +32,12 @@ export default (app) => {
|
|||||||
middlewares.wrap(require("./get-publishable-api-key").default)
|
middlewares.wrap(require("./get-publishable-api-key").default)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
route.post(
|
||||||
|
"/:id",
|
||||||
|
transformBody(AdminPostPublishableApiKeysPublishableApiKeyReq),
|
||||||
|
middlewares.wrap(require("./update-publishable-api-key").default)
|
||||||
|
)
|
||||||
|
|
||||||
route.delete(
|
route.delete(
|
||||||
"/:id",
|
"/:id",
|
||||||
middlewares.wrap(require("./delete-publishable-api-key").default)
|
middlewares.wrap(require("./delete-publishable-api-key").default)
|
||||||
@@ -54,3 +66,5 @@ export type AdminPublishableApiKeysListRes = PaginatedResponse & {
|
|||||||
export type AdminPublishableApiKeyDeleteRes = DeleteResponse
|
export type AdminPublishableApiKeyDeleteRes = DeleteResponse
|
||||||
|
|
||||||
export * from "./list-publishable-api-keys"
|
export * from "./list-publishable-api-keys"
|
||||||
|
export * from "./create-publishable-api-key"
|
||||||
|
export * from "./update-publishable-api-key"
|
||||||
|
|||||||
+6
-2
@@ -11,7 +11,7 @@ import PublishableApiKeyService from "../../../../services/publishable-api-key"
|
|||||||
* description: "List PublishableApiKeys."
|
* description: "List PublishableApiKeys."
|
||||||
* x-authenticated: true
|
* x-authenticated: true
|
||||||
* parameters:
|
* parameters:
|
||||||
* - (query) order_id {string} List publishable keys by id.
|
* - (query) q {string} Query used for searching publishable api keys by title.
|
||||||
* - (query) limit=20 {number} The number of items in the response
|
* - (query) limit=20 {number} The number of items in the response
|
||||||
* - (query) offset=0 {number} The offset of items in response
|
* - (query) offset=0 {number} The offset of items in response
|
||||||
* - (query) expand {string} Comma separated list of relations to include in the results.
|
* - (query) expand {string} Comma separated list of relations to include in the results.
|
||||||
@@ -84,4 +84,8 @@ export default async (req: Request, res: Response) => {
|
|||||||
export class GetPublishableApiKeysParams extends extendedFindParamsMixin({
|
export class GetPublishableApiKeysParams extends extendedFindParamsMixin({
|
||||||
limit: 20,
|
limit: 20,
|
||||||
offset: 0,
|
offset: 0,
|
||||||
}) {}
|
}) {
|
||||||
|
@IsString()
|
||||||
|
@IsOptional()
|
||||||
|
q?: string
|
||||||
|
}
|
||||||
|
|||||||
+97
@@ -0,0 +1,97 @@
|
|||||||
|
import { Request, Response } from "express"
|
||||||
|
import { IsOptional, IsString } from "class-validator"
|
||||||
|
import { EntityManager } from "typeorm"
|
||||||
|
|
||||||
|
import PublishableApiKeyService from "../../../../services/publishable-api-key"
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @oas [post] /publishable-api-key/{id}
|
||||||
|
* operationId: "PostPublishableApiKysPublishableApiKey"
|
||||||
|
* summary: "Updates a PublishableApiKey"
|
||||||
|
* description: "Updates a PublishableApiKey."
|
||||||
|
* x-authenticated: true
|
||||||
|
* parameters:
|
||||||
|
* - (path) id=* {string} The ID of the PublishableApiKey.
|
||||||
|
* requestBody:
|
||||||
|
* content:
|
||||||
|
* application/json:
|
||||||
|
* schema:
|
||||||
|
* properties:
|
||||||
|
* title:
|
||||||
|
* description: A title to update for the key.
|
||||||
|
* type: string
|
||||||
|
* x-codeSamples:
|
||||||
|
* - lang: JavaScript
|
||||||
|
* label: JS Client
|
||||||
|
* source: |
|
||||||
|
* import Medusa from "@medusajs/medusa-js"
|
||||||
|
* const medusa = new Medusa({ baseUrl: MEDUSA_BACKEND_URL, maxRetries: 3 })
|
||||||
|
* // must be previously logged in or use api token
|
||||||
|
* medusa.admin.publishableApiKey.update(publishable_key_id, {
|
||||||
|
* title: "new title"
|
||||||
|
* })
|
||||||
|
* .then(({ publishable_api_key }) => {
|
||||||
|
* console.log(publishable_api_key.id)
|
||||||
|
* })
|
||||||
|
* - lang: Shell
|
||||||
|
* label: cURL
|
||||||
|
* source: |
|
||||||
|
* curl --location --request POST 'https://medusa-url.com/admin/publishable-api-key/{id}' \
|
||||||
|
* --header 'Authorization: Bearer {api_token}' \
|
||||||
|
* --header 'Content-Type: application/json' \
|
||||||
|
* --data-raw '{
|
||||||
|
* "title": "updated title"
|
||||||
|
* }'
|
||||||
|
* security:
|
||||||
|
* - api_token: []
|
||||||
|
* - cookie_auth: []
|
||||||
|
* tags:
|
||||||
|
* - PublishableApiKey
|
||||||
|
* responses:
|
||||||
|
* 200:
|
||||||
|
* description: OK
|
||||||
|
* content:
|
||||||
|
* application/json:
|
||||||
|
* schema:
|
||||||
|
* properties:
|
||||||
|
* publishable_api_key:
|
||||||
|
* $ref: "#/components/schemas/publishable_api_key"
|
||||||
|
* "400":
|
||||||
|
* $ref: "#/components/responses/400_error"
|
||||||
|
* "401":
|
||||||
|
* $ref: "#/components/responses/unauthorized"
|
||||||
|
* "404":
|
||||||
|
* $ref: "#/components/responses/not_found_error"
|
||||||
|
* "409":
|
||||||
|
* $ref: "#/components/responses/invalid_state_error"
|
||||||
|
* "422":
|
||||||
|
* $ref: "#/components/responses/invalid_request_error"
|
||||||
|
* "500":
|
||||||
|
* $ref: "#/components/responses/500_error"
|
||||||
|
*/
|
||||||
|
export default async (req: Request, res: Response) => {
|
||||||
|
const { id } = req.params
|
||||||
|
const { validatedBody } = req as {
|
||||||
|
validatedBody: AdminPostPublishableApiKeysPublishableApiKeyReq
|
||||||
|
}
|
||||||
|
|
||||||
|
const publishableApiKeysService: PublishableApiKeyService = req.scope.resolve(
|
||||||
|
"publishableApiKeyService"
|
||||||
|
)
|
||||||
|
|
||||||
|
const manager: EntityManager = req.scope.resolve("manager")
|
||||||
|
|
||||||
|
const updatedKey = await manager.transaction(async (transactionManager) => {
|
||||||
|
return await publishableApiKeysService
|
||||||
|
.withTransaction(transactionManager)
|
||||||
|
.update(id, validatedBody)
|
||||||
|
})
|
||||||
|
|
||||||
|
res.status(200).json({ publishable_api_key: updatedKey })
|
||||||
|
}
|
||||||
|
|
||||||
|
export class AdminPostPublishableApiKeysPublishableApiKeyReq {
|
||||||
|
@IsString()
|
||||||
|
@IsOptional()
|
||||||
|
title?: string
|
||||||
|
}
|
||||||
@@ -12,7 +12,7 @@ export class publishableApiKey1667815005070 implements MigrationInterface {
|
|||||||
`CREATE TABLE "publishable_api_key_sales_channel" ("sales_channel_id" character varying NOT NULL, "publishable_key_id" character varying NOT NULL, CONSTRAINT "PK_68eaeb14bdac8954460054c567c" PRIMARY KEY ("sales_channel_id", "publishable_key_id"))`
|
`CREATE TABLE "publishable_api_key_sales_channel" ("sales_channel_id" character varying NOT NULL, "publishable_key_id" character varying NOT NULL, CONSTRAINT "PK_68eaeb14bdac8954460054c567c" PRIMARY KEY ("sales_channel_id", "publishable_key_id"))`
|
||||||
)
|
)
|
||||||
await queryRunner.query(
|
await queryRunner.query(
|
||||||
`CREATE TABLE "publishable_api_key" ("id" character varying NOT NULL, "created_at" TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT now(), "updated_at" TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT now(), "created_by" character varying, "revoked_by" character varying, "revoked_at" TIMESTAMP WITH TIME ZONE, CONSTRAINT "PK_9e613278673a87de92c606b4494" PRIMARY KEY ("id"))`
|
`CREATE TABLE "publishable_api_key" ("id" character varying NOT NULL, "created_at" TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT now(), "updated_at" TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT now(), "created_by" character varying, "revoked_by" character varying, "revoked_at" TIMESTAMP WITH TIME ZONE, "title" character varying NOT NULL, CONSTRAINT "PK_9e613278673a87de92c606b4494" PRIMARY KEY ("id"))`
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -17,9 +17,12 @@ export class PublishableApiKey extends BaseEntity {
|
|||||||
@Column({ type: resolveDbType("timestamptz"), nullable: true })
|
@Column({ type: resolveDbType("timestamptz"), nullable: true })
|
||||||
revoked_at?: Date
|
revoked_at?: Date
|
||||||
|
|
||||||
|
@Column()
|
||||||
|
title: string
|
||||||
|
|
||||||
@BeforeInsert()
|
@BeforeInsert()
|
||||||
private beforeInsert(): void {
|
private beforeInsert(): void {
|
||||||
this.id = generateEntityId(this.id, "pubkey")
|
this.id = generateEntityId(this.id, "pk")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -32,7 +35,7 @@ export class PublishableApiKey extends BaseEntity {
|
|||||||
* id:
|
* id:
|
||||||
* type: string
|
* type: string
|
||||||
* description: The key's ID
|
* description: The key's ID
|
||||||
* example: pak_01G1G5V27GYX4QXNARRQCW1N8T
|
* example: pk_01G1G5V27GYX4QXNARRQCW1N8T
|
||||||
* created_by:
|
* created_by:
|
||||||
* type: string
|
* type: string
|
||||||
* description: "The unique identifier of the user that created the key."
|
* description: "The unique identifier of the user that created the key."
|
||||||
|
|||||||
@@ -48,13 +48,17 @@ describe("PublishableApiKeyService", () => {
|
|||||||
})
|
})
|
||||||
|
|
||||||
it("should create a publishable api key and call the repository with the right arguments as well as the event bus service", async () => {
|
it("should create a publishable api key and call the repository with the right arguments as well as the event bus service", async () => {
|
||||||
await publishableApiKeyService.create({
|
await publishableApiKeyService.create(
|
||||||
loggedInUserId: IdMap.getId("admin_user"),
|
{ title: "API key title" },
|
||||||
})
|
{
|
||||||
|
loggedInUserId: IdMap.getId("admin_user"),
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
expect(publishableApiKeyRepository.create).toHaveBeenCalledTimes(1)
|
expect(publishableApiKeyRepository.create).toHaveBeenCalledTimes(1)
|
||||||
expect(publishableApiKeyRepository.create).toHaveBeenCalledWith({
|
expect(publishableApiKeyRepository.create).toHaveBeenCalledWith({
|
||||||
created_by: IdMap.getId("admin_user"),
|
created_by: IdMap.getId("admin_user"),
|
||||||
|
title: "API key title",
|
||||||
})
|
})
|
||||||
expect(EventBusServiceMock.emit).toHaveBeenCalledTimes(1)
|
expect(EventBusServiceMock.emit).toHaveBeenCalledTimes(1)
|
||||||
expect(EventBusServiceMock.emit).toHaveBeenCalledWith(
|
expect(EventBusServiceMock.emit).toHaveBeenCalledWith(
|
||||||
@@ -63,6 +67,19 @@ describe("PublishableApiKeyService", () => {
|
|||||||
)
|
)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
it("should update a publishable api key", async () => {
|
||||||
|
await publishableApiKeyService.update(pubKeyToRetrieve.id, {
|
||||||
|
title: "new title",
|
||||||
|
})
|
||||||
|
|
||||||
|
expect(publishableApiKeyRepository.save).toHaveBeenLastCalledWith(
|
||||||
|
expect.objectContaining({
|
||||||
|
id: pubKeyToRetrieve.id,
|
||||||
|
title: "new title",
|
||||||
|
})
|
||||||
|
)
|
||||||
|
})
|
||||||
|
|
||||||
it("should revoke a publishable api key", async () => {
|
it("should revoke a publishable api key", async () => {
|
||||||
await publishableApiKeyService.revoke("id", {
|
await publishableApiKeyService.revoke("id", {
|
||||||
loggedInUserId: IdMap.getId("admin_user"),
|
loggedInUserId: IdMap.getId("admin_user"),
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { EntityManager } from "typeorm"
|
import { EntityManager, ILike } from "typeorm"
|
||||||
import { MedusaError } from "medusa-core-utils"
|
import { MedusaError } from "medusa-core-utils"
|
||||||
|
|
||||||
import { PublishableApiKeyRepository } from "../repositories/publishable-api-key"
|
import { PublishableApiKeyRepository } from "../repositories/publishable-api-key"
|
||||||
@@ -6,7 +6,11 @@ import { FindConfig, Selector } from "../types/common"
|
|||||||
import { PublishableApiKey } from "../models"
|
import { PublishableApiKey } from "../models"
|
||||||
import { TransactionBaseService } from "../interfaces"
|
import { TransactionBaseService } from "../interfaces"
|
||||||
import EventBusService from "./event-bus"
|
import EventBusService from "./event-bus"
|
||||||
import { buildQuery } from "../utils"
|
import { buildQuery, isDefined, isString } from "../utils"
|
||||||
|
import {
|
||||||
|
CreatePublishableApiKeyInput,
|
||||||
|
UpdatePublishableApiKeyInput,
|
||||||
|
} from "../types/publishable-api-key"
|
||||||
|
|
||||||
type InjectedDependencies = {
|
type InjectedDependencies = {
|
||||||
manager: EntityManager
|
manager: EntityManager
|
||||||
@@ -45,17 +49,22 @@ class PublishableApiKeyService extends TransactionBaseService {
|
|||||||
/**
|
/**
|
||||||
* Create a PublishableApiKey record.
|
* Create a PublishableApiKey record.
|
||||||
*
|
*
|
||||||
* @params context - key creation context object
|
* @param data - partial data for creating the entity
|
||||||
|
* @param context - key creation context object
|
||||||
*/
|
*/
|
||||||
async create(context: {
|
async create(
|
||||||
loggedInUserId: string
|
data: CreatePublishableApiKeyInput,
|
||||||
}): Promise<PublishableApiKey | never> {
|
context: {
|
||||||
|
loggedInUserId: string
|
||||||
|
}
|
||||||
|
): Promise<PublishableApiKey | never> {
|
||||||
return await this.atomicPhase_(async (manager) => {
|
return await this.atomicPhase_(async (manager) => {
|
||||||
const publishableApiKeyRepo = manager.getCustomRepository(
|
const publishableApiKeyRepo = manager.getCustomRepository(
|
||||||
this.publishableApiKeyRepository_
|
this.publishableApiKeyRepository_
|
||||||
)
|
)
|
||||||
|
|
||||||
const publishableApiKey = publishableApiKeyRepo.create({
|
const publishableApiKey = publishableApiKeyRepo.create({
|
||||||
|
...data,
|
||||||
created_by: context.loggedInUserId,
|
created_by: context.loggedInUserId,
|
||||||
})
|
})
|
||||||
|
|
||||||
@@ -122,7 +131,7 @@ class PublishableApiKeyService extends TransactionBaseService {
|
|||||||
* @return an array containing publishable API keys and a total count of records that matches the query
|
* @return an array containing publishable API keys and a total count of records that matches the query
|
||||||
*/
|
*/
|
||||||
async listAndCount(
|
async listAndCount(
|
||||||
selector: Selector<PublishableApiKey>,
|
selector: Selector<PublishableApiKey> & { q?: string },
|
||||||
config: FindConfig<PublishableApiKey> = {
|
config: FindConfig<PublishableApiKey> = {
|
||||||
skip: 0,
|
skip: 0,
|
||||||
take: 20,
|
take: 20,
|
||||||
@@ -133,11 +142,44 @@ class PublishableApiKeyService extends TransactionBaseService {
|
|||||||
this.publishableApiKeyRepository_
|
this.publishableApiKeyRepository_
|
||||||
)
|
)
|
||||||
|
|
||||||
|
let q
|
||||||
|
if (isString(selector.q)) {
|
||||||
|
q = selector.q
|
||||||
|
delete selector.q
|
||||||
|
}
|
||||||
|
|
||||||
const query = buildQuery(selector, config)
|
const query = buildQuery(selector, config)
|
||||||
|
|
||||||
|
if (q) {
|
||||||
|
query.where.title = ILike(`%${q}%`)
|
||||||
|
}
|
||||||
|
|
||||||
return await pubKeyRepo.findAndCount(query)
|
return await pubKeyRepo.findAndCount(query)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async update(
|
||||||
|
publishableApiKeyId: string,
|
||||||
|
data: UpdatePublishableApiKeyInput
|
||||||
|
): Promise<PublishableApiKey> {
|
||||||
|
{
|
||||||
|
return await this.atomicPhase_(async (manager) => {
|
||||||
|
const publishableApiKeyRepository = manager.getCustomRepository(
|
||||||
|
this.publishableApiKeyRepository_
|
||||||
|
)
|
||||||
|
|
||||||
|
const pubKey = await this.retrieve(publishableApiKeyId)
|
||||||
|
|
||||||
|
for (const key of Object.keys(data)) {
|
||||||
|
if (isDefined(data[key])) {
|
||||||
|
pubKey[key] = data[key]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return await publishableApiKeyRepository.save(pubKey)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Delete Publishable API key.
|
* Delete Publishable API key.
|
||||||
*
|
*
|
||||||
|
|||||||
@@ -0,0 +1,7 @@
|
|||||||
|
export type CreatePublishableApiKeyInput = {
|
||||||
|
title: string
|
||||||
|
}
|
||||||
|
|
||||||
|
export type UpdatePublishableApiKeyInput = {
|
||||||
|
title?: string
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user