Add support for more MusicBrainz features, improve test coverage

This commit is contained in:
Brian Beck 2016-12-09 18:55:41 -08:00
parent b6f2e2d3f7
commit 325c6db0ce
65 changed files with 973 additions and 437 deletions

View file

@ -1,7 +1,7 @@
{
"presets": ["es2015", "stage-2"],
"plugins": ["transform-runtime"],
"ignore": "test/**",
"only": ["scripts/**", "src/**", "test/helpers/**"],
"env": {
"test": {
"plugins": ["istanbul"]

View file

@ -34,8 +34,8 @@
"start:dev": "nodemon --exec babel-node src/index.js",
"test": "npm run lint && npm run test:coverage",
"test:coverage": "cross-env NODE_ENV=test nyc npm run test:only",
"test:only": "cross-env VCR_MODE=cache ava",
"test:record": "rimraf test/fixtures && npm run test:only -- --serial",
"test:only": "cross-env VCR_MODE=playback ava",
"test:record": "cross-env VCR_MODE=record ava",
"test:watch": "npm run test:only -- --watch",
"update-schema": "npm run -s print-schema:json > schema.json"
},
@ -64,7 +64,6 @@
"license": "MIT",
"dependencies": {
"babel-runtime": "^6.18.0",
"chalk": "^1.1.3",
"compression": "^1.6.2",
"dashify": "^0.2.2",
"dataloader": "^1.2.0",

View file

@ -1,14 +1,12 @@
import { graphql, introspectionQuery, printSchema } from 'graphql'
import schema from '../src/schema'
if (require.main === module) {
if (process.argv[2] === '--json') {
graphql(schema, introspectionQuery).then(result => {
console.log(JSON.stringify(result, null, 2))
}).catch(err => {
console.error(err)
})
} else {
console.log(printSchema(schema))
}
if (process.argv[2] === '--json') {
graphql(schema, introspectionQuery).then(result => {
console.log(JSON.stringify(result, null, 2))
}).catch(err => {
console.error(err)
})
} else {
console.log(printSchema(schema))
}

View file

@ -1,7 +1,6 @@
import request from 'request'
import retry from 'retry'
import qs from 'qs'
import chalk from 'chalk'
import ExtendableError from 'es6-error'
import RateLimit from './rate-limit'
import pkg from '../package.json'
@ -164,6 +163,9 @@ export default class MusicBrainz {
}
getLookupURL (entity, id, params) {
if (id == null) {
return this.getBrowseURL(entity, params)
}
return this.getURL(`${entity}/${id}`, params)
}
@ -190,19 +192,3 @@ export default class MusicBrainz {
return this.get(url)
}
}
if (require.main === module) {
const client = new MusicBrainz()
const fn = (id) => {
return client.lookup('artist', id).then(artist => {
console.log(chalk.green(`Done: ${id}${artist.name}`))
}).catch(err => {
console.log(chalk.red(`Error: ${id}${err}`))
})
}
fn('f1106b17-dcbb-45f6-b938-199ccfab50cc')
fn('a74b1b7f-71a5-4011-9441-d0b5e4122711')
fn('9b5ae4cc-15ae-4f0b-8a4e-8c44e42ba52a')
fn('26f77379-968b-4435-b486-fc9acb4590d3')
fn('8538e728-ca0b-4321-b7e5-cff6565dd4c0')
}

View file

@ -3,7 +3,6 @@ import { forwardConnectionArgs } from 'graphql-relay'
import { resolveBrowse } from '../resolvers'
import {
MBID,
URLString,
AreaConnection,
ArtistConnection,
EventConnection,
@ -12,10 +11,9 @@ import {
RecordingConnection,
ReleaseConnection,
ReleaseGroupConnection,
URLConnection,
WorkConnection
} from '../types'
import { toWords } from '../types/helpers'
import { toWords, releaseGroupType, releaseStatus } from '../types/helpers'
const area = {
type: MBID,
@ -115,22 +113,19 @@ entity.`,
release, but is not included in the credits for the release itself.`
},
recording,
releaseGroup
releaseGroup,
type: releaseGroupType,
status: releaseStatus
}),
releaseGroups: createBrowseField(ReleaseGroupConnection, {
artist,
collection,
release
release,
type: releaseGroupType
}),
works: createBrowseField(WorkConnection, {
artist,
collection
}),
urls: createBrowseField(URLConnection, {
resource: {
type: URLString,
description: 'The web address for which to browse URL entities.'
}
})
}
})

View file

@ -7,21 +7,23 @@ import {
Event,
Instrument,
Label,
MBID,
Place,
Recording,
Release,
ReleaseGroup,
Series,
URL,
URLString,
Work
} from '../types'
function createLookupField (entity) {
function createLookupField (entity, args) {
const typeName = toWords(entity.name)
return {
type: entity,
description: `Look up a specific ${typeName} by its MBID.`,
args: { mbid },
args: { mbid, ...args },
resolve: resolveLookup
}
}
@ -40,7 +42,18 @@ export const LookupQuery = new GraphQLObjectType({
release: createLookupField(Release),
releaseGroup: createLookupField(ReleaseGroup),
series: createLookupField(Series),
url: createLookupField(URL),
url: createLookupField(URL, {
mbid: {
...mbid,
// Remove the non-null requirement that is usually on the `mbid` field
// so that URLs can be looked up by `resource`.
type: MBID
},
resource: {
type: URLString,
description: 'The web address of the URL entity to look up.'
}
}),
work: createLookupField(Work)
}
})

View file

@ -17,7 +17,6 @@ export default class RateLimit {
this.periodCapacity = this.limit
this.timer = null
this.pendingFlush = false
this.paused = false
this.prevTaskID = null
}
@ -27,20 +26,6 @@ export default class RateLimit {
return id
}
pause () {
clearTimeout(this.timer)
this.paused = true
}
unpause () {
this.paused = false
this.flush()
}
clear () {
this.queues.length = 0
}
enqueue (fn, args, priority = this.defaultPriority) {
priority = Math.max(0, priority)
return new Promise((resolve, reject) => {
@ -76,9 +61,6 @@ export default class RateLimit {
}
flush () {
if (this.paused) {
return
}
if (this.numPending < this.concurrency && this.periodCapacity > 0) {
const task = this.dequeue()
if (task) {
@ -118,37 +100,3 @@ export default class RateLimit {
}
}
}
if (require.main === module) {
const t0 = Date.now()
const logTime = (...args) => {
const t = Date.now()
console.log(`[t=${t - t0}]`, ...args)
}
const limiter = new RateLimit({
limit: 3,
period: 3000,
concurrency: 5
})
const fn = (i) => {
return new Promise((resolve) => {
setTimeout(() => {
logTime(`Finished task ${i}`)
resolve(i)
}, 7000)
})
}
limiter.enqueue(fn, [1])
limiter.enqueue(fn, [2])
limiter.enqueue(fn, [3])
limiter.enqueue(fn, [4], 2)
limiter.enqueue(fn, [5], 10)
limiter.enqueue(fn, [6])
limiter.enqueue(fn, [7])
limiter.enqueue(fn, [8])
limiter.enqueue(fn, [9])
limiter.enqueue(fn, [10])
}

View file

@ -63,9 +63,12 @@ export function includeSubqueries (params, info, fragments = info.fragments) {
return params
}
export function resolveLookup (root, { mbid }, { loaders }, info) {
export function resolveLookup (root, { mbid, ...params }, { loaders }, info) {
if (!mbid && !params.resource) {
throw new Error('Lookups by a field other than MBID must provide: resource')
}
const entityType = toDashed(info.fieldName)
let params = includeSubqueries({}, info)
params = includeSubqueries(params, info)
params = includeRelationships(params, info)
return loaders.lookup.load([entityType, mbid, params])
}

View file

@ -1,8 +1,8 @@
import {
GraphQLObjectType,
GraphQLString,
GraphQLBoolean
} from 'graphql/type'
import { Locale } from './scalars'
import { name, sortName, fieldWithID } from './helpers'
export default new GraphQLObjectType({
@ -17,7 +17,7 @@ entity will be given as a result even if the actual name wouldnt be.`,
},
sortName,
locale: {
type: GraphQLString,
type: Locale,
description: `The locale (language and/or country) in which the alias is
used.`
},

View file

@ -202,6 +202,16 @@ export const artistCredit = {
resolve: createSubqueryResolver()
}
export const releaseGroupType = {
type: new GraphQLList(ReleaseGroupType),
description: 'Filter by one or more release group types.'
}
export const releaseStatus = {
type: new GraphQLList(ReleaseStatus),
description: 'Filter by one or more release statuses.'
}
export const artists = linkedQuery(ArtistConnection)
export const events = linkedQuery(EventConnection)
export const labels = linkedQuery(LabelConnection)
@ -209,22 +219,13 @@ export const places = linkedQuery(PlaceConnection)
export const recordings = linkedQuery(RecordingConnection)
export const releases = linkedQuery(ReleaseConnection, {
args: {
type: {
type: new GraphQLList(ReleaseGroupType),
description: 'Filter by one or more release group types.'
},
status: {
type: new GraphQLList(ReleaseStatus),
description: 'Filter by one or more release statuses.'
}
type: releaseGroupType,
status: releaseStatus
}
})
export const releaseGroups = linkedQuery(ReleaseGroupConnection, {
args: {
type: {
type: new GraphQLList(ReleaseGroupType),
description: 'Filter by one or more release group types.'
}
type: releaseGroupType
}
})
export const tags = linkedQuery(TagConnection, {

View file

@ -1,7 +1,7 @@
import { GraphQLObjectType, GraphQLString, GraphQLList } from 'graphql/type'
import Node from './node'
import Entity from './entity'
import { DateType } from './scalars'
import { ASIN, DateType } from './scalars'
import { ReleaseStatus } from './enums'
import ReleaseEvent from './release-event'
import {
@ -52,6 +52,11 @@ distribution mechanism.`
type: GraphQLString,
description: 'The country in which the release was issued.'
},
asin: {
type: ASIN,
description: `The [Amazon Standard Identification Number](https://musicbrainz.org/doc/ASIN)
of the release.`
},
barcode: {
type: GraphQLString,
description: `The [barcode](https://en.wikipedia.org/wiki/Barcode), if the

View file

@ -1,7 +1,24 @@
import { Kind } from 'graphql/language'
import { GraphQLScalarType } from 'graphql/type'
function createScalar (config) {
return new GraphQLScalarType({
serialize: value => value,
parseValue: value => value,
parseLiteral (ast) {
if (ast.kind === Kind.STRING) {
return ast.value
}
return null
},
...config
})
}
const uuid = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/
const locale = /^([a-z]{2})(_[A-Z]{2})?(\.[A-Z0-9-]+)?$/
// Be extremely lenient; just prevent major input errors.
const url = /^\w+:\/\/[\w-]+\.\w+/
function validateMBID (value) {
if (typeof value === 'string' && uuid.test(value)) {
@ -17,34 +34,38 @@ function validatePositive (value) {
throw new TypeError(`Expected positive value: ${value}`)
}
export const DateType = new GraphQLScalarType({
function validateLocale (value) {
if (typeof value === 'string' && locale.test(value)) {
return value
}
throw new TypeError(`Malformed locale: ${value}`)
}
function validateURL (value) {
if (typeof value === 'string' && url.test(value)) {
return value
}
throw new TypeError(`Malformed URL: ${value}`)
}
export const ASIN = createScalar({
name: 'ASIN',
description: `An [Amazon Standard Identification Number](https://musicbrainz.org/doc/ASIN)
(ASIN) is a 10-character alphanumeric unique identifier assigned by Amazon.com
and its partners for product identification within the Amazon organization.`
})
export const DateType = createScalar({
name: 'Date',
description:
'Year, month (optional), and day (optional) in YYYY-MM-DD format.',
serialize: value => value,
parseValue: value => value,
parseLiteral (ast) {
if (ast.kind === Kind.STRING) {
return ast.value
}
return null
}
description: 'Year, month (optional), and day (optional) in YYYY-MM-DD format.'
})
export const Degrees = new GraphQLScalarType({
export const Degrees = createScalar({
name: 'Degrees',
description: 'Decimal degrees, used for latitude and longitude.',
serialize: value => value,
parseValue: value => value,
parseLiteral (ast) {
if (ast.kind === Kind.STRING) {
return ast.value
}
return null
}
description: 'Decimal degrees, used for latitude and longitude.'
})
export const Duration = new GraphQLScalarType({
export const Duration = createScalar({
name: 'Duration',
description: 'A length of time, in milliseconds.',
serialize: validatePositive,
@ -57,91 +78,41 @@ export const Duration = new GraphQLScalarType({
}
})
export const IPI = new GraphQLScalarType({
export const IPI = createScalar({
name: 'IPI',
description: `An [IPI](https://musicbrainz.org/doc/IPI) (interested party
information) code is an identifying number assigned by the CISAC database for
musical rights management.`,
serialize: value => value,
parseValue: value => value,
parseLiteral (ast) {
if (ast.kind === Kind.STRING) {
return ast.value
}
return null
}
musical rights management.`
})
export const ISNI = new GraphQLScalarType({
export const ISNI = createScalar({
name: 'ISNI',
description: `The [International Standard Name Identifier](https://musicbrainz.org/doc/ISNI)
(ISNI) is an ISO standard for uniquely identifying the public identities of
contributors to media content.`,
serialize: value => value,
parseValue: value => value,
parseLiteral (ast) {
if (ast.kind === Kind.STRING) {
return ast.value
}
return null
}
contributors to media content.`
})
export const ISWC = new GraphQLScalarType({
export const ISWC = createScalar({
name: 'ISWC',
description: `The [International Standard Musical Work Code](https://musicbrainz.org/doc/ISWC)
(ISWC) is an ISO standard similar to ISBNs for identifying musical works /
compositions.`,
serialize: value => value,
parseValue: value => value,
parseLiteral (ast) {
if (ast.kind === Kind.STRING) {
return ast.value
}
return null
}
compositions.`
})
export const Locale = new GraphQLScalarType({
export const Locale = createScalar({
name: 'Locale',
description: 'Language code, optionally with country and encoding.',
serialize: value => value,
parseValue: value => value,
serialize: validateLocale,
parseValue: validateLocale,
parseLiteral (ast) {
if (ast.kind === Kind.STRING) {
return ast.value
return validateLocale(ast.value)
}
return null
}
})
export const Time = new GraphQLScalarType({
name: 'Time',
description: 'A time of day, in 24-hour hh:mm notation.',
serialize: value => value,
parseValue: value => value,
parseLiteral (ast) {
if (ast.kind === Kind.STRING) {
return ast.value
}
return null
}
})
export const URLString = new GraphQLScalarType({
name: 'URLString',
description: 'A web address.',
serialize: value => value,
parseValue: value => value,
parseLiteral (ast) {
if (ast.kind === Kind.STRING) {
return ast.value
}
return null
}
})
export const MBID = new GraphQLScalarType({
export const MBID = createScalar({
name: 'MBID',
description: `The MBID scalar represents MusicBrainz identifiers, which are
36-character UUIDs.`,
@ -154,3 +125,21 @@ export const MBID = new GraphQLScalarType({
return null
}
})
export const Time = createScalar({
name: 'Time',
description: 'A time of day, in 24-hour hh:mm notation.'
})
export const URLString = createScalar({
name: 'URLString',
description: 'A web address.',
serialize: validateURL,
parseValue: validateURL,
parseLiteral (ast) {
if (ast.kind === Kind.STRING) {
return validateURL(ast.value)
}
return null
}
})

View file

@ -1,15 +1,6 @@
import path from 'path'
import test from 'ava'
import sepia from 'sepia'
import MusicBrainz, { MusicBrainzError } from '../src/api'
sepia.fixtureDir(path.join(__dirname, 'fixtures'))
let client
test.before(t => {
client = new MusicBrainz()
})
import { MusicBrainzError } from '../src/api'
import client from './helpers/client'
test('getLookupURL() generates a lookup URL', t => {
t.is(client.getLookupURL('artist', 'c8da2e40-bd28-4d4e-813a-bd2f51958ba8', {
@ -37,9 +28,8 @@ test('lookup() sends a lookup query', t => {
})
test('rejects the promise when the API returns an error', t => {
t.throws(
client.lookup('artist', '5b11f4ce-a62d-471e-81fc-a69a8278c7da', {
inc: ['foobar']
}), MusicBrainzError
)
const req = client.lookup('artist', '5b11f4ce-a62d-471e-81fc-a69a8278c7da', {
inc: ['foobar']
})
return t.throws(req, MusicBrainzError)
})

View file

@ -1,26 +1,26 @@
{
"statusCode": 200,
"headers": {
"date": "Thu, 08 Dec 2016 22:32:00 GMT",
"date": "Fri, 09 Dec 2016 21:53:05 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": "303",
"x-ratelimit-reset": "1481236321",
"x-ratelimit-remaining": "365",
"x-ratelimit-reset": "1481320386",
"last-modified": "Wed, 09 Nov 2016 23:43:24 GMT",
"server": "Jetty(9.3.10.v20160621)",
"access-control-allow-origin": "*",
"content-encoding": "gzip"
},
"url": "http://musicbrainz.org:80/ws/2/recording?query=Burn%20the%20Witch&fmt=json",
"time": 596,
"time": 508,
"request": {
"method": "GET",
"headers": {
"User-Agent": "graphbrainz/3.1.0 ( https://github.com/exogen/graphbrainz )",
"User-Agent": "graphbrainz/3.1.1 ( https://github.com/exogen/graphbrainz )",
"host": "musicbrainz.org",
"accept-encoding": "gzip, deflate",
"accept": "application/json"

Binary file not shown.

View file

@ -0,0 +1,29 @@
{
"statusCode": 200,
"headers": {
"date": "Sat, 10 Dec 2016 02:52:35 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": "352",
"x-ratelimit-reset": "1481338356",
"server": "Plack::Handler::Starlet",
"etag": "W/\"6e4fd0ce8e71cdb68d2f23d878327605\"",
"access-control-allow-origin": "*",
"content-encoding": "gzip"
},
"url": "http://musicbrainz.org:80/ws/2/release/d5cdb7fd-c7e9-460a-9549-8a369655cc52?fmt=json",
"time": 486,
"request": {
"method": "GET",
"headers": {
"User-Agent": "graphbrainz/3.1.1 ( https://github.com/exogen/graphbrainz )",
"host": "musicbrainz.org",
"accept-encoding": "gzip, deflate",
"accept": "application/json"
}
}
}

Binary file not shown.

View file

@ -1,26 +1,26 @@
{
"statusCode": 200,
"headers": {
"date": "Thu, 08 Dec 2016 22:32:02 GMT",
"date": "Fri, 09 Dec 2016 21:53:05 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": "334",
"x-ratelimit-reset": "1481236323",
"last-modified": "Thu, 08 Dec 2016 22:03:30 GMT",
"x-ratelimit-remaining": "363",
"x-ratelimit-reset": "1481320386",
"last-modified": "Fri, 09 Dec 2016 21:37:57 GMT",
"server": "Jetty(9.3.10.v20160621)",
"access-control-allow-origin": "*",
"content-encoding": "gzip"
},
"url": "http://musicbrainz.org:80/ws/2/label?limit=1&query=Apple%20Records&fmt=json",
"time": 1539,
"time": 582,
"request": {
"method": "GET",
"headers": {
"User-Agent": "graphbrainz/3.1.0 ( https://github.com/exogen/graphbrainz )",
"User-Agent": "graphbrainz/3.1.1 ( https://github.com/exogen/graphbrainz )",
"host": "musicbrainz.org",
"accept-encoding": "gzip, deflate",
"accept": "application/json"

View file

@ -1,20 +1,20 @@
{
"statusCode": 400,
"headers": {
"date": "Fri, 09 Dec 2016 00:06:41 GMT",
"date": "Fri, 09 Dec 2016 21:53:05 GMT",
"content-type": "application/json; charset=utf-8",
"content-length": "72",
"connection": "keep-alive",
"keep-alive": "timeout=15",
"x-ratelimit-limit": "700",
"x-ratelimit-remaining": "575",
"x-ratelimit-reset": "1481242002",
"x-ratelimit-remaining": "429",
"x-ratelimit-reset": "1481320386",
"server": "Plack::Handler::Starlet",
"etag": "\"d54d2bc5c412c1bb3c02bfc91d13a900\"",
"access-control-allow-origin": "*"
},
"url": "http://musicbrainz.org:80/ws/2/artist/5b11f4ce-a62d-471e-81fc-a69a8278c7da?inc=foobar&fmt=json",
"time": 613,
"time": 396,
"request": {
"method": "GET",
"headers": {

View file

@ -0,0 +1 @@
{"resource":"http://www.nirvana.com/","id":"4347ffe2-82ec-4059-9520-6a1a3f73a304"}

View file

@ -0,0 +1,27 @@
{
"statusCode": 200,
"headers": {
"date": "Sat, 10 Dec 2016 00:43:25 GMT",
"content-type": "application/json; charset=utf-8",
"content-length": "82",
"connection": "keep-alive",
"keep-alive": "timeout=15",
"x-ratelimit-limit": "700",
"x-ratelimit-remaining": "542",
"x-ratelimit-reset": "1481330606",
"server": "Plack::Handler::Starlet",
"etag": "\"d2b2ef09f302f15cd08587ad1e02d9ef\"",
"access-control-allow-origin": "*"
},
"url": "http://musicbrainz.org:80/ws/2/url?resource=http%3A%2F%2Fwww.nirvana.com%2F&fmt=json",
"time": 486,
"request": {
"method": "GET",
"headers": {
"User-Agent": "graphbrainz/3.1.1 ( https://github.com/exogen/graphbrainz )",
"host": "musicbrainz.org",
"accept-encoding": "gzip, deflate",
"accept": "application/json"
}
}
}

Binary file not shown.

View file

@ -0,0 +1,29 @@
{
"statusCode": 200,
"headers": {
"date": "Sat, 10 Dec 2016 02:31:10 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": "381",
"x-ratelimit-reset": "1481337071",
"server": "Plack::Handler::Starlet",
"etag": "W/\"76d4b01f2f2f2713907e9d82646b801a\"",
"access-control-allow-origin": "*",
"content-encoding": "gzip"
},
"url": "http://musicbrainz.org:80/ws/2/release?artist=5b11f4ce-a62d-471e-81fc-a69a8278c7da&type=ep&status=bootleg&fmt=json",
"time": 590,
"request": {
"method": "GET",
"headers": {
"User-Agent": "graphbrainz/3.1.1 ( https://github.com/exogen/graphbrainz )",
"host": "musicbrainz.org",
"accept-encoding": "gzip, deflate",
"accept": "application/json"
}
}
}

Binary file not shown.

View file

@ -1,26 +1,26 @@
{
"statusCode": 200,
"headers": {
"date": "Thu, 08 Dec 2016 22:31:58 GMT",
"date": "Fri, 09 Dec 2016 21:53:05 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": "180",
"x-ratelimit-reset": "1481236319",
"x-ratelimit-remaining": "368",
"x-ratelimit-reset": "1481320386",
"server": "Plack::Handler::Starlet",
"etag": "W/\"15092bd7b55df20c4f8fd072b82a0f1f\"",
"etag": "W/\"05d3a423b0b8ea0f99c184f8131c7cac\"",
"access-control-allow-origin": "*",
"content-encoding": "gzip"
},
"url": "http://musicbrainz.org:80/ws/2/artist/c8da2e40-bd28-4d4e-813a-bd2f51958ba8?fmt=json",
"time": 426,
"time": 419,
"request": {
"method": "GET",
"headers": {
"User-Agent": "graphbrainz/3.1.0 ( https://github.com/exogen/graphbrainz )",
"User-Agent": "graphbrainz/3.1.1 ( https://github.com/exogen/graphbrainz )",
"host": "musicbrainz.org",
"accept-encoding": "gzip, deflate",
"accept": "application/json"

Binary file not shown.

View file

@ -0,0 +1,29 @@
{
"statusCode": 200,
"headers": {
"date": "Fri, 09 Dec 2016 21:53:05 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": "369",
"x-ratelimit-reset": "1481320386",
"server": "Plack::Handler::Starlet",
"etag": "W/\"617be9e745c8ab9b5097353b14655232\"",
"access-control-allow-origin": "*",
"content-encoding": "gzip"
},
"url": "http://musicbrainz.org:80/ws/2/artist/65314b12-0e08-43fa-ba33-baaa7b874c15?fmt=json",
"time": 416,
"request": {
"method": "GET",
"headers": {
"User-Agent": "graphbrainz/3.1.1 ( https://github.com/exogen/graphbrainz )",
"host": "musicbrainz.org",
"accept-encoding": "gzip, deflate",
"accept": "application/json"
}
}
}

Binary file not shown.

View file

@ -0,0 +1,29 @@
{
"statusCode": 200,
"headers": {
"date": "Fri, 09 Dec 2016 23:32:25 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": "352",
"x-ratelimit-reset": "1481326345",
"server": "Plack::Handler::Starlet",
"etag": "W/\"cd4a6370a1597ab7cb7ed51617049d2f\"",
"access-control-allow-origin": "*",
"content-encoding": "gzip"
},
"url": "http://musicbrainz.org:80/ws/2/area/489ce91b-6658-3307-9877-795b68554c98?fmt=json",
"time": 416,
"request": {
"method": "GET",
"headers": {
"User-Agent": "graphbrainz/3.1.1 ( https://github.com/exogen/graphbrainz )",
"host": "musicbrainz.org",
"accept-encoding": "gzip, deflate",
"accept": "application/json"
}
}
}

Binary file not shown.

View file

@ -1,26 +1,26 @@
{
"statusCode": 200,
"headers": {
"date": "Thu, 08 Dec 2016 22:32:06 GMT",
"date": "Fri, 09 Dec 2016 21:53:18 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": "289",
"x-ratelimit-reset": "1481236327",
"x-ratelimit-remaining": "225",
"x-ratelimit-reset": "1481320398",
"server": "Plack::Handler::Starlet",
"etag": "W/\"6a12c1ded7c7c8d9315107669601ed70\"",
"etag": "W/\"5626413881801f644e96284425f962ef\"",
"access-control-allow-origin": "*",
"content-encoding": "gzip"
},
"url": "http://musicbrainz.org:80/ws/2/artist/300c4c73-33ac-4255-9d57-4e32627f5e13?inc=artist-rels&fmt=json",
"time": 556,
"time": 496,
"request": {
"method": "GET",
"headers": {
"User-Agent": "graphbrainz/3.1.0 ( https://github.com/exogen/graphbrainz )",
"User-Agent": "graphbrainz/3.1.1 ( https://github.com/exogen/graphbrainz )",
"host": "musicbrainz.org",
"accept-encoding": "gzip, deflate",
"accept": "application/json"

Binary file not shown.

View file

@ -1,26 +1,26 @@
{
"statusCode": 200,
"headers": {
"date": "Thu, 08 Dec 2016 22:32:06 GMT",
"date": "Fri, 09 Dec 2016 21:53:18 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": "287",
"x-ratelimit-reset": "1481236327",
"x-ratelimit-remaining": "228",
"x-ratelimit-reset": "1481320398",
"server": "Plack::Handler::Starlet",
"etag": "W/\"cfec247269cfa3680afeab36d412221c\"",
"etag": "W/\"079599c4dc85b7d5376d1ff936519512\"",
"access-control-allow-origin": "*",
"content-encoding": "gzip"
},
"url": "http://musicbrainz.org:80/ws/2/artist/0d4ab0f9-bbda-4ab1-ae2c-f772ffcfbea9?inc=artist-rels&fmt=json",
"time": 517,
"time": 430,
"request": {
"method": "GET",
"headers": {
"User-Agent": "graphbrainz/3.1.0 ( https://github.com/exogen/graphbrainz )",
"User-Agent": "graphbrainz/3.1.1 ( https://github.com/exogen/graphbrainz )",
"host": "musicbrainz.org",
"accept-encoding": "gzip, deflate",
"accept": "application/json"

Binary file not shown.

View file

@ -0,0 +1,29 @@
{
"statusCode": 200,
"headers": {
"date": "Fri, 09 Dec 2016 23:50:40 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": "543",
"x-ratelimit-reset": "1481327442",
"server": "Plack::Handler::Starlet",
"etag": "W/\"80d5da0c3f297592e23e0bc3f0beea82\"",
"access-control-allow-origin": "*",
"content-encoding": "gzip"
},
"url": "http://musicbrainz.org:80/ws/2/artist/f99b7d67-4e63-4678-aa66-4c6ac0f7d24a?inc=aliases&fmt=json",
"time": 539,
"request": {
"method": "GET",
"headers": {
"User-Agent": "graphbrainz/3.1.1 ( https://github.com/exogen/graphbrainz )",
"host": "musicbrainz.org",
"accept-encoding": "gzip, deflate",
"accept": "application/json"
}
}
}

View file

@ -1 +1 @@
{"primary-type":"Album","disambiguation":"","title":"Lures","secondary-types":[],"id":"e37d2740-4503-4e3f-ab6d-e622a25e964d","secondary-type-ids":[],"first-release-date":"2014-10-07","primary-type-id":"f529b476-6e62-324f-b0aa-1f3e33d313fc"}
{"primary-type":"Album","primary-type-id":"f529b476-6e62-324f-b0aa-1f3e33d313fc","title":"Lures","first-release-date":"2014-10-07","secondary-type-ids":[],"disambiguation":"","id":"e37d2740-4503-4e3f-ab6d-e622a25e964d","secondary-types":[]}

View file

@ -1,24 +1,24 @@
{
"statusCode": 200,
"headers": {
"date": "Thu, 08 Dec 2016 22:32:00 GMT",
"date": "Fri, 09 Dec 2016 21:53:05 GMT",
"content-type": "application/json; charset=utf-8",
"content-length": "240",
"connection": "keep-alive",
"keep-alive": "timeout=15",
"x-ratelimit-limit": "700",
"x-ratelimit-remaining": "504",
"x-ratelimit-reset": "1481236321",
"x-ratelimit-remaining": "372",
"x-ratelimit-reset": "1481320386",
"server": "Plack::Handler::Starlet",
"etag": "\"89bacb0f43473ab326be8f6ba1f42afd\"",
"etag": "\"3dba57e694e0ee8ca9730d90a1601bfb\"",
"access-control-allow-origin": "*"
},
"url": "http://musicbrainz.org:80/ws/2/release-group/e37d2740-4503-4e3f-ab6d-e622a25e964d?fmt=json",
"time": 588,
"time": 424,
"request": {
"method": "GET",
"headers": {
"User-Agent": "graphbrainz/3.1.0 ( https://github.com/exogen/graphbrainz )",
"User-Agent": "graphbrainz/3.1.1 ( https://github.com/exogen/graphbrainz )",
"host": "musicbrainz.org",
"accept-encoding": "gzip, deflate",
"accept": "application/json"

View file

@ -0,0 +1 @@
{"id":"ef7d0814-da6a-32f5-a600-ff81cffd1aed","language":"mul","type":"Song","title":"Song of the French Partisan","attributes":[],"disambiguation":"","iswcs":["T-900.755.682-3"],"type-id":"f061270a-2fd6-32f1-a641-f0f8676d14e6"}

View file

@ -0,0 +1,27 @@
{
"statusCode": 200,
"headers": {
"date": "Fri, 09 Dec 2016 23:59:46 GMT",
"content-type": "application/json; charset=utf-8",
"content-length": "227",
"connection": "keep-alive",
"keep-alive": "timeout=15",
"x-ratelimit-limit": "700",
"x-ratelimit-remaining": "399",
"x-ratelimit-reset": "1481327987",
"server": "Plack::Handler::Starlet",
"etag": "\"8cfcefbad616638c208f7d6f28321cac\"",
"access-control-allow-origin": "*"
},
"url": "http://musicbrainz.org:80/ws/2/work/ef7d0814-da6a-32f5-a600-ff81cffd1aed?fmt=json",
"time": 416,
"request": {
"method": "GET",
"headers": {
"User-Agent": "graphbrainz/3.1.1 ( https://github.com/exogen/graphbrainz )",
"host": "musicbrainz.org",
"accept-encoding": "gzip, deflate",
"accept": "application/json"
}
}
}

Binary file not shown.

View file

@ -0,0 +1,29 @@
{
"statusCode": 200,
"headers": {
"date": "Fri, 09 Dec 2016 21:53:11 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": "560",
"x-ratelimit-reset": "1481320392",
"last-modified": "Fri, 09 Dec 2016 21:01:38 GMT",
"server": "Jetty(9.3.10.v20160621)",
"access-control-allow-origin": "*",
"content-encoding": "gzip"
},
"url": "http://musicbrainz.org:80/ws/2/artist?limit=1&query=Leonard%20Cohen&fmt=json",
"time": 419,
"request": {
"method": "GET",
"headers": {
"User-Agent": "graphbrainz/3.1.1 ( https://github.com/exogen/graphbrainz )",
"host": "musicbrainz.org",
"accept-encoding": "gzip, deflate",
"accept": "application/json"
}
}
}

Binary file not shown.

View file

@ -1,26 +1,26 @@
{
"statusCode": 200,
"headers": {
"date": "Thu, 08 Dec 2016 22:32:05 GMT",
"date": "Fri, 09 Dec 2016 21:53:16 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": "697",
"x-ratelimit-reset": "1481236327",
"x-ratelimit-remaining": "104",
"x-ratelimit-reset": "1481320396",
"server": "Plack::Handler::Starlet",
"etag": "W/\"9020613941ca4d7631fab0e7d51d077f\"",
"etag": "W/\"17516f4a3648cd4259751af5341b0d1b\"",
"access-control-allow-origin": "*",
"content-encoding": "gzip"
},
"url": "http://musicbrainz.org:80/ws/2/release?label=cf7fc5cf-e011-4ef4-b511-cd0188537910&limit=1&fmt=json",
"time": 390,
"time": 420,
"request": {
"method": "GET",
"headers": {
"User-Agent": "graphbrainz/3.1.0 ( https://github.com/exogen/graphbrainz )",
"User-Agent": "graphbrainz/3.1.1 ( https://github.com/exogen/graphbrainz )",
"host": "musicbrainz.org",
"accept-encoding": "gzip, deflate",
"accept": "application/json"

Binary file not shown.

View file

@ -1,26 +1,26 @@
{
"statusCode": 200,
"headers": {
"date": "Thu, 08 Dec 2016 22:32:11 GMT",
"date": "Fri, 09 Dec 2016 21:53:22 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": "254",
"x-ratelimit-reset": "1481236331",
"x-ratelimit-remaining": "260",
"x-ratelimit-reset": "1481320402",
"server": "Plack::Handler::Starlet",
"etag": "W/\"285b663debaf6f383194dcb82b6655e1\"",
"etag": "W/\"a2a6b542d767ee02cb3e2a0ec5f79525\"",
"access-control-allow-origin": "*",
"content-encoding": "gzip"
},
"url": "http://musicbrainz.org:80/ws/2/artist/ba550d0e-adac-4864-b88b-407cab5e76af?inc=artist-rels&fmt=json",
"time": 677,
"time": 548,
"request": {
"method": "GET",
"headers": {
"User-Agent": "graphbrainz/3.1.0 ( https://github.com/exogen/graphbrainz )",
"User-Agent": "graphbrainz/3.1.1 ( https://github.com/exogen/graphbrainz )",
"host": "musicbrainz.org",
"accept-encoding": "gzip, deflate",
"accept": "application/json"

Binary file not shown.

View file

@ -0,0 +1,29 @@
{
"statusCode": 200,
"headers": {
"date": "Fri, 09 Dec 2016 21:53:11 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": "555",
"x-ratelimit-reset": "1481320392",
"server": "Plack::Handler::Starlet",
"etag": "W/\"9a9c2cdbc92df7efa25d42bd9169b9a8\"",
"access-control-allow-origin": "*",
"content-encoding": "gzip"
},
"url": "http://musicbrainz.org:80/ws/2/artist?area=3f504d54-c40c-487d-bc16-c1990eac887f&fmt=json",
"time": 1072,
"request": {
"method": "GET",
"headers": {
"User-Agent": "graphbrainz/3.1.1 ( https://github.com/exogen/graphbrainz )",
"host": "musicbrainz.org",
"accept-encoding": "gzip, deflate",
"accept": "application/json"
}
}
}

Binary file not shown.

View file

@ -1,26 +1,26 @@
{
"statusCode": 200,
"headers": {
"date": "Thu, 08 Dec 2016 22:32:01 GMT",
"date": "Fri, 09 Dec 2016 21:53:11 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": "147",
"x-ratelimit-reset": "1481236321",
"x-ratelimit-remaining": "559",
"x-ratelimit-reset": "1481320392",
"server": "Plack::Handler::Starlet",
"etag": "W/\"2dd0bb83c0e5e4f352ffbf3a1272050d\"",
"etag": "W/\"d4f25526cc965da41321e414529517ef\"",
"access-control-allow-origin": "*",
"content-encoding": "gzip"
},
"url": "http://musicbrainz.org:80/ws/2/release-group?artist=c8da2e40-bd28-4d4e-813a-bd2f51958ba8&inc=artist-credits&fmt=json",
"time": 439,
"time": 416,
"request": {
"method": "GET",
"headers": {
"User-Agent": "graphbrainz/3.1.0 ( https://github.com/exogen/graphbrainz )",
"User-Agent": "graphbrainz/3.1.1 ( https://github.com/exogen/graphbrainz )",
"host": "musicbrainz.org",
"accept-encoding": "gzip, deflate",
"accept": "application/json"

Binary file not shown.

View file

@ -1,26 +1,26 @@
{
"statusCode": 200,
"headers": {
"date": "Thu, 08 Dec 2016 22:32:10 GMT",
"date": "Fri, 09 Dec 2016 21:53:22 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": "255",
"x-ratelimit-reset": "1481236331",
"x-ratelimit-remaining": "263",
"x-ratelimit-reset": "1481320402",
"server": "Plack::Handler::Starlet",
"etag": "W/\"dbec5286f8302667b6edff5be3352490\"",
"etag": "W/\"6594e99cea0327cd0491b879ebeb6baf\"",
"access-control-allow-origin": "*",
"content-encoding": "gzip"
},
"url": "http://musicbrainz.org:80/ws/2/artist/49a51491-650e-44b3-8085-2f07ac2986dd?inc=artist-rels&fmt=json",
"time": 672,
"time": 419,
"request": {
"method": "GET",
"headers": {
"User-Agent": "graphbrainz/3.1.0 ( https://github.com/exogen/graphbrainz )",
"User-Agent": "graphbrainz/3.1.1 ( https://github.com/exogen/graphbrainz )",
"host": "musicbrainz.org",
"accept-encoding": "gzip, deflate",
"accept": "application/json"

Binary file not shown.

View file

@ -1,26 +1,26 @@
{
"statusCode": 200,
"headers": {
"date": "Thu, 08 Dec 2016 22:32:06 GMT",
"date": "Fri, 09 Dec 2016 21:53:17 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": "570",
"x-ratelimit-reset": "1481236327",
"x-ratelimit-remaining": "607",
"x-ratelimit-reset": "1481320398",
"server": "Plack::Handler::Starlet",
"etag": "W/\"00e70096bc0434f732f47e3050094637\"",
"etag": "W/\"86e889bbf99d0bd3b567fa8888299b53\"",
"access-control-allow-origin": "*",
"content-encoding": "gzip"
},
"url": "http://musicbrainz.org:80/ws/2/artist?release=2ac3cbf2-f0d0-3678-af5f-b62dcb051bc0&inc=artist-rels&fmt=json",
"time": 841,
"time": 1132,
"request": {
"method": "GET",
"headers": {
"User-Agent": "graphbrainz/3.1.0 ( https://github.com/exogen/graphbrainz )",
"User-Agent": "graphbrainz/3.1.1 ( https://github.com/exogen/graphbrainz )",
"host": "musicbrainz.org",
"accept-encoding": "gzip, deflate",
"accept": "application/json"

Binary file not shown.

View file

@ -1,26 +1,26 @@
{
"statusCode": 200,
"headers": {
"date": "Thu, 08 Dec 2016 22:32:11 GMT",
"date": "Fri, 09 Dec 2016 21:53:22 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": "253",
"x-ratelimit-reset": "1481236331",
"x-ratelimit-remaining": "258",
"x-ratelimit-reset": "1481320402",
"server": "Plack::Handler::Starlet",
"etag": "W/\"262816d2e746629059388bf96eb1241d\"",
"etag": "W/\"8b0d167a80f1f60f6b0ccdb1254ea172\"",
"access-control-allow-origin": "*",
"content-encoding": "gzip"
},
"url": "http://musicbrainz.org:80/ws/2/artist/4d5447d7-c61c-4120-ba1b-d7f471d385b9?inc=artist-rels&fmt=json",
"time": 676,
"time": 508,
"request": {
"method": "GET",
"headers": {
"User-Agent": "graphbrainz/3.1.0 ( https://github.com/exogen/graphbrainz )",
"User-Agent": "graphbrainz/3.1.1 ( https://github.com/exogen/graphbrainz )",
"host": "musicbrainz.org",
"accept-encoding": "gzip, deflate",
"accept": "application/json"

Binary file not shown.

View file

@ -1,26 +1,26 @@
{
"statusCode": 200,
"headers": {
"date": "Thu, 08 Dec 2016 22:32:06 GMT",
"date": "Fri, 09 Dec 2016 21:53:18 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": "288",
"x-ratelimit-reset": "1481236327",
"x-ratelimit-remaining": "220",
"x-ratelimit-reset": "1481320398",
"server": "Plack::Handler::Starlet",
"etag": "W/\"7c937ac3dcff44777fa9d4c32d65b803\"",
"etag": "W/\"5da167e635ba53580f8c82e6958f2ec9\"",
"access-control-allow-origin": "*",
"content-encoding": "gzip"
},
"url": "http://musicbrainz.org:80/ws/2/artist/42a8f507-8412-4611-854f-926571049fa0?inc=artist-rels&fmt=json",
"time": 558,
"time": 488,
"request": {
"method": "GET",
"headers": {
"User-Agent": "graphbrainz/3.1.0 ( https://github.com/exogen/graphbrainz )",
"User-Agent": "graphbrainz/3.1.1 ( https://github.com/exogen/graphbrainz )",
"host": "musicbrainz.org",
"accept-encoding": "gzip, deflate",
"accept": "application/json"

Binary file not shown.

View file

@ -0,0 +1,29 @@
{
"statusCode": 200,
"headers": {
"date": "Fri, 09 Dec 2016 22:08:27 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": "287",
"x-ratelimit-reset": "1481321304",
"server": "Plack::Handler::Starlet",
"etag": "W/\"4466cd809101f3b9c834437d95f317ba\"",
"access-control-allow-origin": "*",
"content-encoding": "gzip"
},
"url": "http://musicbrainz.org:80/ws/2/artist/65314b12-0e08-43fa-ba33-baaa7b874c15?inc=artist-rels%2Brecording-rels%2Brelease-rels&fmt=json",
"time": 3639,
"request": {
"method": "GET",
"headers": {
"User-Agent": "graphbrainz/3.1.1 ( https://github.com/exogen/graphbrainz )",
"host": "musicbrainz.org",
"accept-encoding": "gzip, deflate",
"accept": "application/json"
}
}
}

Binary file not shown.

View file

@ -0,0 +1,29 @@
{
"statusCode": 200,
"headers": {
"date": "Sat, 10 Dec 2016 02:24:17 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": "472",
"x-ratelimit-reset": "1481336658",
"server": "Plack::Handler::Starlet",
"etag": "W/\"b99b4284b3ed400d70ad6d104e56cbb2\"",
"access-control-allow-origin": "*",
"content-encoding": "gzip"
},
"url": "http://musicbrainz.org:80/ws/2/release-group?artist=5b11f4ce-a62d-471e-81fc-a69a8278c7da&type=ep&fmt=json",
"time": 463,
"request": {
"method": "GET",
"headers": {
"User-Agent": "graphbrainz/3.1.1 ( https://github.com/exogen/graphbrainz )",
"host": "musicbrainz.org",
"accept-encoding": "gzip, deflate",
"accept": "application/json"
}
}
}

11
test/helpers/client.js Normal file
View file

@ -0,0 +1,11 @@
import path from 'path'
import sepia from 'sepia'
import MusicBrainz from '../../src/api'
sepia.fixtureDir(path.join(__dirname, '..', 'fixtures'))
const options = /^(playback|cache)$/.test(process.env.VCR_MODE)
? { limit: Infinity, period: 0 }
: {}
export default new MusicBrainz(options)

7
test/helpers/context.js Normal file
View file

@ -0,0 +1,7 @@
import createLoaders from '../../src/loaders'
import client from './client'
export default {
client,
loaders: createLoaders(client)
}

View file

@ -1,25 +1,25 @@
import path from 'path'
import test from 'ava'
import sepia from 'sepia'
import { graphql } from 'graphql'
import MusicBrainz from '../src/api'
import schema from '../src/schema'
import createLoaders from '../src/loaders'
import context from './helpers/context'
sepia.fixtureDir(path.join(__dirname, 'fixtures'))
function testData (t, query, handler) {
return graphql(schema, query, null, context).then(result => {
t.is(result.errors, undefined)
return handler(t, result.data)
})
}
let client
let loaders
let context
function testError (t, query, handler) {
return graphql(schema, query, null, context).then(result => {
t.truthy(result.errors)
t.true(result.errors.length > 0)
return handler(t, result.errors)
})
}
test.before(t => {
client = new MusicBrainz()
loaders = createLoaders(client)
context = { client, loaders }
})
test('schema has a node field', t => {
const query = `
test('schema has a node field', testData,
`
{
node(id: "UmVsZWFzZUdyb3VwOmUzN2QyNzQwLTQ1MDMtNGUzZi1hYjZkLWU2MjJhMjVlOTY0ZA==") {
__typename
@ -28,21 +28,18 @@ test('schema has a node field', t => {
}
}
}
`
return graphql(schema, query, null, context).then(result => {
t.deepEqual(result, {
data: {
node: {
__typename: 'ReleaseGroup',
mbid: 'e37d2740-4503-4e3f-ab6d-e622a25e964d'
}
`, (t, data) => {
t.deepEqual(data, {
node: {
__typename: 'ReleaseGroup',
mbid: 'e37d2740-4503-4e3f-ab6d-e622a25e964d'
}
})
})
})
}
)
test('schema has a lookup query', t => {
const query = `
test('schema has a lookup query', testData,
`
{
lookup {
artist (mbid: "c8da2e40-bd28-4d4e-813a-bd2f51958ba8") {
@ -52,24 +49,20 @@ test('schema has a lookup query', t => {
}
}
}
`
return graphql(schema, query, null, context).then(result => {
t.deepEqual(result, {
data: {
lookup: {
artist: {
mbid: 'c8da2e40-bd28-4d4e-813a-bd2f51958ba8',
name: 'Lures',
type: 'Group'
}
`, (t, data) => {
t.deepEqual(data, {
lookup: {
artist: {
mbid: 'c8da2e40-bd28-4d4e-813a-bd2f51958ba8',
name: 'Lures',
type: 'Group'
}
}
})
})
})
test('schema has a search query', t => {
const query = `
test('schema has a search query', testData,
`
{
search {
recordings (query: "Burn the Witch") {
@ -84,17 +77,15 @@ test('schema has a search query', t => {
}
}
}
`
return graphql(schema, query, null, context).then(result => {
const { recordings } = result.data.search
`, (t, data) => {
const { recordings } = data.search
t.true(recordings.totalCount > 0)
t.true(recordings.edges.length === 25)
recordings.edges.forEach(edge => t.true(edge.score > 0))
})
})
test('schema has a browse query', t => {
const query = `
test('schema has a browse query', testData,
`
{
browse {
releaseGroups (artist: "c8da2e40-bd28-4d4e-813a-bd2f51958ba8") {
@ -115,17 +106,15 @@ test('schema has a browse query', t => {
}
}
}
`
return graphql(schema, query, null, context).then(result => {
const { releaseGroups } = result.data.browse
`, (t, data) => {
const { releaseGroups } = data.browse
t.true(releaseGroups.totalCount > 0)
t.true(releaseGroups.edges.length > 0)
releaseGroups.edges.forEach(edge => t.truthy(edge.node.title))
})
})
test('supports deeply nested queries', t => {
const query = `
test('supports deeply nested queries', testData,
`
query AppleRecordsMarriages {
search {
labels(query: "Apple Records", first: 1) {
@ -193,10 +182,284 @@ test('supports deeply nested queries', t => {
}
}
}
`
return graphql(schema, query, null, context).then(result => {
const { labels } = result.data.search
`, (t, data) => {
const { labels } = data.search
t.true(labels.edges.length > 0)
t.is(labels.edges[0].node.releases.edges.length, 1)
})
})
test('throws an error if given a malformed MBID', testError,
`
{
lookup {
artist(mbid: "ABC123") {
name
}
}
}
`, (t, errors) => {
const err = errors[0]
t.true(err instanceof TypeError)
t.is(err.message, 'Malformed MBID: ABC123')
})
test('Artist beginArea/endArea pulls from begin_area/end_area for lookup queries', testData,
`
{
lookup {
artist(mbid: "65314b12-0e08-43fa-ba33-baaa7b874c15") {
beginArea {
name
}
endArea {
name
}
}
}
}
`, (t, data) => {
const { artist } = data.lookup
t.is(artist.beginArea.name, 'Westmount')
t.is(artist.endArea.name, 'Los Angeles')
})
test('Artist beginArea/endArea pull from begin_area/end_area for browse queries', testData,
`
{
browse {
artists(area: "3f504d54-c40c-487d-bc16-c1990eac887f") {
edges {
node {
beginArea {
name
}
endArea {
name
}
}
}
}
}
}
`, (t, data) => {
const artists = data.browse.artists.edges.map(edge => edge.node)
t.true(artists.length > 1)
t.true(artists.some(artist => artist.beginArea))
t.true(artists.some(artist => artist.endArea))
})
test('Artist beginArea/endArea pulls from begin-area/end-area for search queries', testData,
`
{
search {
artists(query: "Leonard Cohen", first: 1) {
edges {
node {
beginArea {
name
}
endArea {
name
}
}
}
}
}
}
`, (t, data) => {
const artists = data.search.artists.edges.map(edge => edge.node)
t.true(artists.length === 1)
t.is(artists[0].beginArea.name, 'Westmount')
t.is(artists[0].endArea.name, 'Los Angeles')
})
test('relationships filter by type', testData,
`
{
lookup {
artist(mbid: "65314b12-0e08-43fa-ba33-baaa7b874c15") {
relationships {
artists(first: 5) {
edges {
node {
target {
__typename
}
targetType
}
}
}
recordings(first: 5) {
edges {
node {
target {
__typename
}
targetType
}
}
}
releases(first: 5) {
edges {
node {
target {
__typename
}
targetType
}
}
}
}
}
}
}
`, (t, data) => {
const { relationships } = data.lookup.artist
t.is(relationships.artists.edges.length, 5)
relationships.artists.edges.forEach(edge => {
t.is(edge.node.targetType, 'artist')
t.is(edge.node.target.__typename, 'Artist')
})
t.is(relationships.recordings.edges.length, 5)
relationships.recordings.edges.forEach(edge => {
t.is(edge.node.targetType, 'recording')
t.is(edge.node.target.__typename, 'Recording')
})
t.is(relationships.releases.edges.length, 5)
relationships.releases.edges.forEach(edge => {
t.is(edge.node.targetType, 'release')
t.is(edge.node.target.__typename, 'Release')
})
})
test('Area maps iso-3166-1-codes to isoCodes', testData,
`
{
lookup {
area(mbid: "489ce91b-6658-3307-9877-795b68554c98") {
name
isoCodes
}
}
}
`, (t, data) => {
t.deepEqual(data.lookup.area.isoCodes, ['US'])
})
test('Alias locales use the Locale scalar', testData,
`
{
lookup {
artist(mbid: "f99b7d67-4e63-4678-aa66-4c6ac0f7d24a") {
aliases {
name
locale
}
}
}
}
`, (t, data) => {
const { aliases } = data.lookup.artist
t.is(aliases.find(alias => alias.locale === 'en').name, 'PSY')
t.is(aliases.find(alias => alias.locale === 'ko').name, '싸이')
})
test('Work ISWCs use the ISWC scalar', testData,
`
{
lookup {
work(mbid: "ef7d0814-da6a-32f5-a600-ff81cffd1aed") {
title
iswcs
}
}
}
`, (t, data) => {
const { work } = data.lookup
t.is(work.title, 'Song of the French Partisan')
t.deepEqual(work.iswcs, ['T-900.755.682-3'])
})
test('URLs may be looked up by resource', testData,
`
{
lookup {
url(resource: "http://www.nirvana.com/") {
mbid
resource
}
}
}
`, (t, data) => {
const { url } = data.lookup
t.is(url.mbid, '4347ffe2-82ec-4059-9520-6a1a3f73a304')
t.is(url.resource, 'http://www.nirvana.com/')
})
test('throws an error if given a malformed URLString', testError,
`
{
lookup {
url(resource: "http:foo") {
mbid
resource
}
}
}
`, (t, errors) => {
const err = errors[0]
t.true(err instanceof TypeError)
t.is(err.message, 'Malformed URL: http:foo')
})
test('Release groups can be browsed by type', testData,
`
{
browse {
releaseGroups(artist: "5b11f4ce-a62d-471e-81fc-a69a8278c7da", type: EP) {
edges {
node {
primaryType
}
}
}
}
}
`, (t, data) => {
const releaseGroups = data.browse.releaseGroups.edges.map(edge => edge.node)
t.is(releaseGroups.length, 8)
releaseGroups.forEach(releaseGroup => t.is(releaseGroup.primaryType, 'EP'))
})
test('Releases can be browsed by type and status', testData,
`
{
browse {
releases(artist: "5b11f4ce-a62d-471e-81fc-a69a8278c7da", type: EP, status: BOOTLEG) {
edges {
node {
status
}
}
}
}
}
`, (t, data) => {
const releases = data.browse.releases.edges.map(edge => edge.node)
t.is(releases.length, 6)
releases.forEach(release => t.is(release.status, 'BOOTLEG'))
})
test('Releases have an ASIN field', testData,
`
{
lookup {
release(mbid: "d5cdb7fd-c7e9-460a-9549-8a369655cc52") {
asin
}
}
}
`, (t, data) => {
const { release } = data.lookup
t.is(release.asin, 'B01KN6XDS6')
})

181
yarn.lock
View file

@ -32,8 +32,8 @@ ajv-keywords@^1.0.0:
resolved "https://registry.yarnpkg.com/ajv-keywords/-/ajv-keywords-1.2.0.tgz#676c4f087bfe1e8b12dca6fda2f3c74f417b099c"
ajv@^4.7.0:
version "4.9.1"
resolved "https://registry.yarnpkg.com/ajv/-/ajv-4.9.1.tgz#08e1b0a5fddc8b844d28ca7b03510e78812ee3a0"
version "4.9.2"
resolved "https://registry.yarnpkg.com/ajv/-/ajv-4.9.2.tgz#3f7dcda95b0c34bceb2d69945117d146219f1a2c"
dependencies:
co "^4.6.0"
json-stable-stringify "^1.0.1"
@ -322,27 +322,27 @@ babel-cli@^6.18.0:
optionalDependencies:
chokidar "^1.0.0"
babel-code-frame@^6.16.0:
version "6.16.0"
resolved "https://registry.yarnpkg.com/babel-code-frame/-/babel-code-frame-6.16.0.tgz#f90e60da0862909d3ce098733b5d3987c97cb8de"
babel-code-frame@^6.16.0, babel-code-frame@^6.20.0:
version "6.20.0"
resolved "https://registry.yarnpkg.com/babel-code-frame/-/babel-code-frame-6.20.0.tgz#b968f839090f9a8bc6d41938fb96cb84f7387b26"
dependencies:
chalk "^1.1.0"
esutils "^2.0.2"
js-tokens "^2.0.0"
babel-core@^6.17.0, babel-core@^6.18.0:
version "6.18.2"
resolved "https://registry.yarnpkg.com/babel-core/-/babel-core-6.18.2.tgz#d8bb14dd6986fa4f3566a26ceda3964fa0e04e5b"
version "6.20.0"
resolved "https://registry.yarnpkg.com/babel-core/-/babel-core-6.20.0.tgz#ab0d7176d9dea434e66badadaf92237865eab1ec"
dependencies:
babel-code-frame "^6.16.0"
babel-generator "^6.18.0"
babel-code-frame "^6.20.0"
babel-generator "^6.20.0"
babel-helpers "^6.16.0"
babel-messages "^6.8.0"
babel-register "^6.18.0"
babel-runtime "^6.9.1"
babel-runtime "^6.20.0"
babel-template "^6.16.0"
babel-traverse "^6.18.0"
babel-types "^6.18.0"
babel-traverse "^6.20.0"
babel-types "^6.20.0"
babylon "^6.11.0"
convert-source-map "^1.1.0"
debug "^2.1.1"
@ -364,13 +364,13 @@ babel-eslint@^7.1.1:
babylon "^6.13.0"
lodash.pickby "^4.6.0"
babel-generator@^6.1.0, babel-generator@^6.18.0:
version "6.19.0"
resolved "https://registry.yarnpkg.com/babel-generator/-/babel-generator-6.19.0.tgz#9b2f244204777a3d6810ec127c673c87b349fac5"
babel-generator@^6.1.0, babel-generator@^6.18.0, babel-generator@^6.20.0:
version "6.20.0"
resolved "https://registry.yarnpkg.com/babel-generator/-/babel-generator-6.20.0.tgz#fee63614e0449390103b3097f3f6a118016c6766"
dependencies:
babel-messages "^6.8.0"
babel-runtime "^6.9.0"
babel-types "^6.19.0"
babel-runtime "^6.20.0"
babel-types "^6.20.0"
detect-indent "^4.0.0"
jsesc "^1.3.0"
lodash "^4.2.0"
@ -467,14 +467,14 @@ babel-helper-regex@^6.8.0:
lodash "^4.2.0"
babel-helper-remap-async-to-generator@^6.16.0, babel-helper-remap-async-to-generator@^6.16.2:
version "6.18.0"
resolved "https://registry.yarnpkg.com/babel-helper-remap-async-to-generator/-/babel-helper-remap-async-to-generator-6.18.0.tgz#336cdf3cab650bb191b02fc16a3708e7be7f9ce5"
version "6.20.3"
resolved "https://registry.yarnpkg.com/babel-helper-remap-async-to-generator/-/babel-helper-remap-async-to-generator-6.20.3.tgz#9dd3b396f13e35ef63e538098500adc24c63c4e7"
dependencies:
babel-helper-function-name "^6.18.0"
babel-runtime "^6.0.0"
babel-runtime "^6.20.0"
babel-template "^6.16.0"
babel-traverse "^6.18.0"
babel-types "^6.18.0"
babel-traverse "^6.20.0"
babel-types "^6.20.0"
babel-helper-replace-supers@^6.18.0, babel-helper-replace-supers@^6.8.0:
version "6.18.0"
@ -567,8 +567,8 @@ babel-plugin-syntax-object-rest-spread@^6.8.0:
resolved "https://registry.yarnpkg.com/babel-plugin-syntax-object-rest-spread/-/babel-plugin-syntax-object-rest-spread-6.13.0.tgz#fd6536f2bce13836ffa3a5458c4903a597bb3bf5"
babel-plugin-syntax-trailing-function-commas@^6.3.13:
version "6.13.0"
resolved "https://registry.yarnpkg.com/babel-plugin-syntax-trailing-function-commas/-/babel-plugin-syntax-trailing-function-commas-6.13.0.tgz#2b84b7d53dd744f94ff1fad7669406274b23f541"
version "6.20.0"
resolved "https://registry.yarnpkg.com/babel-plugin-syntax-trailing-function-commas/-/babel-plugin-syntax-trailing-function-commas-6.20.0.tgz#442835e19179f45b87e92d477d70b9f1f18b5c4f"
babel-plugin-transform-async-generator-functions@^6.17.0:
version "6.17.0"
@ -619,13 +619,13 @@ babel-plugin-transform-es2015-block-scoped-functions@^6.3.13:
babel-runtime "^6.0.0"
babel-plugin-transform-es2015-block-scoping@^6.18.0:
version "6.18.0"
resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-block-scoping/-/babel-plugin-transform-es2015-block-scoping-6.18.0.tgz#3bfdcfec318d46df22525cdea88f1978813653af"
version "6.20.0"
resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-block-scoping/-/babel-plugin-transform-es2015-block-scoping-6.20.0.tgz#5d8f3e83b1a1ae1064e64a9e5bb83108d8e73be3"
dependencies:
babel-runtime "^6.9.0"
babel-runtime "^6.20.0"
babel-template "^6.15.0"
babel-traverse "^6.18.0"
babel-types "^6.18.0"
babel-traverse "^6.20.0"
babel-types "^6.20.0"
lodash "^4.2.0"
babel-plugin-transform-es2015-classes@^6.18.0:
@ -784,19 +784,17 @@ babel-plugin-transform-exponentiation-operator@^6.3.13:
babel-runtime "^6.0.0"
babel-plugin-transform-object-rest-spread@^6.16.0:
version "6.19.0"
resolved "https://registry.yarnpkg.com/babel-plugin-transform-object-rest-spread/-/babel-plugin-transform-object-rest-spread-6.19.0.tgz#f6ac428ee3cb4c6aa00943ed1422ce813603b34c"
version "6.20.2"
resolved "https://registry.yarnpkg.com/babel-plugin-transform-object-rest-spread/-/babel-plugin-transform-object-rest-spread-6.20.2.tgz#e816c55bba77b14c16365d87e2ae48c8fd18fc2e"
dependencies:
babel-plugin-syntax-object-rest-spread "^6.8.0"
babel-runtime "^6.0.0"
babel-runtime "^6.20.0"
babel-plugin-transform-regenerator@^6.16.0:
version "6.16.1"
resolved "https://registry.yarnpkg.com/babel-plugin-transform-regenerator/-/babel-plugin-transform-regenerator-6.16.1.tgz#a75de6b048a14154aae14b0122756c5bed392f59"
version "6.20.0"
resolved "https://registry.yarnpkg.com/babel-plugin-transform-regenerator/-/babel-plugin-transform-regenerator-6.20.0.tgz#a546cd2aa1c9889929d5c427a31303847847ab75"
dependencies:
babel-runtime "^6.9.0"
babel-types "^6.16.0"
private "~0.1.5"
regenerator-transform "0.9.8"
babel-plugin-transform-runtime@^6.15.0:
version "6.15.0"
@ -812,12 +810,12 @@ babel-plugin-transform-strict-mode@^6.18.0:
babel-types "^6.18.0"
babel-polyfill@^6.16.0:
version "6.16.0"
resolved "https://registry.yarnpkg.com/babel-polyfill/-/babel-polyfill-6.16.0.tgz#2d45021df87e26a374b6d4d1a9c65964d17f2422"
version "6.20.0"
resolved "https://registry.yarnpkg.com/babel-polyfill/-/babel-polyfill-6.20.0.tgz#de4a371006139e20990aac0be367d398331204e7"
dependencies:
babel-runtime "^6.9.1"
babel-runtime "^6.20.0"
core-js "^2.4.0"
regenerator-runtime "^0.9.5"
regenerator-runtime "^0.10.0"
babel-preset-es2015-node4@^2.1.0:
version "2.1.0"
@ -892,12 +890,12 @@ babel-register@^6.18.0:
mkdirp "^0.5.1"
source-map-support "^0.4.2"
babel-runtime@^6.0.0, babel-runtime@^6.11.6, babel-runtime@^6.18.0, babel-runtime@^6.9.0, babel-runtime@^6.9.1:
version "6.18.0"
resolved "https://registry.yarnpkg.com/babel-runtime/-/babel-runtime-6.18.0.tgz#0f4177ffd98492ef13b9f823e9994a02584c9078"
babel-runtime@^6.0.0, babel-runtime@^6.11.6, babel-runtime@^6.18.0, babel-runtime@^6.20.0, babel-runtime@^6.9.0, babel-runtime@^6.9.1:
version "6.20.0"
resolved "https://registry.yarnpkg.com/babel-runtime/-/babel-runtime-6.20.0.tgz#87300bdcf4cd770f09bf0048c64204e17806d16f"
dependencies:
core-js "^2.4.0"
regenerator-runtime "^0.9.5"
regenerator-runtime "^0.10.0"
babel-template@^6.14.0, babel-template@^6.15.0, babel-template@^6.16.0, babel-template@^6.7.0, babel-template@^6.8.0:
version "6.16.0"
@ -909,25 +907,25 @@ babel-template@^6.14.0, babel-template@^6.15.0, babel-template@^6.16.0, babel-te
babylon "^6.11.0"
lodash "^4.2.0"
babel-traverse@^6.15.0, babel-traverse@^6.16.0, babel-traverse@^6.18.0:
version "6.19.0"
resolved "https://registry.yarnpkg.com/babel-traverse/-/babel-traverse-6.19.0.tgz#68363fb821e26247d52a519a84b2ceab8df4f55a"
babel-traverse@^6.15.0, babel-traverse@^6.16.0, babel-traverse@^6.18.0, babel-traverse@^6.20.0:
version "6.20.0"
resolved "https://registry.yarnpkg.com/babel-traverse/-/babel-traverse-6.20.0.tgz#5378d1a743e3d856e6a52289994100bbdfd9872a"
dependencies:
babel-code-frame "^6.16.0"
babel-code-frame "^6.20.0"
babel-messages "^6.8.0"
babel-runtime "^6.9.0"
babel-types "^6.19.0"
babel-runtime "^6.20.0"
babel-types "^6.20.0"
babylon "^6.11.0"
debug "^2.2.0"
globals "^9.0.0"
invariant "^2.2.0"
lodash "^4.2.0"
babel-types@^6.13.0, babel-types@^6.15.0, babel-types@^6.16.0, babel-types@^6.18.0, babel-types@^6.19.0, babel-types@^6.7.2, babel-types@^6.8.0, babel-types@^6.9.0:
version "6.19.0"
resolved "https://registry.yarnpkg.com/babel-types/-/babel-types-6.19.0.tgz#8db2972dbed01f1192a8b602ba1e1e4c516240b9"
babel-types@^6.13.0, babel-types@^6.15.0, babel-types@^6.16.0, babel-types@^6.18.0, babel-types@^6.19.0, babel-types@^6.20.0, babel-types@^6.7.2, babel-types@^6.8.0, babel-types@^6.9.0:
version "6.20.0"
resolved "https://registry.yarnpkg.com/babel-types/-/babel-types-6.20.0.tgz#3869ecb98459533b37df809886b3f7f3b08d2baa"
dependencies:
babel-runtime "^6.9.1"
babel-runtime "^6.20.0"
esutils "^2.0.2"
lodash "^4.2.0"
to-fast-properties "^1.0.1"
@ -1359,20 +1357,6 @@ cross-env@^3.1.3:
dependencies:
cross-spawn "^3.0.1"
cross-spawn-async@^2.0.0:
version "2.2.5"
resolved "https://registry.yarnpkg.com/cross-spawn-async/-/cross-spawn-async-2.2.5.tgz#845ff0c0834a3ded9d160daca6d390906bb288cc"
dependencies:
lru-cache "^4.0.0"
which "^1.2.8"
cross-spawn@2.0.x:
version "2.0.1"
resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-2.0.1.tgz#ab6fd893a099759d9b85220e3a64397de946b0f6"
dependencies:
cross-spawn-async "^2.0.0"
spawn-sync "1.0.13"
cross-spawn@^3.0.1:
version "3.0.1"
resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-3.0.1.tgz#1256037ecb9f0c5f79e3d6ef135e30770184b982"
@ -1387,6 +1371,14 @@ cross-spawn@^4, cross-spawn@^4.0.0:
lru-cache "^4.0.1"
which "^1.2.9"
cross-spawn@^5.0.1:
version "5.0.1"
resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-5.0.1.tgz#a3bbb302db2297cbea3c04edf36941f4613aa399"
dependencies:
lru-cache "^4.0.1"
shebang-command "^1.2.0"
which "^1.2.9"
cryptiles@2.x.x:
version "2.0.5"
resolved "https://registry.yarnpkg.com/cryptiles/-/cryptiles-2.0.5.tgz#3bdfecdc608147c1c67202fa291e7dca59eaa3b8"
@ -1955,7 +1947,7 @@ finalhandler@0.5.0:
find-cache-dir@^0.1.1:
version "0.1.1"
resolved "http://registry.npmjs.org/find-cache-dir/-/find-cache-dir-0.1.1.tgz#c8defae57c8a52a8a784f9e31c57c742e993a0b9"
resolved "https://registry.yarnpkg.com/find-cache-dir/-/find-cache-dir-0.1.1.tgz#c8defae57c8a52a8a784f9e31c57c742e993a0b9"
dependencies:
commondir "^1.0.1"
mkdirp "^0.5.1"
@ -2762,8 +2754,8 @@ jsx-ast-utils@^1.3.3:
object-assign "^4.1.0"
kind-of@^3.0.2:
version "3.0.4"
resolved "https://registry.yarnpkg.com/kind-of/-/kind-of-3.0.4.tgz#7b8ecf18a4e17f8269d73b501c9f232c96887a74"
version "3.1.0"
resolved "https://registry.yarnpkg.com/kind-of/-/kind-of-3.1.0.tgz#475d698a5e49ff5e53d14e3e732429dc8bf4cf47"
dependencies:
is-buffer "^1.0.2"
@ -2956,7 +2948,7 @@ lowercase-keys@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/lowercase-keys/-/lowercase-keys-1.0.0.tgz#4e3366b39e7f5457e35f1324bdf6f88d0bfc7306"
lru-cache@^4.0.0, lru-cache@^4.0.1:
lru-cache@^4.0.1:
version "4.0.2"
resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-4.0.2.tgz#1d17679c069cda5d040991a09dbc2c0db377e55e"
dependencies:
@ -3570,10 +3562,11 @@ power-assert-util-string-width@^1.1.1:
eastasianwidth "^0.1.1"
pre-commit@^1.1.3:
version "1.1.3"
resolved "https://registry.yarnpkg.com/pre-commit/-/pre-commit-1.1.3.tgz#6d5ed90740472072958c711a15f676aa2c231377"
version "1.2.0"
resolved "https://registry.yarnpkg.com/pre-commit/-/pre-commit-1.2.0.tgz#16a1a218861c778d1a1498719c9763717913d5b8"
dependencies:
cross-spawn "2.0.x"
cross-spawn "^5.0.1"
spawn-sync "^1.0.15"
which "1.2.x"
prelude-ls@~1.1.2:
@ -3602,7 +3595,7 @@ pretty-ms@^2.0.0:
parse-ms "^1.0.0"
plur "^1.0.0"
private@^0.1.6, private@~0.1.5:
private@^0.1.6:
version "0.1.6"
resolved "https://registry.yarnpkg.com/private/-/private-0.1.6.tgz#55c6a976d0f9bafb9924851350fe47b9b5fbb7c1"
@ -3771,13 +3764,21 @@ regenerate@^1.2.1:
version "1.3.2"
resolved "https://registry.yarnpkg.com/regenerate/-/regenerate-1.3.2.tgz#d1941c67bad437e1be76433add5b385f95b19260"
regenerator-runtime@^0.9.5:
version "0.9.6"
resolved "https://registry.yarnpkg.com/regenerator-runtime/-/regenerator-runtime-0.9.6.tgz#d33eb95d0d2001a4be39659707c51b0cb71ce029"
regenerator-runtime@^0.10.0:
version "0.10.1"
resolved "https://registry.yarnpkg.com/regenerator-runtime/-/regenerator-runtime-0.10.1.tgz#257f41961ce44558b18f7814af48c17559f9faeb"
regenerator-transform@0.9.8:
version "0.9.8"
resolved "https://registry.yarnpkg.com/regenerator-transform/-/regenerator-transform-0.9.8.tgz#0f88bb2bc03932ddb7b6b7312e68078f01026d6c"
dependencies:
babel-runtime "^6.18.0"
babel-types "^6.19.0"
private "^0.1.6"
regex-cache@^0.4.2:
version "0.4.3"
resolved "http://registry.npmjs.org/regex-cache/-/regex-cache-0.4.3.tgz#9b1a6c35d4d0dfcef5711ae651e8e9d3d7114145"
resolved "https://registry.yarnpkg.com/regex-cache/-/regex-cache-0.4.3.tgz#9b1a6c35d4d0dfcef5711ae651e8e9d3d7114145"
dependencies:
is-equal-shallow "^0.1.3"
is-primitive "^2.0.0"
@ -4049,6 +4050,16 @@ setprototypeof@1.0.2:
version "1.0.2"
resolved "https://registry.yarnpkg.com/setprototypeof/-/setprototypeof-1.0.2.tgz#81a552141ec104b88e89ce383103ad5c66564d08"
shebang-command@^1.2.0:
version "1.2.0"
resolved "https://registry.yarnpkg.com/shebang-command/-/shebang-command-1.2.0.tgz#44aac65b695b03398968c39f363fee5deafdf1ea"
dependencies:
shebang-regex "^1.0.0"
shebang-regex@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/shebang-regex/-/shebang-regex-1.0.0.tgz#da42f49740c0b42db2ca9728571cb190c98efea3"
shelljs@^0.7.5:
version "0.7.5"
resolved "https://registry.yarnpkg.com/shelljs/-/shelljs-0.7.5.tgz#2eef7a50a21e1ccf37da00df767ec69e30ad0675"
@ -4121,9 +4132,9 @@ source-map@^0.5.0, source-map@^0.5.3, source-map@~0.5.1:
version "0.5.6"
resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.5.6.tgz#75ce38f52bf0733c5a7f0c118d81334a2bb5f412"
spawn-sync@1.0.13:
version "1.0.13"
resolved "https://registry.yarnpkg.com/spawn-sync/-/spawn-sync-1.0.13.tgz#904091b9ad48a0f3afb0e84752154c01e82fd8d8"
spawn-sync@^1.0.15:
version "1.0.15"
resolved "https://registry.yarnpkg.com/spawn-sync/-/spawn-sync-1.0.15.tgz#b00799557eb7fb0c8376c29d44e8a1ea67e57476"
dependencies:
concat-stream "^1.4.7"
os-shim "^0.1.2"
@ -4692,7 +4703,7 @@ which-module@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/which-module/-/which-module-1.0.0.tgz#bba63ca861948994ff307736089e3b96026c2a4f"
which@1.2.x, which@^1.2.4, which@^1.2.8, which@^1.2.9:
which@1.2.x, which@^1.2.4, which@^1.2.9:
version "1.2.12"
resolved "https://registry.yarnpkg.com/which/-/which-1.2.12.tgz#de67b5e450269f194909ef23ece4ebe416fa1192"
dependencies: