Skip to content

Commit

Permalink
feat(catalog): add type tracking to the catalog
Browse files Browse the repository at this point in the history
  • Loading branch information
calebmer committed Oct 9, 2016
1 parent 0804bdf commit 0b04ce9
Show file tree
Hide file tree
Showing 6 changed files with 108 additions and 15 deletions.
72 changes: 66 additions & 6 deletions src/catalog/Catalog.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
import NamedType from './type/NamedType'
import AliasType from './type/AliasType'
import ObjectType from './type/object/ObjectType'
import Collection from './collection/Collection'
import Relation from './Relation'

Expand All @@ -22,13 +24,63 @@ import Relation from './Relation'
* *mutable*. Scary, I know.
*/
class Catalog {
private _types = new Map<string, NamedType<any>>()
private _collections = new Map<string, Collection<any>>()
private _relations = new Set<Relation<any, any, any>>()
private _relations = new Map<string, Relation<any, any, any>>()

/**
* Add a type to our catalog. If the type is a composite named type (like an
* alias type or an object type), we will also add the types it is composed
* of to the catalog.
*
* Type names must be unique. So if you had already added a type with the
* same name to the catalog, an error will be thrown.
*/
// TODO: add tests!
public addType (type: NamedType<any>): this {
// If the type has already been added, we good!
if (this.hasType(type))
return this

const name = type.getName()

if (this._types.has(name))
throw new Error(`Type of name '${name}' already exists in the catalog.`)

this._types.set(name, type)

// Add the base type if this is an alias type.
if (type instanceof AliasType)
this.addType(type.getBaseType().getNamedType())

// Add the type for all of the fields if this is an object type.
if (type instanceof ObjectType)
type.getFields().forEach(field => this.addType(field.getType().getNamedType()))

return this
}

/**
* Get all of the types in our catalog.
*/
public getTypes (): Array<NamedType<any>> {
return Array.from(this._types.values())
}

/**
* Returns true if the exact same type exists in this catalog, false if it
* doesn’t.
*/
public hasType (type: NamedType<any>): boolean {
return this._types.get(type.getName()) === type
}

/**
* Adds a single collection to our catalog. If a collection with the same
* name already exists, an error is thrown. If the collection has a
* different catalog, an error is thrown.
*
* We will also add the type for this collection to the catalog.
*/
public addCollection (collection: Collection<any>): this {
if (collection.getCatalog() !== this)
Expand All @@ -37,10 +89,13 @@ class Catalog {
const name = collection.getName()

if (this._collections.has(name))
throw new Error(`Type of name '${name}' already exists in the catalog.`)
throw new Error(`Collection of name '${name}' already exists in the catalog.`)

this._collections.set(name, collection)

// Add the type for this collection.
this.addType(collection.getType())

return this
}

Expand All @@ -52,7 +107,7 @@ class Catalog {
*/
// TODO: Test that the collection object type is returned by `getTypes`.
// TODO: Test that collections do not have the same name.
public getCollections (): Collection<any>[] {
public getCollections (): Array<Collection<any>> {
return Array.from(this._collections.values())
}

Expand Down Expand Up @@ -86,7 +141,12 @@ class Catalog {
if (!this.hasCollection(headCollection))
throw new Error(`Head collection named '${headCollection.getName()}' is not in this catalog.`)

this._relations.add(relation)
const name = relation.getName()

if (this._relations.has(name))
throw new Error(`Relation of name '${name}' already exists in the catalog.`)

this._relations.set(name, relation)

return this
}
Expand All @@ -99,8 +159,8 @@ class Catalog {
* In a graph representation of our catalog, collections would be nodes and
* relations would be directed edges.
*/
public getRelations (): Relation<any, any, any>[] {
return Array.from(this._relations)
public getRelations (): Array<Relation<any, any, any>> {
return Array.from(this._relations.values())
}
}

Expand Down
6 changes: 3 additions & 3 deletions src/catalog/type/AliasType.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,10 +14,10 @@ import NamedType from './NamedType'
* services that don’t support type aliasing. Like in GraphQL which doesn’t
* support aliasing unnamed types.
*/
class AliasType<TValue, TType extends Type<TValue>> extends NamedType<TValue> {
class AliasType<TValue> extends NamedType<TValue> {
constructor (
name: string,
private _baseType: TType,
private _baseType: Type<TValue>,
) {
super(name)
}
Expand All @@ -32,7 +32,7 @@ class AliasType<TValue, TType extends Type<TValue>> extends NamedType<TValue> {
/**
* Returns the base type for this alias type.
*/
public getBaseType (): TType {
public getBaseType (): Type<TValue> {
return this._baseType
}
}
Expand Down
14 changes: 11 additions & 3 deletions src/catalog/type/ListType.ts
Original file line number Diff line number Diff line change
@@ -1,14 +1,15 @@
import Type from './Type'
import NamedType from './NamedType'

/**
* A list type represents the type of a value of which there may be more than
* one. In a list there may be no items of a given type, there may be one item
* of a given type, there may be many items of a given type. A list may contain
* any number of values and this type represents that construct.
*/
class ListType<TValue, TItemType extends Type<TValue>> extends Type<TValue[]> {
class ListType<TValue> extends Type<TValue[]> {
constructor (
private _itemType: TItemType
private _itemType: Type<TValue>
) {
super()
}
Expand All @@ -31,9 +32,16 @@ class ListType<TValue, TItemType extends Type<TValue>> extends Type<TValue[]> {
/**
* Get’s the item type for this list type.
*/
public getItemType (): TItemType {
public getItemType (): Type<TValue> {
return this._itemType
}

/**
* Returns the item type.
*/
public getNamedType (): NamedType<any> {
return this._itemType.getNamedType()
}
}

export default ListType
8 changes: 8 additions & 0 deletions src/catalog/type/NamedType.ts
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,14 @@ abstract class NamedType<TValue> extends Type<TValue> {
public getDescription (): string | undefined {
return this._description
}

/**
* In this case this method just returns the `NamedType` instance straight
* up. Easy.
*/
public getNamedType (): this {
return this
}
}

export default NamedType
14 changes: 11 additions & 3 deletions src/catalog/type/NullableType.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import Type from './Type'
import NamedType from './NamedType'

/**
* A nullable type is an unnamed wrapper around any other type that signifies
Expand Down Expand Up @@ -29,9 +30,9 @@ import Type from './Type'
* Also note that it is possible that a nullable type could wrap another
* nullable type. This may lead to unexpected behavior.
*/
class NullableType<TValue, TBaseType extends Type<TValue>> extends Type<TValue | null> {
class NullableType<TValue> extends Type<TValue | null> {
constructor (
private _baseType: TBaseType,
private _baseType: Type<TValue>,
) {
super()
}
Expand All @@ -50,9 +51,16 @@ class NullableType<TValue, TBaseType extends Type<TValue>> extends Type<TValue |
/**
* Gets the base type for this nullable type.
*/
public getBaseType (): TBaseType {
public getBaseType (): Type<TValue> {
return this._baseType
}

/**
* Returns the base type.
*/
public getNamedType (): NamedType<any> {
return this._baseType.getNamedType()
}
}

export default NullableType
9 changes: 9 additions & 0 deletions src/catalog/type/Type.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import NamedType from './NamedType'

/**
* Any type in our system.
*
Expand All @@ -17,6 +19,13 @@ abstract class Type<TValue> {
* `typeof value === 'string'`.
*/
public abstract isTypeOf (value: any): value is TValue

/**
* Every type should have a named type at it’s “heart” as unnamed types are
* inherently abstract. As catalogs will only let us register named types, we
* need to get the named type.
*/
public abstract getNamedType (): NamedType<any>
}

export default Type

0 comments on commit 0b04ce9

Please sign in to comment.