-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
5 changed files
with
214 additions
and
6 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,21 +1,51 @@ | ||
import 'reflect-metadata'; | ||
import {metadataGet, metadataGetOrSet} from "../utils/metadataFactories"; | ||
import {metadataGet} from "../utils/metadataFactories"; | ||
import {FieldConfig} from "./FieldConfig"; | ||
import {getSuperClass} from "../utils/core"; | ||
|
||
const FIELDS_METADATA_KEY = '__FIELDS_METADATA_KEY'; | ||
|
||
export class FieldsMetadata { | ||
static getForClass = metadataGet<FieldsMetadata>(FIELDS_METADATA_KEY); | ||
static getOrCreateForClass = metadataGetOrSet(FIELDS_METADATA_KEY, FieldsMetadata); | ||
static getOrCreateForClass(klass):FieldsMetadata { | ||
let attributesMetadata = Reflect.getOwnMetadata(FIELDS_METADATA_KEY, klass); | ||
|
||
if (!attributesMetadata) { | ||
attributesMetadata = new FieldsMetadata(); | ||
Reflect.defineMetadata(FIELDS_METADATA_KEY, attributesMetadata, klass); | ||
|
||
protected _fields:{ [fieldName:string]:FieldConfig } = {}; | ||
|
||
let superClass = getSuperClass(klass); | ||
if (superClass) { | ||
attributesMetadata.setParent(FieldsMetadata.getOrCreateForClass(superClass)); | ||
} | ||
} | ||
|
||
return attributesMetadata; | ||
} | ||
|
||
|
||
private parent:FieldsMetadata; | ||
protected _ownFields:{ [fieldName:string]:FieldConfig } = {}; | ||
|
||
getField(fieldName:string):FieldConfig { | ||
return this._fields[fieldName] || (this._fields[fieldName] = new FieldConfig()) | ||
return this._ownFields[fieldName] || (this._ownFields[fieldName] = new FieldConfig()) | ||
} | ||
|
||
getFields():{ [fieldName:string]:FieldConfig } { | ||
return this._fields; | ||
let parentFields = this.parent ? this.parent.getFields() : {}; | ||
|
||
//TODO: consider deep merge of field configuration. In current implementation ownField configuration overrides completely field config from parent | ||
return { | ||
...parentFields, | ||
...this._ownFields | ||
} | ||
} | ||
} | ||
|
||
private setParent(fieldsMetadata:FieldsMetadata) { | ||
this.parent = fieldsMetadata; | ||
} | ||
} | ||
|
||
|
||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,25 @@ | ||
import {FieldsMetadata} from "../../lib/fields-metadata/FieldsMetadata"; | ||
import {GraphQLString} from "graphql"; | ||
import {expect} from 'chai' | ||
|
||
describe(`FieldsMetadata`, () => { | ||
describe('inheritance', () => { | ||
class ParentClass {} | ||
|
||
let fieldsMetadata = FieldsMetadata.getOrCreateForClass(ParentClass); | ||
let parentField = fieldsMetadata.getField('parentField'); | ||
parentField.setType(GraphQLString); | ||
parentField.setDescription('Parent Field Description'); | ||
|
||
it('inherits all parent class properties', () => { | ||
class ChildClass extends ParentClass {} | ||
|
||
let fieldsMetadata = FieldsMetadata.getOrCreateForClass(ChildClass); | ||
let fields = fieldsMetadata.getFields(); | ||
|
||
expect(fields).to.have.keys(['parentField']); | ||
}); | ||
|
||
//TODO: add more test cases | ||
}) | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,146 @@ | ||
import {buildASTSchema, GraphQLInt, GraphQLString, parse, printType} from "graphql"; | ||
import {expect} from 'chai'; | ||
import {GraphQLSchema} from "graphql/type/schema"; | ||
import {createSchema, field, id, list, nonNull, nonNullItems, type} from "../../lib"; | ||
|
||
|
||
@type() | ||
class Mutation { | ||
@field(GraphQLString) | ||
unused:string; | ||
} | ||
|
||
|
||
@type() | ||
class ListResult { | ||
@field(GraphQLInt) @nonNull() | ||
totalCount:number; | ||
} | ||
|
||
@type() | ||
class PersistedObject { | ||
@id() @nonNull() | ||
id:string; | ||
|
||
@field(GraphQLInt) | ||
createdAt:number; //for simplification store as integer timestamp instead of custom scalar | ||
|
||
@field(GraphQLInt) | ||
updatedAt:number; //for simplification store as integer timestamp instead of custom scalar | ||
} | ||
|
||
@type() | ||
class User extends PersistedObject { | ||
@field(GraphQLString) | ||
email:string | ||
} | ||
|
||
@type() | ||
class UsersList extends ListResult { | ||
@list(User) @nonNull() @ nonNullItems() | ||
users:User[] | ||
} | ||
|
||
@type() | ||
class Product extends PersistedObject { | ||
@field(GraphQLString) | ||
productName:string | ||
} | ||
|
||
@type() | ||
class ProductsList extends ListResult { | ||
@list(Product) @nonNull() @ nonNullItems() | ||
products:Product[] | ||
} | ||
|
||
|
||
@type() | ||
class Query { | ||
@field(UsersList) @nonNull() | ||
users:UsersList; | ||
|
||
@field(ProductsList) @nonNull() | ||
products:ProductsList; | ||
} | ||
|
||
|
||
function createdSchemaFromDecoratedClasses():GraphQLSchema { | ||
return createSchema(Query, Mutation); | ||
} | ||
|
||
function createSchemaFromDefinition():GraphQLSchema { | ||
const definition = ` | ||
type User { | ||
id: ID! | ||
createdAt: Int | ||
updatedAt: Int | ||
email: String | ||
} | ||
type Product { | ||
id: ID! | ||
createdAt: Int | ||
updatedAt: Int | ||
productName: String | ||
} | ||
type UsersList { | ||
totalCount: Int! | ||
users: [User!]! | ||
} | ||
type ProductsList { | ||
totalCount: Int! | ||
products: [Product!]! | ||
} | ||
type Query { | ||
users: UsersList! | ||
products: ProductsList! | ||
} | ||
type Mutation { | ||
unused:String | ||
} | ||
`; | ||
return buildASTSchema(parse(definition)); | ||
} | ||
|
||
|
||
function expectTypesEqual(typeName:string) { | ||
expect(printType(createdSchemaFromDecoratedClasses().getType(typeName))) | ||
.to.eql(printType(createSchemaFromDefinition().getType(typeName))); | ||
} | ||
|
||
describe.only("building schema", () => { | ||
|
||
describe("type Query", () => { | ||
it("generates proper type", () => { | ||
expectTypesEqual('Query'); | ||
}); | ||
}); | ||
|
||
describe("type User", () => { | ||
it("generates proper type", () => { | ||
expectTypesEqual('User'); | ||
}); | ||
}); | ||
|
||
describe("type Product", () => { | ||
it("generates proper type", () => { | ||
expectTypesEqual('Product'); | ||
}); | ||
}); | ||
|
||
describe("type UsersList", () => { | ||
it("generates proper type", () => { | ||
expectTypesEqual('UsersList'); | ||
}); | ||
}); | ||
|
||
describe("input ProductsList", () => { | ||
it("generates proper type", () => { | ||
expectTypesEqual('ProductsList'); | ||
}); | ||
}); | ||
}); |