graphql-spotify
Advanced tools
Comparing version 0.4.31 to 0.4.35
{ | ||
"name": "graphql-spotify", | ||
"version": "0.4.31", | ||
"version": "0.4.35", | ||
"description": "GraphQL Schema And Resolvers For Spotify Web API", | ||
@@ -5,0 +5,0 @@ "main": "dist/lib.js", |
@@ -5,3 +5,3 @@ ## A [Spotify API](https://beta.developer.spotify.com/documentation/web-api/reference/) GraphQL Schema Implementation Built with [GraphQL.js](https://github.com/graphql/graphql-js) | ||
Refer to `src/schema/RootQuery.js` for operations currently supported. | ||
Refer to [src/schema/RootQuery.js](src/schema/RootQuery.js) for operations currently supported. | ||
## Getting started | ||
@@ -73,4 +73,4 @@ ```javascript | ||
## Contribute | ||
Anyone is welcome! Take a look at [Roadmap.md](./Roadmap.md) for PR ideas and file some issues! | ||
Anyone is welcome! Take a look at [Roadmap.md](Roadmap.md) for PR ideas and file some issues! | ||
@@ -5,2 +5,5 @@ import { | ||
const MAX_PLAYLIST_TRACKS_FETCH_LIMIT = 100 | ||
const MAX_TOP_TYPE_FETCH_LIMIT = 50 | ||
export function makeResolvers(token) { | ||
@@ -10,3 +13,3 @@ const { | ||
AudioFeaturesLoader, SavedContainsLoader, TracksLoader, CategoriesLoader, RecommendationsLoader, | ||
CategoryPlaylistLoader, CategoryLoader | ||
CategoryPlaylistLoader, CategoryLoader, TopTypeLoader | ||
} = makeLoaders(token); | ||
@@ -46,2 +49,23 @@ | ||
return await RecommendationsLoader.load(args.parameters) | ||
}, | ||
top: async (obj, args) => { | ||
const { type, limit, offset, time_range } = args | ||
const result = await TopTypeLoader.load({ type, limit, offset, time_range }) | ||
if (result.items.length >= limit) { | ||
return result | ||
} | ||
let allItems = result.items | ||
const totalFetchSize = Math.min(limit, result.total) | ||
let currentOffset = allItems.length | ||
let fetches = [] | ||
while (currentOffset < totalFetchSize) { | ||
const fetchSize = Math.min(MAX_TOP_TYPE_FETCH_LIMIT, totalFetchSize - currentOffset); | ||
fetches.push(TopTypeLoader.load({type, time_range, limit: fetchSize, offset: currentOffset })) | ||
currentOffset = currentOffset + fetchSize; | ||
} | ||
const fetchResults = await Promise.all(fetches); | ||
fetchResults.forEach((result) => { | ||
allItems = allItems.concat(result.items) | ||
}) | ||
return { total: result.total, items: allItems, limit: allItems.length, offset } | ||
} | ||
@@ -65,3 +89,3 @@ }, | ||
tracks: async (obj, args) => { | ||
const { id: playlistId , owner: { id: userId }, tracks: { total, offset, items, limit }} = obj | ||
const { id: playlistId , owner: { id: userId }, tracks: { total, offset, items }} = obj | ||
// fetch with limit and offset when specified | ||
@@ -72,3 +96,3 @@ if (args.limit && args.offset) { | ||
// otherwise always fetch all of the tracks | ||
// when resolving a full playlist there are already items | ||
// when resolving a full playlist, tracks are set the first 100 tracks | ||
let allItems = items || [] | ||
@@ -78,4 +102,5 @@ let currentOffset = allItems.length | ||
while (currentOffset < total ) { | ||
fetches.push(PlaylistTracksLoader.load({playlistId, userId, limit, offset: currentOffset })) | ||
currentOffset = currentOffset + limit; | ||
const fetchSize = Math.min(MAX_PLAYLIST_TRACKS_FETCH_LIMIT, total - currentOffset); | ||
fetches.push(PlaylistTracksLoader.load({playlistId, userId, limit: fetchSize, offset: currentOffset })) | ||
currentOffset = currentOffset + fetchSize; | ||
} | ||
@@ -91,3 +116,3 @@ const fetchResults = await Promise.all(fetches); | ||
audio_features: async ({ id }) => { | ||
return await AudioFeatureLoader.load(id) | ||
return await AudioFeaturesLoader.load(id) | ||
}, | ||
@@ -157,2 +182,8 @@ saved: async ({id}) => { | ||
} | ||
if (type === 'artist') { | ||
return 'Artist' | ||
} | ||
if (type === 'track') { | ||
return 'Track' | ||
} | ||
if (type === 'playlist') { | ||
@@ -159,0 +190,0 @@ return 'Playlist' |
@@ -12,2 +12,3 @@ const Artist = ` | ||
external_urls: ExternalUrls | ||
type: String | ||
} | ||
@@ -14,0 +15,0 @@ `; |
@@ -9,2 +9,4 @@ import Playlist from './Playlist' | ||
import Paging from './Paging' | ||
import TimeRange from './TimeRange' | ||
import TopType from './TopType' | ||
import AudioFeatures from './AudioFeatures' | ||
@@ -28,4 +30,5 @@ import PlayHistory from './PlayHistory' | ||
const typeDefs = [SchemaDefinition, RootQuery, Playlist, Image, User, PlaylistTrack, Track, Album, Artist, Paging, | ||
AudioFeatures, PlayHistory, Category, RecommendationParameters, RecommendationsResponse, RecommendationsSeedObject, ExternalUrls]; | ||
AudioFeatures, PlayHistory, Category, RecommendationParameters, RecommendationsResponse, RecommendationsSeedObject, | ||
ExternalUrls, TimeRange, TopType]; | ||
export default typeDefs |
const Paging = ` | ||
union Item = Playlist | PlaylistTrack | Category | ||
union Item = Playlist | PlaylistTrack | Category | Artist | Track | ||
type Paging { | ||
@@ -4,0 +4,0 @@ href: String |
@@ -40,3 +40,8 @@ const RootQuery = ` | ||
recommendations(parameters: RecommendationParameters): RecommendationsResponse | ||
""" | ||
Get the current user’s top artists or tracks based on calculated affinity. | ||
(https://beta.developer.spotify.com/documentation/web-api/reference/personalization/get-users-top-artists-and-tracks/) | ||
Required Scope: **user-top-read** | ||
""" | ||
top(type: TopType!, limit: Int, offset: Int, time_range: TimeRange): Paging | ||
} | ||
@@ -43,0 +48,0 @@ type Mutation { |
@@ -23,2 +23,3 @@ const Track = ` | ||
saved: Boolean | ||
type: String | ||
} | ||
@@ -25,0 +26,0 @@ ` |
@@ -13,7 +13,7 @@ import 'isomorphic-fetch' | ||
function cacheKeyFnForQueryKeys(key) { | ||
return JSON.stringify(key) | ||
return serializeToURLParameters(key) | ||
} | ||
function serializeToURLParameters(obj) { | ||
return Object.entries(obj).map(([key, val]) => `${key}=${val}`).join('&') | ||
return Object.entries(obj).map(([key, val]) => val && `${key}=${val}`).join('&') | ||
} | ||
@@ -100,2 +100,12 @@ | ||
export async function getTopType(token, params) { | ||
const { type, limit, offset, time_range } = params | ||
let res = await fetch(`https://api.spotify.com/v1/me/top/${type}?${serializeToURLParameters({ limit, offset, time_range })}`, { | ||
method: 'GET', | ||
headers: makeHeaders(token) | ||
}); | ||
res = await res.json(); | ||
return res; | ||
} | ||
export async function getPlaylist(token, userId, playlistId) | ||
@@ -257,2 +267,3 @@ { | ||
} | ||
export function makeRecommendationsLoader(token) { | ||
@@ -265,2 +276,9 @@ const batchLoadFn = async ([key]) => { | ||
export function makeGetTopTypeLoader(token) { | ||
const batchLoadFn = async ([key]) => { | ||
return [await getTopType(token, key)] | ||
} | ||
return new Dataloader(batchLoadFn, { batch: false, cacheKeyFn: cacheKeyFnForQueryKeys }) | ||
} | ||
export function makeLoaders(token) { | ||
@@ -279,4 +297,5 @@ return { | ||
CategoryPlaylistLoader: makeCategoriesPlaylistsLoader(token), | ||
CategoryLoader: makeCategoryLoader(token) | ||
CategoryLoader: makeCategoryLoader(token), | ||
TopTypeLoader: makeGetTopTypeLoader(token) | ||
} | ||
} |
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
216866
31
1882
31