Skip to content
This repository has been archived by the owner on Apr 17, 2023. It is now read-only.

WIP: generated relationship details for schema #899

Merged
merged 24 commits into from
Nov 23, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
f077c3a
fix: add relationship dets. to schema
kingsleyzissou Oct 12, 2020
68fc50f
fix: lint issues
kingsleyzissou Oct 14, 2020
0f0e05f
fix: update snapshot
kingsleyzissou Oct 14, 2020
0d47969
test: add test to check if manyToOne relationship is added (#900)
Oct 14, 2020
593eee5
fix: update snapshots
kingsleyzissou Oct 15, 2020
d218ecc
fix: relationship filtering
kingsleyzissou Oct 15, 2020
bc236fa
test: add more tests
kingsleyzissou Oct 15, 2020
78b13ec
chore: remove commented code
kingsleyzissou Oct 15, 2020
d3e33eb
chore: add indentation dep
kingsleyzissou Oct 15, 2020
501705a
chore: add datasync types to json schema
kingsleyzissou Oct 15, 2020
a3b8ef4
chore: refactor createModelType to use jsonSchema
kingsleyzissou Oct 15, 2020
c5521c3
chore: clean up generated code indentation
kingsleyzissou Oct 15, 2020
76b392b
fix: remove unused method
kingsleyzissou Oct 15, 2020
2d0f1b1
fix: fix indentation of generated index file
kingsleyzissou Oct 15, 2020
a27ffa4
chore: add code comments
kingsleyzissou Oct 15, 2020
a8c9772
fix: lint issues
kingsleyzissou Oct 15, 2020
af7ad8a
fix: update snapshots
kingsleyzissou Oct 15, 2020
c681070
fix: add missing dep
kingsleyzissou Oct 15, 2020
7ad987b
chore: remove deleted field from json schema
kingsleyzissou Oct 15, 2020
7fbe7f8
fix: update CLI snapshots
kingsleyzissou Oct 19, 2020
2c851d6
chore: create snapshots for json schema
kingsleyzissou Oct 19, 2020
015df2a
fix: remove datasync filter
kingsleyzissou Oct 22, 2020
500bc29
fix: lint issues
kingsleyzissou Oct 22, 2020
fcef636
fix: breaking snapshots
kingsleyzissou Oct 22, 2020
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
4 changes: 3 additions & 1 deletion packages/datastore/cli/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -24,13 +24,15 @@
"@types/bson": "4.0.3",
"@types/jest": "26.0.15",
"@types/yargs": "15.0.10",
"endent": "^2.0.1",
"jest": "26.6.3",
"rimraf": "3.0.2",
"safe-eval": "^0.4.1",
"ts-jest": "26.4.4",
"typescript": "3.9.7"
},
"dependencies": {
"@graphback/core": "0.16.2",
"@graphback/core": "1.1.0-alpha1",
"@graphql-tools/load-files": "6.2.5",
"@graphql-tools/merge": "6.2.5",
"graphql": "^15.3.0",
Expand Down
24 changes: 12 additions & 12 deletions packages/datastore/cli/src/OffixDataStorePlugin.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,9 @@ import { GraphbackPlugin, GraphbackCoreMetadata } from "@graphback/core";
import { writeFileSync } from "fs";
import { IOffixDataStorePluginConfig } from "./OffixDataStoreConfig";
import { createJsonSchema, createModelType } from "./generate-documents";
import { isDataSyncClientModel, makeDirIfNotExists } from "./utils";
import { makeDirIfNotExists } from "./utils";
import { validateOffixDataStorePluginConfig } from "./OffixDataStorePluginValidator";
import endent from "endent";

export const OFFIX_DATASYNC_PLUGIN_NAME = "OffixDataStorePlugin";

Expand Down Expand Up @@ -34,25 +35,24 @@ export class OffixDataStorePlugin extends GraphbackPlugin {
}

public getDocuments(metadata: GraphbackCoreMetadata) {
const models = metadata.getModelDefinitions()
.filter(model => isDataSyncClientModel(model));

const models = metadata.getModelDefinitions();
const modelJsonSchemas = models
.map(model => (createJsonSchema(model)));

// concatenate all the json documents
const jsonSchema = modelJsonSchemas
.reduce((prev, cur) => ({ ...prev, [cur.name]: cur }), {});
const modelTypes = models.map(model => createModelType(model)).join("\n");

// TODO use actual model type instead of any for ModelJsonSchema
const exports = `import { GeneratedModelSchema } from "offix-datastore";
import jsonSchema from "./schema.json";

export const schema = jsonSchema as GeneratedModelSchema;
const modelTypes = modelJsonSchemas
.map(model => createModelType(model)).join("\n");

export * from "./types";
`;
const exports = endent`
import { GeneratedModelSchema } from "offix-datastore";
import jsonSchema from "./schema.json";

export const schema = jsonSchema as GeneratedModelSchema;
export * from "./types";
`;

return {
json: jsonSchema,
Expand Down
38 changes: 33 additions & 5 deletions packages/datastore/cli/src/generate-documents/createJsonSchema.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,12 +6,9 @@ import {
} from "graphql";
import { convertToTsType } from "../utils";


const getFieldParameters = (fieldName: string, type: GraphQLOutputType): any => {
const options: any = {};

// TODO handle relationships

options.key = fieldName;

if (isNonNullType(type)) {
Expand All @@ -24,22 +21,53 @@ const getFieldParameters = (fieldName: string, type: GraphQLOutputType): any =>

const getModelProperties = (model: ModelDefinition, primaryKey: string) => {
const fieldMap = model.graphqlType.getFields();
const keys = Object.keys(fieldMap);
// get a list of relationships
const relNames = model.relationships.map(r => r.ownerField.name);

const generatedProperties = Object.keys(fieldMap)
// loop through graphback relationship types
const relationships = model.relationships
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is amazing. We do not need to use delta type and getting all the information.

// filter out oneToMany relationships
.filter(r => r.kind !== "oneToMany")
// generate key/value pair with foreign key as key
.map(r => {
const fieldOptions = getFieldParameters(
r.relationForeignKey!,
r.relationType
);
fieldOptions.relationship = r.relationType;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@craicoverflow can you take look if this would work from your side.
We actually getting model relationships and add extra fields to schema

Copy link

@craicoverflow craicoverflow Oct 13, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not entirely sure yet, I think this might generate extra fields which are not part of a model. A ModelDefinition contains relationship information for the opposite relationship field.

For example, in a 1:M relationship between Note.comments and Comment.note, the Note model definition would have 2 relationships.

I think once we have a unit test in for this API, it will be easier to verify this.

cc @kingsleyzissou

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes you're correct @craicoverflow I had to filter out the side of the relationship that I didn't need here. But the relationship info went both ways oneToMany and manyToOne

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this the filtering you meant @kingsleyzissou ?

https://github.com/aerogear/offix/blob/schema-relationships/packages/datastore/cli/src/generate-documents/createJsonSchema.ts#L27

From looking at that I had thought it was filtering out the relationship information that Offix already had and did not want to apply twice.

Copy link
Contributor Author

@kingsleyzissou kingsleyzissou Oct 13, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah that's the filtering I meant @craicoverflow . As far as I can remember, we were having some issues with the comments array being included in our Datastore model and we needed to remove it. So it isn't actually duplicate data at all.

We are only adding the foreign key information the the notes so we can filter those out based on the parent id, in this case the Task.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

See #900 - should this test be passing or am I doing something wrong?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The test should be passing. I'm going to fix the linting and snapshot issues and then I can debug that

return { [r.relationForeignKey!]: fieldOptions };
});

const generatedProperties = keys
// filter out autogenerated relationship properties
.filter(fieldName => !relNames.includes(fieldName))
.map(fieldName => {
const fieldOptions = getFieldParameters(fieldName, fieldMap[fieldName].type);
const fieldOptions = getFieldParameters(
fieldName,
fieldMap[fieldName].type
);
if (fieldName === primaryKey) {
fieldOptions.primary = true;
}
return { [fieldName]: fieldOptions };
})
// add previously created relationship key/values
.concat(relationships)
.reduce((prev, current) => ({ ...prev, ...current }), {});

generatedProperties._version = {
type: "string",
key: "_version",
isRequired: true
};

generatedProperties._lastUpdatedAt = {
type: "number",
key: "_lastUpdatedAt",
isRequired: true
};

return generatedProperties;
};

Expand Down
47 changes: 20 additions & 27 deletions packages/datastore/cli/src/generate-documents/createModelTypes.ts
Original file line number Diff line number Diff line change
@@ -1,35 +1,28 @@
import { ModelDefinition, getPrimaryKey } from "@graphback/core";
import { GraphQLOutputType, isNonNullType, getNullableType } from "graphql";
import { convertToTsType } from "../utils";
import endent from "endent";

const getField = (fieldName: string, type: GraphQLOutputType) => {
if (isNonNullType(type)) {
type = getNullableType(type);
} else {
fieldName = `${fieldName}?`;
}
const getModelProperties = (schema: any) => {
const fieldMap = schema.properties;
const keys = Object.keys(fieldMap);

return `${fieldName}: ${convertToTsType(type)}`;
return keys
.map(fieldName => {
const s = fieldMap[fieldName];
const name = s.isRequired ? fieldName : `${fieldName}?`;
return `${name}: ${s.type}`;
})
.join(";\n");
};

const getModelProperties = (model: ModelDefinition) => {
const fieldMap = model.graphqlType.getFields();
export const createModelType = (schema: any) => {
const { name: modelName, primaryKey } = schema;

return Object.keys(fieldMap)
.map(fieldName => (getField(fieldName, fieldMap[fieldName].type)))
.join(";\n ");
};

export const createModelType = (model: ModelDefinition) => {
const modelName = model.graphqlType.name;
const primaryKey = getPrimaryKey(model.graphqlType).name;
return endent`
export interface ${modelName} {
${getModelProperties(schema)}
}

return `export interface ${modelName} {
${getModelProperties(model)}
_version: number;
}
export type ${modelName}Create = ${primaryKey ? `Omit<${modelName}, "${primaryKey}">` : modelName};
export type ${modelName}Change = ${primaryKey ? `Pick<${modelName}, "${primaryKey}"> & ` : ""}Partial<${modelName}Create>;

export type ${modelName}Create = ${primaryKey ? `Omit<${modelName}, "${primaryKey}">` : modelName};
export type ${modelName}Change = ${primaryKey ? `Pick<${modelName}, "${primaryKey}"> & ` : ""}Partial<${modelName}Create>;
`;
`;
};
Loading