Skip to content

Commit

Permalink
🎉 Works pulling specific shop/brand data into GraphQL
Browse files Browse the repository at this point in the history
  • Loading branch information
whoisryosuke committed Sep 22, 2018
0 parents commit f24c58f
Show file tree
Hide file tree
Showing 6 changed files with 250 additions and 0 deletions.
8 changes: 8 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
# Project dependencies
.cache
node_modules
yarn-error.log

# Build directory
/public
.DS_Store
73 changes: 73 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
# gatsby-source-kushy

Source plugin for pulling data into Gatsby from the [Kushy API](http://kushy.net).

* Demo
* Example site source code

## Getting Started

1. `npm install --save gatsby-source-kushy`
1. Add the plugin to `gatsby-config.js`:

```json
{
resolve: 'gatsby-source-kushy',
options: {
// Access the shops or brands section?
section: 'shops',
// The slug of your shop/brand
slug: 'chronic-pain-relief-center',
},
},
```

3. `gatsby develop`
4. [Use the GraphiQL browser to query Kushy data](http://localhost:8000/___graphql?query=%7B%0A%20%20allKushyPost%7B%0A%20%20%20%20edges%20%7B%0A%09%09%09node%20%7B%0A%20%20%20%20%20%20%20%20id%0A%20%20%20%20%20%20%7D%0A%20%20%20%20%7D%0A%20%20%7D%0A%7D%0A)

## Querying with GraphQL

The plugin creates an endpoint called `KushyPost` and `allKushyPost`. You query `KushyPost` for individual posts and filter by an ID or slug. And you query `allKushyPost` for all imported posts.

All the parameters from the API are plugged into GraphQL, so if an API endpoint has a `avatar` property, you can query GraphQL for it.

Here is a sample query for a shop:

```graphql
{
allKushyPost{
edges {
node {
id
name
featured_img
avatar
location {
latitude
longitude
address
state
city
postal_code
country
}
rating
featured
verified
}
}
}
}
```

## Todo

* [] - Allow users to add a config for section and ID to load a specific shop/brand.
* [] - Add endpoint for shop/brand menu (`KushyMenu` / `allKushyMenu`)
* [] - Add endpoint for shop/brand photos (`KushyPhotos` / `allKushyPhotos`)
* [] - Add endpoint for shop/brand reviews (`KushyReviews` / `allKushyReviews`)

### Pending

* [] - Create async function to load all pages from API, currently only loads first (see: [getPages()](https://github.com/gatsbyjs/gatsby/blob/master/packages/gatsby-source-wordpress/src/fetch.js#L334-L348))
* [] - Create helper function to download all loaded media into cache (see: [downloadMediaFiles()](https://github.com/gatsbyjs/gatsby/blob/master/packages/gatsby-source-wordpress/src/normalize.js#L452))
61 changes: 61 additions & 0 deletions gatsby-node.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
const crypto = require('crypto')
const fetch = require('node-fetch')
const queryString = require('query-string')
// const helpers = require(`./helpers`)


exports.sourceNodes = ({ actions, createNodeId }, configOptions) => {
const { createNode } = actions

// Gatsby adds a configOption that's not needed for this plugin, delete it
delete configOptions.plugins

// Helper function that processes a photo to match Gatsby's node structure
const processPost = post => {
const nodeId = createNodeId(`kushy-post-${post.id}`)
console.log('post', post)
const nodeContent = JSON.stringify(post)
const nodeContentDigest = crypto
.createHash('md5')
.update(nodeContent)
.digest('hex')

const nodeData = Object.assign({}, post, {
id: nodeId,
parent: null,
children: [],
internal: {
type: `KushyPost`,
content: nodeContent,
contentDigest: nodeContentDigest,
},
})

return nodeData
}

// Convert the options object into a query string
const apiOptions = queryString.stringify(configOptions)

// Make sure user has added necessary config
// If not, give them 5 recent dispensaries
if (!configOptions.section && !configOptions.slug) {
const apiUrl = `http://localhost/api/v1/shops/`
}

// Add parameters to the Kushy API URL
const apiUrl = `http://localhost/api/v1/${configOptions.section}/?filter[slug]=${configOptions.slug}&include=categories`

return (
fetch(apiUrl)
.then(response => response.json())
.then(data => {
data.data.forEach(post => {
// Process data to create a GraphQL node matching the structure of the API
const nodeData = processPost(post)
// Use Gatsby's createNode helper to create a node from the node data
createNode(nodeData)
})
})
)
}
60 changes: 60 additions & 0 deletions helpers.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
const { createRemoteFileNode } = require(`gatsby-source-filesystem`)

// Downloads media files and removes "sizes" data as useless in Gatsby context.
exports.downloadMediaFiles = async ({
entities,
store,
cache,
createNode,
createNodeId,
touchNode,
_auth,
}) =>
Promise.all(
entities.map(async e => {
let fileNodeID
if (e.__type === `wordpress__wp_media`) {
const mediaDataCacheKey = `wordpress-media-${e.wordpress_id}`
const cacheMediaData = await cache.get(mediaDataCacheKey)

// If we have cached media data and it wasn't modified, reuse
// previously created file node to not try to redownload
if (cacheMediaData && e.modified === cacheMediaData.modified) {
fileNodeID = cacheMediaData.fileNodeID
touchNode({ nodeId: cacheMediaData.fileNodeID })
}

// If we don't have cached data, download the file
if (!fileNodeID) {
try {
const fileNode = await createRemoteFileNode({
url: e.source_url,
store,
cache,
createNode,
createNodeId,
auth: _auth,
})

if (fileNode) {
fileNodeID = fileNode.id

await cache.set(mediaDataCacheKey, {
fileNodeID,
modified: e.modified,
})
}
} catch (e) {
// Ignore
}
}
}

if (fileNodeID) {
e.localFile___NODE = fileNodeID
delete e.media_details.sizes
}

return e
})
)
32 changes: 32 additions & 0 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

16 changes: 16 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
{
"name": "gatsby-source-kushy",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"keywords": [],
"author": "",
"license": "ISC",
"dependencies": {
"node-fetch": "^2.2.0",
"query-string": "^6.1.0"
}
}

0 comments on commit f24c58f

Please sign in to comment.