feat(types,module-sdk): setup authentication module skeleton (#5943)

**What**
- add authentication module skeleton
This commit is contained in:
Philip Korsholm
2023-12-21 06:56:20 +00:00
committed by GitHub
parent 3f6d79961d
commit 6d1e3cc028
44 changed files with 720 additions and 0 deletions
+6
View File
@@ -0,0 +1,6 @@
---
"@medusajs/modules-sdk": patch
"@medusajs/types": patch
---
feat(modules-sdk, types): add initial authentication module configuration
+6
View File
@@ -0,0 +1,6 @@
/dist
node_modules
.DS_store
.env*
.env
*.sql
+3
View File
@@ -0,0 +1,3 @@
# Authentication Module
The Authentication Module is Medusas authentication engine engine. It provides functions to authenticate users through identity providers and store metadata about users that can be used for authorization purposes.
@@ -0,0 +1,5 @@
describe("Noop test", () => {
it("noop check", async () => {
expect(true).toBe(true)
})
})
@@ -0,0 +1,6 @@
if (typeof process.env.DB_TEMP_NAME === "undefined") {
const tempName = parseInt(process.env.JEST_WORKER_ID || "1")
process.env.DB_TEMP_NAME = `medusa-authentication-integration-${tempName}`
}
process.env.MEDUSA_AUTHENTICATION_DB_SCHEMA = "public"
@@ -0,0 +1,3 @@
import { JestUtils } from "medusa-test-utils"
JestUtils.afterAllHookDropDatabase()
@@ -0,0 +1,6 @@
import { ModuleServiceInitializeOptions } from "@medusajs/types"
export const databaseOptions: ModuleServiceInitializeOptions["database"] = {
schema: "public",
clientUrl: "medusa-authentication-test",
}
@@ -0,0 +1,18 @@
import { TestDatabaseUtils } from "medusa-test-utils"
import * as AuthenticationModels from "@models"
const pathToMigrations = "../../src/migrations"
const mikroOrmEntities = AuthenticationModels as unknown as any[]
export const MikroOrmWrapper = TestDatabaseUtils.getMikroOrmWrapper(
mikroOrmEntities,
pathToMigrations
)
export const MikroOrmConfig = TestDatabaseUtils.getMikroOrmConfig(
mikroOrmEntities,
pathToMigrations
)
export const DB_URL = TestDatabaseUtils.getDatabaseURL()
@@ -0,0 +1,2 @@
export * from "./config"
export * from "./database"
+21
View File
@@ -0,0 +1,21 @@
module.exports = {
moduleNameMapper: {
"^@models": "<rootDir>/src/models",
"^@services": "<rootDir>/src/services",
"^@repositories": "<rootDir>/src/repositories",
},
transform: {
"^.+\\.[jt]s?$": [
"ts-jest",
{
tsConfig: "tsconfig.spec.json",
isolatedModules: true,
},
],
},
testEnvironment: `node`,
moduleFileExtensions: [`js`, `ts`],
modulePathIgnorePatterns: ["dist/"],
setupFiles: ["<rootDir>/integration-tests/setup-env.js"],
setupFilesAfterEnv: ["<rootDir>/integration-tests/setup.js"],
}
@@ -0,0 +1,8 @@
import * as entities from "./src/models"
module.exports = {
entities: Object.values(entities),
schema: "public",
clientUrl: "postgres://postgres@localhost/medusa-authentication",
type: "postgresql",
}
+63
View File
@@ -0,0 +1,63 @@
{
"name": "@medusajs/authentication",
"version": "0.0.1",
"description": "Medusa Authentication module",
"main": "dist/index.js",
"types": "dist/index.d.ts",
"files": [
"dist"
],
"engines": {
"node": ">=16"
},
"bin": {
"medusa-authentication-migrations-down": "dist/scripts/bin/run-migration-down.js",
"medusa-authentication-migrations-up": "dist/scripts/bin/run-migration-up.js",
"medusa-authentication-seed": "dist/scripts/bin/run-seed.js"
},
"repository": {
"type": "git",
"url": "https://github.com/medusajs/medusa",
"directory": "packages/authentication"
},
"publishConfig": {
"access": "public"
},
"author": "Medusa",
"license": "MIT",
"scripts": {
"watch": "tsc --build --watch",
"watch:test": "tsc --build tsconfig.spec.json --watch",
"prepublishOnly": "cross-env NODE_ENV=production tsc --build && tsc-alias -p tsconfig.json",
"build": "rimraf dist && tsc --build && tsc-alias -p tsconfig.json",
"test": "jest --runInBand --bail --forceExit -- src/**/__tests__/**/*.ts",
"test:integration": "jest --runInBand --forceExit -- integration-tests/**/__tests__/**/*.ts",
"migration:generate": " MIKRO_ORM_CLI=./mikro-orm.config.dev.ts mikro-orm migration:generate",
"migration:initial": " MIKRO_ORM_CLI=./mikro-orm.config.dev.ts mikro-orm migration:create --initial",
"migration:create": " MIKRO_ORM_CLI=./mikro-orm.config.dev.ts mikro-orm migration:create",
"migration:up": " MIKRO_ORM_CLI=./mikro-orm.config.dev.ts mikro-orm migration:up",
"orm:cache:clear": " MIKRO_ORM_CLI=./mikro-orm.config.dev.ts mikro-orm cache:clear"
},
"devDependencies": {
"@mikro-orm/cli": "5.7.12",
"cross-env": "^5.2.1",
"jest": "^29.6.3",
"medusa-test-utils": "^1.1.40",
"rimraf": "^3.0.2",
"ts-jest": "^29.1.1",
"ts-node": "^10.9.1",
"tsc-alias": "^1.8.6",
"typescript": "^5.1.6"
},
"dependencies": {
"@medusajs/modules-sdk": "^1.12.5",
"@medusajs/types": "^1.11.9",
"@medusajs/utils": "^1.11.2",
"@mikro-orm/core": "5.7.12",
"@mikro-orm/migrations": "5.7.12",
"@mikro-orm/postgresql": "5.7.12",
"awilix": "^8.0.0",
"dotenv": "^16.1.4",
"knex": "2.4.2"
}
}
+7
View File
@@ -0,0 +1,7 @@
import { moduleDefinition } from "./module-definition"
export default moduleDefinition
export * from "./initialize"
export * from "./loaders"
export * from "./scripts"
@@ -0,0 +1,27 @@
import {
ExternalModuleDeclaration,
InternalModuleDeclaration,
MedusaModule,
MODULE_PACKAGE_NAMES,
Modules,
} from "@medusajs/modules-sdk"
import { IAuthenticationModuleService, ModulesSdkTypes } from "@medusajs/types"
import { moduleDefinition } from "../module-definition"
import { InitializeModuleInjectableDependencies } from "../types"
export const initialize = async (
options?: ModulesSdkTypes.ModuleBootstrapDeclaration,
injectedDependencies?: InitializeModuleInjectableDependencies
): Promise<IAuthenticationModuleService> => {
const loaded = await MedusaModule.bootstrap<IAuthenticationModuleService>({
moduleKey: Modules.AUTHENTICATION,
defaultPath: MODULE_PACKAGE_NAMES[Modules.AUTHENTICATION],
declaration: options as
| InternalModuleDeclaration
| ExternalModuleDeclaration, // TODO: Add provider configuration
injectedDependencies,
moduleExports: moduleDefinition,
})
return loaded[Modules.AUTHENTICATION]
}
@@ -0,0 +1,31 @@
import { Modules } from "@medusajs/modules-sdk"
import { ModuleJoinerConfig } from "@medusajs/types"
import { MapToConfig } from "@medusajs/utils"
import { AuthUser } from "@models"
export const LinkableKeys = {
auth_user_id: AuthUser.name,
}
const entityLinkableKeysMap: MapToConfig = {}
Object.entries(LinkableKeys).forEach(([key, value]) => {
entityLinkableKeysMap[value] ??= []
entityLinkableKeysMap[value].push({
mapTo: key,
valueFrom: key.split("_").pop()!,
})
})
export const entityNameToLinkableKeysMap: MapToConfig = entityLinkableKeysMap
export const joinerConfig: ModuleJoinerConfig = {
serviceName: Modules.AUTHENTICATION,
primaryKeys: ["id"],
linkableKeys: LinkableKeys,
alias: {
name: ["auth_user", "auth_users"],
args: {
entity: AuthUser.name,
},
},
}
@@ -0,0 +1,36 @@
import {
InternalModuleDeclaration,
LoaderOptions,
Modules,
} from "@medusajs/modules-sdk"
import { ModulesSdkTypes } from "@medusajs/types"
import { ModulesSdkUtils } from "@medusajs/utils"
import { EntitySchema } from "@mikro-orm/core"
import * as AuthenticationModules from "../models"
export default async (
{
options,
container,
logger,
}: LoaderOptions<
| ModulesSdkTypes.ModuleServiceInitializeOptions
| ModulesSdkTypes.ModuleServiceInitializeCustomDataLayerOptions
>,
moduleDeclaration?: InternalModuleDeclaration
): Promise<void> => {
const entities = Object.values(
AuthenticationModules
) as unknown as EntitySchema[]
const pathToMigrations = __dirname + "/../migrations"
await ModulesSdkUtils.mikroOrmConnectionLoader({
moduleName: Modules.AUTHENTICATION,
entities,
container,
options,
moduleDeclaration,
logger,
pathToMigrations,
})
}
@@ -0,0 +1,38 @@
import * as defaultRepositories from "@repositories"
import { LoaderOptions } from "@medusajs/modules-sdk"
import { ModulesSdkTypes } from "@medusajs/types"
import { loadCustomRepositories } from "@medusajs/utils"
import { asClass } from "awilix"
export default async ({
container,
options,
}: LoaderOptions<
| ModulesSdkTypes.ModuleServiceInitializeOptions
| ModulesSdkTypes.ModuleServiceInitializeCustomDataLayerOptions
>): Promise<void> => {
const customRepositories = (
options as ModulesSdkTypes.ModuleServiceInitializeCustomDataLayerOptions
)?.repositories
container.register({
// authenticationService: asClass(defaultServices.AuthenticationService).singleton(),
})
if (customRepositories) {
loadCustomRepositories({
defaultRepositories,
customRepositories,
container,
})
} else {
loadDefaultRepositories({ container })
}
}
function loadDefaultRepositories({ container }) {
container.register({
baseRepository: asClass(defaultRepositories.BaseRepository).singleton(),
})
}
@@ -0,0 +1,2 @@
export * from "./connection"
export * from "./container"
@@ -0,0 +1,36 @@
{
"namespaces": [
"public"
],
"name": "public",
"tables": [
{
"columns": {
"id": {
"name": "id",
"type": "text",
"unsigned": false,
"autoincrement": false,
"primary": false,
"nullable": false,
"mappedType": "text"
}
},
"name": "auth_user",
"schema": "public",
"indexes": [
{
"keyName": "auth_user_pkey",
"columnNames": [
"id"
],
"composite": false,
"primary": true,
"unique": true
}
],
"checks": [],
"foreignKeys": {}
}
]
}
@@ -0,0 +1,13 @@
import { Migration } from '@mikro-orm/migrations';
export class Migration20231220132440 extends Migration {
async up(): Promise<void> {
this.addSql('create table "auth_user" ("id" text not null, constraint "auth_user_pkey" primary key ("id"));');
}
async down(): Promise<void> {
this.addSql('drop table if exists "auth_user" cascade;');
}
}
@@ -0,0 +1,18 @@
import { generateEntityId } from "@medusajs/utils"
import { BeforeCreate, Entity, OnInit, PrimaryKey } from "@mikro-orm/core"
@Entity()
export default class AuthUser {
@PrimaryKey({ columnType: "text" })
id!: string
@BeforeCreate()
onCreate() {
this.id = generateEntityId(this.id, "authusr")
}
@OnInit()
onInit() {
this.id = generateEntityId(this.id, "authusr")
}
}
@@ -0,0 +1 @@
export { default as AuthUser } from "./auth-user"
@@ -0,0 +1,12 @@
import { ModuleExports } from "@medusajs/types"
import { AuthenticationModuleService } from "@services"
import loadConnection from "./loaders/connection"
import loadContainer from "./loaders/container"
const service = AuthenticationModuleService
const loaders = [loadContainer, loadConnection] as any
export const moduleDefinition: ModuleExports = {
service,
loaders,
}
@@ -0,0 +1 @@
export { MikroOrmBaseRepository as BaseRepository } from "@medusajs/utils"
@@ -0,0 +1,8 @@
#!/usr/bin/env node
export default (async () => {
const { revertMigration } = await import("../migration-down")
const { config } = await import("dotenv")
config()
await revertMigration()
})()
@@ -0,0 +1,8 @@
#!/usr/bin/env node
export default (async () => {
const { runMigrations } = await import("../migration-up")
const { config } = await import("dotenv")
config()
await runMigrations()
})()
@@ -0,0 +1,19 @@
#!/usr/bin/env node
import { EOL } from "os"
import { run } from "../seed"
const args = process.argv
const path = args.pop() as string
export default (async () => {
const { config } = await import("dotenv")
config()
if (!path) {
throw new Error(
`filePath is required.${EOL}Example: medusa-authentication-seed <filePath>`
)
}
await run({ path })
})()
@@ -0,0 +1,2 @@
export * from "./migration-up"
export * from "./migration-down"
@@ -0,0 +1,50 @@
import { Modules } from "@medusajs/modules-sdk";
import { LoaderOptions, Logger, ModulesSdkTypes } from "@medusajs/types";
import { DALUtils, ModulesSdkUtils } from "@medusajs/utils";
import { EntitySchema } from "@mikro-orm/core";
import * as AuthenticationModels from "@models";
/**
* This script is only valid for mikro orm managers. If a user provide a custom manager
* he is in charge of reverting the migrations.
* @param options
* @param logger
* @param moduleDeclaration
*/
export async function revertMigration({
options,
logger,
}: Pick<
LoaderOptions<ModulesSdkTypes.ModuleServiceInitializeOptions>,
"options" | "logger"
> = {}) {
logger ??= console as unknown as Logger
const dbData = ModulesSdkUtils.loadDatabaseConfig(
Modules.AUTHENTICATION,
options
)!
const entities = Object.values(
AuthenticationModels
) as unknown as EntitySchema[]
const pathToMigrations = __dirname + "/../migrations"
const orm = await DALUtils.mikroOrmCreateConnection(
dbData,
entities,
pathToMigrations
)
try {
const migrator = orm.getMigrator()
await migrator.down()
logger?.info("Authentication module migration executed")
} catch (error) {
logger?.error(
`Authentication module migration failed to run - Error: ${error}`
)
}
await orm.close()
}
@@ -0,0 +1,62 @@
import { Modules } from "@medusajs/modules-sdk";
import { LoaderOptions, Logger, ModulesSdkTypes } from "@medusajs/types";
import { DALUtils, ModulesSdkUtils } from "@medusajs/utils";
import { EntitySchema } from "@mikro-orm/core";
import * as AuthenticationModels from "@models";
/**
* This script is only valid for mikro orm managers. If a user provide a custom manager
* he is in charge of running the migrations.
* @param options
* @param logger
* @param moduleDeclaration
*/
export async function runMigrations({
options,
logger,
}: Pick<
LoaderOptions<ModulesSdkTypes.ModuleServiceInitializeOptions>,
"options" | "logger"
> = {}) {
logger ??= console as unknown as Logger
const dbData = ModulesSdkUtils.loadDatabaseConfig(
Modules.AUTHENTICATION,
options
)!
const entities = Object.values(
AuthenticationModels
) as unknown as EntitySchema[]
const pathToMigrations = __dirname + "/../migrations"
const orm = await DALUtils.mikroOrmCreateConnection(
dbData,
entities,
pathToMigrations
)
try {
const migrator = orm.getMigrator()
const pendingMigrations = await migrator.getPendingMigrations()
logger.info(
`Running pending migrations: ${JSON.stringify(
pendingMigrations,
null,
2
)}`
)
await migrator.up({
migrations: pendingMigrations.map((m) => m.name),
})
logger.info("Authentication module migration executed")
} catch (error) {
logger.error(
`Authentication module migration failed to run - Error: ${error}`
)
}
await orm.close()
}
@@ -0,0 +1,63 @@
import { Modules } from "@medusajs/modules-sdk"
import { LoaderOptions, Logger, ModulesSdkTypes } from "@medusajs/types"
import { DALUtils, ModulesSdkUtils } from "@medusajs/utils"
import { EntitySchema } from "@mikro-orm/core"
import * as AuthenticationModels from "@models"
import { EOL } from "os"
import { resolve } from "path"
export async function run({
options,
logger,
path,
}: Partial<
Pick<
LoaderOptions<ModulesSdkTypes.ModuleServiceInitializeOptions>,
"options" | "logger"
>
> & {
path: string
}) {
logger ??= console as unknown as Logger
logger.info(`Loading seed data from ${path}...`)
const { authenticationData } = await import(
resolve(process.cwd(), path)
).catch((e) => {
logger?.error(
`Failed to load seed data from ${path}. Please, provide a relative path and check that you export the following: authenticationData.${EOL}${e}`
)
throw e
})
const dbData = ModulesSdkUtils.loadDatabaseConfig(
Modules.AUTHENTICATION,
options
)!
const entities = Object.values(
AuthenticationModels
) as unknown as EntitySchema[]
const pathToMigrations = __dirname + "/../migrations"
const orm = await DALUtils.mikroOrmCreateConnection(
dbData,
entities,
pathToMigrations
)
const manager = orm.em.fork()
try {
logger.info("Seeding authentication data..")
// TODO: implement authentication seed data
// await createAuthUsers(manager, authUsersData)
} catch (e) {
logger.error(
`Failed to insert the seed data in the PostgreSQL database ${dbData.clientUrl}.${EOL}${e}`
)
}
await orm.close(true)
}
@@ -0,0 +1,5 @@
describe("Noop test", () => {
it("noop check", async () => {
expect(true).toBe(true)
})
})
@@ -0,0 +1,32 @@
import {
AuthenticationTypes,
DAL,
InternalModuleDeclaration,
ModuleJoinerConfig,
} from "@medusajs/types"
import { AuthUser } from "@models"
import { joinerConfig } from "../joiner-config"
type InjectedDependencies = {
baseRepository: DAL.RepositoryService
}
export default class AuthenticationModuleService<
TAuthUser extends AuthUser = AuthUser
> implements AuthenticationTypes.IAuthenticationModuleService
{
protected baseRepository_: DAL.RepositoryService
constructor(
{ baseRepository }: InjectedDependencies,
protected readonly moduleDeclaration: InternalModuleDeclaration
) {
this.baseRepository_ = baseRepository
}
__joinerConfig(): ModuleJoinerConfig {
return joinerConfig
}
}
@@ -0,0 +1 @@
export { default as AuthenticationModuleService } from "./authentication-module"
@@ -0,0 +1,5 @@
import { Logger } from "@medusajs/types"
export type InitializeModuleInjectableDependencies = {
logger?: Logger
}
+36
View File
@@ -0,0 +1,36 @@
{
"compilerOptions": {
"lib": ["es2020"],
"target": "es2020",
"outDir": "./dist",
"esModuleInterop": true,
"declaration": true,
"module": "commonjs",
"moduleResolution": "node",
"emitDecoratorMetadata": true,
"experimentalDecorators": true,
"sourceMap": false,
"noImplicitReturns": true,
"strictNullChecks": true,
"strictFunctionTypes": true,
"noImplicitThis": true,
"allowJs": true,
"skipLibCheck": true,
"downlevelIteration": true, // to use ES5 specific tooling
"baseUrl": ".",
"resolveJsonModule": true,
"paths": {
"@models": ["./src/models"],
"@services": ["./src/services"],
"@repositories": ["./src/repositories"]
}
},
"include": ["src"],
"exclude": [
"dist",
"./src/**/__tests__",
"./src/**/__mocks__",
"./src/**/__fixtures__",
"node_modules"
]
}
@@ -0,0 +1,8 @@
{
"extends": "./tsconfig.json",
"include": ["src", "integration-tests"],
"exclude": ["node_modules", "dist"],
"compilerOptions": {
"sourceMap": true
}
}
+17
View File
@@ -14,6 +14,7 @@ export enum Modules {
PRODUCT = "productService",
PRICING = "pricingService",
PROMOTION = "promotion",
AUTHENTICATION = "authentication",
}
export enum ModuleRegistrationName {
@@ -24,6 +25,7 @@ export enum ModuleRegistrationName {
PRODUCT = "productModuleService",
PRICING = "pricingModuleService",
PROMOTION = "promotionModuleService",
AUTHENTICATION = "authenticationModuleService",
}
export const MODULE_PACKAGE_NAMES = {
@@ -34,6 +36,7 @@ export const MODULE_PACKAGE_NAMES = {
[Modules.CACHE]: "@medusajs/cache-inmemory",
[Modules.PRICING]: "@medusajs/pricing",
[Modules.PROMOTION]: "@medusajs/promotion",
[Modules.AUTHENTICATION]: "@medusajs/authentication",
}
export const ModulesDefinition: { [key: string | Modules]: ModuleDefinition } =
@@ -137,6 +140,20 @@ export const ModulesDefinition: { [key: string | Modules]: ModuleDefinition } =
resources: MODULE_RESOURCE_TYPE.SHARED,
},
},
[Modules.AUTHENTICATION]: {
key: Modules.AUTHENTICATION,
registrationName: ModuleRegistrationName.AUTHENTICATION,
defaultPackage: false,
label: upperCaseFirst(ModuleRegistrationName.AUTHENTICATION),
isRequired: false,
canOverride: true,
isQueryable: true,
dependencies: ["logger"],
defaultModuleDeclaration: {
scope: MODULE_SCOPE.INTERNAL,
resources: MODULE_RESOURCE_TYPE.SHARED,
},
},
}
export const MODULE_DEFINITIONS: ModuleDefinition[] =
@@ -0,0 +1 @@
export * from "./service"
@@ -0,0 +1,3 @@
import { IModuleService } from "../modules-sdk"
export interface IAuthenticationModuleService extends IModuleService {}
+1
View File
@@ -1,3 +1,4 @@
export * as AuthenticationTypes from "./authentication"
export * as CacheTypes from "./cache"
export * as CommonTypes from "./common"
export * as CustomerTypes from "./customer"
+1
View File
@@ -1,4 +1,5 @@
export * from "./address"
export * from "./authentication"
export * from "./bundles"
export * from "./cache"
export * from "./cart"
+29
View File
@@ -7405,6 +7405,35 @@ __metadata:
languageName: unknown
linkType: soft
"@medusajs/authentication@workspace:packages/authentication":
version: 0.0.0-use.local
resolution: "@medusajs/authentication@workspace:packages/authentication"
dependencies:
"@medusajs/modules-sdk": ^1.12.5
"@medusajs/types": ^1.11.9
"@medusajs/utils": ^1.11.2
"@mikro-orm/cli": 5.7.12
"@mikro-orm/core": 5.7.12
"@mikro-orm/migrations": 5.7.12
"@mikro-orm/postgresql": 5.7.12
awilix: ^8.0.0
cross-env: ^5.2.1
dotenv: ^16.1.4
jest: ^29.6.3
knex: 2.4.2
medusa-test-utils: ^1.1.40
rimraf: ^3.0.2
ts-jest: ^29.1.1
ts-node: ^10.9.1
tsc-alias: ^1.8.6
typescript: ^5.1.6
bin:
medusa-authentication-migrations-down: dist/scripts/bin/run-migration-down.js
medusa-authentication-migrations-up: dist/scripts/bin/run-migration-up.js
medusa-authentication-seed: dist/scripts/bin/run-seed.js
languageName: unknown
linkType: soft
"@medusajs/cache-inmemory@workspace:*, @medusajs/cache-inmemory@workspace:packages/cache-inmemory":
version: 0.0.0-use.local
resolution: "@medusajs/cache-inmemory@workspace:packages/cache-inmemory"