Skip to content

Commit

Permalink
Merge pull request #257 from Quramy/feature/isolate_prisma_instance
Browse files Browse the repository at this point in the history
Isolate PrismaClient instance for each fabbrica file
  • Loading branch information
Quramy authored Jan 15, 2024
2 parents fcd0470 + 664b3f4 commit d1101c9
Show file tree
Hide file tree
Showing 13 changed files with 179 additions and 34 deletions.
3 changes: 2 additions & 1 deletion examples/example-prj/src/__generated__/fabbrica/index.d.ts

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

14 changes: 8 additions & 6 deletions examples/example-prj/src/__generated__/fabbrica/index.js

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
import type { PrismaClient } from "./__generated__/client";
import { initialize, defineUserFactory } from "./__generated__/fabbrica";

import type { PrismaClient as PrismaClientAlt } from "./__generated__/client";
import {
initialize as initializeAlt,
defineUserFactory as defineAltUserFactory,
} from "../field-variation/__generated__/fabbrica";

describe("initialize function", () => {
describe("initialize function binds prisma instance for each fabbrica script", () => {
const prismaClientMock = {
user: {
create: jest.fn(),
},
};
const prismaClientMockAlt = {
user: {
create: jest.fn(),
},
};
const UserFactory = defineUserFactory();
const AltUserFactory = defineAltUserFactory();

beforeAll(() => {
initialize({ prisma: prismaClientMock as unknown as PrismaClient });
initializeAlt({ prisma: prismaClientMockAlt as unknown as PrismaClientAlt });
});

beforeEach(() => {
prismaClientMock.user.create.mockReset();
prismaClientMockAlt.user.create.mockReset();
});

it("should be called multiple clients called when multiple fabbrica are executed", async () => {
await UserFactory.create();
await AltUserFactory.create();

expect(prismaClientMock.user.create).toHaveBeenCalledTimes(1);
expect(prismaClientMockAlt.user.create).toHaveBeenCalledTimes(1);
});

it("should not be called a client unless the corresponding fabbrica is executed", async () => {
await UserFactory.create();

expect(prismaClientMock.user.create).toHaveBeenCalledTimes(1);
expect(prismaClientMockAlt.user.create).not.toHaveBeenCalled();
});
});
});
16 changes: 9 additions & 7 deletions packages/prisma-fabbrica/src/clientHolder.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,18 +3,20 @@ export type PrismaClientLike = {
$disconnect: () => PromiseLike<unknown>;
};

let _client: undefined | (() => PrismaClientLike);
const DEFAULT_CLIENT_MAP_KEY = Symbol("scope:singleton");
const clientMap: Map<unknown, () => PrismaClientLike> = new Map();

export function resetClient() {
_client = undefined;
export function resetClient(key: unknown = DEFAULT_CLIENT_MAP_KEY) {
clientMap.delete(key);
}

export function setClient<T extends PrismaClientLike>(client: T | (() => T)) {
_client = typeof client === "function" ? client : () => client;
export function setClient<T extends PrismaClientLike>(client: T | (() => T), key: unknown = DEFAULT_CLIENT_MAP_KEY) {
const value = typeof client === "function" ? client : () => client;
clientMap.set(key, value);
}

export function getClient<T extends PrismaClientLike>() {
const client = _client?.();
export function getClient<T extends PrismaClientLike>(key?: unknown) {
const client = (clientMap.get(key) ?? clientMap.get(DEFAULT_CLIENT_MAP_KEY))?.();
if (!client) {
throw new Error("No prisma client");
}
Expand Down
56 changes: 51 additions & 5 deletions packages/prisma-fabbrica/src/initialize.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { resetClient, setClient, PrismaClientLike } from "./clientHolder";
import { resetClient, getClient as getClientDelegate, setClient, PrismaClientLike } from "./clientHolder";
import { resetSequence } from "./helpers";
import { resetScalarFieldValueGenerator } from "./internal";
export { resetSequence } from "./helpers";
Expand All @@ -8,13 +8,59 @@ export type InitializeOptions = {
readonly prisma: PrismaClientLike | (() => PrismaClientLike);
};

export function reset() {
resetClient();
resetSequence();
resetScalarFieldValueGenerator();
/**
*
* Create functions to initialize PrismaClient instance.
*
* This function creates "separated" initializer.
*
* ```ts
* // fabbrica_a.ts
* import { createInitializer } from "prisma-fabbrica/lib/internal";
*
* const initializer = createInitializer();
* const { getClient } = initializer;
* export const { initialize } = initializer;
* ```
*
* The above `initialize` function does not affect other fabbrica files.
*
*/
export function createInitializer() {
const initializerKey = Symbol("scope:createInitializer");

const initialize = (options: InitializeOptions) => {
setClient(options.prisma, initializerKey);
resetSequence();
};

const getClient = <T extends PrismaClientLike>() => {
return getClientDelegate<T>(initializerKey);
};

const reset = () => {
resetClient(initializerKey);
resetSequence();
resetScalarFieldValueGenerator();
};

return {
initialize,
reset,
getClient,
};
}

// NOTE:
//
// This static initialize and reset functions are exported only for scripts/jest-prisma/setup.ts
export function initialize(options: InitializeOptions) {
setClient(options.prisma);
resetSequence();
}

export function reset() {
resetClient();
resetSequence();
resetScalarFieldValueGenerator();
}
2 changes: 1 addition & 1 deletion packages/prisma-fabbrica/src/internal.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
export { getClient } from "./clientHolder";
export { ModelWithFields, createScreener } from "./relations/screen";
export { Resolver, normalizeResolver, normalizeList, getSequenceCounter } from "./helpers";
export { initialize, resetSequence } from "./initialize";
export { createInitializer, initialize, resetSequence } from "./initialize";
export {
getScalarFieldValueGenerator,
registerScalarFieldValueGenerator,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,19 @@ exports[`getSourceFile generates TypeScript AST 1`] = `
"import type { User } from "@prisma/client";
import { Prisma } from "@prisma/client";
import type { PrismaClient } from "@prisma/client";
import { getClient, ModelWithFields, createScreener, getScalarFieldValueGenerator, Resolver, normalizeResolver, normalizeList, getSequenceCounter, } from "@quramy/prisma-fabbrica/lib/internal";
export { initialize, resetSequence, registerScalarFieldValueGenerator, resetScalarFieldValueGenerator } from "@quramy/prisma-fabbrica/lib/internal";
import { createInitializer, ModelWithFields, createScreener, getScalarFieldValueGenerator, Resolver, normalizeResolver, normalizeList, getSequenceCounter, } from "@quramy/prisma-fabbrica/lib/internal";
export { resetSequence, registerScalarFieldValueGenerator, resetScalarFieldValueGenerator } from "@quramy/prisma-fabbrica/lib/internal";
type BuildDataOptions = {
readonly seq: number;
};
const initializer = createInitializer();
const { getClient } = initializer;
export const { initialize } = initializer;
const factoryFor = Symbol("factoryFor");
const modelFieldDefinitions: ModelWithFields[] = [{
Expand Down
12 changes: 10 additions & 2 deletions packages/prisma-fabbrica/src/templates/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,7 @@ export const header = (prismaClientModuleSpecifier: string) =>
import { Prisma } from ${() => ast.stringLiteral(prismaClientModuleSpecifier)};
import type { PrismaClient } from ${() => ast.stringLiteral(prismaClientModuleSpecifier)};
import {
getClient,
createInitializer,
ModelWithFields,
createScreener,
getScalarFieldValueGenerator,
Expand All @@ -90,7 +90,7 @@ export const header = (prismaClientModuleSpecifier: string) =>
normalizeList,
getSequenceCounter,
} from "@quramy/prisma-fabbrica/lib/internal";
export { initialize, resetSequence, registerScalarFieldValueGenerator, resetScalarFieldValueGenerator } from "@quramy/prisma-fabbrica/lib/internal";
export { resetSequence, registerScalarFieldValueGenerator, resetScalarFieldValueGenerator } from "@quramy/prisma-fabbrica/lib/internal";
`();

export const buildDataOptions = () =>
Expand All @@ -105,6 +105,13 @@ export const importStatement = (specifier: string, prismaClientModuleSpecifier:
import type { ${() => ast.identifier(specifier)} } from ${() => ast.stringLiteral(prismaClientModuleSpecifier)};
`();

export const initializer = () =>
template.sourceFile`
const initializer = createInitializer();
const { getClient } = initializer;
export const { initialize } = initializer;
`();

export const symbols = () =>
template.sourceFile`
const factoryFor = Symbol("factoryFor");
Expand Down Expand Up @@ -513,6 +520,7 @@ export function getSourceFile({
...modelEnums.map(enumName => importStatement(enumName, prismaClientModuleSpecifier)),
...header(prismaClientModuleSpecifier).statements,
insertLeadingBreakMarker(buildDataOptions()),
...insertLeadingBreakMarker(initializer().statements),
...insertLeadingBreakMarker(symbols().statements),
insertLeadingBreakMarker(modelFieldDefinitions(document.datamodel.models)),
...document.datamodel.models
Expand Down

0 comments on commit d1101c9

Please sign in to comment.