Skip to content

Commit eb029f4

Browse files
committed
Merge
2 parents f20fc92 + 50fdd11 commit eb029f4

File tree

4 files changed

+199
-0
lines changed

4 files changed

+199
-0
lines changed

.circleci/config.yml

+78
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
1+
version: 2.1
2+
orbs:
3+
codecov: codecov/[email protected]
4+
jobs:
5+
build:
6+
docker:
7+
- image: circleci/node:10.15
8+
9+
working_directory: ~/repo
10+
11+
steps:
12+
- checkout
13+
14+
- restore_cache:
15+
keys:
16+
- v1-dependencies-{{ checksum "package.json" }}
17+
- v1-dependencies-
18+
19+
- run: npm install
20+
21+
- save_cache:
22+
paths:
23+
- node_modules
24+
key: v1-dependencies-{{ checksum "package.json" }}
25+
26+
# run tests!
27+
- run: npm run test-report
28+
- run: npm run coverage-report
29+
- run: npm run codecov
30+
31+
# store test results
32+
- store_test_results:
33+
path: test_results
34+
35+
# store code coverage
36+
- store_artifacts:
37+
path: .nyc_output
38+
39+
# upload coverage to codecov
40+
- codecov/upload:
41+
file: coverage.lcov
42+
43+
- persist_to_workspace:
44+
root: ~/repo
45+
paths: .
46+
47+
deploy:
48+
docker:
49+
- image: circleci/node:latest
50+
51+
working_directory: ~/repo
52+
53+
steps:
54+
- attach_workspace:
55+
at: ~/repo
56+
- run:
57+
name: Authenticate with NPM
58+
command: echo "//registry.npmjs.org/:_authToken=$npm_TOKEN" > ~/repo/.npmrc
59+
- run:
60+
name: Publish package
61+
command: npm publish --access public
62+
63+
workflows:
64+
version: 2
65+
test-deploy-beta:
66+
jobs:
67+
- build:
68+
filters:
69+
tags:
70+
only: /.*/
71+
- deploy:
72+
requires:
73+
- build
74+
filters:
75+
branches:
76+
ignore: /.*/
77+
tags:
78+
only: /^v.*/

readme.md

+5
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,11 @@
11
# gatsby-source-gcp-storage
22
A Gatsby source plugin for sourcing data into your Gatsby application from file in a GCP Storage bucket.
33

4+
[![](https://img.shields.io/npm/v/gatsby-source-gcp-storage.svg)](https://www.npmjs.com/package/gatsby-source-gcp-storage)
5+
[![](https://img.shields.io/npm/dm/gatsby-source-gcp-storage.svg)](https://www.npmjs.com/package/gatsby-source-gcp-storage)
6+
[![CircleCI](https://circleci.com/gh/TheMagoo73/gatsby-source-gcp-storage/tree/master.svg?style=svg)](https://circleci.com/gh/TheMagoo73/gatsby-source-gcp-storage/tree/master)
7+
[![codecov](https://codecov.io/gh/TheMagoo73/gatsby-source-gcp-storage/branch/master/graph/badge.svg)](https://codecov.io/gh/TheMagoo73/gatsby-source-gcp-storage)
8+
49
The plugin creates `GCPFile` nodes from files in GCP Storage. It then uses the `gatsby-source-filesystem` to download a local copy of the files from GCP, and generate `File` nodes. The various "transformer" plugins can transform the File nodes into various other types of data e.g. `gatsby-transformer-json` transforms JSON files into JSON data nodes and `gatsby-transform-remark` transforms markdown files into `MarkdownRemark` nodes from which you can query and HTML representation of the markdown.
510

611
## Warnings!

src/gatsby-node.js

+4
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,8 @@ exports.sourceNodes = async (api, pluginOptions) => {
2626
A GCP JSON token file is required to authenticate with the GCP storage API
2727
2828
`)
29+
30+
return
2931
}
3032

3133
if(!gcpBucketName) {
@@ -36,6 +38,8 @@ exports.sourceNodes = async (api, pluginOptions) => {
3638
The GCP storage bucket name is required
3739
3840
`)
41+
42+
return
3943
}
4044

4145
const storage = new Storage({keyFilename: gcpTokenFile})

test/gatsby-node.spec.js

+112
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,112 @@
1+
const chai = require("chai")
2+
const chaiAsPromised = require("chai-as-promised")
3+
const sinonChai = require("sinon-chai")
4+
const sinon = require("sinon")
5+
6+
chai.use(sinonChai)
7+
chai.use(chaiAsPromised)
8+
chai.should()
9+
10+
const rewire = require('rewire')
11+
12+
const {Storage, Bucket, File} = require('@google-cloud/storage')
13+
const {Readable} = require('stream')
14+
const sourceNodes = rewire('../src/gatsby-node')
15+
16+
// Mock the Gatsby API
17+
const apiMock = {
18+
actions: {
19+
createNode: sinon.spy()
20+
},
21+
reporter: {
22+
panic: sinon.stub(),
23+
warn: sinon.stub(),
24+
info: sinon.stub()
25+
},
26+
createNodeId: sinon.stub(),
27+
store: sinon.stub(),
28+
cache: sinon.stub()
29+
}
30+
31+
// Mock gatsby-source-filesystem helper function
32+
const createFileNodeFromBufferStub = sinon.stub().resolves({id: '9876'})
33+
34+
// Mock the @Google Storage API
35+
class FileStub {
36+
constructor() {}
37+
38+
getMetadata() { return Promise.resolve([
39+
{
40+
mediaLink: 'https://foo.com/files/bar.md',
41+
name: 'files/bar.md',
42+
bucket: 'fooBucket',
43+
md5hash: 'ABC123'
44+
}
45+
])}
46+
createReadStream() { return Promise.resolve(new Readable())}
47+
}
48+
49+
class BucketStub {
50+
constructor() {}
51+
52+
getFiles(options) {
53+
if(options.directory !== '/')
54+
return Promise.resolve([[new FileStub()],[]])
55+
else
56+
return Promise.resolve([[],[]])
57+
}
58+
}
59+
60+
class StorageStub {
61+
constructor() {}
62+
63+
bucket() { return new BucketStub()}
64+
}
65+
66+
// Arrange stubs
67+
sourceNodes.__set__("createFileNodeFromBuffer", createFileNodeFromBufferStub)
68+
sourceNodes.__set__("Storage", StorageStub)
69+
70+
describe('gatsby-source-gcp-storage', ()=> {
71+
72+
beforeEach(() => {
73+
apiMock.reporter.panic.resetHistory()
74+
apiMock.reporter.warn.resetHistory()
75+
apiMock.actions.createNode.resetHistory()
76+
createFileNodeFromBufferStub.resetHistory()
77+
})
78+
79+
it('validates required options', async () => {
80+
81+
const params = [
82+
{gcpTokenFile: null, gcpBucketName: "bucket"},
83+
{gcpTokenFile: "token", gcpBucketName: null}
84+
]
85+
86+
for(let i=0; i< params.length; i++){
87+
apiMock.reporter.panic.resetHistory()
88+
await sourceNodes.sourceNodes(apiMock, params[i])
89+
apiMock.reporter.panic.should.be.calledOnce
90+
}
91+
})
92+
93+
it('works', async () => {
94+
await sourceNodes.sourceNodes(apiMock, {gcpTokenFile: "token", gcpBucketName: "bucket", storageDirectory: "/foo"})
95+
apiMock.reporter.panic.should.not.be.called
96+
apiMock.actions.createNode.should.be.calledOnce
97+
apiMock.actions.createNode.args[0][0].internal.type.should.deep.equals('GcpStorage')
98+
apiMock.actions.createNode.args[0][0].internal.mediaType.should.deep.equals('application/gcpfile')
99+
apiMock.actions.createNode.args[0][0].localFile___NODE.should.deep.equals('9876')
100+
})
101+
102+
it("defaults to the root directory", async () => {
103+
await sourceNodes.sourceNodes(apiMock, {gcpTokenFile: "token", gcpBucketName: "bucket"})
104+
apiMock.reporter.warn.should.be.calledOnce
105+
})
106+
107+
it('type can be renamed', async () => {
108+
await sourceNodes.sourceNodes(apiMock, {gcpTokenFile: "token", gcpBucketName: "bucket", storageDirectory: "/foo", name: "FooDocs"})
109+
apiMock.actions.createNode.args[0][0].internal.type.should.deep.equals('FooDocs')
110+
})
111+
112+
})

0 commit comments

Comments
 (0)