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: ________________ + diff --git a/LICENSE b/LICENSE new file mode 100644 index 000000000..f513e6254 --- /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 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 + 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. + + 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/database/datamodel.graphql b/api/database/datamodel.graphql index 9c07196ef..7683fd3fe 100644 --- a/api/database/datamodel.graphql +++ b/api/database/datamodel.graphql @@ -96,6 +96,9 @@ type FinancialBeings { id: ID! @unique type: FBType kind: FBKind + name: String! + slug: String! + avatar: String team: Team @relation (name: "TeamFinancialBeing", onDelete: SET_NULL) status: [FBStatus!]! updatedAt: DateTime! diff --git a/api/docker-compose.api.yml b/api/docker-compose.api.yml index 689c37fa6..54ec81af4 100644 --- a/api/docker-compose.api.yml +++ b/api/docker-compose.api.yml @@ -1,11 +1,8 @@ version: '3' services: api: - image: aateamapi-dev:v1 - build: - context: . - dockerfile: ./Dockerfile - 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/auth/authDirectives.js b/api/src/auth/authDirectives.js index 0ba153fce..057f85a72 100644 --- a/api/src/auth/authDirectives.js +++ b/api/src/auth/authDirectives.js @@ -29,7 +29,7 @@ const isLoggedIn = async ctx => { let member = ctxMember(ctx, memberLocationOnContext) let token = ctxToken(ctx, bearerAccessToken) let memberToken - console.log('isLoggedIn: ', ctx, member, token) + // console.log('isLoggedIn: ', ctx, member, token) if (!member && token) { let scheme, credentials const tokenParts = token.split(' ') @@ -41,16 +41,16 @@ const isLoggedIn = async ctx => { if (/^Bearer$/i.test(scheme)) { token = credentials //verify token - console.log('isLoggedIn2 : ', token) + // console.log('isLoggedIn2 : ', token) try { memberToken = await validateIdToken(token) const authId = memberToken.sub - console.log('isLoggedIn3 : ', await authId, ctx.db.query.member) + // console.log('isLoggedIn3 : ', await authId, ctx.db.query.member) exists = await ctx.db.query.member({ where: { authId: authId } }) - console.log('isLoggedIn4 : ', await exists, createMember ) + // console.log('isLoggedIn4 : ', await exists, createMember ) if (!member && exists === null) { member = await createMember(ctx, memberToken).then(res => { - console.log('isLoggedIn5 : ', res) + // console.log('isLoggedIn5 : ', res) return res }) @@ -62,7 +62,7 @@ const isLoggedIn = async ctx => { } } } - console.log('isLoggedIn5 : ', await member) + // console.log('isLoggedIn5 : ', await member) if (!member) { throw new Error(`Not logged in`) } @@ -95,13 +95,13 @@ const directiveResolvers = { ? source : ctx.request.body.variables ? ctx.request.body.variables : { id: null } const user = await isLoggedIn(ctx) - console.log('directive isOwner 0: ', typeId, user.sub) + // console.log('directive isOwner 0: ', typeId, user.sub) const memberId = user.sub const isOwner = type === `Member` ? memberId === typeId : await isRequestingMemberAlsoOwner({ ctx, memberId, type, typeId }) - console.log('directive isOwner 1: ', source, isOwner, type, typeId, memberId) + // console.log('directive isOwner 1: ', source, isOwner, type, typeId, memberId) if (isOwner) { return next() } diff --git a/api/src/email/sendgrid.js b/api/src/email/sendgrid.js index a30969ea8..79dc68536 100644 --- a/api/src/email/sendgrid.js +++ b/api/src/email/sendgrid.js @@ -67,6 +67,71 @@ const sendTeamMemberInvite = function(email, team) { return sendVerify } +const sendTeamCreateConfirmation = function(email, teamName, botName, sessionToken) { + const dev = process.env.NODE_ENV === 'development' ? true : false + console.log('sendTeamMemberInvite', email, team) + const API_KEY = process.env.SG_APIKEY + let origin = 'https://teams.advancedalgos.net' + if (dev){ + origin = 'http://localhost:3001' + } + + const headers = { + 'Content-Type': 'application/json', + 'Access-Control-Allow-Origin' : origin, + 'Access-Control-Allow-Credentials': 'true' + }; + + const data = JSON.stringify({ + "personalizations": [ + { + "to": [ + { + "email": email + } + ], + dynamic_template_data: { + "aateamdeveloplink": "https://develop.advancedalgos.net/index.html?" + sessionToken, + "aateamname": teamName, + "aabotname": botName, + "subject": "Team " + teamName + " has been created!" + }, + "subject": "Team " + teamName + " has been created!" + } + ], + "from": { + "email": "noreply@advancedalgos.net", + "name": "Advanced Algos Teams" + }, + "reply_to": { + "email": "feedback@advancedalgos.net", + "name": "Advanced Algos Teams" + }, + "template_id": process.env.SG_TEAMCREATE_EMAILID + }) + + let sendTeamCreation = axios({ + method: 'post', + url: 'https://api.sendgrid.com/v3/mail/send', + data: data, + headers:{ + 'content-type': 'application/json', + 'authorization': 'Bearer ' + API_KEY + } + }) + .then(function (response) { + if (response.status >= 200 && response.status < 300) { + return 'Success' + } else { + throw response.data.errors[0].message + } + }) + .catch(function (error) { + return `${error.status}-${error.response.data.errors[0].message}` + }) + return sendTeamCreation +} + const verifyInviteToken = async function(token) { const API_KEY = process.env.SG_APIKEY let verifiedToken @@ -84,4 +149,4 @@ const verifyInviteToken = async function(token) { return verifiedToken } -module.exports = { sendTeamMemberInvite, verifyInviteToken } +module.exports = { sendTeamMemberInvite, sendTeamCreateConfirmation, verifyInviteToken } diff --git a/api/src/generated/prisma.graphql b/api/src/generated/prisma.graphql index 1dade0729..23fbd434a 100644 --- a/api/src/generated/prisma.graphql +++ b/api/src/generated/prisma.graphql @@ -1,5 +1,5 @@ # source: http://localhost:4466 -# timestamp: Thu Sep 27 2018 23:28:16 GMT-0700 (Pacific Daylight Time) +# timestamp: Fri Oct 05 2018 13:16:40 GMT-0700 (Pacific Daylight Time) type AggregateFBStatus { count: Int! @@ -249,6 +249,9 @@ type FinancialBeings implements Node { id: ID! type: FBType kind: FBKind + name: String! + slug: String! + avatar: String team(where: TeamWhereInput): Team status(where: FBStatusWhereInput, orderBy: FBStatusOrderByInput, skip: Int, after: String, before: String, first: Int, last: Int): [FBStatus!] updatedAt: DateTime! @@ -268,6 +271,9 @@ type FinancialBeingsConnection { input FinancialBeingsCreateInput { type: FBType kind: FBKind + name: String! + slug: String! + avatar: String team: TeamCreateOneWithoutFbInput status: FBStatusCreateManyInput } @@ -280,6 +286,9 @@ input FinancialBeingsCreateManyWithoutTeamInput { input FinancialBeingsCreateWithoutTeamInput { type: FBType kind: FBKind + name: String! + slug: String! + avatar: String status: FBStatusCreateManyInput } @@ -299,6 +308,12 @@ enum FinancialBeingsOrderByInput { type_DESC kind_ASC kind_DESC + name_ASC + name_DESC + slug_ASC + slug_DESC + avatar_ASC + avatar_DESC updatedAt_ASC updatedAt_DESC createdAt_ASC @@ -309,6 +324,9 @@ type FinancialBeingsPreviousValues { id: ID! type: FBType kind: FBKind + name: String! + slug: String! + avatar: String updatedAt: DateTime! createdAt: DateTime! } @@ -355,6 +373,9 @@ input FinancialBeingsSubscriptionWhereInput { input FinancialBeingsUpdateInput { type: FBType kind: FBKind + name: String + slug: String + avatar: String team: TeamUpdateOneWithoutFbInput status: FBStatusUpdateManyInput } @@ -371,6 +392,9 @@ input FinancialBeingsUpdateManyWithoutTeamInput { input FinancialBeingsUpdateWithoutTeamDataInput { type: FBType kind: FBKind + name: String + slug: String + avatar: String status: FBStatusUpdateManyInput } @@ -454,6 +478,126 @@ input FinancialBeingsWhereInput { """All values that are not contained in given list.""" kind_not_in: [FBKind!] + name: String + + """All values that are not equal to given value.""" + name_not: String + + """All values that are contained in given list.""" + name_in: [String!] + + """All values that are not contained in given list.""" + name_not_in: [String!] + + """All values less than the given value.""" + name_lt: String + + """All values less than or equal the given value.""" + name_lte: String + + """All values greater than the given value.""" + name_gt: String + + """All values greater than or equal the given value.""" + name_gte: String + + """All values containing the given string.""" + name_contains: String + + """All values not containing the given string.""" + name_not_contains: String + + """All values starting with the given string.""" + name_starts_with: String + + """All values not starting with the given string.""" + name_not_starts_with: String + + """All values ending with the given string.""" + name_ends_with: String + + """All values not ending with the given string.""" + name_not_ends_with: String + slug: String + + """All values that are not equal to given value.""" + slug_not: String + + """All values that are contained in given list.""" + slug_in: [String!] + + """All values that are not contained in given list.""" + slug_not_in: [String!] + + """All values less than the given value.""" + slug_lt: String + + """All values less than or equal the given value.""" + slug_lte: String + + """All values greater than the given value.""" + slug_gt: String + + """All values greater than or equal the given value.""" + slug_gte: String + + """All values containing the given string.""" + slug_contains: String + + """All values not containing the given string.""" + slug_not_contains: String + + """All values starting with the given string.""" + slug_starts_with: String + + """All values not starting with the given string.""" + slug_not_starts_with: String + + """All values ending with the given string.""" + slug_ends_with: String + + """All values not ending with the given string.""" + slug_not_ends_with: String + avatar: String + + """All values that are not equal to given value.""" + avatar_not: String + + """All values that are contained in given list.""" + avatar_in: [String!] + + """All values that are not contained in given list.""" + avatar_not_in: [String!] + + """All values less than the given value.""" + avatar_lt: String + + """All values less than or equal the given value.""" + avatar_lte: String + + """All values greater than the given value.""" + avatar_gt: String + + """All values greater than or equal the given value.""" + avatar_gte: String + + """All values containing the given string.""" + avatar_contains: String + + """All values not containing the given string.""" + avatar_not_contains: String + + """All values starting with the given string.""" + avatar_starts_with: String + + """All values not starting with the given string.""" + avatar_not_starts_with: String + + """All values ending with the given string.""" + avatar_ends_with: String + + """All values not ending with the given string.""" + avatar_not_ends_with: String updatedAt: DateTime """All values that are not equal to given value.""" diff --git a/api/src/graphql/TeamsConnectionsFragment.js b/api/src/graphql/TeamsConnectionsFragment.js index 931d3ac4a..5193475e0 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 @@ -33,6 +34,18 @@ const TEAMS_CONNECTIONS_FRAGMENT = gql`{ createdAt } } + fb { + id + name + slug + avatar + kind + status { + status + reason + createdAt + } + } } } }` diff --git a/api/src/graphql/TeamsFragment.js b/api/src/graphql/TeamsFragment.js index 71069f501..c440139bd 100644 --- a/api/src/graphql/TeamsFragment.js +++ b/api/src/graphql/TeamsFragment.js @@ -31,6 +31,18 @@ const TEAMS_FRAGMENT = gql`{ createdAt } } + fb { + id + name + slug + avatar + kind + status { + status + reason + createdAt + } + } }` module.exports = TEAMS_FRAGMENT diff --git a/api/src/index.js b/api/src/index.js index 13f354638..b728d30d9 100644 --- a/api/src/index.js +++ b/api/src/index.js @@ -8,34 +8,20 @@ const { const { makeExecutableSchema } = require('graphql-tools') const { importSchema } = require('graphql-import') const parser = require('fast-xml-parser') +var axios = require('axios') const checkJwt = require('./auth/middleware/jwt') const { getMember } = require('./auth/middleware/getMember') const { validateIdToken } = require('./auth/validateIdToken') const { directiveResolvers } = require('./auth/authDirectives') -const { sendTeamMemberInvite, verifyInviteToken } = require('./email/sendgrid') - -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 { 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 { sendTeamMemberInvite, sendTeamCreateConfirmation, verifyInviteToken } = require('./email/sendgrid') const TEAMS_FRAGMENT = require('./graphql/TeamsFragment') const TEAMS_CONNECTIONS_FRAGMENT = require('./graphql/TeamsConnectionsFragment') @@ -93,9 +79,8 @@ const resolvers = { async teamWithRole(parent, { teamId, role }, ctx, info) { return ctx.db.query.teamsConnection({where: {AND: [{id: teamId},{members_some:{role: role}}]}, first:1}, info) }, - async teamsByOwner(parent, args, ctx, info) { - const authId = ctx.request.user.sub - return ctx.db.query.teams({where: { owner: authId }, orderBy:'updatedAt_DESC'}, TEAMS_FRAGMENT) + async teamsByOwner(parent, { ownerId }, ctx, info) { + return ctx.db.query.teams({where: { owner: ownerId }, orderBy:'updatedAt_DESC'}, TEAMS_FRAGMENT) }, async teamsByRole(parent, args, ctx, info) { const authId = ctx.request.user.sub @@ -136,6 +121,16 @@ const resolvers = { } return member }, + async sendMemberInviteSG(parent, { email, teamId }, ctx, info) { + const team = await ctx.db.query.team({ where: { id: teamId } }, TEAMS_FRAGMENT) + const addMember = await ctx.db.mutation.upsertTeamMembers({ + where:{email: email}, + create:{role:'MEMBER', email:email, team: {connect: {id: teamId}}, status:{create:{status: 'INVITED', reason: `Invited by ${team.members[0].member.alias}`}}}, + update:{role: 'MEMBER', email: email, team: {connect: {id: teamId}}, status:{create:{status: 'INVITED', reason: `Invite resent by ${team.members[0].member.alias}`}}}}, + `{ id }`) + const sendInvite = await sendTeamMemberInvite(email, team) + return sendInvite + }, async verifyTeamInvite(parent, { token }, ctx, info) { let verifiedToken = null let team = null @@ -154,16 +149,52 @@ const resolvers = { } return team }, - async createTeam(parent, { name, slug }, ctx, info) { + async createTeam(parent, { name, slug, botName, botSlug }, ctx, info) { + console.log('createTeam ctx:', ctx.request.user) const authId = ctx.request.user.sub - return ctx.db.mutation.createTeam({ data: { name: name, slug: slug, owner: authId, members: { create: { member:{ connect:{ authId: authId} }, role: 'OWNER' } }, profile:{create:{avatar:"a"} } } }, TEAMS_FRAGMENT) + const encodedAuthId = encodeURI(authId) + const getUserModuleUser = await axios.get(`https://users-api.advancedalgos.net/graphql?query=%7B%0A%20%20userByAuthId(authId%3A%22${encodedAuthId}%22)%7B%0A%20%20%20%20id%0A%09%09email%0A%20%20%20%20alias%0A%20%20%7D%0A%7D`) + + console.log('getUserModuleUserId: ', await getUserModuleUser.data) + + const alias = await getUserModuleUser.data.data.userByAuthId.alias + const email = await getUserModuleUser.data.data.userByAuthId.email + const avatar = 'https://algobotcommstorage.blob.core.windows.net/aateammodule/aa-avatar-default.png' + const banner = 'https://algobotcommstorage.blob.core.windows.net/aateammodule/aa-banner-default.png' + console.log('getUserModuleUserId: ', alias, email) + /* + const createTeam = await ctx.db.mutation.createTeam({ data: {name: name, slug: slug, owner: authId, members: {create: {member: {connect: {authId: authId}}, role: 'OWNER'}}, profile: {create: {avatar: avatar, banner: banner}}, fb: {create: {name: botName, slug: botSlug, kind:'TRADER', avatar: avatar, status: {create: {status: 'ACTIVE', reason: "Cloned on team creation"}}}}, status: {create: {status: 'ACTIVE', reason:"Team created"}}} }, TEAMS_FRAGMENT) + .catch((err) => { + console.log('createTeam error: ', err) + return err + }) + */ + const createTeamUrl = encodeURI(`${slug}/${name}/${alias}/${botSlug}`) + // const platformUrl = 'https://develop.advancedalgos.net/AABrowserAPI/teamSetup/' + const platformUrl = 'http://localhost:1337/AABrowserAPI/teamSetup/' + const createPlatformTeam = await axios.get(`${platformUrl}${createTeamUrl}`) + console.log('createPlatformTeam: ', await createPlatformTeam) + + + /* update session token on user module + const updateUserSession = await axios.post('https://users-api.advancedalgos.net/graphql', { + query: 'mutation { language }' + }) + */ + + sendTeamCreateConfirmation(email, name, botName, sessionToken) + + return createTeam + }, + async updateTeamProfile(parent, { slug, owner, description, motto, avatar, banner }, ctx, info) { + return ctx.db.mutation.updateTeam({data:{profile: {update: {description: description, motto: motto, avatar: avatar, banner: banner}}}, where:{slug: slug}}, TEAMS_FRAGMENT) .catch((err) => { console.log('createTeam error: ', err) return err }) }, - async updateTeamProfile(parent, { slug, owner, description, motto, avatar, banner }, ctx, info) { - return ctx.db.mutation.updateTeam({data:{profile: {update: {description: description, motto: motto, avatar: avatar, banner: banner}}}, where:{slug: slug}}, TEAMS_FRAGMENT) + async updateFB(parent, { teamId, avatar}, ctx, info) { + return ctx.db.mutation.updateTeam({data:{profile: {update: {avatar: avatar}}}, where:{slug: teamId}}, info) .catch((err) => { console.log('createTeam error: ', err) return err @@ -179,66 +210,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) - - 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) - 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 - }, - async sendMemberInviteSG(parent, { email, teamId }, ctx, info) { - const team = await ctx.db.query.team({ where: { id: teamId } }, TEAMS_FRAGMENT) - const addMember = await ctx.db.mutation.upsertTeamMembers({ - where:{email: email}, - create:{role:'MEMBER', email:email, team: {connect: {id: teamId}}, status:{create:{status: 'INVITED', reason: `Invited by ${team.members[0].member.alias}`}}}, - update:{role: 'MEMBER', email: email, team: {connect: {id: teamId}}, status:{create:{status: 'INVITED', reason: `Invite resent by ${team.members[0].member.alias}`}}}}, - `{ id }`) - const sendInvite = await sendTeamMemberInvite(email, team) - return sendInvite + const SASUrl = createSASQueryURL(teamSlug) + console.log('createSASQueryURL: ', SASUrl) + return SASUrl } } } @@ -283,13 +257,22 @@ 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', + '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' ] + const corsOptionsDelegate = (req, callback) => { var corsOptions if (whitelist.indexOf(req.header('Origin')) !== -1) { diff --git a/api/src/schema.graphql b/api/src/schema.graphql index 0b969a608..d09e62db9 100644 --- a/api/src/schema.graphql +++ b/api/src/schema.graphql @@ -11,7 +11,7 @@ type Query { teamByName(name: String!): Team teamById(teamId: String!): Team teamWithRole(teamId: String!, role: String!): Team - teamsByOwner: [Team!]! @isAuthenticated + teamsByOwner(ownerId: String!): [Team!]! @isAuthenticated teamsByRole: [Team!]! @isAuthenticated owner: Member @isAuthenticated currentMember: Member @isAuthenticated @@ -21,8 +21,9 @@ type Query { type Mutation { authenticate(idToken: String!): Member verifyTeamInvite(token: String!): TeamMembers - createTeam(name: String!, slug: String!): Team @isAuthenticated + createTeam(name: String!, slug: String!, botName: String!, botSlug: String!): Team @isAuthenticated updateTeamProfile(slug: String!, description: String, motto: String, avatar: String, banner: String): Team @isAuthenticated + updateFB(teamId: String!, avatar: String): FinancialBeings @isAuthenticated deleteTeam(slug: String!): Team @isAuthenticated @isOwner(type: "Team") getAzureSAS(teamSlug: String!):String sendMemberInviteSG(email: String!, teamId: String!): String diff --git a/api/src/storage/azure.js b/api/src/storage/azure.js index 324181b04..b718d6ef5 100644 --- a/api/src/storage/azure.js +++ b/api/src/storage/azure.js @@ -1,10 +1,13 @@ -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 * @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) ) @@ -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/.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/package-lock.json b/client/package-lock.json index dbfc71ab6..fb97dc7c1 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.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", + "@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.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", + "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", @@ -7646,6 +7743,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", @@ -13625,6 +13727,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", @@ -14259,11 +14367,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 87896b030..cc6d7d10f 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.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/graphql/teams/CreateTeamMutation.js b/client/src/graphql/teams/CreateTeamMutation.js index 1caa8d47c..da002d67a 100644 --- a/client/src/graphql/teams/CreateTeamMutation.js +++ b/client/src/graphql/teams/CreateTeamMutation.js @@ -2,8 +2,8 @@ import gql from 'graphql-tag' const CREATE_TEAM = gql` - mutation CreateTeamMutation($name: String!, $slug: String!) { - createTeam(name: $name, slug: $slug) { + mutation CreateTeamMutation($name: String!, $slug: String!, $botName: String!, $botSlug: String!) { + createTeam(name: $name, slug: $slug, botName: $botName, botSlug: $botSlug) { id name slug diff --git a/client/src/graphql/teams/GetAllTeamsQuery.js b/client/src/graphql/teams/GetAllTeamsQuery.js index aa484f1f8..859e6c20b 100644 --- a/client/src/graphql/teams/GetAllTeamsQuery.js +++ b/client/src/graphql/teams/GetAllTeamsQuery.js @@ -18,6 +18,7 @@ export const GET_ALL_TEAMS_QUERY = gql` createdAt profile { avatar + banner description motto updatedAt @@ -36,6 +37,18 @@ export const GET_ALL_TEAMS_QUERY = gql` createdAt } } + fb { + id + name + slug + avatar + kind + status { + status + reason + createdAt + } + } } } } diff --git a/client/src/graphql/teams/GetTeamsByOwnerQuery.js b/client/src/graphql/teams/GetTeamsByOwnerQuery.js index c38c6ebff..f7f65cf42 100644 --- a/client/src/graphql/teams/GetTeamsByOwnerQuery.js +++ b/client/src/graphql/teams/GetTeamsByOwnerQuery.js @@ -2,8 +2,8 @@ import gql from 'graphql-tag' export const GET_TEAMS_BY_OWNER = gql` - query teamsByOwnerQuery { - teamsByOwner { + query teamsByOwnerQuery($authId: String!) { + teamsByOwner(ownerId: $authId) { id name slug @@ -16,6 +16,7 @@ export const GET_TEAMS_BY_OWNER = gql` createdAt profile { avatar + banner description motto updatedAt @@ -34,6 +35,18 @@ export const GET_TEAMS_BY_OWNER = gql` createdAt } } + fb { + id + name + slug + avatar + kind + status { + status + reason + createdAt + } + } } } ` diff --git a/client/src/graphql/teams/Teams.js b/client/src/graphql/teams/Teams.js index 4a0c84d37..82bf7bfa7 100644 --- a/client/src/graphql/teams/Teams.js +++ b/client/src/graphql/teams/Teams.js @@ -15,6 +15,7 @@ export const TEAMS_FRAGMENT = gql` createdAt profile { avatar + banner description motto updatedAt @@ -33,6 +34,18 @@ export const TEAMS_FRAGMENT = gql` createdAt } } + fb { + id + name + slug + avatar + kind + status { + status + reason + createdAt + } + } } ` diff --git a/client/src/graphql/teams/UpdateFBMutation.js b/client/src/graphql/teams/UpdateFBMutation.js new file mode 100644 index 000000000..3103dd7bf --- /dev/null +++ b/client/src/graphql/teams/UpdateFBMutation.js @@ -0,0 +1,42 @@ +/* eslint-disable no-unused-vars */ +import gql from 'graphql-tag' + +const UPDATE_FB = gql` + mutation UpdateFBMutation($teamSlug: String!, $avatar: String) { + updateFB(teamId, $teamSlug, avatar: $avatar) { + id + name + slug + owner + status { + status + reason + createdAt + } + createdAt + profile { + avatar + banner + description + motto + updatedAt + } + members { + role + email + member { + alias + authId + } + status { + id + status + reason + createdAt + } + } + } + } +` + +export default UPDATE_FB diff --git a/client/src/graphql/teams/UpdateTeamProfileMutation.js b/client/src/graphql/teams/UpdateTeamProfileMutation.js index 39feabf8d..05a4ddb30 100644 --- a/client/src/graphql/teams/UpdateTeamProfileMutation.js +++ b/client/src/graphql/teams/UpdateTeamProfileMutation.js @@ -28,6 +28,7 @@ const UPDATE_TEAM_PROFILE = gql` createdAt profile { avatar + banner description motto updatedAt diff --git a/client/src/views/callback/index.js b/client/src/views/callback/index.js index c4b615186..2dbf4c27f 100644 --- a/client/src/views/callback/index.js +++ b/client/src/views/callback/index.js @@ -1,5 +1,7 @@ import React from 'react' -const Callback = () =>
Authenticating...
+import { MessageCard } from '@advancedalgos/web-components' + +const Callback = () => export default Callback 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 a386f1022..000000000 --- a/client/src/views/common/UploadImage.js +++ /dev/null @@ -1,344 +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, 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, 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, - 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/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/components/CreateTeamDialog.js b/client/src/views/dashboard/components/CreateTeamDialog.js index 9b2117c04..75f9a0cb9 100644 --- a/client/src/views/dashboard/components/CreateTeamDialog.js +++ b/client/src/views/dashboard/components/CreateTeamDialog.js @@ -21,9 +21,19 @@ import GET_TEAMS_BY_OWNER from '../../../graphql/teams/GetTeamsByOwnerQuery' // import { checkGraphQLError } from '../../../utils/graphql-errors' const styles = theme => ({ + dialogContainer: { + display: 'block', + margin: '3em', + minWidth: 400 + }, dialogStyle: { padding: '3em' }, + textField: { + width: '60%', + marginLeft: '20%', + marginBottom: 10 + }, buttonRight: { // position: 'absolute', // right: '3em' @@ -49,9 +59,9 @@ export class CreateTeamDialog extends Component { this.state = { open: false, name: '', - errors: { - name: '' - } + botName: '', + nameError: '', + botNameError: '' } } @@ -96,43 +106,69 @@ export class CreateTeamDialog extends Component { onClose={this.handleClose} aria-labelledby='form-dialog-title' > - Create a Team - - - - {this.state.errors.name !== '' && ( - {this.state.errors.name} - )} - {error && {errors}} - {loader} - - - - - - +
+ Create a Team + + + Create a name for your team + + {this.state.nameError !== '' && ( + {this.state.nameError} + )} +
+ Create a name for your teams bot. +
+ + (Temporary: In the future, creating multiple Financial Beings
+ will occur in a separate module)
+
+ + {this.state.botNameError !== '' && ( + {this.state.botNameError} + )} + {error && {errors}} + {loader} +
+
+ + + + +
) @@ -150,27 +186,50 @@ export class CreateTeamDialog extends Component { } handleChange (e) { - const errors = this.validate(e.target.value) - if (isEmpty(errors)) { - this.setState({ errors: { name: '' } }) - this.setState({ name: e.target.value }) - } else { - this.setState({ errors: errors }) - this.setState({ name: e.target.value }) + let id = e.target.id + let value = e.target.value + let error = this.validate({ id, value }) + switch (id) { + case 'name': + if (!isEmpty(error)) this.setState({ nameError: error }) + this.setState({ name: value }) + break + case 'botName': + if (!isEmpty(error)) this.setState({ botNameError: error }) + this.setState({ botName: value }) + break + default: } } - async handleSubmit (e, createTeam, name, authId) { + async handleSubmit (e, createTeam, name, botName) { e.preventDefault() - console.log('createTeam submit: ', authId, name) + console.log('createTeam submit: ', name, botName) const slug = this.slugify(name) - await createTeam({ variables: { name, slug } }) + const botSlug = this.slugify(name) + await createTeam({ variables: { name, slug, botName, botSlug } }) this.setState({ name: '', open: false }) } validate (data) { const errors = {} - if (data === '') errors.name = 'Please enter a team name' + switch (data.id) { + case 'name': + if (data.value === '') { + this.setState({ nameError: 'Please enter a team name' }) + } else { + this.setState({ nameError: '' }) + } + break + case 'botName': + if (data.value === '') { + this.setState({ botNameError: 'Please enter a bot name' }) + } else { + this.setState({ botNameError: '' }) + } + break + default: + } return errors } diff --git a/client/src/views/dashboard/components/CreateTeamForm.js b/client/src/views/dashboard/components/CreateTeamForm.js index 98e3aebc6..6a0b90297 100644 --- a/client/src/views/dashboard/components/CreateTeamForm.js +++ b/client/src/views/dashboard/components/CreateTeamForm.js @@ -11,8 +11,6 @@ import DialogContentText from '@material-ui/core/DialogContentText' import DialogTitle from '@material-ui/core/DialogTitle' import Typography from '@material-ui/core/Typography' -import { getItem } from '../../../utils/local-storage' - export class CreateTeamDialog extends Component { constructor (props) { super(props) @@ -25,7 +23,8 @@ export class CreateTeamDialog extends Component { this.state = { open: false, - name: '' + name: '', + botName: '' } } @@ -38,7 +37,16 @@ export class CreateTeamDialog extends Component { } handleChange (e) { - this.setState({ name: e.target.value }) + let value = e.target.value + switch (e.target.id) { + case 'name': + this.setState({ name: value }) + break + case 'botName': + this.setState({ botName: value }) + break + default: + } } render () { @@ -73,6 +81,17 @@ export class CreateTeamDialog extends Component { value={this.state.name} onChange={this.handleChange} /> + Create a name for you teams bot. (Temporary: In the future, creating multiple Financial Beings will occur in a separate module) + - - - - -) +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, 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'