Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 11 additions & 1 deletion .github/actions/setup-step/action.yml
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,21 @@ author: 'Tycho Bokdam'
runs:
using: "composite"
steps:
- name: Get Node version
id: node-version
shell: bash
run: echo "version=$(node --version)" >> $GITHUB_OUTPUT

- name: Cache node modules
id: cache
uses: actions/cache@v3
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

🧩 Analysis chain

🌐 Web query:

latest version actions/cache GitHub Action

💡 Result:

The current recommended release line is v4 — use actions/cache@v4 (or pin to the latest v4 tag/sha, e.g. v4.2.0). [1][2]

Sources:
[1] actions/cache GitHub repo (README).
[2] GitHub Changelog / deprecation notice.


Update actions/cache to v4.

The current recommended release line is v4. Consider updating from actions/cache@v3 to actions/cache@v4 (or pin to the latest v4.2.0).

🤖 Prompt for AI Agents
In .github/actions/setup-step/action.yml around line 15, the workflow uses
actions/cache@v3 which is outdated; update the action reference to the v4
release line (e.g., actions/cache@v4 or pin to actions/[email protected]) so the
workflow uses the current recommended version; replace the reference and verify
the inputs/behavior remain compatible with v4.

with:
path: |
~/.cache/mongodb-memory-server
**/node_modules
key: cache-node-modules-${{ hashFiles('yarn.lock') }}
key: cache-node-modules-${{ steps.node-version.outputs.version }}-${{ hashFiles('yarn.lock') }}

- name: Install dependencies
if: steps.cache.outputs.cache-hit != 'true'
shell: bash
run: yarn install --immutable
31 changes: 31 additions & 0 deletions .github/workflows/publish-preview.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
name: Publish Preview

on:
pull_request:
workflow_dispatch:

env:
NX_BRANCH: ${{ github.event.number }}
NX_RUN_GROUP: ${{ github.run_id }}

jobs:
publish-preview:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0

- name: Use Node.js
uses: actions/setup-node@v4
with:
node-version: 22.x

- name: Setup
uses: ./.github/actions/setup-step

- name: Build
run: yarn nx run-many --target=build --all

- name: Publish Preview
run: npx pkg-pr-new publish './dist/packages/*'
3 changes: 3 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,9 @@
"@docusaurus/module-type-aliases": "3.9.1",
"@docusaurus/preset-classic": "3.9.1",
"@jscutlery/semver": "5.7.1",
"@mikro-orm/better-sqlite": "^6.4.0",
"@mikro-orm/core": "^6.4.0",
"@mikro-orm/nestjs": "^6.1.0",
"@nestjs/apollo": "^13.2.1",
"@nestjs/cli": "11.0.10",
"@nestjs/schematics": "11.0.8",
Expand Down
36 changes: 36 additions & 0 deletions packages/query-mikro-orm/.eslintrc.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
{
"extends": [
"../../.eslintrc.json"
],
"ignorePatterns": [
"!**/*"
],
"parserOptions": {
"project": "./tsconfig.json"
},
"overrides": [
{
"files": [
"*.ts",
"*.tsx",
"*.js",
"*.jsx"
],
"rules": {}
},
{
"files": [
"*.ts",
"*.tsx"
],
"rules": {}
},
{
"files": [
"*.js",
"*.jsx"
],
"rules": {}
}
]
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
import { BetterSqliteDriver } from '@mikro-orm/better-sqlite'
import { MikroORM, Options } from '@mikro-orm/core'

import { seed } from './seeds'
import { TestEntity } from './test.entity'
import { TestRelation } from './test-relation.entity'

export const CONNECTION_OPTIONS: Options<BetterSqliteDriver> = {
driver: BetterSqliteDriver,
dbName: ':memory:',
entities: [TestEntity, TestRelation],
allowGlobalContext: true
}

export async function createTestConnection(): Promise<MikroORM<BetterSqliteDriver>> {
const orm = await MikroORM.init(CONNECTION_OPTIONS)
const generator = orm.getSchemaGenerator()
await generator.createSchema()
return orm
}

export async function truncate(orm: MikroORM<BetterSqliteDriver>): Promise<void> {
const em = orm.em.fork()
await em.nativeDelete(TestRelation, {})
await em.nativeDelete(TestEntity, {})
}

export async function refresh(orm: MikroORM<BetterSqliteDriver>): Promise<void> {
await truncate(orm)
const em = orm.em.fork()
await seed(em)
}
4 changes: 4 additions & 0 deletions packages/query-mikro-orm/__tests__/__fixtures__/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
export * from './connection.fixture'
export * from './seeds'
export * from './test.entity'
export * from './test-relation.entity'
72 changes: 72 additions & 0 deletions packages/query-mikro-orm/__tests__/__fixtures__/seeds.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
import { EntityManager } from '@mikro-orm/core'

import { TestEntity } from './test.entity'
import { TestRelation } from './test-relation.entity'

export const TEST_ENTITIES: Omit<TestEntity, 'testRelations' | 'manyToOneRelation' | 'manyTestRelations' | 'oneTestRelation'>[] =
[1, 2, 3, 4, 5, 6, 7, 8, 9, 10].map((i) => ({
id: `test-entity-${i}`,
boolType: i % 2 === 0,
dateType: new Date(`2020-02-${String(i).padStart(2, '0')} 12:00`),
numberType: i,
stringType: `foo${i}`
}))

export const TEST_RELATIONS: Omit<TestRelation, 'testEntity' | 'manyTestEntities' | 'oneTestEntity'>[] = TEST_ENTITIES.flatMap(
(te) => [
{
id: `test-relations-${te.id}-1`,
relationName: `${te.stringType}-test-relation-one`
},
{
id: `test-relations-${te.id}-2`,
relationName: `${te.stringType}-test-relation-two`
},
{
id: `test-relations-${te.id}-3`,
relationName: `${te.stringType}-test-relation-three`
}
]
)

export async function seed(em: EntityManager): Promise<void> {
const testEntities = TEST_ENTITIES.map((data) => {
const entity = new TestEntity()
Object.assign(entity, data)
return entity
})

for (const entity of testEntities) {
em.persist(entity)
}

const testRelations = TEST_RELATIONS.map((data, index) => {
const relation = new TestRelation()
Object.assign(relation, data)
const entityIndex = Math.floor(index / 3)
relation.testEntity = em.getReference(TestEntity, testEntities[entityIndex].id)
return relation
})

for (const relation of testRelations) {
em.persist(relation)
}

await em.flush()

for (let i = 0; i < testEntities.length; i++) {
const entity = testEntities[i]
const firstRelation = testRelations[i * 3]

entity.oneTestRelation = em.getReference(TestRelation, firstRelation.id)

if (entity.numberType % 2 === 0) {
const twoRelations = testRelations.filter((tr) => tr.relationName.endsWith('two'))
for (const rel of twoRelations) {
entity.manyTestRelations.add(rel)
}
}
}

await em.flush()
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import { Collection, Entity, ManyToMany, ManyToOne, OneToOne, PrimaryKey, Property } from '@mikro-orm/core'

import { TestEntity } from './test.entity'

@Entity()
export class TestRelation {
@PrimaryKey()
id!: string

@Property()
relationName!: string

@ManyToOne(() => TestEntity, { nullable: true })
testEntity?: TestEntity

@ManyToMany(() => TestEntity, (entity) => entity.manyTestRelations, { owner: true })
manyTestEntities = new Collection<TestEntity>(this)

@OneToOne(() => TestEntity, (entity) => entity.oneTestRelation, { nullable: true })
oneTestEntity?: TestEntity
}
33 changes: 33 additions & 0 deletions packages/query-mikro-orm/__tests__/__fixtures__/test.entity.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
import { Collection, Entity, ManyToMany, ManyToOne, OneToMany, OneToOne, PrimaryKey, Property } from '@mikro-orm/core'

import { TestRelation } from './test-relation.entity'

@Entity()
export class TestEntity {
@PrimaryKey()
id!: string

@Property()
stringType!: string

@Property()
boolType!: boolean

@Property()
numberType!: number

@Property()
dateType!: Date

@OneToMany(() => TestRelation, (relation) => relation.testEntity)
testRelations = new Collection<TestRelation>(this)

@ManyToOne(() => TestRelation, { nullable: true })
manyToOneRelation?: TestRelation

@ManyToMany(() => TestRelation, (relation) => relation.manyTestEntities)
manyTestRelations = new Collection<TestRelation>(this)

@OneToOne(() => TestRelation, (relation) => relation.oneTestEntity, { nullable: true, owner: true })
oneTestRelation?: TestRelation
}
69 changes: 69 additions & 0 deletions packages/query-mikro-orm/__tests__/module.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
import { MikroORM } from '@mikro-orm/core'
import { MikroOrmModule } from '@mikro-orm/nestjs'
import { SqliteDriver } from '@mikro-orm/sqlite'
import { Test, TestingModule } from '@nestjs/testing'
import { getQueryServiceToken } from '@ptc-org/nestjs-query-core'

import { MikroOrmQueryService, NestjsQueryMikroOrmModule } from '../src'
import { CONNECTION_OPTIONS, TestEntity, TestRelation } from './__fixtures__'

describe('NestjsQueryMikroOrmModule', () => {
let moduleRef: TestingModule
let orm: MikroORM<SqliteDriver>

afterEach(async () => {
if (orm) {
await orm.close()
}
if (moduleRef) {
await moduleRef.close()
}
})

describe('forFeature', () => {
it('should create query services for entities', async () => {
moduleRef = await Test.createTestingModule({
imports: [MikroOrmModule.forRoot(CONNECTION_OPTIONS), NestjsQueryMikroOrmModule.forFeature([TestEntity, TestRelation])]
}).compile()

orm = moduleRef.get(MikroORM)
await orm.getSchemaGenerator().createSchema()

const testEntityService = moduleRef.get<MikroOrmQueryService<TestEntity>>(getQueryServiceToken(TestEntity))
const testRelationService = moduleRef.get<MikroOrmQueryService<TestRelation>>(getQueryServiceToken(TestRelation))

expect(testEntityService).toBeInstanceOf(MikroOrmQueryService)
expect(testRelationService).toBeInstanceOf(MikroOrmQueryService)
})

it('should create query service with custom DTO', async () => {
class TestEntityDTO {
id!: string

stringType!: string
}

moduleRef = await Test.createTestingModule({
imports: [
MikroOrmModule.forRoot(CONNECTION_OPTIONS),
NestjsQueryMikroOrmModule.forFeature([{ entity: TestEntity, dto: TestEntityDTO }])
]
}).compile()

orm = moduleRef.get(MikroORM)
await orm.getSchemaGenerator().createSchema()

const service = moduleRef.get<MikroOrmQueryService<TestEntityDTO, TestEntity>>(getQueryServiceToken(TestEntityDTO))
expect(service).toBeInstanceOf(MikroOrmQueryService)
})

it('should export MikroOrmModule', async () => {
moduleRef = await Test.createTestingModule({
imports: [MikroOrmModule.forRoot(CONNECTION_OPTIONS), NestjsQueryMikroOrmModule.forFeature([TestEntity])]
}).compile()

orm = moduleRef.get(MikroORM)
expect(orm).toBeDefined()
})
})
})
49 changes: 49 additions & 0 deletions packages/query-mikro-orm/__tests__/providers.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
import { EntityRepository } from '@mikro-orm/core'
import { getRepositoryToken } from '@mikro-orm/nestjs'
import { getQueryServiceToken } from '@ptc-org/nestjs-query-core'

import { createMikroOrmQueryServiceProviders, MikroOrmQueryService } from '../src'
import { TestEntity, TestRelation } from './__fixtures__'

describe('createMikroOrmQueryServiceProviders', () => {
it('should create providers for entity classes', () => {
const providers = createMikroOrmQueryServiceProviders([TestEntity, TestRelation])

expect(providers).toHaveLength(2)
expect(providers[0].provide).toBe(getQueryServiceToken(TestEntity))
expect(providers[1].provide).toBe(getQueryServiceToken(TestRelation))
})

it('should create provider with custom DTO', () => {
class TestEntityDTO {
id!: string
}

const providers = createMikroOrmQueryServiceProviders([{ entity: TestEntity, dto: TestEntityDTO }])

expect(providers).toHaveLength(1)
expect(providers[0].provide).toBe(getQueryServiceToken(TestEntityDTO))
})

it('should inject repository token', () => {
const providers = createMikroOrmQueryServiceProviders([TestEntity])

expect(providers[0].inject).toContain(getRepositoryToken(TestEntity))
})

it('should inject repository token with data source', () => {
const dataSource = 'custom-data-source'
const providers = createMikroOrmQueryServiceProviders([TestEntity], dataSource)

expect(providers[0].inject).toContain(getRepositoryToken(TestEntity, dataSource))
})

it('should create MikroOrmQueryService instance via factory', () => {
const providers = createMikroOrmQueryServiceProviders([TestEntity])
const mockRepo = {} as EntityRepository<TestEntity>

const service = providers[0].useFactory(mockRepo)

expect(service).toBeInstanceOf(MikroOrmQueryService)
})
})
Loading
Loading