Skip to content

Commit

Permalink
Merge pull request #18 from bcgsc/feature/hard-code-reverse-names
Browse files Browse the repository at this point in the history
Feature/hard code reverse names
  • Loading branch information
creisle committed Apr 26, 2022
2 parents ea74c63 + 3c4c88d commit 27c7fea
Show file tree
Hide file tree
Showing 16 changed files with 131 additions and 138 deletions.
16 changes: 0 additions & 16 deletions src/class.ts
Original file line number Diff line number Diff line change
Expand Up @@ -53,20 +53,6 @@ const createClassDefinition = (opt: ClassDefinitionInput): ClassDefinition => {
isEdge = true;
}

let reverseName;

if (opt.isEdge) {
if (name.endsWith('Of')) {
reverseName = `Has${name.slice(0, name.length - 2)}`;
} else if (name.endsWith('By')) {
reverseName = `${name.slice(0, name.length - 3)}s`;
} else if (name === 'Infers') {
reverseName = 'InferredBy';
} else {
reverseName = `${name.slice(0, name.length - 1)}dBy`;
}
}

let defaultRoutes: Expose;

if (opt.isAbstract || opt.embedded) {
Expand All @@ -91,8 +77,6 @@ const createClassDefinition = (opt: ClassDefinitionInput): ClassDefinition => {
embedded: Boolean(opt.embedded),
indices: opt.indices || [],
permissions: { ...defaultPermissions(routes), ...(opt.permissions || {}) },
reverseName,

};
};

Expand Down
27 changes: 9 additions & 18 deletions src/definitions/edges.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,20 +22,23 @@ const edgeModels: PartialSchemaDefn = {
indices: [activeUUID('E'), defineSimpleIndex({ model: 'E', property: 'createdAt' })],
},
AliasOf: {
reverseName: 'HasAlias',
description: 'The source record is an equivalent representation of the target record, both of which are from the same source',
},
Cites: { description: 'Generally refers to relationships between publications. For example, some article cites another' },
CrossReferenceOf: { description: 'The source record is an equivalent representation of the target record from a different source' },
DeprecatedBy: { description: 'The target record is a newer version of the source record' },
ElementOf: { description: 'The source record is part of (or contained within) the target record' },
GeneralizationOf: { description: 'The source record is a less specific (or more general) instance of the target record' },
Cites: { reverseName: 'CitedBy', description: 'Generally refers to relationships between publications. For example, some article cites another' },
CrossReferenceOf: { reverseName: 'HasCrossReference', description: 'The source record is an equivalent representation of the target record from a different source' },
DeprecatedBy: { reverseName: 'Deprecates', description: 'The target record is a newer version of the source record' },
ElementOf: { reverseName: 'HasElement', description: 'The source record is part of (or contained within) the target record' },
GeneralizationOf: { reverseName: 'HasGeneralization', description: 'The source record is a less specific (or more general) instance of the target record' },
Infers: {
description: 'Given the source record, the target record is also expected. For example given some genomic variant we infer the protein change equivalent',
sourceModel: 'Variant',
targetModel: 'Variant',
reverseName: 'InferredBy',
},
SubClassOf: { description: 'The source record is a subset of the target record' },
SubClassOf: { reverseName: 'HasSubclass', description: 'The source record is a subset of the target record' },
TargetOf: {
reverseName: 'HasTarget',
description: 'The source record is a target of the target record. For example some gene is the target of a particular drug',
properties: [
{ ...BASE_PROPERTIES.in },
Expand All @@ -59,20 +62,8 @@ for (const name of [
'TargetOf',
]) {
const sourceProp = { name: 'source', type: 'link' as const, linkedClass: 'Source' };
let reverseName;

if (name.endsWith('Of')) {
reverseName = `Has${name.slice(0, name.length - 2)}`;
} else if (name.endsWith('By')) {
reverseName = `${name.slice(0, name.length - 3)}s`;
} else if (name === 'Infers') {
reverseName = 'InferredBy';
} else {
reverseName = `${name.slice(0, name.length - 1)}dBy`;
}
edgeModels[name] = {
isEdge: true,
reverseName,
inherits: ['E'],
sourceModel: 'Ontology',
targetModel: 'Ontology',
Expand Down
4 changes: 2 additions & 2 deletions src/definitions/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -129,7 +129,7 @@ const BASE_SCHEMA: PartialSchemaDefn = {
mandatory: true,
nullable: false,
description: 'The timestamp at which this terms of use was put into action',
default: timeStampNow,
generateDefault: timeStampNow,
generated: true,
examples: [1547245339649],
}, {
Expand All @@ -155,7 +155,7 @@ const initializeSchema = (
for (const name of Object.keys(inputSchema)) {
if (name !== 'Permissions' && !inputSchema[name].embedded) {
permissionsProperties.push({
min: PERMISSIONS.NONE, max: PERMISSIONS.ALL, type: 'integer', nullable: false, readOnly: false, name,
minimum: PERMISSIONS.NONE, maximum: PERMISSIONS.ALL, type: 'integer', nullable: false, readOnly: false, name,
});
}
}
Expand Down
6 changes: 3 additions & 3 deletions src/definitions/ontology.ts
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,7 @@ const models: PartialSchemaDefn = {
{
name: 'name',
nullable: false,
default: (record) => record.sourceId,
generateDefault: (record) => record?.sourceId,
description: 'Name of the term',
nonEmpty: true,
generationDependencies: true,
Expand Down Expand Up @@ -107,7 +107,7 @@ const models: PartialSchemaDefn = {
{ name: 'url', type: 'string' },
{
...BASE_PROPERTIES.displayName,
default: util.displayOntology,
generateDefault: util.displayOntology,
},
],
isAbstract: true,
Expand Down Expand Up @@ -246,7 +246,7 @@ const models: PartialSchemaDefn = {
},
{
...BASE_PROPERTIES.displayName,
default: util.displayFeature,
generateDefault: util.displayFeature,
},
],
},
Expand Down
16 changes: 8 additions & 8 deletions src/definitions/position.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ const models: PartialSchemaDefn = {
embedded: true,
properties: [
{
name: 'pos', type: 'integer', min: 1, mandatory: true, examples: [12], nullable: true, description: 'The Amino Acid number',
name: 'pos', type: 'integer', minimum: 1, mandatory: true, examples: [12], nullable: true, description: 'The Amino Acid number',
},
{
name: 'refAA', type: 'string', cast: util.uppercase, examples: ['G'], pattern: '^[A-Z*?]$', description: 'The reference Amino Acid (single letter notation)',
Expand All @@ -35,10 +35,10 @@ const models: PartialSchemaDefn = {
name: 'arm', mandatory: true, nullable: false, choices: ['p', 'q'],
},
{
name: 'majorBand', type: 'integer', min: 1, examples: ['11'],
name: 'majorBand', type: 'integer', minimum: 1, examples: ['11'],
},
{
name: 'minorBand', type: 'integer', min: 1, examples: ['1'],
name: 'minorBand', type: 'integer', minimum: 1, examples: ['1'],
},
],
},
Expand All @@ -47,23 +47,23 @@ const models: PartialSchemaDefn = {
inherits: ['Position'],
embedded: true,
properties: [{
name: 'pos', type: 'integer', min: 1, mandatory: true, nullable: true, description: 'The genomic/nucleotide number',
name: 'pos', type: 'integer', minimum: 1, mandatory: true, nullable: true, description: 'The genomic/nucleotide number',
}],
},
ExonicPosition: {
routes: EXPOSE_NONE,
inherits: ['Position'],
embedded: true,
properties: [{
name: 'pos', type: 'integer', min: 1, mandatory: true, nullable: true, description: 'The exon number',
name: 'pos', type: 'integer', minimum: 1, mandatory: true, nullable: true, description: 'The exon number',
}],
},
IntronicPosition: {
routes: EXPOSE_NONE,
inherits: ['Position'],
embedded: true,
properties: [{
name: 'pos', type: 'integer', min: 1, mandatory: true, nullable: true,
name: 'pos', type: 'integer', minimum: 1, mandatory: true, nullable: true,
}],
},
CdsPosition: {
Expand All @@ -85,7 +85,7 @@ const models: PartialSchemaDefn = {
embedded: true,
properties: [
{
name: 'pos', type: 'integer', min: 1, mandatory: true, examples: [55], nullable: true,
name: 'pos', type: 'integer', minimum: 1, mandatory: true, examples: [55], nullable: true,
},
{
name: 'offset', type: 'integer', examples: [-11], description: 'distance from the nearest exon boundary (pos)',
Expand All @@ -100,7 +100,7 @@ const models: PartialSchemaDefn = {
embedded: true,
properties: [
{
name: 'pos', type: 'integer', min: 1, mandatory: true, examples: [55], nullable: true,
name: 'pos', type: 'integer', minimum: 1, mandatory: true, examples: [55], nullable: true,
},
{
name: 'offset', type: 'integer', examples: [-11], description: 'distance from the nearest cds exon boundary',
Expand Down
12 changes: 7 additions & 5 deletions src/definitions/statement.ts
Original file line number Diff line number Diff line change
Expand Up @@ -89,13 +89,15 @@ const models: PartialSchemaDefn = {
name: 'displayNameTemplate',
description: 'The template used in building the display name',
type: 'string',
default: (record) => {
generateDefault: (record) => {
try {
return chooseDefaultTemplate(record);
} catch (err) {
return DEFAULT_TEMPLATE;
}
if (record) {
return chooseDefaultTemplate(record);
}
} catch (err) { }
return DEFAULT_TEMPLATE;
},
generationDependencies: true,
cast: util.castString, // skip default lowercasing
},
],
Expand Down
8 changes: 4 additions & 4 deletions src/definitions/util.ts
Original file line number Diff line number Diff line change
Expand Up @@ -86,7 +86,7 @@ const BASE_PROPERTIES: { [P in BasePropertyName]: PropertyDefinitionInput } = {
readOnly: true,
description: 'Internal identifier for tracking record history',
cast: util.castUUID,
default: uuidV4 as () => string,
generateDefault: uuidV4 as () => string,
generated: true,
examples: ['4198e211-e761-4771-b6f8-dadbcc44e9b9'],
},
Expand All @@ -96,7 +96,7 @@ const BASE_PROPERTIES: { [P in BasePropertyName]: PropertyDefinitionInput } = {
mandatory: true,
nullable: false,
description: 'The timestamp at which the record was created',
default: util.timeStampNow,
generateDefault: util.timeStampNow,
generated: true,
examples: [1547245339649],
},
Expand All @@ -106,7 +106,7 @@ const BASE_PROPERTIES: { [P in BasePropertyName]: PropertyDefinitionInput } = {
mandatory: true,
nullable: false,
description: 'The timestamp at which the record was last updated',
default: util.timeStampNow,
generateDefault: util.timeStampNow,
generated: true,
examples: [1547245339649],
},
Expand Down Expand Up @@ -180,7 +180,7 @@ const BASE_PROPERTIES: { [P in BasePropertyName]: PropertyDefinitionInput } = {
name: 'displayName',
type: 'string',
description: 'Optional string used for display in the web application. Can be overwritten w/o tracking',
default: (rec) => rec.name || null,
generateDefault: (rec) => rec?.name || null,
generationDependencies: true,
cast: util.castString,
},
Expand Down
4 changes: 2 additions & 2 deletions src/definitions/variant.ts
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,7 @@ const models: PartialSchemaDefn = {
type: 'string',
generationDependencies: true,
generated: true,
default: (record) => generateBreakRepr(record.break1Start, record.break1End),
generateDefault: (record) => generateBreakRepr(record.break1Start, record.break1End),
cast: castBreakRepr,
},
{
Expand All @@ -92,7 +92,7 @@ const models: PartialSchemaDefn = {
type: 'string',
generationDependencies: true,
generated: true,
default: (record) => generateBreakRepr(record.break2Start, record.break2End),
generateDefault: (record) => generateBreakRepr(record.break2Start, record.break2End),
cast: castBreakRepr,
},
{
Expand Down
24 changes: 5 additions & 19 deletions src/property.ts
Original file line number Diff line number Diff line change
Expand Up @@ -58,15 +58,15 @@ export const validateProperty = (prop: PropertyDefinition, inputValue: unknown):
});
}
if (castValue !== null) {
if (prop.min !== undefined && prop.min !== null && castValue < prop.min) {
if (prop.minimum !== undefined && prop.minimum !== null && castValue < prop.minimum) {
throw new ValidationError({
message: `Violated the minimum value constraint of ${prop.name} (${castValue} < ${prop.min})`,
message: `Violated the minimum value constraint of ${prop.name} (${castValue} < ${prop.minimum})`,
field: prop.name,
});
}
if (prop.max !== undefined && prop.max !== null && castValue > prop.max) {
if (prop.maximum !== undefined && prop.maximum !== null && castValue > prop.maximum) {
throw new ValidationError({
message: `Violated the maximum value constraint of ${prop.name} (${castValue} > ${prop.max})`,
message: `Violated the maximum value constraint of ${prop.name} (${castValue} > ${prop.maximum})`,
field: prop.name,
});
}
Expand Down Expand Up @@ -116,10 +116,9 @@ export const validateProperty = (prop: PropertyDefinition, inputValue: unknown):
export const createPropertyDefinition = (opt: PropertyDefinitionInput): PropertyDefinition => {
const {
type: inputType,
default: inputDefault,
...rest
} = opt;
const defaultType = ((opt.min !== undefined || opt.max !== undefined) && !opt.type)
const defaultType = ((opt.minimum !== undefined || opt.maximum !== undefined) && !opt.type)
? 'integer'
: 'string';
const type: DbType = inputType || defaultType;
Expand Down Expand Up @@ -148,17 +147,6 @@ export const createPropertyDefinition = (opt: PropertyDefinitionInput): Property
}
}

let generateDefault,
defaultValue: undefined | unknown;

if (opt.default !== undefined) {
if (opt.default instanceof Function) {
generateDefault = opt.default;
} else {
defaultValue = opt.default;
}
}

const castFunction = opt.cast || defaultCast;
let choices: undefined | unknown[];

Expand All @@ -170,8 +158,6 @@ export const createPropertyDefinition = (opt: PropertyDefinitionInput): Property
...rest,
type,
cast: castFunction,
default: defaultValue,
generateDefault,
description: opt.description || '',
generated: Boolean(opt.generated),
generationDependencies: Boolean(opt.generationDependencies),
Expand Down
20 changes: 10 additions & 10 deletions src/schema.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import {
PropertyDefinition, ClassDefinition, GraphRecord, ClassMapping,
PropertyDefinition, ClassDefinition, GraphRecord, ClassMapping, StatementRecord,
} from './types';
import { ValidationError } from './error';
import { validateProperty } from './property';
Expand Down Expand Up @@ -84,12 +84,12 @@ class SchemaDefinition {
* Returns preview of given record based on its '@class' value
* @param {Object} obj - Record to be parsed.
*/
getPreview(obj: GraphRecord): string {
getPreview(obj: Partial<GraphRecord>): string {
if (obj && typeof obj === 'object') {
if (obj['@class'] === 'Statement') {
const { content } = sentenceTemplates.generateStatementSentence(
this.getPreview.bind(this),
obj,
obj as StatementRecord,
);
return content;
}
Expand All @@ -101,7 +101,7 @@ class SchemaDefinition {
return obj.name;
}
if (obj['@rid']) {
return obj['@rid'];
return `${obj['@rid']}`;
}
if (Array.isArray(obj)) { // embedded link set
return `${obj.length}`;
Expand Down Expand Up @@ -342,14 +342,14 @@ class SchemaDefinition {
*/
formatRecord(
modelName: unknown,
record: GraphRecord,
record: Record<string, unknown>,
opt: {
dropExtra?: boolean;
addDefaults?: boolean;
ignoreExtra?: boolean;
ignoreMissing?: boolean;
} = {},
): GraphRecord {
): Partial<GraphRecord> {
const model = this.get(modelName);
// add default options
const {
Expand Down Expand Up @@ -421,7 +421,7 @@ class SchemaDefinition {
for (let [attr, value] of Object.entries(formattedRecord)) {
let { linkedClass, type, iterable } = properties[attr];

if (type.startsWith('embedded') && linkedClass !== undefined && value) {
if (type.startsWith('embedded') && linkedClass !== undefined && value && typeof value === 'object') {
if (value['@class'] && value['@class'] !== linkedClass) {
// record has a class type that doesn't match the expected linkedClass, is it a subclass of it?
if (this.ancestors(value['@class']).includes(linkedClass)) {
Expand All @@ -431,11 +431,11 @@ class SchemaDefinition {
}
}
if (type === 'embedded' && typeof value === 'object') {
value = this.formatRecord(linkedClass, value);
} else if (iterable) {
value = this.formatRecord(linkedClass, value as Record<string, unknown>);
} else if (iterable && Array.isArray(value)) {
value = Array.from(
value,
(v) => this.formatRecord(linkedClass, v as GraphRecord),
(v) => this.formatRecord(linkedClass, v),
);
}
}
Expand Down
Loading

0 comments on commit 27c7fea

Please sign in to comment.