DavidHancu / prisma-util

Merge multiple Prisma schema files, model inheritance, resolving name conflicts and timings reports, all in a simple tool.
Apache License 2.0
40 stars 1 forks source link

Enhanced Introspection - Accepted Proposal #7

Closed DavidHancu closed 1 year ago

DavidHancu commented 1 year ago

Scope

The aim of this proposal is to provide a sneak-peek for the new feature that will be added to Prisma Util v2.0. In this issue, I'll be outlining the process of how you can use the new Enhanced Introspection features to manipulate your schema.

Introduction

This feature will be released under the enhancedIntrospection flag and will require manual activation. Because this feature uses Project Toolchain's Generation API, it will create the typings necessary for configuring the fields of this object. All Enhanced Introspection steps will be ran before the schema is generated, to allow you to modify the models before they are written to the final file.

Configuration

To configure the Enhanced Introspection feature, you're going to have to add an introspection property to your configuration object like this:

export default {
    // Other configuration options
    introspection: {
        modelPatterns: {
            // We'll come back to this option later on.
        }
    }
}

We've created a separate introspection object to facilitate a fieldPatterns option coming in the future.

Model Patterns

Model Patterns are used for defining rules that models should follow. To offer more granular control for your schema, we've implemented the following class that will be passed to your functions:

abstract class Model {
    /**
     * The name of this model. If this parameter hasn't been modified before, it will be the table name from the database.
     */
    public abstract get name();
    /**
     * Set the name for this model.
     */
    public abstract set name(name: string);
    /**
     * Add an attribute to this model.
     * @param attribute The attribute to add. You can use the `schema-creator` module for a list of attributes.
     */
    public abstract addAttribute(attribute: string);
}

The rules for Model Patterns are split into 3 categories:

  1. Using a String

    Using a String is the easiest way to remap a table. To do it, add the following object to your introspection block:

    export default {
    // Other configuration options
    introspection: {
        modelPatterns: {
            $static: {
                "table_name": "ModelName"
            }
        }
    }
    }

    In this example, ModelName will map to table_name.

  2. Using a Function

    Using a function allows more granular control for this table. To do it, add the following object to your introspection block:

    
    import { Constraints } from "prisma-util/schema-creator";

export default { // Other configuration options introspection: { modelPatterns: { $static: { "table_name": async (model: Model) => { model.name = "ModelName"; model.addAttribute(Constraints.Model.UNIQUE(["name", "email"])); return model; } } } } }

In this example, `ModelName` will map to `table_name` and will add the `@@unique([name, email])` attribute to the model.

- ### Regex Changes
Regex changes use regexes to match model names. These regexes are ran with the `gims` flags and are placed under the `$regex` key. To configure a regex replacer, add a function like this:
```ts
export default {
    // Other configuration options
    introspection: {
        modelPatterns: {
            $regex: {
                "model_name(\\d)": async (model: Model, match: string, ...groups: string[]) => {
                    model.name = `ModelName${groups[1]}`;
                    return model;
                }
            }
        }
    }
}

One regex replacer can be called for multiple tables and will pass the correct model as a parameter. In this example, all tables that match the regex will have the underscore removed and will be changed to PascalCase.

Handling Conflicts

Because the Enhanced Introspection step is ran before the Conflict Manager has a chance to run, all models that will trigger a naming conflict will be flagged and the normal rename/skip dialog will be shown.

Prioritizing Changes

Replacers will be ran from the first key to the last key. A table can trigger multiple replacers, if the latter ones match the new name set by the previous replacers. You can use this to your advantage: by reordering the $static, $regex and $all keys you can create a powerful workflow that fits your use-case. One example of such workflow would be leaving the $all key as the last property of the object to enforce default naming conventions on all models.

DavidHancu commented 1 year ago

Feature implemented.