fix(utils): medusa internal service returned data should match typings (#12715)

FIXES SUP-1824

**What**
The medusa internal service update should always return the data in the expected shape described by the interface. The medusa service should not have to handle the reshapre
This commit is contained in:
Adrien de Peretti
2025-06-12 15:55:49 +00:00
committed by GitHub
parent ea0b297b44
commit ab634a14ba
9 changed files with 94 additions and 25 deletions
+10
View File
@@ -0,0 +1,10 @@
---
"@medusajs/utils": patch
"@medusajs/auth": patch
"@medusajs/customer": patch
"@medusajs/fulfillment": patch
"@medusajs/inventory": patch
"@medusajs/payment": patch
---
fix(utils): medusa internal service returned data should match typings
@@ -260,6 +260,13 @@ export function MedusaInternalService<
| InferEntityType<TEntity>[] | InferEntityType<TEntity>[]
} }
let shouldReturnArray = false
if (Array.isArray(input)) {
shouldReturnArray = true
} else if (isObject(input) && "selector" in input) {
shouldReturnArray = true
}
const primaryKeys = AbstractService_.retrievePrimaryKeys(model) const primaryKeys = AbstractService_.retrievePrimaryKeys(model)
const inputArray = Array.isArray(input) ? input : [input] const inputArray = Array.isArray(input) ? input : [input]
@@ -352,7 +359,9 @@ export function MedusaInternalService<
} }
if (!toUpdateData.length) { if (!toUpdateData.length) {
return [] return (shouldReturnArray ? [] : void 0) as
| InferEntityType<TEntity>
| InferEntityType<TEntity>[]
} }
// Manage metadata if needed // Manage metadata if needed
@@ -372,10 +381,12 @@ export function MedusaInternalService<
} }
}) })
return await this[propertyRepositoryName].update( const entities = await this[propertyRepositoryName].update(
toUpdateData, toUpdateData,
sharedContext sharedContext
) )
return shouldReturnArray ? entities : entities[0]
} }
delete(idOrSelector: string, sharedContext?: Context): Promise<string[]> delete(idOrSelector: string, sharedContext?: Context): Promise<string[]>
@@ -245,12 +245,7 @@ export function MedusaService<
): Promise<T | T[]> { ): Promise<T | T[]> {
const serviceData = Array.isArray(data) ? data : [data] const serviceData = Array.isArray(data) ? data : [data]
const service = this.__container__[serviceRegistrationName] const service = this.__container__[serviceRegistrationName]
const models = await service.update(serviceData, sharedContext) const response = await service.update(serviceData, sharedContext)
const response = models.length
? Array.isArray(data)
? models
: models[0]
: []
klassPrototype.aggregatedEvents.bind(this)({ klassPrototype.aggregatedEvents.bind(this)({
action: CommonEvents.UPDATED, action: CommonEvents.UPDATED,
@@ -129,7 +129,7 @@ export default class AuthModuleService
populate: true, populate: true,
}) })
return Array.isArray(data) ? serializedUsers : serializedUsers[0] return serializedUsers
} }
async register( async register(
@@ -206,7 +206,7 @@ export default class AuthModuleService
AuthTypes.ProviderIdentityDTO[] AuthTypes.ProviderIdentityDTO[]
>(updatedProviders) >(updatedProviders)
return Array.isArray(data) ? serializedProviders : serializedProviders[0] return serializedProviders
} }
async updateProvider( async updateProvider(
@@ -218,7 +218,7 @@ export default class CustomerModuleService
populate: true, populate: true,
}) })
return isString(idsOrSelector) ? serialized[0] : serialized return serialized
} }
// @ts-expect-error // @ts-expect-error
@@ -2021,9 +2021,11 @@ export default class FulfillmentModuleService
}) })
} }
const result = await this.baseRepository_.serialize(fulfillment) const result = await this.baseRepository_.serialize<FulfillmentDTO>(
fulfillment
)
return Array.isArray(result) ? result[0] : result return result
} }
async retrieveFulfillmentOptions( async retrieveFulfillmentOptions(
@@ -1052,7 +1052,7 @@ export default class InventoryModuleService
context context
) )
return result[0] return result
} }
@InjectManager() @InjectManager()
@@ -359,15 +359,13 @@ export default class PaymentModuleService
} }
) )
paymentSession = ( paymentSession = await this.paymentSessionService_.update(
await this.paymentSessionService_.update( {
{ id: paymentSession!.id,
id: paymentSession!.id, data: { ...input.data, ...providerPaymentSession.data },
data: { ...input.data, ...providerPaymentSession.data }, },
}, sharedContext
sharedContext )
)
)[0]
} catch (error) { } catch (error) {
if (providerPaymentSession) { if (providerPaymentSession) {
await this.paymentProviderService_.deleteSession(input.provider_id, { await this.paymentProviderService_.deleteSession(input.provider_id, {
@@ -440,7 +438,7 @@ export default class PaymentModuleService
sharedContext sharedContext
) )
return await this.baseRepository_.serialize(updated[0], { populate: true }) return await this.baseRepository_.serialize(updated, { populate: true })
} }
@InjectManager() @InjectManager()
@@ -605,7 +603,7 @@ export default class PaymentModuleService
// NOTE: currently there is no update with the provider but maybe data could be updated // NOTE: currently there is no update with the provider but maybe data could be updated
const result = await this.paymentService_.update(data, sharedContext) const result = await this.paymentService_.update(data, sharedContext)
return await this.baseRepository_.serialize<PaymentDTO>(result[0]) return await this.baseRepository_.serialize<PaymentDTO>(result)
} }
// TODO: This method should return a capture, not a payment // TODO: This method should return a capture, not a payment
@@ -285,6 +285,59 @@ moduleIntegrationTestRunner<Service>({
) )
}) })
it("should update a product and its allowed relations using selector", async () => {
const updateData = [
{
selector: {
id: productOne.id,
},
data: {
title: "update test 1",
},
},
]
const products = await service.update(updateData)
expect(products.length).toEqual(1)
let result = await service.retrieve(productOne.id)
let serialized = JSON.parse(JSON.stringify(result))
expect(serialized).toEqual(
expect.objectContaining({
id: productOne.id,
title: "update test 1",
})
)
})
it("should update a single product and its allowed relations", async () => {
const updateData = {
id: productOne.id,
title: "update test 1",
}
const product = await service.update(updateData)
expect(product).toEqual(
expect.objectContaining({
id: productOne.id,
title: "update test 1",
})
)
let result = await service.retrieve(productOne.id)
let serialized = JSON.parse(JSON.stringify(result))
expect(serialized).toEqual(
expect.objectContaining({
id: productOne.id,
title: "update test 1",
})
)
})
it("should throw an error when id is not present", async () => { it("should throw an error when id is not present", async () => {
let error let error
const updateData = [ const updateData = [