Merge branch 'master' into feature/member-mangement

This commit is contained in:
Barry Low committed 2018-10-06 00:39:15 -07:00
commit 4fe572411e
44 files changed
+1307 -919

No files matched your search

+8 -8
View File
@@ -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()
}
+66 -1
View File
@@ -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 }
+145 -1
View File
@@ -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."""
@@ -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
}
}
}
}
}`
+12
View File
@@ -31,6 +31,18 @@ const TEAMS_FRAGMENT = gql`{
createdAt
}
}
fb {
id
name
slug
avatar
kind
status {
status
reason
createdAt
}
}
}`
module.exports = TEAMS_FRAGMENT
+69 -86
View File
@@ -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) {
+3 -2
View File
@@ -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
+61 -3
View File
@@ -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
}