@contember/schema-utils
Advanced tools
Comparing version 0.12.0-alpha.2 to 0.12.0-alpha.4
@@ -25,3 +25,4 @@ import { Model } from '@contember/schema'; | ||
export declare const isOwningRelation: (relation: Model.Relation) => relation is Model.OwningRelation; | ||
export declare const isColumn: (field: Model.AnyField) => field is Model.AnyColumn<Model.ColumnType>; | ||
export declare const emptyModelSchema: Model.Schema; | ||
//# sourceMappingURL=modelUtils.d.ts.map |
"use strict"; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
exports.emptyModelSchema = exports.isOwningRelation = exports.isInverseRelation = exports.isRelation = exports.acceptRelationTypeVisitor = exports.acceptFieldVisitor = exports.acceptEveryFieldVisitor = exports.getTargetEntity = exports.getColumnType = exports.tryGetColumnName = exports.getColumnName = exports.getEntity = exports.ModelError = exports.ModelErrorCode = void 0; | ||
exports.emptyModelSchema = exports.isColumn = exports.isOwningRelation = exports.isInverseRelation = exports.isRelation = exports.acceptRelationTypeVisitor = exports.acceptFieldVisitor = exports.acceptEveryFieldVisitor = exports.getTargetEntity = exports.getColumnType = exports.tryGetColumnName = exports.getColumnName = exports.getEntity = exports.ModelError = exports.ModelErrorCode = void 0; | ||
const utils_1 = require("../utils"); | ||
@@ -193,3 +193,5 @@ const schema_1 = require("@contember/schema"); | ||
exports.isOwningRelation = isOwningRelation; | ||
const isColumn = (field) => utils_1.isIt(field, 'columnType'); | ||
exports.isColumn = isColumn; | ||
exports.emptyModelSchema = { entities: {}, enums: {} }; | ||
//# sourceMappingURL=modelUtils.js.map |
@@ -7,27 +7,31 @@ "use strict"; | ||
const normalizeSchema = (schema) => { | ||
if (!schema.acl.roles[schema_1.ProjectRole.ADMIN]) { | ||
schema = { | ||
...schema, | ||
acl: { | ||
...schema.acl, | ||
roles: { | ||
...schema.acl.roles, | ||
[schema_1.ProjectRole.ADMIN]: { | ||
stages: '*', | ||
variables: {}, | ||
entities: new acl_1.AllowAllPermissionFactory().create(schema.model), | ||
s3: { | ||
'**': { | ||
upload: true, | ||
read: true, | ||
}, | ||
var _a; | ||
return { | ||
...schema, | ||
acl: { | ||
...schema.acl, | ||
roles: { | ||
...schema.acl.roles, | ||
[schema_1.ProjectRole.ADMIN]: { | ||
stages: '*', | ||
variables: {}, | ||
entities: new acl_1.AllowAllPermissionFactory().create(schema.model), | ||
s3: { | ||
'**': { | ||
upload: true, | ||
read: true, | ||
}, | ||
}, | ||
...(((_a = schema.acl.roles) === null || _a === void 0 ? void 0 : _a[schema_1.ProjectRole.ADMIN]) || {}), | ||
}, | ||
[schema_1.ProjectRole.MIGRATION]: { | ||
stages: '*', | ||
entities: {}, | ||
variables: {}, | ||
}, | ||
}, | ||
}; | ||
} | ||
return schema; | ||
}, | ||
}; | ||
}; | ||
exports.normalizeSchema = normalizeSchema; | ||
//# sourceMappingURL=schemaNormalizer.js.map |
@@ -43,3 +43,3 @@ "use strict"; | ||
// } | ||
const rolePermissions = this.validateRolePermissions(roles[role], validRoles, errorBuilder.for(role)); | ||
const rolePermissions = this.validateRolePermissions(roles[role], Object.keys(roles), errorBuilder.for(role)); | ||
if (rolePermissions !== undefined) { | ||
@@ -79,6 +79,5 @@ validRoles[role] = rolePermissions; | ||
for (const inheritsFrom of inherits) { | ||
if (!roles[inheritsFrom]) { | ||
errorBuilder | ||
.for(inheritsFrom) | ||
.add('Referenced role not exists. Make sure you are defining roles in a right order'); | ||
if (!roles.includes(inheritsFrom)) { | ||
// todo: check recursion | ||
errorBuilder.for(inheritsFrom).add('Referenced role not exists.'); | ||
} | ||
@@ -85,0 +84,0 @@ else { |
@@ -100,3 +100,3 @@ "use strict"; | ||
if (typeof entity.tableName !== 'string') { | ||
errors.for('tableName').add('Entity name must be a string'); | ||
errors.for('tableName').add('Table name must be a string'); | ||
return undefined; | ||
@@ -109,6 +109,26 @@ } | ||
} | ||
const partialEntity = { | ||
name: entity.name, | ||
primary: entity.primary, | ||
primaryColumn: entity.primaryColumn, | ||
tableName: entity.tableName, | ||
fields: {}, | ||
unique: {}, | ||
}; | ||
if (entity.view) { | ||
const viewTmp = entity.view; | ||
if (!utils_1.isObject(viewTmp)) { | ||
errors.for('view').add('View must be an object'); | ||
return undefined; | ||
} | ||
if (typeof viewTmp.sql !== 'string') { | ||
errors.for('view', 'sql').add('View SQL must be a string'); | ||
return undefined; | ||
} | ||
partialEntity.view = entity.view; | ||
} | ||
const validFields = {}; | ||
for (const [fieldName, field] of Object.entries(fields)) { | ||
const fieldErrors = errors.for(fieldName); | ||
const validField = this.validateField(entity.name, field, fieldErrors); | ||
const validField = this.validateField(partialEntity, field, fieldErrors); | ||
if (!validField) { | ||
@@ -130,6 +150,3 @@ continue; | ||
return { | ||
name: entity.name, | ||
primary: entity.primary, | ||
primaryColumn: entity.primaryColumn, | ||
tableName: entity.tableName, | ||
...partialEntity, | ||
fields: validFields, | ||
@@ -174,3 +191,3 @@ unique: validUniqueConstraints, | ||
} | ||
validateField(entityName, field, errors) { | ||
validateField(partialEntity, field, errors) { | ||
if (!utils_1.isObject(field)) { | ||
@@ -192,8 +209,9 @@ errors.add('Field must be an object'); | ||
if (isRelation(field)) { | ||
return this.validateRelation(entityName, field, errors); | ||
return this.validateRelation(partialEntity, field, errors); | ||
} | ||
return field; | ||
} | ||
validateRelation(entityName, field, errors) { | ||
validateRelation(partialEntity, field, errors) { | ||
var _a; | ||
const entityName = partialEntity.name; | ||
const targetEntityName = field.target; // todo | ||
@@ -227,2 +245,10 @@ const targetEntity = this.model.entities[targetEntityName] || undefined; | ||
} | ||
if (partialEntity.view) { | ||
const type = field.type; | ||
if (type === schema_1.Model.RelationType.ManyHasMany || | ||
type === schema_1.Model.RelationType.OneHasMany || | ||
(type === schema_1.Model.RelationType.OneHasOne && !('joiningColumn' in field))) { | ||
errors.add('This relation type is not allowed on a view entity. Only one-has-one owning and many-has one are allowed.'); | ||
} | ||
} | ||
if (model_1.isInverseRelation(field)) { | ||
@@ -276,2 +302,7 @@ // todo | ||
const inversedBy = field.inversedBy; // todo | ||
if (targetEntity.view) { | ||
if ('joiningColumn' in field) { | ||
errors.add(`View entity ${targetEntity.name} cannot be referenced from an owning relation. Try switching the owning side.`); | ||
} | ||
} | ||
if (inversedBy) { | ||
@@ -333,2 +364,3 @@ const targetField = targetEntity.fields[inversedBy]; | ||
const tableNames = {}; | ||
const aliasedTypes = new Map(); | ||
for (const entity of entities) { | ||
@@ -360,3 +392,13 @@ const description = `entity ${entity.name}`; | ||
}, | ||
visitColumn: () => { }, | ||
visitColumn: (entity, column) => { | ||
if (!column.typeAlias) { | ||
return; | ||
} | ||
if (aliasedTypes.has(column.typeAlias) && aliasedTypes.get(column.typeAlias) !== column.type) { | ||
errorBuilder | ||
.for(column.name) | ||
.add(`Type alias ${column.typeAlias} already exists for base type ${column.type}`); | ||
} | ||
aliasedTypes.set(column.typeAlias, column.type); | ||
}, | ||
visitManyHasManyInverse: () => { }, | ||
@@ -363,0 +405,0 @@ visitOneHasMany: () => { }, |
{ | ||
"name": "@contember/schema-utils", | ||
"version": "0.12.0-alpha.2", | ||
"version": "0.12.0-alpha.4", | ||
"license": "Apache-2.0", | ||
@@ -11,7 +11,7 @@ "main": "dist/src/index.js", | ||
"dependencies": { | ||
"@contember/schema": "^0.12.0-alpha.2" | ||
"@contember/schema": "^0.12.0-alpha.4" | ||
}, | ||
"devDependencies": { | ||
"@types/node": "^14.6.4" | ||
"@types/node": "^15.12.5" | ||
} | ||
} |
@@ -251,2 +251,4 @@ import { assertNever, isIt } from '../utils' | ||
export const isColumn = (field: Model.AnyField): field is Model.AnyColumn => isIt<Model.AnyColumn>(field, 'columnType') | ||
export const emptyModelSchema: Model.Schema = { entities: {}, enums: {} } |
@@ -1,28 +0,31 @@ | ||
import { ProjectRole, Schema } from '@contember/schema' | ||
import { Acl, ProjectRole, Schema } from '@contember/schema' | ||
import { AllowAllPermissionFactory } from './acl' | ||
export const normalizeSchema = <S extends Schema>(schema: S): S => { | ||
if (!schema.acl.roles[ProjectRole.ADMIN]) { | ||
schema = { | ||
...schema, | ||
acl: { | ||
...schema.acl, | ||
roles: { | ||
...schema.acl.roles, | ||
[ProjectRole.ADMIN]: { | ||
stages: '*', | ||
variables: {}, | ||
entities: new AllowAllPermissionFactory().create(schema.model), | ||
s3: { | ||
'**': { | ||
upload: true, | ||
read: true, | ||
}, | ||
return { | ||
...schema, | ||
acl: { | ||
...schema.acl, | ||
roles: { | ||
...schema.acl.roles, | ||
[ProjectRole.ADMIN]: { | ||
stages: '*', | ||
variables: {}, | ||
entities: new AllowAllPermissionFactory().create(schema.model), | ||
s3: { | ||
'**': { | ||
upload: true, | ||
read: true, | ||
}, | ||
}, | ||
...((schema.acl.roles?.[ProjectRole.ADMIN] as Acl.RolePermissions | undefined) || {}), | ||
}, | ||
[ProjectRole.MIGRATION]: { | ||
stages: '*', | ||
entities: {}, | ||
variables: {}, | ||
}, | ||
}, | ||
} | ||
}, | ||
} | ||
return schema | ||
} |
@@ -39,3 +39,3 @@ import { Acl, Model } from '@contember/schema' | ||
// } | ||
const rolePermissions = this.validateRolePermissions(roles[role], validRoles, errorBuilder.for(role)) | ||
const rolePermissions = this.validateRolePermissions(roles[role], Object.keys(roles), errorBuilder.for(role)) | ||
if (rolePermissions !== undefined) { | ||
@@ -50,3 +50,3 @@ validRoles[role] = rolePermissions | ||
permissions: unknown, | ||
roles: Acl.Schema['roles'], | ||
roles: string[], | ||
errorBuilder: ErrorBuilder, | ||
@@ -85,7 +85,3 @@ ): Acl.RolePermissions | undefined { | ||
private validateInherits( | ||
inherits: unknown, | ||
roles: Acl.Schema['roles'], | ||
errorBuilder: ErrorBuilder, | ||
): string[] | undefined { | ||
private validateInherits(inherits: unknown, roles: string[], errorBuilder: ErrorBuilder): string[] | undefined { | ||
if (inherits === undefined) { | ||
@@ -100,6 +96,5 @@ return undefined | ||
for (const inheritsFrom of inherits) { | ||
if (!roles[inheritsFrom]) { | ||
errorBuilder | ||
.for(inheritsFrom) | ||
.add('Referenced role not exists. Make sure you are defining roles in a right order') | ||
if (!roles.includes(inheritsFrom)) { | ||
// todo: check recursion | ||
errorBuilder.for(inheritsFrom).add('Referenced role not exists.') | ||
} else { | ||
@@ -106,0 +101,0 @@ validRoles.push(inheritsFrom) |
@@ -101,3 +101,3 @@ import { Model } from '@contember/schema' | ||
if (typeof entity.tableName !== 'string') { | ||
errors.for('tableName').add('Entity name must be a string') | ||
errors.for('tableName').add('Table name must be a string') | ||
return undefined | ||
@@ -110,7 +110,28 @@ } | ||
} | ||
const partialEntity: Model.Entity = { | ||
name: entity.name, | ||
primary: entity.primary, | ||
primaryColumn: entity.primaryColumn, | ||
tableName: entity.tableName, | ||
fields: {}, | ||
unique: {}, | ||
} | ||
if (entity.view) { | ||
const viewTmp = entity.view | ||
if (!isObject(viewTmp)) { | ||
errors.for('view').add('View must be an object') | ||
return undefined | ||
} | ||
if (typeof viewTmp.sql !== 'string') { | ||
errors.for('view', 'sql').add('View SQL must be a string') | ||
return undefined | ||
} | ||
partialEntity.view = entity.view as Model.View | ||
} | ||
const validFields: Model.Entity['fields'] = {} | ||
for (const [fieldName, field] of Object.entries(fields)) { | ||
const fieldErrors = errors.for(fieldName) | ||
const validField = this.validateField(entity.name, field, fieldErrors) | ||
const validField = this.validateField(partialEntity, field, fieldErrors) | ||
if (!validField) { | ||
@@ -138,6 +159,3 @@ continue | ||
return { | ||
name: entity.name, | ||
primary: entity.primary, | ||
primaryColumn: entity.primaryColumn, | ||
tableName: entity.tableName, | ||
...partialEntity, | ||
fields: validFields, | ||
@@ -190,3 +208,3 @@ unique: validUniqueConstraints, | ||
private validateField(entityName: string, field: unknown, errors: ErrorBuilder): Model.AnyField | undefined { | ||
private validateField(partialEntity: Model.Entity, field: unknown, errors: ErrorBuilder): Model.AnyField | undefined { | ||
if (!isObject(field)) { | ||
@@ -208,12 +226,13 @@ errors.add('Field must be an object') | ||
if (isRelation(field as any)) { | ||
return this.validateRelation(entityName, field, errors) | ||
return this.validateRelation(partialEntity, field, errors) | ||
} | ||
return (field as unknown) as Model.AnyColumn | ||
return field as unknown as Model.AnyColumn | ||
} | ||
private validateRelation( | ||
entityName: string, | ||
partialEntity: Model.Entity, | ||
field: UnknownObject, | ||
errors: ErrorBuilder, | ||
): Model.AnyRelation | undefined { | ||
const entityName = partialEntity.name | ||
const targetEntityName = field.target as string // todo | ||
@@ -247,3 +266,15 @@ const targetEntity = this.model.entities[targetEntityName] || undefined | ||
} | ||
if (isInverseRelation((field as any) as Model.Relation)) { | ||
if (partialEntity.view) { | ||
const type = (field as any as Model.Relation).type | ||
if ( | ||
type === Model.RelationType.ManyHasMany || | ||
type === Model.RelationType.OneHasMany || | ||
(type === Model.RelationType.OneHasOne && !('joiningColumn' in field)) | ||
) { | ||
errors.add( | ||
'This relation type is not allowed on a view entity. Only one-has-one owning and many-has one are allowed.', | ||
) | ||
} | ||
} | ||
if (isInverseRelation(field as any as Model.Relation)) { | ||
// todo | ||
@@ -299,2 +330,9 @@ const ownedBy = field.ownedBy | ||
const inversedBy = field.inversedBy as string // todo | ||
if (targetEntity.view) { | ||
if ('joiningColumn' in field) { | ||
errors.add( | ||
`View entity ${targetEntity.name} cannot be referenced from an owning relation. Try switching the owning side.`, | ||
) | ||
} | ||
} | ||
if (inversedBy) { | ||
@@ -345,3 +383,3 @@ const targetField = targetEntity.fields[inversedBy] | ||
} | ||
return (field as any) as Model.AnyRelation // todo | ||
return field as any as Model.AnyRelation // todo | ||
} | ||
@@ -363,2 +401,3 @@ | ||
const tableNames: Record<string, string> = {} | ||
const aliasedTypes = new Map<string, Model.ColumnType>() | ||
for (const entity of entities) { | ||
@@ -396,3 +435,13 @@ const description = `entity ${entity.name}` | ||
}, | ||
visitColumn: () => {}, | ||
visitColumn: (entity, column) => { | ||
if (!column.typeAlias) { | ||
return | ||
} | ||
if (aliasedTypes.has(column.typeAlias) && aliasedTypes.get(column.typeAlias) !== column.type) { | ||
errorBuilder | ||
.for(column.name) | ||
.add(`Type alias ${column.typeAlias} already exists for base type ${column.type}`) | ||
} | ||
aliasedTypes.set(column.typeAlias, column.type) | ||
}, | ||
visitManyHasManyInverse: () => {}, | ||
@@ -399,0 +448,0 @@ visitOneHasMany: () => {}, |
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
3752
243563