feat: Application types generation from project GQL schema's (#8995)

This commit is contained in:
Adrien de Peretti
2024-09-06 11:45:32 +02:00
committed by GitHub
parent ac30a989f4
commit 2c5e72d141
92 changed files with 5129 additions and 443 deletions

View File

@@ -1,12 +1,12 @@
import { IApiKeyModuleService } from "@medusajs/types"
import { ApiKeyType, Module, Modules } from "@medusajs/utils"
import { ApiKeyModuleService } from "@services"
import crypto from "crypto"
import { moduleIntegrationTestRunner } from "medusa-test-utils"
import {
createPublishableKeyFixture,
createSecretKeyFixture,
} from "../__fixtures__"
import { ApiKeyModuleService } from "@services"
jest.setTimeout(100000)
@@ -47,7 +47,9 @@ moduleIntegrationTestRunner<IApiKeyModuleService>({
expect(Object.keys(linkable)).toEqual(["apiKey"])
linkable.apiKey.toJSON = undefined
Object.keys(linkable).forEach((key) => {
delete linkable[key].toJSON
})
expect(linkable.apiKey).toEqual({
id: {
@@ -56,6 +58,12 @@ moduleIntegrationTestRunner<IApiKeyModuleService>({
serviceName: "apiKey",
field: "apiKey",
},
publishable_key_id: {
field: "apiKey",
linkable: "publishable_key_id",
primaryKey: "publishable_key_id",
serviceName: "apiKey",
},
})
})

View File

@@ -0,0 +1,8 @@
import { defineJoinerConfig, Modules } from "@medusajs/utils"
export const joinerConfig = defineJoinerConfig(Modules.API_KEY, {
linkableKeys: {
// Merged with the autogenerated ones to maintain backward compatibility
publishable_key_id: "ApiKey",
},
})

View File

@@ -8,6 +8,7 @@ import {
FindConfig,
IApiKeyModuleService,
InternalModuleDeclaration,
ModuleJoinerConfig,
ModulesSdkTypes,
} from "@medusajs/types"
import { ApiKey } from "@models"
@@ -28,6 +29,7 @@ import {
MedusaService,
promiseAll,
} from "@medusajs/utils"
import { joinerConfig } from "../joiner-config"
const scrypt = util.promisify(crypto.scrypt)
@@ -55,6 +57,10 @@ export class ApiKeyModuleService
this.apiKeyService_ = apiKeyService
}
__joinerConfig(): ModuleJoinerConfig {
return joinerConfig
}
//@ts-expect-error
createApiKeys(
data: ApiKeyTypes.CreateApiKeyDTO[],

View File

@@ -3,9 +3,9 @@ import { FulfillmentSetDTO, IFulfillmentModuleService } from "@medusajs/types"
import { Module, Modules } from "@medusajs/utils"
import { FulfillmentModuleService, FulfillmentProviderService } from "@services"
import {
SuiteOptions,
initModules,
moduleIntegrationTestRunner,
SuiteOptions,
} from "medusa-test-utils"
import { resolve } from "path"
import { createFullDataStructure } from "../../__fixtures__"
@@ -109,10 +109,18 @@ moduleIntegrationTestRunner({
}).linkable
expect(Object.keys(linkable)).toEqual([
"fulfillment",
"fulfillmentAddress",
"fulfillmentItem",
"fulfillmentLabel",
"fulfillmentProvider",
"fulfillmentSet",
"shippingOption",
"fulfillment",
"geoZone",
"serviceZone",
"shippingOptionRule",
"shippingOptionType",
"shippingOption",
"shippingProfile",
])
Object.keys(linkable).forEach((key) => {
@@ -120,12 +128,36 @@ moduleIntegrationTestRunner({
})
expect(linkable).toEqual({
fulfillment: {
fulfillmentAddress: {
id: {
linkable: "fulfillment_id",
linkable: "fulfillment_address_id",
primaryKey: "id",
serviceName: "fulfillment",
field: "fulfillment",
field: "fulfillmentAddress",
},
},
fulfillmentItem: {
id: {
linkable: "fulfillment_item_id",
primaryKey: "id",
serviceName: "fulfillment",
field: "fulfillmentItem",
},
},
fulfillmentLabel: {
id: {
linkable: "fulfillment_label_id",
primaryKey: "id",
serviceName: "fulfillment",
field: "fulfillmentLabel",
},
},
fulfillmentProvider: {
id: {
linkable: "fulfillment_provider_id",
primaryKey: "id",
serviceName: "fulfillment",
field: "fulfillmentProvider",
},
},
fulfillmentSet: {
@@ -136,12 +168,28 @@ moduleIntegrationTestRunner({
field: "fulfillmentSet",
},
},
shippingOption: {
fulfillment: {
id: {
linkable: "shipping_option_id",
linkable: "fulfillment_id",
primaryKey: "id",
serviceName: "fulfillment",
field: "shippingOption",
field: "fulfillment",
},
},
geoZone: {
id: {
linkable: "geo_zone_id",
primaryKey: "id",
serviceName: "fulfillment",
field: "geoZone",
},
},
serviceZone: {
id: {
linkable: "service_zone_id",
primaryKey: "id",
serviceName: "fulfillment",
field: "serviceZone",
},
},
shippingOptionRule: {
@@ -152,6 +200,30 @@ moduleIntegrationTestRunner({
field: "shippingOptionRule",
},
},
shippingOptionType: {
id: {
linkable: "shipping_option_type_id",
primaryKey: "id",
serviceName: "fulfillment",
field: "shippingOptionType",
},
},
shippingOption: {
id: {
linkable: "shipping_option_id",
primaryKey: "id",
serviceName: "fulfillment",
field: "shippingOption",
},
},
shippingProfile: {
id: {
linkable: "shipping_profile_id",
primaryKey: "id",
serviceName: "fulfillment",
field: "shippingProfile",
},
},
})
})

View File

@@ -1,16 +1,20 @@
import { defineJoinerConfig, Modules } from "@medusajs/utils"
import {
Fulfillment,
FulfillmentProvider,
FulfillmentSet,
ShippingOption,
ShippingOptionRule,
} from "@models"
import { default as schema } from "./schema"
export const joinerConfig = defineJoinerConfig(Modules.FULFILLMENT, {
schema,
linkableKeys: {
fulfillment_id: Fulfillment.name,
fulfillment_set_id: FulfillmentSet.name,
shipping_option_id: ShippingOption.name,
shipping_option_rule_id: ShippingOptionRule.name,
fulfillment_provider_id: FulfillmentProvider.name,
},
})

View File

@@ -0,0 +1,167 @@
export default `
enum GeoZoneType {
country
province
city
zip
}
enum ShippingOptionPriceType {
calculated
flat
}
type FulfillmentItem {
id: ID!
title: String!
quantity: Int!
sku: String!
barcode: String!
line_item_id: String
inventory_item_id: String
fulfillment_id: String!
fulfillment: Fulfillment!
created_at: DateTime!
updated_at: DateTime!
deleted_at: DateTime
}
type FulfillmentLabel {
id: ID!
tracking_number: String!
tracking_url: String!
label_url: String!
fulfillment_id: String!
fulfillment: Fulfillment!
created_at: DateTime!
updated_at: DateTime!
deleted_at: DateTime
}
type FulfillmentProvider {
id: ID!
name: String!
metadata: JSON
shipping_options: [ShippingOption!]!
created_at: DateTime!
updated_at: DateTime!
deleted_at: DateTime
}
type FulfillmentSet {
id: ID!
name: String!
type: String!
metadata: JSON
service_zones: [ServiceZone!]!
created_at: DateTime!
updated_at: DateTime!
deleted_at: DateTime
}
type Fulfillment {
id: ID!
location_id: String!
packed_at: DateTime
shipped_at: DateTime
delivered_at: DateTime
canceled_at: DateTime
marked_shipped_by: String
created_by: String
data: JSON
provider_id: String!
shipping_option_id: String
metadata: JSON
shipping_option: ShippingOption
provider: FulfillmentProvider!
delivery_address: FulfillmentAddress!
items: [FulfillmentItem!]!
labels: [FulfillmentLabel!]!
created_at: DateTime!
updated_at: DateTime!
deleted_at: DateTime
}
type GeoZone {
id: ID!
type: GeoZoneType!
country_code: String!
province_code: String
city: String
postal_expression: JSON
metadata: JSON
created_at: DateTime!
updated_at: DateTime!
deleted_at: DateTime
}
type ServiceZone {
id: ID!
name: String!
metadata: JSON
fulfillment_set: FulfillmentSet!
fulfillment_set_id: String!
geo_zones: [GeoZone!]!
shipping_options: [ShippingOption!]!
created_at: DateTime!
updated_at: DateTime!
deleted_at: DateTime
}
type ShippingOptionRule {
id: ID!
attribute: String!
operator: String!
value: JSON
shipping_option_id: String!
shipping_option: ShippingOption!
created_at: DateTime!
updated_at: DateTime!
deleted_at: DateTime
}
type ShippingOptionType {
id: ID!
label: String!
description: String!
code: String!
shipping_option_id: String!
shipping_option: ShippingOption!
created_at: DateTime!
updated_at: DateTime!
deleted_at: DateTime
}
type ShippingOption {
id: ID!
name: String!
price_type: ShippingOptionPriceType!
service_zone_id: String!
shipping_profile_id: String!
provider_id: String!
shipping_option_type_id: String
data: JSON
metadata: JSON
service_zone: ServiceZone!
shipping_profile: ShippingProfile!
fulfillment_provider: FulfillmentProvider!
type: ShippingOptionType!
rules: [ShippingOptionRule!]!
fulfillments: [Fulfillment!]!
created_at: DateTime!
updated_at: DateTime!
deleted_at: DateTime
}
type ShippingProfile {
id: ID!
name: String!
type: String!
metadata: JSON
shipping_options: [ShippingOption!]!
created_at: DateTime!
updated_at: DateTime!
deleted_at: DateTime
}
`

View File

@@ -1,6 +1,11 @@
import { MedusaModule } from "@medusajs/modules-sdk"
import { ModuleJoinerConfig, ModuleJoinerRelationship } from "@medusajs/types"
import { camelToSnakeCase, lowerCaseFirst, toPascalCase } from "@medusajs/utils"
import {
camelToSnakeCase,
isString,
lowerCaseFirst,
toPascalCase,
} from "@medusajs/utils"
import { composeTableName } from "./compose-link-name"
export function generateGraphQLSchema(
@@ -38,15 +43,18 @@ export function generateGraphQLSchema(
)
}
const extJoinerConfig = MedusaModule.getJoinerConfig(
/* const extJoinerConfig = MedusaModule.getJoinerConfig(
extend.relationship.serviceName
)
let extendedEntityName =
extJoinerConfig?.linkableKeys?.[extend.relationship.foreignKey]!
)*/
if (!isReadOnlyLink && !extendedEntityName && (!primary || !foreign)) {
const extendedEntityName =
extendedModule[extend.serviceName].__joinerConfig.linkableKeys[
extend.relationship.primaryKey
]
if (!isReadOnlyLink && (!primary || !foreign || !extendedEntityName)) {
logger.warn(
`Link modules schema: No linkable key found for ${extend.relationship.foreignKey} on module ${extend.relationship.serviceName}.`
`Link modules schema: No linkable key found for ${extend.relationship.primaryKey} on module ${extend.serviceName}.`
)
continue
@@ -57,15 +65,82 @@ export function generateGraphQLSchema(
)
let type = extend.relationship.isList ? `[${entityName}]` : entityName
if (extJoinerConfig?.isReadOnlyLink) {
type = extend.relationship.isList
if (joinerConfig?.isReadOnlyLink) {
// TODO: In readonly, the relation ship of the extend should be applied on all entities in the module that have the relationshiop foregin key attribute
/*type = extend.relationship.isList
? `[${extendedEntityName}]`
: extendedEntityName
: extendedEntityName*/
continue
}
/**
* Find the field aliases shortcut to extend the entity with it
*/
const fieldsAliasesField = Object.entries(extend.fieldAlias || {})
.map(([field, config]) => {
const path = isString(config) ? config : config.path
const isList = isString(config)
? extend.relationship.isList
: config.isList ?? extend.relationship.isList
const pathSegments = path.split(",").reverse()
/*const relationshipMarkerIndex = pathSegments.findIndex((segment) => {
return !!joinerConfig.relationships!.find(
(relation) => relation.alias === targetEntityAlias
)
})
if (relationshipMarkerIndex === -1) {
return
}*/
/*const relationshipPropertyPath = pathSegments
.slice(0, relationshipMarkerIndex + 1)
.reverse()*/
const targetEntityAlias = path.split(".").pop()
const targetEntityRelation = joinerConfig.relationships?.find(
(relation) => relation.alias === targetEntityAlias
)
if (!targetEntityRelation) {
return
}
const targetEntityName = MedusaModule.getJoinerConfig(
targetEntityRelation.serviceName
).linkableKeys?.[targetEntityRelation.foreignKey]
if (!targetEntityName) {
logger.warn(
`Link modules schema: No linkable key found for ${targetEntityRelation.foreignKey} on module ${targetEntityRelation.serviceName}.`
)
return
}
// TODO: Re visit field aliases that access properties from a type
/*const targetEntityType = `${targetEntityName}${
relationshipPropertyPath.length
? relationshipPropertyPath.reduce((acc, value) => {
return `${acc}[${value}]`
}, targetEntityName)
: ""
}`*/
return `${field}: ${
isList ? `[${targetEntityName}]` : targetEntityName
}`
})
.filter(Boolean)
typeDef += `
extend type ${extend.serviceName} {
extend type ${extendedEntityName} {
${fieldName}: ${type}
${fieldsAliasesField.join("\n")}
}
`
}
@@ -92,14 +167,26 @@ export function generateGraphQLSchema(
}
}
// Link table relationships
const primaryField = `${camelToSnakeCase(primary.alias)}: ${toPascalCase(
composeTableName(primary.serviceName)
)}`
// TODO: temporary, every module might always expose their schema
const doesPrimaryExportSchema = !!MedusaModule.getJoinerConfig(
primary.serviceName
)?.schema
const doesForeignExportSchema = !!MedusaModule.getJoinerConfig(
foreign.serviceName
)?.schema
const foreignField = `${camelToSnakeCase(foreign.alias)}: ${toPascalCase(
composeTableName(foreign.serviceName)
)}`
// Link table relationships
const primaryField = doesPrimaryExportSchema
? `${camelToSnakeCase(primary.alias)}: ${toPascalCase(
composeTableName(primary.serviceName)
)}`
: ""
const foreignField = doesForeignExportSchema
? `${camelToSnakeCase(foreign.alias)}: ${toPascalCase(
composeTableName(foreign.serviceName)
)}`
: ""
typeDef += `
type ${entityName} {

View File

@@ -15,26 +15,14 @@ moduleIntegrationTestRunner<IOrderModuleService>({
expect(Object.keys(linkable)).toEqual([
"order",
"orderAddress",
"orderLineItem",
"orderLineItemAdjustment",
"orderLineItemTaxLine",
"orderShippingMethod",
"orderShippingMethodAdjustment",
"orderShippingMethodTaxLine",
"orderTransaction",
"orderChange",
"orderChangeAction",
"orderItem",
"orderSummary",
"orderShipping",
"returnReason",
"return",
"returnItem",
"orderClaim",
"orderClaimItem",
"orderClaimItemImage",
"orderExchange",
"orderExchangeItem",
"orderLineItem",
"orderShippingMethod",
"orderTransaction",
"return",
"returnReason",
])
Object.keys(linkable).forEach((key) => {
@@ -58,6 +46,42 @@ moduleIntegrationTestRunner<IOrderModuleService>({
field: "orderAddress",
},
},
orderChange: {
id: {
linkable: "order_change_id",
primaryKey: "id",
serviceName: "order",
field: "orderChange",
},
},
orderClaim: {
id: {
linkable: "order_claim_id",
primaryKey: "id",
serviceName: "order",
field: "orderClaim",
},
claim_id: {
linkable: "claim_id",
primaryKey: "claim_id",
serviceName: "order",
field: "orderClaim",
},
},
orderExchange: {
id: {
linkable: "order_exchange_id",
primaryKey: "id",
serviceName: "order",
field: "orderExchange",
},
exchange_id: {
linkable: "exchange_id",
primaryKey: "exchange_id",
serviceName: "order",
field: "orderExchange",
},
},
orderLineItem: {
id: {
linkable: "order_line_item_id",
@@ -66,22 +90,6 @@ moduleIntegrationTestRunner<IOrderModuleService>({
field: "orderLineItem",
},
},
orderLineItemAdjustment: {
id: {
linkable: "order_line_item_adjustment_id",
primaryKey: "id",
serviceName: "order",
field: "orderLineItemAdjustment",
},
},
orderLineItemTaxLine: {
id: {
linkable: "order_line_item_tax_line_id",
primaryKey: "id",
serviceName: "order",
field: "orderLineItemTaxLine",
},
},
orderShippingMethod: {
id: {
linkable: "order_shipping_method_id",
@@ -90,22 +98,6 @@ moduleIntegrationTestRunner<IOrderModuleService>({
field: "orderShippingMethod",
},
},
orderShippingMethodAdjustment: {
id: {
linkable: "order_shipping_method_adjustment_id",
primaryKey: "id",
serviceName: "order",
field: "orderShippingMethodAdjustment",
},
},
orderShippingMethodTaxLine: {
id: {
linkable: "order_shipping_method_tax_line_id",
primaryKey: "id",
serviceName: "order",
field: "orderShippingMethodTaxLine",
},
},
orderTransaction: {
id: {
linkable: "order_transaction_id",
@@ -114,54 +106,6 @@ moduleIntegrationTestRunner<IOrderModuleService>({
field: "orderTransaction",
},
},
orderChange: {
id: {
linkable: "order_change_id",
primaryKey: "id",
serviceName: "order",
field: "orderChange",
},
},
orderChangeAction: {
id: {
linkable: "order_change_action_id",
primaryKey: "id",
serviceName: "order",
field: "orderChangeAction",
},
},
orderItem: {
id: {
linkable: "order_item_id",
primaryKey: "id",
serviceName: "order",
field: "orderItem",
},
},
orderSummary: {
id: {
linkable: "order_summary_id",
primaryKey: "id",
serviceName: "order",
field: "orderSummary",
},
},
orderShipping: {
id: {
linkable: "order_shipping_id",
primaryKey: "id",
serviceName: "order",
field: "orderShipping",
},
},
returnReason: {
id: {
linkable: "return_reason_id",
primaryKey: "id",
serviceName: "order",
field: "returnReason",
},
},
return: {
id: {
linkable: "return_id",
@@ -170,52 +114,12 @@ moduleIntegrationTestRunner<IOrderModuleService>({
field: "return",
},
},
returnItem: {
returnReason: {
id: {
linkable: "return_item_id",
linkable: "return_reason_id",
primaryKey: "id",
serviceName: "order",
field: "returnItem",
},
},
orderClaim: {
id: {
linkable: "order_claim_id",
primaryKey: "id",
serviceName: "order",
field: "orderClaim",
},
},
orderClaimItem: {
id: {
linkable: "order_claim_item_id",
primaryKey: "id",
serviceName: "order",
field: "orderClaimItem",
},
},
orderClaimItemImage: {
id: {
linkable: "order_claim_item_image_id",
primaryKey: "id",
serviceName: "order",
field: "orderClaimItemImage",
},
},
orderExchange: {
id: {
linkable: "order_exchange_id",
primaryKey: "id",
serviceName: "order",
field: "orderExchange",
},
},
orderExchangeItem: {
id: {
linkable: "order_exchange_item_id",
primaryKey: "id",
serviceName: "order",
field: "orderExchangeItem",
field: "returnReason",
},
},
})

View File

@@ -1,5 +1,5 @@
import { OrderModuleService } from "@services"
import { Module, Modules } from "@medusajs/utils"
import { OrderModuleService } from "@services"
export default Module(Modules.ORDER, {
service: OrderModuleService,

View File

@@ -0,0 +1,34 @@
import { defineJoinerConfig, Modules } from "@medusajs/utils"
import {
Order,
OrderAddress,
OrderChange,
OrderClaim,
OrderExchange,
OrderLineItem,
OrderShippingMethod,
OrderTransaction,
Return,
ReturnReason,
} from "@models"
import { default as schema } from "./schema"
export const joinerConfig = defineJoinerConfig(Modules.ORDER, {
schema,
linkableKeys: {
claim_id: "OrderClaim",
exchange_id: "OrderExchange",
},
models: [
Order,
OrderAddress,
OrderChange,
OrderClaim,
OrderExchange,
OrderLineItem,
OrderShippingMethod,
OrderTransaction,
Return,
ReturnReason,
],
})

View File

@@ -0,0 +1,573 @@
export default `
enum ChangeActionType {
CANCEL_RETURN_ITEM
FULFILL_ITEM
CANCEL_ITEM_FULFILLMENT
ITEM_ADD
ITEM_REMOVE
ITEM_UPDATE
RECEIVE_DAMAGED_RETURN_ITEM
RECEIVE_RETURN_ITEM
RETURN_ITEM
SHIPPING_ADD
SHIPPING_REMOVE
SHIP_ITEM
WRITE_OFF_ITEM
REINSTATE_ITEM
}
type OrderSummary {
total: Float
subtotal: Float
total_tax: Float
ordered_total: Float
fulfilled_total: Float
returned_total: Float
return_request_total: Float
write_off_total: Float
projected_total: Float
net_total: Float
net_subtotal: Float
net_total_tax: Float
balance: Float
paid_total: Float
refunded_total: Float
pending_difference: Float
raw_pending_difference: JSON
}
type OrderAdjustmentLine {
id: ID!
code: String
amount: Float
order_id: String!
description: String
promotion_id: String
provider_id: String
created_at: DateTime
updated_at: DateTime
}
type OrderShippingMethodAdjustment {
id: ID!
code: String
amount: Float
order_id: String!
description: String
promotion_id: String
provider_id: String
created_at: DateTime
updated_at: DateTime
shipping_method: OrderShippingMethod
shipping_method_id: String!
}
type OrderLineItemAdjustment {
id: ID!
code: String
amount: Float
order_id: String!
description: String
promotion_id: String
provider_id: String
created_at: DateTime
updated_at: DateTime
item: OrderLineItem
item_id: String!
}
type OrderTaxLine {
id: ID!
description: String
tax_rate_id: String
code: String!
rate: Float
provider_id: String
created_at: DateTime
updated_at: DateTime
}
type OrderShippingMethodTaxLine {
id: ID!
description: String
tax_rate_id: String
code: String!
rate: Float
provider_id: String
created_at: DateTime
updated_at: DateTime
shipping_method: OrderShippingMethod
shipping_method_id: String!
total: Float
subtotal: Float
raw_total: JSON
raw_subtotal: JSON
}
type OrderLineItemTaxLine {
id: ID!
description: String
tax_rate_id: String
code: String!
rate: Float
provider_id: String
created_at: DateTime
updated_at: DateTime
item: OrderLineItem
item_id: String!
total: Float
subtotal: Float
raw_total: JSON
raw_subtotal: JSON
}
type OrderAddress {
id: ID!
customer_id: String
first_name: String
last_name: String
phone: String
company: String
address_1: String
address_2: String
city: String
country_code: String
province: String
postal_code: String
metadata: JSON
created_at: DateTime
updated_at: DateTime
}
type OrderShippingMethod {
id: ID!
order_id: String!
name: String!
description: String
amount: Float
raw_amount: JSON
is_tax_inclusive: Boolean
shipping_option_id: String
data: JSON
metadata: JSON
tax_lines: [OrderShippingMethodTaxLine]
adjustments: [OrderShippingMethodAdjustment]
created_at: DateTime
updated_at: DateTime
original_total: Float
original_subtotal: Float
original_tax_total: Float
total: Float
subtotal: Float
tax_total: Float
discount_total: Float
discount_tax_total: Float
raw_original_total: JSON
raw_original_subtotal: JSON
raw_original_tax_total: JSON
raw_total: JSON
raw_subtotal: JSON
raw_tax_total: JSON
raw_discount_total: JSON
raw_discount_tax_total: JSON
}
type OrderLineItem {
id: ID!
title: String!
subtitle: String
thumbnail: String
variant_id: String
product_id: String
product_title: String
product_description: String
product_subtitle: String
product_type: String
product_collection: String
product_handle: String
variant_sku: String
variant_barcode: String
variant_title: String
variant_option_values: JSON
requires_shipping: Boolean!
is_discountable: Boolean!
is_tax_inclusive: Boolean!
compare_at_unit_price: Float
raw_compare_at_unit_price: JSON
unit_price: Float!
raw_unit_price: JSON
quantity: Int!
raw_quantity: JSON
tax_lines: [OrderLineItemTaxLine]
adjustments: [OrderLineItemAdjustment]
detail: OrderItem!
created_at: DateTime!
updated_at: DateTime!
metadata: JSON
original_total: Float
original_subtotal: Float
original_tax_total: Float
item_total: Float
item_subtotal: Float
item_tax_total: Float
total: Float
subtotal: Float
tax_total: Float
discount_total: Float
discount_tax_total: Float
refundable_total: Float
refundable_total_per_unit: Float
raw_original_total: JSON
raw_original_subtotal: JSON
raw_original_tax_total: JSON
raw_item_total: JSON
raw_item_subtotal: JSON
raw_item_tax_total: JSON
raw_total: JSON
raw_subtotal: JSON
raw_tax_total: JSON
raw_discount_total: JSON
raw_discount_tax_total: JSON
raw_refundable_total: JSON
raw_refundable_total_per_unit: JSON
}
type OrderItem {
id: ID!
item_id: String!
item: OrderLineItem!
quantity: Int!
raw_quantity: JSON
fulfilled_quantity: Int!
raw_fulfilled_quantity: JSON
shipped_quantity: Int!
raw_shipped_quantity: JSON
return_requested_quantity: Int!
raw_return_requested_quantity: JSON
return_received_quantity: Int!
raw_return_received_quantity: JSON
return_dismissed_quantity: Int!
raw_return_dismissed_quantity: JSON
written_off_quantity: Int!
raw_written_off_quantity: JSON
metadata: JSON
created_at: DateTime!
updated_at: DateTime!
}
enum OrderStatus {
pending
completed
draft
archived
canceled
requires_action
}
type Order {
id: ID!
version: Int!
order_change: OrderChange
status: OrderStatus!
region_id: String
customer_id: String
sales_channel_id: String
email: String
currency_code: String!
shipping_address: OrderAddress
billing_address: OrderAddress
items: [OrderLineItem]
shipping_methods: [OrderShippingMethod]
transactions: [OrderTransaction]
summary: OrderSummary
metadata: JSON
canceled_at: DateTime
created_at: DateTime!
updated_at: DateTime!
original_item_total: Float!
original_item_subtotal: Float!
original_item_tax_total: Float!
item_total: Float!
item_subtotal: Float!
item_tax_total: Float!
original_total: Float!
original_subtotal: Float!
original_tax_total: Float!
total: Float!
subtotal: Float!
tax_total: Float!
discount_total: Float!
discount_tax_total: Float!
gift_card_total: Float!
gift_card_tax_total: Float!
shipping_total: Float!
shipping_subtotal: Float!
shipping_tax_total: Float!
original_shipping_total: Float!
original_shipping_subtotal: Float!
original_shipping_tax_total: Float!
raw_original_item_total: JSON
raw_original_item_subtotal: JSON
raw_original_item_tax_total: JSON
raw_item_total: JSON
raw_item_subtotal: JSON
raw_item_tax_total: JSON
raw_original_total: JSON
raw_original_subtotal: JSON
raw_original_tax_total: JSON
raw_total: JSON
raw_subtotal: JSON
raw_tax_total: JSON
raw_discount_total: JSON
raw_discount_tax_total: JSON
raw_gift_card_total: JSON
raw_gift_card_tax_total: JSON
raw_shipping_total: JSON
raw_shipping_subtotal: JSON
raw_shipping_tax_total: JSON
raw_original_shipping_total: JSON
raw_original_shipping_subtotal: JSON
raw_original_shipping_tax_total: JSON
}
enum ReturnStatus {
requested
received
partially_received
canceled
}
type Return {
id: ID!
status: ReturnStatus!
refund_amount: Float
order_id: String!
items: [OrderReturnItem]!
}
type OrderReturnItem {
id: ID!
return_id: String!
order_id: String!
item_id: String!
reason_id: String
quantity: Int!
raw_quantity: JSON
received_quantity: Int
raw_received_quantity: JSON
metadata: JSON
created_at: DateTime
updated_at: DateTime
}
type OrderClaimItem {
id: ID!
claim_id: String!
order_id: String!
item_id: String!
quantity: Int!
reason: ClaimReason!
raw_quantity: JSON
metadata: JSON
created_at: DateTime
updated_at: DateTime
}
type OrderClaimItemImage {
id: ID!
claim_item_id: String!
url: String
metadata: JSON
created_at: DateTime
updated_at: DateTime
}
type OrderExchangeItem {
id: ID!
exchange_id: String!
order_id: String!
item_id: String!
quantity: Int!
raw_quantity: JSON
metadata: JSON
created_at: DateTime
updated_at: DateTime
}
type OrderClaim {
order_id: String!
claim_items: [OrderClaimItem]!
additional_items: [OrderClaimItem]!
return: Return
return_id: String
no_notification: Boolean
refund_amount: Float
created_by: String
}
type OrderExchange {
order_id: String!
return_items: [OrderReturnItem]!
additional_items: [OrderClaimItem]!
no_notification: Boolean
difference_due: Float
return: Return
return_id: String
created_by: String
}
enum PaymentStatus {
not_paid
awaiting
authorized
partially_authorized
captured
partially_captured
partially_refunded
refunded
canceled
requires_action
}
enum FulfillmentStatus {
not_fulfilled
partially_fulfilled
fulfilled
partially_shipped
shipped
partially_delivered
delivered
canceled
}
type OrderDetail {
id: ID!
version: Int!
order_change: OrderChange
status: OrderStatus!
region_id: String
customer_id: String
sales_channel_id: String
email: String
currency_code: String!
shipping_address: OrderAddress
billing_address: OrderAddress
items: [OrderLineItem]
shipping_methods: [OrderShippingMethod]
transactions: [OrderTransaction]
summary: OrderSummary
metadata: JSON
canceled_at: DateTime
created_at: DateTime!
updated_at: DateTime!
original_item_total: Float!
original_item_subtotal: Float!
original_item_tax_total: Float!
item_total: Float!
item_subtotal: Float!
item_tax_total: Float!
original_total: Float!
original_subtotal: Float!
original_tax_total: Float!
total: Float!
subtotal: Float!
tax_total: Float!
discount_total: Float!
discount_tax_total: Float!
gift_card_total: Float!
gift_card_tax_total: Float!
shipping_total: Float!
shipping_subtotal: Float!
shipping_tax_total: Float!
original_shipping_total: Float!
original_shipping_subtotal: Float!
original_shipping_tax_total: Float!
raw_original_item_total: JSON
raw_original_item_subtotal: JSON
raw_original_item_tax_total: JSON
raw_item_total: JSON
raw_item_subtotal: JSON
raw_item_tax_total: JSON
raw_original_total: JSON
raw_original_subtotal: JSON
raw_original_tax_total: JSON
raw_total: JSON
raw_subtotal: JSON
raw_tax_total: JSON
raw_discount_total: JSON
raw_discount_tax_total: JSON
raw_gift_card_total: JSON
raw_gift_card_tax_total: JSON
raw_shipping_total: JSON
raw_shipping_subtotal: JSON
raw_shipping_tax_total: JSON
raw_original_shipping_total: JSON
raw_original_shipping_subtotal: JSON
raw_original_shipping_tax_total: JSON
payment_collections: [PaymentCollection]
payment_status: PaymentStatus!
fulfillments: [Fulfillment]
fulfillment_status: FulfillmentStatus!
}
type OrderChange {
id: ID!
version: Int!
change_type: String
order_id: String!
return_id: String
exchange_id: String
claim_id: String
order: Order!
return_order: Return
exchange: OrderExchange
claim: OrderClaim
actions: [OrderChangeAction]!
status: String!
requested_by: String
requested_at: DateTime
confirmed_by: String
confirmed_at: DateTime
declined_by: String
declined_reason: String
metadata: JSON
declined_at: DateTime
canceled_by: String
canceled_at: DateTime
created_at: DateTime!
updated_at: DateTime!
}
type OrderChangeAction {
id: ID!
order_change_id: String
order_change: OrderChange
order_id: String
return_id: String
claim_id: String
exchange_id: String
order: Order
reference: String!
reference_id: String!
action: ChangeActionType!
details: JSON
internal_note: String
created_at: DateTime!
updated_at: DateTime!
}
type OrderTransaction {
id: ID!
order_id: String!
order: Order!
amount: Float!
raw_amount: JSON
currency_code: String!
reference: String!
reference_id: String!
metadata: JSON
created_at: DateTime!
updated_at: DateTime!
}
`

View File

@@ -6,6 +6,7 @@ import {
FindConfig,
IOrderModuleService,
InternalModuleDeclaration,
ModuleJoinerConfig,
ModulesSdkTypes,
OrderDTO,
OrderReturnReasonDTO,
@@ -73,6 +74,7 @@ import {
UpdateOrderShippingMethodTaxLineDTO,
} from "@types"
import { UpdateReturnReasonDTO } from "src/types/return-reason"
import { joinerConfig } from "../joiner-config"
import {
ApplyOrderChangeDTO,
applyChangesToOrder,
@@ -260,6 +262,10 @@ export default class OrderModuleService<
this.orderExchangeService_ = orderExchangeService
}
__joinerConfig(): ModuleJoinerConfig {
return joinerConfig
}
private shouldIncludeTotals(config: FindConfig<any>): boolean {
const totalFields = [
"total",

View File

@@ -4,8 +4,8 @@ import { PaymentModuleService } from "@services"
import { moduleIntegrationTestRunner } from "medusa-test-utils"
import {
createPaymentCollections,
createPayments,
createPaymentSessions,
createPayments,
} from "../../../__fixtures__"
jest.setTimeout(30000)
@@ -23,6 +23,7 @@ moduleIntegrationTestRunner<IPaymentModuleService>({
"payment",
"paymentCollection",
"paymentProvider",
"paymentSession",
"refundReason",
])
@@ -55,6 +56,14 @@ moduleIntegrationTestRunner<IPaymentModuleService>({
field: "paymentProvider",
},
},
paymentSession: {
id: {
field: "paymentSession",
linkable: "payment_session_id",
primaryKey: "id",
serviceName: "payment",
},
},
refundReason: {
id: {
linkable: "refund_reason_id",

View File

@@ -6,8 +6,10 @@ import {
PaymentSession,
RefundReason,
} from "@models"
import { default as schema } from "./schema"
export const joinerConfig = defineJoinerConfig(Modules.PAYMENT, {
schema,
models: [
Payment,
PaymentCollection,

View File

@@ -0,0 +1,111 @@
export default `
enum PaymentCollectionStatus {
not_paid
awaiting
authorized
partially_authorized
canceled
}
enum PaymentSessionStatus {
authorized
captured
pending
requires_more
error
canceled
}
type PaymentCollection {
id: ID!
currency_code: String!
region_id: String!
amount: Float!
authorized_amount: Float
refunded_amount: Float
captured_amount: Float
completed_at: DateTime
created_at: DateTime
updated_at: DateTime
metadata: JSON
status: PaymentCollectionStatus!
payment_providers: [PaymentProviderDTO!]!
payment_sessions: [PaymentSessionDTO]
payments: [PaymentDTO]
}
type Payment {
id: ID!
amount: Float!
raw_amount: Float
authorized_amount: Float
raw_authorized_amount: Float
currency_code: String!
provider_id: String!
cart_id: String
order_id: String
order_edit_id: String
customer_id: String
data: JSON
created_at: DateTime
updated_at: DateTime
captured_at: DateTime
canceled_at: DateTime
captured_amount: Float
raw_captured_amount: Float
refunded_amount: Float
raw_refunded_amount: Float
captures: [CaptureDTO]
refunds: [RefundDTO]
payment_collection_id: String!
payment_collection: PaymentCollectionDTO
payment_session: PaymentSessionDTO
}
type Capture {
id: ID!
amount: Float!
created_at: DateTime!
created_by: String
payment: Payment!
}
type Refund {
id: ID!
amount: Float!
refund_reason_id: String
refund_reason: RefundReason
note: String
created_at: DateTime!
created_by: String
payment: Payment!
}
type PaymentSession {
id: ID!
amount: Float!
currency_code: String!
provider_id: String!
data: JSON!
context: JSON
status: PaymentSessionStatus!
authorized_at: DateTime
payment_collection_id: String!
payment_collection: PaymentCollection
payment: Payment
}
type PaymentProvider {
id: ID!
is_enabled: String!
}
type RefundReason {
id: ID!
label: String!
description: String
metadata: JSON
created_at: DateTime!
updated_at: DateTime!
}
`

View File

@@ -1,6 +1,8 @@
import { defineJoinerConfig, Modules } from "@medusajs/utils"
import { Price, PriceList, PricePreference, PriceSet } from "@models"
import { default as schema } from "./schema"
export const joinerConfig = defineJoinerConfig(Modules.PRICING, {
schema,
models: [PriceSet, PriceList, Price, PricePreference],
})

View File

@@ -1,15 +1,58 @@
export const schema = `
type PriceSet {
id: String!
money_amounts: [MoneyAmount]
id: ID!
prices: [MoneyAmount]
calculated_price: CalculatedPriceSet
}
type MoneyAmount {
id: String!
id: ID!
currency_code: String
amount: Float
min_quantity: Float
max_quantity: Float
rules_count: Int
price_rules: [PriceRule]
created_at: DateTime
updated_at: DateTime
deleted_at: DateTime
}
type PriceRule {
id: ID!
price_set_id: String!
price_set: PriceSet
attribute: String!
value: String!
priority: Int!
price_id: String!
price_list_id: String!
created_at: DateTime
updated_at: DateTime
deleted_at: DateTime
}
type CalculatedPriceSet {
id: ID!
is_calculated_price_price_list: Boolean
is_calculated_price_tax_inclusive: Boolean
calculated_amount: Float
raw_calculated_amount: JSON
is_original_price_price_list: Boolean
is_original_price_tax_inclusive: Boolean
original_amount: Float
raw_original_amount: JSON
currency_code: String
calculated_price: PriceDetails
original_price: PriceDetails
}
type PriceDetails {
id: ID
price_list_id: String
price_list_type: String
min_quantity: Float
max_quantity: Float
}
`

View File

@@ -9,7 +9,7 @@ import {
} from "../__fixtures__/product"
import { IProductModuleService, ProductDTO } from "@medusajs/types"
import { kebabCase, Module, Modules, ProductStatus } from "@medusajs/utils"
import { Module, Modules, ProductStatus, kebabCase } from "@medusajs/utils"
import { SqlEntityManager } from "@mikro-orm/postgresql"
import {
ProductCategoryService,
@@ -77,6 +77,12 @@ moduleIntegrationTestRunner<Service>({
serviceName: "productService",
field: "productVariant",
},
variant_id: {
field: "productVariant",
linkable: "variant_id",
primaryKey: "variant_id",
serviceName: "productService",
},
},
productOption: {
id: {

View File

@@ -9,8 +9,10 @@ import {
ProductVariant,
} from "@models"
import ProductImage from "./models/product-image"
import { default as schema } from "./schema"
export const joinerConfig = defineJoinerConfig(Modules.PRODUCT, {
schema,
models: [
Product,
ProductVariant,
@@ -21,6 +23,10 @@ export const joinerConfig = defineJoinerConfig(Modules.PRODUCT, {
ProductCollection,
ProductCategory,
],
linkableKeys: {
// Merged with the autogenerated ones to maintain backward compatibility
variant_id: "ProductVariant",
},
primaryKeys: ["id", "handle"],
alias: [
{

View File

@@ -0,0 +1,148 @@
export default `
enum ProductStatus {
draft
proposed
published
rejected
}
type Product {
id: ID!
title: String!
handle: String!
subtitle: String
description: String
is_giftcard: Boolean!
status: ProductStatus!
thumbnail: String
width: Float
weight: Float
length: Float
height: Float
origin_country: String
hs_code: String
mid_code: String
material: String
collection: ProductCollection
collection_id: String
categories: [ProductCategory]
type: ProductType
type_id: String
tags: [ProductTag!]!
variants: [ProductVariant!]!
options: [ProductOption!]!
images: [ProductImage!]!
discountable: Boolean
external_id: String
created_at: DateTime!
updated_at: DateTime!
deleted_at: DateTime
metadata: JSON
}
type ProductVariant {
id: ID!
title: String!
sku: String
barcode: String
ean: String
upc: String
allow_backorder: Boolean!
manage_inventory: Boolean!
requires_shipping: Boolean!
hs_code: String
origin_country: String
mid_code: String
material: String
weight: Float
length: Float
height: Float
width: Float
options: [ProductOptionValue!]!
metadata: JSON
product: Product
product_id: String
variant_rank: Int
created_at: DateTime!
updated_at: DateTime!
deleted_at: DateTime
}
type ProductCategory {
id: ID!
name: String!
description: String!
handle: String!
is_active: Boolean!
is_internal: Boolean!
rank: Int!
metadata: JSON
parent_category: ProductCategory
parent_category_id: String
category_children: [ProductCategory!]!
products: [Product!]!
created_at: DateTime!
updated_at: DateTime!
deleted_at: DateTime
}
type ProductTag {
id: ID!
value: String!
metadata: JSON
products: [Product]
}
type ProductCollection {
id: ID!
title: String!
handle: String!
metadata: JSON
created_at: DateTime!
updated_at: DateTime!
deleted_at: DateTime
products: [Product]
}
type ProductType {
id: ID!
value: String!
metadata: JSON
created_at: DateTime!
updated_at: DateTime!
deleted_at: DateTime
}
type ProductOption {
id: ID!
title: String!
product: Product
product_id: String
values: [ProductOptionValue!]!
metadata: JSON
created_at: DateTime!
updated_at: DateTime!
deleted_at: DateTime
}
type ProductImage {
id: ID!
url: String!
metadata: JSON
created_at: DateTime!
updated_at: DateTime!
deleted_at: DateTime
}
type ProductOptionValue {
id: ID!
value: String!
option: ProductOption
option_id: String
metadata: JSON
created_at: DateTime!
updated_at: DateTime!
deleted_at: DateTime
}
`

View File

@@ -1,6 +1,8 @@
import { defineJoinerConfig, Modules } from "@medusajs/utils"
import { Campaign, Promotion, PromotionRule } from "@models"
import { default as schema } from "./schema"
export const joinerConfig = defineJoinerConfig(Modules.PROMOTION, {
schema,
models: [Promotion, Campaign, PromotionRule],
})

View File

@@ -0,0 +1,96 @@
export default `
enum PromotionTypeValues {
standard
buyget
}
enum PromotionRuleOperatorValues {
gt
lt
eq
ne
in
lte
gte
}
enum CampaignBudgetTypeValues {
spend
usage
}
enum ApplicationMethodTypeValues {
fixed
percentage
}
enum ApplicationMethodTargetTypeValues {
order
shipping_methods
items
}
enum ApplicationMethodAllocationValues {
each
across
}
type Promotion {
id: ID!
code: String
type: PromotionTypeValues
is_automatic: Boolean
application_method: ApplicationMethod
rules: [PromotionRule]
campaign_id: String
campaign: Campaign
}
type PromotionRule {
id: ID!
description: String
attribute: String
operator: PromotionRuleOperatorValues
values: [PromotionRuleValue!]!
}
type PromotionRuleValue {
id: ID!
value: String
}
type Campaign {
id: ID!
name: String
description: String
campaign_identifier: String
starts_at: DateTime
ends_at: DateTime
budget: CampaignBudget
promotions: [Promotion]
}
type CampaignBudget {
id: ID!
type: CampaignBudgetTypeValues
limit: Int
used: Int
currency_code: String
}
type ApplicationMethod {
id: ID!
type: ApplicationMethodTypeValues
target_type: ApplicationMethodTargetTypeValues
allocation: ApplicationMethodAllocationValues
value: Float
currency_code: String
max_quantity: Int
buy_rules_min_quantity: Int
apply_to_quantity: Int
promotion: Promotion
target_rules: [PromotionRule]
buy_rules: [PromotionRule]
}
`

View File

@@ -0,0 +1,6 @@
import { defineJoinerConfig, Modules } from "@medusajs/utils"
import { default as schema } from "./schema"
export const joinerConfig = defineJoinerConfig(Modules.SALES_CHANNEL, {
schema,
})

View File

@@ -0,0 +1,12 @@
export default `
type SalesChannel {
id: ID!
name: String!
description: String
is_disabled: Boolean!
created_at: DateTime!
metadata: JSON
updated_at: DateTime!
deleted_at: DateTime
}
`

View File

@@ -5,6 +5,7 @@ import {
FilterableSalesChannelProps,
InternalModuleDeclaration,
ISalesChannelModuleService,
ModuleJoinerConfig,
ModulesSdkTypes,
SalesChannelDTO,
UpdateSalesChannelDTO,
@@ -21,6 +22,7 @@ import {
import { SalesChannel } from "@models"
import { UpdateSalesChanneInput } from "@types"
import { joinerConfig } from "../joinfer-config"
type InjectedDependencies = {
baseRepository: DAL.RepositoryService
@@ -46,6 +48,10 @@ export default class SalesChannelModuleService
this.salesChannelService_ = salesChannelService
}
__joinerConfig(): ModuleJoinerConfig {
return joinerConfig
}
// @ts-expect-error
async createSalesChannels(
data: CreateSalesChannelDTO[],

View File

@@ -15,14 +15,31 @@ moduleIntegrationTestRunner<IStockLocationService>({
service: StockLocationModuleService,
}).linkable
expect(Object.keys(linkable)).toEqual(["stockLocation"])
expect(Object.keys(linkable)).toEqual([
"stockLocationAddress",
"stockLocation",
])
Object.keys(linkable).forEach((key) => {
delete linkable[key].toJSON
})
expect(linkable).toEqual({
stockLocationAddress: {
id: {
linkable: "stock_location_address_id",
primaryKey: "id",
serviceName: "stockLocationService",
field: "stockLocationAddress",
},
},
stockLocation: {
id: {
field: "stockLocation",
linkable: "stock_location_id",
primaryKey: "id",
serviceName: "stockLocationService",
},
location_id: {
linkable: "location_id",
primaryKey: "location_id",

View File

@@ -1,7 +1,9 @@
import { defineJoinerConfig, Modules } from "@medusajs/utils"
import { StockLocation } from "./models"
import { default as schema } from "./schema"
export const joinerConfig = defineJoinerConfig(Modules.STOCK_LOCATION, {
schema,
linkableKeys: {
stock_location_id: StockLocation.name,
location_id: StockLocation.name,

View File

@@ -0,0 +1,29 @@
export default `
type StockLocationAddress {
id: ID
address_1: String!
address_2: String
company: String
country_code: String!
city: String
phone: String
postal_code: String
province: String
metadata: JSON
created_at: DateTime!
updated_at: DateTime!
deleted_at: DateTime
}
type StockLocation {
id: ID!
name: String!
metadata: JSON
address_id: ID!
address: StockLocationAddress
created_at: DateTime!
updated_at: DateTime!
deleted_at: DateTime
}
`