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:
Adrien de PerettiandCarlos R. L. Rodrigues authored and GitHub committed 2024-09-18 19:04:04 +00:00
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",
])
)
})
})
+22 -1
View File
@@ -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[] =
+1 -1
View File
@@ -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"
+1
View File
@@ -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"
+1
View File
@@ -41,3 +41,4 @@ export * from "./workflow"
export * from "./workflows"
export * from "./workflows-sdk"
export * from "./dml"
export * from "./index"
+1
View File
@@ -0,0 +1 @@
export * from "./service"
+6
View File
@@ -0,0 +1,6 @@
import { IModuleService } from "../modules-sdk"
export interface IIndexService extends IModuleService {
query(...args): Promise<any>
queryAndCount(...args): Promise<any>
}
-1
View File
@@ -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
}
}