feat: API key sales channel link (#6851)
What - Add link between API key and sales channels - Add API route for batch adding sales channels to a publishable API key - Clean up API key API routes responses - Move API key test suite from `integration-tests/modules` to `integration-tests/api`
This commit is contained in:
@@ -1,18 +1,19 @@
|
||||
import { revokeApiKeysWorkflow } from "@medusajs/core-flows"
|
||||
import { RevokeApiKeyDTO } from "@medusajs/types"
|
||||
import {
|
||||
ContainerRegistrationKeys,
|
||||
remoteQueryObjectFromString,
|
||||
} from "@medusajs/utils"
|
||||
import {
|
||||
AuthenticatedMedusaRequest,
|
||||
MedusaResponse,
|
||||
} from "../../../../../types/routing"
|
||||
|
||||
import { RevokeApiKeyDTO } from "@medusajs/types"
|
||||
import { revokeApiKeysWorkflow } from "@medusajs/core-flows"
|
||||
|
||||
export const POST = async (
|
||||
req: AuthenticatedMedusaRequest,
|
||||
res: MedusaResponse
|
||||
) => {
|
||||
const id = req.params.id
|
||||
|
||||
const { result, errors } = await revokeApiKeysWorkflow(req.scope).run({
|
||||
const { errors } = await revokeApiKeysWorkflow(req.scope).run({
|
||||
input: {
|
||||
selector: { id: req.params.id },
|
||||
revoke: {
|
||||
@@ -27,5 +28,17 @@ export const POST = async (
|
||||
throw errors[0].error
|
||||
}
|
||||
|
||||
res.status(200).json({ apiKey: result[0] })
|
||||
const remoteQuery = req.scope.resolve(ContainerRegistrationKeys.REMOTE_QUERY)
|
||||
|
||||
const queryObject = remoteQueryObjectFromString({
|
||||
entryPoint: "api_key",
|
||||
variables: {
|
||||
id: req.params.id,
|
||||
},
|
||||
fields: req.remoteQueryConfig.fields,
|
||||
})
|
||||
|
||||
const [apiKey] = await remoteQuery(queryObject)
|
||||
|
||||
res.status(200).json({ api_key: apiKey })
|
||||
}
|
||||
|
||||
@@ -1,21 +1,24 @@
|
||||
import {
|
||||
AuthenticatedMedusaRequest,
|
||||
MedusaResponse,
|
||||
} from "../../../../types/routing"
|
||||
import {
|
||||
deleteApiKeysWorkflow,
|
||||
updateApiKeysWorkflow,
|
||||
} from "@medusajs/core-flows"
|
||||
import {
|
||||
AuthenticatedMedusaRequest,
|
||||
MedusaResponse,
|
||||
} from "../../../../types/routing"
|
||||
|
||||
import { UpdateApiKeyDTO } from "@medusajs/types"
|
||||
import {
|
||||
ContainerRegistrationKeys,
|
||||
remoteQueryObjectFromString,
|
||||
} from "@medusajs/utils"
|
||||
import { defaultAdminApiKeyFields } from "../query-config"
|
||||
import { remoteQueryObjectFromString } from "@medusajs/utils"
|
||||
|
||||
export const GET = async (
|
||||
req: AuthenticatedMedusaRequest,
|
||||
res: MedusaResponse
|
||||
) => {
|
||||
const remoteQuery = req.scope.resolve("remoteQuery")
|
||||
const remoteQuery = req.scope.resolve(ContainerRegistrationKeys.REMOTE_QUERY)
|
||||
|
||||
const variables = { id: req.params.id }
|
||||
|
||||
@@ -46,7 +49,19 @@ export const POST = async (
|
||||
throw errors[0].error
|
||||
}
|
||||
|
||||
res.status(200).json({ apiKey: result[0] })
|
||||
const remoteQuery = req.scope.resolve(ContainerRegistrationKeys.REMOTE_QUERY)
|
||||
|
||||
const queryObject = remoteQueryObjectFromString({
|
||||
entryPoint: "api_key",
|
||||
variables: {
|
||||
id: req.params.id,
|
||||
},
|
||||
fields: req.remoteQueryConfig.fields,
|
||||
})
|
||||
|
||||
const [apiKey] = await remoteQuery(queryObject)
|
||||
|
||||
res.status(200).json({ api_key: apiKey })
|
||||
}
|
||||
|
||||
export const DELETE = async (
|
||||
|
||||
@@ -0,0 +1,62 @@
|
||||
import { addSalesChannelsToApiKeyWorkflow } from "@medusajs/core-flows"
|
||||
import { ModuleRegistrationName } from "@medusajs/modules-sdk"
|
||||
import {
|
||||
ContainerRegistrationKeys,
|
||||
MedusaError,
|
||||
remoteQueryObjectFromString,
|
||||
} from "@medusajs/utils"
|
||||
import {
|
||||
AuthenticatedMedusaRequest,
|
||||
MedusaResponse,
|
||||
} from "../../../../../../../types/routing"
|
||||
import { AdminPostApiKeysApiKeySalesChannelsBatchReq } from "../../../../validators"
|
||||
|
||||
export const POST = async (
|
||||
req: AuthenticatedMedusaRequest,
|
||||
res: MedusaResponse
|
||||
) => {
|
||||
const body = req.validatedBody as AdminPostApiKeysApiKeySalesChannelsBatchReq
|
||||
|
||||
const apiKeyModule = req.scope.resolve(ModuleRegistrationName.API_KEY)
|
||||
|
||||
const apiKey = await apiKeyModule.retrieve(req.params.id)
|
||||
|
||||
if (apiKey.type !== "publishable") {
|
||||
throw new MedusaError(
|
||||
MedusaError.Types.INVALID_DATA,
|
||||
"Sales channels can only be associated with publishable API keys"
|
||||
)
|
||||
}
|
||||
|
||||
const workflowInput = {
|
||||
data: [
|
||||
{
|
||||
api_key_id: req.params.id,
|
||||
sales_channel_ids: body.sales_channel_ids,
|
||||
},
|
||||
],
|
||||
}
|
||||
|
||||
const { errors } = await addSalesChannelsToApiKeyWorkflow(req.scope).run({
|
||||
input: workflowInput,
|
||||
throwOnError: false,
|
||||
})
|
||||
|
||||
if (Array.isArray(errors) && errors[0]) {
|
||||
throw errors[0].error
|
||||
}
|
||||
|
||||
const query = remoteQueryObjectFromString({
|
||||
entryPoint: "api_key",
|
||||
fields: req.remoteQueryConfig.fields,
|
||||
variables: {
|
||||
id: req.params.id,
|
||||
},
|
||||
})
|
||||
|
||||
const remoteQuery = req.scope.resolve(ContainerRegistrationKeys.REMOTE_QUERY)
|
||||
|
||||
const [result] = await remoteQuery(query)
|
||||
|
||||
res.status(200).json({ api_key: result })
|
||||
}
|
||||
@@ -1,13 +1,14 @@
|
||||
import * as QueryConfig from "./query-config"
|
||||
|
||||
import { transformBody, transformQuery } from "../../../api/middlewares"
|
||||
import {
|
||||
AdminGetApiKeysApiKeyParams,
|
||||
AdminGetApiKeysParams,
|
||||
AdminPostApiKeysApiKeyReq,
|
||||
AdminPostApiKeysApiKeySalesChannelsBatchReq,
|
||||
AdminPostApiKeysReq,
|
||||
AdminRevokeApiKeysApiKeyReq,
|
||||
} from "./validators"
|
||||
import { transformBody, transformQuery } from "../../../api/middlewares"
|
||||
|
||||
import { MiddlewareRoute } from "../../../loaders/helpers/routing/types"
|
||||
import { authenticate } from "../../../utils/authenticate-middleware"
|
||||
@@ -40,12 +41,24 @@ export const adminApiKeyRoutesMiddlewares: MiddlewareRoute[] = [
|
||||
{
|
||||
method: ["POST"],
|
||||
matcher: "/admin/api-keys",
|
||||
middlewares: [transformBody(AdminPostApiKeysReq)],
|
||||
middlewares: [
|
||||
transformQuery(
|
||||
AdminGetApiKeysApiKeyParams,
|
||||
QueryConfig.retrieveTransformQueryConfig
|
||||
),
|
||||
transformBody(AdminPostApiKeysReq),
|
||||
],
|
||||
},
|
||||
{
|
||||
method: ["POST"],
|
||||
matcher: "/admin/api-keys/:id",
|
||||
middlewares: [transformBody(AdminPostApiKeysApiKeyReq)],
|
||||
middlewares: [
|
||||
transformQuery(
|
||||
AdminGetApiKeysApiKeyParams,
|
||||
QueryConfig.retrieveTransformQueryConfig
|
||||
),
|
||||
transformBody(AdminPostApiKeysApiKeyReq),
|
||||
],
|
||||
},
|
||||
{
|
||||
method: ["DELETE"],
|
||||
@@ -55,6 +68,23 @@ export const adminApiKeyRoutesMiddlewares: MiddlewareRoute[] = [
|
||||
{
|
||||
method: ["POST"],
|
||||
matcher: "/admin/api-keys/:id/revoke",
|
||||
middlewares: [transformBody(AdminRevokeApiKeysApiKeyReq)],
|
||||
middlewares: [
|
||||
transformQuery(
|
||||
AdminGetApiKeysApiKeyParams,
|
||||
QueryConfig.retrieveTransformQueryConfig
|
||||
),
|
||||
transformBody(AdminRevokeApiKeysApiKeyReq),
|
||||
],
|
||||
},
|
||||
{
|
||||
method: ["POST"],
|
||||
matcher: "/admin/api-keys/:id/sales-channels/batch/add",
|
||||
middlewares: [
|
||||
transformQuery(
|
||||
AdminGetApiKeysApiKeyParams,
|
||||
QueryConfig.retrieveTransformQueryConfig
|
||||
),
|
||||
transformBody(AdminPostApiKeysApiKeySalesChannelsBatchReq),
|
||||
],
|
||||
},
|
||||
]
|
||||
|
||||
@@ -1,5 +1,3 @@
|
||||
export const defaultAdminApiKeyRelations = []
|
||||
export const allowedAdminApiKeyRelations = []
|
||||
export const defaultAdminApiKeyFields = [
|
||||
"id",
|
||||
"title",
|
||||
@@ -11,16 +9,17 @@ export const defaultAdminApiKeyFields = [
|
||||
"created_by",
|
||||
"revoked_at",
|
||||
"revoked_by",
|
||||
"sales_channels.id",
|
||||
"sales_channels.name",
|
||||
]
|
||||
|
||||
export const retrieveTransformQueryConfig = {
|
||||
defaultFields: defaultAdminApiKeyFields,
|
||||
defaultRelations: defaultAdminApiKeyRelations,
|
||||
allowedRelations: allowedAdminApiKeyRelations,
|
||||
defaults: defaultAdminApiKeyFields,
|
||||
isList: false,
|
||||
}
|
||||
|
||||
export const listTransformQueryConfig = {
|
||||
...retrieveTransformQueryConfig,
|
||||
defaultLimit: 20,
|
||||
isList: true,
|
||||
}
|
||||
|
||||
@@ -1,34 +1,33 @@
|
||||
import { createApiKeysWorkflow } from "@medusajs/core-flows"
|
||||
import { CreateApiKeyDTO } from "@medusajs/types"
|
||||
import {
|
||||
ContainerRegistrationKeys,
|
||||
remoteQueryObjectFromString,
|
||||
} from "@medusajs/utils"
|
||||
import {
|
||||
AuthenticatedMedusaRequest,
|
||||
MedusaResponse,
|
||||
} from "../../../types/routing"
|
||||
|
||||
import { CreateApiKeyDTO } from "@medusajs/types"
|
||||
import { createApiKeysWorkflow } from "@medusajs/core-flows"
|
||||
import { defaultAdminApiKeyFields } from "./query-config"
|
||||
import { remoteQueryObjectFromString } from "@medusajs/utils"
|
||||
|
||||
export const GET = async (
|
||||
req: AuthenticatedMedusaRequest,
|
||||
res: MedusaResponse
|
||||
) => {
|
||||
const remoteQuery = req.scope.resolve("remoteQuery")
|
||||
const remoteQuery = req.scope.resolve(ContainerRegistrationKeys.REMOTE_QUERY)
|
||||
|
||||
const queryObject = remoteQueryObjectFromString({
|
||||
entryPoint: "api_key",
|
||||
variables: {
|
||||
filters: req.filterableFields,
|
||||
order: req.listConfig.order,
|
||||
skip: req.listConfig.skip,
|
||||
take: req.listConfig.take,
|
||||
...req.remoteQueryConfig.pagination,
|
||||
},
|
||||
fields: defaultAdminApiKeyFields,
|
||||
fields: req.remoteQueryConfig.fields,
|
||||
})
|
||||
|
||||
const { rows: apiKeys, metadata } = await remoteQuery(queryObject)
|
||||
|
||||
res.json({
|
||||
apiKeys,
|
||||
api_keys: apiKeys,
|
||||
count: metadata.count,
|
||||
offset: metadata.skip,
|
||||
limit: metadata.take,
|
||||
@@ -55,5 +54,7 @@ export const POST = async (
|
||||
throw errors[0].error
|
||||
}
|
||||
|
||||
res.status(200).json({ apiKey: result[0] })
|
||||
// We cannot use remoteQuery here, as we need to show the secret key in the response (and never again)
|
||||
// And the only time we get to see the secret, is when we create it
|
||||
res.status(200).json({ api_key: result[0] })
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { OperatorMap } from "@medusajs/types"
|
||||
import { ApiKeyType } from "@medusajs/utils"
|
||||
import { Type } from "class-transformer"
|
||||
import {
|
||||
IsArray,
|
||||
@@ -9,8 +9,6 @@ import {
|
||||
ValidateNested,
|
||||
} from "class-validator"
|
||||
import { FindParams, extendedFindParamsMixin } from "../../../types/common"
|
||||
import { OperatorMapValidator } from "../../../types/validators/operator-map"
|
||||
import { ApiKeyType } from "@medusajs/utils"
|
||||
|
||||
export class AdminGetApiKeysApiKeyParams extends FindParams {}
|
||||
/**
|
||||
@@ -80,3 +78,8 @@ export class AdminRevokeApiKeysApiKeyReq {
|
||||
}
|
||||
|
||||
export class AdminDeleteApiKeysApiKeyReq {}
|
||||
|
||||
export class AdminPostApiKeysApiKeySalesChannelsBatchReq {
|
||||
@IsArray()
|
||||
sales_channel_ids: string[]
|
||||
}
|
||||
|
||||
@@ -4,10 +4,9 @@ import {
|
||||
} from "@medusajs/utils"
|
||||
import { MedusaRequest, MedusaResponse } from "../../../../types/routing"
|
||||
|
||||
import { AdminPostStockLocationsLocationReq } from "../validators"
|
||||
import { deleteStockLocationsWorkflow, updateStockLocationsWorkflow } from "@medusajs/core-flows"
|
||||
import { MedusaError } from "@medusajs/utils"
|
||||
import { deleteStockLocationsWorkflow } from "@medusajs/core-flows"
|
||||
import { updateStockLocationsWorkflow } from "@medusajs/core-flows"
|
||||
import { AdminPostStockLocationsLocationReq } from "../validators"
|
||||
|
||||
export const POST = async (
|
||||
req: MedusaRequest<AdminPostStockLocationsLocationReq>,
|
||||
|
||||
@@ -1,3 +1 @@
|
||||
export * as publishableApiKey from "./publishable-api-key-service"
|
||||
export * as shippingProfile from "./shipping-profile-service"
|
||||
|
||||
|
||||
@@ -1,28 +0,0 @@
|
||||
import { ModuleJoinerConfig } from "@medusajs/types"
|
||||
|
||||
export default {
|
||||
serviceName: "publishableApiKeyService",
|
||||
primaryKeys: ["id"],
|
||||
linkableKeys: { publishable_key_id: "PublishableApiKey" },
|
||||
schema: `
|
||||
scalar Date
|
||||
scalar JSON
|
||||
|
||||
type PublishableApiKey {
|
||||
id: ID!
|
||||
sales_channel_id: String!
|
||||
publishable_key_id: String!
|
||||
created_at: Date!
|
||||
updated_at: Date!
|
||||
deleted_at: Date
|
||||
}
|
||||
`,
|
||||
alias: [
|
||||
{
|
||||
name: ["publishable_api_key", "publishable_api_keys"],
|
||||
args: {
|
||||
entity: "PublishableApiKey",
|
||||
},
|
||||
},
|
||||
],
|
||||
} as ModuleJoinerConfig
|
||||
Reference in New Issue
Block a user