feat(medusa): Convert IdempotencyKeyService to TypeScript (#1995)
* feat(medusa): Migrate the idempotency key service to ts + fix * feat(medusa): Finalise idempotency migration * Create late-owls-pump.md * feat(medusa): Polish * feat(medusa): Add case to the error handler * feat(medusa): Add case to the error handler Co-authored-by: olivermrbl <oliver@mrbltech.com>
This commit is contained in:
1 parent
4b663cca3a
commit
aaebb38eae
11 files changed
+226
-186
No files matched your search
@@ -0,0 +1,6 @@
|
|||||||
|
---
|
||||||
|
"@medusajs/medusa": patch
|
||||||
|
---
|
||||||
|
|
||||||
|
Convert IdempotencyKeyService to TypeScript
|
||||||
|
Add await to retrieve in lock method
|
||||||
@@ -11,6 +11,7 @@ export const MedusaErrorTypes = {
|
|||||||
NOT_FOUND: "not_found",
|
NOT_FOUND: "not_found",
|
||||||
NOT_ALLOWED: "not_allowed",
|
NOT_ALLOWED: "not_allowed",
|
||||||
UNEXPECTED_STATE: "unexpected_state",
|
UNEXPECTED_STATE: "unexpected_state",
|
||||||
|
CONFLICT: "conflict",
|
||||||
}
|
}
|
||||||
|
|
||||||
export const MedusaErrorCodes = {
|
export const MedusaErrorCodes = {
|
||||||
|
|||||||
@@ -33,6 +33,7 @@ export default () => {
|
|||||||
case QUERY_RUNNER_RELEASED:
|
case QUERY_RUNNER_RELEASED:
|
||||||
case TRANSACTION_STARTED:
|
case TRANSACTION_STARTED:
|
||||||
case TRANSACTION_NOT_STARTED:
|
case TRANSACTION_NOT_STARTED:
|
||||||
|
case MedusaError.Types.CONFLICT:
|
||||||
statusCode = 409
|
statusCode = 409
|
||||||
errObj.code = INVALID_STATE_ERROR
|
errObj.code = INVALID_STATE_ERROR
|
||||||
errObj.message =
|
errObj.message =
|
||||||
|
|||||||
@@ -148,7 +148,7 @@ export default async (req, res) => {
|
|||||||
res.setHeader("Idempotency-Key", idempotencyKey.idempotency_key)
|
res.setHeader("Idempotency-Key", idempotencyKey.idempotency_key)
|
||||||
|
|
||||||
let inProgress = true
|
let inProgress = true
|
||||||
let err = false
|
let err: unknown = false
|
||||||
|
|
||||||
while (inProgress) {
|
while (inProgress) {
|
||||||
switch (idempotencyKey.recovery_point) {
|
switch (idempotencyKey.recovery_point) {
|
||||||
|
|||||||
@@ -34,7 +34,8 @@ export default async (req, res) => {
|
|||||||
|
|
||||||
const headerKey = req.get("Idempotency-Key") || ""
|
const headerKey = req.get("Idempotency-Key") || ""
|
||||||
|
|
||||||
let idempotencyKey!: IdempotencyKey
|
let idempotencyKey
|
||||||
|
|
||||||
try {
|
try {
|
||||||
await manager.transaction(async (transactionManager) => {
|
await manager.transaction(async (transactionManager) => {
|
||||||
idempotencyKey = await idempotencyKeyService
|
idempotencyKey = await idempotencyKeyService
|
||||||
@@ -58,7 +59,7 @@ export default async (req, res) => {
|
|||||||
const cartService: CartService = req.scope.resolve("cartService")
|
const cartService: CartService = req.scope.resolve("cartService")
|
||||||
|
|
||||||
let inProgress = true
|
let inProgress = true
|
||||||
let err = false
|
let err: unknown = false
|
||||||
|
|
||||||
while (inProgress) {
|
while (inProgress) {
|
||||||
switch (idempotencyKey.recovery_point) {
|
switch (idempotencyKey.recovery_point) {
|
||||||
|
|||||||
@@ -108,7 +108,7 @@ export default async (req, res) => {
|
|||||||
const eventBus: EventBusService = req.scope.resolve("eventBusService")
|
const eventBus: EventBusService = req.scope.resolve("eventBusService")
|
||||||
|
|
||||||
let inProgress = true
|
let inProgress = true
|
||||||
let err = false
|
let err: unknown = false
|
||||||
|
|
||||||
while (inProgress) {
|
while (inProgress) {
|
||||||
switch (idempotencyKey.recovery_point) {
|
switch (idempotencyKey.recovery_point) {
|
||||||
|
|||||||
@@ -114,7 +114,7 @@ export default async (req, res) => {
|
|||||||
const returnService: ReturnService = req.scope.resolve("returnService")
|
const returnService: ReturnService = req.scope.resolve("returnService")
|
||||||
|
|
||||||
let inProgress = true
|
let inProgress = true
|
||||||
let err = false
|
let err: unknown = false
|
||||||
|
|
||||||
while (inProgress) {
|
while (inProgress) {
|
||||||
switch (idempotencyKey.recovery_point) {
|
switch (idempotencyKey.recovery_point) {
|
||||||
|
|||||||
@@ -1,173 +0,0 @@
|
|||||||
import { MedusaError } from "medusa-core-utils"
|
|
||||||
import { v4 } from "uuid"
|
|
||||||
import { TransactionBaseService } from "../interfaces"
|
|
||||||
|
|
||||||
const KEY_LOCKED_TIMEOUT = 1000
|
|
||||||
|
|
||||||
class IdempotencyKeyService extends TransactionBaseService {
|
|
||||||
constructor({ manager, idempotencyKeyRepository }) {
|
|
||||||
super({ manager, idempotencyKeyRepository })
|
|
||||||
|
|
||||||
/** @private @constant {EntityManager} */
|
|
||||||
this.manager_ = manager
|
|
||||||
|
|
||||||
/** @private @constant {IdempotencyKeyRepository} */
|
|
||||||
this.idempotencyKeyRepository_ = idempotencyKeyRepository
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Execute the initial steps in a idempotent request.
|
|
||||||
* @param {string} headerKey - potential idempotency key from header
|
|
||||||
* @param {string} reqMethod - method of request
|
|
||||||
* @param {string} reqParams - params of request
|
|
||||||
* @param {string} reqPath - path of request
|
|
||||||
* @return {Promise<IdempotencyKeyModel>} the existing or created idempotency key
|
|
||||||
*/
|
|
||||||
async initializeRequest(headerKey, reqMethod, reqParams, reqPath) {
|
|
||||||
return this.atomicPhase_(async (_) => {
|
|
||||||
// If idempotency key exists, return it
|
|
||||||
let key = await this.retrieve(headerKey)
|
|
||||||
|
|
||||||
if (key) {
|
|
||||||
return key
|
|
||||||
}
|
|
||||||
|
|
||||||
key = await this.create({
|
|
||||||
request_method: reqMethod,
|
|
||||||
request_params: reqParams,
|
|
||||||
request_path: reqPath,
|
|
||||||
})
|
|
||||||
|
|
||||||
return key
|
|
||||||
}, "SERIALIZABLE")
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Creates an idempotency key for a request.
|
|
||||||
* If no idempotency key is provided in request, we will create a unique
|
|
||||||
* identifier.
|
|
||||||
* @param {object} payload - payload of request to create idempotency key for
|
|
||||||
* @return {Promise<IdempotencyKeyModel>} the created idempotency key
|
|
||||||
*/
|
|
||||||
async create(payload) {
|
|
||||||
return this.atomicPhase_(async (manager) => {
|
|
||||||
const idempotencyKeyRepo = manager.getCustomRepository(
|
|
||||||
this.idempotencyKeyRepository_
|
|
||||||
)
|
|
||||||
|
|
||||||
if (!payload.idempotency_key) {
|
|
||||||
payload.idempotency_key = v4()
|
|
||||||
}
|
|
||||||
|
|
||||||
const created = await idempotencyKeyRepo.create(payload)
|
|
||||||
const result = await idempotencyKeyRepo.save(created)
|
|
||||||
return result
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Retrieves an idempotency key
|
|
||||||
* @param {string} idempotencyKey - key to retrieve
|
|
||||||
* @return {Promise<IdempotencyKeyModel>} idempotency key
|
|
||||||
*/
|
|
||||||
async retrieve(idempotencyKey) {
|
|
||||||
const idempotencyKeyRepo = this.manager_.getCustomRepository(
|
|
||||||
this.idempotencyKeyRepository_
|
|
||||||
)
|
|
||||||
|
|
||||||
const key = await idempotencyKeyRepo.findOne({
|
|
||||||
where: { idempotency_key: idempotencyKey },
|
|
||||||
})
|
|
||||||
|
|
||||||
return key
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Locks an idempotency.
|
|
||||||
* @param {string} idempotencyKey - key to lock
|
|
||||||
* @return {Promise} result of the update operation
|
|
||||||
*/
|
|
||||||
async lock(idempotencyKey) {
|
|
||||||
return this.atomicPhase_(async (manager) => {
|
|
||||||
const idempotencyKeyRepo = manager.getCustomRepository(
|
|
||||||
this.idempotencyKeyRepository_
|
|
||||||
)
|
|
||||||
|
|
||||||
const key = this.retrieve(idempotencyKey)
|
|
||||||
|
|
||||||
if (key.locked_at && key.locked_at > Date.now() - KEY_LOCKED_TIMEOUT) {
|
|
||||||
throw new MedusaError("conflict", "Key already locked")
|
|
||||||
}
|
|
||||||
|
|
||||||
const updated = await idempotencyKeyRepo.save({
|
|
||||||
...key,
|
|
||||||
locked_at: Date.now(),
|
|
||||||
})
|
|
||||||
|
|
||||||
return updated
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Locks an idempotency.
|
|
||||||
* @param {string} idempotencyKey - key to update
|
|
||||||
* @param {object} update - update object
|
|
||||||
* @return {Promise} result of the update operation
|
|
||||||
*/
|
|
||||||
async update(idempotencyKey, update) {
|
|
||||||
return this.atomicPhase_(async (manager) => {
|
|
||||||
const idempotencyKeyRepo = manager.getCustomRepository(
|
|
||||||
this.idempotencyKeyRepository_
|
|
||||||
)
|
|
||||||
|
|
||||||
const iKey = await this.retrieve(idempotencyKey)
|
|
||||||
|
|
||||||
for (const [key, value] of Object.entries(update)) {
|
|
||||||
iKey[key] = value
|
|
||||||
}
|
|
||||||
|
|
||||||
const updated = await idempotencyKeyRepo.save(iKey)
|
|
||||||
return updated
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Performs an atomic work stage.
|
|
||||||
* An atomic work stage contains some related functionality, that needs to be
|
|
||||||
* transactionally executed in isolation. An idempotent request will
|
|
||||||
* always consist of 2 or more of these phases. The required phases are
|
|
||||||
* "started" and "finished".
|
|
||||||
* @param {string} idempotencyKey - current idempotency key
|
|
||||||
* @param {Function} func - functionality to execute within the phase
|
|
||||||
* @return {IdempotencyKeyModel} new updated idempotency key
|
|
||||||
*/
|
|
||||||
async workStage(idempotencyKey, func) {
|
|
||||||
try {
|
|
||||||
return await this.atomicPhase_(async (manager) => {
|
|
||||||
let key
|
|
||||||
|
|
||||||
const { recovery_point, response_code, response_body } = await func(
|
|
||||||
manager
|
|
||||||
)
|
|
||||||
|
|
||||||
if (recovery_point) {
|
|
||||||
key = await this.update(idempotencyKey, {
|
|
||||||
recovery_point,
|
|
||||||
})
|
|
||||||
} else {
|
|
||||||
key = await this.update(idempotencyKey, {
|
|
||||||
recovery_point: "finished",
|
|
||||||
response_body,
|
|
||||||
response_code,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
return { key }
|
|
||||||
}, "SERIALIZABLE")
|
|
||||||
} catch (err) {
|
|
||||||
return { error: err }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export default IdempotencyKeyService
|
|
||||||
@@ -0,0 +1,198 @@
|
|||||||
|
import { MedusaError } from "medusa-core-utils"
|
||||||
|
import { v4 } from "uuid"
|
||||||
|
import { TransactionBaseService } from "../interfaces"
|
||||||
|
import { DeepPartial, EntityManager } from "typeorm"
|
||||||
|
import { IdempotencyKeyRepository } from "../repositories/idempotency-key"
|
||||||
|
import { IdempotencyKey } from "../models"
|
||||||
|
import { CreateIdempotencyKeyInput } from "../types/idempotency-key"
|
||||||
|
|
||||||
|
const KEY_LOCKED_TIMEOUT = 1000
|
||||||
|
|
||||||
|
type InjectedDependencies = {
|
||||||
|
manager: EntityManager
|
||||||
|
idempotencyKeyRepository: typeof IdempotencyKeyRepository
|
||||||
|
}
|
||||||
|
|
||||||
|
class IdempotencyKeyService extends TransactionBaseService<IdempotencyKeyService> {
|
||||||
|
protected manager_: EntityManager
|
||||||
|
protected transactionManager_: EntityManager | undefined
|
||||||
|
|
||||||
|
protected readonly idempotencyKeyRepository_: typeof IdempotencyKeyRepository
|
||||||
|
|
||||||
|
constructor({ manager, idempotencyKeyRepository }: InjectedDependencies) {
|
||||||
|
super({ manager, idempotencyKeyRepository })
|
||||||
|
|
||||||
|
this.manager_ = manager
|
||||||
|
this.idempotencyKeyRepository_ = idempotencyKeyRepository
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Execute the initial steps in a idempotent request.
|
||||||
|
* @param headerKey - potential idempotency key from header
|
||||||
|
* @param reqMethod - method of request
|
||||||
|
* @param reqParams - params of request
|
||||||
|
* @param reqPath - path of request
|
||||||
|
* @return the existing or created idempotency key
|
||||||
|
*/
|
||||||
|
async initializeRequest(
|
||||||
|
headerKey: string,
|
||||||
|
reqMethod: string,
|
||||||
|
reqParams: Record<string, unknown>,
|
||||||
|
reqPath: string
|
||||||
|
): Promise<IdempotencyKey> {
|
||||||
|
return await this.atomicPhase_(async () => {
|
||||||
|
const key = await this.retrieve(headerKey).catch(() => void 0)
|
||||||
|
if (key) {
|
||||||
|
return key
|
||||||
|
}
|
||||||
|
return await this.create({
|
||||||
|
request_method: reqMethod,
|
||||||
|
request_params: reqParams,
|
||||||
|
request_path: reqPath,
|
||||||
|
})
|
||||||
|
}, "SERIALIZABLE")
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates an idempotency key for a request.
|
||||||
|
* If no idempotency key is provided in request, we will create a unique
|
||||||
|
* identifier.
|
||||||
|
* @param payload - payload of request to create idempotency key for
|
||||||
|
* @return the created idempotency key
|
||||||
|
*/
|
||||||
|
async create(payload: CreateIdempotencyKeyInput): Promise<IdempotencyKey> {
|
||||||
|
return await this.atomicPhase_(async (manager) => {
|
||||||
|
const idempotencyKeyRepo = manager.getCustomRepository(
|
||||||
|
this.idempotencyKeyRepository_
|
||||||
|
)
|
||||||
|
|
||||||
|
payload.idempotency_key = payload.idempotency_key ?? v4()
|
||||||
|
|
||||||
|
const created = idempotencyKeyRepo.create(payload)
|
||||||
|
return await idempotencyKeyRepo.save(created)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieves an idempotency key
|
||||||
|
* @param idempotencyKey - key to retrieve
|
||||||
|
* @return idempotency key
|
||||||
|
*/
|
||||||
|
async retrieve(idempotencyKey: string): Promise<IdempotencyKey | never> {
|
||||||
|
const idempotencyKeyRepo = this.manager_.getCustomRepository(
|
||||||
|
this.idempotencyKeyRepository_
|
||||||
|
)
|
||||||
|
|
||||||
|
const iKey = await idempotencyKeyRepo.findOne({
|
||||||
|
where: { idempotency_key: idempotencyKey },
|
||||||
|
})
|
||||||
|
|
||||||
|
if (!iKey) {
|
||||||
|
throw new MedusaError(
|
||||||
|
MedusaError.Types.NOT_FOUND,
|
||||||
|
`Idempotency key ${idempotencyKey} was not found`
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
return iKey
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Locks an idempotency.
|
||||||
|
* @param idempotencyKey - key to lock
|
||||||
|
* @return result of the update operation
|
||||||
|
*/
|
||||||
|
async lock(idempotencyKey: string): Promise<IdempotencyKey | never> {
|
||||||
|
return await this.atomicPhase_(async (manager) => {
|
||||||
|
const idempotencyKeyRepo = manager.getCustomRepository(
|
||||||
|
this.idempotencyKeyRepository_
|
||||||
|
)
|
||||||
|
|
||||||
|
const key = await this.retrieve(idempotencyKey)
|
||||||
|
|
||||||
|
const isLocked =
|
||||||
|
key.locked_at &&
|
||||||
|
new Date(key.locked_at).getTime() > Date.now() - KEY_LOCKED_TIMEOUT
|
||||||
|
|
||||||
|
if (isLocked) {
|
||||||
|
throw new MedusaError(MedusaError.Types.CONFLICT, "Key already locked")
|
||||||
|
}
|
||||||
|
|
||||||
|
return await idempotencyKeyRepo.save({
|
||||||
|
...key,
|
||||||
|
locked_at: Date.now(),
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Locks an idempotency.
|
||||||
|
* @param {string} idempotencyKey - key to update
|
||||||
|
* @param {object} update - update object
|
||||||
|
* @return {Promise} result of the update operation
|
||||||
|
*/
|
||||||
|
async update(
|
||||||
|
idempotencyKey: string,
|
||||||
|
update: DeepPartial<IdempotencyKey>
|
||||||
|
): Promise<IdempotencyKey> {
|
||||||
|
return await this.atomicPhase_(async (manager) => {
|
||||||
|
const idempotencyKeyRepo = manager.getCustomRepository(
|
||||||
|
this.idempotencyKeyRepository_
|
||||||
|
)
|
||||||
|
|
||||||
|
const iKey = await this.retrieve(idempotencyKey)
|
||||||
|
|
||||||
|
for (const [key, value] of Object.entries(update)) {
|
||||||
|
iKey[key] = value
|
||||||
|
}
|
||||||
|
|
||||||
|
return await idempotencyKeyRepo.save(iKey)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Performs an atomic work stage.
|
||||||
|
* An atomic work stage contains some related functionality, that needs to be
|
||||||
|
* transactionally executed in isolation. An idempotent request will
|
||||||
|
* always consist of 2 or more of these phases. The required phases are
|
||||||
|
* "started" and "finished".
|
||||||
|
* @param idempotencyKey - current idempotency key
|
||||||
|
* @param callback - functionality to execute within the phase
|
||||||
|
* @return new updated idempotency key
|
||||||
|
*/
|
||||||
|
async workStage(
|
||||||
|
idempotencyKey: string,
|
||||||
|
callback: (transactionManager: EntityManager) => Promise<
|
||||||
|
| {
|
||||||
|
recovery_point?: string
|
||||||
|
response_code?: number
|
||||||
|
response_body?: Record<string, unknown>
|
||||||
|
}
|
||||||
|
| never
|
||||||
|
>
|
||||||
|
): Promise<{ key?: IdempotencyKey; error?: unknown }> {
|
||||||
|
try {
|
||||||
|
return await this.atomicPhase_(async (manager) => {
|
||||||
|
const { recovery_point, response_code, response_body } = await callback(
|
||||||
|
manager
|
||||||
|
)
|
||||||
|
|
||||||
|
const data: DeepPartial<IdempotencyKey> = {
|
||||||
|
recovery_point: recovery_point ?? "finished",
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!recovery_point) {
|
||||||
|
data.response_body = response_body
|
||||||
|
data.response_code = response_code
|
||||||
|
}
|
||||||
|
|
||||||
|
const key = await this.update(idempotencyKey, data)
|
||||||
|
return { key }
|
||||||
|
}, "SERIALIZABLE")
|
||||||
|
} catch (err) {
|
||||||
|
return { error: err }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default IdempotencyKeyService
|
||||||
@@ -1,16 +1,16 @@
|
|||||||
import { EntityManager } from "typeorm"
|
|
||||||
import { MedusaError } from "medusa-core-utils"
|
import { MedusaError } from "medusa-core-utils"
|
||||||
|
import { EntityManager } from "typeorm"
|
||||||
|
|
||||||
import { IdempotencyKey, Order } from "../models"
|
import { IdempotencyKey, Order } from "../models"
|
||||||
import CartService from "../services/cart"
|
import CartService from "../services/cart"
|
||||||
import { RequestContext } from "../types/request"
|
|
||||||
import OrderService from "../services/order"
|
|
||||||
import IdempotencyKeyService from "../services/idempotency-key"
|
import IdempotencyKeyService from "../services/idempotency-key"
|
||||||
|
import OrderService from "../services/order"
|
||||||
import SwapService from "../services/swap"
|
import SwapService from "../services/swap"
|
||||||
|
import { RequestContext } from "../types/request"
|
||||||
|
|
||||||
import {
|
import {
|
||||||
CartCompletionResponse,
|
|
||||||
AbstractCartCompletionStrategy,
|
AbstractCartCompletionStrategy,
|
||||||
|
CartCompletionResponse,
|
||||||
} from "../interfaces"
|
} from "../interfaces"
|
||||||
|
|
||||||
type InjectedDependencies = {
|
type InjectedDependencies = {
|
||||||
@@ -58,7 +58,7 @@ class CartCompletionStrategy extends AbstractCartCompletionStrategy {
|
|||||||
const swapService = this.swapService_
|
const swapService = this.swapService_
|
||||||
|
|
||||||
let inProgress = true
|
let inProgress = true
|
||||||
let err = false
|
let err: unknown = false
|
||||||
|
|
||||||
while (inProgress) {
|
while (inProgress) {
|
||||||
switch (idempotencyKey.recovery_point) {
|
switch (idempotencyKey.recovery_point) {
|
||||||
@@ -96,7 +96,7 @@ class CartCompletionStrategy extends AbstractCartCompletionStrategy {
|
|||||||
inProgress = false
|
inProgress = false
|
||||||
err = error
|
err = error
|
||||||
} else {
|
} else {
|
||||||
idempotencyKey = key
|
idempotencyKey = key as IdempotencyKey
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
break
|
break
|
||||||
@@ -141,7 +141,7 @@ class CartCompletionStrategy extends AbstractCartCompletionStrategy {
|
|||||||
inProgress = false
|
inProgress = false
|
||||||
err = error
|
err = error
|
||||||
} else {
|
} else {
|
||||||
idempotencyKey = key
|
idempotencyKey = key as IdempotencyKey
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
break
|
break
|
||||||
@@ -291,7 +291,7 @@ class CartCompletionStrategy extends AbstractCartCompletionStrategy {
|
|||||||
inProgress = false
|
inProgress = false
|
||||||
err = error
|
err = error
|
||||||
} else {
|
} else {
|
||||||
idempotencyKey = key
|
idempotencyKey = key as IdempotencyKey
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
break
|
break
|
||||||
|
|||||||
@@ -0,0 +1,6 @@
|
|||||||
|
export type CreateIdempotencyKeyInput = {
|
||||||
|
request_method: string
|
||||||
|
request_params: Record<string, unknown>
|
||||||
|
request_path: string
|
||||||
|
idempotency_key?: string
|
||||||
|
}
|
||||||
Reference in new issue
Block a user