Skip to content

Commit

Permalink
Merge pull request #75 from storyblok/feature/api-v2
Browse files Browse the repository at this point in the history
Feature/api v2
  • Loading branch information
onefriendaday authored Nov 24, 2020
2 parents 3babf3a + 9437423 commit 41ce262
Show file tree
Hide file tree
Showing 9 changed files with 240 additions and 35 deletions.
21 changes: 14 additions & 7 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,9 @@ This client is a thin wrapper for the Storyblok API's to use in Node.js and the

## Install

```
npm install storyblok-js-client # yarn add storyblok-js-client
```sh
# as axios is a peerDependency, you should install it too
npm install storyblok-js-client axios # yarn add storyblok-js-client axios
```

## Usage
Expand Down Expand Up @@ -46,7 +47,13 @@ Storyblok.delete(`spaces/${spaceId}/stories/1`, null)
You can import and use the `RichTextResolver` directly:

```js
import RichTextResolver from 'storyblok-js-client/dist/rich-text-resolver'
// you should need to use the format when import
// es - when you are in EsModules environment (like React, Vue apps, for example)
// cjs - when you are in NodeJS environment
// standalone - when you are in Browser environment directly

import RichTextResolver from 'storyblok-js-client/dist/rich-text-resolver.es'
// const RichTextResolver = require('storyblok-js-client/dist/rich-text-resolver.cjs')

const resolver = new RichTextResolver()

Expand All @@ -59,20 +66,20 @@ This package has a standalone version that contains all dependencies and you can

```html
<!-- This import makes the StoryblokClient class available globally -->
<script src="https://cdn.jsdelivr.net/npm/storyblok-js-client@3.0.0/dist/index.standalone.js"></script>
<script src="https://cdn.jsdelivr.net/npm/storyblok-js-client@4.0.0/dist/index.standalone.js"></script>

<!-- This import makes the RichTextResolver class available globally -->
<script src="https://cdn.jsdelivr.net/npm/storyblok-js-client@3.0.0/dist/rich-text-resolver.standalone.js"></script>
<script src="https://cdn.jsdelivr.net/npm/storyblok-js-client@4.0.0/dist/rich-text-resolver.standalone.js"></script>
```

If you want a bundle with Babel (for non-es6 browsers):

```html
<!-- This import makes the StoryblokClient class available globally -->
<script src="https://cdn.jsdelivr.net/npm/storyblok-js-client@3.0.0/dist/es5/index.standalone.js"></script>
<script src="https://cdn.jsdelivr.net/npm/storyblok-js-client@4.0.0/dist/es5/index.standalone.js"></script>

<!-- This import makes the RichTextResolver class available globally -->
<script src="https://cdn.jsdelivr.net/npm/storyblok-js-client@3.0.0/dist/es5/rich-text-resolver.standalone.js"></script>
<script src="https://cdn.jsdelivr.net/npm/storyblok-js-client@4.0.0/dist/es5/rich-text-resolver.standalone.js"></script>
```

### Note about use of Babel
Expand Down
10 changes: 10 additions & 0 deletions index.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
<!DOCTYPE html>
<html>
<head>
<title></title>
<script src="/dist/index.standalone.js"></script>
</head>
<body>

</body>
</html>
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@
"build:es6": "rollup --config",
"build:standalone:es5": "STANDALONE=yes ENABLE_BABEL=yes rollup --config",
"build:standalone:es6": "STANDALONE=yes rollup --config",
"test": "NODE_ENV='test' jest --silent",
"test": "NODE_ENV='test' jest",
"prepare": "npm run build"
},
"repository": {
Expand Down
115 changes: 104 additions & 11 deletions source/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import throttledQueue from './throttlePromise'
import RichTextResolver from './richTextResolver'

let memory = {}
let cacheVersions = {}

import { delay, getOptionsPage, isCDNUrl } from './helpers'

Expand All @@ -16,7 +17,7 @@ class Storyblok {
if (!endpoint) {
let region = config.region ? `-${config.region}` : ''
let protocol = config.https === false ? 'http' : 'https'
endpoint = `${protocol}://api${region}.storyblok.com/v1`
endpoint = `${protocol}://api${region}.storyblok.com/v2`
}

let headers = Object.assign({}, config.headers)
Expand All @@ -39,8 +40,8 @@ class Storyblok {

this.maxRetries = config.maxRetries || 5
this.throttle = throttledQueue(this.throttledRequest, rateLimit, 1000)
this.cacheVersion = (this.cacheVersion || this.newVersion())
this.accessToken = config.accessToken
this.relations = {}
this.cache = (config.cache || { clear: 'manual' })
this.client = axios.create({
baseURL: endpoint,
Expand Down Expand Up @@ -69,14 +70,14 @@ class Storyblok {
params.version = 'published'
}

if (!params.cv) {
params.cv = this.cacheVersion
}

if (!params.token) {
params.token = this.getToken()
}

if (!params.cv) {
params.cv = cacheVersions[params.token]
}

return params
}

Expand Down Expand Up @@ -159,6 +160,77 @@ class Storyblok {
return this.accessToken
}

insertRelations(story, fields) {
let enrich = (jtree) => {
if (jtree == null) {
return
}
if (jtree.constructor === Array) {
for (let item = 0; item < jtree.length; item++) {
enrich(jtree[item])
}
} else if (jtree.constructor === Object && jtree.component && jtree._uid) {
for (let treeItem in jtree) {
if (fields.indexOf(jtree.component + '.' + treeItem) > -1) {
if (typeof jtree[treeItem] === 'string') {
if (this.relations[jtree[treeItem]]) {
jtree[treeItem] = this.relations[jtree[treeItem]]
}
} else if (jtree[treeItem].constructor === Array) {
let stories = []
jtree[treeItem].forEach(function(uuid) {
if (this.relations[uuid]) {
stories.push(this.relations[uuid])
}
})
jtree[treeItem] = stories
}
}
enrich(jtree[treeItem])
}
}
}

enrich(story.content)
}

async resolveRelations(responseData, params) {
let relations = []

if (responseData.rel_uuids) {
const relSize = responseData.rel_uuids.length
let chunks = []
const chunkSize = 50

for (let i = 0; i < relSize; i += chunkSize) {
const end = Math.min(relSize, i + chunkSize)
chunks.push(responseData.rel_uuids.slice(i, end))
}

for (let chunkIndex = 0; chunkIndex < chunks.length; chunkIndex++) {
let relationsRes = await this.getStories({per_page: chunkSize, version: params.version, by_uuids: chunks[chunkIndex]})

relationsRes.data.stories.forEach((rel) => {
relations.push(rel)
})
}
} else {
relations = responseData.rels
}

relations.forEach((story) => {
this.relations[story.uuid] = story
})

if (responseData.story) {
this.insertRelations(responseData.story, params.resolve_relations.split(','))
} else {
responseData.stories.forEach((story) => {
this.insertRelations(story, params.resolve_relations.split(','))
})
}
}

cacheResponse(url, params, retries) {
if (typeof retries === 'undefined') {
retries = 0
Expand All @@ -184,6 +256,7 @@ class Storyblok {
params: params,
paramsSerializer: (params) => stringify(params, { arrayFormat: 'brackets' })
})

let response = { data: res.data, headers: res.headers }

if (res.headers['per-page']) {
Expand All @@ -197,9 +270,22 @@ class Storyblok {
return reject(res)
}

if (typeof params.resolve_relations !== 'undefined' && params.resolve_relations.length > 0) {
await this.resolveRelations(response.data, params)
}

if (params.version === 'published' && url != '/cdn/spaces/me') {
provider.set(cacheKey, response)
}

if (response.data.cv) {
cacheVersions[params.token] = response.data.cv

if (params.version == 'draft' && cacheVersions[params.token] != response.data.cv) {
this.flushCache()
}
}

resolve(response)
} catch (error) {
if (error.response && error.response.status === 429) {
Expand All @@ -220,8 +306,18 @@ class Storyblok {
return this.client[type](url, params)
}

newVersion() {
return new Date().getTime()
cacheVersions() {
return cacheVersions
}

cacheVersion() {
return cacheVersions[this.accessToken]
}

setCacheVersion(cv) {
if (this.accessToken) {
cacheVersions[this.accessToken] = cv
}
}

cacheProvider() {
Expand All @@ -242,8 +338,6 @@ class Storyblok {
}
}
default:
this.cacheVersion = this.newVersion()

return {
get() {},
getAll() {},
Expand All @@ -254,7 +348,6 @@ class Storyblok {
}

async flushCache() {
this.cacheVersion = this.newVersion()
await this.cacheProvider().flush()
return this
}
Expand Down
2 changes: 1 addition & 1 deletion tests/createTestContent.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
const StoryblokClient = require('../source/index')
import StoryblokClient from '../source/index'
const spaceId = 67647

let Storyblok = new StoryblokClient({
Expand Down
18 changes: 6 additions & 12 deletions tests/index.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -46,27 +46,21 @@ describe('test uncached requests', () => {

describe('test cached requests', () => {
test('get(\'cdn/stories\') should be cached when is a published version', async () => {
const cacheVersion = Storyblok.cacheVersion
const cacheVersion = Storyblok.cacheVersion()

await Storyblok.get('cdn/stories')

expect(cacheVersion).toBe(Storyblok.cacheVersion)
expect(cacheVersion).not.toBe(undefined)

await Storyblok.get('cdn/stories')

expect(cacheVersion).toBe(Storyblok.cacheVersion)
const newCacheVersion = Storyblok.cacheVersion()

await Storyblok.get('cdn/stories')

expect(cacheVersion).toBe(Storyblok.cacheVersion)
})
expect(newCacheVersion).toBe(Storyblok.cacheVersion())

test('get(\'cdn/stories\') should be not cached when is a draft version', async () => {
const cacheVersion = Storyblok.cacheVersion

await Storyblok.get('cdn/stories', { version: 'draft' })
await Storyblok.get('cdn/stories')

expect(cacheVersion).not.toBe(Storyblok.cacheVersion)
expect(newCacheVersion).toBe(Storyblok.cacheVersion())
})
})

Expand Down
17 changes: 17 additions & 0 deletions tests/redirect.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
jest.setTimeout(60000)

import StoryblokClient from '../source/index'

let Storyblok = new StoryblokClient({
accessToken: 'trB5kgOeDD22QJQDdPNCjAtt',
cache: { type: 'memory', clear: 'auto' }
})

describe('test cache version', () => {
test('get(\'cdn/stories\') should set the cache version', async () => {
const result = await Storyblok.get('cdn/stories')
const cacheVersion = JSON.parse(JSON.stringify(Storyblok.cacheVersions()))

expect(cacheVersion['trB5kgOeDD22QJQDdPNCjAtt']).toBe(result.data.cv)
})
})
Loading

0 comments on commit 41ce262

Please sign in to comment.