Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(codegen): x-expanded-relations #3442

Merged
merged 8 commits into from
Mar 13, 2023
5 changes: 5 additions & 0 deletions .changeset/tame-forks-marry.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@medusajs/openapi-typescript-codegen": minor
---

feat(codegen): x-expanded-relations
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,7 @@
"test:integration:plugins": "turbo run test --no-daemon --filter=integration-tests-plugins",
"test:integration:repositories": "turbo run test --no-daemon --filter=integration-tests-repositories",
"openapi:generate": "yarn ./packages/oas/oas-github-ci run ci",
"medusa-oas": "yarn ./packages/oas/medusa-oas-cli run medusa-oas",
"generate:services": "typedoc --options typedoc.services.js",
"generate:js-client": "typedoc --options typedoc.js-client.js",
"generate:entities": "typedoc --options typedoc.entities.js",
Expand Down
46 changes: 46 additions & 0 deletions packages/medusa/src/api/routes/store/carts/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -154,6 +154,52 @@ export const defaultStoreCartRelations = [
/**
* @schema StoreCartsRes
* type: object
* x-expanded-relations:
* field: cart
* relations:
* - billing_address
* - discounts
* - discounts.rule
* - gift_cards
* - items
* - items.adjustments
* - items.variant
* - payment
* - payment_sessions
* - region
* - region.countries
* - region.payment_providers
* - shipping_address
* - shipping_methods
* - shipping_methods.shipping_option
* implicit:
olivermrbl marked this conversation as resolved.
Show resolved Hide resolved
* - items.tax_lines
* - items.variant.product
* - region.fulfillment_providers
* - region.payment_providers
* - region.tax_rates
* - shipping_methods.shipping_option
* - shipping_methods.tax_lines
* totals:
olivermrbl marked this conversation as resolved.
Show resolved Hide resolved
* - discount_total
* - gift_card_tax_total
* - gift_card_total
* - item_tax_total
* - refundable_amount
* - refunded_total
* - shipping_tax_total
* - shipping_total
* - subtotal
* - tax_total
* - total
* - items.discount_total
* - items.gift_card_total
* - items.original_tax_total
* - items.original_total
* - items.refundable
* - items.subtotal
* - items.tax_total
* - items.total
* required:
* - cart
* properties:
Expand Down
12 changes: 12 additions & 0 deletions packages/medusa/src/api/routes/store/regions/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,12 @@ export default (app) => {
/**
* @schema StoreRegionsListRes
* type: object
* x-expanded-relations:
* field: regions
* relations:
* - countries
* - payment_providers
* - fulfillment_providers
* required:
* - regions
* properties:
Expand All @@ -31,6 +37,12 @@ export type StoreRegionsListRes = {
/**
* @schema StoreRegionsRes
* type: object
* x-expanded-relations:
* field: region
* relations:
* - countries
* - payment_providers
* - fulfillment_providers
* required:
* - region
* properties:
Expand Down
2 changes: 1 addition & 1 deletion packages/oas/medusa-oas-cli/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@
"scripts": {
"prepare": "cross-env NODE_ENV=production yarn run build",
"build": "tsc --build",
"cli": "ts-node src/index.ts",
"medusa-oas": "ts-node src/index.ts",
"test": "jest src",
"test:unit": "jest src"
},
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ const getTmpDirectory = async () => {
}

const runCLI = async (command: string, options: string[] = []) => {
const params = ["run", "cli", command, ...options]
const params = ["run", "medusa-oas", command, ...options]
try {
const { all: logs } = await execa("yarn", params, {
cwd: basePath,
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,14 @@
import type { Enum } from "./Enum"
import type { Schema } from "./Schema"

export type NestedRelation = {
field: string
nestedRelations: NestedRelation[]
base?: string
isArray?: boolean
hasDepth?: boolean
}

export interface Model extends Schema {
name: string
export:
Expand All @@ -24,4 +32,5 @@ export interface Model extends Schema {
enum: Enum[]
enums: Model[]
properties: Model[]
nestedRelations?: NestedRelation[]
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
export interface WithExtendedRelationsExtension {
"x-expanded-relations"?: {
field: string
relations?: string[]
totals?: string[]
implicit?: string[]
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,15 @@ import type { OpenApiDiscriminator } from "./OpenApiDiscriminator"
import type { OpenApiExternalDocs } from "./OpenApiExternalDocs"
import type { OpenApiReference } from "./OpenApiReference"
import type { OpenApiXml } from "./OpenApiXml"
import { WithExtendedRelationsExtension } from "./Extensions/WithDefaultRelationsExtension"

/**
* https:/OAI/OpenAPI-Specification/blob/master/versions/3.0.2.md#schemaObject
*/
export interface OpenApiSchema extends OpenApiReference, WithEnumExtension {
export interface OpenApiSchema
extends OpenApiReference,
WithEnumExtension,
WithExtendedRelationsExtension {
title?: string
multipleOf?: number
maximum?: number
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import { OpenApiSchema } from "../interfaces/OpenApiSchema"
import { Dictionary } from "../../../utils/types"
import { OpenApiParameter } from "../interfaces/OpenApiParameter"
import { listOperations } from "./listOperations"
import { handleExpandedRelations } from "./getModelsExpandedRelations"

export const getModels = (openApi: OpenApi): Model[] => {
const models: Model[] = []
Expand All @@ -27,6 +28,10 @@ export const getModels = (openApi: OpenApi): Model[] => {
}
}

for (const model of models) {
handleExpandedRelations(model, models)
}

/**
* Bundle all query parameters in a single typed object
* when x-codegen.queryParams is declared on the operation.
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,128 @@
import { Model, NestedRelation } from "../../../client/interfaces/Model"

export const handleExpandedRelations = (model: Model, allModels: Model[]) => {
const xExpandedRelation = model.spec["x-expanded-relations"]
if (!xExpandedRelation) {
return
}
const field = xExpandedRelation.field
const relations = xExpandedRelation.relations ?? []
const totals = xExpandedRelation.totals ?? []
const implicit = xExpandedRelation.implicit ?? []

const nestedRelation: NestedRelation = {
field,
nestedRelations: [],
}

for (const relation of [...relations, ...totals, ...implicit]) {
const splitRelation = relation.split(".")
walkSplitRelations(nestedRelation, splitRelation, 0)
}

walkNestedRelations(allModels, model, model, nestedRelation)
model.imports = [...new Set(model.imports)]

const prop = getPropertyByName(nestedRelation.field, model)
if (prop) {
prop.nestedRelations = [nestedRelation]
}
}

const walkSplitRelations = (
parentNestedRelation: NestedRelation,
splitRelation: string[],
depthIndex: number
) => {
const field = splitRelation[depthIndex]
let nestedRelation: NestedRelation | undefined =
parentNestedRelation.nestedRelations.find(
(nestedRelation) => nestedRelation.field === field
)
if (!nestedRelation) {
nestedRelation = {
field,
nestedRelations: [],
}
parentNestedRelation.nestedRelations.push(nestedRelation)
}
depthIndex++
if (depthIndex < splitRelation.length) {
walkSplitRelations(nestedRelation, splitRelation, depthIndex)
}
}

const walkNestedRelations = (
allModels: Model[],
rootModel: Model,
model: Model,
nestedRelation: NestedRelation,
parentNestedRelation?: NestedRelation
) => {
const prop =
model.export === "all-of"
? findPropInAllOf(nestedRelation.field, model, allModels)
: getPropertyByName(nestedRelation.field, model)
if (!prop) {
return
}
if (!["reference", "array"].includes(prop.export)) {
return
}

nestedRelation.base = prop.type
nestedRelation.isArray = prop.export === "array"

for (const childNestedRelation of nestedRelation.nestedRelations) {
const childModel = getModelByName(prop.type, allModels)
if (!childModel) {
return
}
rootModel.imports.push(prop.type)
if (parentNestedRelation) {
parentNestedRelation.hasDepth = true
}
walkNestedRelations(
allModels,
rootModel,
childModel,
childNestedRelation,
nestedRelation
)
}
}

const findPropInAllOf = (
fieldName: string,
model: Model,
allModels: Model[]
) => {
for (const property of model.properties) {
switch (property.export) {
case "interface":
return getPropertyByName(fieldName, model)
case "reference":
const tmpModel = getModelByName(property.type, allModels)
if (tmpModel) {
return getPropertyByName(fieldName, tmpModel)
}
break
}
}
}

function getModelByName(name: string, models: Model[]): Model | void {
for (const model of models) {
if (model.name === name) {
return model
}
}
}

function getPropertyByName(name: string, model: Model): Model | void {
for (const property of model.properties) {
if (property.name === name) {
return property
}
}
}
Loading