diff --git a/docs/schema.md b/docs/schema.md
index 8723359..f1a5b2e 100644
--- a/docs/schema.md
+++ b/docs/schema.md
@@ -301,10 +301,46 @@ type BrowseQuery {
# The MBID of a release group to which the entity is linked.
releaseGroup: MBID
- # The MBID of a work to which the artist is linked.
+ # The MBID of a work to which the entity is linked.
work: MBID
): ArtistConnection
+ # Browse collection entities linked to the given arguments.
+ collections(
+ after: String
+ first: Int
+
+ # The MBID of an area to which the entity is linked.
+ area: MBID
+
+ # The MBID of an artist to which the entity is linked.
+ artist: MBID
+
+ # The username of the editor who created the collection.
+ editor: String
+
+ # The MBID of an event to which the entity is linked.
+ event: MBID
+
+ # The MBID of a label to which the entity is linked.
+ label: MBID
+
+ # The MBID of a place to which the entity is linked.
+ place: MBID
+
+ # The MBID of a recording to which the entity is linked.
+ recording: MBID
+
+ # The MBID of a release to which the entity is linked.
+ release: MBID
+
+ # The MBID of a release group to which the entity is linked.
+ releaseGroup: MBID
+
+ # The MBID of a work to which the entity is linked.
+ work: MBID
+ ): CollectionConnection
+
# Browse event entities linked to the given arguments.
events(
after: String
@@ -319,7 +355,7 @@ type BrowseQuery {
# The MBID of a collection in which the entity is found.
collection: MBID
- # The MBID of a place to which the event is linked.
+ # The MBID of a place to which the entity is linked.
place: MBID
): EventConnection
@@ -383,7 +419,7 @@ type BrowseQuery {
# The MBID of a collection in which the entity is found.
collection: MBID
- # The MBID of a label to which the release is linked.
+ # The MBID of a label to which the entity is linked.
label: MBID
# The MBID of a track that is included in the release.
@@ -445,6 +481,100 @@ type BrowseQuery {
): WorkConnection
}
+# [Collections](https://musicbrainz.org/doc/Collections) are
+# lists of entities that users can create.
+type Collection implements Node, Entity {
+ # The ID of an object
+ id: ID!
+
+ # The MBID of the entity.
+ mbid: MBID!
+
+ # The official name of the entity.
+ name: String
+
+ # The username of the editor who created the collection.
+ editor: String!
+
+ # The type of entity listed in the collection.
+ entityType: String!
+
+ # The type of collection.
+ type: String
+
+ # The MBID associated with the value of the `type`
+ # field.
+ typeID: MBID
+
+ # A list of areas linked to this entity.
+ areas(after: String, first: Int): AreaConnection
+
+ # A list of artists linked to this entity.
+ artists(after: String, first: Int): ArtistConnection
+
+ # A list of events linked to this entity.
+ events(after: String, first: Int): EventConnection
+
+ # A list of labels linked to this entity.
+ labels(after: String, first: Int): LabelConnection
+
+ # A list of places linked to this entity.
+ places(after: String, first: Int): PlaceConnection
+
+ # A list of recordings linked to this entity.
+ recordings(after: String, first: Int): RecordingConnection
+
+ # A list of releases linked to this entity.
+ releases(
+ after: String
+ first: Int
+
+ # Filter by one or more release group types.
+ type: [ReleaseGroupType]
+
+ # Filter by one or more release statuses.
+ status: [ReleaseStatus]
+ ): ReleaseConnection
+
+ # A list of release groups linked to this entity.
+ releaseGroups(
+ after: String
+ first: Int
+
+ # Filter by one or more release group types.
+ type: [ReleaseGroupType]
+ ): ReleaseGroupConnection
+
+ # A list of works linked to this entity.
+ works(after: String, first: Int): WorkConnection
+}
+
+# A connection to a list of items.
+type CollectionConnection {
+ # Information to aid in pagination.
+ pageInfo: PageInfo!
+
+ # A list of edges.
+ edges: [CollectionEdge]
+
+ # A count of the total number of items in this connection,
+ # ignoring pagination.
+ totalCount: Int
+}
+
+# An edge in a connection.
+type CollectionEdge {
+ # The item at the end of the edge
+ node: Collection
+
+ # A cursor for use in pagination
+ cursor: String!
+
+ # The relevancy score (0–100) assigned by the search engine, if
+ # these results were found through a search.
+ score: Int
+}
+
# Geographic coordinates described with latitude and longitude.
type Coordinates {
# The north–south position of a point on the Earth’s surface.
@@ -475,6 +605,9 @@ scalar Degrees
# offsets and hence the same disc ID.
scalar DiscID
+# A length of time, in milliseconds.
+scalar Duration
+
# An entity in the MusicBrainz schema.
interface Entity {
# The MBID of the entity.
@@ -775,6 +908,12 @@ type LookupQuery {
mbid: MBID!
): Artist
+ # Look up a specific collection by its MBID.
+ collection(
+ # The MBID of the entity.
+ mbid: MBID!
+ ): Collection
+
# Look up a specific event by its MBID.
event(
# The MBID of the entity.
@@ -1002,7 +1141,7 @@ type Recording implements Node, Entity {
# An approximation to the length of the recording, calculated
# from the lengths of the tracks using it.
- length: Int
+ length: Duration
# Whether this is a video recording.
video: Boolean
diff --git a/docs/types.md b/docs/types.md
index da9f59d..c5787cc 100644
--- a/docs/types.md
+++ b/docs/types.md
@@ -14,6 +14,9 @@ You may also be interested in reading the [schema in GraphQL syntax](schema.md).
| length |
- Int |
+ Duration |
An approximation to the length of the recording, calculated
from the lengths of the tracks using it.
@@ -4760,6 +5145,10 @@ hence different disc IDs.
Conversely, two different CDs may happen to have exactly the same set of frame
offsets and hence the same disc ID.
+### Duration
+
+A length of time, in milliseconds.
+
### ID
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.
diff --git a/schema.json b/schema.json
index c6e6ac2..9619465 100644
--- a/schema.json
+++ b/schema.json
@@ -140,6 +140,33 @@
"isDeprecated": false,
"deprecationReason": null
},
+ {
+ "name": "collection",
+ "description": "Look up a specific collection by its MBID.",
+ "args": [
+ {
+ "name": "mbid",
+ "description": "The MBID of the entity.",
+ "type": {
+ "kind": "NON_NULL",
+ "name": null,
+ "ofType": {
+ "kind": "SCALAR",
+ "name": "MBID",
+ "ofType": null
+ }
+ },
+ "defaultValue": null
+ }
+ ],
+ "type": {
+ "kind": "OBJECT",
+ "name": "Collection",
+ "ofType": null
+ },
+ "isDeprecated": false,
+ "deprecationReason": null
+ },
{
"name": "event",
"description": "Look up a specific event by its MBID.",
@@ -863,6 +890,11 @@
"name": "Place",
"ofType": null
},
+ {
+ "kind": "OBJECT",
+ "name": "Collection",
+ "ofType": null
+ },
{
"kind": "OBJECT",
"name": "Instrument",
@@ -961,6 +993,11 @@
"name": "Place",
"ofType": null
},
+ {
+ "kind": "OBJECT",
+ "name": "Collection",
+ "ofType": null
+ },
{
"kind": "OBJECT",
"name": "Instrument",
@@ -2053,7 +2090,7 @@
"args": [],
"type": {
"kind": "SCALAR",
- "name": "Int",
+ "name": "Duration",
"ofType": null
},
"isDeprecated": false,
@@ -2284,6 +2321,16 @@
"enumValues": null,
"possibleTypes": null
},
+ {
+ "kind": "SCALAR",
+ "name": "Duration",
+ "description": "A length of time, in milliseconds.",
+ "fields": null,
+ "inputFields": null,
+ "interfaces": null,
+ "enumValues": null,
+ "possibleTypes": null
+ },
{
"kind": "ENUM",
"name": "ReleaseGroupType",
@@ -6332,6 +6379,573 @@
"enumValues": null,
"possibleTypes": null
},
+ {
+ "kind": "OBJECT",
+ "name": "Collection",
+ "description": "[Collections](https://musicbrainz.org/doc/Collections) are\nlists of entities that users can create.",
+ "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": "The MBID of the entity.",
+ "args": [],
+ "type": {
+ "kind": "NON_NULL",
+ "name": null,
+ "ofType": {
+ "kind": "SCALAR",
+ "name": "MBID",
+ "ofType": null
+ }
+ },
+ "isDeprecated": false,
+ "deprecationReason": null
+ },
+ {
+ "name": "name",
+ "description": "The official name of the entity.",
+ "args": [],
+ "type": {
+ "kind": "SCALAR",
+ "name": "String",
+ "ofType": null
+ },
+ "isDeprecated": false,
+ "deprecationReason": null
+ },
+ {
+ "name": "editor",
+ "description": "The username of the editor who created the collection.",
+ "args": [],
+ "type": {
+ "kind": "NON_NULL",
+ "name": null,
+ "ofType": {
+ "kind": "SCALAR",
+ "name": "String",
+ "ofType": null
+ }
+ },
+ "isDeprecated": false,
+ "deprecationReason": null
+ },
+ {
+ "name": "entityType",
+ "description": "The type of entity listed in the collection.",
+ "args": [],
+ "type": {
+ "kind": "NON_NULL",
+ "name": null,
+ "ofType": {
+ "kind": "SCALAR",
+ "name": "String",
+ "ofType": null
+ }
+ },
+ "isDeprecated": false,
+ "deprecationReason": null
+ },
+ {
+ "name": "type",
+ "description": "The type of collection.",
+ "args": [],
+ "type": {
+ "kind": "SCALAR",
+ "name": "String",
+ "ofType": null
+ },
+ "isDeprecated": false,
+ "deprecationReason": null
+ },
+ {
+ "name": "typeID",
+ "description": "The MBID associated with the value of the `type`\nfield.",
+ "args": [],
+ "type": {
+ "kind": "SCALAR",
+ "name": "MBID",
+ "ofType": null
+ },
+ "isDeprecated": false,
+ "deprecationReason": null
+ },
+ {
+ "name": "areas",
+ "description": "A list of areas linked to this entity.",
+ "args": [
+ {
+ "name": "after",
+ "description": null,
+ "type": {
+ "kind": "SCALAR",
+ "name": "String",
+ "ofType": null
+ },
+ "defaultValue": null
+ },
+ {
+ "name": "first",
+ "description": null,
+ "type": {
+ "kind": "SCALAR",
+ "name": "Int",
+ "ofType": null
+ },
+ "defaultValue": null
+ }
+ ],
+ "type": {
+ "kind": "OBJECT",
+ "name": "AreaConnection",
+ "ofType": null
+ },
+ "isDeprecated": false,
+ "deprecationReason": null
+ },
+ {
+ "name": "artists",
+ "description": "A list of artists linked to this entity.",
+ "args": [
+ {
+ "name": "after",
+ "description": null,
+ "type": {
+ "kind": "SCALAR",
+ "name": "String",
+ "ofType": null
+ },
+ "defaultValue": null
+ },
+ {
+ "name": "first",
+ "description": null,
+ "type": {
+ "kind": "SCALAR",
+ "name": "Int",
+ "ofType": null
+ },
+ "defaultValue": null
+ }
+ ],
+ "type": {
+ "kind": "OBJECT",
+ "name": "ArtistConnection",
+ "ofType": null
+ },
+ "isDeprecated": false,
+ "deprecationReason": null
+ },
+ {
+ "name": "events",
+ "description": "A list of events linked to this entity.",
+ "args": [
+ {
+ "name": "after",
+ "description": null,
+ "type": {
+ "kind": "SCALAR",
+ "name": "String",
+ "ofType": null
+ },
+ "defaultValue": null
+ },
+ {
+ "name": "first",
+ "description": null,
+ "type": {
+ "kind": "SCALAR",
+ "name": "Int",
+ "ofType": null
+ },
+ "defaultValue": null
+ }
+ ],
+ "type": {
+ "kind": "OBJECT",
+ "name": "EventConnection",
+ "ofType": null
+ },
+ "isDeprecated": false,
+ "deprecationReason": null
+ },
+ {
+ "name": "labels",
+ "description": "A list of labels linked to this entity.",
+ "args": [
+ {
+ "name": "after",
+ "description": null,
+ "type": {
+ "kind": "SCALAR",
+ "name": "String",
+ "ofType": null
+ },
+ "defaultValue": null
+ },
+ {
+ "name": "first",
+ "description": null,
+ "type": {
+ "kind": "SCALAR",
+ "name": "Int",
+ "ofType": null
+ },
+ "defaultValue": null
+ }
+ ],
+ "type": {
+ "kind": "OBJECT",
+ "name": "LabelConnection",
+ "ofType": null
+ },
+ "isDeprecated": false,
+ "deprecationReason": null
+ },
+ {
+ "name": "places",
+ "description": "A list of places linked to this entity.",
+ "args": [
+ {
+ "name": "after",
+ "description": null,
+ "type": {
+ "kind": "SCALAR",
+ "name": "String",
+ "ofType": null
+ },
+ "defaultValue": null
+ },
+ {
+ "name": "first",
+ "description": null,
+ "type": {
+ "kind": "SCALAR",
+ "name": "Int",
+ "ofType": null
+ },
+ "defaultValue": null
+ }
+ ],
+ "type": {
+ "kind": "OBJECT",
+ "name": "PlaceConnection",
+ "ofType": null
+ },
+ "isDeprecated": false,
+ "deprecationReason": null
+ },
+ {
+ "name": "recordings",
+ "description": "A list of recordings linked to this entity.",
+ "args": [
+ {
+ "name": "after",
+ "description": null,
+ "type": {
+ "kind": "SCALAR",
+ "name": "String",
+ "ofType": null
+ },
+ "defaultValue": null
+ },
+ {
+ "name": "first",
+ "description": null,
+ "type": {
+ "kind": "SCALAR",
+ "name": "Int",
+ "ofType": null
+ },
+ "defaultValue": null
+ }
+ ],
+ "type": {
+ "kind": "OBJECT",
+ "name": "RecordingConnection",
+ "ofType": null
+ },
+ "isDeprecated": false,
+ "deprecationReason": null
+ },
+ {
+ "name": "releases",
+ "description": "A list of releases linked to this entity.",
+ "args": [
+ {
+ "name": "after",
+ "description": null,
+ "type": {
+ "kind": "SCALAR",
+ "name": "String",
+ "ofType": null
+ },
+ "defaultValue": null
+ },
+ {
+ "name": "first",
+ "description": null,
+ "type": {
+ "kind": "SCALAR",
+ "name": "Int",
+ "ofType": null
+ },
+ "defaultValue": null
+ },
+ {
+ "name": "type",
+ "description": "Filter by one or more release group types.",
+ "type": {
+ "kind": "LIST",
+ "name": null,
+ "ofType": {
+ "kind": "ENUM",
+ "name": "ReleaseGroupType",
+ "ofType": null
+ }
+ },
+ "defaultValue": null
+ },
+ {
+ "name": "status",
+ "description": "Filter by one or more release statuses.",
+ "type": {
+ "kind": "LIST",
+ "name": null,
+ "ofType": {
+ "kind": "ENUM",
+ "name": "ReleaseStatus",
+ "ofType": null
+ }
+ },
+ "defaultValue": null
+ }
+ ],
+ "type": {
+ "kind": "OBJECT",
+ "name": "ReleaseConnection",
+ "ofType": null
+ },
+ "isDeprecated": false,
+ "deprecationReason": null
+ },
+ {
+ "name": "releaseGroups",
+ "description": "A list of release groups linked to this entity.",
+ "args": [
+ {
+ "name": "after",
+ "description": null,
+ "type": {
+ "kind": "SCALAR",
+ "name": "String",
+ "ofType": null
+ },
+ "defaultValue": null
+ },
+ {
+ "name": "first",
+ "description": null,
+ "type": {
+ "kind": "SCALAR",
+ "name": "Int",
+ "ofType": null
+ },
+ "defaultValue": null
+ },
+ {
+ "name": "type",
+ "description": "Filter by one or more release group types.",
+ "type": {
+ "kind": "LIST",
+ "name": null,
+ "ofType": {
+ "kind": "ENUM",
+ "name": "ReleaseGroupType",
+ "ofType": null
+ }
+ },
+ "defaultValue": null
+ }
+ ],
+ "type": {
+ "kind": "OBJECT",
+ "name": "ReleaseGroupConnection",
+ "ofType": null
+ },
+ "isDeprecated": false,
+ "deprecationReason": null
+ },
+ {
+ "name": "works",
+ "description": "A list of works linked to this entity.",
+ "args": [
+ {
+ "name": "after",
+ "description": null,
+ "type": {
+ "kind": "SCALAR",
+ "name": "String",
+ "ofType": null
+ },
+ "defaultValue": null
+ },
+ {
+ "name": "first",
+ "description": null,
+ "type": {
+ "kind": "SCALAR",
+ "name": "Int",
+ "ofType": null
+ },
+ "defaultValue": null
+ }
+ ],
+ "type": {
+ "kind": "OBJECT",
+ "name": "WorkConnection",
+ "ofType": null
+ },
+ "isDeprecated": false,
+ "deprecationReason": null
+ }
+ ],
+ "inputFields": null,
+ "interfaces": [
+ {
+ "kind": "INTERFACE",
+ "name": "Node",
+ "ofType": null
+ },
+ {
+ "kind": "INTERFACE",
+ "name": "Entity",
+ "ofType": null
+ }
+ ],
+ "enumValues": null,
+ "possibleTypes": null
+ },
+ {
+ "kind": "OBJECT",
+ "name": "AreaConnection",
+ "description": "A connection to a list of items.",
+ "fields": [
+ {
+ "name": "pageInfo",
+ "description": "Information to aid in pagination.",
+ "args": [],
+ "type": {
+ "kind": "NON_NULL",
+ "name": null,
+ "ofType": {
+ "kind": "OBJECT",
+ "name": "PageInfo",
+ "ofType": null
+ }
+ },
+ "isDeprecated": false,
+ "deprecationReason": null
+ },
+ {
+ "name": "edges",
+ "description": "A list of edges.",
+ "args": [],
+ "type": {
+ "kind": "LIST",
+ "name": null,
+ "ofType": {
+ "kind": "OBJECT",
+ "name": "AreaEdge",
+ "ofType": null
+ }
+ },
+ "isDeprecated": false,
+ "deprecationReason": null
+ },
+ {
+ "name": "totalCount",
+ "description": "A count of the total number of items in this connection,\nignoring pagination.",
+ "args": [],
+ "type": {
+ "kind": "SCALAR",
+ "name": "Int",
+ "ofType": null
+ },
+ "isDeprecated": false,
+ "deprecationReason": null
+ }
+ ],
+ "inputFields": null,
+ "interfaces": [],
+ "enumValues": null,
+ "possibleTypes": null
+ },
+ {
+ "kind": "OBJECT",
+ "name": "AreaEdge",
+ "description": "An edge in a connection.",
+ "fields": [
+ {
+ "name": "node",
+ "description": "The item at the end of the edge",
+ "args": [],
+ "type": {
+ "kind": "OBJECT",
+ "name": "Area",
+ "ofType": null
+ },
+ "isDeprecated": false,
+ "deprecationReason": null
+ },
+ {
+ "name": "cursor",
+ "description": "A cursor for use in pagination",
+ "args": [],
+ "type": {
+ "kind": "NON_NULL",
+ "name": null,
+ "ofType": {
+ "kind": "SCALAR",
+ "name": "String",
+ "ofType": null
+ }
+ },
+ "isDeprecated": false,
+ "deprecationReason": null
+ },
+ {
+ "name": "score",
+ "description": "The relevancy score (0–100) assigned by the search engine, if\nthese results were found through a search.",
+ "args": [],
+ "type": {
+ "kind": "SCALAR",
+ "name": "Int",
+ "ofType": null
+ },
+ "isDeprecated": false,
+ "deprecationReason": null
+ }
+ ],
+ "inputFields": null,
+ "interfaces": [],
+ "enumValues": null,
+ "possibleTypes": null
+ },
{
"kind": "OBJECT",
"name": "Instrument",
@@ -6870,7 +7484,7 @@
},
{
"name": "work",
- "description": "The MBID of a work to which the artist is linked.",
+ "description": "The MBID of a work to which the entity is linked.",
"type": {
"kind": "SCALAR",
"name": "MBID",
@@ -6887,6 +7501,139 @@
"isDeprecated": false,
"deprecationReason": null
},
+ {
+ "name": "collections",
+ "description": "Browse collection entities linked to the given arguments.",
+ "args": [
+ {
+ "name": "after",
+ "description": null,
+ "type": {
+ "kind": "SCALAR",
+ "name": "String",
+ "ofType": null
+ },
+ "defaultValue": null
+ },
+ {
+ "name": "first",
+ "description": null,
+ "type": {
+ "kind": "SCALAR",
+ "name": "Int",
+ "ofType": null
+ },
+ "defaultValue": null
+ },
+ {
+ "name": "area",
+ "description": "The MBID of an area to which the entity is linked.",
+ "type": {
+ "kind": "SCALAR",
+ "name": "MBID",
+ "ofType": null
+ },
+ "defaultValue": null
+ },
+ {
+ "name": "artist",
+ "description": "The MBID of an artist to which the entity is linked.",
+ "type": {
+ "kind": "SCALAR",
+ "name": "MBID",
+ "ofType": null
+ },
+ "defaultValue": null
+ },
+ {
+ "name": "editor",
+ "description": "The username of the editor who created the collection.",
+ "type": {
+ "kind": "SCALAR",
+ "name": "String",
+ "ofType": null
+ },
+ "defaultValue": null
+ },
+ {
+ "name": "event",
+ "description": "The MBID of an event to which the entity is linked.",
+ "type": {
+ "kind": "SCALAR",
+ "name": "MBID",
+ "ofType": null
+ },
+ "defaultValue": null
+ },
+ {
+ "name": "label",
+ "description": "The MBID of a label to which the entity is linked.",
+ "type": {
+ "kind": "SCALAR",
+ "name": "MBID",
+ "ofType": null
+ },
+ "defaultValue": null
+ },
+ {
+ "name": "place",
+ "description": "The MBID of a place to which the entity is linked.",
+ "type": {
+ "kind": "SCALAR",
+ "name": "MBID",
+ "ofType": null
+ },
+ "defaultValue": null
+ },
+ {
+ "name": "recording",
+ "description": "The MBID of a recording to which the entity is linked.",
+ "type": {
+ "kind": "SCALAR",
+ "name": "MBID",
+ "ofType": null
+ },
+ "defaultValue": null
+ },
+ {
+ "name": "release",
+ "description": "The MBID of a release to which the entity is linked.",
+ "type": {
+ "kind": "SCALAR",
+ "name": "MBID",
+ "ofType": null
+ },
+ "defaultValue": null
+ },
+ {
+ "name": "releaseGroup",
+ "description": "The MBID of a release group to which the entity is linked.",
+ "type": {
+ "kind": "SCALAR",
+ "name": "MBID",
+ "ofType": null
+ },
+ "defaultValue": null
+ },
+ {
+ "name": "work",
+ "description": "The MBID of a work to which the entity is linked.",
+ "type": {
+ "kind": "SCALAR",
+ "name": "MBID",
+ "ofType": null
+ },
+ "defaultValue": null
+ }
+ ],
+ "type": {
+ "kind": "OBJECT",
+ "name": "CollectionConnection",
+ "ofType": null
+ },
+ "isDeprecated": false,
+ "deprecationReason": null
+ },
{
"name": "events",
"description": "Browse event entities linked to the given arguments.",
@@ -6943,7 +7690,7 @@
},
{
"name": "place",
- "description": "The MBID of a place to which the event is linked.",
+ "description": "The MBID of a place to which the entity is linked.",
"type": {
"kind": "SCALAR",
"name": "MBID",
@@ -7205,7 +7952,7 @@
},
{
"name": "label",
- "description": "The MBID of a label to which the release is linked.",
+ "description": "The MBID of a label to which the entity is linked.",
"type": {
"kind": "SCALAR",
"name": "MBID",
@@ -7448,7 +8195,7 @@
},
{
"kind": "OBJECT",
- "name": "AreaConnection",
+ "name": "CollectionConnection",
"description": "A connection to a list of items.",
"fields": [
{
@@ -7476,7 +8223,7 @@
"name": null,
"ofType": {
"kind": "OBJECT",
- "name": "AreaEdge",
+ "name": "CollectionEdge",
"ofType": null
}
},
@@ -7503,7 +8250,7 @@
},
{
"kind": "OBJECT",
- "name": "AreaEdge",
+ "name": "CollectionEdge",
"description": "An edge in a connection.",
"fields": [
{
@@ -7512,7 +8259,7 @@
"args": [],
"type": {
"kind": "OBJECT",
- "name": "Area",
+ "name": "Collection",
"ofType": null
},
"isDeprecated": false,
diff --git a/src/queries/browse.js b/src/queries/browse.js
index 85ff3cb..dc87941 100644
--- a/src/queries/browse.js
+++ b/src/queries/browse.js
@@ -1,10 +1,11 @@
-import { GraphQLObjectType } from 'graphql'
+import { GraphQLObjectType, GraphQLString } from 'graphql'
import { forwardConnectionArgs } from 'graphql-relay'
import { resolveBrowse } from '../resolvers'
import {
MBID,
AreaConnection,
ArtistConnection,
+ CollectionConnection,
EventConnection,
DiscID,
ISRC,
@@ -30,6 +31,18 @@ const collection = {
type: MBID,
description: 'The MBID of a collection in which the entity is found.'
}
+const event = {
+ type: MBID,
+ description: 'The MBID of an event to which the entity is linked.'
+}
+const label = {
+ type: MBID,
+ description: 'The MBID of a label to which the entity is linked.'
+}
+const place = {
+ type: MBID,
+ description: 'The MBID of a place to which the entity is linked.'
+}
const recording = {
type: MBID,
description: 'The MBID of a recording to which the entity is linked.'
@@ -42,6 +55,10 @@ const releaseGroup = {
type: MBID,
description: 'The MBID of a release group to which the entity is linked.'
}
+const work = {
+ type: MBID,
+ description: 'The MBID of a work to which the entity is linked.'
+}
function createBrowseField (connectionType, args) {
const typeName = toWords(connectionType.name.slice(0, -10))
@@ -70,19 +87,28 @@ entity.`,
recording,
release,
releaseGroup,
- work: {
- type: MBID,
- description: 'The MBID of a work to which the artist is linked.'
- }
+ work
+ }),
+ collections: createBrowseField(CollectionConnection, {
+ area,
+ artist,
+ editor: {
+ type: GraphQLString,
+ description: 'The username of the editor who created the collection.'
+ },
+ event,
+ label,
+ place,
+ recording,
+ release,
+ releaseGroup,
+ work
}),
events: createBrowseField(EventConnection, {
area,
artist,
collection,
- place: {
- type: MBID,
- description: 'The MBID of a place to which the event is linked.'
- }
+ place
}),
labels: createBrowseField(LabelConnection, {
area,
@@ -107,10 +133,7 @@ entity.`,
area,
artist,
collection,
- label: {
- type: MBID,
- description: 'The MBID of a label to which the release is linked.'
- },
+ label,
track: {
type: MBID,
description: 'The MBID of a track that is included in the release.'
diff --git a/src/queries/lookup.js b/src/queries/lookup.js
index cf5085e..4f01819 100644
--- a/src/queries/lookup.js
+++ b/src/queries/lookup.js
@@ -4,6 +4,7 @@ import { mbid, toWords } from '../types/helpers'
import {
Area,
Artist,
+ Collection,
Event,
Instrument,
Label,
@@ -34,6 +35,7 @@ export const LookupQuery = new GraphQLObjectType({
fields: {
area: createLookupField(Area),
artist: createLookupField(Artist),
+ collection: createLookupField(Collection),
event: createLookupField(Event),
instrument: createLookupField(Instrument),
label: createLookupField(Label),
diff --git a/src/types/collection.js b/src/types/collection.js
new file mode 100644
index 0000000..d4d2bba
--- /dev/null
+++ b/src/types/collection.js
@@ -0,0 +1,60 @@
+import {
+ GraphQLObjectType,
+ GraphQLNonNull,
+ GraphQLString
+} from 'graphql/type'
+import Node from './node'
+import Entity from './entity'
+import {
+ id,
+ mbid,
+ name,
+ areas,
+ artists,
+ events,
+ labels,
+ places,
+ recordings,
+ releases,
+ releaseGroups,
+ works,
+ fieldWithID,
+ resolveHyphenated,
+ connectionWithExtras
+} from './helpers'
+
+const Collection = new GraphQLObjectType({
+ name: 'Collection',
+ description: `[Collections](https://musicbrainz.org/doc/Collections) are
+lists of entities that users can create.`,
+ interfaces: () => [Node, Entity],
+ fields: () => ({
+ id,
+ mbid,
+ name,
+ editor: {
+ type: new GraphQLNonNull(GraphQLString),
+ description: 'The username of the editor who created the collection.'
+ },
+ entityType: {
+ type: new GraphQLNonNull(GraphQLString),
+ description: 'The type of entity listed in the collection.',
+ resolve: resolveHyphenated
+ },
+ ...fieldWithID('type', {
+ description: 'The type of collection.'
+ }),
+ areas,
+ artists,
+ events,
+ labels,
+ places,
+ recordings,
+ releases,
+ releaseGroups,
+ works
+ })
+})
+
+export const CollectionConnection = connectionWithExtras(Collection)
+export default Collection
diff --git a/src/types/helpers.js b/src/types/helpers.js
index 2c8423d..4d13852 100644
--- a/src/types/helpers.js
+++ b/src/types/helpers.js
@@ -18,6 +18,7 @@ import { MBID } from './scalars'
import { ReleaseGroupType, ReleaseStatus } from './enums'
import Alias from './alias'
import ArtistCredit from './artist-credit'
+import { AreaConnection } from './area'
import { ArtistConnection } from './artist'
import { EventConnection } from './event'
import { LabelConnection } from './label'
@@ -221,6 +222,7 @@ export const releaseStatus = {
description: 'Filter by one or more release statuses.'
}
+export const areas = linkedQuery(AreaConnection)
export const artists = linkedQuery(ArtistConnection)
export const events = linkedQuery(EventConnection)
export const labels = linkedQuery(LabelConnection)
diff --git a/src/types/index.js b/src/types/index.js
index d6e9ca4..5a9dbe2 100644
--- a/src/types/index.js
+++ b/src/types/index.js
@@ -4,6 +4,7 @@ export { default as Node } from './node'
export { default as Entity, EntityConnection } from './entity'
export { default as Area, AreaConnection } from './area'
export { default as Artist, ArtistConnection } from './artist'
+export { default as Collection, CollectionConnection } from './collection'
export { default as Event, EventConnection } from './event'
export { default as Instrument, InstrumentConnection } from './instrument'
export { default as Label, LabelConnection } from './label'
diff --git a/test/fixtures/3d2ee72516840ca0816bd567f766b32a b/test/fixtures/3d2ee72516840ca0816bd567f766b32a
new file mode 100644
index 0000000..c11e816
--- /dev/null
+++ b/test/fixtures/3d2ee72516840ca0816bd567f766b32a
@@ -0,0 +1 @@
+{"editor":"offbeatadam","type-id":"d94659b2-4ce5-3a98-b4b8-da1131cf33ee","id":"85da782d-2ec0-41ec-a97f-9be464bba309","type":"Release","release-count":579,"name":"Beets Music Collection","entity-type":"release"}
\ No newline at end of file
diff --git a/test/fixtures/3d2ee72516840ca0816bd567f766b32a.headers b/test/fixtures/3d2ee72516840ca0816bd567f766b32a.headers
new file mode 100644
index 0000000..5d9d380
--- /dev/null
+++ b/test/fixtures/3d2ee72516840ca0816bd567f766b32a.headers
@@ -0,0 +1,27 @@
+{
+ "statusCode": 200,
+ "headers": {
+ "date": "Mon, 12 Dec 2016 03:50:32 GMT",
+ "content-type": "application/json; charset=utf-8",
+ "content-length": "210",
+ "connection": "keep-alive",
+ "keep-alive": "timeout=15",
+ "x-ratelimit-limit": "700",
+ "x-ratelimit-remaining": "240",
+ "x-ratelimit-reset": "1481514632",
+ "server": "Plack::Handler::Starlet",
+ "etag": "\"a56ef76b83ccc2d2620ac3f66c7ead50\"",
+ "access-control-allow-origin": "*"
+ },
+ "url": "http://musicbrainz.org:80/ws/2/collection/85da782d-2ec0-41ec-a97f-9be464bba309?fmt=json",
+ "time": 404,
+ "request": {
+ "method": "GET",
+ "headers": {
+ "User-Agent": "graphbrainz/4.2.0 ( https://github.com/exogen/graphbrainz )",
+ "host": "musicbrainz.org",
+ "accept-encoding": "gzip, deflate",
+ "accept": "application/json"
+ }
+ }
+}
\ No newline at end of file
diff --git a/test/fixtures/6f51f7057a8ad55969f83563724a58d9 b/test/fixtures/6f51f7057a8ad55969f83563724a58d9
new file mode 100644
index 0000000..db45e08
Binary files /dev/null and b/test/fixtures/6f51f7057a8ad55969f83563724a58d9 differ
diff --git a/test/fixtures/6f51f7057a8ad55969f83563724a58d9.headers b/test/fixtures/6f51f7057a8ad55969f83563724a58d9.headers
new file mode 100644
index 0000000..c99bdc2
--- /dev/null
+++ b/test/fixtures/6f51f7057a8ad55969f83563724a58d9.headers
@@ -0,0 +1,29 @@
+{
+ "statusCode": 200,
+ "headers": {
+ "date": "Mon, 12 Dec 2016 02:59:37 GMT",
+ "content-type": "application/json; charset=utf-8",
+ "transfer-encoding": "chunked",
+ "connection": "keep-alive",
+ "keep-alive": "timeout=15",
+ "vary": "Accept-Encoding",
+ "x-ratelimit-limit": "700",
+ "x-ratelimit-remaining": "433",
+ "x-ratelimit-reset": "1481511578",
+ "server": "Plack::Handler::Starlet",
+ "etag": "W/\"732418d1bfab7578b6e228d91a40da59\"",
+ "access-control-allow-origin": "*",
+ "content-encoding": "gzip"
+ },
+ "url": "http://musicbrainz.org:80/ws/2/artist?collection=06535ef2-adc9-4c50-ad19-ab607d143485&fmt=json",
+ "time": 483,
+ "request": {
+ "method": "GET",
+ "headers": {
+ "User-Agent": "graphbrainz/4.2.0 ( https://github.com/exogen/graphbrainz )",
+ "host": "musicbrainz.org",
+ "accept-encoding": "gzip, deflate",
+ "accept": "application/json"
+ }
+ }
+}
\ No newline at end of file
diff --git a/test/fixtures/77bf843f88aa61a66ec61a238b892956 b/test/fixtures/77bf843f88aa61a66ec61a238b892956
new file mode 100644
index 0000000..9e60e4d
Binary files /dev/null and b/test/fixtures/77bf843f88aa61a66ec61a238b892956 differ
diff --git a/test/fixtures/77bf843f88aa61a66ec61a238b892956.headers b/test/fixtures/77bf843f88aa61a66ec61a238b892956.headers
new file mode 100644
index 0000000..9d30e81
--- /dev/null
+++ b/test/fixtures/77bf843f88aa61a66ec61a238b892956.headers
@@ -0,0 +1,29 @@
+{
+ "statusCode": 200,
+ "headers": {
+ "date": "Mon, 12 Dec 2016 03:50:33 GMT",
+ "content-type": "application/json; charset=utf-8",
+ "transfer-encoding": "chunked",
+ "connection": "keep-alive",
+ "keep-alive": "timeout=15",
+ "vary": "Accept-Encoding",
+ "x-ratelimit-limit": "700",
+ "x-ratelimit-remaining": "619",
+ "x-ratelimit-reset": "1481514634",
+ "server": "Plack::Handler::Starlet",
+ "etag": "W/\"97326a717e48b9b3aa03b6455ba690bc\"",
+ "access-control-allow-origin": "*",
+ "content-encoding": "gzip"
+ },
+ "url": "http://musicbrainz.org:80/ws/2/release?collection=85da782d-2ec0-41ec-a97f-9be464bba309&fmt=json",
+ "time": 656,
+ "request": {
+ "method": "GET",
+ "headers": {
+ "User-Agent": "graphbrainz/4.2.0 ( https://github.com/exogen/graphbrainz )",
+ "host": "musicbrainz.org",
+ "accept-encoding": "gzip, deflate",
+ "accept": "application/json"
+ }
+ }
+}
\ No newline at end of file
diff --git a/test/fixtures/83ae719cb34d0210568c345deed9cfff b/test/fixtures/83ae719cb34d0210568c345deed9cfff
new file mode 100644
index 0000000..7c3f685
Binary files /dev/null and b/test/fixtures/83ae719cb34d0210568c345deed9cfff differ
diff --git a/test/fixtures/83ae719cb34d0210568c345deed9cfff.headers b/test/fixtures/83ae719cb34d0210568c345deed9cfff.headers
new file mode 100644
index 0000000..1fc707c
--- /dev/null
+++ b/test/fixtures/83ae719cb34d0210568c345deed9cfff.headers
@@ -0,0 +1,29 @@
+{
+ "statusCode": 200,
+ "headers": {
+ "date": "Mon, 12 Dec 2016 02:59:36 GMT",
+ "content-type": "application/json; charset=utf-8",
+ "transfer-encoding": "chunked",
+ "connection": "keep-alive",
+ "keep-alive": "timeout=15",
+ "vary": "Accept-Encoding",
+ "x-ratelimit-limit": "700",
+ "x-ratelimit-remaining": "547",
+ "x-ratelimit-reset": "1481511578",
+ "server": "Plack::Handler::Starlet",
+ "etag": "W/\"c9830e30ce33960f506234d5af63ad79\"",
+ "access-control-allow-origin": "*",
+ "content-encoding": "gzip"
+ },
+ "url": "http://musicbrainz.org:80/ws/2/collection?artist=24f1766e-9635-4d58-a4d4-9413f9f98a4c&fmt=json",
+ "time": 516,
+ "request": {
+ "method": "GET",
+ "headers": {
+ "User-Agent": "graphbrainz/4.2.0 ( https://github.com/exogen/graphbrainz )",
+ "host": "musicbrainz.org",
+ "accept-encoding": "gzip, deflate",
+ "accept": "application/json"
+ }
+ }
+}
\ No newline at end of file
diff --git a/test/fixtures/b8876a6891538843bd4c95e0fc301d39 b/test/fixtures/b8876a6891538843bd4c95e0fc301d39
new file mode 100644
index 0000000..57d0d09
Binary files /dev/null and b/test/fixtures/b8876a6891538843bd4c95e0fc301d39 differ
diff --git a/test/fixtures/b8876a6891538843bd4c95e0fc301d39.headers b/test/fixtures/b8876a6891538843bd4c95e0fc301d39.headers
new file mode 100644
index 0000000..508c274
--- /dev/null
+++ b/test/fixtures/b8876a6891538843bd4c95e0fc301d39.headers
@@ -0,0 +1,29 @@
+{
+ "statusCode": 200,
+ "headers": {
+ "date": "Mon, 12 Dec 2016 02:59:37 GMT",
+ "content-type": "application/json; charset=utf-8",
+ "transfer-encoding": "chunked",
+ "connection": "keep-alive",
+ "keep-alive": "timeout=15",
+ "vary": "Accept-Encoding",
+ "x-ratelimit-limit": "700",
+ "x-ratelimit-remaining": "435",
+ "x-ratelimit-reset": "1481511578",
+ "server": "Plack::Handler::Starlet",
+ "etag": "W/\"ab16e83e578aff3fc917a4a296e76557\"",
+ "access-control-allow-origin": "*",
+ "content-encoding": "gzip"
+ },
+ "url": "http://musicbrainz.org:80/ws/2/artist?collection=974fcec4-eca0-4bfe-9a3a-e61aa93c186b&fmt=json",
+ "time": 579,
+ "request": {
+ "method": "GET",
+ "headers": {
+ "User-Agent": "graphbrainz/4.2.0 ( https://github.com/exogen/graphbrainz )",
+ "host": "musicbrainz.org",
+ "accept-encoding": "gzip, deflate",
+ "accept": "application/json"
+ }
+ }
+}
\ No newline at end of file
diff --git a/test/schema.js b/test/schema.js
index 8fd3f0c..27ed87f 100644
--- a/test/schema.js
+++ b/test/schema.js
@@ -83,7 +83,7 @@ test('schema has a search query', testData, `
test('schema has a browse query', testData, `
{
browse {
- releaseGroups (artist: "c8da2e40-bd28-4d4e-813a-bd2f51958ba8") {
+ releaseGroups(artist: "c8da2e40-bd28-4d4e-813a-bd2f51958ba8") {
totalCount
edges {
node {
@@ -592,3 +592,62 @@ test('Recordings have a length in milliseconds', testData, `
const { recording } = data.lookup
t.is(recording.length, 383493)
})
+
+test('Collections can be browsed by the entities they contain', testData, `
+ {
+ browse {
+ collections(artist: "24f1766e-9635-4d58-a4d4-9413f9f98a4c") {
+ totalCount
+ edges {
+ node {
+ name
+ editor
+ entityType
+ type
+ artists {
+ totalCount
+ edges {
+ node {
+ mbid
+ name
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+`, (t, data) => {
+ const collections = data.browse.collections.edges.map(edge => edge.node)
+ t.true(collections.length >= 2)
+ t.true(collections.some(collection => collection.editor === 'arist.on'))
+ t.true(collections.some(collection => collection.editor === 'ListMyCDs.com'))
+ collections.forEach(collection => {
+ t.is(collection.entityType, 'artist')
+ t.is(collection.type, 'Artist')
+ t.true(collection.artists.totalCount > 0)
+ t.true(collection.artists.edges.length > 0)
+ })
+})
+
+test('Collections can be looked up by MBID', testData, `
+ {
+ lookup {
+ collection(mbid: "85da782d-2ec0-41ec-a97f-9be464bba309") {
+ name
+ releases {
+ edges {
+ node {
+ title
+ }
+ }
+ }
+ }
+ }
+ }
+`, (t, data) => {
+ const { collection } = data.lookup
+ t.is(collection.name, 'Beets Music Collection')
+ t.is(collection.releases.edges.length, 25)
+})
|