Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

chore: remove aux fields (unused since a few release ago) #662

Merged
merged 1 commit into from
Sep 1, 2023
Merged
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
3 changes: 1 addition & 2 deletions packages/plugins/openapi/src/rest-generator.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@

import type { DMMF } from '@prisma/generator-helper';
import {
AUXILIARY_FIELDS,
analyzePolicies,
getDataModels,
hasAttribute,
Expand Down Expand Up @@ -829,7 +828,7 @@ export class RESTfulOpenAPIGenerator extends OpenAPIGeneratorBase {
}

private generateModelEntity(model: DataModel, mode: 'read' | 'create' | 'update'): OAPI.SchemaObject {
const fields = model.fields.filter((f) => !AUXILIARY_FIELDS.includes(f.name) && !isIdField(f));
const fields = model.fields.filter((f) => !isIdField(f));

const attributes: Record<string, OAPI.SchemaObject> = {};
const relationships: Record<string, OAPI.ReferenceObject> = {};
Expand Down
17 changes: 7 additions & 10 deletions packages/plugins/openapi/src/rpc-generator.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
// Inspired by: https://github.com/omar-dulaimi/prisma-trpc-generator

import type { DMMF } from '@prisma/generator-helper';
import { analyzePolicies, AUXILIARY_FIELDS, PluginError, requireOption, resolvePath } from '@zenstackhq/sdk';
import { analyzePolicies, PluginError, requireOption, resolvePath } from '@zenstackhq/sdk';
import { DataModel, isDataModel } from '@zenstackhq/sdk/ast';
import {
addMissingInputObjectTypesForAggregate,
Expand Down Expand Up @@ -681,17 +681,16 @@ export class RPCOpenAPIGenerator extends OpenAPIGeneratorBase {
private generateEnumComponent(_enum: DMMF.SchemaEnum): OAPI.SchemaObject {
const schema: OAPI.SchemaObject = {
type: 'string',
enum: _enum.values.filter((f) => !AUXILIARY_FIELDS.includes(f)),
enum: _enum.values,
};
return schema;
}

private generateEntityComponent(model: DMMF.Model): OAPI.SchemaObject {
const properties: Record<string, OAPI.ReferenceObject | OAPI.SchemaObject> = {};

const fields = model.fields.filter((f) => !AUXILIARY_FIELDS.includes(f.name));
const required: string[] = [];
for (const field of fields) {
for (const field of model.fields) {
properties[field.name] = this.generateField(field);
if (field.isRequired && !(field.relationName && field.isList)) {
required.push(field.name);
Expand Down Expand Up @@ -721,8 +720,7 @@ export class RPCOpenAPIGenerator extends OpenAPIGeneratorBase {

private generateInputComponent(input: DMMF.InputType): OAPI.SchemaObject {
const properties: Record<string, OAPI.ReferenceObject | OAPI.SchemaObject> = {};
const fields = input.fields.filter((f) => !AUXILIARY_FIELDS.includes(f.name));
for (const field of fields) {
for (const field of input.fields) {
const options = field.inputTypes
.filter(
(f) =>
Expand All @@ -737,14 +735,13 @@ export class RPCOpenAPIGenerator extends OpenAPIGeneratorBase {
}

const result: OAPI.SchemaObject = { type: 'object', properties };
this.setInputRequired(fields, result);
this.setInputRequired(input.fields, result);
return result;
}

private generateOutputComponent(output: DMMF.OutputType): OAPI.SchemaObject {
const properties: Record<string, OAPI.ReferenceObject | OAPI.SchemaObject> = {};
const fields = output.fields.filter((f) => !AUXILIARY_FIELDS.includes(f.name));
for (const field of fields) {
for (const field of output.fields) {
let outputType: OAPI.ReferenceObject | OAPI.SchemaObject;
switch (field.outputType.location) {
case 'scalar':
Expand All @@ -762,7 +759,7 @@ export class RPCOpenAPIGenerator extends OpenAPIGeneratorBase {
}

const result: OAPI.SchemaObject = { type: 'object', properties };
this.setOutputRequired(fields, result);
this.setOutputRequired(output.fields, result);
return result;
}

Expand Down
15 changes: 0 additions & 15 deletions packages/runtime/src/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,21 +3,6 @@
*/
export const DEFAULT_PASSWORD_SALT_LENGTH = 12;

/**
* Auxiliary database field for supporting policy check for nested writes
*/
export const TRANSACTION_FIELD_NAME = 'zenstack_transaction';

/**
* Auxiliary database field for building up policy check queries
*/
export const GUARD_FIELD_NAME = 'zenstack_guard';

/**
* All Auxiliary fields.
*/
export const AUXILIARY_FIELDS = [TRANSACTION_FIELD_NAME, GUARD_FIELD_NAME];

/**
* Reasons for a CRUD operation to fail
*/
Expand Down
8 changes: 0 additions & 8 deletions packages/runtime/src/enhancements/policy/policy-utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@ import { lowerCaseFirst } from 'lower-case-first';
import { upperCaseFirst } from 'upper-case-first';
import { fromZodError } from 'zod-validation-error';
import {
AUXILIARY_FIELDS,
CrudFailureReason,
FIELD_LEVEL_READ_CHECKER_PREFIX,
FIELD_LEVEL_READ_CHECKER_SELECTOR,
Expand Down Expand Up @@ -952,13 +951,6 @@ export class PolicyUtil {
return;
}

// strip auxiliary fields
for (const auxField of AUXILIARY_FIELDS) {
if (auxField in entityData) {
delete entityData[auxField];
}
}

for (const [field, fieldData] of Object.entries(entityData)) {
if (fieldData === undefined) {
continue;
Expand Down
3 changes: 1 addition & 2 deletions packages/runtime/src/enhancements/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,15 +3,14 @@
import { lowerCaseFirst } from 'lower-case-first';
import path from 'path';
import * as util from 'util';
import { AUXILIARY_FIELDS } from '../constants';
import { DbClientContract } from '../types';
import { ModelMeta } from './types';

/**
* Gets field names in a data model entity, filtering out internal fields.
*/
export function getModelFields(data: object) {
return data ? Object.keys(data).filter((f) => !AUXILIARY_FIELDS.includes(f)) : [];
return data ? Object.keys(data) : [];
}

/**
Expand Down
9 changes: 2 additions & 7 deletions packages/schema/src/cli/config.ts
Original file line number Diff line number Diff line change
@@ -1,15 +1,10 @@
import { GUARD_FIELD_NAME, TRANSACTION_FIELD_NAME } from '@zenstackhq/sdk';
import fs from 'fs';
import z, { ZodError } from 'zod';
import { fromZodError } from 'zod-validation-error';
import { CliError } from './cli-error';

const schema = z
.object({
guardFieldName: z.string().default(GUARD_FIELD_NAME),
transactionFieldName: z.string().default(TRANSACTION_FIELD_NAME),
})
.strict();
// TODO: future use
const schema = z.object({});

export type ConfigType = z.infer<typeof schema>;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -559,7 +559,7 @@ export class ExpressionWriter {
// TODO: do we need short-circuit for logical operators?

if (operator === '&&') {
// // && short-circuit: left && right -> left ? right : { zenstack_guard: false }
// // && short-circuit: left && right -> left ? right : FALSE
// if (!this.hasFieldAccess(expr.left)) {
// this.plain(expr.left);
// this.writer.write(' ? ');
Expand All @@ -573,7 +573,7 @@ export class ExpressionWriter {
});
// }
} else {
// // || short-circuit: left || right -> left ? { zenstack_guard: true } : right
// // || short-circuit: left || right -> left ? TRUE : right
// if (!this.hasFieldAccess(expr.left)) {
// this.plain(expr.left);
// this.writer.write(' ? ');
Expand Down
18 changes: 7 additions & 11 deletions packages/schema/src/plugins/prisma/prisma-builder.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
import { AUXILIARY_FIELDS } from '@zenstackhq/sdk';
import indentString from './indent-string';

/**
Expand Down Expand Up @@ -155,19 +154,16 @@ export class Model extends ContainerDeclaration {
}

toString(): string {
const auxiliaryFields = this.fields.filter((f) => AUXILIARY_FIELDS.includes(f.name));
// eslint-disable-next-line @typescript-eslint/no-explicit-any
const result: any[] = this.fields.filter((f) => !AUXILIARY_FIELDS.includes(f.name));

if (auxiliaryFields.length > 0) {
// Add a blank line before the auxiliary fields
result.push('', ...auxiliaryFields);
if (this.attributes.length > 0) {
// Add a blank line before the attributes
result.push('');
}
const result: any[] = [...this.fields];

if (this.attributes.length > 0) {
// Add a blank line before the attributes
result.push('');
}

result.push(...this.attributes);

return (
super.toString() +
`${this.isView ? 'view' : 'model'} ${this.name} {\n` +
Expand Down
89 changes: 3 additions & 86 deletions packages/schema/src/plugins/prisma/schema-generator.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,18 +27,14 @@ import { match } from 'ts-pattern';

import { PRISMA_MINIMUM_VERSION } from '@zenstackhq/runtime';
import {
analyzePolicies,
getDataModels,
getDMMF,
getLiteral,
getLiteralArray,
getPrismaVersion,
GUARD_FIELD_NAME,
PluginError,
PluginOptions,
resolved,
resolvePath,
TRANSACTION_FIELD_NAME,
} from '@zenstackhq/sdk';
import fs from 'fs';
import { writeFile } from 'fs/promises';
Expand Down Expand Up @@ -85,7 +81,7 @@ export default class PrismaSchemaGenerator {

`;

async generate(model: Model, options: PluginOptions, config?: Record<string, string>) {
async generate(model: Model, options: PluginOptions, _config?: Record<string, string>) {
const warnings: string[] = [];

const prismaVersion = getPrismaVersion();
Expand All @@ -108,7 +104,7 @@ export default class PrismaSchemaGenerator {
break;

case DataModel:
this.generateModel(prisma, decl as DataModel, config);
this.generateModel(prisma, decl as DataModel);
break;

case GeneratorDecl:
Expand Down Expand Up @@ -296,53 +292,12 @@ export default class PrismaSchemaGenerator {
}
}

private generateModel(prisma: PrismaModel, decl: DataModel, config?: Record<string, string>) {
private generateModel(prisma: PrismaModel, decl: DataModel) {
const model = decl.isView ? prisma.addView(decl.name) : prisma.addModel(decl.name);
for (const field of decl.fields) {
this.generateModelField(model, field);
}

if (this.shouldGenerateAuxFields(decl)) {
// generate auxiliary fields for policy check

// add an "zenstack_guard" field for dealing with boolean conditions
const guardField = model.addField(GUARD_FIELD_NAME, 'Boolean', [
new PrismaFieldAttribute('@default', [
new PrismaAttributeArg(undefined, new PrismaAttributeArgValue('Boolean', true)),
]),
]);

if (config?.guardFieldName && config?.guardFieldName !== GUARD_FIELD_NAME) {
// generate a @map to rename field in the database
guardField.addAttribute('@map', [
new PrismaAttributeArg(undefined, new PrismaAttributeArgValue('String', config.guardFieldName)),
]);
}

// add an "zenstack_transaction" field for tracking records created/updated with nested writes
const transactionField = model.addField(TRANSACTION_FIELD_NAME, 'String?');

// create an index for "zenstack_transaction" field
model.addAttribute('@@index', [
new PrismaAttributeArg(
undefined,
new PrismaAttributeArgValue('Array', [
new PrismaAttributeArgValue('FieldReference', TRANSACTION_FIELD_NAME),
])
),
]);

if (config?.transactionFieldName && config?.transactionFieldName !== TRANSACTION_FIELD_NAME) {
// generate a @map to rename field in the database
transactionField.addAttribute('@map', [
new PrismaAttributeArg(
undefined,
new PrismaAttributeArgValue('String', config.transactionFieldName)
),
]);
}
}

for (const attr of decl.attributes.filter((attr) => this.isPrismaAttribute(attr))) {
this.generateContainerAttribute(model, attr);
}
Expand All @@ -355,44 +310,6 @@ export default class PrismaSchemaGenerator {
decl.comments.forEach((c) => model.addComment(c));
}

private shouldGenerateAuxFields(decl: DataModel) {
if (decl.isView) {
return false;
}

const { allowAll, denyAll, hasFieldValidation } = analyzePolicies(decl);

if (!allowAll && !denyAll) {
// has policy conditions
return true;
}

if (hasFieldValidation) {
return true;
}

// check if the model is related by other models, if so
// aux fields are needed for nested queries
const root = decl.$container;
for (const model of getDataModels(root)) {
if (model === decl) {
continue;
}
for (const field of model.fields) {
if (field.type.reference?.ref === decl) {
// found a relation with policies
const otherPolicies = analyzePolicies(model);
if ((!otherPolicies.allowAll && !otherPolicies.denyAll) || otherPolicies.hasFieldValidation) {
// the relating side has policies
return true;
}
}
}
}

return false;
}

private isPrismaAttribute(attr: DataModelAttribute | DataModelFieldAttribute) {
if (!attr.decl.ref) {
return false;
Expand Down
17 changes: 1 addition & 16 deletions packages/schema/src/plugins/zod/generator.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
import { ConnectorType, DMMF } from '@prisma/generator-helper';
import {
AUXILIARY_FIELDS,
PluginOptions,
createProject,
emitProject,
Expand Down Expand Up @@ -120,18 +119,6 @@ async function generateCommonSchemas(project: Project, output: string) {
`
import { z } from 'zod';
export const DecimalSchema = z.union([z.number(), z.string(), z.object({d: z.number().array(), e: z.number(), s: z.number()}).passthrough()]);

// https://stackoverflow.com/a/54487392/20415796
type OmitDistributive<T, K extends PropertyKey> = T extends any ? (T extends object ? OmitRecursively<T, K> : T) : never;
type OmitRecursively<T extends any, K extends PropertyKey> = Omit<
{ [P in keyof T]: OmitDistributive<T[P], K> },
K
>;

/**
* Strips auxiliary fields recursively
*/
export type Purge<T> = OmitRecursively<T, ${AUXILIARY_FIELDS.map((f) => "'" + f + "'").join('|')}>;
`,
{ overwrite: true }
);
Expand Down Expand Up @@ -197,10 +184,8 @@ async function generateModelSchema(model: DataModel, project: Project, output: s
sf.replaceWithText((writer) => {
const fields = model.fields.filter(
(field) =>
!AUXILIARY_FIELDS.includes(field.name) &&
// scalar fields only
!isDataModel(field.type.reference?.ref) &&
!isForeignKeyField(field)
!isDataModel(field.type.reference?.ref) && !isForeignKeyField(field)
);

writer.writeLine('/* eslint-disable */');
Expand Down
Loading
Loading