From fecbac56809e75f5a9c519b55f53c69ddbb44d4a Mon Sep 17 00:00:00 2001 From: Barry Low Date: Thu, 27 Sep 2018 08:25:32 -0700 Subject: [PATCH 01/11] Client: Add Member Management components --- api/database/datamodel.graphql | 2 +- api/src/generated/prisma.graphql | 71 +++---- client/src/views/dashboard/TeamMembers.js | 6 +- .../components/InviteMemberDialog.js | 178 ++++++++++++++++++ .../components/ManageMemberDelete.js | 156 +++++++++++++++ .../dashboard/components/ManageMemberRole.js | 153 +++++++++++++++ .../dashboard/components/ManageMembersItem.js | 64 +++++++ .../components/ManageMembersTeamItem.js | 119 ++++++++++++ .../components/ManageTeamMembersList.js | 142 ++++++++++++++ 9 files changed, 844 insertions(+), 47 deletions(-) create mode 100644 client/src/views/dashboard/components/InviteMemberDialog.js create mode 100644 client/src/views/dashboard/components/ManageMemberDelete.js create mode 100644 client/src/views/dashboard/components/ManageMemberRole.js create mode 100644 client/src/views/dashboard/components/ManageMembersItem.js create mode 100644 client/src/views/dashboard/components/ManageMembersTeamItem.js create mode 100644 client/src/views/dashboard/components/ManageTeamMembersList.js diff --git a/api/database/datamodel.graphql b/api/database/datamodel.graphql index b5627825e..fec8b877e 100644 --- a/api/database/datamodel.graphql +++ b/api/database/datamodel.graphql @@ -63,7 +63,7 @@ type TeamMembers { type Member { id: ID! @unique alias: String - team: TeamMembers @relation (name: "MemberOnTeam", onDelete: SET_NULL) + teams: [TeamMembers!]! @relation (name: "MemberOnTeam", onDelete: SET_NULL) authId: String! @unique visible: Boolean! @default(value: true) createdAt: DateTime! diff --git a/api/src/generated/prisma.graphql b/api/src/generated/prisma.graphql index 99d9f7562..b70971c89 100644 --- a/api/src/generated/prisma.graphql +++ b/api/src/generated/prisma.graphql @@ -1,5 +1,5 @@ # source: http://localhost:4466 -# timestamp: Thu Sep 20 2018 16:52:44 GMT-0700 (Pacific Daylight Time) +# timestamp: Wed Sep 26 2018 23:15:45 GMT-0700 (Pacific Daylight Time) type AggregateFBStatus { count: Int! @@ -513,7 +513,7 @@ scalar Long type Member implements Node { id: ID! alias: String - team(where: TeamMembersWhereInput): TeamMembers + teams(where: TeamMembersWhereInput, orderBy: TeamMembersOrderByInput, skip: Int, after: String, before: String, first: Int, last: Int): [TeamMembers!] authId: String! visible: Boolean! createdAt: DateTime! @@ -533,15 +533,15 @@ input MemberCreateInput { alias: String authId: String! visible: Boolean - team: TeamMembersCreateOneWithoutMemberInput + teams: TeamMembersCreateManyWithoutMemberInput } -input MemberCreateOneWithoutTeamInput { - create: MemberCreateWithoutTeamInput +input MemberCreateOneWithoutTeamsInput { + create: MemberCreateWithoutTeamsInput connect: MemberWhereUniqueInput } -input MemberCreateWithoutTeamInput { +input MemberCreateWithoutTeamsInput { alias: String authId: String! visible: Boolean @@ -806,27 +806,27 @@ input MemberUpdateInput { alias: String authId: String visible: Boolean - team: TeamMembersUpdateOneWithoutMemberInput + teams: TeamMembersUpdateManyWithoutMemberInput } -input MemberUpdateOneWithoutTeamInput { - create: MemberCreateWithoutTeamInput +input MemberUpdateOneWithoutTeamsInput { + create: MemberCreateWithoutTeamsInput connect: MemberWhereUniqueInput disconnect: Boolean delete: Boolean - update: MemberUpdateWithoutTeamDataInput - upsert: MemberUpsertWithoutTeamInput + update: MemberUpdateWithoutTeamsDataInput + upsert: MemberUpsertWithoutTeamsInput } -input MemberUpdateWithoutTeamDataInput { +input MemberUpdateWithoutTeamsDataInput { alias: String authId: String visible: Boolean } -input MemberUpsertWithoutTeamInput { - update: MemberUpdateWithoutTeamDataInput! - create: MemberCreateWithoutTeamInput! +input MemberUpsertWithoutTeamsInput { + update: MemberUpdateWithoutTeamsDataInput! + create: MemberCreateWithoutTeamsInput! } input MemberWhereInput { @@ -984,7 +984,9 @@ input MemberWhereInput { """All values greater than or equal the given value.""" createdAt_gte: DateTime - team: TeamMembersWhereInput + teams_every: TeamMembersWhereInput + teams_some: TeamMembersWhereInput + teams_none: TeamMembersWhereInput } input MemberWhereUniqueInput { @@ -1206,17 +1208,17 @@ type TeamMembersConnection { input TeamMembersCreateInput { role: Role - member: MemberCreateOneWithoutTeamInput + member: MemberCreateOneWithoutTeamsInput team: TeamCreateOneWithoutMembersInput status: MemberStatusCreateManyInput } -input TeamMembersCreateManyWithoutTeamInput { - create: [TeamMembersCreateWithoutTeamInput!] +input TeamMembersCreateManyWithoutMemberInput { + create: [TeamMembersCreateWithoutMemberInput!] } -input TeamMembersCreateOneWithoutMemberInput { - create: TeamMembersCreateWithoutMemberInput +input TeamMembersCreateManyWithoutTeamInput { + create: [TeamMembersCreateWithoutTeamInput!] } input TeamMembersCreateWithoutMemberInput { @@ -1227,7 +1229,7 @@ input TeamMembersCreateWithoutMemberInput { input TeamMembersCreateWithoutTeamInput { role: Role - member: MemberCreateOneWithoutTeamInput + member: MemberCreateOneWithoutTeamsInput status: MemberStatusCreateManyInput } @@ -1297,34 +1299,19 @@ input TeamMembersSubscriptionWhereInput { input TeamMembersUpdateInput { role: Role - member: MemberUpdateOneWithoutTeamInput + member: MemberUpdateOneWithoutTeamsInput team: TeamUpdateOneWithoutMembersInput status: MemberStatusUpdateManyInput } +input TeamMembersUpdateManyWithoutMemberInput { + create: [TeamMembersCreateWithoutMemberInput!] +} + input TeamMembersUpdateManyWithoutTeamInput { create: [TeamMembersCreateWithoutTeamInput!] } -input TeamMembersUpdateOneWithoutMemberInput { - create: TeamMembersCreateWithoutMemberInput - disconnect: Boolean - delete: Boolean - update: TeamMembersUpdateWithoutMemberDataInput - upsert: TeamMembersUpsertWithoutMemberInput -} - -input TeamMembersUpdateWithoutMemberDataInput { - role: Role - team: TeamUpdateOneWithoutMembersInput - status: MemberStatusUpdateManyInput -} - -input TeamMembersUpsertWithoutMemberInput { - update: TeamMembersUpdateWithoutMemberDataInput! - create: TeamMembersCreateWithoutMemberInput! -} - input TeamMembersWhereInput { """Logical AND on all given filters.""" AND: [TeamMembersWhereInput!] diff --git a/client/src/views/dashboard/TeamMembers.js b/client/src/views/dashboard/TeamMembers.js index 8d86d4155..eca8f461e 100644 --- a/client/src/views/dashboard/TeamMembers.js +++ b/client/src/views/dashboard/TeamMembers.js @@ -4,7 +4,7 @@ import PropTypes from 'prop-types' import Typography from '@material-ui/core/Typography' import { withStyles } from '@material-ui/core/styles' -import { MessageCard } from '../common/' +import ManageTeamMembersList from './components/ManageTeamMembersList' const styles = theme => ({ tableContainer: { @@ -17,9 +17,7 @@ const TeamMembers = ({ classes }) => ( Team Members -
- -
+ ) diff --git a/client/src/views/dashboard/components/InviteMemberDialog.js b/client/src/views/dashboard/components/InviteMemberDialog.js new file mode 100644 index 000000000..601c45318 --- /dev/null +++ b/client/src/views/dashboard/components/InviteMemberDialog.js @@ -0,0 +1,178 @@ +import React, { Component } from 'react' +import PropTypes from 'prop-types' +import { Mutation } from 'react-apollo' +import { withStyles } from '@material-ui/core/styles' + +import Button from '@material-ui/core/Button' +import AddIcon from '@material-ui/icons/Add' +import TextField from '@material-ui/core/TextField' +import FormControl from '@material-ui/core/FormControl' +import FormHelperText from '@material-ui/core/FormHelperText' +import Dialog from '@material-ui/core/Dialog' +import DialogActions from '@material-ui/core/DialogActions' +import DialogContent from '@material-ui/core/DialogContent' +import DialogTitle from '@material-ui/core/DialogTitle' +import Typography from '@material-ui/core/Typography' +import { isEmpty } from '../../../utils/js-helpers' + +import CREATE_TEAM from '../../../graphql/teams/CreateTeamMutation' + +// import { checkGraphQLError } from '../../../utils/graphql-errors' + +const styles = theme => ({ + dialogStyle: { + padding: '3em' + }, + buttonRight: { + // position: 'absolute', + // right: '3em' + marginTop: '1em', + marginRight: 'auto', + marginLeft: 'auto', + display: 'block', + lineHeight: '1em' + } +}) + +export class InviteMemberDialog extends Component { + constructor (props) { + super(props) + + this.handleClickOpen = this.handleClickOpen.bind(this) + this.handleClose = this.handleClose.bind(this) + this.handleChange = this.handleChange.bind(this) + this.handleSubmit = this.handleSubmit.bind(this) + this.validate = this.validate.bind(this) + + this.state = { + open: false, + status: null, + email: '', + errors: { + email: '' + } + } + } + + render () { + const { classes, teamId } = this.props + return ( + + {(createTeam, { loading, error, data }) => { + let errors + let loader = null + if (loading) { + loader = ( + Submitting invitation... + ) + } + if (error) { + errors = error.graphQLErrors.map(({ message }, i) => { + // const displayMessage = checkGraphQLError(message) + return ( + + {message} + + ) + }) + } + return ( +
+ + + Invite a New Team Member + + + + {this.state.errors.email !== '' && ( + {this.state.errors.email} + )} + {error && {errors}} + {loader} + + + + + + + +
+ ) + }} +
+ ) + } + + handleClickOpen () { + this.setState({ open: true }) + } + + handleClose () { + this.setState({ open: false }) + } + + handleChange (e) { + const errors = this.validate(e.target.value) + if (isEmpty(errors)) { + this.setState({ errors: { name: '' } }) + this.setState({ email: e.target.value }) + } else { + this.setState({ errors: errors }) + this.setState({ email: e.target.value }) + } + } + + async handleSubmit (e, createTeam, email, teamId) { + e.preventDefault() + console.log('createTeam submit: ', email, teamId) + await createTeam({ variables: { email, teamId } }) + this.setState({ email: '', status: 'sending' }) + } + + validate (data) { + const errors = {} + if (data === '') errors.name = 'Please enter an email address' + return errors + } +} + +InviteMemberDialog.propTypes = { + classes: PropTypes.object.isRequired, + teamId: PropTypes.string +} + +export default withStyles(styles)(InviteMemberDialog) diff --git a/client/src/views/dashboard/components/ManageMemberDelete.js b/client/src/views/dashboard/components/ManageMemberDelete.js new file mode 100644 index 000000000..4419aa55f --- /dev/null +++ b/client/src/views/dashboard/components/ManageMemberDelete.js @@ -0,0 +1,156 @@ +import React, { Component } from 'react' +import PropTypes from 'prop-types' +import { withStyles } from '@material-ui/core/styles' +import { Mutation } from 'react-apollo' + +import Button from '@material-ui/core/Button' +import PersonAddDisabledIcon from '@material-ui/icons/PersonAddDisabled' +import Dialog from '@material-ui/core/Dialog' +import DialogActions from '@material-ui/core/DialogActions' +import DialogContent from '@material-ui/core/DialogContent' +import DialogTitle from '@material-ui/core/DialogTitle' +import Typography from '@material-ui/core/Typography' + +import DELETE_TEAM from '../../../graphql/teams/DeleteTeamMutation' +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 + }, + buttonRight: { + justifyContent: 'flex-end' + } +}) + +export class ManageMemberDelete extends Component { + constructor (props) { + super(props) + + this.handleClickOpen = this.handleClickOpen.bind(this) + this.handleClose = this.handleClose.bind(this) + this.handleChange = this.handleChange.bind(this) + this.handleSubmit = this.handleSubmit.bind(this) + + this.state = { + open: false + } + } + + handleClickOpen () { + this.setState({ open: true }) + } + + handleClose () { + this.setState({ open: false }) + } + + handleChange (e) { + this.setState({ name: e.target.value }) + } + + render () { + console.log(this.props, this.props.teamId) + const { classes, teamId, authId } = this.props + return ( + + {(deleteTeam, { loading, error, data }) => { + let errors + let loader + if (loading) { + loader = ( + Submitting team... + ) + } + if (error) { + errors = error.graphQLErrors.map(({ message }, i) => { + const displayMessage = checkGraphQLError(message) + console.log('createTeam error:', displayMessage) + return ( + + {message} + + ) + }) + } + return ( +
+ + +
+ + Remove Team Member + + + + DANGER - Removing your team cannot be undone + + + Are you sure you want to remove this team member? + + {loader} + {errors} + + + + + +
+
+
+ ) + }} +
+ ) + } + + async handleSubmit (e, deleteTeam, teamId) { + e.preventDefault() + await deleteTeam({ variables: { teamId } }) + this.setState({ open: false }) + } +} + +ManageMemberDelete.propTypes = { + classes: PropTypes.object.isRequired, + teamId: PropTypes.string.isRequired, + authId: PropTypes.string.isRequired +} + +export default withStyles(styles)(ManageMemberDelete) diff --git a/client/src/views/dashboard/components/ManageMemberRole.js b/client/src/views/dashboard/components/ManageMemberRole.js new file mode 100644 index 000000000..2e19bf48c --- /dev/null +++ b/client/src/views/dashboard/components/ManageMemberRole.js @@ -0,0 +1,153 @@ +import React, { Component } from 'react' +import PropTypes from 'prop-types' +import { withStyles } from '@material-ui/core/styles' +import { Mutation } from 'react-apollo' + +import Button from '@material-ui/core/Button' +import AssignmentIndIcon from '@material-ui/icons/AssignmentInd' +import Dialog from '@material-ui/core/Dialog' +import DialogActions from '@material-ui/core/DialogActions' +import DialogContent from '@material-ui/core/DialogContent' +import DialogTitle from '@material-ui/core/DialogTitle' +import Typography from '@material-ui/core/Typography' + +import DELETE_TEAM from '../../../graphql/teams/DeleteTeamMutation' +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 + }, + buttonRight: { + justifyContent: 'flex-end' + } +}) + +export class ManageMemberRole extends Component { + constructor (props) { + super(props) + + this.handleClickOpen = this.handleClickOpen.bind(this) + this.handleClose = this.handleClose.bind(this) + this.handleChange = this.handleChange.bind(this) + this.handleSubmit = this.handleSubmit.bind(this) + + this.state = { + open: false + } + } + + handleClickOpen () { + this.setState({ open: true }) + } + + handleClose () { + this.setState({ open: false }) + } + + handleChange (e) { + this.setState({ name: e.target.value }) + } + + render () { + console.log(this.props, this.props.teamId) + const { classes, teamId, authId } = this.props + return ( + + {(deleteTeam, { loading, error, data }) => { + let errors + let loader + if (loading) { + loader = ( + Submitting team... + ) + } + if (error) { + errors = error.graphQLErrors.map(({ message }, i) => { + const displayMessage = checkGraphQLError(message) + console.log('createTeam error:', displayMessage) + return ( + + {message} + + ) + }) + } + return ( +
+ + +
+ + Set Team Member Role + + + + Admin members can approve new team members, send team invitations, and remove team members. + + {loader} + {errors} + + + + + +
+
+
+ ) + }} +
+ ) + } + + async handleSubmit (e, deleteTeam, teamId) { + e.preventDefault() + await deleteTeam({ variables: { teamId } }) + this.setState({ open: false }) + } +} + +ManageMemberRole.propTypes = { + classes: PropTypes.object.isRequired, + teamId: PropTypes.string.isRequired, + authId: PropTypes.string.isRequired +} + +export default withStyles(styles)(ManageMemberRole) diff --git a/client/src/views/dashboard/components/ManageMembersItem.js b/client/src/views/dashboard/components/ManageMembersItem.js new file mode 100644 index 000000000..4d5ea5597 --- /dev/null +++ b/client/src/views/dashboard/components/ManageMembersItem.js @@ -0,0 +1,64 @@ +import React from 'react' +import PropTypes from 'prop-types' + +import Grid from '@material-ui/core/Grid' +import CardMedia from '@material-ui/core/CardMedia' +import Typography from '@material-ui/core/Typography' + +import ManageMemberDelete from './ManageMemberDelete' +import ManageMemberRole from './ManageMemberRole' +import { MessageCard } from '../../common/' + +export const ManageMembersItem = ({ classes, member, teamId, authId }) => { + console.log('ManageMembersItem', member) + let profile = null + if (member.member !== null) { + profile = member.member + } + let avatar = null + if (member.profile !== undefined && member.profile !== null && member.profile.avatar !== undefined && member.profile.avatar !== 'a') { + avatar = member.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' + } + if (profile !== null) { + return ( + + + + + + + {profile.alias} + + + + + {member.role} | Member since: + + + + + + + + ) + } else { + return ( + + ) + } +} + +ManageMembersItem.propTypes = { + classes: PropTypes.object.isRequired, + member: PropTypes.object.isRequired, + teamId: PropTypes.string.isRequired, + authId: PropTypes.string.isRequired +} + +export default ManageMembersItem diff --git a/client/src/views/dashboard/components/ManageMembersTeamItem.js b/client/src/views/dashboard/components/ManageMembersTeamItem.js new file mode 100644 index 000000000..46da40341 --- /dev/null +++ b/client/src/views/dashboard/components/ManageMembersTeamItem.js @@ -0,0 +1,119 @@ +import React from 'react' +import PropTypes from 'prop-types' +import { withStyles } from '@material-ui/core/styles' + +import Grid from '@material-ui/core/Grid' +import Card from '@material-ui/core/Card' +import CardContent from '@material-ui/core/CardContent' +import CardMedia from '@material-ui/core/CardMedia' +import Typography from '@material-ui/core/Typography' + +import InviteMemberDialog from './InviteMemberDialog' +import ManageMembersItem from './ManageMembersItem' +import { MessageCard } from '../../common/' + +const styles = theme => ({ + heroContent: { + maxWidth: 600, + margin: '0 auto', + padding: `${theme.spacing.unit * 8}px 0 ${theme.spacing.unit * 6}px` + }, + cardGrid: { + padding: `${theme.spacing.unit * 8}px 0` + }, + buttonRight: { + justifyContent: 'flex-end' + }, + card: { + display: 'flex', + width: '100%' + }, + cardDetails: { + display: 'flex', + flex: 1 + }, + cardContent: { + flex: 1 + }, + cardMedia: { + flex: 1, + width: 100, + height: 100, + maxWidth: 100, + justifyContent: 'flex-start' + }, + cardMemberMedia: { + flex: 1, + width: 36, + height: 36, + maxWidth: 36, + justifyContent: 'flex-start' + }, + memberContainer: { + margin: 40, + padding: 12 + } +}) + +export const ManageMembersTeamItem = ({ classes, team, authId }) => { + console.log('ManageMembersItem', team, team.profile, team.profile.avatar) + 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' + } + if (team.members.length > 0) { + return ( + + { team.members.map(member => { + console.log('ManageMembersTeamItem: ', member) + return ( + + +
+ + + + + + + + {team.name} + + + + + + + + +
+
+
+ ) + }) + } +
+ ) + } else { + return + } +} + +ManageMembersTeamItem.propTypes = { + classes: PropTypes.object.isRequired, + team: PropTypes.object.isRequired, + authId: PropTypes.string.isRequired +} + +export default withStyles(styles)(ManageMembersTeamItem) diff --git a/client/src/views/dashboard/components/ManageTeamMembersList.js b/client/src/views/dashboard/components/ManageTeamMembersList.js new file mode 100644 index 000000000..6bf139b0d --- /dev/null +++ b/client/src/views/dashboard/components/ManageTeamMembersList.js @@ -0,0 +1,142 @@ +import React from 'react' +import PropTypes from 'prop-types' +import { Query } from 'react-apollo' +import { withStateHandlers, lifecycle, compose } from 'recompose' + +import Grid from '@material-ui/core/Grid' +import { withStyles } from '@material-ui/core/styles' + +import { isDefined, isString } from '../../../utils/js-helpers' +import { getItem } from '../../../utils/local-storage' + +import GET_TEAMS_BY_OWNER from '../../../graphql/teams/GetTeamsByOwnerQuery' + +import ManageMembersTeamItem from './ManageMembersTeamItem' +import { MessageCard } from '../../common/' + +const styles = theme => ({ + heroContent: { + maxWidth: 600, + margin: '0 auto', + padding: `${theme.spacing.unit * 8}px 0 ${theme.spacing.unit * 6}px` + }, + cardGrid: { + padding: `${theme.spacing.unit * 8}px 0` + }, + buttonRight: { + justifyContent: 'flex-end' + }, + card: { + display: 'flex', + width: '100%' + }, + cardDetails: { + display: 'flex', + flex: 1 + }, + cardContent: { + flex: 1 + }, + cardMedia: { + flex: 1, + width: 100, + height: 100, + maxWidth: 100, + justifyContent: 'flex-start' + } +}) + +export const ManageTeamMembersList = ({ classes, user = null }) => { + let owner + let authId = null + console.log('ManageTeamsList: ', user) + if (user !== null && isString(user)) { + owner = JSON.parse(user) + if (isDefined(owner.authId)) authId = owner.authId + } + console.log('ManageTeamsList 2: ', owner, authId) + if (authId === undefined || authId === null) { + return ( + + + + + + ) + } else { + return ( + + {({ loading, error, data }) => { + console.log('GET_TEAMS_BY_OWNER: ', loading, error, data) + let errors = null + if (error) { + errors = error.graphQLErrors.map(({ message }, i) => { + return + }) + } + if (!loading && !error) { + if (data.teamsByOwner.length > 0) { + return ( + + + {!loading && + data.teamsByOwner.map(team => ( + + )) + } + {errors} + + + ) + } else { + return ( + + + + + + ) + } + } else { + return ( + + + + {errors !== null && } + + + ) + } + }} + + ) + } +} + +ManageTeamMembersList.propTypes = { + user: PropTypes.any, + classes: PropTypes.object.isRequired +} + +const getUserOnMount = lifecycle({ + componentDidMount () { + getItem('user').then(user => { + this.setState({ user }) + }) // Set user to state + } +}) + +const mapStateToProps = withStateHandlers(() => ({ user: null }), { + user: ({ user }) => () => ({ user }) +}) + +const ManageTeamMembersListAuthId = compose( + mapStateToProps, + getUserOnMount +)(ManageTeamMembersList) + +export default withStyles(styles)(ManageTeamMembersListAuthId) From f97deb79464a0d148c9912a7608dbb00ebf6d01d Mon Sep 17 00:00:00 2001 From: Barry Low Date: Thu, 27 Sep 2018 20:14:45 -0700 Subject: [PATCH 02/11] Remove sending authId from Client --- api/database/datamodel.graphql | 19 +- api/package-lock.json | 1429 ++++++++++++++++- api/package.json | 3 +- api/src/generated/prisma.graphql | 429 ++++- api/src/index.js | 20 +- api/src/schema.graphql | 11 +- client/src/App.js | 2 +- .../src/graphql/teams/CreateTeamMutation.js | 4 +- .../src/graphql/teams/GetTeamsByOwnerQuery.js | 4 +- .../teams/UpdateTeamProfileMutation.js | 2 - client/src/views/common/UploadImage.js | 5 +- .../dashboard/components/CreateTeamDialog.js | 16 +- .../views/dashboard/components/DashTeam.js | 53 +- .../dashboard/components/DashTeamItem.js | 59 +- .../dashboard/components/ManageTeamDelete.js | 15 +- .../dashboard/components/ManageTeamEdit.js | 14 +- .../components/ManageTeamMembersList.js | 1 - .../dashboard/components/ManageTeamsItem.js | 4 +- .../dashboard/components/ManageTeamsList.js | 1 - 19 files changed, 1859 insertions(+), 232 deletions(-) diff --git a/api/database/datamodel.graphql b/api/database/datamodel.graphql index fec8b877e..55da2ee34 100644 --- a/api/database/datamodel.graphql +++ b/api/database/datamodel.graphql @@ -7,8 +7,9 @@ enum Role { enum Status { ACTIVE INACTIVE - APPLY - INVITE + APPLIED + INVITED + REMOVED } enum FBType { @@ -53,24 +54,34 @@ type TeamStatus { } type TeamMembers { + id: ID! @unique member: Member @relation (name: "MemberOnTeam", onDelete: SET_NULL) team: Team @relation (name: "TeamMembers", onDelete: SET_NULL) role: Role @default(value: MEMBER) - status: [MemberStatus!]! + status: [TeamMemberStatus!]! + email: String + createdAt: DateTime! updatedAt: DateTime! } +type TeamMemberStatus { + status: Status @Default(value: INACTIVE) + reason: String + createdAt: DateTime! +} + type Member { id: ID! @unique alias: String teams: [TeamMembers!]! @relation (name: "MemberOnTeam", onDelete: SET_NULL) authId: String! @unique visible: Boolean! @default(value: true) + status: [MemberStatus!]! createdAt: DateTime! } type MemberStatus { - status: Status @Default(value: inactive) + status: Status @Default(value: INACTIVE) reason: String createdAt: DateTime! } diff --git a/api/package-lock.json b/api/package-lock.json index 6d1c72d4f..7a7eb3c80 100644 --- a/api/package-lock.json +++ b/api/package-lock.json @@ -163,6 +163,15 @@ "@types/node": "*" } }, + "@types/concat-stream": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/@types/concat-stream/-/concat-stream-1.6.0.tgz", + "integrity": "sha1-OU2+C7X+5Gs42JZzXoto7yOQ0A0=", + "dev": true, + "requires": { + "@types/node": "*" + } + }, "@types/connect": { "version": "3.4.32", "resolved": "https://registry.npmjs.org/@types/connect/-/connect-3.4.32.tgz", @@ -239,6 +248,18 @@ "resolved": "https://registry.npmjs.org/@types/graphql-deduplicator/-/graphql-deduplicator-2.0.0.tgz", "integrity": "sha512-swUwj5hWF1yFzbUXStLJrUa0ksAt11B8+SwhsAjQAX0LYJ1LLioAyuDcJ9bovWbsNzIXJYXLvljSPQw8nR728w==" }, + "@types/jest": { + "version": "23.3.2", + "resolved": "https://registry.npmjs.org/@types/jest/-/jest-23.3.2.tgz", + "integrity": "sha512-D1xlXHZpDonVX+VJ28XtcD5xlu8ex6Fc4cQNnrm2wJvlQnbec9RedhCrhQr6kRAE9XWHSec+JPuTmqJ9jC0qsA==", + "dev": true + }, + "@types/methods": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@types/methods/-/methods-1.1.0.tgz", + "integrity": "sha512-ROomEm+QHlUmcQoDr3CBo3GRm0w4PVoFYjVT9YcfyBha/Per4deb1IpvHU7KTK7YBZCIvOYbSADoEyDnFgaWLA==", + "dev": true + }, "@types/mime": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/@types/mime/-/mime-2.0.0.tgz", @@ -263,6 +284,12 @@ "@types/mime": "*" } }, + "@types/tough-cookie": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/@types/tough-cookie/-/tough-cookie-2.3.3.tgz", + "integrity": "sha512-MDQLxNFRLasqS4UlkWMSACMKeSm1x4Q3TxzUC7KQUsh6RK1ZrQ0VEyE3yzXcBu+K8ejVj4wuX32eUG02yNp+YQ==", + "dev": true + }, "@types/uuid": { "version": "3.4.4", "resolved": "https://registry.npmjs.org/@types/uuid/-/uuid-3.4.4.tgz", @@ -592,7 +619,7 @@ }, "readable-stream": { "version": "2.3.6", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz", + "resolved": "http://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz", "integrity": "sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw==", "dev": true, "requires": { @@ -638,7 +665,7 @@ }, "readable-stream": { "version": "2.3.6", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz", + "resolved": "http://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz", "integrity": "sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw==", "dev": true, "requires": { @@ -1004,6 +1031,16 @@ "tweetnacl": "^0.14.3" } }, + "binary": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/binary/-/binary-0.3.0.tgz", + "integrity": "sha1-n2BVO8XOjDOG87VTz/R0Yq3sqnk=", + "dev": true, + "requires": { + "buffers": "~0.1.1", + "chainsaw": "~0.1.0" + } + }, "binary-extensions": { "version": "1.11.0", "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-1.11.0.tgz", @@ -1027,7 +1064,7 @@ }, "readable-stream": { "version": "2.3.6", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz", + "resolved": "http://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz", "integrity": "sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw==", "dev": true, "requires": { @@ -1213,6 +1250,12 @@ "integrity": "sha1-Iqk2kB4wKa/NdUfrRIfOtpejvwg=", "dev": true }, + "buffers": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/buffers/-/buffers-0.1.1.tgz", + "integrity": "sha1-skV5w77U1tOWru5tmorn9Ugqt7s=", + "dev": true + }, "builtin-modules": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/builtin-modules/-/builtin-modules-1.1.1.tgz", @@ -1323,6 +1366,15 @@ "url-to-options": "^1.0.1" } }, + "chainsaw": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/chainsaw/-/chainsaw-0.1.0.tgz", + "integrity": "sha1-XqtQsor+WAdNDVgpE4iCi15fvJg=", + "dev": true, + "requires": { + "traverse": ">=0.3.0 <0.4" + } + }, "chalk": { "version": "2.4.1", "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.1.tgz", @@ -1586,7 +1638,7 @@ }, "readable-stream": { "version": "2.3.6", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz", + "resolved": "http://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz", "integrity": "sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw==", "dev": true, "requires": { @@ -1615,6 +1667,50 @@ "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=" }, + "concat-stream": { + "version": "1.6.2", + "resolved": "https://registry.npmjs.org/concat-stream/-/concat-stream-1.6.2.tgz", + "integrity": "sha512-27HBghJxjiZtIk3Ycvn/4kbJk/1uZuJFfuPEns6LaEvpvG1f0hTea8lilrouyo9mVc2GWdcEZ8OLoGmSADlrCw==", + "dev": true, + "requires": { + "buffer-from": "^1.0.0", + "inherits": "^2.0.3", + "readable-stream": "^2.2.2", + "typedarray": "^0.0.6" + }, + "dependencies": { + "isarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=", + "dev": true + }, + "readable-stream": { + "version": "2.3.6", + "resolved": "http://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz", + "integrity": "sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw==", + "dev": true, + "requires": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "dev": true, + "requires": { + "safe-buffer": "~5.1.0" + } + } + } + }, "config-chain": { "version": "1.1.12", "resolved": "https://registry.npmjs.org/config-chain/-/config-chain-1.1.12.tgz", @@ -1764,7 +1860,7 @@ }, "readable-stream": { "version": "2.3.6", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz", + "resolved": "http://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz", "integrity": "sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw==", "dev": true, "requires": { @@ -2123,6 +2219,24 @@ } } }, + "doctrine": { + "version": "0.7.2", + "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-0.7.2.tgz", + "integrity": "sha1-fLhgNZujvpDgQLJrcpzkv6ZUxSM=", + "dev": true, + "requires": { + "esutils": "^1.1.6", + "isarray": "0.0.1" + }, + "dependencies": { + "esutils": { + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/esutils/-/esutils-1.1.6.tgz", + "integrity": "sha1-wBzKqa5LiXxtDD4hCuUvPHqEQ3U=", + "dev": true + } + } + }, "dot-case": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/dot-case/-/dot-case-2.1.1.tgz", @@ -3522,6 +3636,29 @@ } } }, + "fstream": { + "version": "0.1.31", + "resolved": "https://registry.npmjs.org/fstream/-/fstream-0.1.31.tgz", + "integrity": "sha1-czfwWPu7vvqMn1YaKMqwhJICyYg=", + "dev": true, + "requires": { + "graceful-fs": "~3.0.2", + "inherits": "~2.0.0", + "mkdirp": "0.5", + "rimraf": "2" + }, + "dependencies": { + "graceful-fs": { + "version": "3.0.11", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-3.0.11.tgz", + "integrity": "sha1-dhPHeKGv6mLyXGMKCG1/Osu92Bg=", + "dev": true, + "requires": { + "natives": "^1.1.0" + } + } + } + }, "function-bind": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", @@ -5228,7 +5365,7 @@ }, "readable-stream": { "version": "2.3.6", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz", + "resolved": "http://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz", "integrity": "sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw==", "dev": true, "requires": { @@ -5546,6 +5683,15 @@ "resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.5.tgz", "integrity": "sha512-c3sIjNUow0+8swNwVpqoH4YCShKNFkMaw6oH1mNS2haDZQqkeZFlHS3dhoeEbKKmJB4vXpJucU6oH75aDYeE9g==" }, + "make-error-cause": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/make-error-cause/-/make-error-cause-1.2.2.tgz", + "integrity": "sha1-3wOI/NCzeBbf8KX7gQiTl3fcvJ0=", + "dev": true, + "requires": { + "make-error": "^1.2.0" + } + }, "map-cache": { "version": "0.2.2", "resolved": "https://registry.npmjs.org/map-cache/-/map-cache-0.2.2.tgz", @@ -5566,7 +5712,7 @@ }, "marked": { "version": "0.3.19", - "resolved": "https://registry.npmjs.org/marked/-/marked-0.3.19.tgz", + "resolved": "http://registry.npmjs.org/marked/-/marked-0.3.19.tgz", "integrity": "sha512-ea2eGWOqNxPcXv8dyERdSr/6FmzvWwzjMxpfGB/sbMccXoct+xY+YukPD+QTUZwyvK7BZwcr4m21WBOW41pAkg==", "dev": true }, @@ -5597,7 +5743,7 @@ }, "chalk": { "version": "1.1.3", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz", + "resolved": "http://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz", "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=", "dev": true, "requires": { @@ -5625,6 +5771,30 @@ } } }, + "match-stream": { + "version": "0.0.2", + "resolved": "https://registry.npmjs.org/match-stream/-/match-stream-0.0.2.tgz", + "integrity": "sha1-mesFAJOzTf+t5CG5rAtBCpz6F88=", + "dev": true, + "requires": { + "buffers": "~0.1.1", + "readable-stream": "~1.0.0" + }, + "dependencies": { + "readable-stream": { + "version": "1.0.34", + "resolved": "http://registry.npmjs.org/readable-stream/-/readable-stream-1.0.34.tgz", + "integrity": "sha1-Elgg40vIQtLyqq+v5MKRbuMsFXw=", + "dev": true, + "requires": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.1", + "isarray": "0.0.1", + "string_decoder": "~0.10.x" + } + } + } + }, "math-random": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/math-random/-/math-random-1.0.1.tgz", @@ -5856,6 +6026,12 @@ "to-regex": "^3.0.1" } }, + "natives": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/natives/-/natives-1.1.5.tgz", + "integrity": "sha512-1pJ+02gl2KJgCPFtpZGtuD4lGSJnIZvvFHCQTOeDRMSXjfu2GmYWuhI8NFMA4W2I5NNFRbfy/YCiVt4CgNpP8A==", + "dev": true + }, "negotiator": { "version": "0.6.1", "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.1.tgz", @@ -6290,6 +6466,12 @@ "integrity": "sha1-u+Z0BseaqFxc/sdm/lc0VV36EnQ=", "dev": true }, + "over": { + "version": "0.0.5", + "resolved": "https://registry.npmjs.org/over/-/over-0.0.5.tgz", + "integrity": "sha1-8phS5w/X4l82DgE6jsRMgq7bVwg=", + "dev": true + }, "p-cancelable": { "version": "0.3.0", "resolved": "https://registry.npmjs.org/p-cancelable/-/p-cancelable-0.3.0.tgz", @@ -6599,6 +6781,33 @@ "integrity": "sha512-ARhBOdzS3e41FbkW/XWrTEtukqqLoK5+Z/4UeDaLuSW+39JPeFgs4gCGqsrJHVZX0fUrx//4OF0K1CUGwlIFow==", "dev": true }, + "popsicle": { + "version": "10.0.1", + "resolved": "https://registry.npmjs.org/popsicle/-/popsicle-10.0.1.tgz", + "integrity": "sha512-IFVBRz+hc05+MiVDH+KH9QoeE6gjFOiIZNxKePIwz+JbH/yP9rLreUT9+GocxRweYBiRh7O9+MfI5X1zKfSH6Q==", + "dev": true, + "requires": { + "@types/concat-stream": "^1.6.0", + "@types/form-data": "0.0.33", + "@types/methods": "^1.1.0", + "@types/tough-cookie": "^2.3.0", + "concat-stream": "^1.4.7", + "form-data": "^2.0.0", + "make-error-cause": "^1.2.1", + "tough-cookie": "^2.0.0" + }, + "dependencies": { + "@types/form-data": { + "version": "0.0.33", + "resolved": "https://registry.npmjs.org/@types/form-data/-/form-data-0.0.33.tgz", + "integrity": "sha1-yayFsqX9GENbjIXZ7LUObWyJP/g=", + "dev": true, + "requires": { + "@types/node": "*" + } + } + } + }, "portfinder": { "version": "1.0.17", "resolved": "https://registry.npmjs.org/portfinder/-/portfinder-1.0.17.tgz", @@ -6670,15 +6879,22 @@ "integrity": "sha1-gV7R9uvGWSb4ZbMQwHE7yzMVzks=", "dev": true }, + "prettier": { + "version": "1.14.3", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-1.14.3.tgz", + "integrity": "sha512-qZDVnCrnpsRJJq5nSsiHCE3BYMED2OtsI+cmzIzF1QIfqm5ALf8tEJcO27zV1gKNKRPdhjO0dNWnrzssDQ1tFg==", + "dev": true + }, "prisma": { - "version": "1.16.4", - "resolved": "https://registry.npmjs.org/prisma/-/prisma-1.16.4.tgz", - "integrity": "sha512-ha+SJDnJ+NVtkoP4nivcmnKV/PmkbQiXVJUPEdYfkQPpuite1kOuFp9seolE4DJFaz3m8DlF24AQf7aePf09AQ==", + "version": "1.17.1", + "resolved": "https://registry.npmjs.org/prisma/-/prisma-1.17.1.tgz", + "integrity": "sha512-3WBVN1AyPhAvP9KAk9YnU/2DJy2qbilJLabXlxoEVLqY2gYBnDhKeciFj0CLvl9s4SnhMazdIPpemavQP+lvCg==", "dev": true, "requires": { "fs-extra": "^5.0.0", - "prisma-cli-core": "1.0.79", - "prisma-cli-engine": "1.0.82", + "prisma-cli-core": "1.17.1", + "prisma-cli-engine": "1.17.1", + "require-onct": "^0.0.2", "semver": "^5.4.1", "source-map-support": "^0.4.18" }, @@ -6797,9 +7013,9 @@ } }, "prisma-cli-core": { - "version": "1.0.79", - "resolved": "https://registry.npmjs.org/prisma-cli-core/-/prisma-cli-core-1.0.79.tgz", - "integrity": "sha512-3xaFE0wK48nZ8LOqx62gMoFgjIob121Uo6RHOnXNXMcCOdtGM7VfGw4o6gyDVwNDvu66fz0EVLaQGcJb5hnTpA==", + "version": "1.17.1", + "resolved": "https://registry.npmjs.org/prisma-cli-core/-/prisma-cli-core-1.17.1.tgz", + "integrity": "sha512-bzbW15BnRFRTKryGpnuDpl7zp6OFH8NNm32i0RU/nyxN46rZTSXT0jEDlJ98TRZ8Duks+pS+jDvdZOitTvAO6A==", "dev": true, "requires": { "adm-zip": "^0.4.7", @@ -6817,9 +7033,9 @@ "fs-extra": "^5.0.0", "globby": "^6.1.0", "graphcool-inquirer": "^1.0.3", - "graphql": "^0.13.1", - "graphql-config": "^2.0.1", - "graphql-config-extension-prisma": "^0.0.12", + "graphql": "0.13", + "graphql-config": "^2.2.0", + "graphql-config-extension-prisma": "^0.2.3", "graphql-playground-middleware-express": "^1.6.2", "inquirer": "^5.2.0", "isomorphic-fetch": "^2.2.1", @@ -6833,9 +7049,11 @@ "opn": "^5.1.0", "pause": "^0.1.0", "portfinder": "^1.0.13", - "prisma-db-introspection": "0.0.50", - "prisma-json-schema": "0.0.4", - "prisma-yml": "1.0.100", + "prisma-client-lib": "1.17.1", + "prisma-db-introspection": "1.17.1", + "prisma-generate-schema": "1.17.1", + "prisma-json-schema": "0.1.2", + "prisma-yml": "1.17.1", "scuid": "^1.0.2", "semver": "^5.4.1", "sillyname": "^0.1.0", @@ -6909,7 +7127,7 @@ }, "dotenv": { "version": "4.0.0", - "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-4.0.0.tgz", + "resolved": "http://registry.npmjs.org/dotenv/-/dotenv-4.0.0.tgz", "integrity": "sha1-hk7xN5rO1Vzm+V3r7NzhefegzR0=", "dev": true }, @@ -6954,19 +7172,25 @@ } }, "graphql-config-extension-prisma": { - "version": "0.0.12", - "resolved": "https://registry.npmjs.org/graphql-config-extension-prisma/-/graphql-config-extension-prisma-0.0.12.tgz", - "integrity": "sha512-p107rA/CH+y9QzOrGYHFUzgJGoYKgdUo3CJrxw2kLCTXoqIeV07N70jRtYvL9Y3e3sB7QTKdPgYO9ZcfLR1vlQ==", + "version": "0.2.4", + "resolved": "https://registry.npmjs.org/graphql-config-extension-prisma/-/graphql-config-extension-prisma-0.2.4.tgz", + "integrity": "sha512-mOpzL0zWfjsHxCj2AQqL+Id0L4FDwt0fs0xEaKK6H64n6KT4J/lVutPmf/hmPRTLuJP1y9AJPCkXNWWygUU2nw==", "dev": true, "requires": { - "graphql-config": "^2.0.1", - "prisma-yml": "1.0.50" + "graphql-config": "2.2.1", + "prisma-yml": "1.0.97" }, "dependencies": { + "prisma-json-schema": { + "version": "0.0.4", + "resolved": "https://registry.npmjs.org/prisma-json-schema/-/prisma-json-schema-0.0.4.tgz", + "integrity": "sha512-NTbourUhkmhM18rrcnp+IdxTdWftUsepZcNX2QntOqkOOwccYADmmZJpTr9tuvCYsW3i48B8LVEwvm0v1pAhxQ==", + "dev": true + }, "prisma-yml": { - "version": "1.0.50", - "resolved": "https://registry.npmjs.org/prisma-yml/-/prisma-yml-1.0.50.tgz", - "integrity": "sha512-Vpt6q+YzS6yozMnIPU75hlHzmDR1Hj4Z3FXaazJrOXUyeQx/xDqeEwRJ+ii4xl/GRYiFnMEVSLfsPzjLehA1Zw==", + "version": "1.0.97", + "resolved": "https://registry.npmjs.org/prisma-yml/-/prisma-yml-1.0.97.tgz", + "integrity": "sha512-0s9azAxrr6oeELUAoGvhyvv9+dWEmNJttKHPa4n7Z3EohhFnbgT2tpT338ez+hygevYl9E9FF8qwhdKwUrLi4w==", "dev": true, "requires": { "ajv": "5", @@ -7003,7 +7227,7 @@ }, "inquirer": { "version": "5.2.0", - "resolved": "https://registry.npmjs.org/inquirer/-/inquirer-5.2.0.tgz", + "resolved": "http://registry.npmjs.org/inquirer/-/inquirer-5.2.0.tgz", "integrity": "sha512-E9BmnJbAKLPGonz0HeWHtbKf+EeSP93paWO3ZYoUpq/aowXvYGjjCSuashhXPpzbArIjBbji39THkxTz9ZeEUQ==", "dev": true, "requires": { @@ -7068,15 +7292,15 @@ } }, "prisma-json-schema": { - "version": "0.0.4", - "resolved": "https://registry.npmjs.org/prisma-json-schema/-/prisma-json-schema-0.0.4.tgz", - "integrity": "sha512-NTbourUhkmhM18rrcnp+IdxTdWftUsepZcNX2QntOqkOOwccYADmmZJpTr9tuvCYsW3i48B8LVEwvm0v1pAhxQ==", + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/prisma-json-schema/-/prisma-json-schema-0.1.2.tgz", + "integrity": "sha512-wC93q981V9anzXffPEhquqPoQcgkvFm9Q1NgsGddQ3L+V5YU6K3G0ZWevgcRVCXbRCle6HRODptuEjR14z4fTw==", "dev": true }, "prisma-yml": { - "version": "1.0.100", - "resolved": "https://registry.npmjs.org/prisma-yml/-/prisma-yml-1.0.100.tgz", - "integrity": "sha512-W2a0dpfbXyapc3WZqcvUBqtsl2r9upQRUR2p53AqQR4tQz+/6nsMcqe30vqgnOW/WmNAnknqQT3Z5P0rsq4K3Q==", + "version": "1.17.1", + "resolved": "https://registry.npmjs.org/prisma-yml/-/prisma-yml-1.17.1.tgz", + "integrity": "sha512-XsEsFBIXkFKrLsiyOMYlA/F0e/yzHVCofz7NMCG0YGjRSeFHgyjy8RHBg7LtKys/w4o4319EVqVCFtfJNeMHKw==", "dev": true, "requires": { "ajv": "5", @@ -7093,7 +7317,7 @@ "json-stable-stringify": "^1.0.1", "jsonwebtoken": "^8.1.0", "lodash": "^4.17.4", - "prisma-json-schema": "0.0.4", + "prisma-json-schema": "0.1.2", "replaceall": "^0.1.6", "scuid": "^1.0.2", "yaml-ast-parser": "^0.0.40" @@ -7129,9 +7353,9 @@ } }, "prisma-cli-engine": { - "version": "1.0.82", - "resolved": "https://registry.npmjs.org/prisma-cli-engine/-/prisma-cli-engine-1.0.82.tgz", - "integrity": "sha512-kEqNtH0bjzVX17Vcs77UiAwk0Ei9Za03bOINcrpIjNUyI9GOOU5p+6ss8cePfK21lxvy5RSlYwthBFLkXE73Gg==", + "version": "1.17.1", + "resolved": "https://registry.npmjs.org/prisma-cli-engine/-/prisma-cli-engine-1.17.1.tgz", + "integrity": "sha512-Lx6cNdHyi8Z63mdBOv6AV3iGXSQjHePblYKUM9j+tltK2I8B0hQzDt7Q03LcMziRt08fEXGcoKlmST0x2icRhA==", "dev": true, "requires": { "@heroku/linewrap": "^1.0.0", @@ -7173,8 +7397,8 @@ "marked-terminal": "^2.0.0", "memfs": "^2.5.3", "opn": "^5.1.0", - "prisma-json-schema": "0.0.4", - "prisma-yml": "1.0.100", + "prisma-json-schema": "0.1.2", + "prisma-yml": "1.17.1", "raven": "2.3.0", "replaceall": "^0.1.6", "rwlockfile": "^1.4.8", @@ -7201,7 +7425,7 @@ }, "dotenv": { "version": "4.0.0", - "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-4.0.0.tgz", + "resolved": "http://registry.npmjs.org/dotenv/-/dotenv-4.0.0.tgz", "integrity": "sha1-hk7xN5rO1Vzm+V3r7NzhefegzR0=", "dev": true }, @@ -7213,7 +7437,7 @@ }, "inquirer": { "version": "5.2.0", - "resolved": "https://registry.npmjs.org/inquirer/-/inquirer-5.2.0.tgz", + "resolved": "http://registry.npmjs.org/inquirer/-/inquirer-5.2.0.tgz", "integrity": "sha512-E9BmnJbAKLPGonz0HeWHtbKf+EeSP93paWO3ZYoUpq/aowXvYGjjCSuashhXPpzbArIjBbji39THkxTz9ZeEUQ==", "dev": true, "requires": { @@ -7233,15 +7457,15 @@ } }, "prisma-json-schema": { - "version": "0.0.4", - "resolved": "https://registry.npmjs.org/prisma-json-schema/-/prisma-json-schema-0.0.4.tgz", - "integrity": "sha512-NTbourUhkmhM18rrcnp+IdxTdWftUsepZcNX2QntOqkOOwccYADmmZJpTr9tuvCYsW3i48B8LVEwvm0v1pAhxQ==", + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/prisma-json-schema/-/prisma-json-schema-0.1.2.tgz", + "integrity": "sha512-wC93q981V9anzXffPEhquqPoQcgkvFm9Q1NgsGddQ3L+V5YU6K3G0ZWevgcRVCXbRCle6HRODptuEjR14z4fTw==", "dev": true }, "prisma-yml": { - "version": "1.0.100", - "resolved": "https://registry.npmjs.org/prisma-yml/-/prisma-yml-1.0.100.tgz", - "integrity": "sha512-W2a0dpfbXyapc3WZqcvUBqtsl2r9upQRUR2p53AqQR4tQz+/6nsMcqe30vqgnOW/WmNAnknqQT3Z5P0rsq4K3Q==", + "version": "1.17.1", + "resolved": "https://registry.npmjs.org/prisma-yml/-/prisma-yml-1.17.1.tgz", + "integrity": "sha512-XsEsFBIXkFKrLsiyOMYlA/F0e/yzHVCofz7NMCG0YGjRSeFHgyjy8RHBg7LtKys/w4o4319EVqVCFtfJNeMHKw==", "dev": true, "requires": { "ajv": "5", @@ -7258,7 +7482,7 @@ "json-stable-stringify": "^1.0.1", "jsonwebtoken": "^8.1.0", "lodash": "^4.17.4", - "prisma-json-schema": "0.0.4", + "prisma-json-schema": "0.1.2", "replaceall": "^0.1.6", "scuid": "^1.0.2", "yaml-ast-parser": "^0.0.40" @@ -7296,10 +7520,40 @@ } } }, + "prisma-client-lib": { + "version": "1.17.1", + "resolved": "https://registry.npmjs.org/prisma-client-lib/-/prisma-client-lib-1.17.1.tgz", + "integrity": "sha512-PIGnOMXJhVqwewNKCdJ1vaDg3itt1cmnm8Dgf5nhxADJlwJkRnMCwjZnG7f4A909XAzakumPAWdvBmDERRoYnQ==", + "dev": true, + "requires": { + "@types/node": "^10.9.4", + "debug": "^4.0.1", + "graphql-tag": "^2.9.2", + "http-link-dataloader": "^0.1.4", + "jsonwebtoken": "^8.3.0", + "lodash.flatten": "^4.4.0", + "prettier": "^1.14.2", + "subscriptions-transport-ws": "^0.9.14", + "uppercamelcase": "^3.0.0", + "ws": "^6.0.0", + "zen-observable": "^0.8.9" + }, + "dependencies": { + "ws": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/ws/-/ws-6.0.0.tgz", + "integrity": "sha512-c2UlYcAZp1VS8AORtpq6y4RJIkJ9dQz18W32SpR/qXGfLDZ2jU4y4wKvvZwqbi7U6gxFQTeE+urMbXU/tsDy4w==", + "dev": true, + "requires": { + "async-limiter": "~1.0.0" + } + } + } + }, "prisma-db-introspection": { - "version": "0.0.50", - "resolved": "https://registry.npmjs.org/prisma-db-introspection/-/prisma-db-introspection-0.0.50.tgz", - "integrity": "sha512-coeJ3SRumezVnsQwc/kwNzxWFUBKHNwnD+keUJeZFFY3j7EDtRFHoL5DD7vTREvMEugoWOiovc+MYHFkDt0l+w==", + "version": "1.17.1", + "resolved": "https://registry.npmjs.org/prisma-db-introspection/-/prisma-db-introspection-1.17.1.tgz", + "integrity": "sha512-y6L5EMCuvD9ML3FzltEAowrM6/xvT0XmRKd9eLSdf6UF5wA+aKbGOFTDyQbIvfiKpoH4Ly8pNN1x/E6f5AyPRQ==", "dev": true, "requires": { "ajv": "5", @@ -7316,7 +7570,8 @@ "lodash": "^4.17.4", "pg": "^7.4.1", "pluralize": "^7.0.0", - "prisma-json-schema": "^0.0.1", + "prisma-json-schema": "0.1.2", + "prisma-yml": "1.17.1", "replaceall": "^0.1.6", "scuid": "^1.0.2", "uppercamelcase": "^3.0.0", @@ -7334,15 +7589,912 @@ }, "dotenv": { "version": "4.0.0", - "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-4.0.0.tgz", + "resolved": "http://registry.npmjs.org/dotenv/-/dotenv-4.0.0.tgz", "integrity": "sha1-hk7xN5rO1Vzm+V3r7NzhefegzR0=", "dev": true }, + "prisma-json-schema": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/prisma-json-schema/-/prisma-json-schema-0.1.2.tgz", + "integrity": "sha512-wC93q981V9anzXffPEhquqPoQcgkvFm9Q1NgsGddQ3L+V5YU6K3G0ZWevgcRVCXbRCle6HRODptuEjR14z4fTw==", + "dev": true + }, + "prisma-yml": { + "version": "1.17.1", + "resolved": "https://registry.npmjs.org/prisma-yml/-/prisma-yml-1.17.1.tgz", + "integrity": "sha512-XsEsFBIXkFKrLsiyOMYlA/F0e/yzHVCofz7NMCG0YGjRSeFHgyjy8RHBg7LtKys/w4o4319EVqVCFtfJNeMHKw==", + "dev": true, + "requires": { + "ajv": "5", + "bluebird": "^3.5.1", + "chalk": "^2.3.0", + "debug": "^3.1.0", + "dotenv": "^4.0.0", + "fs-extra": "^5.0.0", + "graphql-request": "^1.5.0", + "http-proxy-agent": "^2.1.0", + "https-proxy-agent": "^2.2.1", + "isomorphic-fetch": "^2.2.1", + "js-yaml": "^3.10.0", + "json-stable-stringify": "^1.0.1", + "jsonwebtoken": "^8.1.0", + "lodash": "^4.17.4", + "prisma-json-schema": "0.1.2", + "replaceall": "^0.1.6", + "scuid": "^1.0.2", + "yaml-ast-parser": "^0.0.40" + } + } + } + }, + "prisma-generate-schema": { + "version": "1.17.1", + "resolved": "https://registry.npmjs.org/prisma-generate-schema/-/prisma-generate-schema-1.17.1.tgz", + "integrity": "sha512-1qTI5BA9KXFwxme5vTnIwdl01IC5lnJvU/S87rq/Ps5YEdsqY33obLQvXPf/CiaKh2z/1wGbc5ak0up7XYiXFg==", + "dev": true, + "requires": { + "@types/jest": "^23.3.1", + "graphql": "^14.0.2", + "pluralize": "^7.0.0", + "popsicle": "^10.0.1", + "prisma-cli": "^1.0.7", + "tslint-config-prettier": "^1.15.0", + "tslint-eslint-rules": "^5.4.0" + }, + "dependencies": { + "anymatch": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-1.3.2.tgz", + "integrity": "sha512-0XNayC8lTHQ2OI8aljNCN3sSx6hsr/1+rlcDAotXJR7C1oZZHCNsfpbKwMjRA3Uqb5tF1Rae2oloTr4xpq+WjA==", + "dev": true, + "requires": { + "micromatch": "^2.1.5", + "normalize-path": "^2.0.0" + } + }, + "arr-diff": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/arr-diff/-/arr-diff-2.0.0.tgz", + "integrity": "sha1-jzuCf5Vai9ZpaX5KQlasPOrjVs8=", + "dev": true, + "requires": { + "arr-flatten": "^1.0.1" + } + }, + "array-unique": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/array-unique/-/array-unique-0.2.1.tgz", + "integrity": "sha1-odl8yvy8JiXMcPrc6zalDFiwGlM=", + "dev": true + }, + "braces": { + "version": "1.8.5", + "resolved": "https://registry.npmjs.org/braces/-/braces-1.8.5.tgz", + "integrity": "sha1-uneWLhLf+WnWt2cR6RS3N4V79qc=", + "dev": true, + "requires": { + "expand-range": "^1.8.1", + "preserve": "^0.2.0", + "repeat-element": "^1.1.2" + } + }, + "chokidar": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-1.7.0.tgz", + "integrity": "sha1-eY5ol3gVHIB2tLNg5e3SjNortGg=", + "dev": true, + "requires": { + "anymatch": "^1.3.0", + "async-each": "^1.0.0", + "fsevents": "^1.0.0", + "glob-parent": "^2.0.0", + "inherits": "^2.0.1", + "is-binary-path": "^1.0.0", + "is-glob": "^2.0.0", + "path-is-absolute": "^1.0.0", + "readdirp": "^2.0.0" + } + }, + "debug": { + "version": "3.2.5", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.5.tgz", + "integrity": "sha512-D61LaDQPQkxJ5AUM2mbSJRbPkNs/TmdmOeLAi1hgDkpDfIfetSrjmWhccwtuResSwMbACjx/xXQofvM9CE/aeg==", + "dev": true, + "requires": { + "ms": "^2.1.1" + } + }, + "dotenv": { + "version": "4.0.0", + "resolved": "http://registry.npmjs.org/dotenv/-/dotenv-4.0.0.tgz", + "integrity": "sha1-hk7xN5rO1Vzm+V3r7NzhefegzR0=", + "dev": true + }, + "expand-brackets": { + "version": "0.1.5", + "resolved": "https://registry.npmjs.org/expand-brackets/-/expand-brackets-0.1.5.tgz", + "integrity": "sha1-3wcoTjQqgHzXM6xa9yQR5YHRF3s=", + "dev": true, + "requires": { + "is-posix-bracket": "^0.1.0" + } + }, + "extglob": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/extglob/-/extglob-0.3.2.tgz", + "integrity": "sha1-Lhj/PS9JqydlzskCPwEdqo2DSaE=", + "dev": true, + "requires": { + "is-extglob": "^1.0.0" + } + }, + "glob-parent": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-2.0.0.tgz", + "integrity": "sha1-gTg9ctsFT8zPUzbaqQLxgvbtuyg=", + "dev": true, + "requires": { + "is-glob": "^2.0.0" + } + }, + "graphcool-json-schema": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/graphcool-json-schema/-/graphcool-json-schema-1.2.0.tgz", + "integrity": "sha512-OOltel1a2z+aJgdyGx7TmX3gPterUMObZBWrt6UT1pKj8YjH+jDmM/mBIm4rIKBaweNZn+Rff/pm0A93Aw5row==", + "dev": true + }, + "graphql": { + "version": "14.0.2", + "resolved": "https://registry.npmjs.org/graphql/-/graphql-14.0.2.tgz", + "integrity": "sha512-gUC4YYsaiSJT1h40krG3J+USGlwhzNTXSb4IOZljn9ag5Tj+RkoXrWp+Kh7WyE3t1NCfab5kzCuxBIvOMERMXw==", + "dev": true, + "requires": { + "iterall": "^1.2.2" + } + }, + "graphql-cli": { + "version": "2.12.2", + "resolved": "https://registry.npmjs.org/graphql-cli/-/graphql-cli-2.12.2.tgz", + "integrity": "sha512-rq8esQIP/inviUd65apcNSR3t5SucUNZsFtByH5T7sTh+zBptYk/gRVDctqowPHa1cjMuMmeGyJy8ZAGs13ZCA==", + "dev": true, + "requires": { + "adm-zip": "^0.4.7", + "chalk": "^2.3.0", + "command-exists": "^1.2.2", + "cross-spawn": "^5.1.0", + "disparity": "^2.0.0", + "dotenv": "^4.0.0", + "express": "^4.16.2", + "express-request-proxy": "^2.0.0", + "graphql": "0.12.3", + "graphql-cli-prepare": "1.4.11", + "graphql-config": "1.1.7", + "graphql-config-extension-graphcool": "1.0.5", + "graphql-config-extension-prisma": "0.0.3", + "graphql-playground-middleware-express": "1.4.9", + "graphql-schema-linter": "0.0.27", + "inquirer": "5.0.0", + "is-url-superb": "2.0.0", + "js-yaml": "^3.10.0", + "lodash": "^4.17.4", + "node-fetch": "^1.7.3", + "npm-paths": "^1.0.0", + "opn": "^5.1.0", + "ora": "^1.3.0", + "parse-github-url": "^1.0.2", + "request": "^2.83.0", + "rimraf": "2.6.2", + "source-map-support": "^0.5.0", + "yargs": "10.1.1" + }, + "dependencies": { + "graphql": { + "version": "0.12.3", + "resolved": "https://registry.npmjs.org/graphql/-/graphql-0.12.3.tgz", + "integrity": "sha512-Hn9rdu4zacplKXNrLCvR8YFiTGnbM4Zw/UH8FDmzBDsH7ou40lSNH4tIlsxcYnz2TGNVJCpu1WxCM23yd6kzhA==", + "dev": true, + "requires": { + "iterall": "1.1.3" + } + }, + "graphql-config": { + "version": "1.1.7", + "resolved": "https://registry.npmjs.org/graphql-config/-/graphql-config-1.1.7.tgz", + "integrity": "sha512-RJIgwbS43Mik0kqANtcOtGL7Por8TPrK2t8Y+X284eVifialSqyprvZqoAxR4E/LSFIrCWS1txHSHGujx3fS9w==", + "dev": true, + "requires": { + "graphql": "^0.12.3", + "graphql-import": "^0.4.0", + "graphql-request": "^1.4.0", + "js-yaml": "^3.10.0", + "minimatch": "^3.0.4" + } + }, + "inquirer": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/inquirer/-/inquirer-5.0.0.tgz", + "integrity": "sha512-p/UAvVLKRI9fLukkVeeSnP05Ilz76+7najkJptTKOMJApaqJhdwxLGlIymYvn0a1WFqrEG8MqfzKfVMvArwdIQ==", + "dev": true, + "requires": { + "ansi-escapes": "^3.0.0", + "chalk": "^2.0.0", + "cli-cursor": "^2.1.0", + "cli-width": "^2.0.0", + "external-editor": "^2.1.0", + "figures": "^2.0.0", + "lodash": "^4.3.0", + "mute-stream": "0.0.7", + "run-async": "^2.2.0", + "rxjs": "^5.5.2", + "string-width": "^2.1.0", + "strip-ansi": "^4.0.0", + "through": "^2.3.6" + } + }, + "iterall": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/iterall/-/iterall-1.1.3.tgz", + "integrity": "sha512-Cu/kb+4HiNSejAPhSaN1VukdNTTi/r4/e+yykqjlG/IW+1gZH5b4+Bq3whDX4tvbYugta3r8KTMUiqT3fIGxuQ==", + "dev": true + }, + "source-map-support": { + "version": "0.5.9", + "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.9.tgz", + "integrity": "sha512-gR6Rw4MvUlYy83vP0vxoVNzM6t8MUXqNuRsuBmBHQDu1Fh6X015FrLdgoDKcNdkwGubozq0P4N0Q37UyFVr1EA==", + "dev": true, + "requires": { + "buffer-from": "^1.0.0", + "source-map": "^0.6.0" + } + } + } + }, + "graphql-cli-prepare": { + "version": "1.4.11", + "resolved": "https://registry.npmjs.org/graphql-cli-prepare/-/graphql-cli-prepare-1.4.11.tgz", + "integrity": "sha512-aDaqTT+kKFWe9v/8/u0M/Sc4WBVaG0sjfuWSR1UX+4sonBucJExf4IlShpZGVFxP6ro61Z4jX3mygtvGNsAo3Q==", + "dev": true, + "requires": { + "chalk": "2.3.0", + "fs-extra": "5.0.0", + "graphql-import": "0.4.0", + "graphql-static-binding": "0.8.0", + "lodash": "4.17.4" + }, + "dependencies": { + "chalk": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.3.0.tgz", + "integrity": "sha512-Az5zJR2CBujap2rqXGaJKaPHyJ0IrUimvYNX+ncCy8PJP4ltOGTrHUIo097ZaL2zMeKYpiCdqDvS6zdrTFok3Q==", + "dev": true, + "requires": { + "ansi-styles": "^3.1.0", + "escape-string-regexp": "^1.0.5", + "supports-color": "^4.0.0" + } + }, + "lodash": { + "version": "4.17.4", + "resolved": "http://registry.npmjs.org/lodash/-/lodash-4.17.4.tgz", + "integrity": "sha1-eCA6TRwyiuHYbcpkYONptX9AVa4=", + "dev": true + } + } + }, + "graphql-config": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/graphql-config/-/graphql-config-1.2.1.tgz", + "integrity": "sha512-BOtbEOn/fD13jT0peCy3Fzp1DSTsA/1AcZp266AQ5Sk3wFndKCEa/H7donbu5UriOw1V/N1WDirYPnr7rd8E7Q==", + "dev": true, + "requires": { + "graphql": "^0.12.3", + "graphql-import": "^0.4.0", + "graphql-request": "^1.4.0", + "js-yaml": "^3.10.0", + "lodash": "^4.17.4", + "minimatch": "^3.0.4" + }, + "dependencies": { + "graphql": { + "version": "0.12.3", + "resolved": "https://registry.npmjs.org/graphql/-/graphql-0.12.3.tgz", + "integrity": "sha512-Hn9rdu4zacplKXNrLCvR8YFiTGnbM4Zw/UH8FDmzBDsH7ou40lSNH4tIlsxcYnz2TGNVJCpu1WxCM23yd6kzhA==", + "dev": true, + "requires": { + "iterall": "1.1.3" + } + }, + "iterall": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/iterall/-/iterall-1.1.3.tgz", + "integrity": "sha512-Cu/kb+4HiNSejAPhSaN1VukdNTTi/r4/e+yykqjlG/IW+1gZH5b4+Bq3whDX4tvbYugta3r8KTMUiqT3fIGxuQ==", + "dev": true + } + } + }, + "graphql-config-extension-graphcool": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/graphql-config-extension-graphcool/-/graphql-config-extension-graphcool-1.0.5.tgz", + "integrity": "sha512-uoshXJ/2nWBYCIPOJNjuNUrO0eUF7JUtHfe/rmPkkqKr9vZFE59juLBR6WfjII2KgAaFvvPsCw2iBkSCkhqQyQ==", + "dev": true, + "requires": { + "graphcool-yml": "0.4.15", + "graphql-config": "1.1.7" + }, + "dependencies": { + "graphql": { + "version": "0.12.3", + "resolved": "https://registry.npmjs.org/graphql/-/graphql-0.12.3.tgz", + "integrity": "sha512-Hn9rdu4zacplKXNrLCvR8YFiTGnbM4Zw/UH8FDmzBDsH7ou40lSNH4tIlsxcYnz2TGNVJCpu1WxCM23yd6kzhA==", + "dev": true, + "requires": { + "iterall": "1.1.3" + } + }, + "graphql-config": { + "version": "1.1.7", + "resolved": "https://registry.npmjs.org/graphql-config/-/graphql-config-1.1.7.tgz", + "integrity": "sha512-RJIgwbS43Mik0kqANtcOtGL7Por8TPrK2t8Y+X284eVifialSqyprvZqoAxR4E/LSFIrCWS1txHSHGujx3fS9w==", + "dev": true, + "requires": { + "graphql": "^0.12.3", + "graphql-import": "^0.4.0", + "graphql-request": "^1.4.0", + "js-yaml": "^3.10.0", + "minimatch": "^3.0.4" + } + }, + "iterall": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/iterall/-/iterall-1.1.3.tgz", + "integrity": "sha512-Cu/kb+4HiNSejAPhSaN1VukdNTTi/r4/e+yykqjlG/IW+1gZH5b4+Bq3whDX4tvbYugta3r8KTMUiqT3fIGxuQ==", + "dev": true + } + } + }, + "graphql-config-extension-prisma": { + "version": "0.0.3", + "resolved": "https://registry.npmjs.org/graphql-config-extension-prisma/-/graphql-config-extension-prisma-0.0.3.tgz", + "integrity": "sha512-GkBGQSNRoVpEYw0i0GioHEzApoeknWU43aOEYQuj4PuDa/LT65xSM/L3A1pODkkHzp1ABgtGWuZCMb37UBpdNQ==", + "dev": true, + "requires": { + "graphql-config": "^1.1.4", + "prisma-yml": "0.0.4" + }, + "dependencies": { + "fs-extra": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-4.0.3.tgz", + "integrity": "sha512-q6rbdDd1o2mAnQreO7YADIxf/Whx4AHBiRf6d+/cVT8h44ss+lHgxf1FemcqDnQt9X3ct4McHr+JMGlYSsK7Cg==", + "dev": true, + "requires": { + "graceful-fs": "^4.1.2", + "jsonfile": "^4.0.0", + "universalify": "^0.1.0" + } + }, + "prisma-yml": { + "version": "0.0.4", + "resolved": "https://registry.npmjs.org/prisma-yml/-/prisma-yml-0.0.4.tgz", + "integrity": "sha512-8yfLSYT55rG2pHEtDhws01DDtdFv23JA/FECWHT4q2/fITL952LM6d1JSlpxBzqXehEf3UwBssVLWka7WlIrXA==", + "dev": true, + "requires": { + "ajv": "^5.5.1", + "bluebird": "^3.5.1", + "chalk": "^2.3.0", + "debug": "^3.1.0", + "dotenv": "^4.0.0", + "fs-extra": "^4.0.3", + "isomorphic-fetch": "^2.2.1", + "js-yaml": "^3.10.0", + "json-stable-stringify": "^1.0.1", + "jsonwebtoken": "^8.1.0", + "lodash": "^4.17.4", + "prisma-json-schema": "^0.0.1", + "replaceall": "^0.1.6", + "scuid": "^1.0.2", + "yaml-ast-parser": "^0.0.40" + } + } + } + }, + "graphql-import": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/graphql-import/-/graphql-import-0.4.0.tgz", + "integrity": "sha512-saHu8eDvOEdPDPCZDdauP/VsHI5zjKMutOTVqf34Yq/ltXZ5u7mzHUZYqY8mBJVsV2QQ6leEn6PYLU6sWeK2nQ==", + "dev": true, + "requires": { + "graphql": "^0.12.3", + "lodash": "^4.17.4" + }, + "dependencies": { + "graphql": { + "version": "0.12.3", + "resolved": "https://registry.npmjs.org/graphql/-/graphql-0.12.3.tgz", + "integrity": "sha512-Hn9rdu4zacplKXNrLCvR8YFiTGnbM4Zw/UH8FDmzBDsH7ou40lSNH4tIlsxcYnz2TGNVJCpu1WxCM23yd6kzhA==", + "dev": true, + "requires": { + "iterall": "1.1.3" + } + }, + "iterall": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/iterall/-/iterall-1.1.3.tgz", + "integrity": "sha512-Cu/kb+4HiNSejAPhSaN1VukdNTTi/r4/e+yykqjlG/IW+1gZH5b4+Bq3whDX4tvbYugta3r8KTMUiqT3fIGxuQ==", + "dev": true + } + } + }, + "graphql-playground-html": { + "version": "1.4.4", + "resolved": "https://registry.npmjs.org/graphql-playground-html/-/graphql-playground-html-1.4.4.tgz", + "integrity": "sha512-Ytc/R+WwU9iMaHC17lIZjvbySqhUIDX7ysIFDqlDuhRXgBFmKtbLwLJpI7nqIHhQ7mbpYbv+V+lG9edVq43ovQ==", + "dev": true, + "requires": { + "dotenv": "^4.0.0", + "graphql-config": "^1.1.1", + "graphql-config-extension-graphcool": "1.0.4", + "graphql-config-extension-prisma": "0.0.3" + }, + "dependencies": { + "fs-extra": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-4.0.3.tgz", + "integrity": "sha512-q6rbdDd1o2mAnQreO7YADIxf/Whx4AHBiRf6d+/cVT8h44ss+lHgxf1FemcqDnQt9X3ct4McHr+JMGlYSsK7Cg==", + "dev": true, + "requires": { + "graceful-fs": "^4.1.2", + "jsonfile": "^4.0.0", + "universalify": "^0.1.0" + } + }, + "graphcool-yml": { + "version": "0.4.8", + "resolved": "https://registry.npmjs.org/graphcool-yml/-/graphcool-yml-0.4.8.tgz", + "integrity": "sha512-0SXfhHu5AX+Mne9MeorLquqLXOt/k59VBcFywM/4Lb6t/E2F5AZQUSzxIFH/4XEAtGD34iJAhvpri9MDhIyH7A==", + "dev": true, + "requires": { + "ajv": "^5.5.1", + "bluebird": "^3.5.1", + "debug": "^3.1.0", + "dotenv": "^4.0.0", + "fs-extra": "^4.0.3", + "graphcool-json-schema": "1.2.0", + "isomorphic-fetch": "^2.2.1", + "js-yaml": "^3.10.0", + "json-stable-stringify": "^1.0.1", + "jsonwebtoken": "^8.1.0", + "lodash": "^4.17.4", + "replaceall": "^0.1.6", + "scuid": "^1.0.2", + "yaml-ast-parser": "^0.0.40" + } + }, + "graphql-config-extension-graphcool": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/graphql-config-extension-graphcool/-/graphql-config-extension-graphcool-1.0.4.tgz", + "integrity": "sha512-I+zGFshuuciNrUKIQEIW32lF7h3SZgQmj7I3y3NfrU/BUwM5mKDqsPvntnDWWrtrPhWqMVAu6SxV3bsu1X1+Fw==", + "dev": true, + "requires": { + "graphcool-yml": "0.4.8", + "graphql-config": "^1.1.4" + } + } + } + }, + "graphql-playground-middleware-express": { + "version": "1.4.9", + "resolved": "https://registry.npmjs.org/graphql-playground-middleware-express/-/graphql-playground-middleware-express-1.4.9.tgz", + "integrity": "sha512-y4O77g4LDchlc1vv5qdJ1iQZl9Cl/d1d2/Kp4XQCWhwx8hcB6ynH9yMuDwFoKTy+nTPgv5fHLi2Eytjcna7vGg==", + "dev": true, + "requires": { + "graphql-playground-html": "1.4.4" + } + }, + "graphql-schema-linter": { + "version": "0.0.27", + "resolved": "https://registry.npmjs.org/graphql-schema-linter/-/graphql-schema-linter-0.0.27.tgz", + "integrity": "sha512-qhz+LbIFRg9v4NOLus2r4qWUtvg8vKhFagraJxg/sAAlrU3r9U8fTD/L2bcESmPcTziYAk65HKUEGKHnrYXkvw==", + "dev": true, + "requires": { + "chalk": "^2.0.1", + "columnify": "^1.5.4", + "commander": "^2.11.0", + "cosmiconfig": "^3.1.0", + "figures": "^2.0.0", + "glob": "^7.1.2", + "graphql": "^0.10.1", + "graphql-config": "^1.0.0", + "lodash": "^4.17.4" + }, + "dependencies": { + "graphql": { + "version": "0.10.5", + "resolved": "https://registry.npmjs.org/graphql/-/graphql-0.10.5.tgz", + "integrity": "sha512-Q7cx22DiLhwHsEfUnUip1Ww/Vfx7FS0w6+iHItNuN61+XpegHSa3k5U0+6M5BcpavQImBwFiy0z3uYwY7cXMLQ==", + "dev": true, + "requires": { + "iterall": "^1.1.0" + } + } + } + }, + "graphql-static-binding": { + "version": "0.8.0", + "resolved": "https://registry.npmjs.org/graphql-static-binding/-/graphql-static-binding-0.8.0.tgz", + "integrity": "sha512-BWOCN3vb29Go5r9pj70DCgIX7P0AyB+6Hbel0tKtPV0Ksm+Uq5fxOl86R070xhngaAGEqop5uCTs4IJ4TXU4Ug==", + "dev": true, + "requires": { + "cucumber-html-reporter": "^3.0.4", + "graphql": "0.12.3" + }, + "dependencies": { + "graphql": { + "version": "0.12.3", + "resolved": "https://registry.npmjs.org/graphql/-/graphql-0.12.3.tgz", + "integrity": "sha512-Hn9rdu4zacplKXNrLCvR8YFiTGnbM4Zw/UH8FDmzBDsH7ou40lSNH4tIlsxcYnz2TGNVJCpu1WxCM23yd6kzhA==", + "dev": true, + "requires": { + "iterall": "1.1.3" + } + }, + "iterall": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/iterall/-/iterall-1.1.3.tgz", + "integrity": "sha512-Cu/kb+4HiNSejAPhSaN1VukdNTTi/r4/e+yykqjlG/IW+1gZH5b4+Bq3whDX4tvbYugta3r8KTMUiqT3fIGxuQ==", + "dev": true + } + } + }, + "has-flag": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-2.0.0.tgz", + "integrity": "sha1-6CB68cx7MNRGzHC3NLXovhj4jVE=", + "dev": true + }, + "inquirer": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/inquirer/-/inquirer-3.3.0.tgz", + "integrity": "sha512-h+xtnyk4EwKvFWHrUYsWErEVR+igKtLdchu+o0Z1RL7VU/jVMFbYir2bp6bAj8efFNxWqHX0dIss6fJQ+/+qeQ==", + "dev": true, + "requires": { + "ansi-escapes": "^3.0.0", + "chalk": "^2.0.0", + "cli-cursor": "^2.1.0", + "cli-width": "^2.0.0", + "external-editor": "^2.0.4", + "figures": "^2.0.0", + "lodash": "^4.3.0", + "mute-stream": "0.0.7", + "run-async": "^2.2.0", + "rx-lite": "^4.0.8", + "rx-lite-aggregates": "^4.0.8", + "string-width": "^2.1.0", + "strip-ansi": "^4.0.0", + "through": "^2.3.6" + } + }, + "is-extglob": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-1.0.0.tgz", + "integrity": "sha1-rEaBd8SUNAWgkvyPKXYMb/xiBsA=", + "dev": true + }, + "is-glob": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-2.0.1.tgz", + "integrity": "sha1-0Jb5JqPe1WAPP9/ZEZjLCIjC2GM=", + "dev": true, + "requires": { + "is-extglob": "^1.0.0" + } + }, + "kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "dev": true, + "requires": { + "is-buffer": "^1.1.5" + } + }, + "micromatch": { + "version": "2.3.11", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-2.3.11.tgz", + "integrity": "sha1-hmd8l9FyCzY0MdBNDRUpO9OMFWU=", + "dev": true, + "requires": { + "arr-diff": "^2.0.0", + "array-unique": "^0.2.1", + "braces": "^1.8.2", + "expand-brackets": "^0.1.4", + "extglob": "^0.3.1", + "filename-regex": "^2.0.0", + "is-extglob": "^1.0.0", + "is-glob": "^2.0.1", + "kind-of": "^3.0.2", + "normalize-path": "^2.0.1", + "object.omit": "^2.0.0", + "parse-glob": "^3.0.4", + "regex-cache": "^0.4.2" + } + }, + "node-fetch": { + "version": "1.7.3", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-1.7.3.tgz", + "integrity": "sha512-NhZ4CsKx7cYm2vSrBAr2PvFOe6sWDf0UYLRqA6svUYg7+/TSfVAu49jYC4BvQ4Sms9SZgdqGBgroqfDhJdTyKQ==", + "dev": true, + "requires": { + "encoding": "^0.1.11", + "is-stream": "^1.0.1" + } + }, + "prisma-cli": { + "version": "1.0.9", + "resolved": "https://registry.npmjs.org/prisma-cli/-/prisma-cli-1.0.9.tgz", + "integrity": "sha512-f1vF+nF+7li+tZoCFpDX3aGdh+uqPGspSzGwM3sxJIEfCbKO7tmNvb19XVhj82nwG3PqkmpkJFCIOuqzy7B1Xg==", + "dev": true, + "requires": { + "fs-extra": "^5.0.0", + "prisma-cli-core": "1.0.5-2", + "prisma-cli-engine": "1.0.3-7", + "semver": "^5.4.1", + "source-map-support": "^0.4.18" + } + }, + "prisma-cli-core": { + "version": "1.0.5-2", + "resolved": "https://registry.npmjs.org/prisma-cli-core/-/prisma-cli-core-1.0.5-2.tgz", + "integrity": "sha512-Pv57S2ZaYoDonOxI94QHhODJ3oRM9ZaxdiqGAkGsTT4CTRVu39tZovFRvVTVXvtKM4175KUWFAtaps9+5a1d8w==", + "dev": true, + "requires": { + "archiver": "^2.0.3", + "callsites": "^2.0.0", + "chalk": "^2.3.0", + "chokidar": "^1.7.0", + "copy-paste": "^1.3.0", + "cross-spawn": "^5.1.0", + "download-github-repo": "^0.1.3", + "figures": "^2.0.0", + "find-up": "^2.1.0", + "fs-extra": "^5.0.0", + "globby": "^6.1.0", + "graphcool-inquirer": "^1.0.3", + "graphql": "^0.11.7", + "graphql-cli": "2.12.2", + "graphql-config": "^1.1.7", + "inquirer": "^3.3.0", + "isomorphic-fetch": "^2.2.1", + "js-yaml": "^3.9.1", + "jwt-decode": "^2.2.0", + "lodash": "^4.17.4", + "lodash.differenceby": "^4.8.0", + "multimatch": "^2.1.0", + "node-forge": "^0.7.1", + "npm-run": "^4.1.2", + "opn": "^5.1.0", + "pause": "^0.1.0", + "portfinder": "^1.0.13", + "prisma-json-schema": "^0.0.1", + "prisma-yml": "1.0.14-4", + "scuid": "^1.0.2", + "semver": "^5.4.1", + "sillyname": "^0.1.0", + "source-map-support": "^0.4.18", + "table": "^4.0.1", + "unzip": "^0.1.11", + "util.promisify": "^1.0.0", + "validator": "^8.2.0" + }, + "dependencies": { + "graphql": { + "version": "0.11.7", + "resolved": "https://registry.npmjs.org/graphql/-/graphql-0.11.7.tgz", + "integrity": "sha512-x7uDjyz8Jx+QPbpCFCMQ8lltnQa4p4vSYHx6ADe8rVYRTdsyhCJbvSty5DAsLVmU6cGakl+r8HQYolKHxk/tiw==", + "dev": true, + "requires": { + "iterall": "1.1.3" + } + }, + "iterall": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/iterall/-/iterall-1.1.3.tgz", + "integrity": "sha512-Cu/kb+4HiNSejAPhSaN1VukdNTTi/r4/e+yykqjlG/IW+1gZH5b4+Bq3whDX4tvbYugta3r8KTMUiqT3fIGxuQ==", + "dev": true + } + } + }, + "prisma-cli-engine": { + "version": "1.0.3-7", + "resolved": "https://registry.npmjs.org/prisma-cli-engine/-/prisma-cli-engine-1.0.3-7.tgz", + "integrity": "sha512-P4lCfhZYhXspr4b6FqHyavFGFY/H/lXcLcKrG+ZTdaFJvo+sSaQUKJ6UAN/RDm778bhd9+kKQkFdUDR+ulxedQ==", + "dev": true, + "requires": { + "@heroku/linewrap": "^1.0.0", + "ajv": "5", + "ansi-escapes": "^3.0.0", + "ansi-styles": "^3.2.0", + "bluebird": "^3.5.0", + "cache-require-paths": "^0.3.0", + "callsites": "^2.0.0", + "cardinal": "^1.0.0", + "chalk": "^2.3.0", + "charm": "^1.0.2", + "debug": "^3.1.0", + "directory-tree": "^2.0.0", + "dotenv": "^4.0.0", + "figures": "^2.0.0", + "find-up": "^2.1.0", + "fs-extra": "^5.0.0", + "graphcool-inquirer": "^1.0.3", + "graphql-request": "^1.3.4", + "isomorphic-fetch": "^2.2.1", + "jsonwebtoken": "^8.1.0", + "klaw-sync": "^3.0.0", + "lodash": "^4.17.4", + "lodash.ary": "^4.1.1", + "lodash.defaults": "^4.2.0", + "lodash.flatten": "^4.4.0", + "lodash.get": "^4.4.2", + "lodash.groupby": "^4.6.0", + "lodash.identity": "^3.0.0", + "lodash.keys": "^4.2.0", + "lodash.maxby": "4.x", + "lodash.merge": "4.x", + "lodash.partial": "^4.2.1", + "lodash.property": "^4.4.2", + "lodash.result": "^4.5.2", + "lodash.uniqby": "^4.7.0", + "marked": "^0.3.6", + "marked-terminal": "^2.0.0", + "memfs": "^2.5.3", + "opn": "^5.1.0", + "prisma-json-schema": "^0.0.1", + "prisma-yml": "1.0.14-5", + "raven": "2.3.0", + "replaceall": "^0.1.6", + "rwlockfile": "^1.4.8", + "scuid": "^1.0.2", + "serialize-error": "^2.1.0", + "source-map-support": "^0.4.18", + "string": "3.x", + "string-similarity": "^1.2.0", + "strip-ansi": "^4.0.0", + "supports-color": "^4.4.0", + "treeify": "^1.0.1", + "update-notifier": "^2.3.0", + "validator": "^8.2.0", + "yaml-ast-parser": "^0.0.34" + }, + "dependencies": { + "prisma-yml": { + "version": "1.0.14-5", + "resolved": "https://registry.npmjs.org/prisma-yml/-/prisma-yml-1.0.14-5.tgz", + "integrity": "sha512-UxO+txzaMugam6toJs6aQ8FCz1yO8uZ8HjEAWTDtYNLdEHK0vfxCLvRCJnip8p3AgtljoG0LdhF2T3Co876qaQ==", + "dev": true, + "requires": { + "ajv": "5", + "bluebird": "^3.5.1", + "chalk": "^2.3.0", + "debug": "^3.1.0", + "dotenv": "^4.0.0", + "fs-extra": "^5.0.0", + "isomorphic-fetch": "^2.2.1", + "js-yaml": "^3.10.0", + "json-stable-stringify": "^1.0.1", + "jsonwebtoken": "^8.1.0", + "lodash": "^4.17.4", + "prisma-json-schema": "^0.0.1", + "replaceall": "^0.1.6", + "scuid": "^1.0.2", + "yaml-ast-parser": "^0.0.40" + }, + "dependencies": { + "yaml-ast-parser": { + "version": "0.0.40", + "resolved": "https://registry.npmjs.org/yaml-ast-parser/-/yaml-ast-parser-0.0.40.tgz", + "integrity": "sha1-CFNtTnPTIrHJziB6uN1w4E0grm4=", + "dev": true + } + } + }, + "yaml-ast-parser": { + "version": "0.0.34", + "resolved": "https://registry.npmjs.org/yaml-ast-parser/-/yaml-ast-parser-0.0.34.tgz", + "integrity": "sha1-0A88+ddztyQUCa6SpnQNHbGfSeY=", + "dev": true + } + } + }, "prisma-json-schema": { "version": "0.0.1", "resolved": "https://registry.npmjs.org/prisma-json-schema/-/prisma-json-schema-0.0.1.tgz", "integrity": "sha512-V9UNqGtfqX5ZPt0kjv5JfE4Cj5Oc7NjSvkodUeB6M3Rsqob2Amzzft2HlU0IptiBauqPKzWEqiplM2fRCaC27A==", "dev": true + }, + "prisma-yml": { + "version": "1.0.14-4", + "resolved": "https://registry.npmjs.org/prisma-yml/-/prisma-yml-1.0.14-4.tgz", + "integrity": "sha512-dvQiY8lUAG4rakCl36w0HXtsuujYgxPaHPPhfr+ALuj3oisBO+VCvOVAghD2yYcvR+yeJ3dJWNf4kIY68qGv3w==", + "dev": true, + "requires": { + "ajv": "5", + "bluebird": "^3.5.1", + "chalk": "^2.3.0", + "debug": "^3.1.0", + "dotenv": "^4.0.0", + "fs-extra": "^5.0.0", + "isomorphic-fetch": "^2.2.1", + "js-yaml": "^3.10.0", + "json-stable-stringify": "^1.0.1", + "jsonwebtoken": "^8.1.0", + "lodash": "^4.17.4", + "prisma-json-schema": "^0.0.1", + "replaceall": "^0.1.6", + "scuid": "^1.0.2", + "yaml-ast-parser": "^0.0.40" + } + }, + "source-map-support": { + "version": "0.4.18", + "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.4.18.tgz", + "integrity": "sha512-try0/JqxPLF9nOjvSta7tVondkP5dwgyLDjVoyMDlmjugT2lRZ1OfsrYTkCd2hkDnJTKRbO/Rl3orm8vlsUzbA==", + "dev": true, + "requires": { + "source-map": "^0.5.6" + }, + "dependencies": { + "source-map": { + "version": "0.5.7", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", + "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=", + "dev": true + } + } + }, + "supports-color": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-4.5.0.tgz", + "integrity": "sha1-vnoN5ITexcXN34s9WRJQRJEvY1s=", + "dev": true, + "requires": { + "has-flag": "^2.0.0" + } + }, + "validator": { + "version": "8.2.0", + "resolved": "https://registry.npmjs.org/validator/-/validator-8.2.0.tgz", + "integrity": "sha512-Yw5wW34fSv5spzTXNkokD6S6/Oq92d8q/t14TqsS3fAiA1RYnxSFSIZ+CY3n6PGGRCq5HhJTSepQvFUS2QUDxA==", + "dev": true + }, + "yargs": { + "version": "10.1.1", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-10.1.1.tgz", + "integrity": "sha512-7uRL1HZdCbc1QTP+X8mehOPuCYKC/XTaqAPj7gABLfTt6pgLyVRn3QVte4qhtilZouWCvqd1kipgMKl5tKsFiw==", + "dev": true, + "requires": { + "cliui": "^4.0.0", + "decamelize": "^1.1.1", + "find-up": "^2.1.0", + "get-caller-file": "^1.0.1", + "os-locale": "^2.0.0", + "require-directory": "^2.1.1", + "require-main-filename": "^1.0.1", + "set-blocking": "^2.0.0", + "string-width": "^2.0.0", + "which-module": "^2.0.0", + "y18n": "^3.2.1", + "yargs-parser": "^8.1.0" + } + }, + "yargs-parser": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-8.1.0.tgz", + "integrity": "sha512-yP+6QqN8BmrgW2ggLtTbdrOyBNSI7zBa4IykmiV5R1wl1JWNxQvWhMfMdmzIYtKU7oP3OOInY/tl2ov3BDjnJQ==", + "dev": true, + "requires": { + "camelcase": "^4.1.0" + } } } }, @@ -7459,6 +8611,32 @@ "ps-tree": "^1.1.0" } }, + "pullstream": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/pullstream/-/pullstream-0.4.1.tgz", + "integrity": "sha1-1vs79a7Wl+gxFQ6xACwlo/iuExQ=", + "dev": true, + "requires": { + "over": ">= 0.0.5 < 1", + "readable-stream": "~1.0.31", + "setimmediate": ">= 1.0.2 < 2", + "slice-stream": ">= 1.0.0 < 2" + }, + "dependencies": { + "readable-stream": { + "version": "1.0.34", + "resolved": "http://registry.npmjs.org/readable-stream/-/readable-stream-1.0.34.tgz", + "integrity": "sha1-Elgg40vIQtLyqq+v5MKRbuMsFXw=", + "dev": true, + "requires": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.1", + "isarray": "0.0.1", + "string_decoder": "~0.10.x" + } + } + } + }, "punycode": { "version": "1.3.2", "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.3.2.tgz", @@ -7755,6 +8933,12 @@ "resolved": "https://registry.npmjs.org/require-main-filename/-/require-main-filename-1.0.1.tgz", "integrity": "sha1-l/cXtp1IeE9fUmpsWqj/3aBVpNE=" }, + "require-onct": { + "version": "0.0.2", + "resolved": "https://registry.npmjs.org/require-onct/-/require-onct-0.0.2.tgz", + "integrity": "sha512-YpSFdFNOD/FT+HeTIC54pQoIX5lhsTsxMDrp/oXva3xNFbpGExzwAb4Ph/CRHhcLZ8/1+XNeM/qF//I73kKV/A==", + "dev": true + }, "resolve-cwd": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/resolve-cwd/-/resolve-cwd-2.0.0.tgz", @@ -7897,7 +9081,7 @@ "dependencies": { "commander": { "version": "2.8.1", - "resolved": "https://registry.npmjs.org/commander/-/commander-2.8.1.tgz", + "resolved": "http://registry.npmjs.org/commander/-/commander-2.8.1.tgz", "integrity": "sha1-Br42f+v9oMMwqh4qBy09yXYkJdQ=", "dev": true, "requires": { @@ -8026,6 +9210,12 @@ } } }, + "setimmediate": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/setimmediate/-/setimmediate-1.0.5.tgz", + "integrity": "sha1-KQy7Iy4waULX1+qbg3Mqt4VvgoU=", + "dev": true + }, "setprototypeof": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.1.0.tgz", @@ -8085,6 +9275,29 @@ "is-fullwidth-code-point": "^2.0.0" } }, + "slice-stream": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/slice-stream/-/slice-stream-1.0.0.tgz", + "integrity": "sha1-WzO9ZvATsaf4ZGCwPUY97DmtPqA=", + "dev": true, + "requires": { + "readable-stream": "~1.0.31" + }, + "dependencies": { + "readable-stream": { + "version": "1.0.34", + "resolved": "http://registry.npmjs.org/readable-stream/-/readable-stream-1.0.34.tgz", + "integrity": "sha1-Elgg40vIQtLyqq+v5MKRbuMsFXw=", + "dev": true, + "requires": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.1", + "isarray": "0.0.1", + "string_decoder": "~0.10.x" + } + } + } + }, "snake-case": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/snake-case/-/snake-case-2.1.0.tgz", @@ -8506,7 +9719,7 @@ }, "table": { "version": "4.0.3", - "resolved": "https://registry.npmjs.org/table/-/table-4.0.3.tgz", + "resolved": "http://registry.npmjs.org/table/-/table-4.0.3.tgz", "integrity": "sha512-S7rnFITmBH1EnyKcvxBh1LjYeQMmnZtCXSEbHcH6S0NoKit24ZuFO/T1vDcLdYsLQkM188PVVhQmzKIuThNkKg==", "dev": true, "requires": { @@ -8519,9 +9732,9 @@ }, "dependencies": { "ajv": { - "version": "6.5.3", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.5.3.tgz", - "integrity": "sha512-LqZ9wY+fx3UMiiPd741yB2pj3hhil+hQc8taf4o2QGRFpWgZ2V5C8HA165DY9sS3fJwsk7uT7ZlFEyC3Ig3lLg==", + "version": "6.5.4", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.5.4.tgz", + "integrity": "sha512-4Wyjt8+t6YszqaXnLDfMmG/8AlO5Zbcsy3ATHncCzjW/NoPzAId8AK6749Ybjmdt+kUY1gP60fCu46oDxPv/mg==", "dev": true, "requires": { "fast-deep-equal": "^2.0.1", @@ -8545,17 +9758,17 @@ } }, "tar-stream": { - "version": "1.6.1", - "resolved": "https://registry.npmjs.org/tar-stream/-/tar-stream-1.6.1.tgz", - "integrity": "sha512-IFLM5wp3QrJODQFPm6/to3LJZrONdBY/otxcvDIQzu217zKye6yVR3hhi9lAjrC2Z+m/j5oDxMPb1qcd8cIvpA==", + "version": "1.6.2", + "resolved": "https://registry.npmjs.org/tar-stream/-/tar-stream-1.6.2.tgz", + "integrity": "sha512-rzS0heiNf8Xn7/mpdSVVSMAWAoy9bfb1WOTYC78Z0UQKeKa/CWS8FOq0lKGNa8DWKAn9gxjCvMLYc5PGXYlK2A==", "dev": true, "requires": { "bl": "^1.0.0", - "buffer-alloc": "^1.1.0", + "buffer-alloc": "^1.2.0", "end-of-stream": "^1.0.0", "fs-constants": "^1.0.0", "readable-stream": "^2.3.0", - "to-buffer": "^1.1.0", + "to-buffer": "^1.1.1", "xtend": "^4.0.0" }, "dependencies": { @@ -8567,7 +9780,7 @@ }, "readable-stream": { "version": "2.3.6", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz", + "resolved": "http://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz", "integrity": "sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw==", "dev": true, "requires": { @@ -8744,6 +9957,12 @@ } } }, + "traverse": { + "version": "0.3.9", + "resolved": "https://registry.npmjs.org/traverse/-/traverse-0.3.9.tgz", + "integrity": "sha1-cXuPIgzAu3tE5AUUwisui7xw2Lk=", + "dev": true + }, "traverse-chain": { "version": "0.1.0", "resolved": "https://registry.npmjs.org/traverse-chain/-/traverse-chain-0.1.0.tgz", @@ -8791,6 +10010,40 @@ "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.9.3.tgz", "integrity": "sha512-4krF8scpejhaOgqzBEcGM7yDIEfi0/8+8zDRZhNZZ2kjmHJ4hv3zCbQWxoJGz1iw5U0Jl0nma13xzHXcncMavQ==" }, + "tslint-config-prettier": { + "version": "1.15.0", + "resolved": "https://registry.npmjs.org/tslint-config-prettier/-/tslint-config-prettier-1.15.0.tgz", + "integrity": "sha512-06CgrHJxJmNYVgsmeMoa1KXzQRoOdvfkqnJth6XUkNeOz707qxN0WfxfhYwhL5kXHHbYJRby2bqAPKwThlZPhw==", + "dev": true + }, + "tslint-eslint-rules": { + "version": "5.4.0", + "resolved": "https://registry.npmjs.org/tslint-eslint-rules/-/tslint-eslint-rules-5.4.0.tgz", + "integrity": "sha512-WlSXE+J2vY/VPgIcqQuijMQiel+UtmXS+4nvK4ZzlDiqBfXse8FAvkNnTcYhnQyOTW5KFM+uRRGXxYhFpuBc6w==", + "dev": true, + "requires": { + "doctrine": "0.7.2", + "tslib": "1.9.0", + "tsutils": "^3.0.0" + }, + "dependencies": { + "tslib": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.9.0.tgz", + "integrity": "sha512-f/qGG2tUkrISBlQZEjEqoZ3B2+npJjIf04H1wuAv9iA8i04Icp+61KRXxFdha22670NJopsZCIjhC3SnjPRKrQ==", + "dev": true + } + } + }, + "tsutils": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/tsutils/-/tsutils-3.0.0.tgz", + "integrity": "sha512-LjHBWR0vWAUHWdIAoTjoqi56Kz+FDKBgVEuL+gVPG/Pv7QW5IdaDDeK9Txlr6U0Cmckp5EgCIq1T25qe3J6hyw==", + "dev": true, + "requires": { + "tslib": "^1.8.1" + } + }, "tunnel-agent": { "version": "0.6.0", "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz", @@ -8814,6 +10067,12 @@ "mime-types": "~2.1.18" } }, + "typedarray": { + "version": "0.0.6", + "resolved": "https://registry.npmjs.org/typedarray/-/typedarray-0.0.6.tgz", + "integrity": "sha1-hnrHTjhkGHsdPUfZlqeOxciDB3c=", + "dev": true + }, "ultron": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/ultron/-/ultron-1.1.1.tgz", @@ -8837,7 +10096,7 @@ }, "buffer": { "version": "3.6.0", - "resolved": "https://registry.npmjs.org/buffer/-/buffer-3.6.0.tgz", + "resolved": "http://registry.npmjs.org/buffer/-/buffer-3.6.0.tgz", "integrity": "sha1-pyyTb3e5a/UvX357RnGAYoVR3vs=", "dev": true, "requires": { @@ -8974,6 +10233,34 @@ } } }, + "unzip": { + "version": "0.1.11", + "resolved": "https://registry.npmjs.org/unzip/-/unzip-0.1.11.tgz", + "integrity": "sha1-iXScY7BY19kNYZ+GuYqhU107l/A=", + "dev": true, + "requires": { + "binary": ">= 0.3.0 < 1", + "fstream": ">= 0.1.30 < 1", + "match-stream": ">= 0.0.2 < 1", + "pullstream": ">= 0.4.1 < 1", + "readable-stream": "~1.0.31", + "setimmediate": ">= 1.0.1 < 2" + }, + "dependencies": { + "readable-stream": { + "version": "1.0.34", + "resolved": "http://registry.npmjs.org/readable-stream/-/readable-stream-1.0.34.tgz", + "integrity": "sha1-Elgg40vIQtLyqq+v5MKRbuMsFXw=", + "dev": true, + "requires": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.1", + "isarray": "0.0.1", + "string_decoder": "~0.10.x" + } + } + } + }, "unzip-response": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/unzip-response/-/unzip-response-2.0.1.tgz", @@ -9440,7 +10727,7 @@ }, "readable-stream": { "version": "2.3.6", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz", + "resolved": "http://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz", "integrity": "sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw==", "dev": true, "requires": { diff --git a/api/package.json b/api/package.json index 9e5311fb1..bb93c6fdd 100644 --- a/api/package.json +++ b/api/package.json @@ -8,6 +8,7 @@ }, "dependencies": { "@azure/storage-blob": "^10.0.0-preview", + "axios": "^0.18.0", "azure-storage": "^2.10.1", "cors": "^2.8.4", "debug": "^4.0.1", @@ -27,6 +28,6 @@ "devDependencies": { "graphql-cli": "^2.16.5", "npm-run-all": "4.1.3", - "prisma": "^1.16.4" + "prisma": "^1.17.1" } } diff --git a/api/src/generated/prisma.graphql b/api/src/generated/prisma.graphql index b70971c89..171d8462a 100644 --- a/api/src/generated/prisma.graphql +++ b/api/src/generated/prisma.graphql @@ -1,5 +1,5 @@ # source: http://localhost:4466 -# timestamp: Wed Sep 26 2018 23:15:45 GMT-0700 (Pacific Daylight Time) +# timestamp: Thu Sep 27 2018 08:38:09 GMT-0700 (Pacific Daylight Time) type AggregateFBStatus { count: Int! @@ -25,6 +25,10 @@ type AggregateTeamMembers { count: Int! } +type AggregateTeamMemberStatus { + count: Int! +} + type AggregateTeamProfile { count: Int! } @@ -516,6 +520,7 @@ type Member implements Node { teams(where: TeamMembersWhereInput, orderBy: TeamMembersOrderByInput, skip: Int, after: String, before: String, first: Int, last: Int): [TeamMembers!] authId: String! visible: Boolean! + status(where: MemberStatusWhereInput, orderBy: MemberStatusOrderByInput, skip: Int, after: String, before: String, first: Int, last: Int): [MemberStatus!] createdAt: DateTime! } @@ -534,6 +539,7 @@ input MemberCreateInput { authId: String! visible: Boolean teams: TeamMembersCreateManyWithoutMemberInput + status: MemberStatusCreateManyInput } input MemberCreateOneWithoutTeamsInput { @@ -545,6 +551,7 @@ input MemberCreateWithoutTeamsInput { alias: String authId: String! visible: Boolean + status: MemberStatusCreateManyInput } """An edge in a connection.""" @@ -807,6 +814,7 @@ input MemberUpdateInput { authId: String visible: Boolean teams: TeamMembersUpdateManyWithoutMemberInput + status: MemberStatusUpdateManyInput } input MemberUpdateOneWithoutTeamsInput { @@ -822,6 +830,7 @@ input MemberUpdateWithoutTeamsDataInput { alias: String authId: String visible: Boolean + status: MemberStatusUpdateManyInput } input MemberUpsertWithoutTeamsInput { @@ -987,6 +996,9 @@ input MemberWhereInput { teams_every: TeamMembersWhereInput teams_some: TeamMembersWhereInput teams_none: TeamMembersWhereInput + status_every: MemberStatusWhereInput + status_some: MemberStatusWhereInput + status_none: MemberStatusWhereInput } input MemberWhereUniqueInput { @@ -1001,16 +1013,20 @@ type Mutation { createFinancialBeings(data: FinancialBeingsCreateInput!): FinancialBeings! createTeamProfile(data: TeamProfileCreateInput!): TeamProfile! createWallet(data: WalletCreateInput!): Wallet! - createFBStatus(data: FBStatusCreateInput!): FBStatus! - createTeamStatus(data: TeamStatusCreateInput!): TeamStatus! createMemberStatus(data: MemberStatusCreateInput!): MemberStatus! + createFBStatus(data: FBStatusCreateInput!): FBStatus! + createTeamMemberStatus(data: TeamMemberStatusCreateInput!): TeamMemberStatus! + createTeamStatus(data: TeamStatusCreateInput!): TeamStatus! updateTeam(data: TeamUpdateInput!, where: TeamWhereUniqueInput!): Team + updateTeamMembers(data: TeamMembersUpdateInput!, where: TeamMembersWhereUniqueInput!): TeamMembers updateMember(data: MemberUpdateInput!, where: MemberWhereUniqueInput!): Member updateFinancialBeings(data: FinancialBeingsUpdateInput!, where: FinancialBeingsWhereUniqueInput!): FinancialBeings deleteTeam(where: TeamWhereUniqueInput!): Team + deleteTeamMembers(where: TeamMembersWhereUniqueInput!): TeamMembers deleteMember(where: MemberWhereUniqueInput!): Member deleteFinancialBeings(where: FinancialBeingsWhereUniqueInput!): FinancialBeings upsertTeam(where: TeamWhereUniqueInput!, create: TeamCreateInput!, update: TeamUpdateInput!): Team! + upsertTeamMembers(where: TeamMembersWhereUniqueInput!, create: TeamMembersCreateInput!, update: TeamMembersUpdateInput!): TeamMembers! upsertMember(where: MemberWhereUniqueInput!, create: MemberCreateInput!, update: MemberUpdateInput!): Member! upsertFinancialBeings(where: FinancialBeingsWhereUniqueInput!, create: FinancialBeingsCreateInput!, update: FinancialBeingsUpdateInput!): FinancialBeings! updateManyTeams(data: TeamUpdateInput!, where: TeamWhereInput): BatchPayload! @@ -1019,18 +1035,20 @@ type Mutation { updateManyFinancialBeingses(data: FinancialBeingsUpdateInput!, where: FinancialBeingsWhereInput): BatchPayload! updateManyTeamProfiles(data: TeamProfileUpdateInput!, where: TeamProfileWhereInput): BatchPayload! updateManyWallets(data: WalletUpdateInput!, where: WalletWhereInput): BatchPayload! - updateManyFBStatuses(data: FBStatusUpdateInput!, where: FBStatusWhereInput): BatchPayload! - updateManyTeamStatuses(data: TeamStatusUpdateInput!, where: TeamStatusWhereInput): BatchPayload! updateManyMemberStatuses(data: MemberStatusUpdateInput!, where: MemberStatusWhereInput): BatchPayload! + updateManyFBStatuses(data: FBStatusUpdateInput!, where: FBStatusWhereInput): BatchPayload! + updateManyTeamMemberStatuses(data: TeamMemberStatusUpdateInput!, where: TeamMemberStatusWhereInput): BatchPayload! + updateManyTeamStatuses(data: TeamStatusUpdateInput!, where: TeamStatusWhereInput): BatchPayload! deleteManyTeams(where: TeamWhereInput): BatchPayload! deleteManyTeamMemberses(where: TeamMembersWhereInput): BatchPayload! deleteManyMembers(where: MemberWhereInput): BatchPayload! deleteManyFinancialBeingses(where: FinancialBeingsWhereInput): BatchPayload! deleteManyTeamProfiles(where: TeamProfileWhereInput): BatchPayload! deleteManyWallets(where: WalletWhereInput): BatchPayload! - deleteManyFBStatuses(where: FBStatusWhereInput): BatchPayload! - deleteManyTeamStatuses(where: TeamStatusWhereInput): BatchPayload! deleteManyMemberStatuses(where: MemberStatusWhereInput): BatchPayload! + deleteManyFBStatuses(where: FBStatusWhereInput): BatchPayload! + deleteManyTeamMemberStatuses(where: TeamMemberStatusWhereInput): BatchPayload! + deleteManyTeamStatuses(where: TeamStatusWhereInput): BatchPayload! } enum MutationType { @@ -1067,10 +1085,12 @@ type Query { financialBeingses(where: FinancialBeingsWhereInput, orderBy: FinancialBeingsOrderByInput, skip: Int, after: String, before: String, first: Int, last: Int): [FinancialBeings]! teamProfiles(where: TeamProfileWhereInput, orderBy: TeamProfileOrderByInput, skip: Int, after: String, before: String, first: Int, last: Int): [TeamProfile]! wallets(where: WalletWhereInput, orderBy: WalletOrderByInput, skip: Int, after: String, before: String, first: Int, last: Int): [Wallet]! - fBStatuses(where: FBStatusWhereInput, orderBy: FBStatusOrderByInput, skip: Int, after: String, before: String, first: Int, last: Int): [FBStatus]! - teamStatuses(where: TeamStatusWhereInput, orderBy: TeamStatusOrderByInput, skip: Int, after: String, before: String, first: Int, last: Int): [TeamStatus]! memberStatuses(where: MemberStatusWhereInput, orderBy: MemberStatusOrderByInput, skip: Int, after: String, before: String, first: Int, last: Int): [MemberStatus]! + fBStatuses(where: FBStatusWhereInput, orderBy: FBStatusOrderByInput, skip: Int, after: String, before: String, first: Int, last: Int): [FBStatus]! + teamMemberStatuses(where: TeamMemberStatusWhereInput, orderBy: TeamMemberStatusOrderByInput, skip: Int, after: String, before: String, first: Int, last: Int): [TeamMemberStatus]! + teamStatuses(where: TeamStatusWhereInput, orderBy: TeamStatusOrderByInput, skip: Int, after: String, before: String, first: Int, last: Int): [TeamStatus]! team(where: TeamWhereUniqueInput!): Team + teamMembers(where: TeamMembersWhereUniqueInput!): TeamMembers member(where: MemberWhereUniqueInput!): Member financialBeings(where: FinancialBeingsWhereUniqueInput!): FinancialBeings teamsConnection(where: TeamWhereInput, orderBy: TeamOrderByInput, skip: Int, after: String, before: String, first: Int, last: Int): TeamConnection! @@ -1079,9 +1099,10 @@ type Query { financialBeingsesConnection(where: FinancialBeingsWhereInput, orderBy: FinancialBeingsOrderByInput, skip: Int, after: String, before: String, first: Int, last: Int): FinancialBeingsConnection! teamProfilesConnection(where: TeamProfileWhereInput, orderBy: TeamProfileOrderByInput, skip: Int, after: String, before: String, first: Int, last: Int): TeamProfileConnection! walletsConnection(where: WalletWhereInput, orderBy: WalletOrderByInput, skip: Int, after: String, before: String, first: Int, last: Int): WalletConnection! - fBStatusesConnection(where: FBStatusWhereInput, orderBy: FBStatusOrderByInput, skip: Int, after: String, before: String, first: Int, last: Int): FBStatusConnection! - teamStatusesConnection(where: TeamStatusWhereInput, orderBy: TeamStatusOrderByInput, skip: Int, after: String, before: String, first: Int, last: Int): TeamStatusConnection! memberStatusesConnection(where: MemberStatusWhereInput, orderBy: MemberStatusOrderByInput, skip: Int, after: String, before: String, first: Int, last: Int): MemberStatusConnection! + fBStatusesConnection(where: FBStatusWhereInput, orderBy: FBStatusOrderByInput, skip: Int, after: String, before: String, first: Int, last: Int): FBStatusConnection! + teamMemberStatusesConnection(where: TeamMemberStatusWhereInput, orderBy: TeamMemberStatusOrderByInput, skip: Int, after: String, before: String, first: Int, last: Int): TeamMemberStatusConnection! + teamStatusesConnection(where: TeamStatusWhereInput, orderBy: TeamStatusOrderByInput, skip: Int, after: String, before: String, first: Int, last: Int): TeamStatusConnection! """Fetches an object given its ID""" node( @@ -1099,8 +1120,9 @@ enum Role { enum Status { ACTIVE INACTIVE - APPLY - INVITE + APPLIED + INVITED + REMOVED } type Subscription { @@ -1110,9 +1132,10 @@ type Subscription { financialBeings(where: FinancialBeingsSubscriptionWhereInput): FinancialBeingsSubscriptionPayload teamProfile(where: TeamProfileSubscriptionWhereInput): TeamProfileSubscriptionPayload wallet(where: WalletSubscriptionWhereInput): WalletSubscriptionPayload - fBStatus(where: FBStatusSubscriptionWhereInput): FBStatusSubscriptionPayload - teamStatus(where: TeamStatusSubscriptionWhereInput): TeamStatusSubscriptionPayload memberStatus(where: MemberStatusSubscriptionWhereInput): MemberStatusSubscriptionPayload + fBStatus(where: FBStatusSubscriptionWhereInput): FBStatusSubscriptionPayload + teamMemberStatus(where: TeamMemberStatusSubscriptionWhereInput): TeamMemberStatusSubscriptionPayload + teamStatus(where: TeamStatusSubscriptionWhereInput): TeamStatusSubscriptionPayload } type Team implements Node { @@ -1188,11 +1211,14 @@ type TeamEdge { cursor: String! } -type TeamMembers { +type TeamMembers implements Node { + id: ID! member(where: MemberWhereInput): Member team(where: TeamWhereInput): Team role: Role - status(where: MemberStatusWhereInput, orderBy: MemberStatusOrderByInput, skip: Int, after: String, before: String, first: Int, last: Int): [MemberStatus!] + status(where: TeamMemberStatusWhereInput, orderBy: TeamMemberStatusOrderByInput, skip: Int, after: String, before: String, first: Int, last: Int): [TeamMemberStatus!] + email: String + createdAt: DateTime! updatedAt: DateTime! } @@ -1208,29 +1234,34 @@ type TeamMembersConnection { input TeamMembersCreateInput { role: Role + email: String member: MemberCreateOneWithoutTeamsInput team: TeamCreateOneWithoutMembersInput - status: MemberStatusCreateManyInput + status: TeamMemberStatusCreateManyInput } input TeamMembersCreateManyWithoutMemberInput { create: [TeamMembersCreateWithoutMemberInput!] + connect: [TeamMembersWhereUniqueInput!] } input TeamMembersCreateManyWithoutTeamInput { create: [TeamMembersCreateWithoutTeamInput!] + connect: [TeamMembersWhereUniqueInput!] } input TeamMembersCreateWithoutMemberInput { role: Role + email: String team: TeamCreateOneWithoutMembersInput - status: MemberStatusCreateManyInput + status: TeamMemberStatusCreateManyInput } input TeamMembersCreateWithoutTeamInput { role: Role + email: String member: MemberCreateOneWithoutTeamsInput - status: MemberStatusCreateManyInput + status: TeamMemberStatusCreateManyInput } """An edge in a connection.""" @@ -1243,18 +1274,23 @@ type TeamMembersEdge { } enum TeamMembersOrderByInput { - role_ASC - role_DESC - updatedAt_ASC - updatedAt_DESC id_ASC id_DESC + role_ASC + role_DESC + email_ASC + email_DESC createdAt_ASC createdAt_DESC + updatedAt_ASC + updatedAt_DESC } type TeamMembersPreviousValues { + id: ID! role: Role + email: String + createdAt: DateTime! updatedAt: DateTime! } @@ -1297,19 +1333,250 @@ input TeamMembersSubscriptionWhereInput { node: TeamMembersWhereInput } +type TeamMemberStatus { + status: Status + reason: String + createdAt: DateTime! +} + +"""A connection to a list of items.""" +type TeamMemberStatusConnection { + """Information to aid in pagination.""" + pageInfo: PageInfo! + + """A list of edges.""" + edges: [TeamMemberStatusEdge]! + aggregate: AggregateTeamMemberStatus! +} + +input TeamMemberStatusCreateInput { + status: Status + reason: String +} + +input TeamMemberStatusCreateManyInput { + create: [TeamMemberStatusCreateInput!] +} + +"""An edge in a connection.""" +type TeamMemberStatusEdge { + """The item at the end of the edge.""" + node: TeamMemberStatus! + + """A cursor for use in pagination.""" + cursor: String! +} + +enum TeamMemberStatusOrderByInput { + status_ASC + status_DESC + reason_ASC + reason_DESC + createdAt_ASC + createdAt_DESC + id_ASC + id_DESC + updatedAt_ASC + updatedAt_DESC +} + +type TeamMemberStatusPreviousValues { + status: Status + reason: String + createdAt: DateTime! +} + +type TeamMemberStatusSubscriptionPayload { + mutation: MutationType! + node: TeamMemberStatus + updatedFields: [String!] + previousValues: TeamMemberStatusPreviousValues +} + +input TeamMemberStatusSubscriptionWhereInput { + """Logical AND on all given filters.""" + AND: [TeamMemberStatusSubscriptionWhereInput!] + + """Logical OR on all given filters.""" + OR: [TeamMemberStatusSubscriptionWhereInput!] + + """Logical NOT on all given filters combined by AND.""" + NOT: [TeamMemberStatusSubscriptionWhereInput!] + + """ + The subscription event gets dispatched when it's listed in mutation_in + """ + mutation_in: [MutationType!] + + """ + The subscription event gets only dispatched when one of the updated fields names is included in this list + """ + updatedFields_contains: String + + """ + The subscription event gets only dispatched when all of the field names included in this list have been updated + """ + updatedFields_contains_every: [String!] + + """ + The subscription event gets only dispatched when some of the field names included in this list have been updated + """ + updatedFields_contains_some: [String!] + node: TeamMemberStatusWhereInput +} + +input TeamMemberStatusUpdateInput { + status: Status + reason: String +} + +input TeamMemberStatusUpdateManyInput { + create: [TeamMemberStatusCreateInput!] +} + +input TeamMemberStatusWhereInput { + """Logical AND on all given filters.""" + AND: [TeamMemberStatusWhereInput!] + + """Logical OR on all given filters.""" + OR: [TeamMemberStatusWhereInput!] + + """Logical NOT on all given filters combined by AND.""" + NOT: [TeamMemberStatusWhereInput!] + status: Status + + """All values that are not equal to given value.""" + status_not: Status + + """All values that are contained in given list.""" + status_in: [Status!] + + """All values that are not contained in given list.""" + status_not_in: [Status!] + reason: String + + """All values that are not equal to given value.""" + reason_not: String + + """All values that are contained in given list.""" + reason_in: [String!] + + """All values that are not contained in given list.""" + reason_not_in: [String!] + + """All values less than the given value.""" + reason_lt: String + + """All values less than or equal the given value.""" + reason_lte: String + + """All values greater than the given value.""" + reason_gt: String + + """All values greater than or equal the given value.""" + reason_gte: String + + """All values containing the given string.""" + reason_contains: String + + """All values not containing the given string.""" + reason_not_contains: String + + """All values starting with the given string.""" + reason_starts_with: String + + """All values not starting with the given string.""" + reason_not_starts_with: String + + """All values ending with the given string.""" + reason_ends_with: String + + """All values not ending with the given string.""" + reason_not_ends_with: String + createdAt: DateTime + + """All values that are not equal to given value.""" + createdAt_not: DateTime + + """All values that are contained in given list.""" + createdAt_in: [DateTime!] + + """All values that are not contained in given list.""" + createdAt_not_in: [DateTime!] + + """All values less than the given value.""" + createdAt_lt: DateTime + + """All values less than or equal the given value.""" + createdAt_lte: DateTime + + """All values greater than the given value.""" + createdAt_gt: DateTime + + """All values greater than or equal the given value.""" + createdAt_gte: DateTime +} + input TeamMembersUpdateInput { role: Role + email: String member: MemberUpdateOneWithoutTeamsInput team: TeamUpdateOneWithoutMembersInput - status: MemberStatusUpdateManyInput + status: TeamMemberStatusUpdateManyInput } input TeamMembersUpdateManyWithoutMemberInput { create: [TeamMembersCreateWithoutMemberInput!] + connect: [TeamMembersWhereUniqueInput!] + disconnect: [TeamMembersWhereUniqueInput!] + delete: [TeamMembersWhereUniqueInput!] + update: [TeamMembersUpdateWithWhereUniqueWithoutMemberInput!] + upsert: [TeamMembersUpsertWithWhereUniqueWithoutMemberInput!] } input TeamMembersUpdateManyWithoutTeamInput { create: [TeamMembersCreateWithoutTeamInput!] + connect: [TeamMembersWhereUniqueInput!] + disconnect: [TeamMembersWhereUniqueInput!] + delete: [TeamMembersWhereUniqueInput!] + update: [TeamMembersUpdateWithWhereUniqueWithoutTeamInput!] + upsert: [TeamMembersUpsertWithWhereUniqueWithoutTeamInput!] +} + +input TeamMembersUpdateWithoutMemberDataInput { + role: Role + email: String + team: TeamUpdateOneWithoutMembersInput + status: TeamMemberStatusUpdateManyInput +} + +input TeamMembersUpdateWithoutTeamDataInput { + role: Role + email: String + member: MemberUpdateOneWithoutTeamsInput + status: TeamMemberStatusUpdateManyInput +} + +input TeamMembersUpdateWithWhereUniqueWithoutMemberInput { + where: TeamMembersWhereUniqueInput! + data: TeamMembersUpdateWithoutMemberDataInput! +} + +input TeamMembersUpdateWithWhereUniqueWithoutTeamInput { + where: TeamMembersWhereUniqueInput! + data: TeamMembersUpdateWithoutTeamDataInput! +} + +input TeamMembersUpsertWithWhereUniqueWithoutMemberInput { + where: TeamMembersWhereUniqueInput! + update: TeamMembersUpdateWithoutMemberDataInput! + create: TeamMembersCreateWithoutMemberInput! +} + +input TeamMembersUpsertWithWhereUniqueWithoutTeamInput { + where: TeamMembersWhereUniqueInput! + update: TeamMembersUpdateWithoutTeamDataInput! + create: TeamMembersCreateWithoutTeamInput! } input TeamMembersWhereInput { @@ -1321,6 +1588,46 @@ input TeamMembersWhereInput { """Logical NOT on all given filters combined by AND.""" NOT: [TeamMembersWhereInput!] + id: ID + + """All values that are not equal to given value.""" + id_not: ID + + """All values that are contained in given list.""" + id_in: [ID!] + + """All values that are not contained in given list.""" + id_not_in: [ID!] + + """All values less than the given value.""" + id_lt: ID + + """All values less than or equal the given value.""" + id_lte: ID + + """All values greater than the given value.""" + id_gt: ID + + """All values greater than or equal the given value.""" + id_gte: ID + + """All values containing the given string.""" + id_contains: ID + + """All values not containing the given string.""" + id_not_contains: ID + + """All values starting with the given string.""" + id_starts_with: ID + + """All values not starting with the given string.""" + id_not_starts_with: ID + + """All values ending with the given string.""" + id_ends_with: ID + + """All values not ending with the given string.""" + id_not_ends_with: ID role: Role """All values that are not equal to given value.""" @@ -1331,6 +1638,68 @@ input TeamMembersWhereInput { """All values that are not contained in given list.""" role_not_in: [Role!] + email: String + + """All values that are not equal to given value.""" + email_not: String + + """All values that are contained in given list.""" + email_in: [String!] + + """All values that are not contained in given list.""" + email_not_in: [String!] + + """All values less than the given value.""" + email_lt: String + + """All values less than or equal the given value.""" + email_lte: String + + """All values greater than the given value.""" + email_gt: String + + """All values greater than or equal the given value.""" + email_gte: String + + """All values containing the given string.""" + email_contains: String + + """All values not containing the given string.""" + email_not_contains: String + + """All values starting with the given string.""" + email_starts_with: String + + """All values not starting with the given string.""" + email_not_starts_with: String + + """All values ending with the given string.""" + email_ends_with: String + + """All values not ending with the given string.""" + email_not_ends_with: String + createdAt: DateTime + + """All values that are not equal to given value.""" + createdAt_not: DateTime + + """All values that are contained in given list.""" + createdAt_in: [DateTime!] + + """All values that are not contained in given list.""" + createdAt_not_in: [DateTime!] + + """All values less than the given value.""" + createdAt_lt: DateTime + + """All values less than or equal the given value.""" + createdAt_lte: DateTime + + """All values greater than the given value.""" + createdAt_gt: DateTime + + """All values greater than or equal the given value.""" + createdAt_gte: DateTime updatedAt: DateTime """All values that are not equal to given value.""" @@ -1355,9 +1724,13 @@ input TeamMembersWhereInput { updatedAt_gte: DateTime member: MemberWhereInput team: TeamWhereInput - status_every: MemberStatusWhereInput - status_some: MemberStatusWhereInput - status_none: MemberStatusWhereInput + status_every: TeamMemberStatusWhereInput + status_some: TeamMemberStatusWhereInput + status_none: TeamMemberStatusWhereInput +} + +input TeamMembersWhereUniqueInput { + id: ID } enum TeamOrderByInput { diff --git a/api/src/index.js b/api/src/index.js index 6c61ceed8..465712204 100644 --- a/api/src/index.js +++ b/api/src/index.js @@ -87,9 +87,18 @@ const resolvers = { async teamByName(parent, { name }, ctx, info) { return ctx.db.query.team({ where: { name: name } }, `{ name }`) }, - async teamsByOwner(parent, { ownerId }, ctx, info) { - console.log('teamsByOwner: ', ctx.user) - return ctx.db.query.teams({where: { owner: ownerId }, orderBy:'updatedAt_DESC'}, TEAMS_FRAGMENT) + 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 teamsByRole(parent, args, ctx, info) { + const authId = ctx.request.user.sub + let teamAdmin = await ctx.db.query.teams({where: {members_some: {OR: [{role: 'ADMIN'}, {role: 'OWNER'}], AND: [{member: {authId: authId}}]}}, orderBy: 'updatedAt_DESC'}, TEAMS_FRAGMENT) + console.log('teamsByRole teamAdmin: ', teamAdmin) + return teamAdmin }, async owner(parent, args, ctx, info) { console.log('resolver.query.owner ctx: ', ctxMember(ctx)) @@ -124,8 +133,9 @@ const resolvers = { } return member }, - async createTeam(parent, { name, slug, owner }, ctx, info) { - return ctx.db.mutation.createTeam({ data: { name: name, slug: slug, owner: owner, members: { create: { member:{ connect:{ authId: owner} }, role: 'OWNER' } }, profile:{create:{avatar:"a"} } } }, TEAMS_FRAGMENT) + async createTeam(parent, { name, slug }, ctx, info) { + 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) .catch((err) => { console.log('createTeam error: ', err) return err diff --git a/api/src/schema.graphql b/api/src/schema.graphql index f27657ccb..6b835e9ec 100644 --- a/api/src/schema.graphql +++ b/api/src/schema.graphql @@ -10,7 +10,9 @@ type Query { teamDetails: [Team!]! @isAuthenticated teamByName(name: String!): Team teamById(teamId: String!): Team - teamsByOwner(ownerId: String!): [Team!]! @isAuthenticated + teamWithRole(teamId: String!, role: String!): Team + teamsByOwner: [Team!]! @isAuthenticated + teamsByRole: [Team!]! @isAuthenticated owner: Member @isAuthenticated currentMember: Member @isAuthenticated member(authId: String!): Member @isAuthenticated @@ -18,10 +20,13 @@ type Query { type Mutation { authenticate(idToken: String!): Member - createTeam(name: String!, slug: String!, owner: String!): Team @isAuthenticated - updateTeamProfile(slug: String!, owner: String!, description: String, motto: String, avatar: String, banner: String): Team @isAuthenticated + createTeam(name: String!, slug: String!): Team @isAuthenticated + updateTeamProfile(slug: String!, description: String, motto: String, avatar: String, banner: String): Team @isAuthenticated deleteTeam(slug: String!): Team @isAuthenticated @isOwner(type: "Team") getAzureSAS(teamSlug: String!):String + sendMemberInviteSG(email: String!, teamId: String!): String + confirmMember(token: String!): String + removeMember(email: String!, teamId: String!): String } fragment MemberInfo on Member { diff --git a/client/src/App.js b/client/src/App.js index 1fd6e8cb8..8ed26d1a8 100644 --- a/client/src/App.js +++ b/client/src/App.js @@ -64,7 +64,7 @@ wsClient.onReconnected(() => { }) const httpLink = new HttpLink({ uri: graphqlEndpoint }) -const httpUserLink = new HttpLink({ uri: 'http://104.43.240.255:4000/graphql' }) +const httpUserLink = new HttpLink({ uri: 'https://users-api.advancedalgos.net/graphql' }) const link = split( ({ query }) => { diff --git a/client/src/graphql/teams/CreateTeamMutation.js b/client/src/graphql/teams/CreateTeamMutation.js index 3d0324230..1caa8d47c 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!, $owner: String!) { - createTeam(name: $name, slug: $slug, owner: $owner) { + mutation CreateTeamMutation($name: String!, $slug: String!) { + createTeam(name: $name, slug: $slug) { id name slug diff --git a/client/src/graphql/teams/GetTeamsByOwnerQuery.js b/client/src/graphql/teams/GetTeamsByOwnerQuery.js index d15834b34..7f10b0b07 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($authId: String!) { - teamsByOwner(ownerId: $authId) { + query teamsByOwnerQuery { + teamsByOwner { id name slug diff --git a/client/src/graphql/teams/UpdateTeamProfileMutation.js b/client/src/graphql/teams/UpdateTeamProfileMutation.js index cd5e26256..08b229b98 100644 --- a/client/src/graphql/teams/UpdateTeamProfileMutation.js +++ b/client/src/graphql/teams/UpdateTeamProfileMutation.js @@ -4,7 +4,6 @@ import gql from 'graphql-tag' const UPDATE_TEAM_PROFILE = gql` mutation UpdateTeamProfileMutation( $slug: String!, - $owner: String!, $description: String, $motto: String, $avatar: String, @@ -12,7 +11,6 @@ const UPDATE_TEAM_PROFILE = gql` ) { updateTeamProfile( slug: $slug, - owner: $owner, description: $description, motto: $motto avatar: $avatar, diff --git a/client/src/views/common/UploadImage.js b/client/src/views/common/UploadImage.js index 9aff6ce47..a386f1022 100644 --- a/client/src/views/common/UploadImage.js +++ b/client/src/views/common/UploadImage.js @@ -111,7 +111,7 @@ class UploadImage extends Component { } render () { - const { classes, team, authId, handleAvatar, handleBanner } = this.props + 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) { @@ -128,7 +128,7 @@ class UploadImage extends Component { let handleImage this.state.edit === 'avatar' ? handleImage = handleAvatar : handleImage = handleBanner - console.log('UploadImage', team, avatar, banner, authId, this.state) + console.log('UploadImage', team, avatar, banner, this.state) return (
@@ -337,7 +337,6 @@ class UploadImage extends Component { UploadImage.propTypes = { classes: PropTypes.object.isRequired, team: PropTypes.object, - authId: PropTypes.string.isRequired, handleAvatar: PropTypes.func.isRequired, handleBanner: PropTypes.func.isRequired } diff --git a/client/src/views/dashboard/components/CreateTeamDialog.js b/client/src/views/dashboard/components/CreateTeamDialog.js index d215581a4..9b2117c04 100644 --- a/client/src/views/dashboard/components/CreateTeamDialog.js +++ b/client/src/views/dashboard/components/CreateTeamDialog.js @@ -56,16 +56,11 @@ export class CreateTeamDialog extends Component { } render () { - const { classes, authId } = this.props + const { classes } = this.props return ( {(createTeam, { loading, error, data }) => { let errors @@ -131,7 +126,7 @@ export class CreateTeamDialog extends Component {
diff --git a/client/src/views/dashboard/components/ManageTeamsList.js b/client/src/views/dashboard/components/ManageTeamsList.js index d72921486..235d6a5ba 100644 --- a/client/src/views/dashboard/components/ManageTeamsList.js +++ b/client/src/views/dashboard/components/ManageTeamsList.js @@ -69,7 +69,6 @@ export const ManageTeamsList = ({ classes, user = null }) => { {({ loading, error, data }) => { console.log('GET_TEAMS_BY_OWNER: ', loading, error, data) From 87403b336e428c6fea3095aa4b01051a81282978 Mon Sep 17 00:00:00 2001 From: Barry Low Date: Thu, 27 Sep 2018 20:27:50 -0700 Subject: [PATCH 03/11] Server: Add sendgrid member invite --- api/src/email/sendgrid.js | 68 +++++++++++++++++++++++++++++++++++++++ api/src/index.js | 9 ++++++ 2 files changed, 77 insertions(+) create mode 100644 api/src/email/sendgrid.js diff --git a/api/src/email/sendgrid.js b/api/src/email/sendgrid.js new file mode 100644 index 000000000..4ae21ad84 --- /dev/null +++ b/api/src/email/sendgrid.js @@ -0,0 +1,68 @@ +var jwt = require('jsonwebtoken') +var axios = require('axios') + +const sendTeamMemberInvite = function(email, team) { + const dev = process.env.NODE_ENV === 'development' ? true : false + + const API_KEY = process.env.SG_APIKEY + const token = jwt.sign({ email: email, team: team.slug }, API_KEY, { expiresIn: '7d' }) + const origin = 'https://teams.advancedalgos.net' + const params = '/activate-team-membership?token=' + if (dev){ + origin = 'http://localhost:3001' + } + + var headers = { + 'Content-Type': 'application/json', + 'Access-Control-Allow-Origin' : origin, + 'Access-Control-Allow-Credentials': 'true' + }; + + var data = JSON.stringify({ + "personalizations": [ + { + "to": [ + { + "email": email + } + ], + "subject": "INVITATION: Join team " + team.name, + "substitutions": { + "-aainvitelink-": origin + params + token + } + } + ], + "from": { + "email": "noreply@advancedalgos.net", + "name": "Advanced Algos Teams" + }, + "reply_to": { + "email": "feedback@advancedalgos.net", + "name": "Advanced Algos Teams" + }, + "template_id": process.env.SG_INVITE_EMAILID + }) + + var sendVerify = 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 sendVerify +} + +module.exports = { sendTeamMemberInvite } diff --git a/api/src/index.js b/api/src/index.js index 465712204..b997d7e0b 100644 --- a/api/src/index.js +++ b/api/src/index.js @@ -13,6 +13,9 @@ const checkJwt = require('./auth/middleware/jwt') const { getMember } = require('./auth/middleware/getMember') const { validateIdToken } = require('./auth/validateIdToken') const { directiveResolvers } = require('./auth/authDirectives') + +const { sendTeamMemberInvite } = require('./email/sendgrid') + const { createStoragePipline, createServiceURL, @@ -208,6 +211,12 @@ const resolvers = { resourceTypes: SASResourceTypes }, SKC ) return SASQueryParameters + }, + async sendMemberInviteSG(parent, { email, teamId }, ctx, info) { + let team = await ctx.db.query.team({ where: { teamId } }, info) + console.log(email, teamId) + let email = await sendTeamMemberInvite(email, team) + return 'Success' } } } From 1359f606fe63d0060b1e1e052cc0bd55e8cdfeec Mon Sep 17 00:00:00 2001 From: Barry Low Date: Thu, 27 Sep 2018 22:41:19 -0700 Subject: [PATCH 04/11] Connect sendMemberInvite to server --- api/src/email/sendgrid.js | 22 +- api/src/index.js | 7 +- client/src/graphql/teams/SendMemberInvite.js | 10 + .../components/InviteMemberDialog.js | 199 ++++++++++-------- .../components/ManageMembersTeamItem.js | 2 +- 5 files changed, 140 insertions(+), 100 deletions(-) create mode 100644 client/src/graphql/teams/SendMemberInvite.js diff --git a/api/src/email/sendgrid.js b/api/src/email/sendgrid.js index 4ae21ad84..20e1e9ffe 100644 --- a/api/src/email/sendgrid.js +++ b/api/src/email/sendgrid.js @@ -3,22 +3,22 @@ var axios = require('axios') const sendTeamMemberInvite = function(email, team) { const dev = process.env.NODE_ENV === 'development' ? true : false - + console.log('sendTeamMemberInvite', email, team) const API_KEY = process.env.SG_APIKEY const token = jwt.sign({ email: email, team: team.slug }, API_KEY, { expiresIn: '7d' }) - const origin = 'https://teams.advancedalgos.net' + let origin = 'https://teams.advancedalgos.net' const params = '/activate-team-membership?token=' if (dev){ origin = 'http://localhost:3001' } - var headers = { + const headers = { 'Content-Type': 'application/json', 'Access-Control-Allow-Origin' : origin, 'Access-Control-Allow-Credentials': 'true' }; - var data = JSON.stringify({ + const data = JSON.stringify({ "personalizations": [ { "to": [ @@ -26,10 +26,12 @@ const sendTeamMemberInvite = function(email, team) { "email": email } ], - "subject": "INVITATION: Join team " + team.name, - "substitutions": { - "-aainvitelink-": origin + params + token - } + dynamic_template_data: { + "aateaminvitelink": origin + params + token, + "aateamname": team.name, + "subject": "INVITATION: Join team " + team.name + }, + "subject": "INVITATION: Join team " + team.name } ], "from": { @@ -43,7 +45,7 @@ const sendTeamMemberInvite = function(email, team) { "template_id": process.env.SG_INVITE_EMAILID }) - var sendVerify = axios({ + let sendVerify = axios({ method: 'post', url: 'https://api.sendgrid.com/v3/mail/send', data: data, @@ -56,7 +58,7 @@ const sendTeamMemberInvite = function(email, team) { if (response.status >= 200 && response.status < 300) { return 'Success' } else { - throw response.data.errors[0].message + throw response.data.errors[0].message } }) .catch(function (error) { diff --git a/api/src/index.js b/api/src/index.js index b997d7e0b..0e56e0e7d 100644 --- a/api/src/index.js +++ b/api/src/index.js @@ -213,10 +213,9 @@ const resolvers = { return SASQueryParameters }, async sendMemberInviteSG(parent, { email, teamId }, ctx, info) { - let team = await ctx.db.query.team({ where: { teamId } }, info) - console.log(email, teamId) - let email = await sendTeamMemberInvite(email, team) - return 'Success' + const team = await ctx.db.query.team({ where: { id: teamId } }, TEAMS_FRAGMENT) + const sendInvite = await sendTeamMemberInvite(email, team) + return sendInvite } } } diff --git a/client/src/graphql/teams/SendMemberInvite.js b/client/src/graphql/teams/SendMemberInvite.js new file mode 100644 index 000000000..31f63a3fd --- /dev/null +++ b/client/src/graphql/teams/SendMemberInvite.js @@ -0,0 +1,10 @@ +/* eslint-disable no-unused-vars */ +import gql from 'graphql-tag' + +export const SEND_MEMBER_INVITE = gql` + mutation sendMemberInvite($email: String!, $teamId: String!) { + sendMemberInviteSG(email: $email, teamId: $teamId) + } +` + +export default SEND_MEMBER_INVITE diff --git a/client/src/views/dashboard/components/InviteMemberDialog.js b/client/src/views/dashboard/components/InviteMemberDialog.js index 601c45318..f34b8c9ee 100644 --- a/client/src/views/dashboard/components/InviteMemberDialog.js +++ b/client/src/views/dashboard/components/InviteMemberDialog.js @@ -15,7 +15,7 @@ import DialogTitle from '@material-ui/core/DialogTitle' import Typography from '@material-ui/core/Typography' import { isEmpty } from '../../../utils/js-helpers' -import CREATE_TEAM from '../../../graphql/teams/CreateTeamMutation' +import SEND_MEMBER_INVITE from '../../../graphql/teams/SendMemberInvite' // import { checkGraphQLError } from '../../../utils/graphql-errors' @@ -46,7 +46,6 @@ export class InviteMemberDialog extends Component { this.state = { open: false, - status: null, email: '', errors: { email: '' @@ -56,85 +55,115 @@ export class InviteMemberDialog extends Component { render () { const { classes, teamId } = this.props - return ( - - {(createTeam, { loading, error, data }) => { - let errors - let loader = null - if (loading) { - loader = ( - Submitting invitation... - ) - } - if (error) { - errors = error.graphQLErrors.map(({ message }, i) => { - // const displayMessage = checkGraphQLError(message) - return ( - - {message} - + + if (this.state.open) { + return ( + + {(sendMemberInvite, { loading, error, data }) => { + console.log('sendMemberInvite: ', data) + let errors + let loader = null + if (loading) { + loader = ( + Submitting invitation... ) - }) - } - return ( -
- - - Invite a New Team Member - - - - {this.state.errors.email !== '' && ( - {this.state.errors.email} + } + if (error) { + errors = error.graphQLErrors.map(({ message }, i) => { + // const displayMessage = checkGraphQLError(message) + return ( + + {message} + + ) + }) + } + return ( +
+ + + Invite a New Team Member + + {(data === undefined || data.sendMemberInviteSG !== 'Success') && ( + + this.handleChange(e)} + error={this.state.errors.email !== ''} + /> + {this.state.errors.email !== '' && ( + {this.state.errors.email} + )} + {error && {errors}} + {loader} + )} - {error && {errors}} - {loader} - - - - - - - -
- ) - }} - - ) + {data !== undefined && data.sendMemberInviteSG === 'Success' && ( + Invitation successfully sent! + )} +
+ + {(data === undefined || data.sendMemberInviteSG !== 'Success') && ( + + + + + )} + {data !== undefined && data.sendMemberInviteSG === 'Success' && ( + + )} + +
+
+ ) + }} +
+ ) + } else { + return ( + + ) + } } handleClickOpen () { @@ -148,7 +177,7 @@ export class InviteMemberDialog extends Component { handleChange (e) { const errors = this.validate(e.target.value) if (isEmpty(errors)) { - this.setState({ errors: { name: '' } }) + this.setState({ errors: { email: '' } }) this.setState({ email: e.target.value }) } else { this.setState({ errors: errors }) @@ -156,16 +185,16 @@ export class InviteMemberDialog extends Component { } } - async handleSubmit (e, createTeam, email, teamId) { + async handleSubmit (e, sendMemberInvite, email, teamId) { e.preventDefault() - console.log('createTeam submit: ', email, teamId) - await createTeam({ variables: { email, teamId } }) - this.setState({ email: '', status: 'sending' }) + console.log('sendMemberInvite submit: ', email, teamId) + await sendMemberInvite({ variables: { email, teamId } }) + this.setState({ email: '' }) } validate (data) { const errors = {} - if (data === '') errors.name = 'Please enter an email address' + if (data === '') errors.email = 'Please enter an email address' return errors } } diff --git a/client/src/views/dashboard/components/ManageMembersTeamItem.js b/client/src/views/dashboard/components/ManageMembersTeamItem.js index 46da40341..2969aaba2 100644 --- a/client/src/views/dashboard/components/ManageMembersTeamItem.js +++ b/client/src/views/dashboard/components/ManageMembersTeamItem.js @@ -87,7 +87,7 @@ export const ManageMembersTeamItem = ({ classes, team, authId }) => { - + Date: Thu, 27 Sep 2018 23:14:02 -0700 Subject: [PATCH 05/11] Save invite to team --- api/database/datamodel.graphql | 2 +- api/src/generated/prisma.graphql | 3 ++- api/src/graphql/TeamsConnectionsFragment.js | 6 ++++++ api/src/graphql/TeamsFragment.js | 7 ++++++- api/src/index.js | 5 +++++ 5 files changed, 20 insertions(+), 3 deletions(-) diff --git a/api/database/datamodel.graphql b/api/database/datamodel.graphql index 55da2ee34..59a2226f6 100644 --- a/api/database/datamodel.graphql +++ b/api/database/datamodel.graphql @@ -59,7 +59,7 @@ type TeamMembers { team: Team @relation (name: "TeamMembers", onDelete: SET_NULL) role: Role @default(value: MEMBER) status: [TeamMemberStatus!]! - email: String + email: String @unique createdAt: DateTime! updatedAt: DateTime! } diff --git a/api/src/generated/prisma.graphql b/api/src/generated/prisma.graphql index 171d8462a..2ecbd89d3 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 08:38:09 GMT-0700 (Pacific Daylight Time) +# timestamp: Thu Sep 27 2018 22:50:26 GMT-0700 (Pacific Daylight Time) type AggregateFBStatus { count: Int! @@ -1731,6 +1731,7 @@ input TeamMembersWhereInput { input TeamMembersWhereUniqueInput { id: ID + email: String } enum TeamOrderByInput { diff --git a/api/src/graphql/TeamsConnectionsFragment.js b/api/src/graphql/TeamsConnectionsFragment.js index 89a22a8c4..a7494d8c0 100644 --- a/api/src/graphql/TeamsConnectionsFragment.js +++ b/api/src/graphql/TeamsConnectionsFragment.js @@ -21,10 +21,16 @@ const TEAMS_CONNECTIONS_FRAGMENT = gql`{ } members { role + email member { alias authId } + status { + status + reason + createdAt + } } } } diff --git a/api/src/graphql/TeamsFragment.js b/api/src/graphql/TeamsFragment.js index f243480f2..95a111aff 100644 --- a/api/src/graphql/TeamsFragment.js +++ b/api/src/graphql/TeamsFragment.js @@ -13,17 +13,22 @@ const TEAMS_FRAGMENT = gql`{ createdAt profile { avatar - banner description motto updatedAt } members { role + email member { alias authId } + status { + status + reason + createdAt + } } }` diff --git a/api/src/index.js b/api/src/index.js index 0e56e0e7d..92b24b1e1 100644 --- a/api/src/index.js +++ b/api/src/index.js @@ -214,6 +214,11 @@ const resolvers = { }, 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 } From caba8bb33809b0ae91c9466becd312cc851eac88 Mon Sep 17 00:00:00 2001 From: Barry Low Date: Fri, 28 Sep 2018 01:10:06 -0700 Subject: [PATCH 06/11] Member management forms --- api/database/datamodel.graphql | 1 + api/src/generated/prisma.graphql | 80 ++++++++++++++++++- api/src/graphql/TeamsConnectionsFragment.js | 1 + api/src/graphql/TeamsFragment.js | 1 + api/src/schema.graphql | 2 +- client/src/graphql/teams/GetAllTeamsQuery.js | 8 +- .../src/graphql/teams/GetTeamsByOwnerQuery.js | 8 +- .../graphql/teams/RemoveTeamMemberMutation.js | 12 +++ .../teams/SetTeamMemberRoleMutation.js | 12 +++ client/src/graphql/teams/Teams.js | 8 +- .../teams/UpdateTeamProfileMutation.js | 8 +- .../components/ManageMemberDelete.js | 24 +++--- .../dashboard/components/ManageMemberRole.js | 48 +++++++---- .../dashboard/components/ManageMembersItem.js | 38 ++++----- .../components/ManageMembersTeamItem.js | 73 ++++++++--------- 15 files changed, 226 insertions(+), 98 deletions(-) create mode 100644 client/src/graphql/teams/RemoveTeamMemberMutation.js create mode 100644 client/src/graphql/teams/SetTeamMemberRoleMutation.js diff --git a/api/database/datamodel.graphql b/api/database/datamodel.graphql index 59a2226f6..9c07196ef 100644 --- a/api/database/datamodel.graphql +++ b/api/database/datamodel.graphql @@ -65,6 +65,7 @@ type TeamMembers { } type TeamMemberStatus { + id: ID! @unique status: Status @Default(value: INACTIVE) reason: String createdAt: DateTime! diff --git a/api/src/generated/prisma.graphql b/api/src/generated/prisma.graphql index 2ecbd89d3..1dade0729 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 22:50:26 GMT-0700 (Pacific Daylight Time) +# timestamp: Thu Sep 27 2018 23:28:16 GMT-0700 (Pacific Daylight Time) type AggregateFBStatus { count: Int! @@ -1021,14 +1021,17 @@ type Mutation { updateTeamMembers(data: TeamMembersUpdateInput!, where: TeamMembersWhereUniqueInput!): TeamMembers updateMember(data: MemberUpdateInput!, where: MemberWhereUniqueInput!): Member updateFinancialBeings(data: FinancialBeingsUpdateInput!, where: FinancialBeingsWhereUniqueInput!): FinancialBeings + updateTeamMemberStatus(data: TeamMemberStatusUpdateInput!, where: TeamMemberStatusWhereUniqueInput!): TeamMemberStatus deleteTeam(where: TeamWhereUniqueInput!): Team deleteTeamMembers(where: TeamMembersWhereUniqueInput!): TeamMembers deleteMember(where: MemberWhereUniqueInput!): Member deleteFinancialBeings(where: FinancialBeingsWhereUniqueInput!): FinancialBeings + deleteTeamMemberStatus(where: TeamMemberStatusWhereUniqueInput!): TeamMemberStatus upsertTeam(where: TeamWhereUniqueInput!, create: TeamCreateInput!, update: TeamUpdateInput!): Team! upsertTeamMembers(where: TeamMembersWhereUniqueInput!, create: TeamMembersCreateInput!, update: TeamMembersUpdateInput!): TeamMembers! upsertMember(where: MemberWhereUniqueInput!, create: MemberCreateInput!, update: MemberUpdateInput!): Member! upsertFinancialBeings(where: FinancialBeingsWhereUniqueInput!, create: FinancialBeingsCreateInput!, update: FinancialBeingsUpdateInput!): FinancialBeings! + upsertTeamMemberStatus(where: TeamMemberStatusWhereUniqueInput!, create: TeamMemberStatusCreateInput!, update: TeamMemberStatusUpdateInput!): TeamMemberStatus! updateManyTeams(data: TeamUpdateInput!, where: TeamWhereInput): BatchPayload! updateManyTeamMemberses(data: TeamMembersUpdateInput!, where: TeamMembersWhereInput): BatchPayload! updateManyMembers(data: MemberUpdateInput!, where: MemberWhereInput): BatchPayload! @@ -1093,6 +1096,7 @@ type Query { teamMembers(where: TeamMembersWhereUniqueInput!): TeamMembers member(where: MemberWhereUniqueInput!): Member financialBeings(where: FinancialBeingsWhereUniqueInput!): FinancialBeings + teamMemberStatus(where: TeamMemberStatusWhereUniqueInput!): TeamMemberStatus teamsConnection(where: TeamWhereInput, orderBy: TeamOrderByInput, skip: Int, after: String, before: String, first: Int, last: Int): TeamConnection! teamMembersesConnection(where: TeamMembersWhereInput, orderBy: TeamMembersOrderByInput, skip: Int, after: String, before: String, first: Int, last: Int): TeamMembersConnection! membersConnection(where: MemberWhereInput, orderBy: MemberOrderByInput, skip: Int, after: String, before: String, first: Int, last: Int): MemberConnection! @@ -1333,7 +1337,8 @@ input TeamMembersSubscriptionWhereInput { node: TeamMembersWhereInput } -type TeamMemberStatus { +type TeamMemberStatus implements Node { + id: ID! status: Status reason: String createdAt: DateTime! @@ -1356,6 +1361,7 @@ input TeamMemberStatusCreateInput { input TeamMemberStatusCreateManyInput { create: [TeamMemberStatusCreateInput!] + connect: [TeamMemberStatusWhereUniqueInput!] } """An edge in a connection.""" @@ -1368,19 +1374,20 @@ type TeamMemberStatusEdge { } enum TeamMemberStatusOrderByInput { + id_ASC + id_DESC status_ASC status_DESC reason_ASC reason_DESC createdAt_ASC createdAt_DESC - id_ASC - id_DESC updatedAt_ASC updatedAt_DESC } type TeamMemberStatusPreviousValues { + id: ID! status: Status reason: String createdAt: DateTime! @@ -1425,6 +1432,11 @@ input TeamMemberStatusSubscriptionWhereInput { node: TeamMemberStatusWhereInput } +input TeamMemberStatusUpdateDataInput { + status: Status + reason: String +} + input TeamMemberStatusUpdateInput { status: Status reason: String @@ -1432,6 +1444,22 @@ input TeamMemberStatusUpdateInput { input TeamMemberStatusUpdateManyInput { create: [TeamMemberStatusCreateInput!] + connect: [TeamMemberStatusWhereUniqueInput!] + disconnect: [TeamMemberStatusWhereUniqueInput!] + delete: [TeamMemberStatusWhereUniqueInput!] + update: [TeamMemberStatusUpdateWithWhereUniqueNestedInput!] + upsert: [TeamMemberStatusUpsertWithWhereUniqueNestedInput!] +} + +input TeamMemberStatusUpdateWithWhereUniqueNestedInput { + where: TeamMemberStatusWhereUniqueInput! + data: TeamMemberStatusUpdateDataInput! +} + +input TeamMemberStatusUpsertWithWhereUniqueNestedInput { + where: TeamMemberStatusWhereUniqueInput! + update: TeamMemberStatusUpdateDataInput! + create: TeamMemberStatusCreateInput! } input TeamMemberStatusWhereInput { @@ -1443,6 +1471,46 @@ input TeamMemberStatusWhereInput { """Logical NOT on all given filters combined by AND.""" NOT: [TeamMemberStatusWhereInput!] + id: ID + + """All values that are not equal to given value.""" + id_not: ID + + """All values that are contained in given list.""" + id_in: [ID!] + + """All values that are not contained in given list.""" + id_not_in: [ID!] + + """All values less than the given value.""" + id_lt: ID + + """All values less than or equal the given value.""" + id_lte: ID + + """All values greater than the given value.""" + id_gt: ID + + """All values greater than or equal the given value.""" + id_gte: ID + + """All values containing the given string.""" + id_contains: ID + + """All values not containing the given string.""" + id_not_contains: ID + + """All values starting with the given string.""" + id_starts_with: ID + + """All values not starting with the given string.""" + id_not_starts_with: ID + + """All values ending with the given string.""" + id_ends_with: ID + + """All values not ending with the given string.""" + id_not_ends_with: ID status: Status """All values that are not equal to given value.""" @@ -1517,6 +1585,10 @@ input TeamMemberStatusWhereInput { createdAt_gte: DateTime } +input TeamMemberStatusWhereUniqueInput { + id: ID +} + input TeamMembersUpdateInput { role: Role email: String diff --git a/api/src/graphql/TeamsConnectionsFragment.js b/api/src/graphql/TeamsConnectionsFragment.js index a7494d8c0..931d3ac4a 100644 --- a/api/src/graphql/TeamsConnectionsFragment.js +++ b/api/src/graphql/TeamsConnectionsFragment.js @@ -27,6 +27,7 @@ const TEAMS_CONNECTIONS_FRAGMENT = gql`{ authId } status { + id status reason createdAt diff --git a/api/src/graphql/TeamsFragment.js b/api/src/graphql/TeamsFragment.js index 95a111aff..71069f501 100644 --- a/api/src/graphql/TeamsFragment.js +++ b/api/src/graphql/TeamsFragment.js @@ -25,6 +25,7 @@ const TEAMS_FRAGMENT = gql`{ authId } status { + id status reason createdAt diff --git a/api/src/schema.graphql b/api/src/schema.graphql index 6b835e9ec..b6d7c40e7 100644 --- a/api/src/schema.graphql +++ b/api/src/schema.graphql @@ -26,7 +26,7 @@ type Mutation { getAzureSAS(teamSlug: String!):String sendMemberInviteSG(email: String!, teamId: String!): String confirmMember(token: String!): String - removeMember(email: String!, teamId: String!): String + removeTeamMember(teamId: String!, memberId: String!): String } fragment MemberInfo on Member { diff --git a/client/src/graphql/teams/GetAllTeamsQuery.js b/client/src/graphql/teams/GetAllTeamsQuery.js index 0bd9ed522..aa484f1f8 100644 --- a/client/src/graphql/teams/GetAllTeamsQuery.js +++ b/client/src/graphql/teams/GetAllTeamsQuery.js @@ -18,17 +18,23 @@ export const GET_ALL_TEAMS_QUERY = gql` createdAt profile { avatar - banner description motto updatedAt } members { role + email member { alias authId } + status { + id + status + reason + createdAt + } } } } diff --git a/client/src/graphql/teams/GetTeamsByOwnerQuery.js b/client/src/graphql/teams/GetTeamsByOwnerQuery.js index 7f10b0b07..c38c6ebff 100644 --- a/client/src/graphql/teams/GetTeamsByOwnerQuery.js +++ b/client/src/graphql/teams/GetTeamsByOwnerQuery.js @@ -16,17 +16,23 @@ export const GET_TEAMS_BY_OWNER = gql` createdAt profile { avatar - banner description motto updatedAt } members { role + email member { alias authId } + status { + id + status + reason + createdAt + } } } } diff --git a/client/src/graphql/teams/RemoveTeamMemberMutation.js b/client/src/graphql/teams/RemoveTeamMemberMutation.js new file mode 100644 index 000000000..2abf62acd --- /dev/null +++ b/client/src/graphql/teams/RemoveTeamMemberMutation.js @@ -0,0 +1,12 @@ +/* eslint-disable no-unused-vars */ +import gql from 'graphql-tag' + +export const REMOVE_TEAM_MEMBER = gql` + mutation removeTeamMember($teamId: String!, $memberId: String!) { + removeTeamMember(teamId: $teamId, memberId: $memberId) { + id + } + } +` + +export default REMOVE_TEAM_MEMBER diff --git a/client/src/graphql/teams/SetTeamMemberRoleMutation.js b/client/src/graphql/teams/SetTeamMemberRoleMutation.js new file mode 100644 index 000000000..283315275 --- /dev/null +++ b/client/src/graphql/teams/SetTeamMemberRoleMutation.js @@ -0,0 +1,12 @@ +/* eslint-disable no-unused-vars */ +import gql from 'graphql-tag' + +export const SET_TEAM_MEMBER_ROLE = gql` + mutation setTeamMemberRole($teamId: String!, $memberId: String!, $role: String!) { + setTeamMemberRole(teamId: $teamId, memberId: $memberId, role: $role) { + id + } + } +` + +export default SET_TEAM_MEMBER_ROLE diff --git a/client/src/graphql/teams/Teams.js b/client/src/graphql/teams/Teams.js index ce0a6f36b..4a0c84d37 100644 --- a/client/src/graphql/teams/Teams.js +++ b/client/src/graphql/teams/Teams.js @@ -15,17 +15,23 @@ export const TEAMS_FRAGMENT = gql` createdAt profile { avatar - banner description motto updatedAt } members { role + email member { alias authId } + status { + id + status + reason + createdAt + } } } ` diff --git a/client/src/graphql/teams/UpdateTeamProfileMutation.js b/client/src/graphql/teams/UpdateTeamProfileMutation.js index 08b229b98..39feabf8d 100644 --- a/client/src/graphql/teams/UpdateTeamProfileMutation.js +++ b/client/src/graphql/teams/UpdateTeamProfileMutation.js @@ -28,17 +28,23 @@ const UPDATE_TEAM_PROFILE = gql` createdAt profile { avatar - banner description motto updatedAt } members { role + email member { alias authId } + status { + id + status + reason + createdAt + } } } } diff --git a/client/src/views/dashboard/components/ManageMemberDelete.js b/client/src/views/dashboard/components/ManageMemberDelete.js index 4419aa55f..b3ee25d1b 100644 --- a/client/src/views/dashboard/components/ManageMemberDelete.js +++ b/client/src/views/dashboard/components/ManageMemberDelete.js @@ -11,7 +11,7 @@ import DialogContent from '@material-ui/core/DialogContent' import DialogTitle from '@material-ui/core/DialogTitle' import Typography from '@material-ui/core/Typography' -import DELETE_TEAM from '../../../graphql/teams/DeleteTeamMutation' +import REMOVE_TEAM_MEMBER from '../../../graphql/teams/RemoveTeamMemberMutation' import GET_TEAMS_BY_OWNER from '../../../graphql/teams/GetTeamsByOwnerQuery' import { checkGraphQLError } from '../../../utils/graphql-errors' @@ -58,13 +58,8 @@ export class ManageMemberDelete extends Component { const { classes, teamId, authId } = this.props return ( {(deleteTeam, { loading, error, data }) => { let errors @@ -86,7 +81,7 @@ export class ManageMemberDelete extends Component { }) } return ( -
+
- + ) }}
) } - async handleSubmit (e, deleteTeam, teamId) { + async handleSubmit (e, deleteTeam, teamId, memberId) { e.preventDefault() - await deleteTeam({ variables: { teamId } }) + await deleteTeam({ variables: { teamId, memberId } }) this.setState({ open: false }) } } @@ -150,7 +146,7 @@ export class ManageMemberDelete extends Component { ManageMemberDelete.propTypes = { classes: PropTypes.object.isRequired, teamId: PropTypes.string.isRequired, - authId: PropTypes.string.isRequired + authId: PropTypes.string } export default withStyles(styles)(ManageMemberDelete) diff --git a/client/src/views/dashboard/components/ManageMemberRole.js b/client/src/views/dashboard/components/ManageMemberRole.js index 2e19bf48c..6edf5ddf1 100644 --- a/client/src/views/dashboard/components/ManageMemberRole.js +++ b/client/src/views/dashboard/components/ManageMemberRole.js @@ -10,8 +10,10 @@ import DialogActions from '@material-ui/core/DialogActions' import DialogContent from '@material-ui/core/DialogContent' import DialogTitle from '@material-ui/core/DialogTitle' import Typography from '@material-ui/core/Typography' +import TextField from '@material-ui/core/TextField' +import MenuItem from '@material-ui/core/MenuItem' -import DELETE_TEAM from '../../../graphql/teams/DeleteTeamMutation' +import SET_TEAM_MEMBER_ROLE from '../../../graphql/teams/SetTeamMemberRoleMutation' import GET_TEAMS_BY_OWNER from '../../../graphql/teams/GetTeamsByOwnerQuery' import { checkGraphQLError } from '../../../utils/graphql-errors' @@ -27,6 +29,11 @@ const styles = theme => ({ } }) +const roles = [ + { role: 'Admin' }, + { role: 'Member' } +] + export class ManageMemberRole extends Component { constructor (props) { super(props) @@ -37,7 +44,8 @@ export class ManageMemberRole extends Component { this.handleSubmit = this.handleSubmit.bind(this) this.state = { - open: false + open: false, + role: '' } } @@ -58,15 +66,10 @@ export class ManageMemberRole extends Component { const { classes, teamId, authId } = this.props return ( - {(deleteTeam, { loading, error, data }) => { + {(setTeamMemberRole, { loading, error, data }) => { let errors let loader if (loading) { @@ -86,7 +89,7 @@ export class ManageMemberRole extends Component { }) } return ( -
+
- + ) }}
) } - async handleSubmit (e, deleteTeam, teamId) { + async handleSubmit (e, setTeamMemberRole, teamId, authId) { e.preventDefault() - await deleteTeam({ variables: { teamId } }) + const role = this.state.role + await setTeamMemberRole({ variables: { teamId, memberId: authId, role: role } }) this.setState({ open: false }) } } diff --git a/client/src/views/dashboard/components/ManageMembersItem.js b/client/src/views/dashboard/components/ManageMembersItem.js index 4d5ea5597..1e082c8c5 100644 --- a/client/src/views/dashboard/components/ManageMembersItem.js +++ b/client/src/views/dashboard/components/ManageMembersItem.js @@ -2,7 +2,6 @@ import React from 'react' import PropTypes from 'prop-types' import Grid from '@material-ui/core/Grid' -import CardMedia from '@material-ui/core/CardMedia' import Typography from '@material-ui/core/Typography' import ManageMemberDelete from './ManageMemberDelete' @@ -12,38 +11,35 @@ import { MessageCard } from '../../common/' export const ManageMembersItem = ({ classes, member, teamId, authId }) => { console.log('ManageMembersItem', member) let profile = null + let email = null + let memberStatus = null if (member.member !== null) { profile = member.member } - let avatar = null - if (member.profile !== undefined && member.profile !== null && member.profile.avatar !== undefined && member.profile.avatar !== 'a') { - avatar = member.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' + if (member.member === null && member.email !== null) { + email = member.email } - if (profile !== null) { + if (member.status.length > 0) memberStatus = member.status + if (profile !== null || email !== null) { return ( - - - - - {profile.alias} + + {profile !== null ? profile.alias : email} - - {member.role} | Member since: + + {email !== null ? `${memberStatus[memberStatus.length - 1].status} | Sent: ${memberStatus[memberStatus.length - 1].createdAt}` : `${member.role} | Member since: `} - - - + + { profile !== null && member.role !== 'OWNER' && ( + + )} + {(email !== null || member.role !== 'OWNER') && ( + + )} ) diff --git a/client/src/views/dashboard/components/ManageMembersTeamItem.js b/client/src/views/dashboard/components/ManageMembersTeamItem.js index 2969aaba2..6d0731513 100644 --- a/client/src/views/dashboard/components/ManageMembersTeamItem.js +++ b/client/src/views/dashboard/components/ManageMembersTeamItem.js @@ -50,8 +50,8 @@ const styles = theme => ({ justifyContent: 'flex-start' }, memberContainer: { - margin: 40, - padding: 12 + margin: 10, + padding: 6 } }) @@ -66,43 +66,40 @@ export const ManageMembersTeamItem = ({ classes, team, authId }) => { if (team.members.length > 0) { return ( - { team.members.map(member => { - console.log('ManageMembersTeamItem: ', member) - return ( - - -
- - - - - - - - {team.name} - - - - - - - + +
+ + + + - -
-
- - ) - }) - } + + + + {team.name} + + + + + + + {team.members.length > 0 && team.members.map((member, index) => ( + + ))} +
+
+
+
) } else { From 4ca444addf1e785ce56aa34b86352dbce1d974e9 Mon Sep 17 00:00:00 2001 From: Barry Low Date: Fri, 28 Sep 2018 08:49:33 -0700 Subject: [PATCH 07/11] Custom Auth Login + verify invite token --- api/src/email/sendgrid.js | 21 +++++++- api/src/index.js | 20 +++++++- api/src/schema.graphql | 1 + client/package-lock.json | 33 +++++++++--- client/package.json | 1 + client/src/App.js | 6 +++ client/src/auth/index.js | 60 +++++++++++++++++++++- client/src/views/index.js | 2 + client/src/views/teams/AcceptTeamInvite.js | 31 +++++++++++ 9 files changed, 162 insertions(+), 13 deletions(-) create mode 100644 client/src/views/teams/AcceptTeamInvite.js diff --git a/api/src/email/sendgrid.js b/api/src/email/sendgrid.js index 20e1e9ffe..a30969ea8 100644 --- a/api/src/email/sendgrid.js +++ b/api/src/email/sendgrid.js @@ -7,7 +7,7 @@ const sendTeamMemberInvite = function(email, team) { const API_KEY = process.env.SG_APIKEY const token = jwt.sign({ email: email, team: team.slug }, API_KEY, { expiresIn: '7d' }) let origin = 'https://teams.advancedalgos.net' - const params = '/activate-team-membership?token=' + const params = '/activate-team-membership/' if (dev){ origin = 'http://localhost:3001' } @@ -67,4 +67,21 @@ const sendTeamMemberInvite = function(email, team) { return sendVerify } -module.exports = { sendTeamMemberInvite } +const verifyInviteToken = async function(token) { + const API_KEY = process.env.SG_APIKEY + let verifiedToken + try { + verifiedToken = await jwt.verify(token, API_KEY, {maxAge: '7d'}) + } catch(err) { + if (err.name === "TokenExpiredError"){ + throw new Error ("Error: Token Expired. Please request new invitation or apply to the team again") + } else { + throw new Error (`Error: Token error - ${err.message}`) + } + } + + console.log('verifyInviteToken', verifiedToken) + return verifiedToken +} + +module.exports = { sendTeamMemberInvite, verifyInviteToken } diff --git a/api/src/index.js b/api/src/index.js index 92b24b1e1..13f354638 100644 --- a/api/src/index.js +++ b/api/src/index.js @@ -14,7 +14,7 @@ const { getMember } = require('./auth/middleware/getMember') const { validateIdToken } = require('./auth/validateIdToken') const { directiveResolvers } = require('./auth/authDirectives') -const { sendTeamMemberInvite } = require('./email/sendgrid') +const { sendTeamMemberInvite, verifyInviteToken } = require('./email/sendgrid') const { createStoragePipline, @@ -136,6 +136,24 @@ const resolvers = { } return member }, + async verifyTeamInvite(parent, { token }, ctx, info) { + let verifiedToken = null + let team = null + try { + verifiedToken = await verifyInviteToken(token) + team = { + email: verifiedToken.email, + team: { + slug: verifiedToken.team + } + } + console.log('verifyTeamInvite.token: ', await verifiedToken) + } catch (err) { + console.log('verifyTeamInvite. err: ', err) + throw new Error(err.message) + } + return team + }, async createTeam(parent, { name, slug }, ctx, info) { 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) diff --git a/api/src/schema.graphql b/api/src/schema.graphql index b6d7c40e7..0b969a608 100644 --- a/api/src/schema.graphql +++ b/api/src/schema.graphql @@ -20,6 +20,7 @@ type Query { type Mutation { authenticate(idToken: String!): Member + verifyTeamInvite(token: String!): TeamMembers createTeam(name: String!, slug: String!): Team @isAuthenticated updateTeamProfile(slug: String!, description: String, motto: String, avatar: String, banner: String): Team @isAuthenticated deleteTeam(slug: String!): Team @isAuthenticated @isOwner(type: "Team") diff --git a/client/package-lock.json b/client/package-lock.json index c0fbda736..dbfc71ab6 100644 --- a/client/package-lock.json +++ b/client/package-lock.json @@ -6266,8 +6266,7 @@ "decode-uri-component": { "version": "0.2.0", "resolved": "https://registry.npmjs.org/decode-uri-component/-/decode-uri-component-0.2.0.tgz", - "integrity": "sha1-6zkTMzRYd1y4TNGh+uBiEGu4dUU=", - "dev": true + "integrity": "sha1-6zkTMzRYd1y4TNGh+uBiEGu4dUU=" }, "decompress-response": { "version": "3.3.0", @@ -12102,6 +12101,18 @@ "prepend-http": "^1.0.0", "query-string": "^4.1.0", "sort-keys": "^1.0.0" + }, + "dependencies": { + "query-string": { + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/query-string/-/query-string-4.3.4.tgz", + "integrity": "sha1-u7aTucqRXCMlFbIosaArYJBD2+s=", + "dev": true, + "requires": { + "object-assign": "^4.1.0", + "strict-uri-encode": "^1.0.0" + } + } } }, "npm-run-all": { @@ -13791,13 +13802,19 @@ "integrity": "sha512-N5ZAX4/LxJmF+7wN74pUD6qAh9/wnvdQcjq9TZjevvXzSUo7bfmw91saqMjzGS2xq91/odN2dW/WOl7qQHNDGA==" }, "query-string": { - "version": "4.3.4", - "resolved": "https://registry.npmjs.org/query-string/-/query-string-4.3.4.tgz", - "integrity": "sha1-u7aTucqRXCMlFbIosaArYJBD2+s=", - "dev": true, + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/query-string/-/query-string-6.1.0.tgz", + "integrity": "sha512-pNB/Gr8SA8ff8KpUFM36o/WFAlthgaThka5bV19AD9PNTH20Pwq5Zxodif2YyHwrctp6SkL4GqlOot0qR/wGaw==", "requires": { - "object-assign": "^4.1.0", - "strict-uri-encode": "^1.0.0" + "decode-uri-component": "^0.2.0", + "strict-uri-encode": "^2.0.0" + }, + "dependencies": { + "strict-uri-encode": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/strict-uri-encode/-/strict-uri-encode-2.0.0.tgz", + "integrity": "sha1-ucczDHBChi9rFC3CdLvMWGbONUY=" + } } }, "querystring": { diff --git a/client/package.json b/client/package.json index 34a09ec19..950e4394c 100644 --- a/client/package.json +++ b/client/package.json @@ -89,6 +89,7 @@ "graphql": "^0.13.2", "graphql-tag": "^2.9.2", "prop-types": "^15.6.2", + "query-string": "^6.1.0", "react": "^16.5.0", "react-apollo": "^2.1.11", "react-dom": "^16.5.0", diff --git a/client/src/App.js b/client/src/App.js index 8ed26d1a8..b0fe69f45 100644 --- a/client/src/App.js +++ b/client/src/App.js @@ -19,6 +19,7 @@ import { NavBar, Footer, Callback, + AcceptTeamInvite, Home, About, Dashboard, @@ -171,6 +172,11 @@ class App extends Component { return }} /> + } + />