From 8759943638d201603f9c2f365c4145c635fb420b Mon Sep 17 00:00:00 2001 From: Brian Beck Date: Tue, 30 Aug 2016 23:33:29 -0700 Subject: [PATCH] wip --- package.json | 2 + schema.json | 374 ++++++++++++++++++++++++++++++++++--- src/api.js | 4 +- src/schema.js | 2 + src/types/area.js | 14 +- src/types/artist.js | 12 +- src/types/entity.js | 6 +- src/types/event.js | 12 +- src/types/helpers.js | 46 +---- src/types/index.js | 2 + src/types/instrument.js | 5 +- src/types/label.js | 12 +- src/types/node.js | 21 +++ src/types/place.js | 12 +- src/types/recording.js | 12 +- src/types/release-group.js | 12 +- src/types/release.js | 12 +- src/types/series.js | 30 +++ src/types/url.js | 10 +- src/types/work.js | 12 +- 20 files changed, 504 insertions(+), 108 deletions(-) create mode 100644 src/types/node.js diff --git a/package.json b/package.json index e705861..c4ba3e6 100644 --- a/package.json +++ b/package.json @@ -43,6 +43,8 @@ "express": "^4.14.0", "express-graphql": "^0.5.3", "graphql": "^0.6.2", + "graphql-relay": "^0.4.2", + "pascalcase": "^0.1.1", "qs": "^6.2.1", "request": "^2.74.0", "retry": "^0.9.0" diff --git a/schema.json b/schema.json index 8fe2550..480a5de 100644 --- a/schema.json +++ b/schema.json @@ -65,13 +65,13 @@ "args": [ { "name": "id", - "description": null, + "description": "The ID of an object", "type": { "kind": "NON_NULL", "name": null, "ofType": { "kind": "SCALAR", - "name": "MBID", + "name": "ID", "ofType": null } }, @@ -92,13 +92,13 @@ "args": [ { "name": "id", - "description": null, + "description": "The ID of an object", "type": { "kind": "NON_NULL", "name": null, "ofType": { "kind": "SCALAR", - "name": "MBID", + "name": "ID", "ofType": null } }, @@ -119,13 +119,13 @@ "args": [ { "name": "id", - "description": null, + "description": "The ID of an object", "type": { "kind": "NON_NULL", "name": null, "ofType": { "kind": "SCALAR", - "name": "MBID", + "name": "ID", "ofType": null } }, @@ -146,13 +146,13 @@ "args": [ { "name": "id", - "description": null, + "description": "The ID of an object", "type": { "kind": "NON_NULL", "name": null, "ofType": { "kind": "SCALAR", - "name": "MBID", + "name": "ID", "ofType": null } }, @@ -173,13 +173,13 @@ "args": [ { "name": "id", - "description": null, + "description": "The ID of an object", "type": { "kind": "NON_NULL", "name": null, "ofType": { "kind": "SCALAR", - "name": "MBID", + "name": "ID", "ofType": null } }, @@ -200,13 +200,13 @@ "args": [ { "name": "id", - "description": null, + "description": "The ID of an object", "type": { "kind": "NON_NULL", "name": null, "ofType": { "kind": "SCALAR", - "name": "MBID", + "name": "ID", "ofType": null } }, @@ -227,13 +227,13 @@ "args": [ { "name": "id", - "description": null, + "description": "The ID of an object", "type": { "kind": "NON_NULL", "name": null, "ofType": { "kind": "SCALAR", - "name": "MBID", + "name": "ID", "ofType": null } }, @@ -254,13 +254,13 @@ "args": [ { "name": "id", - "description": null, + "description": "The ID of an object", "type": { "kind": "NON_NULL", "name": null, "ofType": { "kind": "SCALAR", - "name": "MBID", + "name": "ID", "ofType": null } }, @@ -281,13 +281,13 @@ "args": [ { "name": "id", - "description": null, + "description": "The ID of an object", "type": { "kind": "NON_NULL", "name": null, "ofType": { "kind": "SCALAR", - "name": "MBID", + "name": "ID", "ofType": null } }, @@ -308,13 +308,13 @@ "args": [ { "name": "id", - "description": null, + "description": "The ID of an object", "type": { "kind": "NON_NULL", "name": null, "ofType": { "kind": "SCALAR", - "name": "MBID", + "name": "ID", "ofType": null } }, @@ -335,13 +335,13 @@ "args": [ { "name": "id", - "description": null, + "description": "The ID of an object", "type": { "kind": "NON_NULL", "name": null, "ofType": { "kind": "SCALAR", - "name": "MBID", + "name": "ID", "ofType": null } }, @@ -364,8 +364,8 @@ }, { "kind": "SCALAR", - "name": "MBID", - "description": "The `MBID` scalar represents MusicBrainz identifiers, which are 36-character UUIDs.", + "name": "ID", + "description": "The `ID` scalar type represents a unique identifier, often used to refetch an object or as key for a cache. The ID type appears in a JSON response as a String; however, it is not intended to be human-readable. When expected as an input type, any string (such as `\"4\"`) or integer (such as `4`) input value will be accepted as an ID.", "fields": null, "inputFields": null, "interfaces": null, @@ -379,6 +379,22 @@ "fields": [ { "name": "id", + "description": "The ID of an object", + "args": [], + "type": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "SCALAR", + "name": "ID", + "ofType": null + } + }, + "isDeprecated": false, + "deprecationReason": null + }, + { + "name": "mbid", "description": null, "args": [], "type": { @@ -681,6 +697,11 @@ ], "inputFields": null, "interfaces": [ + { + "kind": "INTERFACE", + "name": "Node", + "ofType": null + }, { "kind": "INTERFACE", "name": "Entity", @@ -690,13 +711,96 @@ "enumValues": null, "possibleTypes": null }, + { + "kind": "INTERFACE", + "name": "Node", + "description": "An object with an ID", + "fields": [ + { + "name": "id", + "description": "The id of the object.", + "args": [], + "type": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "SCALAR", + "name": "ID", + "ofType": null + } + }, + "isDeprecated": false, + "deprecationReason": null + } + ], + "inputFields": null, + "interfaces": null, + "enumValues": null, + "possibleTypes": [ + { + "kind": "OBJECT", + "name": "Area", + "ofType": null + }, + { + "kind": "OBJECT", + "name": "Artist", + "ofType": null + }, + { + "kind": "OBJECT", + "name": "Recording", + "ofType": null + }, + { + "kind": "OBJECT", + "name": "Release", + "ofType": null + }, + { + "kind": "OBJECT", + "name": "Label", + "ofType": null + }, + { + "kind": "OBJECT", + "name": "ReleaseGroup", + "ofType": null + }, + { + "kind": "OBJECT", + "name": "Work", + "ofType": null + }, + { + "kind": "OBJECT", + "name": "Event", + "ofType": null + }, + { + "kind": "OBJECT", + "name": "Place", + "ofType": null + }, + { + "kind": "OBJECT", + "name": "Instrument", + "ofType": null + }, + { + "kind": "OBJECT", + "name": "URL", + "ofType": null + } + ] + }, { "kind": "INTERFACE", "name": "Entity", "description": "An entity in the MusicBrainz schema.", "fields": [ { - "name": "id", + "name": "mbid", "description": null, "args": [], "type": { @@ -773,6 +877,16 @@ } ] }, + { + "kind": "SCALAR", + "name": "MBID", + "description": "The `MBID` scalar represents MusicBrainz identifiers, which are 36-character UUIDs.", + "fields": null, + "inputFields": null, + "interfaces": null, + "enumValues": null, + "possibleTypes": null + }, { "kind": "SCALAR", "name": "String", @@ -800,6 +914,22 @@ "fields": [ { "name": "id", + "description": "The ID of an object", + "args": [], + "type": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "SCALAR", + "name": "ID", + "ofType": null + } + }, + "isDeprecated": false, + "deprecationReason": null + }, + { + "name": "mbid", "description": null, "args": [], "type": { @@ -1209,6 +1339,11 @@ ], "inputFields": null, "interfaces": [ + { + "kind": "INTERFACE", + "name": "Node", + "ofType": null + }, { "kind": "INTERFACE", "name": "Entity", @@ -1375,6 +1510,22 @@ "fields": [ { "name": "id", + "description": "The ID of an object", + "args": [], + "type": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "SCALAR", + "name": "ID", + "ofType": null + } + }, + "isDeprecated": false, + "deprecationReason": null + }, + { + "name": "mbid", "description": null, "args": [], "type": { @@ -1590,6 +1741,11 @@ ], "inputFields": null, "interfaces": [ + { + "kind": "INTERFACE", + "name": "Node", + "ofType": null + }, { "kind": "INTERFACE", "name": "Entity", @@ -1795,6 +1951,22 @@ "fields": [ { "name": "id", + "description": "The ID of an object", + "args": [], + "type": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "SCALAR", + "name": "ID", + "ofType": null + } + }, + "isDeprecated": false, + "deprecationReason": null + }, + { + "name": "mbid", "description": null, "args": [], "type": { @@ -2148,6 +2320,11 @@ ], "inputFields": null, "interfaces": [ + { + "kind": "INTERFACE", + "name": "Node", + "ofType": null + }, { "kind": "INTERFACE", "name": "Entity", @@ -2199,6 +2376,22 @@ "fields": [ { "name": "id", + "description": "The ID of an object", + "args": [], + "type": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "SCALAR", + "name": "ID", + "ofType": null + } + }, + "isDeprecated": false, + "deprecationReason": null + }, + { + "name": "mbid", "description": null, "args": [], "type": { @@ -2425,6 +2618,11 @@ ], "inputFields": null, "interfaces": [ + { + "kind": "INTERFACE", + "name": "Node", + "ofType": null + }, { "kind": "INTERFACE", "name": "Entity", @@ -2451,6 +2649,22 @@ "fields": [ { "name": "id", + "description": "The ID of an object", + "args": [], + "type": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "SCALAR", + "name": "ID", + "ofType": null + } + }, + "isDeprecated": false, + "deprecationReason": null + }, + { + "name": "mbid", "description": null, "args": [], "type": { @@ -2710,6 +2924,11 @@ ], "inputFields": null, "interfaces": [ + { + "kind": "INTERFACE", + "name": "Node", + "ofType": null + }, { "kind": "INTERFACE", "name": "Entity", @@ -3700,6 +3919,22 @@ "fields": [ { "name": "id", + "description": "The ID of an object", + "args": [], + "type": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "SCALAR", + "name": "ID", + "ofType": null + } + }, + "isDeprecated": false, + "deprecationReason": null + }, + { + "name": "mbid", "description": null, "args": [], "type": { @@ -3842,6 +4077,11 @@ ], "inputFields": null, "interfaces": [ + { + "kind": "INTERFACE", + "name": "Node", + "ofType": null + }, { "kind": "INTERFACE", "name": "Entity", @@ -3858,6 +4098,22 @@ "fields": [ { "name": "id", + "description": "The ID of an object", + "args": [], + "type": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "SCALAR", + "name": "ID", + "ofType": null + } + }, + "isDeprecated": false, + "deprecationReason": null + }, + { + "name": "mbid", "description": null, "args": [], "type": { @@ -3971,6 +4227,11 @@ ], "inputFields": null, "interfaces": [ + { + "kind": "INTERFACE", + "name": "Node", + "ofType": null + }, { "kind": "INTERFACE", "name": "Entity", @@ -3997,6 +4258,22 @@ "fields": [ { "name": "id", + "description": "The ID of an object", + "args": [], + "type": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "SCALAR", + "name": "ID", + "ofType": null + } + }, + "isDeprecated": false, + "deprecationReason": null + }, + { + "name": "mbid", "description": null, "args": [], "type": { @@ -4147,6 +4424,11 @@ ], "inputFields": null, "interfaces": [ + { + "kind": "INTERFACE", + "name": "Node", + "ofType": null + }, { "kind": "INTERFACE", "name": "Entity", @@ -4208,6 +4490,22 @@ "fields": [ { "name": "id", + "description": "The ID of an object", + "args": [], + "type": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "SCALAR", + "name": "ID", + "ofType": null + } + }, + "isDeprecated": false, + "deprecationReason": null + }, + { + "name": "mbid", "description": null, "args": [], "type": { @@ -4285,6 +4583,11 @@ ], "inputFields": null, "interfaces": [ + { + "kind": "INTERFACE", + "name": "Node", + "ofType": null + }, { "kind": "INTERFACE", "name": "Entity", @@ -4301,6 +4604,22 @@ "fields": [ { "name": "id", + "description": "The ID of an object", + "args": [], + "type": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "SCALAR", + "name": "ID", + "ofType": null + } + }, + "isDeprecated": false, + "deprecationReason": null + }, + { + "name": "mbid", "description": null, "args": [], "type": { @@ -4346,6 +4665,11 @@ ], "inputFields": null, "interfaces": [ + { + "kind": "INTERFACE", + "name": "Node", + "ofType": null + }, { "kind": "INTERFACE", "name": "Entity", diff --git a/src/api.js b/src/api.js index c4d7a7d..6bc326c 100644 --- a/src/api.js +++ b/src/api.js @@ -40,8 +40,8 @@ export default class MusicBrainz { // we then wait a few seconds before making more. In practice this can // seemingly be set to about 5 requests every 5 seconds before we're // considered to exceed the rate limit. - limit: 3, - limitPeriod: 3000, + limit: 5, + limitPeriod: 5000, concurrency: 10, retries: 10, // It's OK for `retryDelayMin` to be less than one second, even 0, because diff --git a/src/schema.js b/src/schema.js index 63345e7..6db22e7 100644 --- a/src/schema.js +++ b/src/schema.js @@ -1,10 +1,12 @@ import { GraphQLSchema, GraphQLObjectType } from 'graphql' import { LookupQuery, BrowseQuery, SearchQuery } from './queries' +import { nodeField } from './types/node' export default new GraphQLSchema({ query: new GraphQLObjectType({ name: 'RootQuery', fields: () => ({ + node: nodeField, lookup: { type: LookupQuery, resolve: () => ({}) }, browse: { type: BrowseQuery, resolve: () => ({}) }, search: { type: SearchQuery, resolve: () => ({}) } diff --git a/src/types/area.js b/src/types/area.js index c249493..ee628c7 100644 --- a/src/types/area.js +++ b/src/types/area.js @@ -1,7 +1,10 @@ -import { GraphQLObjectType, GraphQLString, GraphQLList } from 'graphql/type' +import { GraphQLObjectType, GraphQLString, GraphQLList } from 'graphql' +import { connectionDefinitions } from 'graphql-relay' +import Node from './node' import Entity from './entity' import { id, + mbid, name, sortName, disambiguation, @@ -9,16 +12,16 @@ import { events, labels, places, - releases, - createPageType + releases } from './helpers' const Area = new GraphQLObjectType({ name: 'Area', description: 'A country, region, city or the like.', - interfaces: () => [Entity], + interfaces: () => [Node, Entity], fields: () => ({ id, + mbid, name, sortName, disambiguation, @@ -34,5 +37,6 @@ const Area = new GraphQLObjectType({ }) }) -export const AreaPage = createPageType(Area) +const { connectionType: AreaConnection } = connectionDefinitions({ nodeType: Area }) +export { AreaConnection } export default Area diff --git a/src/types/artist.js b/src/types/artist.js index b4cc134..c515916 100644 --- a/src/types/artist.js +++ b/src/types/artist.js @@ -1,4 +1,6 @@ import { GraphQLObjectType, GraphQLString, GraphQLList } from 'graphql/type' +import { connectionDefinitions } from 'graphql-relay' +import Node from './node' import Entity from './entity' import Alias from './alias' import Area from './area' @@ -6,6 +8,7 @@ import { getFallback, fieldWithID, id, + mbid, name, sortName, disambiguation, @@ -14,8 +17,7 @@ import { releases, releaseGroups, works, - relations, - createPageType + relations } from './helpers' const Artist = new GraphQLObjectType({ @@ -23,9 +25,10 @@ const Artist = new GraphQLObjectType({ description: 'An artist is generally a musician, a group of musicians, or another ' + 'music professional (composer, engineer, illustrator, producer, etc.)', - interfaces: () => [Entity], + interfaces: () => [Node, Entity], fields: () => ({ id, + mbid, name, sortName, disambiguation, @@ -65,5 +68,6 @@ const Artist = new GraphQLObjectType({ }) }) -export const ArtistPage = createPageType(Artist) +const { connectionType: ArtistConnection } = connectionDefinitions({ nodeType: Artist }) +export { ArtistConnection } export default Artist diff --git a/src/types/entity.js b/src/types/entity.js index cb4ce4b..2f0c0dc 100644 --- a/src/types/entity.js +++ b/src/types/entity.js @@ -1,5 +1,5 @@ -import { GraphQLInterfaceType } from 'graphql/type' -import { id } from './helpers' +import { GraphQLInterfaceType } from 'graphql' +import { mbid } from './helpers' export default new GraphQLInterfaceType({ name: 'Entity', @@ -10,6 +10,6 @@ export default new GraphQLInterfaceType({ } }, fields: () => ({ - id + mbid }) }) diff --git a/src/types/event.js b/src/types/event.js index bbfd505..3a96ca3 100644 --- a/src/types/event.js +++ b/src/types/event.js @@ -1,13 +1,15 @@ import { GraphQLObjectType, GraphQLString, GraphQLBoolean } from 'graphql/type' +import { connectionDefinitions } from 'graphql-relay' +import Node from './node' import Entity from './entity' import { Time } from './scalars' import { fieldWithID, id, + mbid, name, disambiguation, - lifeSpan, - createPageType + lifeSpan } from './helpers' const Event = new GraphQLObjectType({ @@ -15,9 +17,10 @@ const Event = new GraphQLObjectType({ description: 'An organized event which people can attend, usually live performances ' + 'like concerts and festivals.', - interfaces: () => [Entity], + interfaces: () => [Node, Entity], fields: () => ({ id, + mbid, name, disambiguation, lifeSpan, @@ -28,5 +31,6 @@ const Event = new GraphQLObjectType({ }) }) -export const EventPage = createPageType(Event) +const { connectionType: EventConnection } = connectionDefinitions({ nodeType: Event }) +export { EventConnection } export default Event diff --git a/src/types/helpers.js b/src/types/helpers.js index 712ea6a..b4c13c2 100644 --- a/src/types/helpers.js +++ b/src/types/helpers.js @@ -1,12 +1,14 @@ import dashify from 'dashify' +import pascalCase from 'pascalcase' import { GraphQLObjectType, GraphQLString, GraphQLInt, GraphQLList, GraphQLNonNull -} from 'graphql/type' -import { MBID, DateType } from './scalars' +} from 'graphql' +import { globalIdField } from 'graphql-relay' +import { MBID } from './scalars' import { ReleaseGroupType, ReleaseStatus } from './enums' import ArtistCredit from './artist-credit' import Artist from './artist' @@ -27,6 +29,9 @@ import { includeRelations } from '../resolvers' +export const toNodeType = pascalCase +export const toEntityType = dashify + export function getByline (data) { const credit = data['artist-credit'] if (credit && credit.length) { @@ -94,41 +99,8 @@ export function searchQuery (entityPage) { } } -export function createPageType (type) { - const singularName = dashify(type.name) - const pluralName = singularName + (singularName.endsWith('s') ? '' : 's') - return new GraphQLObjectType({ - name: `${type.name}Page`, - description: `A page of ${type.name} results from browsing or searching.`, - fields: { - count: { - type: new GraphQLNonNull(GraphQLInt), - resolve: list => { - if (list.count != null) { - return list.count - } - return list[`${singularName}-count`] - } - }, - offset: { - type: new GraphQLNonNull(GraphQLInt), - resolve: list => { - if (list.offset != null) { - return list.offset - } - return list[`${singularName}-offset`] - } - }, - created: { type: DateType }, - results: { - type: new GraphQLNonNull(new GraphQLList(type)), - resolve: list => list[pluralName] - } - } - }) -} - -export const id = { type: new GraphQLNonNull(MBID) } +export const id = globalIdField() +export const mbid = { type: new GraphQLNonNull(MBID) } export const name = { type: GraphQLString } export const sortName = { type: GraphQLString, resolve: getHyphenated } export const title = { type: GraphQLString } diff --git a/src/types/index.js b/src/types/index.js index 41e1514..93a3460 100644 --- a/src/types/index.js +++ b/src/types/index.js @@ -1,5 +1,6 @@ export { MBID, DateType, IPI, URLString } from './scalars' export { ReleaseGroupType, ReleaseStatus } from './enums' +export { default as Node } from './node' export { default as Entity } from './entity' export { default as Area, AreaPage } from './area' export { default as Artist, ArtistPage } from './artist' @@ -10,5 +11,6 @@ export { default as Place, PlacePage } from './place' export { default as Recording, RecordingPage } from './recording' export { default as Release, ReleasePage } from './release' export { default as ReleaseGroup, ReleaseGroupPage } from './release-group' +export { default as Series, SeriesPage } from './series' export { default as URL, URLPage } from './url' export { default as Work, WorkPage } from './work' diff --git a/src/types/instrument.js b/src/types/instrument.js index 9a83cb1..c753417 100644 --- a/src/types/instrument.js +++ b/src/types/instrument.js @@ -1,8 +1,10 @@ import { GraphQLObjectType, GraphQLString } from 'graphql/type' +import Node from './node' import Entity from './entity' import { fieldWithID, id, + mbid, name, disambiguation } from './helpers' @@ -11,9 +13,10 @@ const Instrument = new GraphQLObjectType({ name: 'Instrument', description: 'Instruments are devices created or adapted to make musical sounds.', - interfaces: () => [Entity], + interfaces: () => [Node, Entity], fields: () => ({ id, + mbid, name, disambiguation, description: { type: GraphQLString }, diff --git a/src/types/label.js b/src/types/label.js index 9c05733..ee223bf 100644 --- a/src/types/label.js +++ b/src/types/label.js @@ -4,26 +4,29 @@ import { GraphQLString, GraphQLInt } from 'graphql/type' +import { connectionDefinitions } from 'graphql-relay' +import Node from './node' import Entity from './entity' import { IPI } from './scalars' import Area from './area' import { id, + mbid, name, sortName, disambiguation, lifeSpan, releases, - fieldWithID, - createPageType + fieldWithID } from './helpers' const Label = new GraphQLObjectType({ name: 'Label', description: 'Labels represent mostly (but not only) imprints.', - interfaces: () => [Entity], + interfaces: () => [Node, Entity], fields: () => ({ id, + mbid, name, sortName, disambiguation, @@ -37,5 +40,6 @@ const Label = new GraphQLObjectType({ }) }) -export const LabelPage = createPageType(Label) +const { connectionType: LabelConnection } = connectionDefinitions({ nodeType: Label }) +export { LabelConnection } export default Label diff --git a/src/types/node.js b/src/types/node.js new file mode 100644 index 0000000..c44b147 --- /dev/null +++ b/src/types/node.js @@ -0,0 +1,21 @@ +import { nodeDefinitions, fromGlobalId } from 'graphql-relay' +import { lookupLoader } from '../loaders' +import { toEntityType } from './helpers' + +const { nodeInterface, nodeField } = nodeDefinitions( + (globalID) => { + const { type, id } = fromGlobalId(globalID) + const entityType = toEntityType(type) + return lookupLoader.load([entityType, id]) + }, + (obj) => { + try { + return require(`./${obj.entityType}`) + } catch (err) { + return null + } + } +) + +export default nodeInterface +export { nodeInterface, nodeField } diff --git a/src/types/place.js b/src/types/place.js index 7fdd628..d116b37 100644 --- a/src/types/place.js +++ b/src/types/place.js @@ -1,15 +1,17 @@ import { GraphQLObjectType, GraphQLString } from 'graphql/type' +import { connectionDefinitions } from 'graphql-relay' +import Node from './node' import Entity from './entity' import { Degrees } from './scalars' import Area from './area' import { id, + mbid, name, disambiguation, lifeSpan, events, - fieldWithID, - createPageType + fieldWithID } from './helpers' export const Coordinates = new GraphQLObjectType({ @@ -26,9 +28,10 @@ const Place = new GraphQLObjectType({ description: 'A venue, studio or other place where music is performed, recorded, ' + 'engineered, etc.', - interfaces: () => [Entity], + interfaces: () => [Node, Entity], fields: () => ({ id, + mbid, name, disambiguation, address: { type: GraphQLString }, @@ -40,5 +43,6 @@ const Place = new GraphQLObjectType({ }) }) -export const PlacePage = createPageType(Place) +const { connectionType: PlaceConnection } = connectionDefinitions({ nodeType: Place }) +export { PlaceConnection } export default Place diff --git a/src/types/recording.js b/src/types/recording.js index 7e08d2e..62a0c23 100644 --- a/src/types/recording.js +++ b/src/types/recording.js @@ -1,14 +1,16 @@ import { GraphQLObjectType, GraphQLInt, GraphQLBoolean } from 'graphql/type' +import { connectionDefinitions } from 'graphql-relay' +import Node from './node' import Entity from './entity' import { id, + mbid, title, disambiguation, artistCredit, artists, releases, - relations, - createPageType + relations } from './helpers' const Recording = new GraphQLObjectType({ @@ -16,9 +18,10 @@ const Recording = new GraphQLObjectType({ description: 'Represents a unique mix or edit. Has title, artist credit, duration, ' + 'list of PUIDs and ISRCs.', - interfaces: () => [Entity], + interfaces: () => [Node, Entity], fields: () => ({ id, + mbid, title, disambiguation, artistCredit, @@ -30,5 +33,6 @@ const Recording = new GraphQLObjectType({ }) }) -export const RecordingPage = createPageType(Recording) +const { connectionType: RecordingConnection } = connectionDefinitions({ nodeType: Recording }) +export { RecordingConnection } export default Recording diff --git a/src/types/release-group.js b/src/types/release-group.js index 7cfeba9..501fb6b 100644 --- a/src/types/release-group.js +++ b/src/types/release-group.js @@ -1,8 +1,11 @@ import { GraphQLObjectType, GraphQLString, GraphQLList } from 'graphql/type' +import { connectionDefinitions } from 'graphql-relay' +import Node from './node' import Entity from './entity' import { DateType } from './scalars' import { id, + mbid, title, disambiguation, artistCredit, @@ -10,8 +13,7 @@ import { releases, relations, getHyphenated, - fieldWithID, - createPageType + fieldWithID } from './helpers' const ReleaseGroup = new GraphQLObjectType({ @@ -19,9 +21,10 @@ const ReleaseGroup = new GraphQLObjectType({ description: 'Represents an abstract "album" (or "single", or "EP") entity. ' + 'Technically it’s a group of releases, with a specified type.', - interfaces: () => [Entity], + interfaces: () => [Node, Entity], fields: () => ({ id, + mbid, title, disambiguation, artistCredit, @@ -34,5 +37,6 @@ const ReleaseGroup = new GraphQLObjectType({ }) }) -export const ReleaseGroupPage = createPageType(ReleaseGroup) +const { connectionType: ReleaseGroupConnection } = connectionDefinitions({ nodeType: ReleaseGroup }) +export { ReleaseGroupConnection } export default ReleaseGroup diff --git a/src/types/release.js b/src/types/release.js index 68026a8..b5b2e65 100644 --- a/src/types/release.js +++ b/src/types/release.js @@ -1,9 +1,12 @@ import { GraphQLObjectType, GraphQLString, GraphQLList } from 'graphql/type' +import { connectionDefinitions } from 'graphql-relay' +import Node from './node' import Entity from './entity' import { DateType } from './scalars' import ReleaseEvent from './release-event' import { id, + mbid, title, disambiguation, artistCredit, @@ -13,8 +16,7 @@ import { releaseGroups, relations, getHyphenated, - fieldWithID, - createPageType + fieldWithID } from './helpers' const Release = new GraphQLObjectType({ @@ -23,9 +25,10 @@ const Release = new GraphQLObjectType({ 'Real-world release object you can buy in your music store. It has ' + 'release date and country, list of catalog number and label pairs, ' + 'packaging type and release status.', - interfaces: () => [Entity], + interfaces: () => [Node, Entity], fields: () => ({ id, + mbid, title, disambiguation, artistCredit, @@ -47,5 +50,6 @@ const Release = new GraphQLObjectType({ }) }) -export const ReleasePage = createPageType(Release) +const { connectionType: ReleaseConnection } = connectionDefinitions({ nodeType: Release }) +export { ReleaseConnection } export default Release diff --git a/src/types/series.js b/src/types/series.js index e69de29..da5d714 100644 --- a/src/types/series.js +++ b/src/types/series.js @@ -0,0 +1,30 @@ +import { GraphQLObjectType } from 'graphql/type' +import { connectionDefinitions } from 'graphql-relay' +import Node from './node' +import Entity from './entity' +import { + id, + mbid, + name, + disambiguation, + fieldWithID +} from './helpers' + +const Series = new GraphQLObjectType({ + name: 'Series', + description: + 'A series is a sequence of separate release groups, releases, ' + + 'recordings, works or events with a common theme.', + interfaces: () => [Node, Entity], + fields: () => ({ + id, + mbid, + name, + disambiguation, + ...fieldWithID('type') + }) +}) + +const { connectionType: SeriesConnection } = connectionDefinitions({ nodeType: Series }) +export { SeriesConnection } +export default Series diff --git a/src/types/url.js b/src/types/url.js index bc9cff4..1c46396 100644 --- a/src/types/url.js +++ b/src/types/url.js @@ -1,7 +1,9 @@ import { GraphQLObjectType, GraphQLNonNull } from 'graphql/type' +import { connectionDefinitions } from 'graphql-relay' +import Node from './node' import Entity from './entity' import { URLString } from './scalars' -import { id, relations, createPageType } from './helpers' +import { id, mbid, relations } from './helpers' const URL = new GraphQLObjectType({ name: 'URL', @@ -9,13 +11,15 @@ const URL = new GraphQLObjectType({ 'A URL pointing to a resource external to MusicBrainz, i.e. an official ' + 'homepage, a site where music can be acquired, an entry in another ' + 'database, etc.', - interfaces: () => [Entity], + interfaces: () => [Node, Entity], fields: () => ({ id, + mbid, resource: { type: new GraphQLNonNull(URLString) }, relations }) }) -export const URLPage = createPageType(URL) +const { connectionType: URLConnection } = connectionDefinitions({ nodeType: URL }) +export { URLConnection } export default URL diff --git a/src/types/work.js b/src/types/work.js index e56506c..6dcf16a 100644 --- a/src/types/work.js +++ b/src/types/work.js @@ -1,13 +1,15 @@ import { GraphQLObjectType, GraphQLString, GraphQLList } from 'graphql/type' +import { connectionDefinitions } from 'graphql-relay' +import Node from './node' import Entity from './entity' import { id, + mbid, title, disambiguation, artists, relations, - fieldWithID, - createPageType + fieldWithID } from './helpers' const Work = new GraphQLObjectType({ @@ -15,9 +17,10 @@ const Work = new GraphQLObjectType({ description: 'A distinct intellectual or artistic creation, which can be expressed in ' + 'the form of one or more audio recordings', - interfaces: () => [Entity], + interfaces: () => [Node, Entity], fields: () => ({ id, + mbid, title, disambiguation, iswcs: { type: new GraphQLList(GraphQLString) }, @@ -28,5 +31,6 @@ const Work = new GraphQLObjectType({ }) }) -export const WorkPage = createPageType(Work) +const { connectionType: WorkConnection } = connectionDefinitions({ nodeType: Work }) +export { WorkConnection } export default Work