From 028ca095f3325184d150a64b91b83102fc40c633 Mon Sep 17 00:00:00 2001 From: Julian Molina Date: Thu, 27 Sep 2018 13:32:36 +0200 Subject: [PATCH 1/9] Create LICENSE --- LICENSE | 42 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 42 insertions(+) create mode 100644 LICENSE diff --git a/LICENSE b/LICENSE new file mode 100644 index 000000000..a47c572e6 --- /dev/null +++ b/LICENSE @@ -0,0 +1,42 @@ +COPYRIGHT NOTICE + +© Copyright 2018 Advanced Algos LTD. + + +LICENSE NOTICE + +Advanced Algos LTD. is a startup, currently at a very early stage. Because certain aspects of +the business are still not clearly defined, we have chosen to --temporarily-- grant a +restrictive license for the use of this module, until those business aspects are clarified +and a proper open source license can be granted. + +Advanced Algos LTD. is publishing the code of this software for several reasons. However, +it shall not be considered free software yet. Advanced Algos LTD. is a for-profit company +in the crypto space and this module is still proprietary software. + +The reasons for opening up the code of AACloud at this stage are the following: + + * We believe that transparency is paramount to achieving our goals, so we want everyone + to be able to understand what this module does and how. Opening up the code of this module allows + anyone to take a close look. + + * We welcome contributions from the community. Contributions are compensated on a case + by case basis with ALGO tokens. Contributions are deemed _work for hire_ and contributors + agree to provide an express grant of patent rights for their contributions. + + +LICENSE WORDING + +Advanced Algos LTD. grants a limited license for the following uses: + +1. CODE REVIEW AND AUDITS. You are granted permission to clone the AACoud repository to analyze, + review or audit the code. + +2. COLLABORATING WITH ADVANCED ALGOS LTD. You are granted permission to fork the this module repository + with the sole purpose of correcting bugs and developing functionality in agreement with + Advanced Algos LTD. Before your contributions are merged into our repository, you will be requested + to sign a Contributors Work for Hire Agreement. + +PLEASE SEE CONTRIBUTING.MD FOR MORE DETAILS ON THE IMPLICATIONS SURROUNDING YOUR CONTRIBUTIONS TO THIS REPOSITORY. + + From beeccdd221f882e1cc93ec35b9a7780b2fd3aa38 Mon Sep 17 00:00:00 2001 From: Julian Molina Date: Thu, 27 Sep 2018 13:32:59 +0200 Subject: [PATCH 2/9] Create CONTRIBUTING.md --- CONTRIBUTING.md | 51 +++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 51 insertions(+) create mode 100644 CONTRIBUTING.md diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md new file mode 100644 index 000000000..700786f3e --- /dev/null +++ b/CONTRIBUTING.md @@ -0,0 +1,51 @@ +# Contributing + +We are thrilled you are interested in contributing to the this module. Your help may be essential to the project's success and we want you in! + +Advanced Algos LTD is a startup, currently at a very early stage. Although this module is not open source software yet, we do welcome contributions from the community. + +Contributions are deemed _work for hire_ and --as such-- are compensated on a case by case basis, with ALGO tokens. Contributors agree to provide an express grant of patent rights for their contributions. Our Balanced Contributor Intellectual Property Agreement is a direct implementation of GitHub's [Balanced Employee Intellectual Property Agreement 1.0.1](https://github.com/github/balanced-employee-ip-agreement/blob/master/Balanced_Employee_IP_Agreement.md). + +# Balanced Contributor Intellectual Property Agreement 1.0.1 + +This BALANCED CONTRIBUTOR INTELLECTUAL PROPERTY AGREEMENT is between person named below and Advanced Algos ("Company"). + +**What is this?** This is the Company's Intellectual Property Agreement ("Agreement"). If you've worked in the technology space before, there's a good chance that you've run across one or more of these in the past. This document is the official, entire and exclusive agreement on what intellectual property ("IP") is yours, and what belongs to the Company. + +**Why is this?** The Company needs to be clear on what IP it owns and has rights to. Its customers, contributors, and investors depend on the Company having the legal rights to the products and services it is providing so that the Company can continue operating and doing business. + +The Company also believes that it's important to be clear on what it doesn't own. The Company doesn't want you looking over your shoulder every time you work on something personal or worrying that the Company will someday seize your open source non-lethal mousetrap simulation software. In other words, the Company isn't interested in appropriating your personal projects. + +**Read this.** Please read this document and be sure you understand it before you sign it. Due to legal risk and corporate obligations, the Company cannot, by and large, negotiate its terms. If you feel you have a particular circumstance that keeps you from signing, please let the Company's legal department ("Legal") know. And, of course, you're always free and encouraged to get your own legal counsel to explain anything you're not clear on. + +Cool? Then, by signing this Agreement, and as a condition of your contribution, you agree to the following: + +1. **What the Company owns.** The Company owns any IP ("Company IP") that you create, or help create _as its contributor or contractor_, but only if it meets one or more of these additional conditions: The IP was (i) related to an existing or prospective Company product or service at the time you developed, invented, or created it, (ii) developed for use by the Company, or (iii) developed or promoted with existing Company IP or with the Company's endorsement. You hereby grant and assign, and will grant and assign, to the Company all rights and interests in all Company IP. + + "Company IP" includes concepts, designs, developments, discoveries, ideas, improvements, inventions, trade secrets, trademarks, and works of authorship. + +2. **What the Company doesn't own.** If you create IP that doesn't fit into one of the categories listed above, the Company doesn't own it. In other words, the Company doesn't own concepts, designs, developments, discoveries, ideas, improvements, inventions, trade secrets, trademarks, or works of authorship that you develop, invent, or create that do not meet any of the conditions in Section 1. This is true regardless of the computer you use to develop your IP, including the one furnished to you by the Company. + +3. **Contribution of your IP to Company projects.** If you include your own IP – such as IP you created prior to working for the Company – into a Company product or service, it's still yours, of course, but you grant the Company a non-exclusive, irrevocable, fully paid-up, royalty-free, perpetual, sublicensable, transferable, worldwide license to use it without restriction in any way or implementation, in modified form, or as is, by itself, or incorporated into another product or service ("License"). If you include your name in any project over which the Company would have rights under this Agreement, such as in a copyright notice or a comment in code or documentation, then the License covers the rights associated with that use of your name as well. + +4. **Your contributions to non-Company projects.** The Company recognizes that you may be engaged in work that requires you to submit IP to entities other than the Company, such as open source projects used by the Company. Please make sure that Legal is aware of what you're working on so that Legal can help with any licensing issues. If any project asks you to sign a contribution agreement, you should check with Legal before doing so. These agreements may be legally binding on the Company and so should be run through Legal first. + +5. **Works made for hire.** All Company IP that you are involved with or create as part of your work for the Company is [work made for hire](http://www.copyright.gov/circs/circ09.pdf) under copyright law and compensated by your regular salary or pay. + +6. **No conflicts.** You agree that you don't have any outstanding agreements or obligations that conflict with those in this Agreement, and that you won't enter into conflicting agreements in the future. + +7. **IP protection.** The Company might someday need to show the work that went into the development of IP that it uses or has used, or to make government filings to establish that it owns the IP or has rights over it. To help in those situations, you agree not to destroy any records you maintain relating to the development of any Company IP or IP under License (together, "All IP"), and, if the Company asks, to provide those records to the Company. You agree to help the Company secure and defend its rights in All IP, including after you leave the Company. For your help the Company will compensate you at a reasonable rate. If the Company is unable to secure your help, you authorize the Company to act on your behalf (as your agent and attorney-in-fact) in securing all rights related to All IP. + +8. **Confidentiality.** As an contributor or contractor of the Company, you will have access to sensitive confidential information that is important to the Company's business. You are responsible for keeping this information confidential, including after you end your work for the Company. + + "Confidential Information" includes non-public technical details about our products or services, financial information, business strategies and forecasts, customer data, or any other information, data or know-how that is valuable to the Company because it is not publicly known. + + The Company's Confidential Information is sensitive and we expect that you'll treat it as such. You agree to only use the Company's Confidential Information for the purpose of performing your job and for the benefit of the Company. You will do your best to keep Confidential Information secret. If you are not sure if something is Confidential Information, you should assume that it is, until you can confirm otherwise. + + +Print Name: _____________ + +Sign: ________________ + +Date: ________________ + From 5596f54864499fbace8a33a244fdb43bbb3afeba Mon Sep 17 00:00:00 2001 From: Barry Low Date: Fri, 28 Sep 2018 17:31:19 -0700 Subject: [PATCH 3/9] Refactor upload-image to image-upload component --- api/src/index.js | 23 +- api/src/storage/azure.js | 2 +- client/src/views/common/ImageUpload.js | 264 ++++++++++++++++++ .../dashboard/components/ManageTeamEdit.js | 39 ++- 4 files changed, 306 insertions(+), 22 deletions(-) create mode 100644 client/src/views/common/ImageUpload.js diff --git a/api/src/index.js b/api/src/index.js index 6c61ceed8..5ec890d4b 100644 --- a/api/src/index.js +++ b/api/src/index.js @@ -13,18 +13,7 @@ const checkJwt = require('./auth/middleware/jwt') const { getMember } = require('./auth/middleware/getMember') const { validateIdToken } = require('./auth/validateIdToken') const { directiveResolvers } = require('./auth/authDirectives') -const { - createStoragePipline, - createServiceURL, - listContainers, - createContainer, - createBlob, - uploadBlob, - downloadBlob, - deleteBlob, - deleteContainer -} = require('./storage/azure') -//var azure = require('azure-storage') + const Azure = require("@azure/storage-blob") const azureAccount = process.env.AZURE_STORAGE_ACCOUNT const azureKey = process.env.AZURE_STORAGE_ACCESS_KEY @@ -160,6 +149,7 @@ const resolvers = { const containerName = teamSlug const containerURL = Azure.ContainerURL.fromServiceURL(serviceURL, containerName) + //list container and check if already exists. let marker let containerCheck = null do { @@ -167,25 +157,28 @@ const resolvers = { Azure.Aborter.None, marker, ) - console.log(`ContainerCheck marker: `, listContainersResponse) + // console.log(`ContainerCheck marker: `, listContainersResponse) marker = listContainersResponse.marker; for (const container of listContainersResponse.containerItems) { - console.log(`ContainerCheck: ${container.name} | ${containerName} | ${marker}`) + // console.log(`ContainerCheck: ${container.name} | ${containerName} | ${marker}`) if(container.name === containerName){ containerCheck = container.name } } } while (marker) + + // if container doesn't exist, create one let newContainer if(containerCheck === null){ newContainer = await containerURL.create(Azure.Aborter.None, { access: 'blob' }) } - console.log('getAzureSAS container: ', containerURL, containerCheck, newContainer) + // console.log('getAzureSAS container: ', containerURL, containerCheck, newContainer) // Set permissions for service, resource types and containers const SASServicePerms = Azure.AccountSASServices.parse('b') const SASResourceTypes = Azure.AccountSASResourceTypes.parse('co') const SASContainerPerms = Azure.ContainerSASPermissions.parse('rwl') + // Generate SAS url const SASQueryParameters = Azure.generateAccountSASQueryParameters( { diff --git a/api/src/storage/azure.js b/api/src/storage/azure.js index 324181b04..abeea558e 100644 --- a/api/src/storage/azure.js +++ b/api/src/storage/azure.js @@ -4,7 +4,7 @@ const Azure = require('@azure/storage-blob') * @accountName: Azure Blob Storage account name (required) - Environment variable * @accountKey: Azure Blob Storage storage key (required) - Environment variable */ -const createStoragePipline = async (accountNAme, accountKey) => { +const createStoragePipline = async (accountName, accountKey) => { return Azure.StorageURL.newPipeline( new Azure.SharedKeyCredential(accountName, accountKey) ) diff --git a/client/src/views/common/ImageUpload.js b/client/src/views/common/ImageUpload.js new file mode 100644 index 000000000..fd4d837bb --- /dev/null +++ b/client/src/views/common/ImageUpload.js @@ -0,0 +1,264 @@ +import React, { Component } from 'react' +import PropTypes from 'prop-types' +import { Mutation } from 'react-apollo' +import Dropzone from 'react-dropzone' +import { Cropper } from 'react-image-cropper' +import * as Azure from '@azure/storage-blob' +import readAndCompressImage from 'browser-image-resizer' +import { imgSrcToBlob } from 'blob-util' +import { withStyles } from '@material-ui/core/styles' + +import Dialog from '@material-ui/core/Dialog' +import DialogActions from '@material-ui/core/DialogActions' +import DialogContent from '@material-ui/core/DialogContent' +import Button from '@material-ui/core/Button' +import Typography from '@material-ui/core/Typography' + +const styles = theme => ({ + dropzoneContainer: { + display: 'block', + margin: '1em', + height: 200 + }, + dropzone: { + display: 'block', + padding: '3em', + height: 200, + borderWidth: 2, + borderColor: '#999', + backgroundColor: '#eee' + }, + dropzoneText: { + textAlign: 'center' + }, + existingImagePreview: { + width: 100, + height: 100 + }, + cropImageBox: { + width: 350, + height: 350 + } +}) + +// placeholder image if no existing image +const placeHolder = 'data:image/svg+xml;charset=UTF-8,%3Csvg%20width%3D%22288%22%20height%3D%22225%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20viewBox%3D%220%200%20288%20225%22%20preserveAspectRatio%3D%22none%22%3E%3Cdefs%3E%3Cstyle%20type%3D%22text%2Fcss%22%3E%23holder_164edaf95ee%20text%20%7B%20fill%3A%23eceeef%3Bfont-weight%3Abold%3Bfont-family%3AArial%2C%20Helvetica%2C%20Open%20Sans%2C%20sans-serif%2C%20monospace%3Bfont-size%3A14pt%20%7D%20%3C%2Fstyle%3E%3C%2Fdefs%3E%3Cg%20id%3D%22holder_164edaf95ee%22%3E%3Crect%20width%3D%22288%22%20height%3D%22225%22%20fill%3D%22%2355595c%22%3E%3C%2Frect%3E%3Cg%3E%3Ctext%20x%3D%2296.32500076293945%22%20y%3D%22118.8%22%3EAvatar%3C%2Ftext%3E%3C%2Fg%3E%3C%2Fg%3E%3C%2Fsvg%3E' + +// config for image resize and quality +const imageResizeConfig = { + quality: 0.6, + maxWidth: 200, + maxHeight: 200, + autoRotate: true, + debug: true, + mimeType: 'image/jpeg' +} + +class UploadImage extends Component { + constructor (props) { + super(props) + + this.onDrop = this.onDrop.bind(this) + this.handleCrop = this.handleCrop.bind(this) + this.handleClickOpen = this.handleClickOpen.bind(this) + this.handleClose = this.handleClose.bind(this) + this.handleChangeImage = this.handleChangeImage.bind(this) + this.cancelChangeImage = this.cancelChangeImage.bind(this) + this.saveImage = this.saveImage.bind(this) + + this.state = { + open: false, + change: false, + file: null, + src: null, + crop: { + x: 10, + y: 10, + width: 200, + height: 200 + }, + cropping: false, + cropped: null, + uploaded: false, + image: null, + sasqr: null + } + } + + render () { + const { classes, cropContainer, saveImageConfig, containerName, fileName, existingImage, handleUrl, AZURE_SAS_QUERY } = this.props + let image = null + let resizeImage = null + if (existingImage !== undefined && existingImage !== null) { + image = existingImage + } else { + image = placeHolder + } + + if (saveImageConfig !== undefined && saveImageConfig !== null) { + resizeImage = saveImageConfig + } else { + resizeImage = imageResizeConfig + } + + return ( +
+ + {(getAzureSAS, { loading, error, data }) => { + if (this.state.cropping) { + return ( + Cropping... + ) + } else { + return ( + +
+ {(this.state.change || this.state.src === null) && ( + + this.onDrop(e, this.state.edit)} + className={classes.dropzone} + multiple={false} + > +

Drag-and-drop image here or click to select
Only *.jpeg and *.png images

+
+ +
+ )} + {!this.state.change && ( + + Existing Image Preview + + + )} + {this.state.image !== null && ( + Save profile to save image changes! + )} +
+ +
+ +
+ {this.state.src !== null && ( + { this.cropper = ref }} + onImgLoad={this.onImageLoaded} + ratio={1} + /> + )} +
+
+ + + + +
+
+
+ ) + } + }} +
+
+ ) + } + + onDrop (file, editImageType) { + console.log('onDrop', file) + this.onSelectFile(file, editImageType) + this.setState({ + file + }) + } + + async handleCrop (e, getAzureSAS, resizeImage, containerName, fileName, handleUrl) { + e.preventDefault() + const data = await getAzureSAS({ variables: { containerName } }) + + const cropped = this.cropper.crop() + const croppedBlob = await imgSrcToBlob(cropped, 'image/png', 'Anonymous') + let resizedBlob = await readAndCompressImage(croppedBlob, resizeImage) + + return this.setState({ cropped: resizedBlob, cropping: true, open: false }, () => { + this.saveImage(data.getAzureSAS, containerName, fileName, handleUrl) + }) + } + + async saveImage (SASQueryParameters, containerName, fileName, handleUrl) { + const SASurl = `${process.env.AZURE_STORAGE_URL}?${this.state.sasqr}` + + // create Azure BlockBlobURL to save image to + const pipeline = Azure.StorageURL.newPipeline(new Azure.AnonymousCredential()) + const serviceURL = new Azure.ServiceURL(SASurl, pipeline) + + const containerURL = Azure.ContainerURL.fromServiceURL(serviceURL, containerName) + const blockBlobURL = Azure.BlockBlobURL.fromContainerURL(containerURL, fileName) + + await Azure.uploadBrowserDataToBlockBlob(Azure.Aborter.None, this.state.cropped, blockBlobURL) + + const image = `${process.env.AZURE_STORAGE_URL}${containerName}/${fileName}?${Math.random()}` // random number keeps browser from using cached version of existing image + handleUrl(image) // pass image URL up to be save to DB + this.setState({ cropping: false, uploaded: true, change: false, image: image }) + return image + } + + handleChangeImage (e, cropContainer) { + e.preventDefault() + let crop = cropContainer // dimensions of cropContainer passed from above + this.setState({ + change: true, + crop: crop + }) + } + + cancelChangeImage () { + this.setState({ change: false }) + } + + handleClickOpen (e) { + e.preventDefault() + this.setState({ open: true }) + } + + handleClose () { + this.setState({ open: false, src: null, change: false }) + } +} + +UploadImage.propTypes = { + classes: PropTypes.object.isRequired, + cropContainer: PropTypes.object, // { x: 10, y: 10, width: 200, height: 200 } (default) + saveImageConfig: PropTypes.object, // see imageResizeConfig above for default (default) + containerName: PropTypes.string.isRequired, // name of Azure Container image will be saved in + fileName: PropTypes.string.isRequired, // filename to save image as + existingImage: PropTypes.string, // image url of existing image (optional) + handleUrl: PropTypes.func.isRequired, // function that handles uploaded image file url + AZURE_SAS_QUERY: PropTypes.any.isRequired // GraphQL for getting Azure SAS URL +} + +export default withStyles(styles)(UploadImage) diff --git a/client/src/views/dashboard/components/ManageTeamEdit.js b/client/src/views/dashboard/components/ManageTeamEdit.js index 6cb6596b8..de5e9aa83 100644 --- a/client/src/views/dashboard/components/ManageTeamEdit.js +++ b/client/src/views/dashboard/components/ManageTeamEdit.js @@ -20,7 +20,8 @@ import GET_TEAMS_BY_OWNER from '../../../graphql/teams/GetTeamsByOwnerQuery' import { checkGraphQLError } from '../../../utils/graphql-errors' -import { UploadImage } from '../../common' +import { ImageUpload } from '../../common' +import GET_AZURE_SAS from '../../graphql/teams/GetAzureSASMutation' const styles = theme => ({ dialogContainer: { @@ -111,11 +112,37 @@ export class ManageTeamEdit extends Component { Edit Team Details - + Date: Mon, 1 Oct 2018 08:26:29 +0200 Subject: [PATCH 4/9] Update LICENSE --- LICENSE | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/LICENSE b/LICENSE index a47c572e6..f513e6254 100644 --- a/LICENSE +++ b/LICENSE @@ -14,7 +14,7 @@ Advanced Algos LTD. is publishing the code of this software for several reasons. it shall not be considered free software yet. Advanced Algos LTD. is a for-profit company in the crypto space and this module is still proprietary software. -The reasons for opening up the code of AACloud at this stage are the following: +The reasons for opening up the code of this module at this stage are the following: * We believe that transparency is paramount to achieving our goals, so we want everyone to be able to understand what this module does and how. Opening up the code of this module allows From 836a9f2530d9c36b59c3fd2a05518b156a90f8a1 Mon Sep 17 00:00:00 2001 From: Barry Low Date: Tue, 2 Oct 2018 12:13:57 -0700 Subject: [PATCH 5/9] Client: switch common to @advancedalgos/web-components --- api/docker-compose.api.yml | 5 +- client/package-lock.json | 519 +++++++++++------- client/package.json | 6 +- client/src/App.js | 2 +- client/src/views/common/ImageUpload.js | 264 --------- client/src/views/common/MessageCard.js | 45 -- client/src/views/common/UploadImage.js | 345 ------------ client/src/views/common/index.js | 4 - .../dashboard/components/ManageTeamEdit.js | 80 +-- 9 files changed, 360 insertions(+), 910 deletions(-) delete mode 100644 client/src/views/common/ImageUpload.js delete mode 100644 client/src/views/common/MessageCard.js delete mode 100644 client/src/views/common/UploadImage.js delete mode 100644 client/src/views/common/index.js diff --git a/api/docker-compose.api.yml b/api/docker-compose.api.yml index 689c37fa6..6282e3a87 100644 --- a/api/docker-compose.api.yml +++ b/api/docker-compose.api.yml @@ -1,10 +1,7 @@ version: '3' services: api: - image: aateamapi-dev:v1 - build: - context: . - dockerfile: ./Dockerfile + image: advancedalgosteamsmodule/teammodule-api:v1 env_file: ./.env container_name: aateamapi tty: true diff --git a/client/package-lock.json b/client/package-lock.json index c0fbda736..4a43e9c8d 100644 --- a/client/package-lock.json +++ b/client/package-lock.json @@ -4,6 +4,133 @@ "lockfileVersion": 1, "requires": true, "dependencies": { + "@advancedalgos/web-components": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/@advancedalgos/web-components/-/web-components-0.0.1.tgz", + "integrity": "sha512-RDi7oR5oY4HbcXdzTDhtgYVHRO+/ApBMossZ/UkCrI2lYAWhQaq4mYaKaJ2mtVKnwV1BkeOLFE6rbPZwIkhXJA==", + "requires": { + "@azure/storage-blob": "^10.1.0-preview", + "@material-ui/core": "^3.0.3", + "@material-ui/icons": "^3.0.1", + "apollo-client": "^2.4.2", + "blob-util": "^2.0.2", + "browser-image-resizer": "^1.2.0", + "graphql": "^14.0.2", + "graphql-tag": "^2.9.2", + "prop-types": "^15.6.2", + "react-apollo": "^2.1.11", + "react-dropzone": "^5.1.0", + "react-image-crop": "^5.0.0", + "react-image-cropper": "^1.3.0" + }, + "dependencies": { + "@azure/storage-blob": { + "version": "10.1.0-preview", + "resolved": "https://registry.npmjs.org/@azure/storage-blob/-/storage-blob-10.1.0-preview.tgz", + "integrity": "sha512-vKY4dF8VCRCOOQ03Z1ResyjZbWg4jPOnQtRtlSEKE6a7WwFE3EuIbhE7OQfEihuNYyE+XWLkTjrHxeHDFx1hFg==", + "requires": { + "events": "3.0.0", + "ms-rest-js": "0.22.425", + "tslib": "^1.9.3" + } + }, + "@types/node": { + "version": "9.6.32", + "resolved": "https://registry.npmjs.org/@types/node/-/node-9.6.32.tgz", + "integrity": "sha512-5+L3wQ+FHoQ589EaH6rYICleuj8gnunq+1CJkM9fxklirErIOv+kxm3s/vecYnpJOYnFowE5uUizcb3hgjHUug==" + }, + "apollo-cache": { + "version": "1.1.17", + "resolved": "https://registry.npmjs.org/apollo-cache/-/apollo-cache-1.1.17.tgz", + "integrity": "sha512-7sp24n2HZO4vXgTaKNomLyIfGxG4gDdDkBB0jkRzRi7HhnKmfwhiF/RCiKNbgLdrPX151INdls0KwIVliD0dHQ==", + "requires": { + "apollo-utilities": "^1.0.21" + } + }, + "apollo-client": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/apollo-client/-/apollo-client-2.4.2.tgz", + "integrity": "sha512-g1z23umaVSoKLj9xNl0aAnk2KBF4JeBi7MeKFc9CGTixH7TkqeQUQtxcjrC7j2h4KmDbuhOAHOFUGf8YshN+ag==", + "requires": { + "@types/async": "2.0.49", + "@types/zen-observable": "^0.8.0", + "apollo-cache": "1.1.17", + "apollo-link": "^1.0.0", + "apollo-link-dedup": "^1.0.0", + "apollo-utilities": "1.0.21", + "symbol-observable": "^1.0.2", + "zen-observable": "^0.8.0" + } + }, + "apollo-utilities": { + "version": "1.0.21", + "resolved": "https://registry.npmjs.org/apollo-utilities/-/apollo-utilities-1.0.21.tgz", + "integrity": "sha512-ZcxELlEl+sDCYBgEMdNXJAsZtRVm8wk4HIA58bMsqYfd1DSAJQEtZ93F0GZgYNAGy3QyaoBeZtbb0/01++G8JQ==", + "requires": { + "fast-json-stable-stringify": "^2.0.0", + "fclone": "^1.0.11" + } + }, + "events": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/events/-/events-3.0.0.tgz", + "integrity": "sha512-Dc381HFWJzEOhQ+d8pkNon++bk9h6cdAoAj4iE6Q4y6xgTzySWXlKn05/TVNpjnfRqi/X0EpJEJohPjNI3zpVA==" + }, + "graphql": { + "version": "14.0.2", + "resolved": "https://registry.npmjs.org/graphql/-/graphql-14.0.2.tgz", + "integrity": "sha512-gUC4YYsaiSJT1h40krG3J+USGlwhzNTXSb4IOZljn9ag5Tj+RkoXrWp+Kh7WyE3t1NCfab5kzCuxBIvOMERMXw==", + "requires": { + "iterall": "^1.2.2" + } + }, + "ms-rest-js": { + "version": "0.22.425", + "resolved": "https://registry.npmjs.org/ms-rest-js/-/ms-rest-js-0.22.425.tgz", + "integrity": "sha512-PM9l0Si+T3kFxce8hwB/LlCxPJSHwSlFUSpjdJHzPvF+2w8lw7huE/FCPnRfdaUm7IikQESkJhydnbqX6g2OuQ==", + "requires": { + "@types/express": "^4.11.1", + "@types/form-data": "^2.2.1", + "@types/node": "^9.4.6", + "@types/uuid": "^3.4.3", + "axios": "^0.18.0", + "form-data": "^2.3.2", + "tough-cookie": "^2.4.3", + "tslib": "^1.9.2", + "uuid": "^3.2.1", + "xml2js": "^0.4.19" + } + }, + "symbol-observable": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/symbol-observable/-/symbol-observable-1.2.0.tgz", + "integrity": "sha512-e900nM8RRtGhlV36KGEU9k65K3mPb1WV70OdjfxlG2EAuM1noi/E/BaW/uMhL7bPEssK8QV57vN3esixjUvcXQ==" + }, + "tough-cookie": { + "version": "2.4.3", + "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.4.3.tgz", + "integrity": "sha512-Q5srk/4vDM54WJsJio3XNn6K2sCG+CQ8G5Wz6bZhRZoAe/+TxjWB/GlFAnYEbkYVlON9FMk/fE3h2RLpPXo4lQ==", + "requires": { + "psl": "^1.1.24", + "punycode": "^1.4.1" + } + }, + "xml2js": { + "version": "0.4.19", + "resolved": "https://registry.npmjs.org/xml2js/-/xml2js-0.4.19.tgz", + "integrity": "sha512-esZnJZJOiJR9wWKMyuvSE1y6Dq5LCuJanqhxslH2bxM6duahNZ+HMpCLhBQGZkbX6xRf8x1Y2eJlgt2q3qo49Q==", + "requires": { + "sax": ">=0.6.0", + "xmlbuilder": "~9.0.1" + } + }, + "xmlbuilder": { + "version": "9.0.7", + "resolved": "https://registry.npmjs.org/xmlbuilder/-/xmlbuilder-9.0.7.tgz", + "integrity": "sha1-Ey7mPS7FVlxVfiD0wi35rKaGsQ0=" + } + } + }, "@azure/storage-blob": { "version": "10.0.0-preview", "resolved": "https://registry.npmjs.org/@azure/storage-blob/-/storage-blob-10.0.0-preview.tgz", @@ -22,18 +149,18 @@ } }, "@babel/core": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.0.1.tgz", - "integrity": "sha512-7Yy2vRB6KYbhWeIrrwJmKv9UwDxokmlo43wi6AV84oNs4Gi71NTNGh3YxY/hK3+CxuSc6wcKSl25F2tQOhm1GQ==", + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.1.2.tgz", + "integrity": "sha512-IFeSSnjXdhDaoysIlev//UzHZbdEmm7D0EIH2qtse9xK7mXEZQpYjs2P00XlP1qYsYvid79p+Zgg6tz1mp6iVw==", "dev": true, "requires": { "@babel/code-frame": "^7.0.0", - "@babel/generator": "^7.0.0", - "@babel/helpers": "^7.0.0", - "@babel/parser": "^7.0.0", - "@babel/template": "^7.0.0", - "@babel/traverse": "^7.0.0", - "@babel/types": "^7.0.0", + "@babel/generator": "^7.1.2", + "@babel/helpers": "^7.1.2", + "@babel/parser": "^7.1.2", + "@babel/template": "^7.1.2", + "@babel/traverse": "^7.1.0", + "@babel/types": "^7.1.2", "convert-source-map": "^1.1.0", "debug": "^3.1.0", "json5": "^0.5.0", @@ -43,22 +170,13 @@ "source-map": "^0.5.0" }, "dependencies": { - "@babel/code-frame": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.0.0.tgz", - "integrity": "sha512-OfC2uemaknXr87bdLUkWog7nYuliM9Ij5HUcajsVcMCpQrcLmtxRbVFTIqmcSkSeYRBFBRxs2FiUqFJDLdiebA==", - "dev": true, - "requires": { - "@babel/highlight": "^7.0.0" - } - }, "@babel/generator": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.0.0.tgz", - "integrity": "sha512-/BM2vupkpbZXq22l1ALO7MqXJZH2k8bKVv8Y+pABFnzWdztDB/ZLveP5At21vLz5c2YtSE6p7j2FZEsqafMz5Q==", + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.1.2.tgz", + "integrity": "sha512-70A9HWLS/1RHk3Ck8tNHKxOoKQuSKocYgwDN85Pyl/RBduss6AKxUR7RIZ/lzduQMSYfWEM4DDBu6A+XGbkFig==", "dev": true, "requires": { - "@babel/types": "^7.0.0", + "@babel/types": "^7.1.2", "jsesc": "^2.5.1", "lodash": "^4.17.10", "source-map": "^0.5.0", @@ -66,67 +184,44 @@ } }, "@babel/helper-function-name": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.0.0.tgz", - "integrity": "sha512-Zo+LGvfYp4rMtz84BLF3bavFTdf8y4rJtMPTe2J+rxYmnDOIeH8le++VFI/pRJU+rQhjqiXxE4LMaIau28Tv1Q==", + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.1.0.tgz", + "integrity": "sha512-A95XEoCpb3TO+KZzJ4S/5uW5fNe26DjBGqf1o9ucyLyCmi1dXq/B3c8iaWTfBk3VvetUxl16e8tIrd5teOCfGw==", "dev": true, "requires": { "@babel/helper-get-function-arity": "^7.0.0", - "@babel/template": "^7.0.0", + "@babel/template": "^7.1.0", "@babel/types": "^7.0.0" } }, - "@babel/helper-get-function-arity": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/@babel/helper-get-function-arity/-/helper-get-function-arity-7.0.0.tgz", - "integrity": "sha512-r2DbJeg4svYvt3HOS74U4eWKsUAMRH01Z1ds1zx8KNTPtpTL5JAsdFv8BNyOpVqdFhHkkRDIg5B4AsxmkjAlmQ==", - "dev": true, - "requires": { - "@babel/types": "^7.0.0" - } - }, - "@babel/helper-split-export-declaration": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.0.0.tgz", - "integrity": "sha512-MXkOJqva62dfC0w85mEf/LucPPS/1+04nmmRMPEBUB++hiiThQ2zPtX/mEWQ3mtzCEjIJvPY8nuwxXtQeQwUag==", - "dev": true, - "requires": { - "@babel/types": "^7.0.0" - } - }, - "@babel/highlight": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.0.0.tgz", - "integrity": "sha512-UFMC4ZeFC48Tpvj7C8UgLvtkaUuovQX+5xNWrsIoMG8o2z+XFKjKaN9iVmS84dPwVN00W4wPmqvYoZF3EGAsfw==", - "dev": true, - "requires": { - "chalk": "^2.0.0", - "esutils": "^2.0.2", - "js-tokens": "^4.0.0" - } + "@babel/parser": { + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.1.2.tgz", + "integrity": "sha512-x5HFsW+E/nQalGMw7hu+fvPqnBeBaIr0lWJ2SG0PPL2j+Pm9lYvCrsZJGIgauPIENx0v10INIyFjmSNUD/gSqQ==", + "dev": true }, "@babel/template": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.0.0.tgz", - "integrity": "sha512-VLQZik/G5mjYJ6u19U3W2u7eM+rA/NGzH+GtHDFFkLTKLW66OasFrxZ/yK7hkyQcswrmvugFyZpDFRW0DjcjCw==", + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.1.2.tgz", + "integrity": "sha512-SY1MmplssORfFiLDcOETrW7fCLl+PavlwMh92rrGcikQaRq4iWPVH0MpwPpY3etVMx6RnDjXtr6VZYr/IbP/Ag==", "dev": true, "requires": { "@babel/code-frame": "^7.0.0", - "@babel/parser": "^7.0.0", - "@babel/types": "^7.0.0" + "@babel/parser": "^7.1.2", + "@babel/types": "^7.1.2" } }, "@babel/traverse": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.0.0.tgz", - "integrity": "sha512-ka/lwaonJZTlJyn97C4g5FYjPOx+Oxd3ab05hbDr1Mx9aP1FclJ+SUHyLx3Tx40sGmOVJApDxE6puJhd3ld2kw==", + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.1.0.tgz", + "integrity": "sha512-bwgln0FsMoxm3pLOgrrnGaXk18sSM9JNf1/nHC/FksmNGFbYnPWY4GYCfLxyP1KRmfsxqkRpfoa6xr6VuuSxdw==", "dev": true, "requires": { "@babel/code-frame": "^7.0.0", "@babel/generator": "^7.0.0", - "@babel/helper-function-name": "^7.0.0", + "@babel/helper-function-name": "^7.1.0", "@babel/helper-split-export-declaration": "^7.0.0", - "@babel/parser": "^7.0.0", + "@babel/parser": "^7.1.0", "@babel/types": "^7.0.0", "debug": "^3.1.0", "globals": "^11.1.0", @@ -134,21 +229,15 @@ } }, "@babel/types": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.0.0.tgz", - "integrity": "sha512-5tPDap4bGKTLPtci2SUl/B7Gv8RnuJFuQoWx26RJobS0fFrz4reUA3JnwIM+HVHEmWE0C1mzKhDtTp8NsWY02Q==", + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.1.2.tgz", + "integrity": "sha512-pb1I05sZEKiSlMUV9UReaqsCPUpgbHHHu2n1piRm7JkuBkm6QxcaIzKu6FMnMtCbih/cEYTR+RGYYC96Yk9HAg==", "dev": true, "requires": { "esutils": "^2.0.2", "lodash": "^4.17.10", "to-fast-properties": "^2.0.0" } - }, - "js-tokens": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", - "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", - "dev": true } } }, @@ -1183,100 +1272,55 @@ } }, "@babel/helpers": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.0.0.tgz", - "integrity": "sha512-jbvgR8iLZPnyk6m/UqdXYsSxbVtRi7Pd3CzB4OPwPBnmhNG1DWjiiy777NTuoyIcniszK51R40L5pgfXAfHDtw==", + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.1.2.tgz", + "integrity": "sha512-Myc3pUE8eswD73aWcartxB16K6CGmHDv9KxOmD2CeOs/FaEAQodr3VYGmlvOmog60vNQ2w8QbatuahepZwrHiA==", "dev": true, "requires": { - "@babel/template": "^7.0.0", - "@babel/traverse": "^7.0.0", - "@babel/types": "^7.0.0" + "@babel/template": "^7.1.2", + "@babel/traverse": "^7.1.0", + "@babel/types": "^7.1.2" }, "dependencies": { - "@babel/code-frame": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.0.0.tgz", - "integrity": "sha512-OfC2uemaknXr87bdLUkWog7nYuliM9Ij5HUcajsVcMCpQrcLmtxRbVFTIqmcSkSeYRBFBRxs2FiUqFJDLdiebA==", - "dev": true, - "requires": { - "@babel/highlight": "^7.0.0" - } - }, - "@babel/generator": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.0.0.tgz", - "integrity": "sha512-/BM2vupkpbZXq22l1ALO7MqXJZH2k8bKVv8Y+pABFnzWdztDB/ZLveP5At21vLz5c2YtSE6p7j2FZEsqafMz5Q==", - "dev": true, - "requires": { - "@babel/types": "^7.0.0", - "jsesc": "^2.5.1", - "lodash": "^4.17.10", - "source-map": "^0.5.0", - "trim-right": "^1.0.1" - } - }, "@babel/helper-function-name": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.0.0.tgz", - "integrity": "sha512-Zo+LGvfYp4rMtz84BLF3bavFTdf8y4rJtMPTe2J+rxYmnDOIeH8le++VFI/pRJU+rQhjqiXxE4LMaIau28Tv1Q==", + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.1.0.tgz", + "integrity": "sha512-A95XEoCpb3TO+KZzJ4S/5uW5fNe26DjBGqf1o9ucyLyCmi1dXq/B3c8iaWTfBk3VvetUxl16e8tIrd5teOCfGw==", "dev": true, "requires": { "@babel/helper-get-function-arity": "^7.0.0", - "@babel/template": "^7.0.0", + "@babel/template": "^7.1.0", "@babel/types": "^7.0.0" } }, - "@babel/helper-get-function-arity": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/@babel/helper-get-function-arity/-/helper-get-function-arity-7.0.0.tgz", - "integrity": "sha512-r2DbJeg4svYvt3HOS74U4eWKsUAMRH01Z1ds1zx8KNTPtpTL5JAsdFv8BNyOpVqdFhHkkRDIg5B4AsxmkjAlmQ==", - "dev": true, - "requires": { - "@babel/types": "^7.0.0" - } - }, - "@babel/helper-split-export-declaration": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.0.0.tgz", - "integrity": "sha512-MXkOJqva62dfC0w85mEf/LucPPS/1+04nmmRMPEBUB++hiiThQ2zPtX/mEWQ3mtzCEjIJvPY8nuwxXtQeQwUag==", - "dev": true, - "requires": { - "@babel/types": "^7.0.0" - } - }, - "@babel/highlight": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.0.0.tgz", - "integrity": "sha512-UFMC4ZeFC48Tpvj7C8UgLvtkaUuovQX+5xNWrsIoMG8o2z+XFKjKaN9iVmS84dPwVN00W4wPmqvYoZF3EGAsfw==", - "dev": true, - "requires": { - "chalk": "^2.0.0", - "esutils": "^2.0.2", - "js-tokens": "^4.0.0" - } + "@babel/parser": { + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.1.2.tgz", + "integrity": "sha512-x5HFsW+E/nQalGMw7hu+fvPqnBeBaIr0lWJ2SG0PPL2j+Pm9lYvCrsZJGIgauPIENx0v10INIyFjmSNUD/gSqQ==", + "dev": true }, "@babel/template": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.0.0.tgz", - "integrity": "sha512-VLQZik/G5mjYJ6u19U3W2u7eM+rA/NGzH+GtHDFFkLTKLW66OasFrxZ/yK7hkyQcswrmvugFyZpDFRW0DjcjCw==", + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.1.2.tgz", + "integrity": "sha512-SY1MmplssORfFiLDcOETrW7fCLl+PavlwMh92rrGcikQaRq4iWPVH0MpwPpY3etVMx6RnDjXtr6VZYr/IbP/Ag==", "dev": true, "requires": { "@babel/code-frame": "^7.0.0", - "@babel/parser": "^7.0.0", - "@babel/types": "^7.0.0" + "@babel/parser": "^7.1.2", + "@babel/types": "^7.1.2" } }, "@babel/traverse": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.0.0.tgz", - "integrity": "sha512-ka/lwaonJZTlJyn97C4g5FYjPOx+Oxd3ab05hbDr1Mx9aP1FclJ+SUHyLx3Tx40sGmOVJApDxE6puJhd3ld2kw==", + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.1.0.tgz", + "integrity": "sha512-bwgln0FsMoxm3pLOgrrnGaXk18sSM9JNf1/nHC/FksmNGFbYnPWY4GYCfLxyP1KRmfsxqkRpfoa6xr6VuuSxdw==", "dev": true, "requires": { "@babel/code-frame": "^7.0.0", "@babel/generator": "^7.0.0", - "@babel/helper-function-name": "^7.0.0", + "@babel/helper-function-name": "^7.1.0", "@babel/helper-split-export-declaration": "^7.0.0", - "@babel/parser": "^7.0.0", + "@babel/parser": "^7.1.0", "@babel/types": "^7.0.0", "debug": "^3.1.0", "globals": "^11.1.0", @@ -1284,21 +1328,15 @@ } }, "@babel/types": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.0.0.tgz", - "integrity": "sha512-5tPDap4bGKTLPtci2SUl/B7Gv8RnuJFuQoWx26RJobS0fFrz4reUA3JnwIM+HVHEmWE0C1mzKhDtTp8NsWY02Q==", + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.1.2.tgz", + "integrity": "sha512-pb1I05sZEKiSlMUV9UReaqsCPUpgbHHHu2n1piRm7JkuBkm6QxcaIzKu6FMnMtCbih/cEYTR+RGYYC96Yk9HAg==", "dev": true, "requires": { "esutils": "^2.0.2", "lodash": "^4.17.10", "to-fast-properties": "^2.0.0" } - }, - "js-tokens": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", - "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", - "dev": true } } }, @@ -3433,48 +3471,10 @@ } }, "babel-core": { - "version": "6.26.3", - "resolved": "https://registry.npmjs.org/babel-core/-/babel-core-6.26.3.tgz", - "integrity": "sha512-6jyFLuDmeidKmUEb3NM+/yawG0M2bDZ9Z1qbZP59cyHLz8kYGKYwpJP0UwUKKUiTRNvxfLesJnTedqczP7cTDA==", - "dev": true, - "requires": { - "babel-code-frame": "^6.26.0", - "babel-generator": "^6.26.0", - "babel-helpers": "^6.24.1", - "babel-messages": "^6.23.0", - "babel-register": "^6.26.0", - "babel-runtime": "^6.26.0", - "babel-template": "^6.26.0", - "babel-traverse": "^6.26.0", - "babel-types": "^6.26.0", - "babylon": "^6.18.0", - "convert-source-map": "^1.5.1", - "debug": "^2.6.9", - "json5": "^0.5.1", - "lodash": "^4.17.4", - "minimatch": "^3.0.4", - "path-is-absolute": "^1.0.1", - "private": "^0.1.8", - "slash": "^1.0.0", - "source-map": "^0.5.7" - }, - "dependencies": { - "babylon": { - "version": "6.18.0", - "resolved": "https://registry.npmjs.org/babylon/-/babylon-6.18.0.tgz", - "integrity": "sha512-q/UEjfGJ2Cm3oKV71DJz9d25TPnq5rhBVL2Q4fA5wcC3jcrdn7+SssEybFIxwAvvP+YCsCYNKughoF33GxgycQ==", - "dev": true - }, - "debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", - "dev": true, - "requires": { - "ms": "2.0.0" - } - } - } + "version": "7.0.0-bridge.0", + "resolved": "https://registry.npmjs.org/babel-core/-/babel-core-7.0.0-bridge.0.tgz", + "integrity": "sha512-poPX9mZH/5CSanm50Q+1toVci6pv5KSRv/5TWCwtzQS5XEwn40BcCrgIeMFWP9CKKIniKXNxoIOnOq4VVlGXhg==", + "dev": true }, "babel-eslint": { "version": "9.0.0", @@ -3728,15 +3728,14 @@ } }, "babel-loader": { - "version": "8.0.2", - "resolved": "https://registry.npmjs.org/babel-loader/-/babel-loader-8.0.2.tgz", - "integrity": "sha512-Law0PGtRV1JL8Y9Wpzc0d6EE0GD7LzXWCfaeWwboUMcBWNG6gvaWTK1/+BK7a4X5EmeJiGEuDDFxUsOa8RSWCw==", + "version": "7.1.5", + "resolved": "https://registry.npmjs.org/babel-loader/-/babel-loader-7.1.5.tgz", + "integrity": "sha512-iCHfbieL5d1LfOQeeVJEUyD9rTwBcP/fcEbRCfempxTDuqrKpu0AZjLAQHEQa3Yqyj9ORKe2iHfoj4rHLf7xpw==", "dev": true, "requires": { "find-cache-dir": "^1.0.0", "loader-utils": "^1.0.2", - "mkdirp": "^0.5.1", - "util.promisify": "^1.0.0" + "mkdirp": "^0.5.1" } }, "babel-messages": { @@ -3757,6 +3756,50 @@ "babel-core": "^6.26.0", "babel-preset-minify": "^0.3.0", "webpack-sources": "^1.0.1" + }, + "dependencies": { + "babel-core": { + "version": "6.26.3", + "resolved": "https://registry.npmjs.org/babel-core/-/babel-core-6.26.3.tgz", + "integrity": "sha512-6jyFLuDmeidKmUEb3NM+/yawG0M2bDZ9Z1qbZP59cyHLz8kYGKYwpJP0UwUKKUiTRNvxfLesJnTedqczP7cTDA==", + "dev": true, + "requires": { + "babel-code-frame": "^6.26.0", + "babel-generator": "^6.26.0", + "babel-helpers": "^6.24.1", + "babel-messages": "^6.23.0", + "babel-register": "^6.26.0", + "babel-runtime": "^6.26.0", + "babel-template": "^6.26.0", + "babel-traverse": "^6.26.0", + "babel-types": "^6.26.0", + "babylon": "^6.18.0", + "convert-source-map": "^1.5.1", + "debug": "^2.6.9", + "json5": "^0.5.1", + "lodash": "^4.17.4", + "minimatch": "^3.0.4", + "path-is-absolute": "^1.0.1", + "private": "^0.1.8", + "slash": "^1.0.0", + "source-map": "^0.5.7" + } + }, + "babylon": { + "version": "6.18.0", + "resolved": "https://registry.npmjs.org/babylon/-/babylon-6.18.0.tgz", + "integrity": "sha512-q/UEjfGJ2Cm3oKV71DJz9d25TPnq5rhBVL2Q4fA5wcC3jcrdn7+SssEybFIxwAvvP+YCsCYNKughoF33GxgycQ==", + "dev": true + }, + "debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dev": true, + "requires": { + "ms": "2.0.0" + } + } } }, "babel-plugin-check-es2015-constants": { @@ -4534,6 +4577,50 @@ "lodash": "^4.17.4", "mkdirp": "^0.5.1", "source-map-support": "^0.4.15" + }, + "dependencies": { + "babel-core": { + "version": "6.26.3", + "resolved": "https://registry.npmjs.org/babel-core/-/babel-core-6.26.3.tgz", + "integrity": "sha512-6jyFLuDmeidKmUEb3NM+/yawG0M2bDZ9Z1qbZP59cyHLz8kYGKYwpJP0UwUKKUiTRNvxfLesJnTedqczP7cTDA==", + "dev": true, + "requires": { + "babel-code-frame": "^6.26.0", + "babel-generator": "^6.26.0", + "babel-helpers": "^6.24.1", + "babel-messages": "^6.23.0", + "babel-register": "^6.26.0", + "babel-runtime": "^6.26.0", + "babel-template": "^6.26.0", + "babel-traverse": "^6.26.0", + "babel-types": "^6.26.0", + "babylon": "^6.18.0", + "convert-source-map": "^1.5.1", + "debug": "^2.6.9", + "json5": "^0.5.1", + "lodash": "^4.17.4", + "minimatch": "^3.0.4", + "path-is-absolute": "^1.0.1", + "private": "^0.1.8", + "slash": "^1.0.0", + "source-map": "^0.5.7" + } + }, + "babylon": { + "version": "6.18.0", + "resolved": "https://registry.npmjs.org/babylon/-/babylon-6.18.0.tgz", + "integrity": "sha512-q/UEjfGJ2Cm3oKV71DJz9d25TPnq5rhBVL2Q4fA5wcC3jcrdn7+SssEybFIxwAvvP+YCsCYNKughoF33GxgycQ==", + "dev": true + }, + "debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dev": true, + "requires": { + "ms": "2.0.0" + } + } } }, "babel-runtime": { @@ -4543,6 +4630,13 @@ "requires": { "core-js": "^2.4.0", "regenerator-runtime": "^0.11.0" + }, + "dependencies": { + "regenerator-runtime": { + "version": "0.11.1", + "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.11.1.tgz", + "integrity": "sha512-MguG95oij0fC3QV3URf4V2SDYGJhJnJGqvIIgdECeODCT98wSWDAJ94SSuVpYQUoTcGUIL6L4yNB7j1DFFHSBg==" + } } }, "babel-template": { @@ -5799,10 +5893,13 @@ "dev": true }, "convert-source-map": { - "version": "1.5.1", - "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.5.1.tgz", - "integrity": "sha1-uCeAl7m8IpNl3lxiz1/K7YtVmeU=", - "dev": true + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.6.0.tgz", + "integrity": "sha512-eFu7XigvxdZ1ETfbgPBohgyQ/Z++C0eEhTor0qRwBw9unw+L0/6V8wkSuGgzdThkiS5lSpdptOQPD8Ak40a+7A==", + "dev": true, + "requires": { + "safe-buffer": "~5.1.1" + } }, "cookiejar": { "version": "2.1.2", @@ -7647,6 +7744,11 @@ } } }, + "fclone": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/fclone/-/fclone-1.0.11.tgz", + "integrity": "sha1-EOhdo4v+p/xZk0HClu4ddyZu5kA=" + }, "figures": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/figures/-/figures-2.0.0.tgz", @@ -13614,6 +13716,12 @@ "integrity": "sha512-piXx9N2WT8hWb7PBbX1glAuJVIkEyUV9F5fMXFINpZ0x3otVOFKKeGmeuiclFJlP/UrgTckyV606VjH2rNK4bw==", "dev": true }, + "regenerator-runtime": { + "version": "0.11.1", + "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.11.1.tgz", + "integrity": "sha512-MguG95oij0fC3QV3URf4V2SDYGJhJnJGqvIIgdECeODCT98wSWDAJ94SSuVpYQUoTcGUIL6L4yNB7j1DFFHSBg==", + "dev": true + }, "regexpp": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/regexpp/-/regexpp-1.1.0.tgz", @@ -14242,11 +14350,6 @@ "regenerate": "^1.4.0" } }, - "regenerator-runtime": { - "version": "0.11.1", - "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.11.1.tgz", - "integrity": "sha512-MguG95oij0fC3QV3URf4V2SDYGJhJnJGqvIIgdECeODCT98wSWDAJ94SSuVpYQUoTcGUIL6L4yNB7j1DFFHSBg==" - }, "regenerator-transform": { "version": "0.13.3", "resolved": "https://registry.npmjs.org/regenerator-transform/-/regenerator-transform-0.13.3.tgz", diff --git a/client/package.json b/client/package.json index 34a09ec19..8b5491695 100644 --- a/client/package.json +++ b/client/package.json @@ -18,13 +18,14 @@ "author": "Barry Low ", "license": "AGPL-3.0", "devDependencies": { - "@babel/core": "^7.0.1", + "@babel/core": "^7.1.2", "@babel/plugin-proposal-class-properties": "^7.0.0", "@babel/preset-env": "^7.0.0", "@babel/preset-react": "^7.0.0", "autoprefixer": "^8.6.5", + "babel-core": "^7.0.0-bridge.0", "babel-eslint": "^9.0.0", - "babel-loader": "^8.0.2", + "babel-loader": "^7.1.5", "babel-minify-webpack-plugin": "^0.3.1", "babel-plugin-dynamic-import-node": "^1.2.0", "babel-plugin-syntax-dynamic-import": "^6.18.0", @@ -67,6 +68,7 @@ "workbox-cli": "^3.4.1" }, "dependencies": { + "@advancedalgos/web-components": "0.0.1", "@azure/storage-blob": "^10.0.0-preview", "@material-ui/core": "^3.0.3", "@material-ui/icons": "^3.0.1", diff --git a/client/src/App.js b/client/src/App.js index 1fd6e8cb8..8ed26d1a8 100644 --- a/client/src/App.js +++ b/client/src/App.js @@ -64,7 +64,7 @@ wsClient.onReconnected(() => { }) const httpLink = new HttpLink({ uri: graphqlEndpoint }) -const httpUserLink = new HttpLink({ uri: 'http://104.43.240.255:4000/graphql' }) +const httpUserLink = new HttpLink({ uri: 'https://users-api.advancedalgos.net/graphql' }) const link = split( ({ query }) => { diff --git a/client/src/views/common/ImageUpload.js b/client/src/views/common/ImageUpload.js deleted file mode 100644 index fd4d837bb..000000000 --- a/client/src/views/common/ImageUpload.js +++ /dev/null @@ -1,264 +0,0 @@ -import React, { Component } from 'react' -import PropTypes from 'prop-types' -import { Mutation } from 'react-apollo' -import Dropzone from 'react-dropzone' -import { Cropper } from 'react-image-cropper' -import * as Azure from '@azure/storage-blob' -import readAndCompressImage from 'browser-image-resizer' -import { imgSrcToBlob } from 'blob-util' -import { withStyles } from '@material-ui/core/styles' - -import Dialog from '@material-ui/core/Dialog' -import DialogActions from '@material-ui/core/DialogActions' -import DialogContent from '@material-ui/core/DialogContent' -import Button from '@material-ui/core/Button' -import Typography from '@material-ui/core/Typography' - -const styles = theme => ({ - dropzoneContainer: { - display: 'block', - margin: '1em', - height: 200 - }, - dropzone: { - display: 'block', - padding: '3em', - height: 200, - borderWidth: 2, - borderColor: '#999', - backgroundColor: '#eee' - }, - dropzoneText: { - textAlign: 'center' - }, - existingImagePreview: { - width: 100, - height: 100 - }, - cropImageBox: { - width: 350, - height: 350 - } -}) - -// placeholder image if no existing image -const placeHolder = 'data:image/svg+xml;charset=UTF-8,%3Csvg%20width%3D%22288%22%20height%3D%22225%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20viewBox%3D%220%200%20288%20225%22%20preserveAspectRatio%3D%22none%22%3E%3Cdefs%3E%3Cstyle%20type%3D%22text%2Fcss%22%3E%23holder_164edaf95ee%20text%20%7B%20fill%3A%23eceeef%3Bfont-weight%3Abold%3Bfont-family%3AArial%2C%20Helvetica%2C%20Open%20Sans%2C%20sans-serif%2C%20monospace%3Bfont-size%3A14pt%20%7D%20%3C%2Fstyle%3E%3C%2Fdefs%3E%3Cg%20id%3D%22holder_164edaf95ee%22%3E%3Crect%20width%3D%22288%22%20height%3D%22225%22%20fill%3D%22%2355595c%22%3E%3C%2Frect%3E%3Cg%3E%3Ctext%20x%3D%2296.32500076293945%22%20y%3D%22118.8%22%3EAvatar%3C%2Ftext%3E%3C%2Fg%3E%3C%2Fg%3E%3C%2Fsvg%3E' - -// config for image resize and quality -const imageResizeConfig = { - quality: 0.6, - maxWidth: 200, - maxHeight: 200, - autoRotate: true, - debug: true, - mimeType: 'image/jpeg' -} - -class UploadImage extends Component { - constructor (props) { - super(props) - - this.onDrop = this.onDrop.bind(this) - this.handleCrop = this.handleCrop.bind(this) - this.handleClickOpen = this.handleClickOpen.bind(this) - this.handleClose = this.handleClose.bind(this) - this.handleChangeImage = this.handleChangeImage.bind(this) - this.cancelChangeImage = this.cancelChangeImage.bind(this) - this.saveImage = this.saveImage.bind(this) - - this.state = { - open: false, - change: false, - file: null, - src: null, - crop: { - x: 10, - y: 10, - width: 200, - height: 200 - }, - cropping: false, - cropped: null, - uploaded: false, - image: null, - sasqr: null - } - } - - render () { - const { classes, cropContainer, saveImageConfig, containerName, fileName, existingImage, handleUrl, AZURE_SAS_QUERY } = this.props - let image = null - let resizeImage = null - if (existingImage !== undefined && existingImage !== null) { - image = existingImage - } else { - image = placeHolder - } - - if (saveImageConfig !== undefined && saveImageConfig !== null) { - resizeImage = saveImageConfig - } else { - resizeImage = imageResizeConfig - } - - return ( -
- - {(getAzureSAS, { loading, error, data }) => { - if (this.state.cropping) { - return ( - Cropping... - ) - } else { - return ( - -
- {(this.state.change || this.state.src === null) && ( - - this.onDrop(e, this.state.edit)} - className={classes.dropzone} - multiple={false} - > -

Drag-and-drop image here or click to select
Only *.jpeg and *.png images

-
- -
- )} - {!this.state.change && ( - - Existing Image Preview - - - )} - {this.state.image !== null && ( - Save profile to save image changes! - )} -
- -
- -
- {this.state.src !== null && ( - { this.cropper = ref }} - onImgLoad={this.onImageLoaded} - ratio={1} - /> - )} -
-
- - - - -
-
-
- ) - } - }} -
-
- ) - } - - onDrop (file, editImageType) { - console.log('onDrop', file) - this.onSelectFile(file, editImageType) - this.setState({ - file - }) - } - - async handleCrop (e, getAzureSAS, resizeImage, containerName, fileName, handleUrl) { - e.preventDefault() - const data = await getAzureSAS({ variables: { containerName } }) - - const cropped = this.cropper.crop() - const croppedBlob = await imgSrcToBlob(cropped, 'image/png', 'Anonymous') - let resizedBlob = await readAndCompressImage(croppedBlob, resizeImage) - - return this.setState({ cropped: resizedBlob, cropping: true, open: false }, () => { - this.saveImage(data.getAzureSAS, containerName, fileName, handleUrl) - }) - } - - async saveImage (SASQueryParameters, containerName, fileName, handleUrl) { - const SASurl = `${process.env.AZURE_STORAGE_URL}?${this.state.sasqr}` - - // create Azure BlockBlobURL to save image to - const pipeline = Azure.StorageURL.newPipeline(new Azure.AnonymousCredential()) - const serviceURL = new Azure.ServiceURL(SASurl, pipeline) - - const containerURL = Azure.ContainerURL.fromServiceURL(serviceURL, containerName) - const blockBlobURL = Azure.BlockBlobURL.fromContainerURL(containerURL, fileName) - - await Azure.uploadBrowserDataToBlockBlob(Azure.Aborter.None, this.state.cropped, blockBlobURL) - - const image = `${process.env.AZURE_STORAGE_URL}${containerName}/${fileName}?${Math.random()}` // random number keeps browser from using cached version of existing image - handleUrl(image) // pass image URL up to be save to DB - this.setState({ cropping: false, uploaded: true, change: false, image: image }) - return image - } - - handleChangeImage (e, cropContainer) { - e.preventDefault() - let crop = cropContainer // dimensions of cropContainer passed from above - this.setState({ - change: true, - crop: crop - }) - } - - cancelChangeImage () { - this.setState({ change: false }) - } - - handleClickOpen (e) { - e.preventDefault() - this.setState({ open: true }) - } - - handleClose () { - this.setState({ open: false, src: null, change: false }) - } -} - -UploadImage.propTypes = { - classes: PropTypes.object.isRequired, - cropContainer: PropTypes.object, // { x: 10, y: 10, width: 200, height: 200 } (default) - saveImageConfig: PropTypes.object, // see imageResizeConfig above for default (default) - containerName: PropTypes.string.isRequired, // name of Azure Container image will be saved in - fileName: PropTypes.string.isRequired, // filename to save image as - existingImage: PropTypes.string, // image url of existing image (optional) - handleUrl: PropTypes.func.isRequired, // function that handles uploaded image file url - AZURE_SAS_QUERY: PropTypes.any.isRequired // GraphQL for getting Azure SAS URL -} - -export default withStyles(styles)(UploadImage) diff --git a/client/src/views/common/MessageCard.js b/client/src/views/common/MessageCard.js deleted file mode 100644 index fff952915..000000000 --- a/client/src/views/common/MessageCard.js +++ /dev/null @@ -1,45 +0,0 @@ -import React from 'react' -import PropTypes from 'prop-types' - -import Typography from '@material-ui/core/Typography' -import { withStyles } from '@material-ui/core/styles' - -import Card from '@material-ui/core/Card' -import CardContent from '@material-ui/core/CardContent' - -const styles = theme => ({ - card: { - display: 'flex', - width: '100%', - margin: '1.5em', - padding: '3em' - }, - cardDetails: { - display: 'flex', - flex: 1 - }, - cardContent: { - flex: 1 - } -}) - -export const MessageCard = ({ classes, message, children }) => ( - -
- - - {message} - - {children} - -
-
-) - -MessageCard.propTypes = { - children: PropTypes.node, - classes: PropTypes.object.isRequired, - message: PropTypes.string -} - -export default withStyles(styles)(MessageCard) diff --git a/client/src/views/common/UploadImage.js b/client/src/views/common/UploadImage.js deleted file mode 100644 index 9aff6ce47..000000000 --- a/client/src/views/common/UploadImage.js +++ /dev/null @@ -1,345 +0,0 @@ -import React, { Component } from 'react' -import PropTypes from 'prop-types' -import { Mutation } from 'react-apollo' -import Dropzone from 'react-dropzone' -import { Cropper } from 'react-image-cropper' -import * as Azure from '@azure/storage-blob' -import readAndCompressImage from 'browser-image-resizer' -import { imgSrcToBlob } from 'blob-util' -import { withStyles } from '@material-ui/core/styles' - -import Dialog from '@material-ui/core/Dialog' -import DialogActions from '@material-ui/core/DialogActions' -import DialogContent from '@material-ui/core/DialogContent' -import Button from '@material-ui/core/Button' -import Typography from '@material-ui/core/Typography' - -import MessageCard from './MessageCard' - -import GET_AZURE_SAS from '../../graphql/teams/GetAzureSASMutation' - -const styles = theme => ({ - dropzoneContainer: { - display: 'block', - margin: '1em', - minWidth: 200, - height: 200 - }, - dropzone: { - display: 'block', - padding: '3em', - minWidth: 200, - height: 200, - borderWidth: 2, - borderColor: '#999', - backgroundColor: '#eee' - }, - dropzoneText: { - textAlign: 'center' - }, - cropAvatarPreview: { - width: 100, - height: 100 - }, - cropBannerPreview: { - width: 400, - height: 100 - }, - cropAvatarBox: { - width: 350, - height: 350 - }, - cropBannerBox: { - width: 650, - height: 350 - }, - reactCrop: { - } -}) - -const imageAvatarConfig = { - quality: 0.6, - maxWidth: 200, - maxHeight: 200, - autoRotate: true, - debug: true, - mimeType: 'image/jpeg' -} - -const imageBannerConfig = { - quality: 0.6, - maxWidth: 800, - maxHeight: 200, - autoRotate: true, - debug: true, - mimeType: 'image/jpeg' -} - -class UploadImage extends Component { - constructor (props) { - super(props) - - this.onDrop = this.onDrop.bind(this) - this.handleCrop = this.handleCrop.bind(this) - this.handleClickOpen = this.handleClickOpen.bind(this) - this.handleClose = this.handleClose.bind(this) - this.handleSubmit = this.handleSubmit.bind(this) - this.handleChangeImage = this.handleChangeImage.bind(this) - this.cancelChangeImage = this.cancelChangeImage.bind(this) - this.saveImage = this.saveImage.bind(this) - - this.state = { - open: false, - edit: 'avatar', - change: false, - file: null, - src: null, - crop: { - x: 10, - y: 10, - width: 200, - height: 200 - }, - cropping: false, - cropped: null, - rejected: null, - uploaded: false, - avatarImage: null, - bannerImage: null, - sasqr: null - } - } - - render () { - const { classes, team, authId, handleAvatar, handleBanner } = this.props - let avatar = null - let banner = null - if ((team.profile !== null && team.profile.avatar !== undefined && team.profile.avatar !== null && team.profile.avatar !== 'a') || this.state.avatarImage !== null) { - avatar = this.state.avatarImage !== null ? this.state.avatarImage : `${team.profile.avatar}?${Math.random()}` - } else { - avatar = 'data:image/svg+xml;charset=UTF-8,%3Csvg%20width%3D%22288%22%20height%3D%22225%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20viewBox%3D%220%200%20288%20225%22%20preserveAspectRatio%3D%22none%22%3E%3Cdefs%3E%3Cstyle%20type%3D%22text%2Fcss%22%3E%23holder_164edaf95ee%20text%20%7B%20fill%3A%23eceeef%3Bfont-weight%3Abold%3Bfont-family%3AArial%2C%20Helvetica%2C%20Open%20Sans%2C%20sans-serif%2C%20monospace%3Bfont-size%3A14pt%20%7D%20%3C%2Fstyle%3E%3C%2Fdefs%3E%3Cg%20id%3D%22holder_164edaf95ee%22%3E%3Crect%20width%3D%22288%22%20height%3D%22225%22%20fill%3D%22%2355595c%22%3E%3C%2Frect%3E%3Cg%3E%3Ctext%20x%3D%2296.32500076293945%22%20y%3D%22118.8%22%3EAvatar%3C%2Ftext%3E%3C%2Fg%3E%3C%2Fg%3E%3C%2Fsvg%3E' - } - if ((team.profile !== null && team.profile.banner !== undefined && team.profile.banner !== null && team.profile.banner !== '') || this.state.bannerImage !== null) { - banner = this.state.bannerImage !== null ? this.state.bannerImage : `${team.profile.banner}?${Math.random()}` - } else { - banner = 'data:image/svg+xml;charset=UTF-8,%3Csvg%20width%3D%22800%22%20height%3D%22200%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20xmlns%3Asvg%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20preserveAspectRatio%3D%22none%22%3E%3Cdefs%3E%3Cstyle%20type%3D%22text%2Fcss%22%3E%23banner_164edaf95ee%20text%20%7B%20fill%3A%23eceeef%3Bfont-weight%3Abold%3Bfont-family%3AArial%2C%20Helvetica%2C%20Open%20Sans%2C%20sans-serif%2C%20monospace%3Bfont-size%3A14pt%20%7D%3C%2Fstyle%3E%3C%2Fdefs%3E%3Cg%3E%3Ctitle%3EBanner%20Placeholder%3C%2Ftitle%3E%3Crect%20stroke%3D%22null%22%20id%3D%22svg_1%22%20fill%3D%22%2355595c%22%20height%3D%22200%22%20width%3D%22800%22%2F%3E%3Cg%20stroke%3D%22null%22%20id%3D%22banner_164edaf95ee%22%3E%3Ctext%20stroke%3D%22null%22%20id%3D%22svg_3%22%20y%3D%22106.5%22%20x%3D%22367.73438%22%3EBanner%3C%2Ftext%3E%3C%2Fg%3E%3C%2Fg%3E%3C%2Fsvg%3E' - } - - let handleImage - this.state.edit === 'avatar' ? handleImage = handleAvatar : handleImage = handleBanner - - console.log('UploadImage', team, avatar, banner, authId, this.state) - return ( -
- - {(getAzureSAS, { loading, error, data }) => { - console.log('getAzureSAS: ', error, data) - if (!this.state.uploaded) { - if (data !== undefined && data.getAzureSAS !== null) { - this.saveImage(data.getAzureSAS, team.slug, handleImage) - } - // this.handleSubmit(getAzureSAS, team.slug) - } - if (this.state.cropping) { - return ( -
- -
- ) - } else { - return ( - -
- {(this.state.change || (avatar === null && banner === null && this.state.src === null)) && ( - - this.onDrop(e, this.state.edit)} - className={classes.dropzone} - multiple={false} - > -

Drag-and-drop team {this.state.edit} here or click to select files.
Only *.jpeg and *.png images

-
- -
- )} - {!this.state.change && ( - - Banner Crop Preview - - - )} - {!this.state.change && ( - - AvatarCrop Preview - - - )} - {(this.state.avatarImage !== null || this.state.bannerImage !== null) && ( - Click Update Team to save image changes! - )} -
- -
- -
- {this.state.src !== null && ( - { this.cropper = ref }} - onImgLoad={this.onImageLoaded} - ratio={this.state.edit === 'avatar' ? 1 : 4} - /> - )} -
-
- - - - -
-
-
- ) - } - }} -
-
- ) - } - - onDrop (file, editImageType) { - console.log('onDrop', file) - this.onSelectFile(file, editImageType) - this.setState({ - file - }) - } - - onSelectFile (file, editImageType) { - if (file && file.length > 0) { - const reader = new window.FileReader() - reader.addEventListener( - 'load', - () => - this.setState({ - src: reader.result, - open: true - }), - false - ) - reader.readAsDataURL(file[0]) - } - } - - async handleCrop (e, getAzureSAS, teamSlug, handleImage) { - e.preventDefault() - const data = await getAzureSAS({ variables: { teamSlug } }) - console.log('handleCrop', this.cropper, data) - const cropped = this.cropper.crop() - const croppedBlob = await imgSrcToBlob(cropped, 'image/png', 'Anonymous') - let imageConfig = this.state.edit === 'avatar' ? imageAvatarConfig : imageBannerConfig - let resizedBlob = await readAndCompressImage(croppedBlob, imageConfig) - - return this.setState({ cropped: resizedBlob, cropping: true, open: false }, () => { - this.saveImage(data.getAzureSAS, teamSlug, handleImage) - }) - } - - async handleSubmit (e, getAzureSAS, teamSlug) { - e.preventDefault() - let SAS = await getAzureSAS({ variables: { teamSlug } }) - console.log('handleSubmit: ', await SAS) - return this.setState({ sasqr: SAS }) - } - - async saveImage (SASQueryParameters, teamSlug, handleImage) { - const fileName = this.state.edit === 'avatar' ? `${teamSlug}-avatar.jpg` : `${teamSlug}-banner.jpg` - let SASurl - if (this.state.sasqr === null) { - SASurl = process.env.AZURE_SAS_URL - } else { - SASurl = `${process.env.AZURE_STORAGE_URL}?${this.state.sasqr}` - } - console.log('getAzureSAS2 SASurl: ', SASurl, this.state.sasqr) - - const pipeline = Azure.StorageURL.newPipeline(new Azure.AnonymousCredential()) - const serviceURL = new Azure.ServiceURL(SASurl, pipeline) - - const containerName = teamSlug - const containerURL = Azure.ContainerURL.fromServiceURL(serviceURL, containerName) - const blockBlobURL = Azure.BlockBlobURL.fromContainerURL(containerURL, fileName) - - await Azure.uploadBrowserDataToBlockBlob(Azure.Aborter.None, this.state.cropped, blockBlobURL) - - const image = `${process.env.AZURE_STORAGE_URL}${teamSlug}/${fileName}?${Math.random()}` - handleImage(image) - let avatarImage = this.state.edit === 'avatar' ? image : null - let bannerImage = this.state.edit === 'banner' ? image : null - this.setState({ cropping: false, uploaded: true, change: false, avatarImage: avatarImage, bannerImage: bannerImage }) - return image - } - - handleChangeImage (e, editImageType) { - e.preventDefault() - let crop = editImageType === 'avatar' ? { x: 10, y: 10, width: 200, height: 200 } : { x: 10, y: 10, width: 800, height: 200 } - this.setState({ - edit: editImageType, - change: true, - crop: crop - }) - } - - cancelChangeImage () { - this.setState({ change: false }) - } - - handleClickOpen (e, editImageType) { - e.preventDefault() - this.setState({ - open: true - }) - } - - handleClose () { - this.setState({ open: false, src: null, change: false }) - } -} - -UploadImage.propTypes = { - classes: PropTypes.object.isRequired, - team: PropTypes.object, - authId: PropTypes.string.isRequired, - handleAvatar: PropTypes.func.isRequired, - handleBanner: PropTypes.func.isRequired -} - -export default withStyles(styles)(UploadImage) diff --git a/client/src/views/common/index.js b/client/src/views/common/index.js deleted file mode 100644 index 12cfe68a0..000000000 --- a/client/src/views/common/index.js +++ /dev/null @@ -1,4 +0,0 @@ -import MessageCard from './MessageCard' -import UploadImage from './UploadImage' - -export { MessageCard, UploadImage } diff --git a/client/src/views/dashboard/components/ManageTeamEdit.js b/client/src/views/dashboard/components/ManageTeamEdit.js index de5e9aa83..a069787c4 100644 --- a/client/src/views/dashboard/components/ManageTeamEdit.js +++ b/client/src/views/dashboard/components/ManageTeamEdit.js @@ -13,6 +13,8 @@ import DialogContentText from '@material-ui/core/DialogContentText' import DialogTitle from '@material-ui/core/DialogTitle' import Typography from '@material-ui/core/Typography' +import { MessageCard, ImageUpload } from '@advancedalgos/web-components' + import { getItem } from '../../../utils/local-storage' import UPDATE_TEAM_PROFILE from '../../../graphql/teams/UpdateTeamProfileMutation' @@ -20,8 +22,7 @@ import GET_TEAMS_BY_OWNER from '../../../graphql/teams/GetTeamsByOwnerQuery' import { checkGraphQLError } from '../../../utils/graphql-errors' -import { ImageUpload } from '../../common' -import GET_AZURE_SAS from '../../graphql/teams/GetAzureSASMutation' +import GET_AZURE_SAS from '../../../graphql/teams/GetAzureSASMutation' const styles = theme => ({ dialogContainer: { @@ -62,14 +63,13 @@ export class ManageTeamEdit extends Component { render () { console.log(this.props, this.props.slug) - const { classes, team, authId } = this.props + const { classes, team } = this.props return ( @@ -112,38 +112,45 @@ export class ManageTeamEdit extends Component { Edit Team Details - + {(getAzureSAS, { loading, error, data }) => { + console.log('getAzureSAS: ', loading, error, data) + const AzureStorageUrl = process.env.AZURE_STORAGE_URL + const containerName = team.slug + let AzureSASURL + if (!loading && data !== undefined) { + AzureSASURL = data.getAzureSAS + } else { + getAzureSAS({ variables: { teamSlug: containerName } }) + } + + if (loading || data === undefined) { + return () + } else { + return ( + + + + ) + } }} - AZURE_SAS_QUERY={GET_AZURE_SAS} - /> - + Date: Thu, 4 Oct 2018 01:36:56 -0700 Subject: [PATCH 6/9] Integrate Image Upload Component --- api/src/index.js | 81 +++++-------------- api/src/storage/azure.js | 62 +++++++++++++- client/package-lock.json | 12 +-- client/package.json | 2 +- client/src/views/dashboard/FinancialBeings.js | 2 +- client/src/views/dashboard/TeamMembers.js | 2 +- .../views/dashboard/components/DashTeam.js | 2 +- .../dashboard/components/DashTeamItem.js | 4 +- .../dashboard/components/ManageTeamEdit.js | 57 ++++++++++--- .../dashboard/components/ManageTeamsList.js | 2 +- client/src/views/dashboard/settings.js | 2 +- 11 files changed, 139 insertions(+), 89 deletions(-) diff --git a/api/src/index.js b/api/src/index.js index 5ec890d4b..e6fc19e54 100644 --- a/api/src/index.js +++ b/api/src/index.js @@ -15,14 +15,11 @@ const { validateIdToken } = require('./auth/validateIdToken') const { directiveResolvers } = require('./auth/authDirectives') const Azure = require("@azure/storage-blob") +const { createSASQueryURL } = require('./storage/azure') const azureAccount = process.env.AZURE_STORAGE_ACCOUNT const azureKey = process.env.AZURE_STORAGE_ACCESS_KEY const azureStorageUrl = process.env.AZURE_STORAGE_URL -var debug = require('debug')('http') - , http = require('http') - , name = 'My App'; - const TEAMS_FRAGMENT = require('./graphql/TeamsFragment') const TEAMS_CONNECTIONS_FRAGMENT = require('./graphql/TeamsConnectionsFragment') @@ -77,7 +74,7 @@ const resolvers = { return ctx.db.query.team({ where: { name: name } }, `{ name }`) }, async teamsByOwner(parent, { ownerId }, ctx, info) { - console.log('teamsByOwner: ', ctx.user) + console.log('teamsByOwner: ', ctx) return ctx.db.query.teams({where: { owner: ownerId }, orderBy:'updatedAt_DESC'}, TEAMS_FRAGMENT) }, async owner(parent, args, ctx, info) { @@ -137,60 +134,9 @@ const resolvers = { }) }, async getAzureSAS(parent, { teamSlug }, ctx, info) { - // Create start and expiry times - let today = new Date() - let week = new Date() - week.setDate(today.getDate() + 7) - // Create SharedKeyCredential and attach to pipline - const SKC = new Azure.SharedKeyCredential(azureAccount, azureKey) - const pipeline = Azure.StorageURL.newPipeline(SKC) - // Create container URL - const serviceURL = new Azure.ServiceURL(azureStorageUrl, pipeline) - const containerName = teamSlug - const containerURL = Azure.ContainerURL.fromServiceURL(serviceURL, containerName) - - //list container and check if already exists. - let marker - let containerCheck = null - do { - const listContainersResponse = await serviceURL.listContainersSegment( - Azure.Aborter.None, - marker, - ) - // console.log(`ContainerCheck marker: `, listContainersResponse) - marker = listContainersResponse.marker; - for (const container of listContainersResponse.containerItems) { - // console.log(`ContainerCheck: ${container.name} | ${containerName} | ${marker}`) - if(container.name === containerName){ - containerCheck = container.name - } - } - } while (marker) - - // if container doesn't exist, create one - let newContainer - if(containerCheck === null){ - newContainer = await containerURL.create(Azure.Aborter.None, { access: 'blob' }) - } - // console.log('getAzureSAS container: ', containerURL, containerCheck, newContainer) - - // Set permissions for service, resource types and containers - const SASServicePerms = Azure.AccountSASServices.parse('b') - const SASResourceTypes = Azure.AccountSASResourceTypes.parse('co') - const SASContainerPerms = Azure.ContainerSASPermissions.parse('rwl') - - // Generate SAS url - const SASQueryParameters = Azure.generateAccountSASQueryParameters( - { - version: '2017-11-09', - permissions: SASContainerPerms, - startTime: today, - expiryTime: week, - protocol: 'https', - services: SASServicePerms, - resourceTypes: SASResourceTypes - }, SKC ) - return SASQueryParameters + const SASUrl = createSASQueryURL(teamSlug) + console.log('createSASQueryURL: ', SASUrl) + return SASUrl } } } @@ -235,13 +181,26 @@ server.express.use( server.express.post(server.options.endpoint, (req, res, done) => getUser(req, res, done, db)) -const whitelist = [ +let whitelist = [ 'http://localhost:3000', 'http://localhost:3001', 'http://localhost:3002', 'http://localhost:4000', - 'http://localhost:4002' + 'http://localhost:4002', + 'http://localhost:1337' ] +if(process.env.NODE_ENV === 'production'){ + whitelist = [ + 'https://teams.advancedalgos.net', + 'https://users.advancedalgos.net', + 'https://users-api.advancedalgos.net', + 'https://keyvault.advancedalgos.net', + 'https://keyvault-api.advancedalgos.net', + 'http://localhost:1337' + ] +} + + const corsOptionsDelegate = (req, callback) => { var corsOptions if (whitelist.indexOf(req.header('Origin')) !== -1) { diff --git a/api/src/storage/azure.js b/api/src/storage/azure.js index abeea558e..b718d6ef5 100644 --- a/api/src/storage/azure.js +++ b/api/src/storage/azure.js @@ -1,4 +1,7 @@ -const Azure = require('@azure/storage-blob') +const Azure = require("@azure/storage-blob") +const azureAccount = process.env.AZURE_STORAGE_ACCOUNT +const azureKey = process.env.AZURE_STORAGE_ACCESS_KEY +const azureStorageUrl = process.env.AZURE_STORAGE_URL /* Create Azure Storage pipeline * @accountName: Azure Blob Storage account name (required) - Environment variable @@ -120,6 +123,60 @@ const deleteContainer = async (containerURL) => { return true } +const createSASQueryURL = async (containerName) => { + let today = new Date() + let week = new Date() + week.setDate(today.getDate() + 7) + // Create SharedKeyCredential and attach to pipline + const SKC = new Azure.SharedKeyCredential(azureAccount, azureKey) + const pipeline = Azure.StorageURL.newPipeline(SKC) + // Create container URL + const serviceURL = new Azure.ServiceURL(azureStorageUrl, pipeline) + const containerURL = Azure.ContainerURL.fromServiceURL(serviceURL, containerName) + + //list container and check if already exists. + let marker + let containerCheck = null + do { + const listContainersResponse = await serviceURL.listContainersSegment( + Azure.Aborter.None, + marker, + ) + // console.log(`ContainerCheck marker: `, listContainersResponse) + marker = listContainersResponse.marker; + for (const container of listContainersResponse.containerItems) { + // console.log(`ContainerCheck: ${container.name} | ${containerName} | ${marker}`) + if(container.name === containerName){ + containerCheck = container.name + } + } + } while (marker) + + // if container doesn't exist, create one + let newContainer + if(containerCheck === null){ + newContainer = await containerURL.create(Azure.Aborter.None, { access: 'blob' }) + } + console.log('createSASQueryURL container: ', containerURL, containerCheck, newContainer) + + // Set permissions for service, resource types and containers + const SASContainerPerms = await Azure.AccountSASPermissions.parse('rwlacu') + const SASServicePerms = await Azure.AccountSASServices.parse('b') + const SASResourceTypes = await Azure.AccountSASResourceTypes.parse('co') + console.log('createSASQueryURL permissions: ', SASServicePerms, SASResourceTypes, SASContainerPerms) + // Generate SAS url + const SASQueryParameters = await Azure.generateBlobSASQueryParameters( + { + version: '2017-11-09', + protocol: 'https,http', + expiryTime: week, + permissions: 'rwlac', + containerName: containerName + }, SKC ) + console.log('createSASQueryURL SASQueryParameters: ', containerURL.url, SASQueryParameters) + return SASQueryParameters +} + module.exports = { createStoragePipline, createServiceURL, @@ -129,5 +186,6 @@ module.exports = { uploadBlob, downloadBlob, deleteBlob, - deleteContainer + deleteContainer, + createSASQueryURL } diff --git a/client/package-lock.json b/client/package-lock.json index 4a43e9c8d..b1ce31784 100644 --- a/client/package-lock.json +++ b/client/package-lock.json @@ -5,9 +5,9 @@ "requires": true, "dependencies": { "@advancedalgos/web-components": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/@advancedalgos/web-components/-/web-components-0.0.1.tgz", - "integrity": "sha512-RDi7oR5oY4HbcXdzTDhtgYVHRO+/ApBMossZ/UkCrI2lYAWhQaq4mYaKaJ2mtVKnwV1BkeOLFE6rbPZwIkhXJA==", + "version": "0.0.5", + "resolved": "https://registry.npmjs.org/@advancedalgos/web-components/-/web-components-0.0.5.tgz", + "integrity": "sha512-mgaS2AwtVcbP0W8/Rk95TNFTrDmp21rDmoqrO177WGLCcTH5n3rHisQtrdVaEvLiUGoZ/8us/GeclljVJP1edw==", "requires": { "@azure/storage-blob": "^10.1.0-preview", "@material-ui/core": "^3.0.3", @@ -35,9 +35,9 @@ } }, "@types/node": { - "version": "9.6.32", - "resolved": "https://registry.npmjs.org/@types/node/-/node-9.6.32.tgz", - "integrity": "sha512-5+L3wQ+FHoQ589EaH6rYICleuj8gnunq+1CJkM9fxklirErIOv+kxm3s/vecYnpJOYnFowE5uUizcb3hgjHUug==" + "version": "9.6.34", + "resolved": "https://registry.npmjs.org/@types/node/-/node-9.6.34.tgz", + "integrity": "sha512-PzJpSs2afoYqBA4yLBgaKUdZRk8+1yvkxcUBW6958h4vYOC+pc4k4C+QmQ6AO5Pt7uA4EIIboFog6YNCuITD0g==" }, "apollo-cache": { "version": "1.1.17", diff --git a/client/package.json b/client/package.json index 8b5491695..c40d1672e 100644 --- a/client/package.json +++ b/client/package.json @@ -68,7 +68,7 @@ "workbox-cli": "^3.4.1" }, "dependencies": { - "@advancedalgos/web-components": "0.0.1", + "@advancedalgos/web-components": "0.0.5", "@azure/storage-blob": "^10.0.0-preview", "@material-ui/core": "^3.0.3", "@material-ui/icons": "^3.0.1", diff --git a/client/src/views/dashboard/FinancialBeings.js b/client/src/views/dashboard/FinancialBeings.js index be6ac2aba..c4d13ee80 100644 --- a/client/src/views/dashboard/FinancialBeings.js +++ b/client/src/views/dashboard/FinancialBeings.js @@ -4,7 +4,7 @@ import PropTypes from 'prop-types' import Typography from '@material-ui/core/Typography' import { withStyles } from '@material-ui/core/styles' -import { MessageCard } from '../common/' +import { MessageCard } from '@advancedalgos/web-components' const styles = theme => ({ tableContainer: { diff --git a/client/src/views/dashboard/TeamMembers.js b/client/src/views/dashboard/TeamMembers.js index 8d86d4155..1bbb78295 100644 --- a/client/src/views/dashboard/TeamMembers.js +++ b/client/src/views/dashboard/TeamMembers.js @@ -4,7 +4,7 @@ import PropTypes from 'prop-types' import Typography from '@material-ui/core/Typography' import { withStyles } from '@material-ui/core/styles' -import { MessageCard } from '../common/' +import { MessageCard } from '@advancedalgos/web-components' const styles = theme => ({ tableContainer: { diff --git a/client/src/views/dashboard/components/DashTeam.js b/client/src/views/dashboard/components/DashTeam.js index 1f6fc42c7..cc2f3b1b4 100644 --- a/client/src/views/dashboard/components/DashTeam.js +++ b/client/src/views/dashboard/components/DashTeam.js @@ -11,7 +11,7 @@ import GET_TEAMS_BY_OWNER from '../../../graphql/teams/GetTeamsByOwnerQuery' import DashTeamItem from './DashTeamItem' import CreateTeamDialog from './CreateTeamDialog' -import { MessageCard } from '../../common/' +import { MessageCard } from '@advancedalgos/web-components' import { isDefined, isString } from '../../../utils/js-helpers' import { getItem } from '../../../utils/local-storage' diff --git a/client/src/views/dashboard/components/DashTeamItem.js b/client/src/views/dashboard/components/DashTeamItem.js index e917b9006..db7f1b481 100644 --- a/client/src/views/dashboard/components/DashTeamItem.js +++ b/client/src/views/dashboard/components/DashTeamItem.js @@ -35,13 +35,15 @@ const styles = theme => ({ } }) +const placeholder = 'data:image/svg+xml;charset=UTF-8,%3Csvg%20width%3D%22288%22%20height%3D%22225%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20viewBox%3D%220%200%20288%20225%22%20preserveAspectRatio%3D%22none%22%3E%3Cdefs%3E%3Cstyle%20type%3D%22text%2Fcss%22%3E%23holder_164edaf95ee%20text%20%7B%20fill%3A%23eceeef%3Bfont-weight%3Abold%3Bfont-family%3AArial%2C%20Helvetica%2C%20Open%20Sans%2C%20sans-serif%2C%20monospace%3Bfont-size%3A14pt%20%7D%20%3C%2Fstyle%3E%3C%2Fdefs%3E%3Cg%20id%3D%22holder_164edaf95ee%22%3E%3Crect%20width%3D%22288%22%20height%3D%22225%22%20fill%3D%22%2355595c%22%3E%3C%2Frect%3E%3Cg%3E%3Ctext%20x%3D%2296.32500076293945%22%20y%3D%22118.8%22%3EThumbnail%3C%2Ftext%3E%3C%2Fg%3E%3C%2Fg%3E%3C%2Fsvg%3E' // eslint-disable-line max-len + export const DashTeamItem = ({ classes, team }) => (
diff --git a/client/src/views/dashboard/components/ManageTeamEdit.js b/client/src/views/dashboard/components/ManageTeamEdit.js index a069787c4..900dfa62f 100644 --- a/client/src/views/dashboard/components/ManageTeamEdit.js +++ b/client/src/views/dashboard/components/ManageTeamEdit.js @@ -15,8 +15,6 @@ import Typography from '@material-ui/core/Typography' import { MessageCard, ImageUpload } from '@advancedalgos/web-components' -import { getItem } from '../../../utils/local-storage' - import UPDATE_TEAM_PROFILE from '../../../graphql/teams/UpdateTeamProfileMutation' import GET_TEAMS_BY_OWNER from '../../../graphql/teams/GetTeamsByOwnerQuery' @@ -51,6 +49,7 @@ export class ManageTeamEdit extends Component { const description = props.team.profile.description || '' const avatar = props.team.profile.avatar || '' const banner = props.team.profile.banner || '' + this.authId = props.authId this.state = { open: false, @@ -62,14 +61,15 @@ export class ManageTeamEdit extends Component { } render () { - console.log(this.props, this.props.slug) + console.log('ManageTeamEdit ', this.props, this.props.slug, this.props.team) const { classes, team } = this.props return ( @@ -114,7 +114,7 @@ export class ManageTeamEdit extends Component { {(getAzureSAS, { loading, error, data }) => { - console.log('getAzureSAS: ', loading, error, data) + console.log('getAzureSAS: ', loading, error, data, team.profile) const AzureStorageUrl = process.env.AZURE_STORAGE_URL const containerName = team.slug let AzureSASURL @@ -124,16 +124,46 @@ export class ManageTeamEdit extends Component { getAzureSAS({ variables: { teamSlug: containerName } }) } + let avatar = null + if (this.state.avatar !== null) avatar = this.state.avatar + if (team.profile !== null && team.profile.avatar !== undefined && team.profile.avatar !== null) avatar = team.profile.avatar + + let banner = null + if (this.state.banner !== null) banner = this.state.banner + if (team.profile !== null && team.profile.banner !== undefined && team.profile.banner !== null) banner = team.profile.banner + console.log('team images: ', avatar, banner) + if (loading || data === undefined) { return () } else { return ( + ) @@ -230,24 +262,22 @@ export class ManageTeamEdit extends Component { handleAvatar (avatarUrl) { console.log('handleAvatar: ', avatarUrl) - this.setState({ avatar: avatarUrl }) + this.setState({ avatar: `${avatarUrl}?${Math.random()}` }) } handleBanner (bannerUrl) { console.log('handleBanner: ', bannerUrl) - this.setState({ banner: bannerUrl }) + this.setState({ banner: `${bannerUrl}?${Math.random()}` }) } async handleSubmit (e, updateTeamProfile, slug) { console.log('handleSubmit: ', this.state) e.preventDefault() - const currentUser = await getItem('user') - let authId = JSON.parse(currentUser) - authId = authId.authId + await updateTeamProfile({ variables: { slug, - owner: authId, + owner: this.authId, description: this.state.description, motto: this.state.motto, avatar: this.state.avatar, @@ -261,7 +291,8 @@ export class ManageTeamEdit extends Component { ManageTeamEdit.propTypes = { classes: PropTypes.object.isRequired, slug: PropTypes.string.isRequired, - team: PropTypes.object + team: PropTypes.object, + authId: PropTypes.string.isRequired } export default withStyles(styles)(ManageTeamEdit) diff --git a/client/src/views/dashboard/components/ManageTeamsList.js b/client/src/views/dashboard/components/ManageTeamsList.js index d72921486..816a4db88 100644 --- a/client/src/views/dashboard/components/ManageTeamsList.js +++ b/client/src/views/dashboard/components/ManageTeamsList.js @@ -4,6 +4,7 @@ import { Query } from 'react-apollo' import { withStateHandlers, lifecycle, compose } from 'recompose' import Grid from '@material-ui/core/Grid' +import { MessageCard } from '@advancedalgos/web-components' import { withStyles } from '@material-ui/core/styles' import { isDefined, isString } from '../../../utils/js-helpers' @@ -13,7 +14,6 @@ import GET_TEAMS_BY_OWNER from '../../../graphql/teams/GetTeamsByOwnerQuery' import ManageTeamsItem from './ManageTeamsItem' import CreateTeamDialog from './CreateTeamDialog' -import { MessageCard } from '../../common/' const styles = theme => ({ heroContent: { diff --git a/client/src/views/dashboard/settings.js b/client/src/views/dashboard/settings.js index d28df4d79..f427a01e2 100644 --- a/client/src/views/dashboard/settings.js +++ b/client/src/views/dashboard/settings.js @@ -4,7 +4,7 @@ import PropTypes from 'prop-types' import Typography from '@material-ui/core/Typography' import { withStyles } from '@material-ui/core/styles' -import { MessageCard } from '../common/' +import { MessageCard } from '@advancedalgos/web-components' const styles = theme => ({ tableContainer: { From f50bca125ded72326dba4791c358238ef0fddc43 Mon Sep 17 00:00:00 2001 From: Barry Low Date: Thu, 4 Oct 2018 01:57:04 -0700 Subject: [PATCH 7/9] Incorporate team images in public team profiles --- .../views/teams/components/TeamsDetails.js | 107 +++++++++++------- .../src/views/teams/components/TeamsItem.js | 82 ++++++++------ 2 files changed, 115 insertions(+), 74 deletions(-) diff --git a/client/src/views/teams/components/TeamsDetails.js b/client/src/views/teams/components/TeamsDetails.js index 5383895b6..9351197d7 100644 --- a/client/src/views/teams/components/TeamsDetails.js +++ b/client/src/views/teams/components/TeamsDetails.js @@ -42,49 +42,76 @@ const styles = theme => ({ maxWidth: 600, margin: '0 auto', padding: `${theme.spacing.unit * 8}px 0 ${theme.spacing.unit * 6}px` + }, + banner: { + maxWidth: '100%', + height: 'auto', + margin: '0 auto' + }, + avatar: { + maxWidth: 100, + height: 100, + margin: '0 1em 1em 0', + float: 'left' } }) -const TeamsDetails = ({ classes, team }) => ( - - -
- -
- ← Back to all teams - - {team.name} - - - {team.createdAt} - - - Motto: {team.profile.motto} - - - Description: {team.profile.description} - - - Members: {team.members.length} - - - Team Admin:  - {team.members.map(member => { - if (member.role === 'OWNER' || member.role === 'ADMIN') { - return member.member.alias - } - })} - -
-
-
-
-) +const TeamsDetails = ({ classes, team }) => { + let avatar + if (team.profile.avatar !== undefined && team.profile.avatar !== 'a') { + avatar = team.profile.avatar + } else { + avatar = 'data:image/svg+xml;charset=UTF-8,%3Csvg%20width%3D%22288%22%20height%3D%22225%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20viewBox%3D%220%200%20288%20225%22%20preserveAspectRatio%3D%22none%22%3E%3Cdefs%3E%3Cstyle%20type%3D%22text%2Fcss%22%3E%23holder_164edaf95ee%20text%20%7B%20fill%3A%23eceeef%3Bfont-weight%3Abold%3Bfont-family%3AArial%2C%20Helvetica%2C%20Open%20Sans%2C%20sans-serif%2C%20monospace%3Bfont-size%3A14pt%20%7D%20%3C%2Fstyle%3E%3C%2Fdefs%3E%3Cg%20id%3D%22holder_164edaf95ee%22%3E%3Crect%20width%3D%22288%22%20height%3D%22225%22%20fill%3D%22%2355595c%22%3E%3C%2Frect%3E%3Cg%3E%3Ctext%20x%3D%2296.32500076293945%22%20y%3D%22118.8%22%3EThumbnail%3C%2Ftext%3E%3C%2Fg%3E%3C%2Fg%3E%3C%2Fsvg%3E' + } + let banner + if (team.profile.banner !== undefined && team.profile.banner !== 'a') { + banner = team.profile.banner + } else { + banner = 'data:image/svg+xml;charset=UTF-8,%3Csvg%20width%3D%22288%22%20height%3D%22225%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20viewBox%3D%220%200%20288%20225%22%20preserveAspectRatio%3D%22none%22%3E%3Cdefs%3E%3Cstyle%20type%3D%22text%2Fcss%22%3E%23holder_164edaf95ee%20text%20%7B%20fill%3A%23eceeef%3Bfont-weight%3Abold%3Bfont-family%3AArial%2C%20Helvetica%2C%20Open%20Sans%2C%20sans-serif%2C%20monospace%3Bfont-size%3A14pt%20%7D%20%3C%2Fstyle%3E%3C%2Fdefs%3E%3Cg%20id%3D%22holder_164edaf95ee%22%3E%3Crect%20width%3D%22288%22%20height%3D%22225%22%20fill%3D%22%2355595c%22%3E%3C%2Frect%3E%3Cg%3E%3Ctext%20x%3D%2296.32500076293945%22%20y%3D%22118.8%22%3EThumbnail%3C%2Ftext%3E%3C%2Fg%3E%3C%2Fg%3E%3C%2Fsvg%3E' + } + return ( + + +
+ +
+ ← Back to all teams + {team.name} + {team.name} + + {team.name} + + + {team.createdAt} + + + Motto: {team.profile.motto} + + + Description: {team.profile.description} + + + Members: {team.members.length} + + + Team Admin:  + {team.members.map(member => { + if (member.role === 'OWNER' || member.role === 'ADMIN') { + return member.member.alias + } + })} + +
+
+
+
+ ) +} TeamsDetails.propTypes = { classes: PropTypes.object.isRequired, team: PropTypes.object.isRequired diff --git a/client/src/views/teams/components/TeamsItem.js b/client/src/views/teams/components/TeamsItem.js index 9f0d78bf9..b82242757 100644 --- a/client/src/views/teams/components/TeamsItem.js +++ b/client/src/views/teams/components/TeamsItem.js @@ -12,40 +12,54 @@ import Typography from '@material-ui/core/Typography' import { Link } from 'react-router-dom' -export const TeamsItem = ({ classes, team }) => ( - - - - - - - {team.name} - - - {team.createdAt} - - - Members: 1 - - - Now recruiting! - - - - - - - - - - -) +export const TeamsItem = ({ classes, team }) => { + /* let avatar + if (team.profile.avatar !== undefined && team.profile.avatar !== 'a') { + avatar = team.profile.avatar + } else { + avatar = 'data:image/svg+xml;charset=UTF-8,%3Csvg%20width%3D%22288%22%20height%3D%22225%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20viewBox%3D%220%200%20288%20225%22%20preserveAspectRatio%3D%22none%22%3E%3Cdefs%3E%3Cstyle%20type%3D%22text%2Fcss%22%3E%23holder_164edaf95ee%20text%20%7B%20fill%3A%23eceeef%3Bfont-weight%3Abold%3Bfont-family%3AArial%2C%20Helvetica%2C%20Open%20Sans%2C%20sans-serif%2C%20monospace%3Bfont-size%3A14pt%20%7D%20%3C%2Fstyle%3E%3C%2Fdefs%3E%3Cg%20id%3D%22holder_164edaf95ee%22%3E%3Crect%20width%3D%22288%22%20height%3D%22225%22%20fill%3D%22%2355595c%22%3E%3C%2Frect%3E%3Cg%3E%3Ctext%20x%3D%2296.32500076293945%22%20y%3D%22118.8%22%3EThumbnail%3C%2Ftext%3E%3C%2Fg%3E%3C%2Fg%3E%3C%2Fsvg%3E' + } */ + let banner + if (team.profile.banner !== undefined && team.profile.banner !== null) { + banner = team.profile.banner + } else { + banner = 'data:image/svg+xml;charset=UTF-8,%3Csvg%20width%3D%22288%22%20height%3D%22225%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20viewBox%3D%220%200%20288%20225%22%20preserveAspectRatio%3D%22none%22%3E%3Cdefs%3E%3Cstyle%20type%3D%22text%2Fcss%22%3E%23holder_164edaf95ee%20text%20%7B%20fill%3A%23eceeef%3Bfont-weight%3Abold%3Bfont-family%3AArial%2C%20Helvetica%2C%20Open%20Sans%2C%20sans-serif%2C%20monospace%3Bfont-size%3A14pt%20%7D%20%3C%2Fstyle%3E%3C%2Fdefs%3E%3Cg%20id%3D%22holder_164edaf95ee%22%3E%3Crect%20width%3D%22288%22%20height%3D%22225%22%20fill%3D%22%2355595c%22%3E%3C%2Frect%3E%3Cg%3E%3Ctext%20x%3D%2296.32500076293945%22%20y%3D%22118.8%22%3EThumbnail%3C%2Ftext%3E%3C%2Fg%3E%3C%2Fg%3E%3C%2Fsvg%3E' + } + return ( + + + + + + + {team.name} + + + {team.createdAt} + + + Members: 1 + + + Now recruiting! + + + + + + + + + + + ) +} TeamsItem.propTypes = { classes: PropTypes.object.isRequired, From f7ce9b550261f51428bfabb296959e34a2024f42 Mon Sep 17 00:00:00 2001 From: Barry Low Date: Thu, 4 Oct 2018 02:02:33 -0700 Subject: [PATCH 8/9] debug teams details --- api/src/graphql/TeamsConnectionsFragment.js | 1 + client/src/views/teams/components/TeamsDetails.js | 8 -------- 2 files changed, 1 insertion(+), 8 deletions(-) diff --git a/api/src/graphql/TeamsConnectionsFragment.js b/api/src/graphql/TeamsConnectionsFragment.js index 89a22a8c4..37efc6c13 100644 --- a/api/src/graphql/TeamsConnectionsFragment.js +++ b/api/src/graphql/TeamsConnectionsFragment.js @@ -15,6 +15,7 @@ const TEAMS_CONNECTIONS_FRAGMENT = gql`{ createdAt profile { avatar + banner description motto updatedAt diff --git a/client/src/views/teams/components/TeamsDetails.js b/client/src/views/teams/components/TeamsDetails.js index 9351197d7..5de7a2765 100644 --- a/client/src/views/teams/components/TeamsDetails.js +++ b/client/src/views/teams/components/TeamsDetails.js @@ -98,14 +98,6 @@ const TeamsDetails = ({ classes, team }) => { Members: {team.members.length} - - Team Admin:  - {team.members.map(member => { - if (member.role === 'OWNER' || member.role === 'ADMIN') { - return member.member.alias - } - })} -
From ac6c7581f88734e7ccf8db603e62e88b93770aa8 Mon Sep 17 00:00:00 2001 From: Barry Low Date: Fri, 5 Oct 2018 12:49:11 -0700 Subject: [PATCH 9/9] update deployment --- api/.gitignore | 1 + api/docker-compose.api.yml | 4 ++-- api/src/index.js | 20 ++++++++------------ client/.gitignore | 1 + client/tools/webpack/webpack.prod.config.js | 1 + 5 files changed, 13 insertions(+), 14 deletions(-) diff --git a/api/.gitignore b/api/.gitignore index 131de51f8..d5985783d 100644 --- a/api/.gitignore +++ b/api/.gitignore @@ -1,4 +1,5 @@ /.env +/.env.prod /database/.env /database/docker-compose.prisma.prod.yml /docker-compose.prisma.prod.yml diff --git a/api/docker-compose.api.yml b/api/docker-compose.api.yml index 6282e3a87..54ec81af4 100644 --- a/api/docker-compose.api.yml +++ b/api/docker-compose.api.yml @@ -1,8 +1,8 @@ version: '3' services: api: - image: advancedalgosteamsmodule/teammodule-api:v1 - env_file: ./.env + image: advancedalgosteamsmodule/teammodule-api:v2 + env_file: ./.env.prod container_name: aateamapi tty: true stdin_open: true diff --git a/api/src/index.js b/api/src/index.js index e6fc19e54..7c81ae29e 100644 --- a/api/src/index.js +++ b/api/src/index.js @@ -187,19 +187,15 @@ let whitelist = [ 'http://localhost:3002', 'http://localhost:4000', 'http://localhost:4002', - 'http://localhost:1337' + 'http://localhost:1337', + 'https://teams.advancedalgos.net', + 'https://users.advancedalgos.net', + 'https://users-api.advancedalgos.net', + 'https://keyvault.advancedalgos.net', + 'https://keyvault-api.advancedalgos.net', + 'https://aawebdevelop.azurewebsites.net', + 'https://develop.advancedalgos.net' ] -if(process.env.NODE_ENV === 'production'){ - whitelist = [ - 'https://teams.advancedalgos.net', - 'https://users.advancedalgos.net', - 'https://users-api.advancedalgos.net', - 'https://keyvault.advancedalgos.net', - 'https://keyvault-api.advancedalgos.net', - 'http://localhost:1337' - ] -} - const corsOptionsDelegate = (req, callback) => { var corsOptions diff --git a/client/.gitignore b/client/.gitignore index 64458ae7a..7c1f225e5 100644 --- a/client/.gitignore +++ b/client/.gitignore @@ -56,6 +56,7 @@ typings/ # dotenv environment variables file .env +.env.prod # Build folder js files build/ diff --git a/client/tools/webpack/webpack.prod.config.js b/client/tools/webpack/webpack.prod.config.js index 14724d7ad..b5810a748 100644 --- a/client/tools/webpack/webpack.prod.config.js +++ b/client/tools/webpack/webpack.prod.config.js @@ -12,6 +12,7 @@ const Dotenv = require('dotenv-webpack') module.exports = merge(commonConfig, { mode: 'production', + devtool: 'inline-sourcemap', output: { path: path.resolve(__dirname, '../../build'), filename: 'script.js'