feat(api-key): Add CRUD functionalities to the api key module
This commit is contained in:
@@ -1 +1,14 @@
|
||||
// noop
|
||||
import { CreateApiKeyDTO } from "@types"
|
||||
import { ApiKeyType } from "@medusajs/utils"
|
||||
|
||||
export const createSecretKeyFixture: CreateApiKeyDTO = {
|
||||
title: "Secret key",
|
||||
type: ApiKeyType.SECRET,
|
||||
created_by: "test",
|
||||
}
|
||||
|
||||
export const createPublishableKeyFixture: CreateApiKeyDTO = {
|
||||
title: "Test API Key",
|
||||
type: ApiKeyType.PUBLISHABLE,
|
||||
created_by: "test",
|
||||
}
|
||||
|
||||
@@ -1,19 +1,266 @@
|
||||
import crypto from "crypto"
|
||||
import { Modules } from "@medusajs/modules-sdk"
|
||||
import { IApiKeyModuleService } from "@medusajs/types"
|
||||
import { ApiKeyType } from "@medusajs/utils"
|
||||
import { moduleIntegrationTestRunner, SuiteOptions } from "medusa-test-utils"
|
||||
import {
|
||||
createSecretKeyFixture,
|
||||
createPublishableKeyFixture,
|
||||
} from "../__fixtures__"
|
||||
|
||||
jest.setTimeout(100000)
|
||||
|
||||
const mockPublishableKeyBytes = () => {
|
||||
jest.spyOn(crypto, "randomBytes").mockImplementationOnce(() => {
|
||||
return Buffer.from(
|
||||
"44de31ebcf085fa423fc584aa854067025e937a79edb565f472404345f0f23be",
|
||||
"hex"
|
||||
)
|
||||
})
|
||||
}
|
||||
|
||||
const mockSecretKeyBytes = () => {
|
||||
jest
|
||||
.spyOn(crypto, "randomBytes")
|
||||
.mockImplementationOnce(() => {
|
||||
return Buffer.from(
|
||||
"44de31ebcf085fa423fc584aa854067025e937a79edb565f472404345f0f23be",
|
||||
"hex"
|
||||
)
|
||||
})
|
||||
.mockImplementationOnce(() => {
|
||||
return Buffer.from("44de31ebcf085fa423fc584aa8540670", "hex")
|
||||
})
|
||||
}
|
||||
|
||||
moduleIntegrationTestRunner({
|
||||
moduleName: Modules.API_KEY,
|
||||
testSuite: ({
|
||||
MikroOrmWrapper,
|
||||
service,
|
||||
}: SuiteOptions<IApiKeyModuleService>) => {
|
||||
afterEach(() => {
|
||||
jest.restoreAllMocks()
|
||||
})
|
||||
|
||||
describe("API Key Module Service", () => {
|
||||
describe("noop", () => {
|
||||
it("should run", function () {
|
||||
expect(true).toBe(true)
|
||||
describe("creating a publishable API key", () => {
|
||||
it("should create it successfully", async function () {
|
||||
mockPublishableKeyBytes()
|
||||
const apiKey = await service.create(createPublishableKeyFixture)
|
||||
|
||||
expect(apiKey).toEqual(
|
||||
expect.objectContaining({
|
||||
title: "Test API Key",
|
||||
type: ApiKeyType.PUBLISHABLE,
|
||||
salt: "",
|
||||
created_by: "test",
|
||||
last_used_at: null,
|
||||
revoked_by: null,
|
||||
revoked_at: null,
|
||||
redacted: "pk_44d***3be",
|
||||
token:
|
||||
"pk_44de31ebcf085fa423fc584aa854067025e937a79edb565f472404345f0f23be",
|
||||
})
|
||||
)
|
||||
})
|
||||
})
|
||||
|
||||
describe("creating a secret API key", () => {
|
||||
it("should get created successfully", async function () {
|
||||
mockSecretKeyBytes()
|
||||
const apiKey = await service.create(createSecretKeyFixture)
|
||||
|
||||
expect(apiKey).toEqual(
|
||||
expect.objectContaining({
|
||||
title: "Secret key",
|
||||
type: ApiKeyType.SECRET,
|
||||
salt: "44de31ebcf085fa423fc584aa8540670",
|
||||
created_by: "test",
|
||||
last_used_at: null,
|
||||
revoked_by: null,
|
||||
revoked_at: null,
|
||||
redacted: "sk_44d***3be",
|
||||
token:
|
||||
"sk_44de31ebcf085fa423fc584aa854067025e937a79edb565f472404345f0f23be",
|
||||
})
|
||||
)
|
||||
})
|
||||
|
||||
it("should only allow creating one active token", async function () {
|
||||
expect(
|
||||
service.create([createSecretKeyFixture, createSecretKeyFixture])
|
||||
).rejects.toThrow(
|
||||
"You can only create one secret key at a time. You tried to create 2 secret keys."
|
||||
)
|
||||
|
||||
await service.create(createSecretKeyFixture)
|
||||
const err = await service
|
||||
.create(createSecretKeyFixture)
|
||||
.catch((e) => e)
|
||||
expect(err.message).toEqual(
|
||||
"You can only have one active secret key a time. Revoke or delete your existing key before creating a new one."
|
||||
)
|
||||
})
|
||||
|
||||
it("should allow for at most two tokens, where one is revoked", async function () {
|
||||
const firstApiKey = await service.create(createSecretKeyFixture)
|
||||
await service.revoke({
|
||||
id: firstApiKey.id,
|
||||
revoked_by: "test",
|
||||
})
|
||||
|
||||
await service.create(createSecretKeyFixture)
|
||||
const err = await service
|
||||
.create(createSecretKeyFixture)
|
||||
.catch((e) => e)
|
||||
expect(err.message).toEqual(
|
||||
"You can only have one active secret key a time. Revoke or delete your existing key before creating a new one."
|
||||
)
|
||||
})
|
||||
})
|
||||
|
||||
describe("revoking API keys", () => {
|
||||
it("should have the revoked at and revoked by set when a key is revoked", async function () {
|
||||
const firstApiKey = await service.create(createSecretKeyFixture)
|
||||
const revokedKey = await service.revoke({
|
||||
id: firstApiKey.id,
|
||||
revoked_by: "test",
|
||||
})
|
||||
|
||||
expect(revokedKey).toEqual(
|
||||
expect.objectContaining({
|
||||
revoked_by: "test",
|
||||
revoked_at: expect.any(Date),
|
||||
})
|
||||
)
|
||||
})
|
||||
|
||||
it("should not allow revoking an already revoked API key", async function () {
|
||||
const firstApiKey = await service.create(createSecretKeyFixture)
|
||||
await service.revoke({
|
||||
id: firstApiKey.id,
|
||||
revoked_by: "test",
|
||||
})
|
||||
|
||||
const err = await service
|
||||
.revoke({
|
||||
id: firstApiKey.id,
|
||||
revoked_by: "test2",
|
||||
})
|
||||
.catch((e) => e)
|
||||
|
||||
expect(err.message).toEqual(
|
||||
`There are 1 secret keys that are already revoked.`
|
||||
)
|
||||
})
|
||||
})
|
||||
|
||||
describe("updating an API key", () => {
|
||||
it("should update the name successfully", async function () {
|
||||
const createdApiKey = await service.create(createSecretKeyFixture)
|
||||
|
||||
const updatedApiKey = await service.update({
|
||||
id: createdApiKey.id,
|
||||
title: "New Name",
|
||||
})
|
||||
expect(updatedApiKey.title).toEqual("New Name")
|
||||
})
|
||||
|
||||
it("should not reflect any updates on other fields", async function () {
|
||||
const createdApiKey = await service.create(createSecretKeyFixture)
|
||||
|
||||
const updatedApiKey = await service.update({
|
||||
id: createdApiKey.id,
|
||||
title: createdApiKey.title,
|
||||
revoked_by: "test",
|
||||
revoked_at: new Date(),
|
||||
last_used_at: new Date(),
|
||||
})
|
||||
|
||||
// These should not be returned on an update
|
||||
createdApiKey.token = ""
|
||||
createdApiKey.salt = ""
|
||||
expect(createdApiKey).toEqual(updatedApiKey)
|
||||
})
|
||||
})
|
||||
|
||||
describe("deleting API keys", () => {
|
||||
it("should successfully delete existing api keys", async function () {
|
||||
const createdApiKeys = await service.create([
|
||||
createPublishableKeyFixture,
|
||||
createSecretKeyFixture,
|
||||
])
|
||||
await service.delete([createdApiKeys[0].id, createdApiKeys[1].id])
|
||||
|
||||
const apiKeysInDatabase = await service.list()
|
||||
expect(apiKeysInDatabase).toHaveLength(0)
|
||||
})
|
||||
})
|
||||
|
||||
describe("retrieving API keys", () => {
|
||||
it("should successfully return all existing api keys", async function () {
|
||||
await service.create([
|
||||
createPublishableKeyFixture,
|
||||
createSecretKeyFixture,
|
||||
])
|
||||
|
||||
const apiKeysInDatabase = await service.list()
|
||||
expect(apiKeysInDatabase).toHaveLength(2)
|
||||
})
|
||||
|
||||
it("should not return the token and salt for secret keys when listing", async function () {
|
||||
await service.create([createSecretKeyFixture])
|
||||
|
||||
const apiKeysInDatabase = await service.list()
|
||||
expect(apiKeysInDatabase).toHaveLength(1)
|
||||
expect(apiKeysInDatabase[0].token).toBeFalsy()
|
||||
expect(apiKeysInDatabase[0].salt).toBeFalsy()
|
||||
})
|
||||
|
||||
it("should return the token for publishable keys when listing", async function () {
|
||||
await service.create([createPublishableKeyFixture])
|
||||
|
||||
const apiKeysInDatabase = await service.list()
|
||||
expect(apiKeysInDatabase).toHaveLength(1)
|
||||
expect(apiKeysInDatabase[0].token).toBeTruthy()
|
||||
expect(apiKeysInDatabase[0].salt).toBeFalsy()
|
||||
})
|
||||
|
||||
it("should not return the token and salt for secret keys when listing and counting", async function () {
|
||||
await service.create([createSecretKeyFixture])
|
||||
|
||||
const [apiKeysInDatabase] = await service.listAndCount()
|
||||
expect(apiKeysInDatabase).toHaveLength(1)
|
||||
expect(apiKeysInDatabase[0].token).toBeFalsy()
|
||||
expect(apiKeysInDatabase[0].salt).toBeFalsy()
|
||||
})
|
||||
|
||||
it("should return the token for publishable keys when listing and counting", async function () {
|
||||
await service.create([createPublishableKeyFixture])
|
||||
|
||||
const [apiKeysInDatabase] = await service.listAndCount()
|
||||
expect(apiKeysInDatabase).toHaveLength(1)
|
||||
expect(apiKeysInDatabase[0].token).toBeTruthy()
|
||||
expect(apiKeysInDatabase[0].salt).toBeFalsy()
|
||||
})
|
||||
|
||||
it("should not return the token and salt for secret keys when retrieving", async function () {
|
||||
const [createdApiKey] = await service.create([createSecretKeyFixture])
|
||||
|
||||
const apiKeyInDatabase = await service.retrieve(createdApiKey.id)
|
||||
expect(apiKeyInDatabase.token).toBeFalsy()
|
||||
expect(apiKeyInDatabase.salt).toBeFalsy()
|
||||
})
|
||||
|
||||
it("should return the token for publishable keys when retrieving", async function () {
|
||||
const [createdApiKey] = await service.create([
|
||||
createPublishableKeyFixture,
|
||||
])
|
||||
|
||||
const apiKeyInDatabase = await service.retrieve(createdApiKey.id)
|
||||
expect(apiKeyInDatabase.token).toBeTruthy()
|
||||
expect(apiKeyInDatabase.salt).toBeFalsy()
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
Reference in New Issue
Block a user