feat(index): Index module foundation (#9095)
**What** Index module foundation Co-authored-by: Carlos R. L. Rodrigues <37986729+carlos-r-l-rodrigues@users.noreply.github.com>
This commit is contained in:
1 parent
3cfcd075ae
commit
58167b5dfa
53 files changed
+4796
-1201
No files matched your search
@@ -1,24 +1,25 @@
|
||||
import { asValue } from "awilix"
|
||||
import { MedusaAppOutput } from "@medusajs/modules-sdk"
|
||||
import { ContainerLike, MedusaContainer } from "@medusajs/types"
|
||||
import {
|
||||
ContainerRegistrationKeys,
|
||||
createMedusaContainer,
|
||||
} from "@medusajs/utils"
|
||||
import { asValue } from "awilix"
|
||||
import { createDatabase, dropDatabase } from "pg-god"
|
||||
import { getDatabaseURL } from "./database"
|
||||
import { startApp } from "./medusa-test-runner-utils/bootstrap-app"
|
||||
import { clearInstances } from "./medusa-test-runner-utils/clear-instances"
|
||||
import { configLoaderOverride } from "./medusa-test-runner-utils/config"
|
||||
import {
|
||||
initDb,
|
||||
migrateDatabase,
|
||||
syncLinks,
|
||||
} from "./medusa-test-runner-utils/use-db"
|
||||
import { configLoaderOverride } from "./medusa-test-runner-utils/config"
|
||||
import { applyEnvVarsToProcess } from "./medusa-test-runner-utils/utils"
|
||||
import { clearInstances } from "./medusa-test-runner-utils/clear-instances"
|
||||
|
||||
const DB_HOST = process.env.DB_HOST
|
||||
const DB_USERNAME = process.env.DB_USERNAME
|
||||
const DB_PASSWORD = process.env.DB_PASSWORD
|
||||
const DB_USERNAME = process.env.DB_USERNAME ?? ''
|
||||
const DB_PASSWORD = process.env.DB_PASSWORD ?? ''
|
||||
|
||||
const pgGodCredentials = {
|
||||
user: DB_USERNAME,
|
||||
@@ -26,11 +27,14 @@ const pgGodCredentials = {
|
||||
host: DB_HOST,
|
||||
}
|
||||
|
||||
const dbTestUtilFactory = (): any => ({
|
||||
export const dbTestUtilFactory = (): any => ({
|
||||
pgConnection_: null,
|
||||
|
||||
create: async function (dbName: string) {
|
||||
await createDatabase({ databaseName: dbName }, pgGodCredentials)
|
||||
await createDatabase(
|
||||
{ databaseName: dbName, errorIfExist: false },
|
||||
pgGodCredentials
|
||||
)
|
||||
},
|
||||
|
||||
teardown: async function ({ schema }: { schema?: string } = {}) {
|
||||
@@ -80,11 +84,14 @@ export interface MedusaSuiteOptions<TService = unknown> {
|
||||
schema: string
|
||||
clientUrl: string
|
||||
}
|
||||
getMedusaApp: () => MedusaAppOutput
|
||||
}
|
||||
|
||||
export function medusaIntegrationTestRunner({
|
||||
moduleName,
|
||||
dbName,
|
||||
medusaConfigFile,
|
||||
loadApplication,
|
||||
schema = "public",
|
||||
env = {},
|
||||
debug = false,
|
||||
@@ -94,6 +101,8 @@ export function medusaIntegrationTestRunner({
|
||||
moduleName?: string
|
||||
env?: Record<string, any>
|
||||
dbName?: string
|
||||
medusaConfigFile?: string
|
||||
loadApplication?: boolean
|
||||
schema?: string
|
||||
debug?: boolean
|
||||
inApp?: boolean
|
||||
@@ -110,12 +119,13 @@ export function medusaIntegrationTestRunner({
|
||||
debug,
|
||||
}
|
||||
|
||||
const cwd = process.cwd()
|
||||
const cwd = medusaConfigFile ?? process.cwd()
|
||||
|
||||
let shutdown = async () => void 0
|
||||
const dbUtils = dbTestUtilFactory()
|
||||
let globalContainer: ContainerLike
|
||||
let apiUtils: any
|
||||
let loadedApplication: any
|
||||
|
||||
let options = {
|
||||
api: new Proxy(
|
||||
@@ -134,6 +144,7 @@ export function medusaIntegrationTestRunner({
|
||||
},
|
||||
}
|
||||
),
|
||||
getMedusaApp: () => loadedApplication,
|
||||
getContainer: () => globalContainer,
|
||||
dbConfig: {
|
||||
dbName,
|
||||
@@ -175,6 +186,10 @@ export function medusaIntegrationTestRunner({
|
||||
let serverShutdownRes: () => any
|
||||
let portRes: number
|
||||
|
||||
if (loadApplication) {
|
||||
loadedApplication = await appLoader.load()
|
||||
}
|
||||
|
||||
try {
|
||||
const {
|
||||
shutdown = () => void 0,
|
||||
|
||||
@@ -0,0 +1,94 @@
|
||||
import { mergeTypeDefs } from "@graphql-tools/merge"
|
||||
import { makeExecutableSchema } from "@graphql-tools/schema"
|
||||
import { gqlGetFieldsAndRelations } from "../../utils"
|
||||
|
||||
const userModule = `
|
||||
type User {
|
||||
id: ID!
|
||||
name: String!
|
||||
blabla: WHATEVER
|
||||
}
|
||||
|
||||
type Post {
|
||||
author: User!
|
||||
}
|
||||
`
|
||||
|
||||
const postModule = `
|
||||
type Post {
|
||||
id: ID!
|
||||
title: String!
|
||||
date: String
|
||||
}
|
||||
|
||||
type User {
|
||||
posts: [Post!]!
|
||||
}
|
||||
|
||||
type WHATEVER {
|
||||
random_field: String
|
||||
post: Post
|
||||
}
|
||||
`
|
||||
|
||||
const mergedSchema = mergeTypeDefs([userModule, postModule])
|
||||
const schema = makeExecutableSchema({
|
||||
typeDefs: mergedSchema,
|
||||
})
|
||||
|
||||
const types = schema.getTypeMap()
|
||||
|
||||
describe("gqlGetFieldsAndRelations", function () {
|
||||
it("Should get all fields of a given entity", async function () {
|
||||
const fields = gqlGetFieldsAndRelations(types, "User")
|
||||
expect(fields).toEqual(expect.arrayContaining(["id", "name"]))
|
||||
})
|
||||
|
||||
it("Should get all fields of a given entity and a relation", async function () {
|
||||
const fields = gqlGetFieldsAndRelations(types, "User", ["posts"])
|
||||
expect(fields).toEqual(
|
||||
expect.arrayContaining([
|
||||
"id",
|
||||
"name",
|
||||
"posts.id",
|
||||
"posts.title",
|
||||
"posts.date",
|
||||
])
|
||||
)
|
||||
})
|
||||
|
||||
it("Should get all fields of a given entity and many relations", async function () {
|
||||
const fields = gqlGetFieldsAndRelations(types, "User", [
|
||||
"posts",
|
||||
"blabla",
|
||||
"blabla.post",
|
||||
])
|
||||
expect(fields).toEqual(
|
||||
expect.arrayContaining([
|
||||
"id",
|
||||
"name",
|
||||
"posts.id",
|
||||
"posts.title",
|
||||
"posts.date",
|
||||
"blabla.random_field",
|
||||
"blabla.post.id",
|
||||
"blabla.post.title",
|
||||
"blabla.post.date",
|
||||
])
|
||||
)
|
||||
})
|
||||
|
||||
it("Should get all fields of a given entity and many relations limited to the relations given", async function () {
|
||||
const fields = gqlGetFieldsAndRelations(types, "User", ["posts", "blabla"])
|
||||
expect(fields).toEqual(
|
||||
expect.arrayContaining([
|
||||
"id",
|
||||
"name",
|
||||
"posts.id",
|
||||
"posts.title",
|
||||
"posts.date",
|
||||
"blabla.random_field",
|
||||
])
|
||||
)
|
||||
})
|
||||
})
|
||||
@@ -1,5 +1,9 @@
|
||||
import { ModuleDefinition } from "@medusajs/types"
|
||||
import { Modules, upperCaseFirst } from "@medusajs/utils"
|
||||
import {
|
||||
ContainerRegistrationKeys,
|
||||
Modules,
|
||||
upperCaseFirst,
|
||||
} from "@medusajs/utils"
|
||||
import { MODULE_RESOURCE_TYPE, MODULE_SCOPE } from "./types"
|
||||
|
||||
export const MODULE_PACKAGE_NAMES = {
|
||||
@@ -27,6 +31,7 @@ export const MODULE_PACKAGE_NAMES = {
|
||||
[Modules.CURRENCY]: "@medusajs/currency",
|
||||
[Modules.FILE]: "@medusajs/file",
|
||||
[Modules.NOTIFICATION]: "@medusajs/notification",
|
||||
[Modules.INDEX]: "@medusajs/index",
|
||||
}
|
||||
|
||||
export const ModulesDefinition: {
|
||||
@@ -308,6 +313,22 @@ export const ModulesDefinition: {
|
||||
resources: MODULE_RESOURCE_TYPE.SHARED,
|
||||
},
|
||||
},
|
||||
[Modules.INDEX]: {
|
||||
key: Modules.INDEX,
|
||||
defaultPackage: false,
|
||||
label: upperCaseFirst(Modules.INDEX),
|
||||
isRequired: false,
|
||||
isQueryable: false,
|
||||
dependencies: [
|
||||
Modules.EVENT_BUS,
|
||||
"logger",
|
||||
ContainerRegistrationKeys.REMOTE_QUERY,
|
||||
],
|
||||
defaultModuleDeclaration: {
|
||||
scope: MODULE_SCOPE.INTERNAL,
|
||||
resources: MODULE_RESOURCE_TYPE.SHARED,
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
export const MODULE_DEFINITIONS: ModuleDefinition[] =
|
||||
|
||||
@@ -5,4 +5,4 @@ export * from "./medusa-module"
|
||||
export * from "./remote-link"
|
||||
export * from "./remote-query"
|
||||
export * from "./types"
|
||||
export { gqlSchemaToTypes } from "./utils"
|
||||
export * from "./utils"
|
||||
@@ -102,6 +102,11 @@ export async function loadModules(
|
||||
let declaration: any = {}
|
||||
let definition: Partial<ModuleDefinition> | undefined = undefined
|
||||
|
||||
// Skip disabled modules
|
||||
if (mod === false) {
|
||||
return
|
||||
}
|
||||
|
||||
if (isObject(mod)) {
|
||||
const mod_ = mod as unknown as InternalModuleDeclaration
|
||||
path = mod_.resolve ?? MODULE_PACKAGE_NAMES[moduleName]
|
||||
|
||||
@@ -0,0 +1,43 @@
|
||||
import { GraphQLNamedType, GraphQLObjectType, isObjectType } from "graphql"
|
||||
|
||||
/**
|
||||
* Generate a list of fields and fields relations for a given type with the requested relations
|
||||
* @param schemaTypeMap
|
||||
* @param typeName
|
||||
* @param relations
|
||||
*/
|
||||
export function gqlGetFieldsAndRelations(
|
||||
schemaTypeMap: { [key: string]: GraphQLNamedType },
|
||||
typeName: string,
|
||||
relations: string[] = []
|
||||
) {
|
||||
const result: string[] = []
|
||||
|
||||
function traverseFields(typeName, prefix = "") {
|
||||
const type = schemaTypeMap[typeName]
|
||||
|
||||
if (!(type instanceof GraphQLObjectType)) {
|
||||
return
|
||||
}
|
||||
|
||||
const fields = type.getFields()
|
||||
|
||||
for (const fieldName in fields) {
|
||||
const field = fields[fieldName]
|
||||
let fieldType = field.type as any
|
||||
|
||||
while (fieldType.ofType) {
|
||||
fieldType = fieldType.ofType
|
||||
}
|
||||
|
||||
if (!isObjectType(fieldType)) {
|
||||
result.push(`${prefix}${fieldName}`)
|
||||
} else if (relations.includes(prefix + fieldName)) {
|
||||
traverseFields(fieldType.name, `${prefix}${fieldName}.`)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
traverseFields(typeName)
|
||||
return result
|
||||
}
|
||||
@@ -1,3 +1,4 @@
|
||||
export * from "./clean-graphql-schema"
|
||||
export * from "./graphql-schema-to-fields"
|
||||
export * from "./gql-schema-to-types"
|
||||
export * from "./gql-get-fields-and-relations"
|
||||
@@ -31,3 +31,4 @@ export * as UserTypes from "./user"
|
||||
export * as WorkflowTypes from "./workflow"
|
||||
export * as WorkflowsSdkTypes from "./workflows-sdk"
|
||||
export * as DmlTypes from "./dml"
|
||||
export * as IndexTypes from "./index/index"
|
||||
@@ -41,3 +41,4 @@ export * from "./workflow"
|
||||
export * from "./workflows"
|
||||
export * from "./workflows-sdk"
|
||||
export * from "./dml"
|
||||
export * from "./index"
|
||||
@@ -0,0 +1 @@
|
||||
export * from "./service"
|
||||
@@ -0,0 +1,6 @@
|
||||
import { IModuleService } from "../modules-sdk"
|
||||
|
||||
export interface IIndexService extends IModuleService {
|
||||
query(...args): Promise<any>
|
||||
queryAndCount(...args): Promise<any>
|
||||
}
|
||||
@@ -29,7 +29,6 @@ export const MedusaErrorCodes = {
|
||||
* @extends Error
|
||||
*/
|
||||
export class MedusaError extends Error {
|
||||
// Symbol is not serializable
|
||||
__isMedusaError = true
|
||||
|
||||
public type: string
|
||||
|
||||
File diff suppressed because it is too large.
Load diff
@@ -49,6 +49,7 @@ export enum Modules {
|
||||
CURRENCY = "Currency",
|
||||
FILE = "File",
|
||||
NOTIFICATION = "Notification",
|
||||
INDEX = "Index",
|
||||
}
|
||||
|
||||
export const ModuleRegistrationName = Modules
|
||||
@@ -78,5 +79,6 @@ declare module "@medusajs/types" {
|
||||
[Modules.CURRENCY]: ICurrencyModuleService
|
||||
[Modules.FILE]: IFileModuleService
|
||||
[Modules.NOTIFICATION]: INotificationModuleService
|
||||
[Modules.INDEX]: any // TODO: define index module interface
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user