105 Commits

Author SHA1 Message Date
efarooqui
fdee3fa581 Minor changes to media endpoint 2021-06-20 23:11:38 -07:00
efarooqui
f093253d87 Removing unecessary console error 2021-06-09 20:01:20 -07:00
efarooqui
011c667805 Unpacking params for request from media 2021-06-09 01:14:02 -07:00
efarooqui
66d7d42497 Adding media fetch endpoint; working query with hardcoded incident 2021-06-08 18:46:33 -07:00
Ebrahem Farooqui
71a8687003 Merge pull request #74 from forensic-architecture/feature/add-shapes-filter-panel
Adding export_shapes endpoint
2021-05-24 22:47:57 -07:00
Ebrahem Farooqui
63d168a964 changing push branch to dev 2021-05-24 22:18:28 -07:00
Ebrahem Farooqui
4d310270d5 changing push branch temp 2021-05-24 22:17:10 -07:00
efarooqui
a6cc9491c6 Adding export_shapes endpoint 2021-05-24 16:10:34 -07:00
efarooqui
0ee207dc75 Linting fixes 2021-04-30 19:34:29 -07:00
efarooqui
7e97c5c227 Minor comment to trigger job 2021-04-30 19:31:16 -07:00
efarooqui
978d572660 Minor comment to trigger job 2021-04-14 19:58:55 -07:00
efarooqui
8bd5f38ac4 Error logging for gsheet fetch 2021-04-14 18:07:48 -07:00
efarooqui
6ab37ccb70 Extraneous comment added to trigger job 2021-04-14 16:35:27 -07:00
efarooqui
4976bfd985 Merge branch 'develop' of https://www.github.com/forensic-architecture/datasheet-server into develop 2021-04-14 16:12:46 -07:00
efarooqui
d713a15879 Adding comment to trigger job 2021-04-14 16:11:40 -07:00
Ebrahem Farooqui
679407f421 adding env vars for test and lint 2021-03-01 16:05:16 -08:00
Ebrahem Farooqui
3eebf811fb Incorrect syntax for unpacking head ref 2021-02-22 09:50:46 -08:00
Ebrahem Farooqui
be03a3983a Adding branch to checkout
Adding the correct branch to checkout with for workflow
2021-02-22 09:47:05 -08:00
Lachlan Kermode
d6d565a0fc Topic/cd workflow (#66)
* scaffold cd dispatch

* fix bug

update

update

update

* repo -> runtime_args

* remove travis

* only run on commits to develop
2021-01-19 22:07:10 +01:00
efarooqui
ca104e4abe Merge branch 'develop' of https://www.github.com/forensic-architecture/datasheet-server into develop 2021-01-18 20:50:14 -08:00
Ebrahem Farooqui
c18c935b54 CI Workflow (#58)
* Create main.yml

Initial action ci file

* Linting fixes

Co-authored-by: efarooqui <efarooqui@pandora.com>
2021-01-19 15:17:32 +13:00
efarooqui
828a739d72 Merge branch 'develop' of https://www.github.com/forensic-architecture/datasheet-server into develop 2020-12-08 19:32:49 -08:00
Juan Camilo González
ac80c8f256 Feature/fix duplicate associations sample xlsx (#63)
* Release v0.5.0 (#61)

* remove explicit ID from events in timemap setup

* Fixed internal anchor link headline

* update fmt

* Bump lodash from 4.17.15 to 4.17.19

Bumps [lodash](https://github.com/lodash/lodash) from 4.17.15 to 4.17.19.
- [Release notes](https://github.com/lodash/lodash/releases)
- [Commits](https://github.com/lodash/lodash/compare/4.17.15...4.17.19)

Signed-off-by: dependabot[bot] <support@github.com>

* Wrote validation functions and getter to grab appropriate validation function

* Collapsed filters and narratives into associations; modified default tabs to reflect

* Refactoring events to have only associations; created associations tab and related export tab

* Clean up with event related associations

* Delete validation for now; not relevant to this PR

* Bump node-fetch from 2.6.0 to 2.6.1

Bumps [node-fetch](https://github.com/bitinn/node-fetch) from 2.6.0 to 2.6.1.
- [Release notes](https://github.com/bitinn/node-fetch/releases)
- [Changelog](https://github.com/node-fetch/node-fetch/blob/master/docs/CHANGELOG.md)
- [Commits](https://github.com/bitinn/node-fetch/compare/v2.6.0...v2.6.1)

Signed-off-by: dependabot[bot] <support@github.com>

* Removed gsheets tab in config, unused import in package json

* try/catch to prioritise local.config.js

* Removing export categories endpoint

* Removed categories from list and using test_export_events

* Test associations

* Removed comment regarding categories endpoint

* Removing unnecessary test_export_events endpoint

* Linting threw error stating that we use path module to join instead of +

* generalise LocalFetcher to support many sheet types (xlsx, ods, etc)

* rm getFileExt

* use tab as delimiter in intermediate representation

* Bump googleapis from 32.0.0 to 39.1.0

Bumps [googleapis](https://github.com/googleapis/google-api-nodejs-client) from 32.0.0 to 39.1.0.
- [Release notes](https://github.com/googleapis/google-api-nodejs-client/releases)
- [Changelog](https://github.com/googleapis/google-api-nodejs-client/blob/master/CHANGELOG.md)
- [Commits](https://github.com/googleapis/google-api-nodejs-client/compare/v32.0.0...v39.1.0)

Signed-off-by: dependabot[bot] <support@github.com>

* Fixed tests

* fix example sheet

Co-authored-by: Christoph Knoth <github@christoph-knoth.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: efarooqui <efarooqui@pandora.com>
Co-authored-by: Ebrahem Farooqui <ebefarooqui@gmail.com>
Co-authored-by: Lachlan <Kermode>

* Spin up demo fixes

Co-authored-by: Lachlan Kermode <lachiekermode@gmail.com>
Co-authored-by: Christoph Knoth <github@christoph-knoth.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: efarooqui <efarooqui@pandora.com>
Co-authored-by: Ebrahem Farooqui <ebefarooqui@gmail.com>
2020-12-07 11:12:26 +01:00
Lachlan Kermode
79999342d1 Merge pull request #53 from forensic-architecture/dependabot/npm_and_yarn/googleapis-39.1.0
Bump googleapis from 32.0.0 to 39.1.0
2020-11-25 13:48:54 +00:00
efarooqui
224a5e6fe4 Reverting deeprows changes 2020-11-24 14:48:55 -08:00
Lachlan Kermode
6f7a5a19ad Merge pull request #59 from forensic-architecture/fix/timemap_data
fix example sheet
2020-11-20 10:17:43 +00:00
Lachlan Kermode
386407138f fix example sheet 2020-11-20 11:15:37 +01:00
efarooqui
17607cf69f Merge branch 'develop' of https://www.github.com/forensic-architecture/datasheet-server into develop 2020-11-16 08:55:00 -08:00
efarooqui
f3f574380c Fixed tests 2020-11-16 08:54:50 -08:00
dependabot[bot]
64f7775a83 Bump googleapis from 32.0.0 to 39.1.0
Bumps [googleapis](https://github.com/googleapis/google-api-nodejs-client) from 32.0.0 to 39.1.0.
- [Release notes](https://github.com/googleapis/google-api-nodejs-client/releases)
- [Changelog](https://github.com/googleapis/google-api-nodejs-client/blob/master/CHANGELOG.md)
- [Commits](https://github.com/googleapis/google-api-nodejs-client/compare/v32.0.0...v39.1.0)

Signed-off-by: dependabot[bot] <support@github.com>
2020-11-12 19:32:05 +00:00
Lachlan Kermode
7dd9d69d1f Merge pull request #57 from forensic-architecture/topic/better-local
generalise LocalFetcher to support many sheet types (xlsx, ods, etc)
2020-11-12 19:31:16 +00:00
Lachlan
848d5b20f7 use tab as delimiter in intermediate representation 2020-11-12 13:25:39 +00:00
Lachlan
f5ada3d326 rm getFileExt 2020-11-12 10:12:25 +00:00
Lachlan
8dad1de61e generalise LocalFetcher to support many sheet types (xlsx, ods, etc) 2020-11-12 10:06:02 +00:00
Ebrahem Farooqui
eaa4d1f2c1 Merge pull request #56 from forensic-architecture/feature/refactor-categories
Feature/refactor categories
2020-10-20 15:22:26 -07:00
efarooqui
29182f8ec2 Linting threw error stating that we use path module to join instead of + 2020-10-19 09:02:03 -07:00
efarooqui
1c600bc222 Removing unnecessary test_export_events endpoint 2020-10-19 08:56:31 -07:00
efarooqui
8492c5cbaa Removed comment regarding categories endpoint 2020-10-15 21:33:13 -07:00
efarooqui
2e1d098d12 Test associations 2020-10-14 08:12:56 -07:00
efarooqui
e5288c2599 Removed categories from list and using test_export_events 2020-10-13 08:45:41 -07:00
efarooqui
ea52a8bd8c Removing export categories endpoint 2020-10-07 09:03:30 -07:00
Lachlan Kermode
5068ea8543 Merge pull request #54 from forensic-architecture/feature/refactor-filters-and-narratives-to-associations
Feature/refactor filters and narratives to associations
2020-09-23 17:12:14 +01:00
Lachlan Kermode
1e2a991708 try/catch to prioritise local.config.js 2020-09-23 18:08:40 +02:00
efarooqui
35f8460eb4 Removed gsheets tab in config, unused import in package json 2020-09-14 08:17:48 -07:00
Lachlan Kermode
98ec281973 Merge pull request #55 from forensic-architecture/dependabot/npm_and_yarn/node-fetch-2.6.1
Bump node-fetch from 2.6.0 to 2.6.1
2020-09-14 10:35:32 +02:00
dependabot[bot]
2a11bf1ec7 Bump node-fetch from 2.6.0 to 2.6.1
Bumps [node-fetch](https://github.com/bitinn/node-fetch) from 2.6.0 to 2.6.1.
- [Release notes](https://github.com/bitinn/node-fetch/releases)
- [Changelog](https://github.com/node-fetch/node-fetch/blob/master/docs/CHANGELOG.md)
- [Commits](https://github.com/bitinn/node-fetch/compare/v2.6.0...v2.6.1)

Signed-off-by: dependabot[bot] <support@github.com>
2020-09-13 00:13:08 +00:00
efarooqui
75831cbb52 Delete validation for now; not relevant to this PR 2020-09-11 08:28:55 -07:00
efarooqui
9dd2a66ce1 Clean up with event related associations 2020-09-11 08:25:52 -07:00
efarooqui
ae12de5933 Refactoring events to have only associations; created associations tab and related export tab 2020-09-09 21:23:49 -07:00
efarooqui
5c25a8d1d0 Collapsed filters and narratives into associations; modified default tabs to reflect 2020-08-25 08:55:19 -07:00
efarooqui
fba74d8e9c Wrote validation functions and getter to grab appropriate validation function 2020-08-11 21:40:34 -07:00
Lachlan Kermode
f3115007e2 Merge pull request #52 from forensic-architecture/dependabot/npm_and_yarn/lodash-4.17.19
Bump lodash from 4.17.15 to 4.17.19
2020-07-21 09:57:07 +02:00
Lachlan Kermode
70149b905f Merge pull request #51 from christophknoth/patch-1
Fixed internal anchor link headline
2020-07-21 09:56:48 +02:00
dependabot[bot]
afa52bffb6 Bump lodash from 4.17.15 to 4.17.19
Bumps [lodash](https://github.com/lodash/lodash) from 4.17.15 to 4.17.19.
- [Release notes](https://github.com/lodash/lodash/releases)
- [Commits](https://github.com/lodash/lodash/compare/4.17.15...4.17.19)

Signed-off-by: dependabot[bot] <support@github.com>
2020-07-21 04:11:55 +00:00
Lachlan Kermode
533ab6e6f9 update fmt 2020-07-15 09:58:56 +02:00
Christoph Knoth
d06f4a5b68 Fixed internal anchor link headline 2020-06-30 12:30:30 +02:00
Lachlan Kermode
95cb7a6f80 remove explicit ID from events in timemap setup 2020-06-19 12:07:57 +02:00
Lachlan Kermode
e7718f18c7 update readme 2020-06-18 11:04:30 +02:00
Lachlan Kermode
f43f0f322c update default sheet 2020-06-18 10:10:41 +02:00
Lachlan Kermode
33a3c57036 update 2020-06-17 19:10:40 +02:00
Lachlan Kermode
ce3475b147 fix empty rows 2020-06-17 18:58:44 +02:00
Lachlan Kermode
83e6897b75 fix for windows npm 2020-06-17 18:03:56 +02:00
Lachlan Kermode
a620b17090 stop tracking 2020-06-17 12:14:22 +02:00
FA Internal SSH
c273e681a4 make config cleaner 2020-06-17 08:37:49 +00:00
FA Internal SSH
1d0735ffb6 switch to npm 2020-06-17 08:30:17 +00:00
FA Internal SSH
b1aa8e6703 update prefixedTabs 2020-06-17 08:29:47 +00:00
Lachlan Kermode
4663e23940 add update button 2020-06-14 19:53:04 +02:00
Lachlan Kermode
b48345a5d4 rm git hooks 2020-06-14 19:31:04 +02:00
Lachlan Kermode
59157d44ba add basic hbs view 2020-06-14 19:27:23 +02:00
Lachlan Kermode
8a91c6af56 update config 2020-06-14 18:58:22 +02:00
Lachlan Kermode
63c26bdb12 add handlebars for blueprints 2020-06-02 14:58:22 +02:00
Lachlan Kermode
aa30489be9 Update sheets_config.js 2020-05-02 16:04:45 +02:00
Lachlan Kermode
d113181bb4 fix differential args for fetchers 2020-04-26 17:17:34 +02:00
Lachlan Kermode
f34b9224aa gitignore sheets_config.js 2020-04-12 12:05:53 +02:00
Lachlan Kermode
ebe2930a53 Update sheets_config.js 2020-04-12 12:04:44 +02:00
Lachlan Kermode
84b6ecf05f correct example config 2020-04-12 12:04:19 +02:00
Lachlan Kermode
1b5e0ebecf Merge pull request #50 from forensic-architecture/topic/xlsx
Topic/xlsx
2020-04-06 14:16:56 +02:00
Lachlan Kermode
f659b46233 all excel data as strings 2020-03-27 18:42:15 +01:00
Lachlan Kermode
5d7eb0af05 add xlsx support
by factoring out a Fetcher class
2020-03-27 12:30:21 +01:00
Lachlan Kermode
2e343a17dd enc 2020-03-26 06:45:49 +01:00
Lachlan Kermode
5feeff589a update sheets_config 2020-03-26 06:45:06 +01:00
Lachlan Kermode
eeb040c6ef Merge pull request #44 from forensic-architecture/dependabot/npm_and_yarn/js-yaml-3.13.1
Bump js-yaml from 3.12.0 to 3.13.1
2020-03-26 06:43:17 +01:00
Lachlan Kermode
e217b89a02 Merge pull request #45 from forensic-architecture/dependabot/npm_and_yarn/mixin-deep-1.3.2
Bump mixin-deep from 1.3.1 to 1.3.2
2020-03-26 06:43:06 +01:00
Lachlan Kermode
9767e76336 Merge pull request #46 from forensic-architecture/dependabot/npm_and_yarn/esm-3.2.25
Bump esm from 3.0.84 to 3.2.25
2020-03-26 06:42:54 +01:00
Lachlan Kermode
57c65ed5f4 Merge pull request #47 from forensic-architecture/dependabot/npm_and_yarn/axios-0.18.1
Bump axios from 0.18.0 to 0.18.1
2020-03-26 06:42:42 +01:00
Lachlan Kermode
21a597fa5b Merge pull request #48 from forensic-architecture/dependabot/npm_and_yarn/lodash.merge-4.6.2
Bump lodash.merge from 4.6.1 to 4.6.2
2020-03-26 06:42:30 +01:00
Lachlan Kermode
7a19356bb4 Merge pull request #49 from forensic-architecture/dependabot/npm_and_yarn/lodash-4.17.15
Bump lodash from 4.17.11 to 4.17.15
2020-03-26 06:42:17 +01:00
dependabot[bot]
77ab257ed3 Bump lodash from 4.17.11 to 4.17.15
Bumps [lodash](https://github.com/lodash/lodash) from 4.17.11 to 4.17.15.
- [Release notes](https://github.com/lodash/lodash/releases)
- [Commits](https://github.com/lodash/lodash/compare/4.17.11...4.17.15)

Signed-off-by: dependabot[bot] <support@github.com>
2020-03-26 05:41:06 +00:00
dependabot[bot]
0e7547c58c Bump esm from 3.0.84 to 3.2.25
Bumps [esm](https://github.com/standard-things/esm) from 3.0.84 to 3.2.25.
- [Release notes](https://github.com/standard-things/esm/releases)
- [Commits](https://github.com/standard-things/esm/compare/3.0.84...3.2.25)

Signed-off-by: dependabot[bot] <support@github.com>
2020-03-26 05:41:05 +00:00
dependabot[bot]
d4a74b5d70 Bump axios from 0.18.0 to 0.18.1
Bumps [axios](https://github.com/axios/axios) from 0.18.0 to 0.18.1.
- [Release notes](https://github.com/axios/axios/releases)
- [Changelog](https://github.com/axios/axios/blob/v0.18.1/CHANGELOG.md)
- [Commits](https://github.com/axios/axios/compare/v0.18.0...v0.18.1)

Signed-off-by: dependabot[bot] <support@github.com>
2020-03-26 05:41:05 +00:00
dependabot[bot]
e33417381a Bump lodash.merge from 4.6.1 to 4.6.2
Bumps [lodash.merge](https://github.com/lodash/lodash) from 4.6.1 to 4.6.2.
- [Release notes](https://github.com/lodash/lodash/releases)
- [Commits](https://github.com/lodash/lodash/commits)

Signed-off-by: dependabot[bot] <support@github.com>
2020-03-26 05:41:05 +00:00
dependabot[bot]
dd5428d7b6 Bump js-yaml from 3.12.0 to 3.13.1
Bumps [js-yaml](https://github.com/nodeca/js-yaml) from 3.12.0 to 3.13.1.
- [Release notes](https://github.com/nodeca/js-yaml/releases)
- [Changelog](https://github.com/nodeca/js-yaml/blob/master/CHANGELOG.md)
- [Commits](https://github.com/nodeca/js-yaml/compare/3.12.0...3.13.1)

Signed-off-by: dependabot[bot] <support@github.com>
2020-03-26 05:41:03 +00:00
dependabot[bot]
f83251e308 Bump mixin-deep from 1.3.1 to 1.3.2
Bumps [mixin-deep](https://github.com/jonschlinkert/mixin-deep) from 1.3.1 to 1.3.2.
- [Release notes](https://github.com/jonschlinkert/mixin-deep/releases)
- [Commits](https://github.com/jonschlinkert/mixin-deep/compare/1.3.1...1.3.2)

Signed-off-by: dependabot[bot] <support@github.com>
2020-03-26 05:41:03 +00:00
Lachlan Kermode
0374955469 Merge pull request #43 from forensic-architecture/dependabot/npm_and_yarn/acorn-6.4.1
Bump acorn from 6.0.4 to 6.4.1
2020-03-26 06:40:40 +01:00
Lachlan Kermode
c294e4964e Merge pull request #42 from forensic-architecture/dependabot/npm_and_yarn/eslint-utils-1.4.3
Bump eslint-utils from 1.3.1 to 1.4.3
2020-03-26 06:40:30 +01:00
dependabot[bot]
1c2abded7b Bump acorn from 6.0.4 to 6.4.1
Bumps [acorn](https://github.com/acornjs/acorn) from 6.0.4 to 6.4.1.
- [Release notes](https://github.com/acornjs/acorn/releases)
- [Commits](https://github.com/acornjs/acorn/compare/6.0.4...6.4.1)

Signed-off-by: dependabot[bot] <support@github.com>
2020-03-14 18:57:07 +00:00
dependabot[bot]
25a57f95d3 Bump eslint-utils from 1.3.1 to 1.4.3
Bumps [eslint-utils](https://github.com/mysticatea/eslint-utils) from 1.3.1 to 1.4.3.
- [Release notes](https://github.com/mysticatea/eslint-utils/releases)
- [Commits](https://github.com/mysticatea/eslint-utils/compare/v1.3.1...v1.4.3)

Signed-off-by: dependabot[bot] <support@github.com>
2019-11-02 03:40:28 +00:00
Lachlan Kermode
13f93a2b04 Merge pull request #38 from forensic-architecture/issue/blueprints-json-formatting-#30
Issue/blueprints json formatting #30
2019-05-23 17:03:46 +01:00
Lachlan Kermode
fdcf4f28d0 encrypt with maintainer secret 2019-05-23 16:40:29 +01:00
SAM LUDFORD
9deb5aae3f multiple resources collapsed into single blueprint 2019-05-23 11:43:10 +01:00
SAM LUDFORD
8f397e395d list of list of blueprints flattened into single list 2019-05-23 11:31:16 +01:00
Lachlan Kermode
824b672a5e license fix 2019-05-16 12:13:18 +01:00
Lachlan Kermode
1c82292344 update license 2019-05-16 12:10:05 +01:00
Lachlan Kermode
2632661bbe .env.enc for travis tests 2019-05-15 15:52:18 +01:00
Lachlan Kermode
849a0ce9ed update readme with new wiki 2019-05-15 15:47:09 +01:00
35 changed files with 7994 additions and 4813 deletions

BIN
.env.enc

Binary file not shown.

19
.github/workflows/cd.yml vendored Normal file
View File

@@ -0,0 +1,19 @@
name: CD
on:
push:
branches: [ develop ]
# pull_request:
# branches: [ develop ]
jobs:
build:
runs-on: ubuntu-latest
steps:
- name: Trigger CD build
uses: peter-evans/repository-dispatch@v1
with:
token: ${{ secrets.CI_DISPATCH_TOKEN }}
repository: forensic-architecture/configs
event-type: remote-build
client-payload: '{"runtime_args": "datasheet", "branch": "${GITHUB_REF##*/}"}'

25
.github/workflows/ci.yml vendored Normal file
View File

@@ -0,0 +1,25 @@
name: CI
on:
push:
branches: [ develop ]
pull_request:
branches: [ develop ]
jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
with:
ref: ${{ github.head_ref }}
- uses: actions/setup-node@v2-beta
with:
node-version: '12'
- run: npm install
- run: npm test
env:
CI: true
- run: npm run lint
env:
CI: true

8
.gitignore vendored
View File

@@ -7,7 +7,11 @@
.env
*service-account-key\.json
src/config.js
/yarn-error.log
*.pem
.travis.yml.old
.travis.yml.old
tags
tags.lock
tags.temp
src/config.js
src/local.config.js

View File

@@ -1,17 +0,0 @@
language: node_js
node_js:
- stable
cache:
directories:
- node_modules
before_script:
- npm install -g yarn
install:
- yarn
script:
- yarn build
- yarn lint
- yarn test
before_install:
- openssl aes-256-cbc -K $encrypted_eeb74e8d35d1_key -iv $encrypted_eeb74e8d35d1_iv
-in .env.enc -out .env -d

View File

@@ -10,11 +10,11 @@ RUN cd /www; yarn
COPY . /www
WORKDIR /www
RUN yarn build
RUN mkdir -p temp
RUN mkdir -p data
# set your port
ENV PORT 8080
EXPOSE 8080
ENV PORT 4040
EXPOSE 4040
# start command as per package.json
CMD ["yarn", "start"]

20
LICENSE
View File

@@ -1,20 +0,0 @@
The MIT License (MIT)
Copyright (c) 2018 Forensic Architecture
Permission is hereby granted, free of charge, to any person obtaining a copy of
this software and associated documentation files (the "Software"), to deal in
the Software without restriction, including without limitation the rights to
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
the Software, and to permit persons to whom the Software is furnished to do so,
subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

66
LICENSE.md Normal file
View File

@@ -0,0 +1,66 @@
Do No Harm License
**Preamble**
Most software today is developed with little to no thought of how it will be used, or the consequences for our society and planet.
As software developers, we engineer the infrastructure of the 21st century. We recognise that our infrastructure has great power to shape the world and the lives of those we share it with, and we choose to consciously take responsibility for the social and environmental impacts of what we build.
We envisage a world free from injustice, inequality, and the reckless destruction of lives and our planet. We reject slavery in all its forms, whether by force, indebtedness, or by algorithms that hack human vulnerabilities. We seek a world where humankind is at peace with our neighbours, nature, and ourselves. We want our work to enrich the physical, mental and spiritual wellbeing of all society.
We build software to further this vision of a just world, or at the very least, to not put that vision further from reach.
**Terms**
*Copyright* (c) 2019 Forensic Architecture. All rights reserved.
Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
4. This software must not be used by any organisation, website, product or service that:
a) lobbies for, promotes, or derives a majority of income from actions that support or contribute to:
* sex trafficking
* human trafficking
* slavery
* indentured servitude
* gambling
* tobacco
* adversely addictive behaviours
* nuclear energy
* warfare
* weapons manufacturing
* war crimes
* violence (except when required to protect public safety)
* burning of forests
* deforestation
* hate speech or discrimination based on age, gender, gender identity, race, sexuality, religion, nationality
b) lobbies against, or derives a majority of income from actions that discourage or frustrate:
* peace
* access to the rights set out in the Universal Declaration of Human Rights and the Convention on the Rights of the Child
* peaceful assembly and association (including worker associations)
* a safe environment or action to curtail the use of fossil fuels or prevent climate change
* democratic processes
5. All redistribution of source code or binary form, including any modifications must be under these terms. You must inform recipients that the code is governed by these conditions, and how they can obtain a copy of this license. You may not attempt to alter the conditions of who may/may not use this software.
We define:
**Forests** to be 0.5 or more hectares of trees that were either planted more than 50 years ago or were not planted by humans or human made equipment.
**Deforestation** to be the clearing, burning or destruction of 0.5 or more hectares of forests within a 1 year period.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
**Attribution**
Do No Harm License [Contributor Covenant][homepage], (pre 1.0),
available at https://github.com/raisely/NoHarm
[homepage]: https://github.com/raisely/NoHarm

View File

@@ -34,49 +34,10 @@ Datasheet server is a Node server developed at [Forensic Architecture](https://f
Querying data directly from spreadsheets is brittle, as it relies on the maintenance of a rigid structure in the sheets at all times. By putting Datasheet Server as a proxy that sits in between source sheets and their consumers, it is possible to dynamically modify sheets without breaking applications. A data admin can then use Datasheet Server to ensure that applications always receive eligible data, without foregoing the spreadsheets as sources of truth.
To see how to get a local instance of datasheet server running in practice, see [this wiki](https://github.com/forensic-architecture/timemap/wiki/Setting-up-a-local-instance-of-Timemap) explaining how to use it to feed data from a Google Sheet to a local instance of [Timemap](https://github.com/forensic-architecture/timemap).
### Design Concepts
The codebase currently only supports Google Sheets as a source, and a REST-like format as a query language. It is designed, however, with extensibility in mind.
**Sources**
A source represents a sheet-like collection of data, such as a Google Sheet. A source has one or more **tabs**, each of which contains a 2-dimensional grids of cells. Each cell contains a body of text (a string).
**Resources**
The data from sources are made available as resources, which are structured blocks of data that are granularly accessible through a query language. Resources are the outfacing aspect of Datasheet Server, and represent the only kind of data that can be queried by applications. Each resource is configured with one or more **query languages**. (Currently only a REST-like query language supported.)
**Blueprints**
Blueprints are a data structure that represent the way that infromation from **sources** are to be turned into **resources**. For each tab in a source, there is a corresponding Blueprint. Blueprints are created through a [blueprinter function](/src/blueprinters) invoked on the raw data from a source tab.
Blueprints are JSON objects. There have two forms:
1. _desaturated_ -- describes the resources and query languages available on data from a source tab.
2. _saturated_ -- both describes resources available on data from a source etab, and contains that data.
A desaturated Blueprint can be saturated by retrieving its data from the server's **model layer**, which stores tab data from sources.
A JSON catalogue of the available blueprints (desaturated) in a server is available at `/api/blueprints`.
## [Configuration](#configuration)
Copy the example environment file:
```
cp .env.example .env
```
Inside this file, you will need to modify at least the `SERVICE_ACCOUNT_EMAIL` and `SERVICE_ACCOUNT_PRIVATE_KEY` fields. These fields refer to the credentials of a [Google service account](https://cloud.google.com/iam/docs/understanding-service-accounts). Google requires that developers create these when attempting to access their services programmatically, so that they can attribute requests to users. Service accounts also contain identity information, which means that asset owners can allow certain service accounts access to certain sheets, just as one might differentially grant certain users access to certain cloud assets.
Once you have [created a service account](https://support.google.com/a/answer/7378726?hl=en), create and download an [API key for that account](https://cloud.google.com/iam/docs/creating-managing-service-account-keys). The JSON file for the API key that you download when you create it contains both a service account private key, and an email associated with the service account: add these respectively in the strings in .env for `SERVICE_ACCOUNT_PRIVATE_KEY` and `SERVICE_ACCOUNT_EMAIL`.
The last thing to do is to grant the service account access to the sheet that
datasheet-server is pulling from. You can add a service account to a sheet as
you would any other Google user: just enter the email address associated. (Note
that this step is not necessary if you are accessing a publicly available
sheet.)
Other configuration options, such as the port at which the server will expose
resources, are also modifiable from the .env file.
To see how to get a local instance of datasheet server running in practice, see [this wiki](https://github.com/forensic-architecture/timemap/wiki/running-timemap-and-datasheet-server-locally) explaining how to use it to feed data from a Google Sheet to a local instance of [Timemap](https://github.com/forensic-architecture/timemap).
#### [Sources](#sources)
Sources are specified in [src/sheets_config.js](https://github.com/forensic-architecture/datasheet-server/blob/develop/src/sheets_config.js). Datasheet server currently only supports Google Sheets as a source.
Sources are specified in [src/config.js](https://github.com/forensic-architecture/datasheet-server/blob/develop/src/config.js). Datasheet server currently only supports Google Sheets as a source.
###### [Google Sheets](#source-google-sheets)
| sheets | A list of objects, one for each sheet that is being used as a source. Each sheet object has a `name` (String), an `id` (String), and a `tabs` (object) field, which are explained below. | object |
@@ -86,34 +47,13 @@ Each Google Sheet being used as a as source requires a corresponding object in `
| Option | Description | Type |
| ------ | ----------- | ---- |
| name | Used to refer to data served from this source | string |
| id | The ID of the sheet in Google. (You can find it in the address bar when the Sheet is open in a browser. It is the string that follows 'spreadsheets/d/'). | string |
| path/id | The path to the XLSX sheet on your local, or the ID of the sheet in Google. (You can find it in the address bar when the Sheet is open in a browser. It is the string that follows 'spreadsheets/d/'). | string |
| tabs | An object that maps each tab in the source to one or more Blueprinters. All of the Blueprinters in the [blueprinters folder](/lib/blueprinters) are available through a single import as at the top of [example.config.js](/src/example.config.js). <br> To correctly associate a Blueprinter, the object key needs to be _the tab name with all lowercase letters, and spaces replaced by a '-'_. For example, if the tab name in Google Sheets is 'Info About SHEEP', the object key should be 'info-about-sheep'. <br> The value should be the Blueprinter function that you want to use for the data in that tab. If you require more than one endpoint for a single tab, you can support multiple blueprinters by making the item an array. See the example of a configuration object below. <br>TODO: no Blueprinter is used by default. | object |
###### Example Configuration Object
```js
import BP from './lib/blueprinters'
export default {
googleSheets: {
email: 'project-name@reliable-baptist-23338.iam.gserviceaccount.com',
privateKey: 'SOME_PRIVATE_KEY',
sheets: [
{
name: 'example',
id: '1s-vfBR8Uy-B-TLO_C5Ozw4z-L0E3hdP8ohMV761ouRI',
tabs: {
'objects': BP.rows,
'fruit': [BP.columns, BP.ids],
}
},
]
}
}
```
See src/config.js for an example configuration sheet.
## [Quickstart](#quickstart)
## [Quickstart](#quickstart)
Clone the repository to your local:
```
git clone https://www.github.com/forensic-architecture/datasheet-server

3
data/.gitignore vendored Normal file
View File

@@ -0,0 +1,3 @@
*
!.gitignore
!timemap_data.xlsx

BIN
data/timemap_data.xlsx Normal file

Binary file not shown.

View File

@@ -8,10 +8,10 @@ management of the sheets it represents.
It exposes an API with four endpoints:
* `/:sheet/:tab/:resource` - the primary means of accessing data in the
server. The name of a sheet is specified in
[sheets_config.js](https://github.com/forensic-architecture/datasheet-server/blob/develop/src/sheets_config.js#L7).
[_config.js](https://github.com/forensic-architecture/datasheet-server/blob/develop/src/config.js#L7).
Each tab on the sheet is in lower case, with spaces replaced by
underscores. The resource name is the same as the name of the blueprinter
specified for the tab is sheets_config.js.
specified for the tab is config.js.
* `/:sheet/:tab/:resource/:id` - when the resource is a list, items in the list
can be accessed individually as well.
* `/update` - when this route is queried with a GET request, each of the

22
docs/design-concepts.md Normal file
View File

@@ -0,0 +1,22 @@
### Design Concepts
The codebase currently only supports Google Sheets as a source, and a REST-like format as a query language. It is designed, however, with extensibility in mind.
**Sources**
A source represents a sheet-like collection of data, such as a Google Sheet. A source has one or more **tabs**, each of which contains a 2-dimensional grids of cells. Each cell contains a body of text (a string).
**Resources**
The data from sources are made available as resources, which are structured blocks of data that are granularly accessible through a query language. Resources are the outfacing aspect of Datasheet Server, and represent the only kind of data that can be queried by applications. Each resource is configured with one or more **query languages**. (Currently only a REST-like query language supported.)
**Blueprints**
Blueprints are a data structure that represent the way that infromation from **sources** are to be turned into **resources**. For each tab in a source, there is a corresponding Blueprint. Blueprints are created through a [blueprinter function](/src/blueprinters) invoked on the raw data from a source tab.
Blueprints are JSON objects. There have two forms:
1. _desaturated_ -- describes the resources and query languages available on data from a source tab.
2. _saturated_ -- both describes resources available on data from a source etab, and contains that data.
A desaturated Blueprint can be saturated by retrieving its data from the server's **model layer**, which stores tab data from sources.
A JSON catalogue of the available blueprints (desaturated) in a server is available at `/api/blueprints`.

19
docs/gsheet-config.md Normal file
View File

@@ -0,0 +1,19 @@
## [Configuration](#configuration)
Copy the example environment file:
```
cp .env.example .env
```
Inside this file, you will need to modify at least the `SERVICE_ACCOUNT_EMAIL` and `SERVICE_ACCOUNT_PRIVATE_KEY` fields. These fields refer to the credentials of a [Google service account](https://cloud.google.com/iam/docs/understanding-service-accounts). Google requires that developers create these when attempting to access their services programmatically, so that they can attribute requests to users. Service accounts also contain identity information, which means that asset owners can allow certain service accounts access to certain sheets, just as one might differentially grant certain users access to certain cloud assets.
Once you have [created a service account](https://support.google.com/a/answer/7378726?hl=en), create and download an [API key for that account](https://cloud.google.com/iam/docs/creating-managing-service-account-keys). The JSON file for the API key that you download when you create it contains both a service account private key, and an email associated with the service account: add these respectively in the strings in .env for `SERVICE_ACCOUNT_PRIVATE_KEY` and `SERVICE_ACCOUNT_EMAIL`.
The last thing to do is to grant the service account access to the sheet that
datasheet-server is pulling from. You can add a service account to a sheet as
you would any other Google user: just enter the email address associated. (Note
that this step is not necessary if you are accessing a publicly available
sheet.)
Other configuration options, such as the port at which the server will expose
resources, are also modifiable from the .env file.

7461
package-lock.json generated Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -4,19 +4,14 @@
"description": "Starter project for an ES6 RESTful Express API",
"main": "dist",
"scripts": {
"dev": "NODE_ENV=development nodemon -w src --exec \"babel-node src\"",
"build": "NODE_ENV=production npx babel src -d dist",
"dev": "env NODE_ENV=development nodemon -w src --exec \"babel-node src\"",
"build": "env NODE_ENV=production npx babel src -d dist",
"start": "node dist",
"lint": "standard \"src/**/*.js\" \"test/**/*.js\"",
"test-watch": "ava --watch",
"test": "ava --verbose",
"travis-encrypt": "./scripts/encrypt.sh"
},
"husky": {
"hooks": {
"pre-push": "./scripts/check-branch.sh && yarn lint && yarn test"
}
},
"repository": {
"type": "git",
"url": "git+https://github.com/developit/express-es6-rest-api.git"
@@ -30,14 +25,16 @@
"dotenv": "^6.1.0",
"express": "^4.13.3",
"express-graphql": "^0.6.12",
"googleapis": "^32.0.0",
"express-handlebars": "^4.0.4",
"googleapis": "^39.1.0",
"graphql": "^0.13.2",
"morgan": "^1.8.0",
"mz": "^2.7.0",
"node-fetch": "^2.3.0",
"node-fetch": "^2.6.1",
"object-hash": "^1.3.0",
"ramda": "^0.25.0",
"resource-router-middleware": "^0.6.0"
"resource-router-middleware": "^0.6.0",
"xlsx": "^0.16.8"
},
"devDependencies": {
"@babel/cli": "^7.1.2",
@@ -47,7 +44,6 @@
"@babel/preset-env": "^7.1.0",
"@babel/register": "^7.0.0",
"ava": "1.0.0-beta.8",
"husky": "^1.2.0",
"nodemon": "1.18.7",
"standard": "^12.0.1"
},

View File

@@ -1,11 +0,0 @@
#!/bin/bash
ENCRYPT_MESSAGE="\nThe .env.enc file has not changed its contents from the version on origin/develop.\nTo ensure that tests pass in Travis, you need to encrypt the contents of .env before pushing to the remote, so that the Travis server can use your service account credentials.\nEnsure that the Travis CLI is installed on your local, run\n\n\tnpm run travis-encrypt\n\nand then push to the remote again.\nIf you don't care whether your build passes on Travis, you can run:\n\n\tgit push --no-verify\n\nand bypass this check.\n\n"
# check whether .env.enc has changed
if [ -z "`git diff origin/develop -- .env.enc`" ]; then
echo $ENCRYPT_MESSAGE
exit 1
fi

View File

@@ -1,38 +0,0 @@
#!/bin/bash
echo "Encrypting .env file for Travis..."
# confirm that the user has the core repo as origin, and is therefore a maintainer
# if [ -z `git config --get remote.origin.url | grep "forensic-architecture/datasheet-server"` ]; then
# echo "Travis encryption not required for satellite contributors, continuing.."
# exit 0
# fi
# confirm travis is installed
if [ ! hash travis 2>/dev/null ]; then
echo "============================================================================================"
echo "ERROR: Travis CLI is not installed on your local. Please install from:"
echo "\thttps://github.com/travis-ci/travis.rb"
echo "After installing, make sure that you login with:"
echo "\ttravis login --pro"
echo "============================================================================================"
exit 3
fi
# confirm there is a .env file to encrypt
if [ ! -f .env ]; then
echo "============================================================================================"
echo "ERROR: You must create a .env file and add your credentials. See .env.example for an example"
echo "============================================================================================"
exit 3
fi
# regex to match and delete 'before_install' and everything after it
# necessary to delete these lines to get Travis to build for multiple accounts
echo "creating new .travis.yml configuration"
sed -i.old '/^before_install.*/,$ d' .travis.yml
echo "old config file saved as .travis.yml.old"
travis encrypt-file .env --add --force --org
git add .env.enc
git add .travis.yml
echo ".env.enc created and added to commit"

View File

@@ -1,5 +1,7 @@
import { version } from '../../package.json'
import fetch from 'node-fetch'
import { Router } from 'express'
import { getParameterByName } from '../lib/util'
import copy from '../copy/en'
export default ({ config, controller }) => {
@@ -12,7 +14,29 @@ export default ({ config, controller }) => {
})
api.get('/blueprints', (req, res) => {
res.json(controller.blueprints())
const bps = controller.blueprints()
res.render('blueprints', {
bps: bps.map(bp => ({
source: bp.sheet.name,
tab: bp.name,
urls: bp.urls
}))
})
})
api.get('/media/:code', (req, res) => {
const { code } = req.params
fetch(`${process.env.MEDIA_API_ENDPOINT}/${code}`, {
method: 'get',
headers: {
'Authorization': 'Basic ' + new Buffer(process.env.MEDIA_AUTH_USER + ":" + process.env.MEDIA_AUTH_PWORD).toString("base64")
}
})
.then(response => response.json())
.then(data => res.send(data))
.catch(err => {
res.status(err.status || 404).send({ error: err.message })
});
})
api.get('/update', (req, res) => {
@@ -23,10 +47,10 @@ export default ({ config, controller }) => {
success: msg
})
)
.catch(err =>
.catch(err => {
res.status(404)
.send({ error: err.message, err })
)
})
})
api.get('/:sheet/:tab/:resource/:frag', (req, res) => {

View File

@@ -63,8 +63,9 @@ export default (data) => {
structure.__flat.forEach(label => {
deepRow[label] = baseRow[label]
})
output.push(deepRow)
if (!Object.keys(deepRow).every(k => deepRow[k] === '')) {
output.push(deepRow)
}
})
return output

12
src/config.js Normal file
View File

@@ -0,0 +1,12 @@
import { timemap } from './lib'
export default {
gsheets: [],
xlsx: [
{
name: 'timemap_data',
path: 'data/timemap_data.xlsx',
tabs: timemap.default
}
]
}

View File

@@ -1,15 +1,21 @@
import http from 'http'
import path from 'path'
import express from 'express'
import initialize from './initialize'
import middleware from './middleware'
import api from './api'
// import config from './sheets_config'
import dotenv from 'dotenv'
const hbs = require('express-handlebars')
dotenv.config()
let app = express()
app.server = http.createServer(app)
app.engine('.hbs', hbs({
extname: '.hbs',
defaultLayout: 'default'
}))
app.set('view engine', '.hbs')
// enable cross origin requests explicitly in development
if (process.env.NODE_ENV === 'development') {
@@ -35,8 +41,11 @@ initialize(controller => {
})
)
app.use(express.static(path.join(__dirname, 'public')))
app.server.listen(process.env.PORT || 4040, () => {
console.log(`Started on port ${app.server.address().port}`)
console.log('===========================================')
console.log(`Server running on port ${app.server.address().port}`)
})
})

View File

@@ -1,37 +1,43 @@
import StoreJson from './models/StoreJson'
import Fetcher from './lib/Fetcher'
import fetchers from './lib/Fetcher'
import Controller from './lib/Controller'
import sheetsConfig from './sheets_config'
import R from 'ramda'
const { googleSheets } = sheetsConfig
const { sheets } = googleSheets
const isntNull = n => n !== null
const filterNull = ls => R.filter(isntNull, ls)
const flattenfilterNull = ls => filterNull(R.flatten(ls))
let themFetchers
function authenticate (_fetcher) {
return _fetcher.fetcher.authenticate(process.env.SERVICE_ACCOUNT_EMAIL, process.env.SERVICE_ACCOUNT_PRIVATE_KEY).then(msg => {
console.log(msg)
return true
})
let config
try {
config = require('./local.config.js').default
} catch (_) {
config = require('./config.js').default
}
export default callback => {
const fetchers = sheets.map(sheet => {
return {
name: sheet.name,
fetcher: new Fetcher(new StoreJson(), sheet.name, sheet.id, sheet.tabs)
}
})
Promise.all(fetchers.map(authenticate))
.then(() => {
console.log(`===================`)
console.log(`grant access to: ${process.env.SERVICE_ACCOUNT_EMAIL}`)
console.log(`===================`)
// NB: reformat fetchers as config for controller
const config = {}
fetchers.forEach(fetcher => {
config[fetcher.name] = fetcher.fetcher
return Promise.resolve().then(() => {
return Object.keys(config).map(fType => {
// skip config attrs that don't have corresponding fetchers
if (!(fType in fetchers)) return null
const FFetcher = fetchers[fType]
return config[fType].map(sheet => {
const otherArgs = { ...sheet }
delete otherArgs.name
delete otherArgs.tabs
return {
name: sheet.name,
fetcher: new FFetcher(new StoreJson(), sheet.name, sheet.tabs, ...Object.values(otherArgs))
}
})
})
})
.then(res => {
themFetchers = flattenfilterNull(res)
})
.then(() => Promise.all(themFetchers.map(f => f.fetcher.authenticate(process.env))))
.then(fetchers => {
const config = R.zipObj(themFetchers.map(f => f.name), fetchers)
const controller = new Controller(config)
callback(controller)
})

18
src/lib.js Normal file
View File

@@ -0,0 +1,18 @@
import BP from './lib/blueprinters'
function prefixedTabs (prefix, cfg) {
if (!cfg) cfg = {}
const prf = key => cfg[key] ? `${prefix}_` : ''
return {
[`${prf('events')}export_events`]: BP.deeprows,
[`${prf('associations')}export_associations`]: BP.deeprows,
[`${prf('sources')}export_sources`]: BP.deepids,
[`${prf('shapes')}export_shapes`]: BP.deeprows,
[`${prf('sites')}export_sites`]: BP.rows
}
}
export const timemap = {
default: prefixedTabs(),
prefixedTabs
}

View File

@@ -1,7 +1,7 @@
import copy from '../copy/en'
/**
* Controller
* Controller class
*
*/
class Controller {
@@ -16,16 +16,22 @@ class Controller {
blueprints () {
return Object.keys(this.fetchers).map(
sheet => this.fetchers[sheet].blueprints
)
).reduce((acc, curr) => acc.concat(curr))
}
rebuildBlueprintsAsync () {
Object.values(this.fetchers).forEach(t => t._buildBlueprintsAsync())
}
update () {
const me = this
return Promise.all(
Object.keys(this.fetchers).map(sheet => {
return this.fetchers[sheet].update()
})
).then(results => {
if (results.every(r => r)) {
me.rebuildBlueprintsAsync()
return copy.success.update
} else {
throw new Error(copy.errors.update)

View File

@@ -1,45 +1,39 @@
// FetcherTwo class interfaces with Google Sheet, and saves to a specified db
import { google } from 'googleapis'
import R from 'ramda'
import { createHash } from 'crypto'
import { buildDesaturated } from './blueprinters'
import {
fmtName,
fmtBlueprinterTitles,
isFunction
} from './util'
import { createHash } from 'crypto'
import R from 'ramda'
/* GsheetFetcher deps */
import { google } from 'googleapis'
/* LocalFetcher deps */
import X from 'xlsx'
class Fetcher {
constructor (db, sheetName, sheetId, blueprinters) {
constructor (db, name, bps) {
/*
* The database that the fetcher should use. This should be an instance of a model-compliant class.
* See models/Interface.js for the specifications for a model-compliant class.
*/
this.db = db
/*
* ID of the Google Sheet where the data is sheetd. Note that the privateKey.clientEmail
* loaded here must be added to the sheet as an editor.
*/
this.sheetId = sheetId
/*
* The name of the sheet. This will prefix tabs saved in the database.
*/
this.sheetName = sheetName
this.sheetName = name
/*
* A unique ID for the Fetcher to identify its elements in the model layer
*/
this.id = createHash('md5').update(sheetName).update(sheetId).digest('hex')
const bpsstring = Object.keys(bps).join(';')
this.id = createHash('md5').update(name).update(bpsstring).digest('hex')
/*
* These are the available tabs for storing and retrieving data.
* Each blueprinter is a function that returns a Blueprint from a
* list of lists (which will be retrieved from gsheets).
*/
this.blueprinters = fmtBlueprinterTitles(blueprinters)
this.blueprinters = fmtBlueprinterTitles(bps)
/*
* This object is the canonical represenation for the data that a Fetcher
* proxies. When the fetcher is initialized, its model layer (db) is indexed,
@@ -49,13 +43,6 @@ class Fetcher {
*/
this.blueprints = null
this._buildBlueprintsAsync() // NB: modifies this.blueprints on completion
/*
* Google API setup
*/
this.API = google.sheets('v4')
this.auth = null
/** curry to allow convenient syntax with map */
this._saveViaBlueprinter = R.curry(this._saveViaBlueprinter.bind(this))
}
@@ -66,20 +53,33 @@ class Fetcher {
const allParts = allUrls.reduce((acc, url) => {
if (url.startsWith(this.id)) {
const parts = url.split('/')
acc.push([ parts[1], parts[2] ])
return acc
} else {
return acc
let duplicateTab = acc.reduce((tabFound, p) => {
return tabFound || p[0] === parts[1]
}, false)
if (duplicateTab) {
acc.forEach(p => {
if (p[0] === parts[1]) {
p[1].push(parts[2])
}
})
} else {
acc.push([ parts[1], [ parts[2] ] ])
}
}
return acc
}, [])
return allParts
.map(parts => buildDesaturated(
this.sheetId,
this.sheetName,
parts[0],
parts[1]
))
.map(parts => {
const bp = buildDesaturated(
this.sheetId,
this.sheetName,
parts[0],
parts[1]
)
bp.urls = Object.keys(bp.resources).map(r => `/api/${bp.sheet.name}/${bp.name}/${r}`)
return bp
})
})
.then(res => {
this.blueprints = res
@@ -104,20 +104,80 @@ class Fetcher {
)
}
save (_tab, data) {
const tab = fmtName(_tab)
if (Object.keys(this.blueprinters).indexOf(tab) > -1) {
const bpConfig = this.blueprinters[tab]
if (isFunction(bpConfig)) {
// if bpConfig specifies a single blueprinter
return this._saveViaBlueprinter(tab, data, bpConfig)
} else {
// if bpConfig specifies an array of blueprinters
return bpConfig.map(this._saveViaBlueprinter(tab, data))
}
} else {
// NB: if a blueprinter is not specified for a tab,
// just skip it.
return true
}
}
// NB: could combine these functions by checking kwargs length
retrieve (tab, resource) {
const title = fmtName(tab)
const url = `${this.id}/${tab}/${resource}`
return this.db.load(url, this.blueprints[title])
}
retrieveFrag (tab, resource, frag) {
const title = fmtName(tab)
const url = `${this.id}/${tab}/${resource}/${frag || ''}`
return this.db.load(url, this.blueprints[title])
}
/** Run on startup. Should be overridden if explicit auth is required **/
authenticate (env) {
return Promise.resolve(this)
}
}
class GsheetFetcher extends Fetcher {
constructor (db, name, bps, sheetId) {
super(db, name, bps)
this.type = 'Google Sheet'
if (arguments.length < 4) throw Error('You must provide the sheet ID')
/*
* ID of the Google Sheet where the data is sheetd. Note that the privateKey.clientEmail
* loaded here must be added to the sheet as an editor.
*/
this.sheetId = sheetId
/*
* Google API setup
*/
this.API = google.sheets('v4')
this.auth = null
}
/** returns a Promise that resolves if access is granted to the account, and rejects otherwise. */
authenticate (clientEmail, privateKey) {
const googleAuth = new google.auth.JWT(clientEmail, null, privateKey, [
authenticate (env) {
const googleAuth = new google.auth.JWT(env.SERVICE_ACCOUNT_EMAIL, null, env.SERVICE_ACCOUNT_PRIVATE_KEY, [
'https://www.googleapis.com/auth/spreadsheets'
])
this.auth = googleAuth
const { sheetId } = this
const me = this
return new Promise((resolve, reject) => {
googleAuth.authorize(function (err) {
if (err) {
reject(err)
} else {
resolve(`Connected to ${sheetId}.`)
console.log(`Connected to ${me.sheetName}. (${me.type} with ID ${sheetId}).`)
console.log(` grant access to: ${process.env.SERVICE_ACCOUNT_EMAIL}`)
resolve(me)
}
})
})
@@ -157,41 +217,37 @@ class Fetcher {
})
.then(this._buildBlueprintsAsync())
.then(() => true)
.catch(() => false)
}
save (_tab, data) {
const tab = fmtName(_tab)
if (Object.keys(this.blueprinters).indexOf(tab) > -1) {
const bpConfig = this.blueprinters[tab]
if (isFunction(bpConfig)) {
// if bpConfig specifies a single blueprinter
return this._saveViaBlueprinter(tab, data, bpConfig)
} else {
// if bpConfig specifies an array of blueprinters
return bpConfig.map(this._saveViaBlueprinter(tab, data))
}
} else {
// NB: if a blueprinter is not specified for a tab,
// just skip it.
return true
}
}
// NB: could combine these functions by checking kwargs length
retrieve (tab, resource) {
const title = fmtName(tab)
const url = `${this.id}/${tab}/${resource}`
return this.db.load(url, this.blueprints[title])
}
retrieveFrag (tab, resource, frag) {
const title = fmtName(tab)
const url = `${this.id}/${tab}/${resource}/${frag || ''}`
return this.db.load(url, this.blueprints[title])
.catch((err) => {
console.log(`Error fetching gsheets: ${err.message} `)
return false
})
}
}
export default Fetcher
class LocalFetcher extends Fetcher {
constructor (db, name, bps, path) {
super(db, name, bps)
this.path = path
this.update().then(res =>
console.log(`${res ? 'Successful' : 'Couldn\'t'} update ${name}`)
)
}
update () {
const wb = X.readFile(this.path)
wb.SheetNames.forEach(name => {
const sh = wb.Sheets[name]
const csv = X.utils.sheet_to_csv(sh, { FS: '\t' })
const ll = csv.split('\n').map(line => line.split('\t'))
this.save(name, ll)
})
return Promise.resolve(true)
}
}
export default {
'gsheets': GsheetFetcher,
'xlsx': LocalFetcher,
'ods': LocalFetcher,
'local': LocalFetcher
}

View File

@@ -15,12 +15,15 @@ export const defaultResource = {
data: []
}
export function buildDesaturated (sheetId, sheetName, tab, resource) {
export function buildDesaturated (sheetId, sheetName, tab, resources) {
const bp = R.clone(defaultBlueprint)
bp.sheet.name = sheetName
bp.sheet.id = sheetId
bp.name = tab
bp.resources[resource] = null
bp.resources = resources.reduce((acc, r) => {
acc[r] = null
return acc
}, {})
return bp
}

View File

@@ -1,7 +1,7 @@
import fs from 'mz/fs'
import errors from '../lib/errors'
const STORAGE_DIRNAME = 'temp'
const STORAGE_DIRNAME = 'data'
function partsFromFilename (fname) {
const body = fname.slice(0, -5)
@@ -19,7 +19,6 @@ class StoreJson {
save (url, data) {
const parts = url.split('/')
return fs.writeFile(
`${STORAGE_DIRNAME}/${parts[0]}__${parts[1]}__${parts[2]}.json`,
JSON.stringify(data)

View File

@@ -1,20 +0,0 @@
import BP from './lib/blueprinters'
export default {
googleSheets: {
sheets: [
{
name: 'example',
id: '1UC7DkCFeUXHfpUxUGruExwFbP4pqVBdJLOKfo6wDDGk',
tabs: {
export_events: [BP.deeprows, BP.rows],
export_categories: [BP.groups, BP.rows],
export_narratives: BP.rows,
export_sources: BP.deepids,
export_sites: BP.rows,
export_tags: BP.tree
}
}
]
}
}

2
temp/.gitignore vendored
View File

@@ -1,2 +0,0 @@
*
!.gitignore

View File

@@ -12,6 +12,8 @@ const egInput1 = [
[4, 5, 6]
]
// Test default blueprint exports
// Smoke tests
test('defaultBlueprint exports', t => {
const expected = {
sheet: {

View File

@@ -54,14 +54,7 @@ test('should launch', t => {
})
const passUrls = [
// /
'/api/',
// /blueprints
'/api/blueprints',
// /:sheet/:tab/:resource
'/api/example/export_events/rows',
// /:sheet/:tab/:resource/:frag
'/api/example/export_events/rows/1'
'/api/'
]
const failUrls = [
@@ -77,6 +70,7 @@ passUrls.forEach(function (url) {
.then(checkStatus)
.then(res => res.json())
.then(json => {
console.info('JSON: ', json)
t.pass()
})
})

91
views/blueprints.hbs Normal file
View File

@@ -0,0 +1,91 @@
<h1>Available Endpoints</h1>
<hr>
<div class="main-container">
{{#each bps}}
<div class="blueprint-container">
<div class="bp-header">
<div class="bp-tab">{{ tab }}</div>
<div class="bp-source">{{ source }}</div>
</div>
{{#each urls}}
<div><a target="_blank" href="http://localhost:4040{{ this }}">{{ this }}</a></div>
{{/each}}
</div>
{{ else }}
<div>No endpoints found. Have you updated?</div>
{{/each}}
</div>
<a class="bp-update-container" target="_blank" href="http://localhost:4040/api/update">
<div class="bp-button">Update</div>
</div>
<style>
:root {
--grey: #8a8a8a;
--btn-width: 200px;
}
.main-container {
display: flex;
flex-wrap: wrap;
}
.blueprint-container {
display: flex;
flex: 1 0 31%;
flex-direction: column;
border: 1px solid var(--grey);
padding: 1em;
margin: 1em;
}
.bp-header {
display: flex;
flex-direction: row;
justify-content: space-between;
}
.bp-tab {
font-size: 24pt;
}
.bp-source {
color: var(--grey);
}
.bp-update-container {
position: fixed;
top: 2px;
right: 2em;
display: flex;
justify-content: center;
align-items: center;
}
.bp-button {
transition: all 0.3s ease;
text-transform: uppercase;
font-size: 24pt;
font-weight: bold;
width: var(--btn-width);
display: flex;
justify-content: center;
align-items: center;
border: 3px solid black;
padding: .5em;
text-decoration: none;
text-underline: none;
color: black;
}
.bp-update-container:hover {
background-color: black;
transition: all 0.3s ease;
cursor: pointer;
}
.bp-update-container:hover div {
color: white;
}
</style>

15
views/layouts/default.hbs Normal file
View File

@@ -0,0 +1,15 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<title>Datasheet Server</title>
<meta name="HandheldFriendly" content="True" />
<meta name="viewport" content="width=device-width, initial-scale=1.0, viewport-fit=cover" />
</head>
<body>
<div class="container">
{{{body}}}
</div>
</body>
</html>

4502
yarn.lock

File diff suppressed because it is too large Load Diff