├── app ├── utils │ ├── .gitkeep │ ├── schema.js │ └── handlers.js ├── app.icns ├── package.json ├── components │ ├── Log │ │ ├── Log.css │ │ └── index.js │ ├── Sync │ │ └── Sync.css │ ├── SavedSync │ │ └── SavedSync.css │ ├── Taps │ │ ├── TapConfiguration │ │ │ ├── S3 │ │ │ │ └── S3.css │ │ │ ├── index.js │ │ │ └── IncrementalSyncToggle │ │ │ │ └── index.js │ │ └── Tap │ │ │ └── index.js │ ├── Header │ │ └── index.js │ ├── Targets │ │ ├── TargetConfiguration │ │ │ ├── index.js │ │ │ └── Stitch │ │ │ │ └── index.js │ │ └── Target │ │ │ └── index.js │ ├── Schema │ │ ├── Checkbox │ │ │ └── index.js │ │ └── Dropdown │ │ │ └── index.js │ ├── Home │ │ ├── Create │ │ │ └── index.js │ │ └── Knots │ │ │ └── index.js │ └── KnotProgress │ │ ├── index.js │ │ └── Item │ │ └── index.js ├── img │ ├── tap-adwords.svg │ ├── target-stitch.svg │ ├── tap-facebook.svg │ ├── tap-redshift.svg │ ├── tap-s3-csv.svg │ ├── knots.svg │ └── tap-mysql.svg ├── .eslintrc ├── store │ ├── configureStore.js │ ├── configureStore.prod.js │ └── configureStore.dev.js ├── containers │ ├── App.js │ ├── HomePage.js │ ├── Knots.js │ ├── Adwords.js │ ├── S3.js │ ├── Redshift.js │ ├── Facebook.js │ ├── Salesforce.js │ ├── Postgres.js │ ├── Stitch.js │ ├── DataWorld.js │ ├── TapConfiguration.js │ ├── TargetConfiguration.js │ ├── Root.js │ ├── Sync.js │ ├── MySQL.js │ ├── SavedSync.js │ ├── Taps.js │ ├── Schema.js │ ├── KnotProgress.js │ └── Targets.js ├── backend │ ├── targets.js │ ├── routes │ │ ├── index.js │ │ ├── docker.js │ │ ├── taps.js │ │ ├── targets.js │ │ └── knots.js │ ├── index.js │ └── docker.js ├── actions │ ├── progress.js │ ├── user.js │ ├── connect.js │ └── targets.js ├── reducers │ ├── index.js │ ├── targets.js │ ├── user.js │ ├── progress.js │ └── knots.js ├── logos.js ├── index.js ├── routes.js ├── app.global.scss ├── app.html └── electron-oauth.js ├── internals ├── mocks │ └── fileMock.js ├── flow │ ├── WebpackAsset.js.flow │ └── CSSModule.js.flow ├── img │ ├── js.png │ ├── npm.png │ ├── flow.png │ ├── jest.png │ ├── react.png │ ├── redux.png │ ├── yarn.png │ ├── eslint.png │ ├── webpack.png │ ├── js-padded.png │ ├── eslint-padded.png │ ├── flow-padded.png │ ├── jest-padded.png │ ├── react-padded.png │ ├── react-router.png │ ├── redux-padded.png │ ├── yarn-padded.png │ ├── flow-padded-90.png │ ├── jest-padded-90.png │ ├── react-padded-90.png │ ├── redux-padded-90.png │ ├── webpack-padded.png │ ├── yarn-padded-90.png │ ├── eslint-padded-90.png │ ├── webpack-padded-90.png │ ├── react-router-padded.png │ └── react-router-padded-90.png └── scripts │ ├── CheckNodeEnv.js │ ├── CheckPortInUse.js │ ├── ElectronRebuild.js │ └── CheckNativeDep.js ├── .stylelintrc ├── .prettierrc.json ├── resources ├── icon.ico ├── icon.png ├── icon.icns └── icons │ ├── 16x16.png │ ├── 24x24.png │ ├── 32x32.png │ ├── 48x48.png │ ├── 64x64.png │ ├── 96x96.png │ ├── 128x128.png │ ├── 256x256.png │ ├── 512x512.png │ └── 1024x1024.png ├── flow-typed └── module_vx.x.x.js ├── .gitattributes ├── webpack.config.eslint.js ├── docker └── images │ ├── tap-s3-csv │ └── Dockerfile │ ├── tap-facebook │ └── Dockerfile │ ├── tap-salesforce │ └── Dockerfile │ ├── tap-adwords │ └── Dockerfile │ ├── tap-mysql │ └── Dockerfile │ ├── target-stitch │ └── Dockerfile │ └── tap-postgres │ └── Dockerfile ├── .github └── pull_request_template.md ├── .editorconfig ├── test ├── .eslintrc ├── backend │ ├── targets.spec.js │ └── docker.spec.js ├── actions │ └── user.actions.spec.js ├── reducers │ ├── targets.reducer.spec.js │ ├── user.reducer.spec.js │ └── progress.reducer.spec.js ├── utils.js ├── utils │ └── schema.spec.js ├── components │ └── Home.spec.js └── samples.js ├── .vscode └── settings.json ├── .babelrc ├── .flowconfig ├── .eslintignore ├── .gitignore ├── webpack.config.base.js ├── .eslintrc ├── webpack.config.main.prod.js ├── .circleci └── config.yml └── README.md /app/utils/.gitkeep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /internals/mocks/fileMock.js: -------------------------------------------------------------------------------- 1 | export default 'test-file-stub'; 2 | -------------------------------------------------------------------------------- /.stylelintrc: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "stylelint-config-standard" 3 | } 4 | -------------------------------------------------------------------------------- /app/app.icns: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/singer-io/knots/HEAD/app/app.icns -------------------------------------------------------------------------------- /internals/flow/WebpackAsset.js.flow: -------------------------------------------------------------------------------- 1 | // @flow 2 | declare export default string 3 | -------------------------------------------------------------------------------- /.prettierrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "singleQuote": true, 3 | "arrowParens": "always" 4 | } 5 | -------------------------------------------------------------------------------- /resources/icon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/singer-io/knots/HEAD/resources/icon.ico -------------------------------------------------------------------------------- /resources/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/singer-io/knots/HEAD/resources/icon.png -------------------------------------------------------------------------------- /internals/img/js.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/singer-io/knots/HEAD/internals/img/js.png -------------------------------------------------------------------------------- /internals/img/npm.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/singer-io/knots/HEAD/internals/img/npm.png -------------------------------------------------------------------------------- /resources/icon.icns: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/singer-io/knots/HEAD/resources/icon.icns -------------------------------------------------------------------------------- /flow-typed/module_vx.x.x.js: -------------------------------------------------------------------------------- 1 | declare module 'module' { 2 | declare module.exports: any; 3 | } 4 | -------------------------------------------------------------------------------- /internals/flow/CSSModule.js.flow: -------------------------------------------------------------------------------- 1 | // @flow 2 | 3 | declare export default { [key: string]: string } -------------------------------------------------------------------------------- /internals/img/flow.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/singer-io/knots/HEAD/internals/img/flow.png -------------------------------------------------------------------------------- /internals/img/jest.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/singer-io/knots/HEAD/internals/img/jest.png -------------------------------------------------------------------------------- /internals/img/react.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/singer-io/knots/HEAD/internals/img/react.png -------------------------------------------------------------------------------- /internals/img/redux.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/singer-io/knots/HEAD/internals/img/redux.png -------------------------------------------------------------------------------- /internals/img/yarn.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/singer-io/knots/HEAD/internals/img/yarn.png -------------------------------------------------------------------------------- /internals/img/eslint.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/singer-io/knots/HEAD/internals/img/eslint.png -------------------------------------------------------------------------------- /internals/img/webpack.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/singer-io/knots/HEAD/internals/img/webpack.png -------------------------------------------------------------------------------- /resources/icons/16x16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/singer-io/knots/HEAD/resources/icons/16x16.png -------------------------------------------------------------------------------- /resources/icons/24x24.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/singer-io/knots/HEAD/resources/icons/24x24.png -------------------------------------------------------------------------------- /resources/icons/32x32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/singer-io/knots/HEAD/resources/icons/32x32.png -------------------------------------------------------------------------------- /resources/icons/48x48.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/singer-io/knots/HEAD/resources/icons/48x48.png -------------------------------------------------------------------------------- /resources/icons/64x64.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/singer-io/knots/HEAD/resources/icons/64x64.png -------------------------------------------------------------------------------- /resources/icons/96x96.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/singer-io/knots/HEAD/resources/icons/96x96.png -------------------------------------------------------------------------------- /internals/img/js-padded.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/singer-io/knots/HEAD/internals/img/js-padded.png -------------------------------------------------------------------------------- /resources/icons/128x128.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/singer-io/knots/HEAD/resources/icons/128x128.png -------------------------------------------------------------------------------- /resources/icons/256x256.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/singer-io/knots/HEAD/resources/icons/256x256.png -------------------------------------------------------------------------------- /resources/icons/512x512.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/singer-io/knots/HEAD/resources/icons/512x512.png -------------------------------------------------------------------------------- /internals/img/eslint-padded.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/singer-io/knots/HEAD/internals/img/eslint-padded.png -------------------------------------------------------------------------------- /internals/img/flow-padded.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/singer-io/knots/HEAD/internals/img/flow-padded.png -------------------------------------------------------------------------------- /internals/img/jest-padded.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/singer-io/knots/HEAD/internals/img/jest-padded.png -------------------------------------------------------------------------------- /internals/img/react-padded.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/singer-io/knots/HEAD/internals/img/react-padded.png -------------------------------------------------------------------------------- /internals/img/react-router.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/singer-io/knots/HEAD/internals/img/react-router.png -------------------------------------------------------------------------------- /internals/img/redux-padded.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/singer-io/knots/HEAD/internals/img/redux-padded.png -------------------------------------------------------------------------------- /internals/img/yarn-padded.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/singer-io/knots/HEAD/internals/img/yarn-padded.png -------------------------------------------------------------------------------- /resources/icons/1024x1024.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/singer-io/knots/HEAD/resources/icons/1024x1024.png -------------------------------------------------------------------------------- /internals/img/flow-padded-90.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/singer-io/knots/HEAD/internals/img/flow-padded-90.png -------------------------------------------------------------------------------- /internals/img/jest-padded-90.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/singer-io/knots/HEAD/internals/img/jest-padded-90.png -------------------------------------------------------------------------------- /internals/img/react-padded-90.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/singer-io/knots/HEAD/internals/img/react-padded-90.png -------------------------------------------------------------------------------- /internals/img/redux-padded-90.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/singer-io/knots/HEAD/internals/img/redux-padded-90.png -------------------------------------------------------------------------------- /internals/img/webpack-padded.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/singer-io/knots/HEAD/internals/img/webpack-padded.png -------------------------------------------------------------------------------- /internals/img/yarn-padded-90.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/singer-io/knots/HEAD/internals/img/yarn-padded-90.png -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | * text eol=lf 2 | *.png binary 3 | *.jpg binary 4 | *.jpeg binary 5 | *.ico binary 6 | *.icns binary 7 | -------------------------------------------------------------------------------- /internals/img/eslint-padded-90.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/singer-io/knots/HEAD/internals/img/eslint-padded-90.png -------------------------------------------------------------------------------- /internals/img/webpack-padded-90.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/singer-io/knots/HEAD/internals/img/webpack-padded-90.png -------------------------------------------------------------------------------- /webpack.config.eslint.js: -------------------------------------------------------------------------------- 1 | require('babel-register'); 2 | 3 | module.exports = require('./webpack.config.renderer.dev'); 4 | -------------------------------------------------------------------------------- /internals/img/react-router-padded.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/singer-io/knots/HEAD/internals/img/react-router-padded.png -------------------------------------------------------------------------------- /internals/img/react-router-padded-90.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/singer-io/knots/HEAD/internals/img/react-router-padded-90.png -------------------------------------------------------------------------------- /docker/images/tap-s3-csv/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM python:3.6-alpine 2 | ARG version 3 | RUN pip install tap-s3-csv==${version} 4 | WORKDIR /app 5 | CMD ["tap-s3-csv"] 6 | -------------------------------------------------------------------------------- /docker/images/tap-facebook/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM python:3.6-alpine 2 | ARG version 3 | RUN pip install tap-facebook==${version} 4 | WORKDIR /app 5 | CMD ["tap-facebook"] 6 | -------------------------------------------------------------------------------- /docker/images/tap-salesforce/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM python:3.6-alpine 2 | ARG version 3 | RUN pip install tap-salesforce==${version} 4 | WORKDIR /app 5 | CMD ["tap-salesforce"] 6 | -------------------------------------------------------------------------------- /.github/pull_request_template.md: -------------------------------------------------------------------------------- 1 | # Description of change 2 | (write a short description or paste a link to JIRA) 3 | 4 | # Manual QA steps 5 | - 6 | 7 | # Risks 8 | - 9 | 10 | # Rollback steps 11 | - revert this branch 12 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*] 4 | indent_style = space 5 | indent_size = 2 6 | end_of_line = lf 7 | charset = utf-8 8 | trim_trailing_whitespace = true 9 | insert_final_newline = true 10 | 11 | [*.md] 12 | trim_trailing_whitespace = false 13 | -------------------------------------------------------------------------------- /docker/images/tap-adwords/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM python:3.6-alpine 2 | ARG version 3 | RUN apk update && \ 4 | apk add --virtual .build-deps gcc musl-dev linux-headers libxml2-dev libxslt-dev && \ 5 | pip install tap-adwords==${version} 6 | 7 | WORKDIR /app 8 | CMD ["tap-adwords"] -------------------------------------------------------------------------------- /test/.eslintrc: -------------------------------------------------------------------------------- 1 | { 2 | "env": { 3 | "jest/globals": true 4 | }, 5 | "plugins": [ 6 | "jest" 7 | ], 8 | "rules": { 9 | "jest/no-disabled-tests": "warn", 10 | "jest/no-focused-tests": "error", 11 | "jest/no-identical-title": "error" 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /docker/images/tap-mysql/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM python:3.6-alpine 2 | ARG version 3 | RUN apk update && \ 4 | apk add --virtual .build-deps gcc musl-dev linux-headers && \ 5 | pip install tap-mysql==${version} && \ 6 | apk --purge del .build-deps 7 | 8 | WORKDIR /app 9 | CMD ["tap-mysql"] -------------------------------------------------------------------------------- /docker/images/target-stitch/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM python:3.6-alpine 2 | ARG version 3 | RUN apk update && \ 4 | apk add --virtual .build-deps gcc musl-dev linux-headers && \ 5 | pip install target-stitch==${version} && \ 6 | apk --purge del .build-deps 7 | 8 | WORKDIR /app 9 | CMD ["target-stitch"] 10 | -------------------------------------------------------------------------------- /docker/images/tap-postgres/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM python:3.6-alpine 2 | ARG version 3 | RUN apk update && \ 4 | apk add postgresql-libs && \ 5 | apk add --virtual .build-deps gcc musl-dev postgresql-dev && \ 6 | pip install tap-postgres==${version} --no-cache-dir && \ 7 | apk --purge del .build-deps 8 | 9 | WORKDIR /app 10 | CMD ["tap-postgres"] -------------------------------------------------------------------------------- /internals/scripts/CheckNodeEnv.js: -------------------------------------------------------------------------------- 1 | // @flow 2 | import chalk from 'chalk'; 3 | 4 | export default function CheckNodeEnv(expectedEnv: string) { 5 | if (!expectedEnv) { 6 | throw new Error('"expectedEnv" not set'); 7 | } 8 | 9 | if (process.env.NODE_ENV !== expectedEnv) { 10 | console.log( 11 | chalk.whiteBright.bgRed.bold( 12 | `"process.env.NODE_ENV" must be "${expectedEnv}" to use this webpack config` 13 | ) 14 | ); 15 | process.exit(2); 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /test/backend/targets.spec.js: -------------------------------------------------------------------------------- 1 | import { getTargets } from '../../app/backend/targets'; 2 | import { targets } from '../../app/backend/constants'; 3 | 4 | describe('targets functions', () => { 5 | describe('get targets', () => { 6 | it('should return a list of available targets', () => { 7 | getTargets() 8 | .then((res) => { 9 | expect(res).toEqual(targets); 10 | }) 11 | .catch((err) => { 12 | expect(err).toBeUndefined(); 13 | }); 14 | }); 15 | }); 16 | }); 17 | -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "javascript.validate.enable": false, 3 | "flow.useNPMPackagedFlow": true, 4 | "search.exclude": { 5 | ".git": true, 6 | ".eslintcache": true, 7 | "app/dist": true, 8 | "app/main.prod.js": true, 9 | "app/main.prod.js.map": true, 10 | "bower_components": true, 11 | "dll": true, 12 | "flow-typed": true, 13 | "release": true, 14 | "node_modules": true, 15 | "npm-debug.log.*": true, 16 | "test/**/__snapshots__": true, 17 | "yarn.lock": true 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "presets": [ 3 | ["env", { 4 | "targets": { "node": 7 }, 5 | "useBuiltIns": true 6 | }], 7 | "stage-0", 8 | "react" 9 | ], 10 | "plugins": ["add-module-exports"], 11 | "env": { 12 | "production": { 13 | "presets": ["react-optimize"], 14 | "plugins": ["dev-expression"] 15 | }, 16 | "development": { 17 | "plugins": [ 18 | "transform-class-properties", 19 | "transform-es2015-classes", 20 | ["flow-runtime", { 21 | "assert": true, 22 | "annotate": true 23 | }] 24 | ] 25 | } 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /app/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "knots", 3 | "version": "1.0.0-beta.7", 4 | "description": "This app allows you to configure and download executable Singer pipelines", 5 | "main": "./main.prod.js", 6 | "scripts": { 7 | "electron-rebuild": "node -r babel-register ../internals/scripts/ElectronRebuild.js", 8 | "postinstall": "npm run electron-rebuild" 9 | }, 10 | "license": "Apache-2.0", 11 | "author": { 12 | "name": "Stitch, Inc.", 13 | "email": "hello@singer.io" 14 | }, 15 | "dependencies": { 16 | "bufferutil": "^3.0.4", 17 | "socket.io": "^2.1.0", 18 | "utf-8-validate": "^4.0.1" 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /internals/scripts/CheckPortInUse.js: -------------------------------------------------------------------------------- 1 | // @flow 2 | /* eslint-disable */ 3 | import chalk from 'chalk'; 4 | import detectPort from 'detect-port'; 5 | 6 | (function CheckPortInUse() { 7 | const port: string = process.env.PORT || '1212'; 8 | 9 | detectPort(port, (err: ?Error, availablePort: number) => { 10 | if (port !== String(availablePort)) { 11 | throw new Error( 12 | chalk.whiteBright.bgRed.bold( 13 | `Port "${port}" on "localhost" is already in use. Please use another port. ex: PORT=4343 npm run dev` 14 | ) 15 | ); 16 | } else { 17 | process.exit(0); 18 | } 19 | }); 20 | })(); 21 | -------------------------------------------------------------------------------- /internals/scripts/ElectronRebuild.js: -------------------------------------------------------------------------------- 1 | // @flow 2 | import path from 'path'; 3 | import { execSync } from 'child_process'; 4 | import fs from 'fs'; 5 | import dependencies from '../../app/package.json'; 6 | 7 | const nodeModulesPath = path.join(__dirname, '..', '..', 'app', 'node_modules'); 8 | 9 | if ( 10 | Object.keys(dependencies || {}).length > 0 && 11 | fs.existsSync(nodeModulesPath) 12 | ) { 13 | const electronRebuildCmd = 14 | '../node_modules/.bin/electron-rebuild --parallel --force --types prod,dev,optional --module-dir .'; 15 | 16 | const cmd = 17 | process.platform === 'win32' 18 | ? electronRebuildCmd.replace(/\//g, '\\') 19 | : electronRebuildCmd; 20 | 21 | execSync(cmd, { 22 | cwd: path.join(__dirname, '..', '..', 'app') 23 | }); 24 | } 25 | -------------------------------------------------------------------------------- /app/components/Log/Log.css: -------------------------------------------------------------------------------- 1 | /* 2 | * knots 3 | * Copyright 2018 data.world, Inc. 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the 7 | * License. 8 | * 9 | * You may obtain a copy of the License at 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, software 13 | * distributed under the License is distributed on an "AS IS" BASIS, 14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 15 | * implied. See the License for the specific language governing 16 | * permissions and limitations under the License. 17 | * 18 | * This product includes software developed at 19 | * data.world, Inc.(http://data.world/). 20 | */ 21 | 22 | .wrap { 23 | white-space: pre-wrap; 24 | } 25 | -------------------------------------------------------------------------------- /test/actions/user.actions.spec.js: -------------------------------------------------------------------------------- 1 | import configureStore from 'redux-mock-store'; 2 | import thunk from 'redux-thunk'; 3 | 4 | import * as userActions from '../../app/actions/user'; 5 | 6 | const middlewares = [thunk]; 7 | const mockStore = configureStore(middlewares); 8 | 9 | describe('user actions', () => { 10 | it('should dispatch UPDATE_TARGET_FIELD', () => { 11 | const store = mockStore({}); 12 | const target = 'target-datadotworld'; 13 | const field = 'dataset_owner'; 14 | const value = 'tester'; 15 | const expectedActions = [ 16 | { 17 | type: userActions.UPDATE_TARGET_FIELD, 18 | target, 19 | field, 20 | value 21 | } 22 | ]; 23 | 24 | store.dispatch(userActions.updateTargetField(target, field, value)); 25 | expect(store.getActions()).toEqual(expectedActions); 26 | }); 27 | }); 28 | -------------------------------------------------------------------------------- /app/img/tap-adwords.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 5 | 10 | 11 | 12 | 14 | 15 | -------------------------------------------------------------------------------- /app/img/target-stitch.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 5 | 6 | 10 | 11 | -------------------------------------------------------------------------------- /app/components/Sync/Sync.css: -------------------------------------------------------------------------------- 1 | /* 2 | * knots 3 | * Copyright 2018 data.world, Inc. 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the 7 | * License. 8 | * 9 | * You may obtain a copy of the License at 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, software 13 | * distributed under the License is distributed on an "AS IS" BASIS, 14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 15 | * implied. See the License for the specific language governing 16 | * permissions and limitations under the License. 17 | * 18 | * This product includes software developed at 19 | * data.world, Inc.(http://data.world/). 20 | */ 21 | 22 | .logo { 23 | width: 40px; 24 | height: 40px; 25 | mix-blend-mode: multiply; 26 | } 27 | -------------------------------------------------------------------------------- /app/components/SavedSync/SavedSync.css: -------------------------------------------------------------------------------- 1 | /* 2 | * knots 3 | * Copyright 2018 data.world, Inc. 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the 7 | * License. 8 | * 9 | * You may obtain a copy of the License at 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, software 13 | * distributed under the License is distributed on an "AS IS" BASIS, 14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 15 | * implied. See the License for the specific language governing 16 | * permissions and limitations under the License. 17 | * 18 | * This product includes software developed at 19 | * data.world, Inc.(http://data.world/). 20 | */ 21 | 22 | .logo { 23 | width: 40px; 24 | height: 40px; 25 | mix-blend-mode: multiply; 26 | } 27 | -------------------------------------------------------------------------------- /app/.eslintrc: -------------------------------------------------------------------------------- 1 | { 2 | "rules": { 3 | "flowtype/boolean-style": ["error", "boolean"], 4 | "flowtype/define-flow-type": "warn", 5 | "flowtype/delimiter-dangle": ["error", "never"], 6 | "flowtype/generic-spacing": ["error", "never"], 7 | "flowtype/no-primitive-constructor-types": "error", 8 | "flowtype/no-weak-types": "warn", 9 | "flowtype/object-type-delimiter": ["error", "comma"], 10 | "flowtype/require-parameter-type": "off", 11 | "flowtype/require-return-type": "off", 12 | "flowtype/require-valid-file-annotation": "off", 13 | "flowtype/semi": ["error", "always"], 14 | "flowtype/space-after-type-colon": ["error", "always"], 15 | "flowtype/space-before-generic-bracket": ["error", "never"], 16 | "flowtype/space-before-type-colon": ["error", "never"], 17 | "flowtype/union-intersection-spacing": ["error", "always"], 18 | "flowtype/use-flow-type": "error", 19 | "flowtype/valid-syntax": "error" 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /.flowconfig: -------------------------------------------------------------------------------- 1 | [ignore] 2 | /node_modules/* 3 | /app/main.prod.js 4 | /app/main.prod.js.map 5 | /app/dist/.* 6 | /resources/.* 7 | /release/.* 8 | /dll/.* 9 | /release/.* 10 | /git/.* 11 | 12 | [include] 13 | 14 | [libs] 15 | 16 | [options] 17 | esproposal.class_static_fields=enable 18 | esproposal.class_instance_fields=enable 19 | esproposal.export_star_as=enable 20 | module.name_mapper.extension='css' -> '/internals/flow/CSSModule.js.flow' 21 | module.name_mapper.extension='styl' -> '/internals/flow/CSSModule.js.flow' 22 | module.name_mapper.extension='scss' -> '/internals/flow/CSSModule.js.flow' 23 | module.name_mapper.extension='png' -> '/internals/flow/WebpackAsset.js.flow' 24 | module.name_mapper.extension='jpg' -> '/internals/flow/WebpackAsset.js.flow' 25 | suppress_comment=\\(.\\|\n\\)*\\$FlowFixMe 26 | suppress_comment=\\(.\\|\n\\)*\\$FlowIssue 27 | -------------------------------------------------------------------------------- /app/img/tap-facebook.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 7 | 10 | 13 | 14 | -------------------------------------------------------------------------------- /app/store/configureStore.js: -------------------------------------------------------------------------------- 1 | /* 2 | * knots 3 | * Copyright 2018 data.world, Inc. 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the 7 | * License. 8 | * 9 | * You may obtain a copy of the License at 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, software 13 | * distributed under the License is distributed on an "AS IS" BASIS, 14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 15 | * implied. See the License for the specific language governing 16 | * permissions and limitations under the License. 17 | * 18 | * This product includes software developed at 19 | * data.world, Inc.(http://data.world/). 20 | */ 21 | 22 | // @flow 23 | 24 | if (process.env.NODE_ENV === 'production') { 25 | module.exports = require('./configureStore.prod'); // eslint-disable-line global-require 26 | } else { 27 | module.exports = require('./configureStore.dev'); // eslint-disable-line global-require 28 | } 29 | -------------------------------------------------------------------------------- /.eslintignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | 5 | # Runtime data 6 | pids 7 | *.pid 8 | *.seed 9 | 10 | # Directory for instrumented libs generated by jscoverage/JSCover 11 | lib-cov 12 | 13 | # Coverage directory used by tools like istanbul 14 | coverage 15 | 16 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) 17 | .grunt 18 | 19 | # node-waf configuration 20 | .lock-wscript 21 | 22 | # Compiled binary addons (http://nodejs.org/api/addons.html) 23 | build/Release 24 | .eslintcache 25 | 26 | # Dependency directory 27 | # https://www.npmjs.org/doc/misc/npm-faq.html#should-i-check-my-node_modules-folder-into-git 28 | node_modules 29 | app/node_modules 30 | 31 | # OSX 32 | .DS_Store 33 | 34 | # flow-typed 35 | flow-typed/npm/* 36 | !flow-typed/npm/module_vx.x.x.js 37 | 38 | # App packaged 39 | release 40 | app/main.prod.js 41 | app/main.prod.js.map 42 | app/renderer.prod.js 43 | app/renderer.prod.js.map 44 | app/style.css 45 | app/style.css.map 46 | dist 47 | dll 48 | main.js 49 | main.js.map 50 | 51 | .idea 52 | npm-debug.log.* 53 | __snapshots__ 54 | -------------------------------------------------------------------------------- /app/containers/App.js: -------------------------------------------------------------------------------- 1 | /* 2 | * knots 3 | * Copyright 2018 data.world, Inc. 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the 7 | * License. 8 | * 9 | * You may obtain a copy of the License at 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, software 13 | * distributed under the License is distributed on an "AS IS" BASIS, 14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 15 | * implied. See the License for the specific language governing 16 | * permissions and limitations under the License. 17 | * 18 | * This product includes software developed at 19 | * data.world, Inc.(http://data.world/). 20 | */ 21 | 22 | // @flow 23 | 24 | import * as React from 'react'; 25 | 26 | type Props = { 27 | children: React.Node 28 | }; 29 | 30 | export default class App extends React.Component { 31 | props: Props; 32 | 33 | render() { 34 | return
{this.props.children}
; 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /app/img/tap-redshift.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | -------------------------------------------------------------------------------- /app/backend/targets.js: -------------------------------------------------------------------------------- 1 | /* 2 | * knots 3 | * Copyright 2018 data.world, Inc. 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the 7 | * License. 8 | * 9 | * You may obtain a copy of the License at 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, software 13 | * distributed under the License is distributed on an "AS IS" BASIS, 14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 15 | * implied. See the License for the specific language governing 16 | * permissions and limitations under the License. 17 | * 18 | * This product includes software developed at 19 | * data.world, Inc.(http://data.world/). 20 | */ 21 | 22 | const { targets } = require('./constants'); 23 | 24 | const getTargets = () => 25 | new Promise((resolve, reject) => { 26 | if (targets) { 27 | resolve(targets); 28 | } else { 29 | reject(new Error('No targets available')); 30 | } 31 | }); 32 | 33 | module.exports = { getTargets }; 34 | -------------------------------------------------------------------------------- /app/actions/progress.js: -------------------------------------------------------------------------------- 1 | /* 2 | * knots 3 | * Copyright 2018 data.world, Inc. 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the 7 | * License. 8 | * 9 | * You may obtain a copy of the License at 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, software 13 | * distributed under the License is distributed on an "AS IS" BASIS, 14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 15 | * implied. See the License for the specific language governing 16 | * permissions and limitations under the License. 17 | * 18 | * This product includes software developed at 19 | * data.world, Inc.(http://data.world/). 20 | */ 21 | 22 | export function updateProgress() { 23 | return (dispatch: (action: actionType) => void) => { 24 | dispatch({ 25 | type: 'SCHEMA_LOADING' 26 | }); 27 | }; 28 | } 29 | 30 | export function update() { 31 | return (dispatch: (action: actionType) => void) => { 32 | dispatch({ 33 | type: 'SCHEMA_LOADING' 34 | }); 35 | }; 36 | } 37 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | 5 | # Runtime data 6 | pids 7 | *.pid 8 | *.seed 9 | 10 | # Directory for instrumented libs generated by jscoverage/JSCover 11 | lib-cov 12 | 13 | # Coverage directory used by tools like istanbul 14 | coverage 15 | 16 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) 17 | .grunt 18 | 19 | # node-waf configuration 20 | .lock-wscript 21 | 22 | # Compiled binary addons (http://nodejs.org/api/addons.html) 23 | build/Release 24 | .eslintcache 25 | 26 | # Dependency directory 27 | # https://www.npmjs.org/doc/misc/npm-faq.html#should-i-check-my-node_modules-folder-into-git 28 | node_modules 29 | app/node_modules 30 | 31 | # OSX 32 | .DS_Store 33 | 34 | # flow-typed 35 | flow-typed/npm/* 36 | !flow-typed/npm/module_vx.x.x.js 37 | 38 | # App packaged 39 | release 40 | app/main.prod.js 41 | app/main.prod.js.map 42 | app/renderer.prod.js 43 | app/renderer.prod.js.map 44 | app/style.css 45 | app/style.css.map 46 | dist 47 | dll 48 | main.js 49 | main.js.map 50 | 51 | *.iml 52 | .env 53 | npm-debug.log.* 54 | 55 | # IDE files 56 | *.iml 57 | .idea 58 | 59 | # Knot temp files 60 | tmp 61 | /knots 62 | -------------------------------------------------------------------------------- /app/img/tap-s3-csv.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /webpack.config.base.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Base webpack config used across other specific configs 3 | */ 4 | 5 | import path from 'path'; 6 | import webpack from 'webpack'; 7 | import { dependencies as externals } from './app/package.json'; 8 | 9 | export default { 10 | externals: Object.keys(externals || {}), 11 | 12 | module: { 13 | rules: [ 14 | { 15 | test: /\.jsx?$/, 16 | exclude: /node_modules/, 17 | use: { 18 | loader: 'babel-loader', 19 | options: { 20 | cacheDirectory: true 21 | } 22 | } 23 | } 24 | ] 25 | }, 26 | 27 | output: { 28 | path: path.join(__dirname, 'app'), 29 | // https://github.com/webpack/webpack/issues/1114 30 | libraryTarget: 'commonjs2' 31 | }, 32 | 33 | /** 34 | * Determine the array of extensions that should be used to resolve modules. 35 | */ 36 | resolve: { 37 | extensions: ['.js', '.jsx', '.json'], 38 | modules: [path.join(__dirname, 'app'), 'node_modules'] 39 | }, 40 | 41 | plugins: [ 42 | new webpack.EnvironmentPlugin({ 43 | NODE_ENV: 'production' 44 | }), 45 | 46 | new webpack.NamedModulesPlugin() 47 | ] 48 | }; 49 | -------------------------------------------------------------------------------- /app/components/Taps/TapConfiguration/S3/S3.css: -------------------------------------------------------------------------------- 1 | /* 2 | * knots 3 | * Copyright 2018 data.world, Inc. 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the 7 | * License. 8 | * 9 | * You may obtain a copy of the License at 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, software 13 | * distributed under the License is distributed on an "AS IS" BASIS, 14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 15 | * implied. See the License for the specific language governing 16 | * permissions and limitations under the License. 17 | * 18 | * This product includes software developed at 19 | * data.world, Inc.(http://data.world/). 20 | */ 21 | 22 | .tag-badge { 23 | margin-right: 2px; 24 | } 25 | 26 | .tag-badge a::before { 27 | content: ' ×'; 28 | } 29 | 30 | .tag-remove { 31 | cursor: pointer; 32 | font-weight: bold; 33 | } 34 | 35 | .transparent-input { 36 | background: transparent; 37 | border: 0; 38 | width: 50%; 39 | margin: 0; 40 | padding: 0; 41 | outline: none; 42 | font-size: 0.75rem; 43 | } 44 | -------------------------------------------------------------------------------- /app/backend/routes/index.js: -------------------------------------------------------------------------------- 1 | /* 2 | * knots 3 | * Copyright 2018 data.world, Inc. 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the 7 | * License. 8 | * 9 | * You may obtain a copy of the License at 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, software 13 | * distributed under the License is distributed on an "AS IS" BASIS, 14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 15 | * implied. See the License for the specific language governing 16 | * permissions and limitations under the License. 17 | * 18 | * This product includes software developed at 19 | * data.world, Inc.(http://data.world/). 20 | */ 21 | 22 | const dockerRoutes = require('./docker'); 23 | const knotsRoutes = require('./knots'); 24 | const tapsRoutes = require('./taps'); 25 | const targetsRoutes = require('./targets'); 26 | 27 | const router = (app) => { 28 | app.use('/docker', dockerRoutes); 29 | app.use('/knots', knotsRoutes); 30 | app.use('/taps', tapsRoutes); 31 | app.use('/targets', targetsRoutes); 32 | }; 33 | 34 | module.exports = router; 35 | -------------------------------------------------------------------------------- /app/actions/user.js: -------------------------------------------------------------------------------- 1 | /* 2 | * knots 3 | * Copyright 2018 data.world, Inc. 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the 7 | * License. 8 | * 9 | * You may obtain a copy of the License at 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, software 13 | * distributed under the License is distributed on an "AS IS" BASIS, 14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 15 | * implied. See the License for the specific language governing 16 | * permissions and limitations under the License. 17 | * 18 | * This product includes software developed at 19 | * data.world, Inc.(http://data.world/). 20 | */ 21 | 22 | // @flow 23 | 24 | export const UPDATE_TARGET_FIELD = 'UPDATE_TARGET_FIELD'; 25 | 26 | type actionType = { 27 | +type: string 28 | }; 29 | 30 | export function updateTargetField( 31 | target: string, 32 | field: string, 33 | value: string 34 | ) { 35 | return (dispatch: (action: actionType) => void) => { 36 | dispatch({ 37 | type: UPDATE_TARGET_FIELD, 38 | target, 39 | field, 40 | value 41 | }); 42 | }; 43 | } 44 | -------------------------------------------------------------------------------- /app/containers/HomePage.js: -------------------------------------------------------------------------------- 1 | /* 2 | * knots 3 | * Copyright 2018 data.world, Inc. 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the 7 | * License. 8 | * 9 | * You may obtain a copy of the License at 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, software 13 | * distributed under the License is distributed on an "AS IS" BASIS, 14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 15 | * implied. See the License for the specific language governing 16 | * permissions and limitations under the License. 17 | * 18 | * This product includes software developed at 19 | * data.world, Inc.(http://data.world/). 20 | */ 21 | 22 | import { bindActionCreators } from 'redux'; 23 | import { connect } from 'react-redux'; 24 | 25 | import * as KnotsActions from '../actions/knots'; 26 | import Home from '../components/Home'; 27 | 28 | function mapStateToProps(state) { 29 | return { 30 | knotsStore: state.knots 31 | }; 32 | } 33 | 34 | function mapDispatchToProps(dispatch) { 35 | return bindActionCreators(KnotsActions, dispatch); 36 | } 37 | 38 | export default connect(mapStateToProps, mapDispatchToProps)(Home); 39 | -------------------------------------------------------------------------------- /app/containers/Knots.js: -------------------------------------------------------------------------------- 1 | /* 2 | * knots 3 | * Copyright 2018 data.world, Inc. 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the 7 | * License. 8 | * 9 | * You may obtain a copy of the License at 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, software 13 | * distributed under the License is distributed on an "AS IS" BASIS, 14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 15 | * implied. See the License for the specific language governing 16 | * permissions and limitations under the License. 17 | * 18 | * This product includes software developed at 19 | * data.world, Inc.(http://data.world/). 20 | */ 21 | 22 | import { bindActionCreators } from 'redux'; 23 | import { connect } from 'react-redux'; 24 | 25 | import * as KnotActions from '../actions/knots'; 26 | import Knots from '../components/Home/Knots'; 27 | 28 | function mapStateToProps(state) { 29 | return { 30 | knotsStore: state.knots 31 | }; 32 | } 33 | 34 | function mapDispatchToProps(dispatch) { 35 | return bindActionCreators(KnotActions, dispatch); 36 | } 37 | 38 | export default connect(mapStateToProps, mapDispatchToProps)(Knots); 39 | -------------------------------------------------------------------------------- /app/containers/Adwords.js: -------------------------------------------------------------------------------- 1 | /* 2 | * knots 3 | * Copyright 2018 data.world, Inc. 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the 7 | * License. 8 | * 9 | * You may obtain a copy of the License at 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, software 13 | * distributed under the License is distributed on an "AS IS" BASIS, 14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 15 | * implied. See the License for the specific language governing 16 | * permissions and limitations under the License. 17 | * 18 | * This product includes software developed at 19 | * data.world, Inc.(http://data.world/). 20 | */ 21 | 22 | import { bindActionCreators } from 'redux'; 23 | import { connect } from 'react-redux'; 24 | 25 | import * as TapsActions from '../actions/taps'; 26 | import Adwords from '../components/Taps/TapConfiguration/Adwords'; 27 | 28 | function mapStateToProps(state) { 29 | return { tapsStore: state.taps }; 30 | } 31 | 32 | function mapDispatchToProps(dispatch) { 33 | return bindActionCreators(TapsActions, dispatch); 34 | } 35 | 36 | export default connect(mapStateToProps, mapDispatchToProps)(Adwords); 37 | -------------------------------------------------------------------------------- /app/containers/S3.js: -------------------------------------------------------------------------------- 1 | /* 2 | * knots 3 | * Copyright 2018 data.world, Inc. 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the 7 | * License. 8 | * 9 | * You may obtain a copy of the License at 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, software 13 | * distributed under the License is distributed on an "AS IS" BASIS, 14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 15 | * implied. See the License for the specific language governing 16 | * permissions and limitations under the License. 17 | * 18 | * This product includes software developed at 19 | * data.world, Inc.(http://data.world/). 20 | */ 21 | 22 | import { bindActionCreators } from 'redux'; 23 | import { connect } from 'react-redux'; 24 | 25 | import * as TapsActions from '../actions/taps'; 26 | import S3 from '../components/Taps/TapConfiguration/S3'; 27 | 28 | function mapStateToProps(state) { 29 | return { tap: state.taps['tap-s3-csv'] }; 30 | } 31 | 32 | function mapDispatchToProps(dispatch) { 33 | return bindActionCreators(TapsActions, dispatch); 34 | } 35 | 36 | export default connect( 37 | mapStateToProps, 38 | mapDispatchToProps 39 | )(S3); 40 | -------------------------------------------------------------------------------- /app/containers/Redshift.js: -------------------------------------------------------------------------------- 1 | /* 2 | * knots 3 | * Copyright 2018 data.world, Inc. 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the 7 | * License. 8 | * 9 | * You may obtain a copy of the License at 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, software 13 | * distributed under the License is distributed on an "AS IS" BASIS, 14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 15 | * implied. See the License for the specific language governing 16 | * permissions and limitations under the License. 17 | * 18 | * This product includes software developed at 19 | * data.world, Inc.(http://data.world/). 20 | */ 21 | 22 | import { bindActionCreators } from 'redux'; 23 | import { connect } from 'react-redux'; 24 | 25 | import * as TapActions from '../actions/taps'; 26 | import Redshift from '../components/Taps/TapConfiguration/Redshift'; 27 | 28 | function mapStateToProps(state) { 29 | return { 30 | tapsStore: state.taps 31 | }; 32 | } 33 | 34 | function mapDispatchToProps(dispatch) { 35 | return bindActionCreators(TapActions, dispatch); 36 | } 37 | 38 | export default connect(mapStateToProps, mapDispatchToProps)(Redshift); 39 | -------------------------------------------------------------------------------- /app/containers/Facebook.js: -------------------------------------------------------------------------------- 1 | /* 2 | * knots 3 | * Copyright 2018 data.world, Inc. 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the 7 | * License. 8 | * 9 | * You may obtain a copy of the License at 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, software 13 | * distributed under the License is distributed on an "AS IS" BASIS, 14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 15 | * implied. See the License for the specific language governing 16 | * permissions and limitations under the License. 17 | * 18 | * This product includes software developed at 19 | * data.world, Inc.(http://data.world/). 20 | */ 21 | 22 | import { bindActionCreators } from 'redux'; 23 | import { connect } from 'react-redux'; 24 | 25 | import * as TapActions from '../actions/taps'; 26 | import Facebook from '../components/Taps/TapConfiguration/Facebook'; 27 | 28 | function mapStateToProps(state) { 29 | return { tapsStore: state.taps }; 30 | } 31 | 32 | function mapDispatchToProps(dispatch) { 33 | return bindActionCreators(TapActions, dispatch); 34 | } 35 | 36 | export default connect( 37 | mapStateToProps, 38 | mapDispatchToProps 39 | )(Facebook); 40 | -------------------------------------------------------------------------------- /app/containers/Salesforce.js: -------------------------------------------------------------------------------- 1 | /* 2 | * knots 3 | * Copyright 2018 data.world, Inc. 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the 7 | * License. 8 | * 9 | * You may obtain a copy of the License at 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, software 13 | * distributed under the License is distributed on an "AS IS" BASIS, 14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 15 | * implied. See the License for the specific language governing 16 | * permissions and limitations under the License. 17 | * 18 | * This product includes software developed at 19 | * data.world, Inc.(http://data.world/). 20 | */ 21 | 22 | import { bindActionCreators } from 'redux'; 23 | import { connect } from 'react-redux'; 24 | 25 | import * as TapsActions from '../actions/taps'; 26 | import Salesforce from '../components/Taps/TapConfiguration/Salesforce'; 27 | 28 | function mapStateToProps(state) { 29 | return { 30 | tapsStore: state.taps 31 | }; 32 | } 33 | 34 | function mapDispatchToProps(dispatch) { 35 | return bindActionCreators(TapsActions, dispatch); 36 | } 37 | 38 | export default connect(mapStateToProps, mapDispatchToProps)(Salesforce); 39 | -------------------------------------------------------------------------------- /app/reducers/index.js: -------------------------------------------------------------------------------- 1 | /* 2 | * knots 3 | * Copyright 2018 data.world, Inc. 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the 7 | * License. 8 | * 9 | * You may obtain a copy of the License at 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, software 13 | * distributed under the License is distributed on an "AS IS" BASIS, 14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 15 | * implied. See the License for the specific language governing 16 | * permissions and limitations under the License. 17 | * 18 | * This product includes software developed at 19 | * data.world, Inc.(http://data.world/). 20 | */ 21 | 22 | import { combineReducers } from 'redux'; 23 | import { routerReducer as router } from 'react-router-redux'; 24 | import knots from './knots'; 25 | import taps from './taps'; 26 | import targets from './targets'; 27 | import user from './user'; 28 | import progress from './progress'; 29 | 30 | const appReducer = combineReducers({ 31 | progress, 32 | knots, 33 | taps, 34 | targets, 35 | user, 36 | router 37 | }); 38 | 39 | const rootReducer = (state, action) => appReducer(state, action); 40 | export default rootReducer; 41 | -------------------------------------------------------------------------------- /app/containers/Postgres.js: -------------------------------------------------------------------------------- 1 | /* 2 | * knots 3 | * Copyright 2018 data.world, Inc. 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the 7 | * License. 8 | * 9 | * You may obtain a copy of the License at 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, software 13 | * distributed under the License is distributed on an "AS IS" BASIS, 14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 15 | * implied. See the License for the specific language governing 16 | * permissions and limitations under the License. 17 | * 18 | * This product includes software developed at 19 | * data.world, Inc.(http://data.world/). 20 | */ 21 | 22 | import { bindActionCreators } from 'redux'; 23 | import { connect } from 'react-redux'; 24 | 25 | import * as TapActions from '../actions/taps'; 26 | import Postgres from '../components/Taps/TapConfiguration/Postgres'; 27 | 28 | function mapStateToProps(state) { 29 | return { tapsStore: state.taps, knotsStore: state.knots }; 30 | } 31 | 32 | function mapDispatchToProps(dispatch) { 33 | return bindActionCreators(TapActions, dispatch); 34 | } 35 | 36 | export default connect( 37 | mapStateToProps, 38 | mapDispatchToProps 39 | )(Postgres); 40 | -------------------------------------------------------------------------------- /app/containers/Stitch.js: -------------------------------------------------------------------------------- 1 | /* 2 | * knots 3 | * Copyright 2018 data.world, Inc. 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the 7 | * License. 8 | * 9 | * You may obtain a copy of the License at 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, software 13 | * distributed under the License is distributed on an "AS IS" BASIS, 14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 15 | * implied. See the License for the specific language governing 16 | * permissions and limitations under the License. 17 | * 18 | * This product includes software developed at 19 | * data.world, Inc.(http://data.world/). 20 | */ 21 | 22 | import { bindActionCreators } from 'redux'; 23 | import { connect } from 'react-redux'; 24 | 25 | import * as UserActions from '../actions/user'; 26 | import Stitch from '../components/Targets/TargetConfiguration/Stitch'; 27 | 28 | function mapStateToProps(state) { 29 | return { 30 | userStore: state.user, 31 | tapsStore: state.taps 32 | }; 33 | } 34 | 35 | function mapDispatchToProps(dispatch) { 36 | return bindActionCreators(UserActions, dispatch); 37 | } 38 | 39 | export default connect(mapStateToProps, mapDispatchToProps)(Stitch); 40 | -------------------------------------------------------------------------------- /app/containers/DataWorld.js: -------------------------------------------------------------------------------- 1 | /* 2 | * knots 3 | * Copyright 2018 data.world, Inc. 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the 7 | * License. 8 | * 9 | * You may obtain a copy of the License at 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, software 13 | * distributed under the License is distributed on an "AS IS" BASIS, 14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 15 | * implied. See the License for the specific language governing 16 | * permissions and limitations under the License. 17 | * 18 | * This product includes software developed at 19 | * data.world, Inc.(http://data.world/). 20 | */ 21 | 22 | import { bindActionCreators } from 'redux'; 23 | import { connect } from 'react-redux'; 24 | 25 | import * as UserActions from '../actions/user'; 26 | import DataWorld from '../components/Targets/TargetConfiguration/DataWorld'; 27 | 28 | function mapStateToProps(state) { 29 | return { 30 | userStore: state.user, 31 | tapsStore: state.taps 32 | }; 33 | } 34 | 35 | function mapDispatchToProps(dispatch) { 36 | return bindActionCreators(UserActions, dispatch); 37 | } 38 | 39 | export default connect(mapStateToProps, mapDispatchToProps)(DataWorld); 40 | -------------------------------------------------------------------------------- /app/containers/TapConfiguration.js: -------------------------------------------------------------------------------- 1 | /* 2 | * knots 3 | * Copyright 2018 data.world, Inc. 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the 7 | * License. 8 | * 9 | * You may obtain a copy of the License at 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, software 13 | * distributed under the License is distributed on an "AS IS" BASIS, 14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 15 | * implied. See the License for the specific language governing 16 | * permissions and limitations under the License. 17 | * 18 | * This product includes software developed at 19 | * data.world, Inc.(http://data.world/). 20 | */ 21 | 22 | import { bindActionCreators } from 'redux'; 23 | import { connect } from 'react-redux'; 24 | 25 | import * as tapActions from '../actions/targets'; 26 | import TapConfiguration from '../components/Taps/TapConfiguration'; 27 | 28 | function mapStateToProps(state) { 29 | return { 30 | userStore: state.user, 31 | tapsStore: state.taps 32 | }; 33 | } 34 | 35 | function mapDispatchToProps(dispatch) { 36 | return bindActionCreators(tapActions, dispatch); 37 | } 38 | 39 | export default connect(mapStateToProps, mapDispatchToProps)(TapConfiguration); 40 | -------------------------------------------------------------------------------- /app/containers/TargetConfiguration.js: -------------------------------------------------------------------------------- 1 | /* 2 | * knots 3 | * Copyright 2018 data.world, Inc. 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the 7 | * License. 8 | * 9 | * You may obtain a copy of the License at 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, software 13 | * distributed under the License is distributed on an "AS IS" BASIS, 14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 15 | * implied. See the License for the specific language governing 16 | * permissions and limitations under the License. 17 | * 18 | * This product includes software developed at 19 | * data.world, Inc.(http://data.world/). 20 | */ 21 | 22 | import { bindActionCreators } from 'redux'; 23 | import { connect } from 'react-redux'; 24 | 25 | import * as targetActions from '../actions/targets'; 26 | import Target from '../components/Targets/TargetConfiguration'; 27 | 28 | function mapStateToProps(state) { 29 | return { 30 | userStore: state.user, 31 | targetsStore: state.targets 32 | }; 33 | } 34 | 35 | function mapDispatchToProps(dispatch) { 36 | return bindActionCreators(targetActions, dispatch); 37 | } 38 | 39 | export default connect(mapStateToProps, mapDispatchToProps)(Target); 40 | -------------------------------------------------------------------------------- /app/containers/Root.js: -------------------------------------------------------------------------------- 1 | /* 2 | * knots 3 | * Copyright 2018 data.world, Inc. 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the 7 | * License. 8 | * 9 | * You may obtain a copy of the License at 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, software 13 | * distributed under the License is distributed on an "AS IS" BASIS, 14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 15 | * implied. See the License for the specific language governing 16 | * permissions and limitations under the License. 17 | * 18 | * This product includes software developed at 19 | * data.world, Inc.(http://data.world/). 20 | */ 21 | 22 | // @flow 23 | 24 | import React, { Component } from 'react'; 25 | import { Provider } from 'react-redux'; 26 | import { ConnectedRouter } from 'react-router-redux'; 27 | import Routes from '../routes'; 28 | 29 | type Props = { 30 | store: {}, 31 | history: {} 32 | }; 33 | 34 | export default class Root extends Component { 35 | render() { 36 | return ( 37 | 38 | 39 | 40 | 41 | 42 | ); 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /app/store/configureStore.prod.js: -------------------------------------------------------------------------------- 1 | /* 2 | * knots 3 | * Copyright 2018 data.world, Inc. 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the 7 | * License. 8 | * 9 | * You may obtain a copy of the License at 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, software 13 | * distributed under the License is distributed on an "AS IS" BASIS, 14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 15 | * implied. See the License for the specific language governing 16 | * permissions and limitations under the License. 17 | * 18 | * This product includes software developed at 19 | * data.world, Inc.(http://data.world/). 20 | */ 21 | 22 | import { createStore, applyMiddleware } from 'redux'; 23 | import thunk from 'redux-thunk'; 24 | import { createBrowserHistory } from 'history'; 25 | import { routerMiddleware } from 'react-router-redux'; 26 | import rootReducer from '../reducers'; 27 | 28 | const history = createBrowserHistory(); 29 | const router = routerMiddleware(history); 30 | const enhancer = applyMiddleware(thunk, router); 31 | 32 | function configureStore(initialState) { 33 | return createStore(rootReducer, initialState, enhancer); 34 | } 35 | 36 | export default { configureStore, history }; 37 | -------------------------------------------------------------------------------- /app/containers/Sync.js: -------------------------------------------------------------------------------- 1 | /* 2 | * knots 3 | * Copyright 2018 data.world, Inc. 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the 7 | * License. 8 | * 9 | * You may obtain a copy of the License at 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, software 13 | * distributed under the License is distributed on an "AS IS" BASIS, 14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 15 | * implied. See the License for the specific language governing 16 | * permissions and limitations under the License. 17 | * 18 | * This product includes software developed at 19 | * data.world, Inc.(http://data.world/). 20 | */ 21 | 22 | import { bindActionCreators } from 'redux'; 23 | import { connect } from 'react-redux'; 24 | 25 | import * as knotActions from '../actions/knots'; 26 | import Sync from '../components/Sync'; 27 | 28 | function mapStateToProps(state) { 29 | return { 30 | knotsStore: state.knots, 31 | userStore: state.user, 32 | tapStore: state.taps, 33 | targetsStore: state.targets 34 | }; 35 | } 36 | 37 | function mapDispatchToProps(dispatch) { 38 | return bindActionCreators(knotActions, dispatch); 39 | } 40 | 41 | export default connect(mapStateToProps, mapDispatchToProps)(Sync); 42 | -------------------------------------------------------------------------------- /app/containers/MySQL.js: -------------------------------------------------------------------------------- 1 | /* 2 | * knots 3 | * Copyright 2018 data.world, Inc. 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the 7 | * License. 8 | * 9 | * You may obtain a copy of the License at 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, software 13 | * distributed under the License is distributed on an "AS IS" BASIS, 14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 15 | * implied. See the License for the specific language governing 16 | * permissions and limitations under the License. 17 | * 18 | * This product includes software developed at 19 | * data.world, Inc.(http://data.world/). 20 | */ 21 | 22 | import { bindActionCreators } from 'redux'; 23 | import { connect } from 'react-redux'; 24 | 25 | import * as TapActions from '../actions/taps'; 26 | import MySQL from '../components/Taps/TapConfiguration/MySQL'; 27 | 28 | function mapStateToProps(state) { 29 | return { 30 | userStore: state.user, 31 | tapsStore: state.taps, 32 | knotsStore: state.knots 33 | }; 34 | } 35 | 36 | function mapDispatchToProps(dispatch) { 37 | return bindActionCreators(TapActions, dispatch); 38 | } 39 | 40 | export default connect( 41 | mapStateToProps, 42 | mapDispatchToProps 43 | )(MySQL); 44 | -------------------------------------------------------------------------------- /app/containers/SavedSync.js: -------------------------------------------------------------------------------- 1 | /* 2 | * knots 3 | * Copyright 2018 data.world, Inc. 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the 7 | * License. 8 | * 9 | * You may obtain a copy of the License at 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, software 13 | * distributed under the License is distributed on an "AS IS" BASIS, 14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 15 | * implied. See the License for the specific language governing 16 | * permissions and limitations under the License. 17 | * 18 | * This product includes software developed at 19 | * data.world, Inc.(http://data.world/). 20 | */ 21 | 22 | import { bindActionCreators } from 'redux'; 23 | import { connect } from 'react-redux'; 24 | 25 | import * as knotActions from '../actions/knots'; 26 | import SavedSync from '../components/SavedSync'; 27 | 28 | function mapStateToProps(state) { 29 | return { 30 | knotsStore: state.knots, 31 | userStore: state.user, 32 | tapStore: state.taps, 33 | targetsStore: state.targets 34 | }; 35 | } 36 | 37 | function mapDispatchToProps(dispatch) { 38 | return bindActionCreators(knotActions, dispatch); 39 | } 40 | 41 | export default connect(mapStateToProps, mapDispatchToProps)(SavedSync); 42 | -------------------------------------------------------------------------------- /app/components/Log/index.js: -------------------------------------------------------------------------------- 1 | /* 2 | * knots 3 | * Copyright 2018 data.world, Inc. 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the 7 | * License. 8 | * 9 | * You may obtain a copy of the License at 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, software 13 | * distributed under the License is distributed on an "AS IS" BASIS, 14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 15 | * implied. See the License for the specific language governing 16 | * permissions and limitations under the License. 17 | * 18 | * This product includes software developed at 19 | * data.world, Inc.(http://data.world/). 20 | */ 21 | 22 | import React, { Component } from 'react'; 23 | import { scrolled } from 'react-stay-scrolled'; 24 | import classNames from 'classnames'; 25 | 26 | import styles from './Log.css'; 27 | 28 | type Props = { 29 | log: text, 30 | stayScrolled: () => void 31 | }; 32 | 33 | class Log extends Component { 34 | componentDidMount() { 35 | const { stayScrolled } = this.props; 36 | stayScrolled(); 37 | } 38 | 39 | render() { 40 | const { log } = this.props; 41 | 42 | return
{log}
; 43 | } 44 | } 45 | 46 | export default scrolled(Log); 47 | -------------------------------------------------------------------------------- /app/containers/Taps.js: -------------------------------------------------------------------------------- 1 | /* 2 | * knots 3 | * Copyright 2018 data.world, Inc. 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the 7 | * License. 8 | * 9 | * You may obtain a copy of the License at 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, software 13 | * distributed under the License is distributed on an "AS IS" BASIS, 14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 15 | * implied. See the License for the specific language governing 16 | * permissions and limitations under the License. 17 | * 18 | * This product includes software developed at 19 | * data.world, Inc.(http://data.world/). 20 | */ 21 | 22 | import { bindActionCreators } from 'redux'; 23 | import { connect } from 'react-redux'; 24 | 25 | import * as TapsActions from '../actions/taps'; 26 | import * as KnotsActions from '../actions/knots'; 27 | import Taps from '../components/Taps'; 28 | 29 | function mapStateToProps(state) { 30 | return { 31 | tapsStore: state.taps, 32 | knotsStore: state.knots 33 | }; 34 | } 35 | 36 | function mapDispatchToProps(dispatch) { 37 | return bindActionCreators( 38 | Object.assign({}, TapsActions, KnotsActions), 39 | dispatch 40 | ); 41 | } 42 | 43 | export default connect(mapStateToProps, mapDispatchToProps)(Taps); 44 | -------------------------------------------------------------------------------- /app/containers/Schema.js: -------------------------------------------------------------------------------- 1 | /* 2 | * knots 3 | * Copyright 2018 data.world, Inc. 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the 7 | * License. 8 | * 9 | * You may obtain a copy of the License at 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, software 13 | * distributed under the License is distributed on an "AS IS" BASIS, 14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 15 | * implied. See the License for the specific language governing 16 | * permissions and limitations under the License. 17 | * 18 | * This product includes software developed at 19 | * data.world, Inc.(http://data.world/). 20 | */ 21 | 22 | import { bindActionCreators } from 'redux'; 23 | import { connect } from 'react-redux'; 24 | 25 | import * as TapActions from '../actions/taps'; 26 | import * as KnotsActions from '../actions/knots'; 27 | import Schema from '../components/Schema'; 28 | 29 | function mapStateToProps(state) { 30 | return { 31 | tapsStore: state.taps, 32 | knotsStore: state.knots 33 | }; 34 | } 35 | 36 | function mapDispatchToProps(dispatch) { 37 | return bindActionCreators( 38 | Object.assign({}, TapActions, KnotsActions), 39 | dispatch 40 | ); 41 | } 42 | 43 | export default connect(mapStateToProps, mapDispatchToProps)(Schema); 44 | -------------------------------------------------------------------------------- /app/backend/routes/docker.js: -------------------------------------------------------------------------------- 1 | /* 2 | * knots 3 | * Copyright 2018 data.world, Inc. 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the 7 | * License. 8 | * 9 | * You may obtain a copy of the License at 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, software 13 | * distributed under the License is distributed on an "AS IS" BASIS, 14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 15 | * implied. See the License for the specific language governing 16 | * permissions and limitations under the License. 17 | * 18 | * This product includes software developed at 19 | * data.world, Inc.(http://data.world/). 20 | */ 21 | 22 | const router = require('express').Router(); 23 | 24 | const { dockerInstalled, dockerRunning } = require('../docker'); 25 | 26 | router.get('/installed', (req, res) => { 27 | dockerInstalled() 28 | .then((version) => { 29 | res.json({ version }); 30 | }) 31 | .catch((error) => { 32 | res.status(500).json({ message: error.message }); 33 | }); 34 | }); 35 | 36 | router.get('/running', (req, res) => { 37 | dockerRunning() 38 | .then(() => { 39 | res.json({}); 40 | }) 41 | .catch((error) => { 42 | res.status(500).json({ message: error.message }); 43 | }); 44 | }); 45 | 46 | module.exports = router; 47 | -------------------------------------------------------------------------------- /app/containers/KnotProgress.js: -------------------------------------------------------------------------------- 1 | /* 2 | * knots 3 | * Copyright 2018 data.world, Inc. 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the 7 | * License. 8 | * 9 | * You may obtain a copy of the License at 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, software 13 | * distributed under the License is distributed on an "AS IS" BASIS, 14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 15 | * implied. See the License for the specific language governing 16 | * permissions and limitations under the License. 17 | * 18 | * This product includes software developed at 19 | * data.world, Inc.(http://data.world/). 20 | */ 21 | 22 | import { bindActionCreators } from 'redux'; 23 | import { connect } from 'react-redux'; 24 | 25 | import * as ProgressActions from '../actions/progress'; 26 | import KnotProgress from '../components/KnotProgress'; 27 | 28 | function mapStateToProps(state) { 29 | return { 30 | progressStore: state.progress, 31 | tapsStore: state.taps, 32 | targetsStore: state.targets, 33 | knotsStore: state.knots, 34 | userStore: state.user 35 | }; 36 | } 37 | 38 | function mapDispatchToProps(dispatch) { 39 | return bindActionCreators(ProgressActions, dispatch); 40 | } 41 | 42 | export default connect(mapStateToProps, mapDispatchToProps)(KnotProgress); 43 | -------------------------------------------------------------------------------- /app/containers/Targets.js: -------------------------------------------------------------------------------- 1 | /* 2 | * knots 3 | * Copyright 2018 data.world, Inc. 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the 7 | * License. 8 | * 9 | * You may obtain a copy of the License at 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, software 13 | * distributed under the License is distributed on an "AS IS" BASIS, 14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 15 | * implied. See the License for the specific language governing 16 | * permissions and limitations under the License. 17 | * 18 | * This product includes software developed at 19 | * data.world, Inc.(http://data.world/). 20 | */ 21 | 22 | import { bindActionCreators } from 'redux'; 23 | import { connect } from 'react-redux'; 24 | 25 | import * as TargetsActions from '../actions/targets'; 26 | import * as KnotsActions from '../actions/knots'; 27 | import Targets from '../components/Targets'; 28 | 29 | function mapStateToProps(state) { 30 | return { 31 | targetsStore: state.targets, 32 | userStore: state.user, 33 | knotsStore: state.knots 34 | }; 35 | } 36 | 37 | function mapDispatchToProps(dispatch) { 38 | return bindActionCreators( 39 | Object.assign({}, TargetsActions, KnotsActions), 40 | dispatch 41 | ); 42 | } 43 | 44 | export default connect(mapStateToProps, mapDispatchToProps)(Targets); 45 | -------------------------------------------------------------------------------- /app/components/Header/index.js: -------------------------------------------------------------------------------- 1 | /* 2 | * knots 3 | * Copyright 2018 data.world, Inc. 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the 7 | * License. 8 | * 9 | * You may obtain a copy of the License at 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, software 13 | * distributed under the License is distributed on an "AS IS" BASIS, 14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 15 | * implied. See the License for the specific language governing 16 | * permissions and limitations under the License. 17 | * 18 | * This product includes software developed at 19 | * data.world, Inc.(http://data.world/). 20 | */ 21 | 22 | // @flow 23 | 24 | import React from 'react'; 25 | import { NavLink } from 'react-router-dom'; 26 | import { Container, Nav, Navbar, NavbarBrand, NavItem } from 'reactstrap'; 27 | import logo from '../../img/knots.svg'; 28 | 29 | const Header = () => ( 30 | 31 | 32 | 33 | KNOTS logo 34 | 35 | 42 | 43 | 44 | ); 45 | 46 | export default Header; 47 | -------------------------------------------------------------------------------- /app/logos.js: -------------------------------------------------------------------------------- 1 | /* 2 | * knots 3 | * Copyright 2018 data.world, Inc. 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the 7 | * License. 8 | * 9 | * You may obtain a copy of the License at 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, software 13 | * distributed under the License is distributed on an "AS IS" BASIS, 14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 15 | * implied. See the License for the specific language governing 16 | * permissions and limitations under the License. 17 | * 18 | * This product includes software developed at 19 | * data.world, Inc.(http://data.world/). 20 | */ 21 | 22 | const importAll = (r) => { 23 | const images = {}; 24 | const storeItem = (item) => { 25 | images[item.replace('./', '')] = r(item); 26 | }; 27 | 28 | r.keys().map(storeItem); 29 | return images; 30 | }; 31 | 32 | // require.context does not work with tests 33 | // https://github.com/facebook/create-react-app/issues/517 34 | let images = {}; 35 | if (process.env.NODE_ENV === 'test') { 36 | images = { 37 | 'knots.svg': 'knots', 38 | 'tap-postgres.svg': 'postgres', 39 | 'tap-redshift.svg': 'redshift', 40 | 'tap-salesforce.svg': 'salesforce', 41 | 'target-datadotworld.svg': 'datadotworld', 42 | 'target-stitch.svg': 'stitch' 43 | }; 44 | } else { 45 | images = importAll(require.context('./img', false, /\.svg$/)); 46 | } 47 | 48 | export default (logo) => images[`${logo}.svg`]; 49 | -------------------------------------------------------------------------------- /.eslintrc: -------------------------------------------------------------------------------- 1 | { 2 | "parser": "babel-eslint", 3 | "parserOptions": { 4 | "sourceType": "module", 5 | "allowImportExportEverywhere": true 6 | }, 7 | "extends": "airbnb", 8 | "env": { 9 | "browser": true, 10 | "node": true 11 | }, 12 | "rules": { 13 | "arrow-parens": ["off"], 14 | "compat/compat": "error", 15 | "consistent-return": "off", 16 | "comma-dangle": "off", 17 | "generator-star-spacing": "off", 18 | "import/no-unresolved": "error", 19 | "import/no-extraneous-dependencies": "off", 20 | "jsx-a11y/anchor-is-valid": "off", 21 | "no-console": "off", 22 | "no-use-before-define": "off", 23 | "no-multi-assign": "off", 24 | "promise/param-names": "error", 25 | "promise/catch-or-return": "error", 26 | "promise/no-native": "off", 27 | "react/sort-comp": [ 28 | "error", 29 | { 30 | "order": [ 31 | "type-annotations", 32 | "static-methods", 33 | "lifecycle", 34 | "everything-else", 35 | "render" 36 | ] 37 | } 38 | ], 39 | "react/jsx-no-bind": "off", 40 | "react/jsx-filename-extension": [ 41 | "error", 42 | { "extensions": [".js", ".jsx"] } 43 | ], 44 | "react/prefer-stateless-function": "off", 45 | "object-curly-newline": 0, 46 | "function-paren-newline": 0, 47 | "indent": 0 48 | }, 49 | "plugins": ["flowtype", "import", "promise", "compat", "react"], 50 | "settings": { 51 | "import/resolver": { 52 | "webpack": { 53 | "config": "webpack.config.eslint.js" 54 | } 55 | } 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /app/index.js: -------------------------------------------------------------------------------- 1 | /* 2 | * knots 3 | * Copyright 2018 data.world, Inc. 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the 7 | * License. 8 | * 9 | * You may obtain a copy of the License at 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, software 13 | * distributed under the License is distributed on an "AS IS" BASIS, 14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 15 | * implied. See the License for the specific language governing 16 | * permissions and limitations under the License. 17 | * 18 | * This product includes software developed at 19 | * data.world, Inc.(http://data.world/). 20 | */ 21 | 22 | import React from 'react'; 23 | import { render } from 'react-dom'; 24 | import { AppContainer } from 'react-hot-loader'; 25 | 26 | import Root from './containers/Root'; 27 | import { configureStore, history } from './store/configureStore'; 28 | import './app.global.scss'; 29 | 30 | const store = configureStore(); 31 | 32 | render( 33 | 34 | 35 | , 36 | document.getElementById('root') 37 | ); 38 | 39 | if (module.hot) { 40 | module.hot.accept('./containers/Root', () => { 41 | const NextRoot = require('./containers/Root'); // eslint-disable-line global-require 42 | render( 43 | 44 | 45 | , 46 | document.getElementById('root') 47 | ); 48 | }); 49 | } 50 | -------------------------------------------------------------------------------- /app/routes.js: -------------------------------------------------------------------------------- 1 | /* 2 | * knots 3 | * Copyright 2018 data.world, Inc. 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the 7 | * License. 8 | * 9 | * You may obtain a copy of the License at 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, software 13 | * distributed under the License is distributed on an "AS IS" BASIS, 14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 15 | * implied. See the License for the specific language governing 16 | * permissions and limitations under the License. 17 | * 18 | * This product includes software developed at 19 | * data.world, Inc.(http://data.world/). 20 | */ 21 | 22 | /* eslint flowtype-errors/show-errors: 0 */ 23 | import React from 'react'; 24 | import { Switch, Route } from 'react-router'; 25 | import App from './containers/App'; 26 | import HomePage from './containers/HomePage'; 27 | import Taps from './containers/Taps'; 28 | import Schema from './containers/Schema'; 29 | import Targets from './containers/Targets'; 30 | import Sync from './containers/Sync'; 31 | import SavedSync from './containers/SavedSync'; 32 | 33 | export default () => ( 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | ); 45 | -------------------------------------------------------------------------------- /app/components/Targets/TargetConfiguration/index.js: -------------------------------------------------------------------------------- 1 | /* 2 | * knots 3 | * Copyright 2018 data.world, Inc. 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the 7 | * License. 8 | * 9 | * You may obtain a copy of the License at 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, software 13 | * distributed under the License is distributed on an "AS IS" BASIS, 14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 15 | * implied. See the License for the specific language governing 16 | * permissions and limitations under the License. 17 | * 18 | * This product includes software developed at 19 | * data.world, Inc.(http://data.world/). 20 | */ 21 | 22 | // @flow 23 | 24 | import React, { Component } from 'react'; 25 | import { Container, Row } from 'reactstrap'; 26 | 27 | import DataWorld from '../../../containers/DataWorld'; 28 | import Stitch from '../../../containers/Stitch'; 29 | 30 | type Props = { 31 | targetsStore: { 32 | selectedTarget: { name: string, image: string } 33 | } 34 | }; 35 | 36 | export default class Target extends Component { 37 | selectedTarget = () => { 38 | const { selectedTarget } = this.props.targetsStore; 39 | switch (selectedTarget.name) { 40 | case 'target-datadotworld': 41 | return ; 42 | case 'target-stitch': 43 | return ; 44 | default: 45 | return
Unknown Target
; 46 | } 47 | }; 48 | 49 | render() { 50 | return ( 51 |
52 | 53 | {this.selectedTarget()} 54 | 55 |
56 | ); 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /webpack.config.main.prod.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Webpack config for production electron main process 3 | */ 4 | 5 | import webpack from 'webpack'; 6 | import merge from 'webpack-merge'; 7 | import UglifyJSPlugin from 'uglifyjs-webpack-plugin'; 8 | import { BundleAnalyzerPlugin } from 'webpack-bundle-analyzer'; 9 | import baseConfig from './webpack.config.base'; 10 | import CheckNodeEnv from './internals/scripts/CheckNodeEnv'; 11 | 12 | CheckNodeEnv('production'); 13 | 14 | export default merge.smart(baseConfig, { 15 | devtool: 'source-map', 16 | 17 | target: 'electron-main', 18 | 19 | entry: './app/main.dev', 20 | 21 | output: { 22 | path: __dirname, 23 | filename: './app/main.prod.js' 24 | }, 25 | 26 | plugins: [ 27 | new UglifyJSPlugin({ 28 | parallel: true, 29 | sourceMap: true 30 | }), 31 | 32 | new BundleAnalyzerPlugin({ 33 | analyzerMode: 34 | process.env.OPEN_ANALYZER === 'true' ? 'server' : 'disabled', 35 | openAnalyzer: process.env.OPEN_ANALYZER === 'true' 36 | }), 37 | 38 | /** 39 | * Create global constants which can be configured at compile time. 40 | * 41 | * Useful for allowing different behaviour between development builds and 42 | * release builds 43 | * 44 | * NODE_ENV should be production so that modules do not perform certain 45 | * development checks 46 | */ 47 | new webpack.EnvironmentPlugin({ 48 | NODE_ENV: 'production', 49 | DEBUG_PROD: 'false' 50 | }) 51 | ], 52 | 53 | /** 54 | * Disables webpack processing of __dirname and __filename. 55 | * If you run the bundle in node.js it falls back to these values of node.js. 56 | * https://github.com/webpack/webpack/issues/2010 57 | */ 58 | node: { 59 | __dirname: false, 60 | __filename: false 61 | } 62 | }); 63 | -------------------------------------------------------------------------------- /test/backend/docker.spec.js: -------------------------------------------------------------------------------- 1 | import mockSpawn from 'mock-spawn'; 2 | import { dockerInstalled, dockerRunning } from '../../app/backend/docker'; 3 | 4 | const mySpawn = mockSpawn(); 5 | 6 | describe('docker commands', () => { 7 | describe('dockerInstalled', () => { 8 | it('should return Docker version number if installed', () => { 9 | mySpawn.setDefault( 10 | mySpawn.simple(0, 'Docker version 18.03.1-ce, build 9ee9f40') 11 | ); 12 | dockerInstalled(mySpawn) 13 | .then((res) => { 14 | expect(res).toEqual('Docker version 18.03.1-ce, build 9ee9f40'); 15 | }) 16 | .catch((err) => { 17 | expect(err).toBeUndefined(); 18 | }); 19 | }); 20 | 21 | it('should reject promise if Docker is not installed', () => { 22 | mySpawn.setDefault(mySpawn.simple(1)); 23 | dockerInstalled(mySpawn) 24 | .then((res) => { 25 | expect(res).toBeUndefined(); 26 | }) 27 | .catch((err) => { 28 | expect(err).toEqual(new Error('Unable to run Docker')); 29 | }); 30 | }); 31 | }); 32 | 33 | describe('dockerRunning', () => { 34 | it('should resolve promise if Docker is running', () => { 35 | mySpawn.setDefault(mySpawn.simple(0, 'DRIVER VOLUME NAME')); 36 | dockerRunning(mySpawn) 37 | .then((res) => { 38 | expect(res).toEqual('DRIVER VOLUME NAME'); 39 | }) 40 | .catch((err) => { 41 | expect(err).toBeUndefined(); 42 | }); 43 | }); 44 | 45 | it('should reject promise if Docker is not running', () => { 46 | mySpawn.setDefault(mySpawn.simple(1)); 47 | dockerRunning(mySpawn) 48 | .then((res) => { 49 | expect(res).toBeUndefined(); 50 | }) 51 | .catch((err) => { 52 | expect(err).toEqual(new Error('Unable to get Docker volumes')); 53 | }); 54 | }); 55 | }); 56 | }); 57 | -------------------------------------------------------------------------------- /app/components/Schema/Checkbox/index.js: -------------------------------------------------------------------------------- 1 | /* 2 | * knots 3 | * Copyright 2018 data.world, Inc. 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the 7 | * License. 8 | * 9 | * You may obtain a copy of the License at 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, software 13 | * distributed under the License is distributed on an "AS IS" BASIS, 14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 15 | * implied. See the License for the specific language governing 16 | * permissions and limitations under the License. 17 | * 18 | * This product includes software developed at 19 | * data.world, Inc.(http://data.world/). 20 | */ 21 | 22 | // @flow 23 | 24 | import React, { Component } from 'react'; 25 | import { FormGroup, Input } from 'reactstrap'; 26 | 27 | type Props = { 28 | checked: boolean, 29 | index: string, 30 | handleChange: (field: string, index: string, value: boolean | string) => void 31 | }; 32 | 33 | type State = { 34 | checked: boolean 35 | }; 36 | 37 | export default class Checkbox extends Component { 38 | constructor(props: Props) { 39 | super(); 40 | 41 | this.state = { checked: props.checked }; 42 | } 43 | 44 | componentWillReceiveProps(nextProps: Props) { 45 | this.setState({ checked: nextProps.checked }); 46 | } 47 | 48 | toggle = () => { 49 | const checked = !this.state.checked; 50 | this.props.handleChange('selected', this.props.index, checked); 51 | this.setState({ checked }); 52 | }; 53 | 54 | render() { 55 | return ( 56 | 57 | 63 | 64 | ); 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /app/backend/routes/taps.js: -------------------------------------------------------------------------------- 1 | /* 2 | * knots 3 | * Copyright 2018 data.world, Inc. 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the 7 | * License. 8 | * 9 | * You may obtain a copy of the License at 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, software 13 | * distributed under the License is distributed on an "AS IS" BASIS, 14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 15 | * implied. See the License for the specific language governing 16 | * permissions and limitations under the License. 17 | * 18 | * This product includes software developed at 19 | * data.world, Inc.(http://data.world/). 20 | */ 21 | 22 | const router = require('express').Router(); 23 | 24 | const { getTaps, createKnot, addConfig, writeSchema } = require('../taps'); 25 | 26 | router.get('/', (req, res) => { 27 | getTaps() 28 | .then((taps) => res.json({ taps })) 29 | .catch((error) => { 30 | res.status(500).json({ message: error.message }); 31 | }); 32 | }); 33 | 34 | router.post('/select', (req, res) => { 35 | const { tap, knot, uuid } = req.body; 36 | 37 | createKnot(tap, uuid, !!knot) 38 | .then(() => { 39 | res.json({}); 40 | }) 41 | .catch((error) => { 42 | res.status(500).json({ message: error.message }); 43 | }); 44 | }); 45 | 46 | router.post('/config/', (req, res) => { 47 | addConfig(req) 48 | .then((schema) => { 49 | res.json({ schema: schema.streams }); 50 | }) 51 | .catch((error) => { 52 | res.status(500).json({ message: error.message }); 53 | }); 54 | }); 55 | 56 | router.put('/schema/', (req, res) => { 57 | writeSchema(req.body.schema, req.body.uuid) 58 | .then(() => { 59 | res.json({}); 60 | }) 61 | .catch((error) => { 62 | res.status(500).json({ message: error.message }); 63 | }); 64 | }); 65 | 66 | module.exports = router; 67 | -------------------------------------------------------------------------------- /app/backend/index.js: -------------------------------------------------------------------------------- 1 | /* 2 | * knots 3 | * Copyright 2018 data.world, Inc. 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the 7 | * License. 8 | * 9 | * You may obtain a copy of the License at 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, software 13 | * distributed under the License is distributed on an "AS IS" BASIS, 14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 15 | * implied. See the License for the specific language governing 16 | * permissions and limitations under the License. 17 | * 18 | * This product includes software developed at 19 | * data.world, Inc.(http://data.world/). 20 | */ 21 | 22 | const express = require('express'); 23 | const bodyParser = require('body-parser'); 24 | const socketIo = require('socket.io'); 25 | const http = require('http'); 26 | const routes = require('./routes'); 27 | const terminate = require('terminate'); 28 | 29 | const app = express(); 30 | const server = http.createServer(app); 31 | const io = socketIo(server); 32 | const { terminateDiscovery } = require('./taps'); 33 | const { terminateSync } = require('./knots'); 34 | 35 | app.use(bodyParser.json({ limit: '50mb' })); 36 | app.use((req, res, next) => { 37 | req.io = io; 38 | next(); 39 | }); 40 | 41 | routes(app); 42 | 43 | const PORT = 4321; // Random number that's unikely to clash with other apps 44 | 45 | server.listen(PORT, () => console.log(`KNOTS server running on port ${PORT}`)); 46 | 47 | io.on('connection', (socket) => { 48 | socket.on('terminate', (mode) => { 49 | let runningProcess; 50 | if (mode === 'discovery') { 51 | runningProcess = terminateDiscovery(); 52 | } else if (mode === 'sync') { 53 | runningProcess = terminateSync(); 54 | } 55 | 56 | terminate(runningProcess); 57 | }); 58 | }); 59 | 60 | io.on('disconnect', () => { 61 | console.log('Socket disconnected'); 62 | }); 63 | -------------------------------------------------------------------------------- /app/backend/routes/targets.js: -------------------------------------------------------------------------------- 1 | /* 2 | * knots 3 | * Copyright 2018 data.world, Inc. 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the 7 | * License. 8 | * 9 | * You may obtain a copy of the License at 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, software 13 | * distributed under the License is distributed on an "AS IS" BASIS, 14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 15 | * implied. See the License for the specific language governing 16 | * permissions and limitations under the License. 17 | * 18 | * This product includes software developed at 19 | * data.world, Inc.(http://data.world/). 20 | */ 21 | 22 | const path = require('path'); 23 | const router = require('express').Router(); 24 | 25 | const { getTargets } = require('../targets'); 26 | const { 27 | addKnotAttribute, 28 | getTemporaryKnotFolder, 29 | writeFile 30 | } = require('../util'); 31 | 32 | router.get('/', (req, res) => { 33 | getTargets() 34 | .then((targets) => res.json({ targets })) 35 | .catch((error) => { 36 | res.status(500).json({ message: error.message }); 37 | }); 38 | }); 39 | 40 | router.post('/', (req, res) => { 41 | const configPath = path.resolve( 42 | getTemporaryKnotFolder(req.body.uuid), 43 | 'target', 44 | 'config.json' 45 | ); 46 | 47 | writeFile(configPath, JSON.stringify(req.body.fieldValues)) 48 | .then(() => res.json({})) 49 | .catch((error) => { 50 | res.status(500).json({ message: error.message }); 51 | }); 52 | }); 53 | 54 | router.post('/select', (req, res) => { 55 | addKnotAttribute( 56 | { field: 'target', value: req.body.target }, 57 | null, 58 | req.body.uuid 59 | ) 60 | .then(() => { 61 | res.json({}); 62 | }) 63 | .catch((error) => { 64 | res.status(500).json({ message: error.message }); 65 | }); 66 | }); 67 | 68 | module.exports = router; 69 | -------------------------------------------------------------------------------- /test/reducers/targets.reducer.spec.js: -------------------------------------------------------------------------------- 1 | import targetReducer, { defaultState } from '../../app/reducers/targets'; 2 | import * as targetActions from '../../app/actions/targets'; 3 | import { LOADED_KNOT, RESET_STORE } from '../../app/actions/knots'; 4 | import { sampleTargets } from '../utils'; 5 | 6 | describe('target reducer', () => { 7 | it('should return the initial state', () => { 8 | expect(targetReducer(undefined, {})).toEqual(defaultState()); 9 | }); 10 | 11 | it('should handle TARGETS_LOADING', () => { 12 | expect( 13 | targetReducer(undefined, { 14 | type: targetActions.TARGETS_LOADING 15 | }) 16 | ).toEqual( 17 | Object.assign({}, defaultState(), { 18 | targetsLoading: true 19 | }) 20 | ); 21 | }); 22 | 23 | it('should handle UPDATE_TARGETS', () => { 24 | expect( 25 | targetReducer(undefined, { 26 | type: targetActions.UPDATE_TARGETS, 27 | targets: sampleTargets, 28 | error: '' 29 | }) 30 | ).toEqual( 31 | Object.assign({}, defaultState(), { 32 | targetsLoading: false, 33 | targets: sampleTargets 34 | }) 35 | ); 36 | }); 37 | 38 | it('should handle TARGET_SELECTED', () => { 39 | expect( 40 | targetReducer(undefined, { 41 | type: targetActions.TARGET_SELECTED, 42 | target: sampleTargets[0], 43 | error: '' 44 | }) 45 | ).toEqual( 46 | Object.assign({}, defaultState(), { 47 | targetSelected: true, 48 | selectedTarget: sampleTargets[0] 49 | }) 50 | ); 51 | }); 52 | 53 | it('should handle LOADED_KNOT', () => { 54 | expect( 55 | targetReducer(undefined, { 56 | type: LOADED_KNOT, 57 | target: sampleTargets[0] 58 | }) 59 | ).toEqual( 60 | Object.assign({}, defaultState(), { 61 | selectedTarget: sampleTargets[0] 62 | }) 63 | ); 64 | }); 65 | 66 | it('should handle RESET_STORE', () => { 67 | expect( 68 | targetReducer(undefined, { 69 | type: RESET_STORE 70 | }) 71 | ).toEqual(Object.assign({}, defaultState())); 72 | }); 73 | }); 74 | -------------------------------------------------------------------------------- /test/utils.js: -------------------------------------------------------------------------------- 1 | /* 2 | * knots 3 | * Copyright 2018 data.world, Inc. 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the 7 | * License. 8 | * 9 | * You may obtain a copy of the License at 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, software 13 | * distributed under the License is distributed on an "AS IS" BASIS, 14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 15 | * implied. See the License for the specific language governing 16 | * permissions and limitations under the License. 17 | * 18 | * This product includes software developed at 19 | * data.world, Inc.(http://data.world/). 20 | */ 21 | 22 | export const sampleTargets = [ 23 | { 24 | name: 'SampleTarget1', 25 | targetKey: 'target-sample1', 26 | targetImage: '', 27 | repo: '' 28 | }, 29 | { 30 | name: 'SampleTarget2', 31 | targetKey: 'target-sample2', 32 | targetImage: '', 33 | repo: '' 34 | } 35 | ]; 36 | 37 | export const sampleTaps = [ 38 | { 39 | name: 'sampleTap1', 40 | tapImage: 'sampleTap1', 41 | tapKey: 'tap-sampleTap1' 42 | }, 43 | { 44 | name: 'sampleTap2', 45 | tapImage: 'sampleTap2', 46 | tapKey: 'tap-sampleTap2' 47 | } 48 | ]; 49 | 50 | export const sampleSelectedTap = { 51 | name: 'tap-salesforce', 52 | image: '' 53 | }; 54 | 55 | export const sampleUpdatedSFTap = { 56 | valid: false, 57 | fieldValues: { 58 | api_type: 'BULK', 59 | client_id: '12090', 60 | client_secret: '', 61 | refresh_token: '', 62 | select_fields_by_default: true, 63 | start_date: '' 64 | } 65 | }; 66 | 67 | export const sampleUpdatedRedshiftTap = { 68 | valid: false, 69 | fieldValues: { 70 | host: 'testing', 71 | port: undefined, 72 | dbname: '', 73 | schema: 'public', 74 | user: '', 75 | password: '', 76 | start_date: '' 77 | } 78 | }; 79 | 80 | export const sampleTapConfig = { start_date: '01-01-2017' }; 81 | 82 | export const sampleSchema = [{ tap_stream_id: 'testing' }]; 83 | -------------------------------------------------------------------------------- /app/components/Home/Create/index.js: -------------------------------------------------------------------------------- 1 | /* 2 | * knots 3 | * Copyright 2018 data.world, Inc. 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the 7 | * License. 8 | * 9 | * You may obtain a copy of the License at 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, software 13 | * distributed under the License is distributed on an "AS IS" BASIS, 14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 15 | * implied. See the License for the specific language governing 16 | * permissions and limitations under the License. 17 | * 18 | * This product includes software developed at 19 | * data.world, Inc.(http://data.world/). 20 | */ 21 | 22 | // @flow 23 | 24 | import React, { Component } from 'react'; 25 | import { Link } from 'react-router-dom'; 26 | import { Button, Jumbotron } from 'reactstrap'; 27 | import { shell } from 'electron'; 28 | 29 | type Props = { 30 | dockerInstalled: boolean, 31 | dockerRunning: boolean, 32 | generateUUID: () => void 33 | }; 34 | 35 | class Create extends Component { 36 | openLink = (e: SyntheticEvent, url: string) => { 37 | e.preventDefault(); 38 | shell.openExternal(url); 39 | }; 40 | 41 | render() { 42 | const { dockerInstalled, dockerRunning, generateUUID } = this.props; 43 | 44 | return ( 45 | 46 |

47 | There is knothing here yet. 48 |

49 |

50 | KNOTS allows you to set up simple data pipelines leveraging{' '} 51 | this.openLink(e, 'https://singer.io')}> 52 | Singer 53 | 54 |  open-source technology. 55 |

56 | 57 | 65 | 66 |
67 | ); 68 | } 69 | } 70 | 71 | export default Create; 72 | -------------------------------------------------------------------------------- /app/components/KnotProgress/index.js: -------------------------------------------------------------------------------- 1 | /* 2 | * knots 3 | * Copyright 2018 data.world, Inc. 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the 7 | * License. 8 | * 9 | * You may obtain a copy of the License at 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, software 13 | * distributed under the License is distributed on an "AS IS" BASIS, 14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 15 | * implied. See the License for the specific language governing 16 | * permissions and limitations under the License. 17 | * 18 | * This product includes software developed at 19 | * data.world, Inc.(http://data.world/). 20 | */ 21 | 22 | import React from 'react'; 23 | import { Row, Col, Progress, Nav } from 'reactstrap'; 24 | 25 | import Item from './Item'; 26 | 27 | type Props = { 28 | progressStore: {}, 29 | tapsStore: { selectedTap: { name: string } }, 30 | targetsStore: { selectedTarget: { name: string } }, 31 | knotsStore: { knotName: string }, 32 | userStore: {} 33 | }; 34 | 35 | const calculateProgress = (items) => { 36 | let indexOfActiveItem = 0; 37 | Object.keys(items).forEach((key, index) => { 38 | if (items[key].active) { 39 | indexOfActiveItem = index; 40 | } 41 | }); 42 | 43 | if (items[3].complete) { 44 | return 100; 45 | } 46 | 47 | return indexOfActiveItem * 33.3; 48 | }; 49 | 50 | const KnotProgress = (props: Props) => ( 51 | 52 | 53 | 58 | 59 | 72 | 73 | 74 | ); 75 | 76 | export default KnotProgress; 77 | -------------------------------------------------------------------------------- /internals/scripts/CheckNativeDep.js: -------------------------------------------------------------------------------- 1 | // @flow 2 | import fs from 'fs'; 3 | import chalk from 'chalk'; 4 | import { execSync } from 'child_process'; 5 | import { dependencies } from '../../package.json'; 6 | 7 | (() => { 8 | if (!dependencies) return; 9 | 10 | const dependenciesKeys = Object.keys(dependencies); 11 | const nativeDeps = fs 12 | .readdirSync('node_modules') 13 | .filter((folder) => fs.existsSync(`node_modules/${folder}/binding.gyp`)); 14 | 15 | try { 16 | // Find the reason for why the dependency is installed. If it is installed 17 | // because of a devDependency then that is okay. Warn when it is installed 18 | // because of a dependency 19 | const dependenciesObject = JSON.parse( 20 | execSync(`npm ls ${nativeDeps.join(' ')} --json`).toString() 21 | ); 22 | const rootDependencies = Object.keys(dependenciesObject.dependencies); 23 | const filteredRootDependencies = rootDependencies.filter((rootDependency) => 24 | dependenciesKeys.includes(rootDependency) 25 | ); 26 | 27 | if (filteredRootDependencies.length > 0) { 28 | const plural = filteredRootDependencies.length > 1; 29 | console.log(` 30 | 31 | ${chalk.whiteBright.bgYellow.bold( 32 | 'Webpack does not work with native dependencies.' 33 | )} 34 | ${chalk.bold(filteredRootDependencies.join(', '))} ${ 35 | plural ? 'are native dependencies' : 'is a native dependency' 36 | } and should be installed inside of the "./app" folder. 37 | 38 | 39 | First uninstall the packages from "./package.json": 40 | ${chalk.whiteBright.bgGreen.bold('npm uninstall your-package')} 41 | 42 | ${chalk.bold( 43 | 'Then, instead of installing the package to the root "./package.json":' 44 | )} 45 | ${chalk.whiteBright.bgRed.bold('npm install your-package --save')} 46 | 47 | ${chalk.bold('Install the package to "./app/package.json"')} 48 | ${chalk.whiteBright.bgGreen.bold('cd ./app && npm install your-package --save')} 49 | 50 | 51 | Read more about native dependencies at: 52 | ${chalk.bold( 53 | 'https://github.com/chentsulin/electron-react-boilerplate/wiki/Module-Structure----Two-package.json-Structure' 54 | )} 55 | 56 | 57 | `); 58 | 59 | process.exit(1); 60 | } 61 | } catch (e) { 62 | console.log('Native dependencies could not be checked'); 63 | } 64 | })(); 65 | -------------------------------------------------------------------------------- /app/reducers/targets.js: -------------------------------------------------------------------------------- 1 | /* 2 | * knots 3 | * Copyright 2018 data.world, Inc. 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the 7 | * License. 8 | * 9 | * You may obtain a copy of the License at 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, software 13 | * distributed under the License is distributed on an "AS IS" BASIS, 14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 15 | * implied. See the License for the specific language governing 16 | * permissions and limitations under the License. 17 | * 18 | * This product includes software developed at 19 | * data.world, Inc.(http://data.world/). 20 | */ 21 | 22 | import { 23 | UPDATE_TARGETS, 24 | TARGETS_LOADING, 25 | TARGET_SELECTED 26 | } from '../actions/targets'; 27 | import { LOADED_KNOT, RESET_STORE, LOADED_KNOT_JSON } from '../actions/knots'; 28 | 29 | export type targetsStateType = { 30 | +targets: Array<{}>, 31 | +targetsLoading: boolean, 32 | +targetSelected: boolean, 33 | +selectedTarget: { name: sring, image: string } 34 | }; 35 | 36 | export function defaultState() { 37 | return { 38 | targets: [], 39 | targetsLoading: false, 40 | targetInstalled: false, 41 | targetSelected: false, 42 | selectedTarget: { name: '', image: '' } 43 | }; 44 | } 45 | 46 | export default function targets(state = defaultState(), action) { 47 | switch (action.type) { 48 | case TARGETS_LOADING: 49 | return Object.assign({}, state, { 50 | targetsLoading: true 51 | }); 52 | case UPDATE_TARGETS: 53 | return Object.assign({}, state, { 54 | targetsLoading: false, 55 | targets: action.targets 56 | }); 57 | case TARGET_SELECTED: 58 | return Object.assign({}, state, { 59 | targetSelected: true, 60 | selectedTarget: action.target 61 | }); 62 | case LOADED_KNOT: 63 | return Object.assign({}, state, { 64 | selectedTarget: action.target 65 | }); 66 | case LOADED_KNOT_JSON: 67 | return Object.assign({}, state, { 68 | selectedTarget: action.target 69 | }); 70 | case RESET_STORE: 71 | return defaultState(); 72 | default: 73 | return state; 74 | } 75 | } 76 | -------------------------------------------------------------------------------- /app/backend/docker.js: -------------------------------------------------------------------------------- 1 | /* 2 | * knots 3 | * Copyright 2018 data.world, Inc. 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the 7 | * License. 8 | * 9 | * You may obtain a copy of the License at 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, software 13 | * distributed under the License is distributed on an "AS IS" BASIS, 14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 15 | * implied. See the License for the specific language governing 16 | * permissions and limitations under the License. 17 | * 18 | * This product includes software developed at 19 | * data.world, Inc.(http://data.world/). 20 | */ 21 | 22 | const { spawn } = require('child_process'); 23 | 24 | const dockerInstalled = (mockSpawn) => 25 | new Promise((resolve, reject) => { 26 | const spawnFunction = mockSpawn || spawn; 27 | 28 | // Try to find out the docker version installed 29 | const dockerVersion = spawnFunction('docker', ['-v']); 30 | 31 | // A version number was returned, docker is installed 32 | dockerVersion.stdout.on('data', (version) => { 33 | resolve(version.toString('utf8')); 34 | }); 35 | 36 | dockerVersion.on('error', () => { 37 | reject(new Error('Unable to run Docker')); 38 | }); 39 | 40 | dockerVersion.on('exit', (code) => { 41 | if (code > 0) { 42 | reject(new Error('Unable to run Docker')); 43 | } 44 | }); 45 | }); 46 | 47 | const dockerRunning = (mockSpawn) => 48 | new Promise((resolve, reject) => { 49 | const spawnFunction = mockSpawn || spawn; 50 | 51 | // Try to find out the docker version installed 52 | const dockerVolumes = spawnFunction('docker', ['volume', 'ls']); 53 | 54 | // A version number was returned, docker is installed 55 | dockerVolumes.stdout.on('data', (version) => { 56 | resolve(version.toString('utf8')); 57 | }); 58 | 59 | dockerVolumes.on('error', () => { 60 | reject(new Error('Unable to get Docker volumes')); 61 | }); 62 | 63 | dockerVolumes.on('exit', (code) => { 64 | if (code > 0) { 65 | reject(new Error('Unable to get Docker volumes')); 66 | } 67 | }); 68 | }); 69 | 70 | module.exports = { dockerInstalled, dockerRunning }; 71 | -------------------------------------------------------------------------------- /app/utils/schema.js: -------------------------------------------------------------------------------- 1 | /* 2 | * knots 3 | * Copyright 2018 data.world, Inc. 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the 7 | * License. 8 | * 9 | * You may obtain a copy of the License at 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, software 13 | * distributed under the License is distributed on an "AS IS" BASIS, 14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 15 | * implied. See the License for the specific language governing 16 | * permissions and limitations under the License. 17 | * 18 | * This product includes software developed at 19 | * data.world, Inc.(http://data.world/). 20 | */ 21 | // @flow 22 | 23 | import type { Stream, SubMetadata } from './sharedTypes'; 24 | 25 | // Find metadata with empty breadcrumb and return its index and metadata 26 | export const getMetadata = ( 27 | stream: Stream 28 | ): { index?: number, metadata?: {} } => { 29 | const { metadata = [] } = stream; 30 | 31 | let index; 32 | metadata.forEach((meta, metaIndex) => { 33 | if (meta.breadcrumb.length === 0) { 34 | index = metaIndex; 35 | } 36 | }); 37 | 38 | if (index === undefined) { 39 | return { index }; 40 | } 41 | 42 | return { index, metadata: metadata[index].metadata }; 43 | }; 44 | 45 | export const getColumns = (stream: Stream): Array => { 46 | const { metadata = [] } = stream; 47 | 48 | const columns = metadata 49 | .filter((meta) => { 50 | if (meta.breadcrumb.length > 1) { 51 | if ( 52 | meta.breadcrumb[0] === 'properties' && 53 | meta.metadata.inclusion !== 'unsupported' 54 | ) { 55 | return true; 56 | } 57 | } 58 | return false; 59 | }) 60 | .map((meta) => meta.breadcrumb[1]); 61 | 62 | return columns; 63 | }; 64 | 65 | export const getReplicationKey = ( 66 | stream: { replication_key?: string }, 67 | metadata: SubMetadata, 68 | specImplementation: { usesMetadata?: { replicationKey?: boolean } } 69 | ): string => { 70 | const { replicationKey: repKeyMetadata = true } = 71 | specImplementation.usesMetadata || {}; 72 | if (!repKeyMetadata) { 73 | return stream.replication_key || ''; 74 | } 75 | 76 | return metadata['replication-key'] || ''; 77 | }; 78 | -------------------------------------------------------------------------------- /app/app.global.scss: -------------------------------------------------------------------------------- 1 | /* 2 | * knots 3 | * Copyright 2018 data.world, Inc. 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the 7 | * License. 8 | * 9 | * You may obtain a copy of the License at 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, software 13 | * distributed under the License is distributed on an "AS IS" BASIS, 14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 15 | * implied. See the License for the specific language governing 16 | * permissions and limitations under the License. 17 | * 18 | * This product includes software developed at 19 | * data.world, Inc.(http://data.world/). 20 | */ 21 | 22 | /* 23 | * @NOTE: Prepend a `~` to css file paths that are in your node_modules 24 | * See https://github.com/webpack-contrib/sass-loader#imports 25 | */ 26 | 27 | $theme-colors: ( 28 | 'primary': #5c56a5 29 | ); 30 | 31 | @import '~bootstrap/scss/bootstrap'; 32 | 33 | body { 34 | margin: 0; 35 | font-family: Arial, Helvetica, sans-serif; 36 | } 37 | 38 | .knot-wiz .progress { 39 | position: relative; 40 | bottom: calc(-1.25rem + 1px); 41 | left: 12.5%; 42 | z-index: -1; 43 | width: 75%; 44 | } 45 | 46 | .knot-wiz .nav-link { 47 | width: 32px; 48 | height: 32px; 49 | margin: 0 auto 0.25rem; 50 | padding: 0; 51 | border-radius: 50% !important; 52 | background-color: #e9ecef; 53 | border: 2px solid #e9ecef; 54 | box-sizing: initial; 55 | display: flex; 56 | align-items: center; 57 | justify-content: center; 58 | position: relative; 59 | transition: border-color linear 0.2s, background-color linear 0.2s; 60 | } 61 | 62 | .knot-wiz .nav-link .oi, 63 | .knot-wiz .nav-link .step { 64 | transition: all linear 0.2s; 65 | position: absolute; 66 | top: 50%; 67 | transform: translateY(-50%); 68 | font-weight: bold; 69 | } 70 | 71 | .knot-wiz .nav-link .oi { 72 | opacity: 0; 73 | position: absolute; 74 | top: 100%; 75 | } 76 | 77 | .knot-wiz .nav-link.active { 78 | border-color: #5c56a5; 79 | } 80 | 81 | .knot-wiz .nav-link.completed { 82 | border-color: #28a745; 83 | color: #28a745; 84 | background-color: #fff; 85 | } 86 | 87 | .knot-wiz .nav-link.completed .step { 88 | opacity: 0; 89 | } 90 | 91 | .knot-wiz .nav-link.completed .oi { 92 | opacity: 1; 93 | top: calc(50% + 1px); 94 | } 95 | -------------------------------------------------------------------------------- /app/components/Taps/TapConfiguration/index.js: -------------------------------------------------------------------------------- 1 | /* 2 | * knots 3 | * Copyright 2018 data.world, Inc. 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the 7 | * License. 8 | * 9 | * You may obtain a copy of the License at 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, software 13 | * distributed under the License is distributed on an "AS IS" BASIS, 14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 15 | * implied. See the License for the specific language governing 16 | * permissions and limitations under the License. 17 | * 18 | * This product includes software developed at 19 | * data.world, Inc.(http://data.world/). 20 | */ 21 | 22 | // @flow 23 | 24 | import React, { Component } from 'react'; 25 | import { Container, Row } from 'reactstrap'; 26 | 27 | import Redshift from '../../../containers/Redshift'; 28 | import Salesforce from '../../../containers/Salesforce'; 29 | import Postgres from '../../../containers/Postgres'; 30 | import Adwords from '../../../containers/Adwords'; 31 | import MySQL from '../../../containers/MySQL'; 32 | import Facebook from '../../../containers/Facebook'; 33 | import S3 from '../../../containers/S3'; 34 | 35 | type TapConfigType = { 36 | fieldValues: T & { 37 | start_date: string 38 | }, 39 | valid?: boolean 40 | }; 41 | 42 | type Props = { 43 | tapsStore: { 44 | selectedTap: { name: string, image: string } 45 | } 46 | }; 47 | 48 | class Tap extends Component { 49 | selectedTarget = () => { 50 | const { selectedTap } = this.props.tapsStore; 51 | switch (selectedTap.name) { 52 | case 'tap-redshift': 53 | return ; 54 | case 'tap-salesforce': 55 | return ; 56 | case 'tap-postgres': 57 | return ; 58 | case 'tap-adwords': 59 | return ; 60 | case 'tap-mysql': 61 | return ; 62 | case 'tap-facebook': 63 | return ; 64 | case 'tap-s3-csv': 65 | return ; 66 | default: 67 | return
Unknown Tap
; 68 | } 69 | }; 70 | render() { 71 | return ( 72 | 73 | {this.selectedTarget()} 74 | 75 | ); 76 | } 77 | } 78 | 79 | export default Tap; 80 | export type { TapConfigType }; 81 | -------------------------------------------------------------------------------- /app/app.html: -------------------------------------------------------------------------------- 1 | 2 | 22 | 23 | 24 | 25 | 26 | 27 | KNOTS 28 | 39 | 40 | 41 | 42 | 43 | 44 |
45 | 46 | 71 | 72 | 73 | 74 | -------------------------------------------------------------------------------- /app/components/Taps/TapConfiguration/IncrementalSyncToggle/index.js: -------------------------------------------------------------------------------- 1 | /* 2 | * knots 3 | * Copyright 2018 data.world, Inc. 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the 7 | * License. 8 | * 9 | * You may obtain a copy of the License at 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, software 13 | * distributed under the License is distributed on an "AS IS" BASIS, 14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 15 | * implied. See the License for the specific language governing 16 | * permissions and limitations under the License. 17 | * 18 | * This product includes software developed at 19 | * data.world, Inc.(http://data.world/). 20 | */ 21 | 22 | // @flow 23 | 24 | import React, { Component } from 'react'; 25 | import { FormGroup, Input, Label } from 'reactstrap'; 26 | 27 | type Props = { 28 | checked: boolean, 29 | toggleModal: (checkBoxState: boolean) => void, 30 | updateLogBaseRepMethod: (usesLogBaseRepMethod: boolean) => void, 31 | currentValue: boolean, 32 | knotLoaded: boolean, 33 | deactivateNavigation: () => void, 34 | activateNavigation: () => void 35 | }; 36 | 37 | type State = { 38 | checked: boolean 39 | }; 40 | 41 | export default class IncrementalSyncToggle extends Component { 42 | state = { checked: this.props.checked }; 43 | 44 | componentWillReceiveProps(nextProps: Props) { 45 | this.setState({ checked: nextProps.checked }); 46 | } 47 | 48 | handleChange = (e) => { 49 | const checkBoxState = e.target.checked; 50 | const { currentValue, knotLoaded } = this.props; 51 | if (knotLoaded) { 52 | if (currentValue !== checkBoxState) { 53 | this.props.deactivateNavigation(); 54 | } else { 55 | this.props.activateNavigation(); 56 | } 57 | } 58 | if (!checkBoxState) { 59 | this.props.updateLogBaseRepMethod(false); 60 | } 61 | this.props.toggleModal(checkBoxState); 62 | this.setState({ checked: checkBoxState }); 63 | }; 64 | 65 | render() { 66 | return ( 67 | 68 | 76 | 77 | ); 78 | } 79 | } 80 | -------------------------------------------------------------------------------- /app/img/knots.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 5 | 9 | 10 | 11 | 12 | 14 | 15 | 17 | 27 | 28 | 30 | 31 | 34 | 35 | 36 | -------------------------------------------------------------------------------- /app/img/tap-mysql.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 5 | 6 | 7 | 23 | 26 | 27 | 28 | -------------------------------------------------------------------------------- /app/reducers/user.js: -------------------------------------------------------------------------------- 1 | /* 2 | * knots 3 | * Copyright 2018 data.world, Inc. 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the 7 | * License. 8 | * 9 | * You may obtain a copy of the License at 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, software 13 | * distributed under the License is distributed on an "AS IS" BASIS, 14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 15 | * implied. See the License for the specific language governing 16 | * permissions and limitations under the License. 17 | * 18 | * This product includes software developed at 19 | * data.world, Inc.(http://data.world/). 20 | */ 21 | 22 | /* eslint-disable no-case-declarations */ 23 | 24 | import { UPDATE_TARGET_FIELD } from '../actions/user'; 25 | import { LOADED_KNOT, RESET_STORE } from '../actions/knots'; 26 | 27 | export type targetsStateType = { 28 | +targetConfigured: boolean, 29 | +'target-datadotworld': { 30 | fieldValues: { 31 | dataset_url: string, 32 | dataset_id: string, 33 | dataset_owner: string, 34 | api_token: string 35 | } 36 | }, 37 | +'target-stitch': { fieldValues: { client_id: string, token: string } } 38 | }; 39 | 40 | export const defaultState = { 41 | targetConfigured: false, 42 | 'target-datadotworld': { 43 | fieldValues: { 44 | dataset_id: '', 45 | dataset_owner: '', 46 | dataset_url: '', 47 | api_token: '' 48 | } 49 | }, 50 | 'target-stitch': { fieldValues: { client_id: '', token: '' } } 51 | }; 52 | 53 | export default function targets(state = defaultState, action) { 54 | switch (action.type) { 55 | case UPDATE_TARGET_FIELD: 56 | const target = state[action.target]; 57 | target.fieldValues[action.field] = action.value; 58 | 59 | return Object.assign({}, state, { 60 | [action.target]: target 61 | }); 62 | case LOADED_KNOT: 63 | const savedTarget = state[action.target.name]; 64 | savedTarget.fieldValues = action.targetConfig; 65 | 66 | return Object.assign({}, state, { [action.target.name]: savedTarget }); 67 | 68 | case RESET_STORE: 69 | // Fact that objects are passed by reference makes this necessary, open to other suggestions 70 | return { 71 | targetConfigured: false, 72 | 'target-datadotworld': { 73 | fieldValues: { 74 | dataset_id: '', 75 | dataset_owner: '', 76 | dataset_url: '', 77 | api_token: '' 78 | } 79 | }, 80 | 'target-stitch': { fieldValues: { client_id: '', token: '' } } 81 | }; 82 | default: 83 | return state; 84 | } 85 | } 86 | -------------------------------------------------------------------------------- /test/reducers/user.reducer.spec.js: -------------------------------------------------------------------------------- 1 | import userReducer, { defaultState } from '../../app/reducers/user'; 2 | import * as userActions from '../../app/actions/user'; 3 | import { LOADED_KNOT, RESET_STORE } from '../../app/actions/knots'; 4 | 5 | const selectedTarget = { 6 | name: 'target-datadotworld' 7 | }; 8 | const updatedDataWorldTarget = { 9 | fieldValues: { 10 | dataset_id: 'testing', 11 | dataset_owner: '', 12 | dataset_url: '', 13 | api_token: '' 14 | } 15 | }; 16 | const updatedStitchTarget = { 17 | fieldValues: { 18 | client_id: 'testing', 19 | token: '' 20 | } 21 | }; 22 | const targetConfig = { 23 | dataset_owner: 'tester' 24 | }; 25 | 26 | describe('user reducer', () => { 27 | it('should return the initial state', () => { 28 | expect(userReducer(undefined, {})).toEqual(defaultState); 29 | }); 30 | 31 | it('should handle UPDATE_TARGET_FIELD', () => { 32 | expect( 33 | userReducer(undefined, { 34 | type: userActions.UPDATE_TARGET_FIELD, 35 | target: 'target-datadotworld', 36 | field: 'dataset_id', 37 | value: 'testing' 38 | }) 39 | ).toEqual( 40 | Object.assign({}, defaultState, { 41 | 'target-datadotworld': updatedDataWorldTarget 42 | }) 43 | ); 44 | 45 | expect( 46 | userReducer(undefined, { 47 | type: userActions.UPDATE_TARGET_FIELD, 48 | target: 'target-stitch', 49 | field: 'client_id', 50 | value: 'testing' 51 | }) 52 | ).toEqual( 53 | Object.assign({}, defaultState, { 54 | 'target-stitch': updatedStitchTarget 55 | }) 56 | ); 57 | }); 58 | 59 | it('should handle LOADED_KNOT', () => { 60 | expect( 61 | userReducer(undefined, { 62 | type: LOADED_KNOT, 63 | target: selectedTarget, 64 | targetConfig 65 | }) 66 | ).toEqual( 67 | Object.assign({}, defaultState, { 68 | 'target-datadotworld': { 69 | fieldValues: { 70 | dataset_owner: 'tester' 71 | } 72 | } 73 | }) 74 | ); 75 | }); 76 | 77 | it('should handle RESET_STORE', () => { 78 | expect( 79 | userReducer(undefined, { 80 | type: RESET_STORE 81 | }) 82 | ).toEqual( 83 | Object.assign({}, defaultState, { 84 | targetConfigured: false, 85 | 'target-datadotworld': { 86 | fieldValues: { 87 | dataset_id: '', 88 | dataset_owner: '', 89 | dataset_url: '', 90 | api_token: '' 91 | } 92 | }, 93 | 'target-stitch': { 94 | fieldValues: { 95 | client_id: '', 96 | token: '' 97 | } 98 | } 99 | }) 100 | ); 101 | }); 102 | }); 103 | -------------------------------------------------------------------------------- /app/components/Targets/Target/index.js: -------------------------------------------------------------------------------- 1 | /* 2 | * knots 3 | * Copyright 2018 data.world, Inc. 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the 7 | * License. 8 | * 9 | * You may obtain a copy of the License at 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, software 13 | * distributed under the License is distributed on an "AS IS" BASIS, 14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 15 | * implied. See the License for the specific language governing 16 | * permissions and limitations under the License. 17 | * 18 | * This product includes software developed at 19 | * data.world, Inc.(http://data.world/). 20 | */ 21 | 22 | // @flow 23 | 24 | import React, { Component } from 'react'; 25 | import { Col, Card, CardBody, CardTitle } from 'reactstrap'; 26 | 27 | import getLogo from '../../../logos'; 28 | 29 | type Props = { 30 | name: string, 31 | targetKey: string, 32 | targetImage: string, 33 | selected: string, 34 | selectTarget: ( 35 | tap: { name: string, image: string }, 36 | uuid: string, 37 | knotName: string 38 | ) => void, 39 | uuid: string, 40 | knotName: string 41 | }; 42 | 43 | type State = { 44 | hovered: boolean 45 | }; 46 | 47 | export default class Target extends Component { 48 | state = { 49 | hovered: false 50 | }; 51 | 52 | getBorderState = () => { 53 | const { hovered } = this.state; 54 | const { targetKey, selected } = this.props; 55 | 56 | if (hovered) { 57 | return 'border-primary'; 58 | } else if (selected === targetKey) { 59 | return 'border-success'; 60 | } 61 | 62 | return ''; 63 | }; 64 | 65 | render() { 66 | const { targetKey, targetImage, uuid, knotName, name } = this.props; 67 | return ( 68 | 69 | this.setState({ hovered: true })} 73 | onMouseLeave={() => this.setState({ hovered: false })} 74 | onClick={() => { 75 | this.props.selectTarget( 76 | { 77 | name: targetKey, 78 | image: targetImage 79 | }, 80 | uuid, 81 | knotName 82 | ); 83 | }} 84 | > 85 | 86 | 87 | {name} 93 | {name} 94 | 95 | 96 | 97 | 98 | ); 99 | } 100 | } 101 | -------------------------------------------------------------------------------- /app/store/configureStore.dev.js: -------------------------------------------------------------------------------- 1 | /* 2 | * knots 3 | * Copyright 2018 data.world, Inc. 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the 7 | * License. 8 | * 9 | * You may obtain a copy of the License at 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, software 13 | * distributed under the License is distributed on an "AS IS" BASIS, 14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 15 | * implied. See the License for the specific language governing 16 | * permissions and limitations under the License. 17 | * 18 | * This product includes software developed at 19 | * data.world, Inc.(http://data.world/). 20 | */ 21 | 22 | import { createStore, applyMiddleware, compose } from 'redux'; 23 | import thunk from 'redux-thunk'; 24 | import { createHashHistory } from 'history'; 25 | import { routerMiddleware, routerActions } from 'react-router-redux'; 26 | import { createLogger } from 'redux-logger'; 27 | 28 | import rootReducer from '../reducers'; 29 | import * as knotsActions from '../actions/knots'; 30 | import * as tapsActions from '../actions/taps'; 31 | import * as targetsActions from '../actions/targets'; 32 | import * as connectActions from '../actions/connect'; 33 | import * as userActions from '../actions/user'; 34 | 35 | const history = createHashHistory(); 36 | 37 | const configureStore = (initialState) => { 38 | // Redux Configuration 39 | const middleware = []; 40 | const enhancers = []; 41 | 42 | // Thunk Middleware 43 | middleware.push(thunk); 44 | 45 | // Logging Middleware 46 | const logger = createLogger({ 47 | level: 'info', 48 | collapsed: true 49 | }); 50 | 51 | // Skip redux logs in console during the tests 52 | if (process.env.NODE_ENV !== 'test') { 53 | middleware.push(logger); 54 | } 55 | 56 | // Router Middleware 57 | const router = routerMiddleware(history); 58 | middleware.push(router); 59 | 60 | // Redux DevTools Configuration 61 | const actionCreators = { 62 | ...knotsActions, 63 | ...tapsActions, 64 | ...targetsActions, 65 | ...connectActions, 66 | ...userActions, 67 | ...routerActions 68 | }; 69 | // If Redux DevTools Extension is installed use it, otherwise use Redux compose 70 | /* eslint-disable no-underscore-dangle */ 71 | const composeEnhancers = window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__ 72 | ? window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__({ 73 | // Options: http://zalmoxisus.github.io/redux-devtools-extension/API/Arguments.html 74 | actionCreators 75 | }) 76 | : compose; 77 | /* eslint-enable no-underscore-dangle */ 78 | 79 | // Apply Middleware & Compose Enhancers 80 | enhancers.push(applyMiddleware(...middleware)); 81 | const enhancer = composeEnhancers(...enhancers); 82 | 83 | // Create Store 84 | const store = createStore(rootReducer, initialState, enhancer); 85 | 86 | if (module.hot) { 87 | module.hot.accept( 88 | '../reducers', 89 | () => store.replaceReducer(require('../reducers')) // eslint-disable-line global-require 90 | ); 91 | } 92 | 93 | return store; 94 | }; 95 | 96 | export default { configureStore, history }; 97 | -------------------------------------------------------------------------------- /app/components/Taps/Tap/index.js: -------------------------------------------------------------------------------- 1 | /* 2 | * knots 3 | * Copyright 2018 data.world, Inc. 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the 7 | * License. 8 | * 9 | * You may obtain a copy of the License at 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, software 13 | * distributed under the License is distributed on an "AS IS" BASIS, 14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 15 | * implied. See the License for the specific language governing 16 | * permissions and limitations under the License. 17 | * 18 | * This product includes software developed at 19 | * data.world, Inc.(http://data.world/). 20 | */ 21 | 22 | // @flow 23 | 24 | import React, { Component } from 'react'; 25 | import { Card, CardBody, CardTitle, Col } from 'reactstrap'; 26 | 27 | import getLogo from '../../../logos'; 28 | import type { 29 | SpecImplementationPropType, 30 | TapPropertiesType 31 | } from '../../../utils/sharedTypes'; 32 | 33 | type Props = { 34 | name: string, 35 | tapKey: string, 36 | tapImage: string, 37 | selected: string, 38 | repo: string, 39 | specImplementation?: SpecImplementationPropType, 40 | selectTap: (tap: TapPropertiesType, knotName: string, uuid: string) => void, 41 | knotName: string, 42 | uuid: string 43 | }; 44 | 45 | type State = { 46 | hovered: boolean 47 | }; 48 | 49 | export default class Tap extends Component { 50 | static defaultProps = { 51 | specImplementation: {} 52 | }; 53 | 54 | state = { 55 | hovered: false 56 | }; 57 | 58 | getBorderState = () => { 59 | const { hovered } = this.state; 60 | const { tapKey, selected } = this.props; 61 | 62 | if (hovered) { 63 | return 'border-primary'; 64 | } else if (selected === tapKey) { 65 | return 'border-success'; 66 | } 67 | 68 | return ''; 69 | }; 70 | 71 | render() { 72 | const { 73 | tapKey, 74 | tapImage, 75 | repo, 76 | specImplementation, 77 | knotName, 78 | name 79 | } = this.props; 80 | 81 | return ( 82 | 83 | this.setState({ hovered: true })} 87 | onMouseLeave={() => this.setState({ hovered: false })} 88 | onClick={() => { 89 | this.props.selectTap( 90 | { 91 | name: tapKey, 92 | image: tapImage, 93 | repo, 94 | specImplementation 95 | }, 96 | this.props.uuid, 97 | knotName 98 | ); 99 | }} 100 | > 101 | 102 | 103 | {name} 109 | {name} 110 | 111 | 112 | 113 | 114 | ); 115 | } 116 | } 117 | -------------------------------------------------------------------------------- /app/actions/connect.js: -------------------------------------------------------------------------------- 1 | /* 2 | * knots 3 | * Copyright 2018 data.world, Inc. 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the 7 | * License. 8 | * 9 | * You may obtain a copy of the License at 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, software 13 | * distributed under the License is distributed on an "AS IS" BASIS, 14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 15 | * implied. See the License for the specific language governing 16 | * permissions and limitations under the License. 17 | * 18 | * This product includes software developed at 19 | * data.world, Inc.(http://data.world/). 20 | */ 21 | 22 | import axios from 'axios'; 23 | import { SET_SYNC_MODE } from './knots'; 24 | 25 | const baseUrl = 'http://localhost:4321'; 26 | 27 | export const UPDATE_TAPS = 'UPDATE_TAPS'; 28 | export const TAPS_LOADING = 'TAPS_LOADING'; 29 | export const UPDATE_TAP_FIELDS = 'UPDATE_TAP_FIELDS'; 30 | export const SET_TAP_FIELDS = 'SET_TAP_FIELDS'; 31 | export const SCHEMA_RECEIVED = 'SCHEMA_RECEIVED'; 32 | export const DISCOVER_SCHEMA = 'DISCOVER_SCHEMA'; 33 | export const SET_KNOT = 'SET_KNOT'; 34 | export const TAP_CONFIG_LOADING = 'TAP_CONFIG_LOADING'; 35 | 36 | type actionType = { 37 | +type: string 38 | }; 39 | 40 | export function fetchTapFields(tap, version, knot) { 41 | return (dispatch: (action: actionType) => void) => { 42 | dispatch({ 43 | type: TAP_CONFIG_LOADING 44 | }); 45 | console.log('Happening'); 46 | 47 | axios 48 | .post(`${baseUrl}/taps/`, { 49 | tap, 50 | version, 51 | knot 52 | }) 53 | .then((response) => { 54 | dispatch({ 55 | type: UPDATE_TAP_FIELDS, 56 | dockerVersion: response.data.dockerVersion, 57 | tapFields: response.data.config || [], 58 | fieldValues: response.data.fieldValues 59 | }); 60 | }) 61 | .catch(() => 62 | dispatch({ 63 | type: UPDATE_TAP_FIELDS, 64 | dockerInstalled: true, 65 | tapFields: [] 66 | }) 67 | ); 68 | }; 69 | } 70 | 71 | export function setTapFields(key, value, index) { 72 | return (dispatch: (action: actionType) => void) => { 73 | dispatch({ 74 | type: SET_TAP_FIELDS, 75 | key, 76 | value, 77 | index 78 | }); 79 | }; 80 | } 81 | 82 | export function submitConfig(config) { 83 | return (dispatch: (action: actionType) => void) => { 84 | dispatch({ 85 | type: TAPS_LOADING, 86 | schema: [] 87 | }); 88 | 89 | axios 90 | .post(`${baseUrl}/tap/schema/`, { 91 | config 92 | }) 93 | .then((response) => { 94 | dispatch({ 95 | type: SCHEMA_RECEIVED, 96 | schema: response.data || [] 97 | }); 98 | }) 99 | .catch(() => { 100 | dispatch({ 101 | type: SCHEMA_RECEIVED, 102 | schema: [] 103 | }); 104 | }); 105 | }; 106 | } 107 | 108 | export function setKnot(knot) { 109 | return (dispatch: (action: actionType) => void) => { 110 | dispatch({ 111 | type: SET_KNOT, 112 | knot 113 | }); 114 | dispatch({ 115 | type: SET_SYNC_MODE, 116 | syncMode: 'incremental' 117 | }); 118 | }; 119 | } 120 | -------------------------------------------------------------------------------- /app/components/Targets/TargetConfiguration/Stitch/index.js: -------------------------------------------------------------------------------- 1 | /* 2 | * knots 3 | * Copyright 2018 data.world, Inc. 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the 7 | * License. 8 | * 9 | * You may obtain a copy of the License at 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, software 13 | * distributed under the License is distributed on an "AS IS" BASIS, 14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 15 | * implied. See the License for the specific language governing 16 | * permissions and limitations under the License. 17 | * 18 | * This product includes software developed at 19 | * data.world, Inc.(http://data.world/). 20 | */ 21 | 22 | /* eslint-disable camelcase */ 23 | // @flow 24 | 25 | import React, { Component } from 'react'; 26 | import { Label, Input, FormFeedback, FormGroup, InputGroup } from 'reactstrap'; 27 | 28 | type Props = { 29 | userStore: { 30 | 'target-stitch': { 31 | fieldValues: { client_id: number | string, token: string } 32 | } 33 | }, 34 | updateTargetField: (target: string, name: string, value: string) => void 35 | }; 36 | 37 | type State = { 38 | client_id: {}, 39 | token: {} 40 | }; 41 | 42 | export default class Stitch extends Component { 43 | state = { 44 | client_id: {}, 45 | token: {} 46 | }; 47 | 48 | validate = (field: string, value: string) => { 49 | if (value) { 50 | this.setState({ [field]: { valid: true } }); 51 | } else { 52 | this.setState({ [field]: { invalid: true } }); 53 | } 54 | }; 55 | 56 | handleChange = (e: SyntheticEvent) => { 57 | const { name, value } = e.currentTarget; 58 | this.setState({ [name]: { valid: true } }); 59 | this.props.updateTargetField('target-stitch', name, value); 60 | }; 61 | 62 | render() { 63 | // eslint-disable-next-line 64 | const { client_id, token } = this.props.userStore[ 65 | 'target-stitch' 66 | ].fieldValues; 67 | return ( 68 |
69 | 70 | 71 | 72 | { 77 | const { value } = event.currentTarget; 78 | this.validate('client_id', value); 79 | }} 80 | {...this.state.client_id} 81 | /> 82 | Required 83 | 84 | 85 | 86 | 87 | 88 | { 94 | const { value } = event.currentTarget; 95 | this.validate('token', value); 96 | }} 97 | {...this.state.token} 98 | /> 99 | Required 100 | 101 | 102 |
103 | ); 104 | } 105 | } 106 | -------------------------------------------------------------------------------- /app/backend/routes/knots.js: -------------------------------------------------------------------------------- 1 | /* 2 | * knots 3 | * Copyright 2018 data.world, Inc. 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the 7 | * License. 8 | * 9 | * You may obtain a copy of the License at 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, software 13 | * distributed under the License is distributed on an "AS IS" BASIS, 14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 15 | * implied. See the License for the specific language governing 16 | * permissions and limitations under the License. 17 | * 18 | * This product includes software developed at 19 | * data.world, Inc.(http://data.world/). 20 | */ 21 | 22 | const router = require('express').Router(); 23 | 24 | const { 25 | getKnots, 26 | saveKnot, 27 | sync, 28 | deleteKnot, 29 | packageKnot, 30 | downloadKnot, 31 | loadValues, 32 | loadKnot, 33 | cancel 34 | } = require('../knots'); 35 | 36 | router.get('/', (req, res) => { 37 | getKnots() 38 | .then((knots) => res.json({ knots })) 39 | .catch((error) => { 40 | res.status(500).json({ message: error.message }); 41 | }); 42 | }); 43 | 44 | router.post('/save/', (req, res) => { 45 | const { knotName, currentName, uuid } = req.body; 46 | 47 | saveKnot(knotName, uuid, currentName) 48 | .then(() => { 49 | sync(req); 50 | res.json({}); 51 | }) 52 | .catch((error) => { 53 | res.status(500).json({ message: error.message }); 54 | }); 55 | }); 56 | 57 | router.post('/delete/', (req, res) => { 58 | // eslint-disable-next-line 59 | const { knot } = req.body; 60 | 61 | deleteKnot(knot) 62 | .then(() => { 63 | res.json({}); 64 | }) 65 | .catch((error) => { 66 | res.status(500).json({ message: error.message }); 67 | }); 68 | }); 69 | 70 | router.post('/download/', (req, res) => { 71 | // eslint-disable-next-line 72 | const { knot } = req.body; 73 | 74 | packageKnot(knot) 75 | .then(() => { 76 | res.json({}); 77 | }) 78 | .catch((error) => { 79 | res.status(500).json({ message: error.message }); 80 | }); 81 | }); 82 | 83 | router.get('/download/', (req, res) => { 84 | downloadKnot(req, res); 85 | }); 86 | 87 | router.post('/full-sync/', (req, res) => { 88 | sync(req); 89 | res.json({}); 90 | }); 91 | 92 | router.post('/partial-sync/', (req, res) => { 93 | sync(req, 'partial'); 94 | res.json({}); 95 | }); 96 | 97 | router.post('/load/', (req, res) => { 98 | const { knot, uuid } = req.body; 99 | 100 | loadValues(knot, uuid) 101 | .then((result) => { 102 | res.json(result); 103 | }) 104 | .catch((error) => { 105 | res.status(500).json({ message: error.message }); 106 | }); 107 | }); 108 | 109 | router.post('/loadknot/', (req, res) => { 110 | const { knot } = req.body; 111 | 112 | loadKnot(knot) 113 | .then((result) => { 114 | res.json(result); 115 | }) 116 | .catch((error) => { 117 | res.status(500).json({ message: error.message }); 118 | }); 119 | }); 120 | 121 | router.post('/cancel/', (req, res) => { 122 | cancel(req.body.knot) 123 | .then(() => { 124 | res.json({}); 125 | }) 126 | .catch((error) => { 127 | res.status(500).json({ message: error.message }); 128 | }); 129 | }); 130 | 131 | module.exports = router; 132 | -------------------------------------------------------------------------------- /app/actions/targets.js: -------------------------------------------------------------------------------- 1 | /* 2 | * knots 3 | * Copyright 2018 data.world, Inc. 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the 7 | * License. 8 | * 9 | * You may obtain a copy of the License at 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, software 13 | * distributed under the License is distributed on an "AS IS" BASIS, 14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 15 | * implied. See the License for the specific language governing 16 | * permissions and limitations under the License. 17 | * 18 | * This product includes software developed at 19 | * data.world, Inc.(http://data.world/). 20 | */ 21 | 22 | // @flow 23 | 24 | import axios from 'axios'; 25 | 26 | const baseUrl = 'http://localhost:4321'; 27 | 28 | export const TARGETS_PAGE_LOADED = 'TARGETS_PAGE_LOADED'; 29 | 30 | export const TARGETS_LOADING = 'TARGETS_LOADING'; 31 | export const TARGET_SELECTED = 'TARGET_SELECTED'; 32 | export const UPDATE_TARGETS = 'UPDATE_TARGETS'; 33 | export const TARGET_INSTALLED = 'TARGET_INSTALLED'; 34 | export const TARGET_CONFIGURING = 'TARGET_CONFIGURING'; 35 | export const TARGET_CONFIGURED = 'TARGET_CONFIGURED'; 36 | 37 | type actionType = { 38 | +type: string 39 | }; 40 | 41 | export function targetsPageLoaded() { 42 | return (dispatch: (action: actionType) => void) => { 43 | dispatch({ 44 | type: TARGETS_PAGE_LOADED 45 | }); 46 | }; 47 | } 48 | 49 | export function getTargets() { 50 | return (dispatch: (action: actionType) => void) => { 51 | dispatch({ 52 | type: TARGETS_LOADING 53 | }); 54 | 55 | return axios 56 | .get(`${baseUrl}/targets/`) 57 | .then((response) => { 58 | dispatch({ 59 | type: UPDATE_TARGETS, 60 | targets: response.data.targets 61 | }); 62 | }) 63 | .catch((error) => { 64 | dispatch({ 65 | type: UPDATE_TARGETS, 66 | targets: [], 67 | error: error.response ? error.response.data.message : error.message 68 | }); 69 | }); 70 | }; 71 | } 72 | 73 | export function selectTarget( 74 | target: { name: string, image: string }, 75 | uuid: string, 76 | knot: ?string 77 | ) { 78 | return (dispatch: (action: actionType) => void) => { 79 | dispatch({ 80 | type: TARGET_SELECTED, 81 | target 82 | }); 83 | 84 | return axios 85 | .post(`${baseUrl}/targets/select`, { target, uuid, knot }) 86 | .then(() => { 87 | dispatch({ 88 | type: TARGET_INSTALLED 89 | }); 90 | }) 91 | .catch((error) => { 92 | dispatch({ 93 | type: TARGET_INSTALLED, 94 | error: error.response ? error.response.data.message : error.message 95 | }); 96 | }); 97 | }; 98 | } 99 | 100 | export function submitFields(fieldValues: {}, uuid: string) { 101 | return (dispatch: (action: actionType) => void) => { 102 | dispatch({ 103 | type: TARGET_CONFIGURING 104 | }); 105 | 106 | return axios 107 | .post(`${baseUrl}/targets/`, { fieldValues, uuid }) 108 | .then(() => { 109 | dispatch({ 110 | type: TARGET_CONFIGURED 111 | }); 112 | }) 113 | .catch((error) => { 114 | dispatch({ 115 | type: TARGET_CONFIGURED, 116 | error: error.response ? error.response.data.message : error.message 117 | }); 118 | }); 119 | }; 120 | } 121 | -------------------------------------------------------------------------------- /app/components/Schema/Dropdown/index.js: -------------------------------------------------------------------------------- 1 | /* 2 | * knots 3 | * Copyright 2018 data.world, Inc. 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the 7 | * License. 8 | * 9 | * You may obtain a copy of the License at 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, software 13 | * distributed under the License is distributed on an "AS IS" BASIS, 14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 15 | * implied. See the License for the specific language governing 16 | * permissions and limitations under the License. 17 | * 18 | * This product includes software developed at 19 | * data.world, Inc.(http://data.world/). 20 | */ 21 | 22 | import React, { Component } from 'react'; 23 | import Select from 'react-select'; 24 | 25 | type Props = { 26 | editField: boolean, 27 | placeholder: string, 28 | values: Array, 29 | defaultValues: Array, 30 | isMulti: boolean, 31 | streamMetadata?: { index?: number, metadata?: {} }, 32 | index: number, 33 | handleChange: () => void, 34 | field: string, 35 | usesLogBaseRepMethod: boolean 36 | }; 37 | 38 | const colourStyles = { 39 | option: (styles, { isFocused }) => ({ 40 | ...styles, 41 | backgroundColor: isFocused ? '#dbd5ff' : null 42 | }), 43 | multiValue: (styles, { isDisabled }) => ({ 44 | ...styles, 45 | backgroundColor: isDisabled ? 'hsl(0,0%,90%)' : '#5c56a5', 46 | color: isDisabled ? '#000' : '#fff' 47 | }), 48 | multiValueLabel: (styles, { isDisabled }) => ({ 49 | ...styles, 50 | color: isDisabled ? '#000' : '#fff' 51 | }) 52 | }; 53 | 54 | export default class Dropdown extends Component { 55 | constructor(props) { 56 | super(props); 57 | 58 | const { defaultValues, editField } = props; 59 | 60 | this.state = { 61 | selectedOptions: defaultValues.map((value) => ({ 62 | value, 63 | label: value 64 | })), 65 | editField 66 | }; 67 | } 68 | 69 | getOptions = () => { 70 | const { values } = this.props; 71 | 72 | return values.map((value) => ({ 73 | value, 74 | label: value 75 | })); 76 | }; 77 | 78 | handleChange = (selectedOptions) => { 79 | const { field } = this.props; 80 | 81 | if (field === 'keyFields') { 82 | const metadata = this.props.streamMetadata; 83 | const metadataIndex = metadata.index; 84 | const propertyType = metadata.metadata['is-view'] 85 | ? 'view-key-properties' 86 | : 'table-key-properties'; 87 | 88 | this.props.handleChange( 89 | this.props.index, 90 | `metadata[${metadataIndex}].metadata[${propertyType}]`, 91 | selectedOptions.map((option) => option.value) 92 | ); 93 | } else if (field === 'timestamp') { 94 | this.props.handleChange( 95 | 'replication-key', 96 | this.props.index.toString(), 97 | selectedOptions.value 98 | ); 99 | } 100 | 101 | this.setState({ selectedOptions }); 102 | }; 103 | 104 | render() { 105 | if (this.props.values.length < 1 || this.props.usesLogBaseRepMethod) { 106 | return 'N/A'; 107 | } 108 | 109 | return ( 110 |