401 error when performing a Query in AppSync
See original GitHub issueBefore opening, please confirm:
- I have searched for duplicate or closed issues and discussions.
- I have read the guide for submitting bug reports.
- I have done my best to include a minimal, self-contained set of instructions for consistently reproducing the issue.
JavaScript Framework
React
Amplify APIs
Authentication, GraphQL API
Amplify Categories
auth, storage, function, api
Environment information
# Put output below this line
System:
OS: Linux 5.11 Pop!_OS 21.04
CPU: (8) x64 Intel(R) Core(TM) i5-8250U CPU @ 1.60GHz
Memory: 409.40 MB / 15.52 GB
Container: Yes
Shell: 5.8 - /usr/bin/zsh
Binaries:
Node: 12.21.0 - /usr/bin/node
Yarn: 1.22.10 - /usr/local/bin/yarn
npm: 8.0.0 - /usr/local/bin/npm
Browsers:
Brave Browser: 96.1.32.106
Chrome: 95.0.4638.69
Firefox: 90.0
npmPackages:
@aws-amplify/ui-react: ^0.2.35 => 0.2.38
@babel/cli: ^7.15.4 => 7.15.4
@babel/core: ^7.15.5 => 7.15.5 (7.13.16, 7.12.3)
@babel/preset-env: ^7.15.6 => 7.15.6 (7.13.15, 7.12.1)
@babel/preset-typescript: ^7.15.0 => 7.15.0 (7.12.1)
@fortawesome/fontawesome-free: ^5.15.2 => 5.15.3
@fortawesome/fontawesome-svg-core: ^1.2.34 => 1.2.35
@fortawesome/free-brands-svg-icons: ^5.15.2 => 5.15.3
@fortawesome/free-regular-svg-icons: ^5.15.3 => 5.15.3
@fortawesome/free-solid-svg-icons: ^5.15.2 => 5.15.3
@fortawesome/react-fontawesome: ^0.1.14 => 0.1.14
@material-ui/core: ^4.11.3 => 4.11.4
@material-ui/lab: ^4.0.0-alpha.57 => 4.0.0-alpha.58
@testing-library/jest-dom: ^5.11.4 => 5.12.0
@testing-library/react: ^11.1.0 => 11.2.6
@testing-library/user-event: ^12.1.10 => 12.8.3
@types/history: ^4.7.3 => 4.7.8
@types/jest: ^26.0.15 => 26.0.23
@types/material-ui-phone-number: ^2.2.1 => 2.2.1
@types/node: ^12.0.0 => 12.20.11 (15.0.1)
@types/react: ^16.9.53 => 16.14.5 (17.0.4)
@types/react-dom: ^16.9.8 => 16.9.12
@types/react-router-dom: ^5.1.7 => 5.1.7
@types/uuid: ^8.3.1 => 8.3.1
aws-amplify: ^3.3.20 => 3.3.27
aws-sdk: ^2.974.0 => 2.974.0
formik: ^2.2.6 => 2.2.6
geoplugin: ^1.0.5 => 1.0.5
graphql-tag: ^2.12.5 => 2.12.5
html-parse-stringify2: ^2.0.1 => 2.0.1
i18next: ^19.8.8 => 19.9.2
i18next-browser-languagedetector: ^6.0.1 => 6.1.0
i18next-http-backend: ^1.1.0 => 1.2.1
i18next-xhr-backend: ^3.2.2 => 3.2.2
material-ui-phone-number: ^2.2.6 => 2.2.6
react: ^17.0.2 => 17.0.2 (16.14.0, 15.7.0)
react-activity: ^2.1.1 => 2.1.1
react-activity-indicator: ^0.7.77 => 0.7.77
react-dom: ^17.0.2 => 17.0.2 (16.14.0, 15.7.0)
react-i18next: ^11.8.7 => 11.8.14
react-multi-carousel: ^2.6.2 => 2.6.2
react-native-paper: ^4.9.2 => 4.9.2
react-router-dom: ^5.2.0 => 5.2.0
react-scripts: 4.0.1 => 4.0.1
react-spinners: ^0.11.0 => 0.11.0
styled-components: ^5.2.1 => 5.2.3
styled-components/macro: undefined ()
styled-components/native: undefined ()
styled-components/primitives: undefined ()
typescript: ^4.0.3 => 4.2.4
uuid: ^8.3.2 => 8.3.2 (3.4.0, 3.3.2)
web-vitals: ^0.2.4 => 0.2.4
yup: ^0.32.9 => 0.32.9
yup-phone: ^1.2.19 => 1.2.19
npmGlobalPackages:
@aws-amplify/cli: 7.6.1
depcheck: 1.4.2
expo-cli: 4.11.0
npm: 8.0.0
typescript: 4.4.2
yarn: 1.22.10
Describe the bug
To give a bit of context to our problem: we updated our Amplify framework to the version 7.6.1 but it had a lot of errors, so we decided to take down our backend and build it again.
This allowed us to fix some errors that we had prior to the update but it has now introduced a new one. Currently, we each time a user tries to access the API a 401 error is thrown.
We believe this error might be due to lack of IAM permitions but we have added the permitions that we previously had and still does not work.
Before this upgrade everything was working completly fine.
Edit:
This issue was created during a case (Case ID: 9301932391)
We tried to perform a listEvents query logged as a user in “Person” group and did not work. We were able to query it if the user is not logged in
Expected behavior
Not throw 401 error
Reproduction steps
- run amplify init
- run amplify add auth
- run amplify add api
- do a listEvents query
Code Snippet
// Put your code below this line.
const inputObject = { query: listEvents }
const response = (await API.graphql(inputObject)) as GraphQLResult<Types.ListEventsQuery>
Log output
// Put your logs below this line
{data: {…}, errors: Array(1)}
data: {listEvents: null}
errors: Array(1)
0:
data: null
errorInfo: null
errorType: "Unauthorized"
locations: [{…}]
message: "Not Authorized to access listEvents on type ModelEventConnection"
path: ['listEvents']
[[Prototype]]: Object
length: 1
[[Prototype]]: Array(0)
[[Prototype]]: Object
aws-exports.js
/* eslint-disable */ // WARNING: DO NOT EDIT. This file is automatically generated by AWS Amplify. It will be overwritten.
const awsmobile = { “aws_project_region”: “eu-west-2”, “aws_cognito_identity_pool_id”: “eu-west-2:950a8d3b-5a6c-4466-bdc8-bcfd21c1e5a9”, “aws_cognito_region”: “eu-west-2”, “aws_user_pools_id”: “eu-west-2_rqlB6WEZ2”, “aws_user_pools_web_client_id”: “7ee5vfhnub1na7ub0vb9c31h5g”, “oauth”: {}, “aws_cognito_username_attributes”: [ “EMAIL” ], “aws_cognito_social_providers”: [], “aws_cognito_signup_attributes”: [ “EMAIL”, “NAME”, “PHONE_NUMBER” ], “aws_cognito_mfa_configuration”: “OFF”, “aws_cognito_mfa_types”: [ “SMS” ], “aws_cognito_password_protection_settings”: { “passwordPolicyMinLength”: 8, “passwordPolicyCharacters”: [ “REQUIRES_LOWERCASE”, “REQUIRES_UPPERCASE”, “REQUIRES_NUMBERS” ] }, “aws_cognito_verification_mechanisms”: [ “PHONE_NUMBER” ], “aws_appsync_graphqlEndpoint”: “https://zhonadyidvaeddgqv4fy6fqb7u.appsync-api.eu-west-2.amazonaws.com/graphql”, “aws_appsync_region”: “eu-west-2”, “aws_appsync_authenticationType”: “AWS_IAM”, “aws_user_files_s3_bucket”: “outgoingawsbackend736404c325ee484e89fd39a698851231256-dev”, “aws_user_files_s3_bucket_region”: “eu-west-2” };
export default awsmobile;
Manual configuration
No response
Additional configuration
This is our schema.graphql:
"""
Description:
* A company represents an entity that can create, delete and update events whose tickets and consumables can be sold
trough our app.
Directives:
* @model: Creates an object in our database
* @auth: Specifies who can access this object and what operations they can perform
* @hasOne: allows connection to a single image
* @manyToMany(PersonCompanyConnection): allows connecting companies to a set of partners
* @hasMany(byCompanyByName): allows connecting to a list of groups and sort them by name
* @hasMany(byCompanyByName): allows connecting to a set of collaborators and sort them by their name
* @hasMany(byCompanyByDate): allows connecting to a set of events and sort them by date
Arguments:
* id: company's identity descriptor
* owner: reference to the owner of this object -> is the same as the owner's email
* name: name of the company
* email: company's email -> used for contact purposes
* iban: holds bank account number which will receive payments after an event is over
* consumablesFee: percentage of fee taken by us from this company for their consumable transactions
* ticketsFee: percentage of fee taken by us from this company for their tickets transactions
* avatar: logo/icon of the company -> if is null, then we should show the default image on the frontend
* initials: company's short name or abreviation
* associates: list of common user that are partners of this company
* groups: holds a list of groups held by this company
* collaborators: holds a list of all collaborators
* events: list of upcoming events provided by the company
"""
type Company @model
@auth(rules:[
# Allows the owner of this object to update and delete info
{ allow: owner, ownerField: "owner", operations: [update, delete, read] },
# Only those who belong to this group can create a table for themselves
{ allow: groups, groups: ["Company"], operations: [create, read] },
# Anyone without credentials can read this object
{ allow: public, provider: iam, operations: [read] }
]) {
id: ID !
owner: String !
name: String !
email: AWSEmail !
iban: String !
initials: String !
consumablesFee: Float !
ticketsFee: Float !
avatar: Image @hasOne
associates: [Person !] ! @manyToMany(relationName: "PersonCompanyConnection")
groups: [Group !] ! @hasMany(indexName: "byCompanyByName", fields: ["id"])
collaborators: [Collaborator !] ! @hasMany(indexName: "byCompanyByName", fields: ["id"])
events: [Event !] ! @hasMany(indexName: "byCompanyByDate", fields: ["id"])
}
"""
Description:
* Represents a person that works for a specific company.
Directives:
* @model: Creates an object in our database
* @auth: Specifies who can access this object and what operations they can perform
* @index(byCompanyByName): allows connecting to a company and sort all company's collaborators by name
* @index(byGroupByName): allows connecting to a group and sort all group's collaborators by name
* @hasOne: allows connection to a single image
* @hasMany(byCollaboratorByEvent): creates link between an collaborator and a set of transactions. Sorts by event
* @manyToMany(CollaboratorEventConnection): connects multiple collaborators to multiple events
Arguments:
* id: collaborator's identity descriptor -> UUID provided by user object upon login
* owner: reference to the owner of this object -> is the same as the owner's email
* name: person's name
* email: collaborator's email
* phone: collaborator's phone number
* activity: description of the collaborator's work
* companyID: company for which this person works
* groupID: group for which this collaborator belongs -> can be null which means that he does not have any group
* avatar: image representing the user -> if is null, then we should show the default image on the frontend
* history: lits of previous transactions
* events: list holding all the assigned events to this person
"""
type Collaborator @model
@auth(rules:[
# Allows Companies can read, update and delete their collaborators
{ allow: owner, ownerField: "owner", operations: [update, delete, read] },
# Only Company accounts can create a set of collaborators and list them
{ allow: groups, groups: ["Company"], operations: [create] }
]) {
id: ID !
owner: String !
name: String !
email: AWSEmail !
phone: AWSPhone !
activity: String
companyID: ID ! @index(name: "byCompanyByName", sortKeyFields: ["name"], queryField: "collaboratorByCompany")
groupID: ID @index(name: "byGroupByName", sortKeyFields: ["name"], queryField: "collaboratorByGroupByName")
avatar: Image @hasOne
history: [ConsumableTransaction !] ! @hasMany(indexName: "byCollaboratorByEvent", fields: ["id"])
events: [Event !] ! @manyToMany(relationName: "CollaboratorEventConnection")
}
"""
Description:
* Represents a group/stall in a party. Collaborators and consumables can be associated to a stall. Is basically a group of
collaborators.
Directives:
* @model: Creates an object in our database
* @auth: Specifies who can access this object and what operations they can perform
* @index(byCompanyByName): allows connecting to a company and sort by group's id
* @hasMany(byGroupByName): allows connecting to a set of collaborators and sorts by name
* @hasMany(byGroupByPrice): allows connecting to a list of consumables and sorts them by price
Arguments:
* id: group's identity descriptor
* owner: reference to the owner of this object -> is the same as the owner's email
* name: group's name
* companyID: company that hold this group
* collaborators: list of collaborators belonging to this group
* consumables: list of consumables sold by this group
"""
type Group @model
@auth(rules:[
# Only the company that created this group can update and delete them
{ allow: owner, ownerField: "owner", operations: [update, delete, read] },
# Only a Company can create a group
{ allow: groups, groups: ["Company"], operations: [create] }
]) {
id: ID !
owner: String !
name: String !
companyID: ID ! @index(name: "byCompanyByName" , sortKeyFields: ["name"], queryField: "groupsByCompany")
collaborators: [Collaborator !] ! @hasMany(indexName: "byGroupByName", fields: ["id"])
consumables: [Consumable !] ! @hasMany(indexName: "byGroupByPrice", fields: ["id"])
}
"""
Description:
* Describes an event created by a Company and that can have a lot of attendees. Also allows the upload of images,
a description of the event and the creation of consumables entities to be purchased during the event.
Directives:
* @model: Creates an object in our database
* @searchable: Allows complex searches on this object using a dynamodb stream and open search service from aws
* @auth: Specifies who can access this object and what operations they can perform
* @index(byCompanyByDate): allows connecting to a company and sort all company's events by their date
* @hasOne: allows connecting an event to a banner (main image)
* @hasOne: allows connecting an event to a poster
* @hasMany(byEvent4): allows connecting a set of streaks to an event
* @hasMany(byEvent3): allows connecting a set of images to an event
* @hasMany(byEventByPrice2): connects multiple events to multiple tickets
* @manyToMany(CollaboratorEventConnection): connects multiple events to multiple collaborators
* @hasMany(byEventByPrice): allows connecting an event with the sold consumables and sort them by price
* @hasMany(byEventByDate1): allows linking a set of ticket purchases with an event and sort by date
* @hasMany(byEventByDate2): allows linking a set of consumable purchases with an event and sort by date
Arguments:
* id: event's identity descriptor
* owner: reference to the owner of this object -> is the same as the owner's email
* name: name of the event
* maxNumberOfPeople: maximum number of attendees -> can be null (doesn't have a limit)
* noEntriesDatetime: datetime after which nobody else will enter the event -> can be null (doesn't have a restriction)
* isPromoted: tells if the owner is paying to put it in the frontpage of the app
* location: exact location where the event will be held (long, lat)
* addressAlias: simple reference to the place of the event
* companyID: company which holds this event -> used to link both entities
* description: short description that is meant to captive more people -> written by the event provider
* startDate: event start datetime -> used mainly for sorting and filters
* endDate: event end datetime -> used mainly for sorting and filters
* date: date span of occurrence in "Javascript Date" -> ISO format 8601
* banner: photo that is shown in the frontpage -> all events should have one (even little ones)
* poster: list of artists that are going to perform in the event
* streaks: list of consumable streaks
* photos: lits of photos associated with this event (menu, singers, ect...) -> is a set of Images
* tickets: list of tickets being sold for this event
* collaborators: list of collaborators that are going to be serving in this event
* transactionsTickets: list of ticket transactions in this event
* transactionsConsumables: list of consumables transactions in this event
"""
type Event @model # TODO: @searchable is currently disabled because of an issue (Check Amplify GitHub)
@auth(rules:[
# Only the company that created this event can update/delete it
{ allow: owner, ownerField: "owner", operations: [update, delete] },
# Only a Company can create an event
{ allow: groups, groups: ["Company"], operations: [create] },
# Anyone can read info from the event
{ allow: public, provider: iam, operations: [read] }
]) {
id: ID !
owner: String !
name: String !
maxNumberOfPeople: Int
noEntriesDatetime: AWSDateTime
isPromoted: Boolean !
location: Location !
addressAlias: String !
companyID: ID ! @index(name: "byCompanyByDate", sortKeyFields: ["startDate"], queryField: "eventsByCompanyByDate")
description: String !
startDate: AWSDateTime !
endDate: AWSDateTime !
date: [[AWSDateTime !] !] !
nrOfPeopleGoing: Int !
banner: Image ! @hasOne
poster: Poster @hasOne
streaks: [Streak !] ! @hasMany(indexName: "byEvent4", fields: ["id"])
photos: [Image ! ] ! @hasMany(indexName: "byEvent3", fields: ["id"])
tickets: [Ticket !] ! @hasMany(indexName: "byEventByPrice2", fields: ["id"])
collaborators: [Collaborator !] ! @manyToMany(relationName: "CollaboratorEventConnection")
consumables: [Consumable !] ! @hasMany(indexName: "byEventByPrice", fields:["id"])
transactionsTickets: [TicketTransaction !] ! @hasMany(indexName: "byEventByDate1", fields: ["id"])
transactionsConsumables:[ConsumableTransaction !] ! @hasMany(indexName: "byEventByDate2", fields: ["id"])
}
"""
Description:
* This object is used to categorise a ticket for an event. A ticket can have multiple days, prices and can have
been already read or not. Can also change its price according to the day
Directives:
* @model: Creates an object in our database -> Doesn't need queries because we can access trough both objects
* @auth: Specifies who can access this object and what operations they can perform
* @index(byEventByPrice2): allows connecting to an event and sorts by price
Arguments:
* id: ticket's identity descriptor
* owner: reference to the owner of this object -> is the same as the owner's email
* stock: number of tickets available -> can be null (does not have limit)
* price: normal selling price
* memberPrice: company associate selling price. Can be null
* priceOverRideDate: date after which the official price will be changed
* overridePrice: normal selling price that becomes active after a certain date. Can be null
* memberOverridePrice: company associate selling price that becomes active after a certain date. Can be null
* validDates: 2 element array which symbols a time span in which this ticket is valid
* eventID: event which holds this ticket
"""
type Ticket @model
@auth(rules:[
# Only the company that created this ticket can update/delete it
{ allow: owner, ownerField: "owner", operations: [update, delete] },
# Only a Company can create a ticket
{ allow: groups, groups: ["Company"], operations: [create] },
# Anyone can read info from the ticket
{ allow: public, provider: iam, operations: [read] }
]) {
id: ID !
owner: String !
stock: Int
price: Float !
memberPrice: Float
priceOverRideDate: AWSDateTime
overridePrice: Float
memberOverridePrice: Float
validDates: [[AWSDateTime !] !] !
eventID: ID ! @index(name: "byEventByPrice2", sortKeyFields: ["price"], queryField: "ticketsByEventByPrice")
}
"""
Description:
* This object holds the event's poster. The poster has all the artists that are going to perform and the time at
which they will do it.
Directives:
* @model: Creates an object in our database -> Doesn't need queries because we can access trough both objects
* @auth: Specifies who can access this object and what operations they can perform
Arguments:
* id: poster's identity descriptor
* owner: reference to the owner of this object -> is the same as the owner's email
* artists: set of artists -> each index maps to a day of the event and the inner index maps to an artist of that day.
if the outer field is null then, that day does not have any performances
"""
type Poster @model
@auth(rules:[
# Only the company that created this poster can update/delete it
{ allow: owner, ownerField: "owner", operations: [update, delete] },
# Only a Company can create a poster
{ allow: groups, groups: ["Company"], operations: [create] },
# Anyone can read info from the poster
{ allow: public, provider: iam, operations: [read] }
]) {
id: ID !
owner: String !
artists: [[Artist !] ] !
}
"""
Description:
* Represents a purchase of tickets in our database. A user can purchase tickets from the app or from the website.
Directives:
* @model: Creates an object in our database
* @auth: Specifies who can access this object and what operations they can perform
* @index(byPersonByDate1): allows linking a set of transactions with a person and sorts them by date
* @index(byEventByDate1): allows linking a set of transactions with an event and sorts them by date
* @hasOne: allows linking a ticket with a purchase made by the user
Arguments:
* id: transaction's identity descriptor
* owner: reference to the owner of this object -> is the same as the owner's email
* date: date of transaction
* price: amount of money payed for the ticket
* personID: id of the person buying this ticket
* eventID: id of ticket's event
* ticket: ticket that was bought
"""
type TicketTransaction @model
@auth(rules: [
# Only a Person can buy a ticket
{ allow: groups, groups: ["Person"], operations: [create] },
# Only the owner can update or delete the transaction
{ allow: owner, ownerField: "owner", operations: [delete, update] },
# Anyone can see that a person has a ticket for this event
{ allow: public, provider: iam, operations: [read] }
]) {
id: ID !
owner: String !
date: AWSDateTime !
price: Float !
personID: ID ! @index(name: "byPersonByDate1", sortKeyFields: ["date"], queryField: "ticketTransactionByPerson")
eventID: ID ! @index(name: "byEventByDate1", sortKeyFields: ["date"], queryField: "ticketTransactionByEvent")
ticket: Ticket ! @hasOne
}
"""
Description:
* Represents a transaction made with our payment affiliates to a user.
Directives:
* @model: Creates an object in our database
* @auth: Specifies who can access this object and what operations they can perform
* @index(byPerson4): allows linking a set of transactions with a person and sorts them by date
Arguments:
* id: transaction's identity descriptor
* owner: reference to the owner of this object -> is the same as the owner's email
* date: date of transaction
* amount: amount of money put in the wallet
* method: which payment method was used
* status: transaction current status
* transactionID: id of the transaction with our payment provider
* personID: id of the person buying this ticket
"""
type WalletTransaction @model
@auth(rules: [
# Only a Person can put money in their wallet
{ allow: groups, groups: ["Person"], operations: [create] },
# Only the owner can manage the transaction
{ allow: owner, ownerField: "owner", operations: [delete, update, read] },
]) {
id: ID !
owner: String !
date: AWSDateTime !
amount: Float !
method: PaymentMethod
status: PaymentStatus !
transactionID: String !
personID: ID ! @index(name: "byPerson4", sortKeyFields: ["date"])
}
"""
Description:
* Represents a consumable transaction in our database. A collaborator has to read a QR from a person's phone and
register a transaction holding a specific amount of a set of products.
Directives:
* @model: Creates an object in our database
* @auth: Specifies who can access this object and what operations they can perform
* @index(byEventByDate2): allows linking a transaction with an event and sort it by date
* @index(byPersonByDate2): allows linking a transaction with a person and sorts them by date
* @index(byCollaboratorByEvent): creates link between a transaction and an collaborator. Sorts by event
* @hasMany(byTransaction): allows linking a transaction with a set of consumables + amount
Arguments:
* id: transaction's identity descriptor
* owner: reference to the owner of this object -> is the same as the owner's email (in this case, the Person's)
* date: date of transaction
* price: amount payed for this transaction
* collaboratorID: id of the collaborator that performed this transaction
* personID: id of the person buying this items
* eventID: id of the event in which this transaction took place
* cart: cart that holds
"""
type ConsumableTransaction @model
@auth(rules: [
# The person that payed for this transaction can only read it
{ allow: owner, ownerField: "owner", operations: [read] },
# Only collaborators can create, delete and update transactions
{ allow: groups, groups: ["Collaborator"], operations: [create, delete, update, read] }
]) {
id: ID !
owner: String !
date: AWSDateTime !
price: Float !
collaboratorID: ID ! @index(name: "byCollaboratorByEvent", sortKeyFields: ["eventID"], queryField: "consumableTransactionByCollaborator")
personID: ID ! @index(name: "byPersonByDate2", sortKeyFields: ["date"], queryField: "consumableTransactionByPerson")
eventID: ID ! @index(name: "byEventByDate2" , sortKeyFields: ["date"], queryField: "consumableTransactionByEvent")
cart: [PairTransaction !] ! @hasMany(indexName: "byTransaction", fields: ["id"])
}
"""
Description:
* Represents a ticket held by a user. Holds a reference to the type of ticket and if it has been read or not.
Directives:
* @model: Creates an object in our database
* @auth: Specifies who can access this object and what operations they can perform
* @index(byPerson2): allows linking a person with a set of tickets
* @hasOne: links a personTicket with a ticket
* @hasOne(personID): links a personTicket with a person
Arguments:
* id: person's identity descriptor
* owner: reference to the owner of this object -> is the same as the owner's email
* readDate: holds the date of reading of this ticket -> if was not read, is the start of ISO format date
* eventID: event's id which this ticket belongs to
* personID: person ID that holds this ticket -> mainly used for the connection
* ownerPhone: ticket's owner's phone number -> used for queries through his contacts
* visibility: holds user's privacy policy -> mainly used for fetching users in one query
* ticket: event ticket
* person: person object that holds this ticket
"""
type PersonTicket @model
@auth(rules: [
# Only a person in Persons can buy tickets
{ allow: groups, groups: ["Person"], operations: [create] },
# The owner can update/delete their info
{ allow: owner, ownerField: "owner", operations: [update, delete, read] },
# Anyone can see which events another is going to attend
{ allow: public, provider: iam, operations: [read] }
]) {
id: ID !
owner: String !
readDate: AWSDateTime !
eventID: ID !
personID: ID ! @index(name: "byPerson2", queryField: "personTicketByPerson")
ownerPhone: AWSPhone !
visibility: Privacy !
ticket: Ticket ! @hasOne
person: Person ! @hasOne(fields: ["personID"])
}
"""
Description:
* Represents a common user. Participates in events and can purchase consumables. Also holds a wallet.
Directives:
* @model: Creates an object in our database
* @auth: Specifies who can access this object and what operations they can perform
* @hasOne: links a single profile picture with a user
* @manyToMany(PersonCompanyConnection): connects people to a set of companies
* @hasMany(byPerson1): connects one person to a set of consumables
* @hasMany(byPerson2): connects one person to a set of event tickets
* @hasMany(byPersonByDate1): allows linking a set of ticket transactions with a person and sorts by date
* @hasMany(byPersonByDate2): allows linking a set of consumable transactions with a person and sorts by date
* @hasMany(byPerson4): allows linking a set of wallet transactions with a person and sorts by date
Arguments:
* id: person's identity descriptor
* owner: reference to the owner of this object -> is the same as the owner's email
* name: person's name
* email: person's email
* phone: person's phone number
* dateOfBirth: user's date of birth
* university: person's university
* wallet: amount of money available for in-app purchases
* privacy: user's privacy definition
* currentEventID: event's ID which this person is currently in -> if is null, then the user is not in a party
* avatar: user's profile picture -> if is null, then we should show the default image on the frontend
* memberships: set of companyID's which the user is a member/associate/partner of
* consumables: list of consumables + amount that this user has bought
* tickets: list of events which this user will attend
* historyTicket: list of past ticket purchases
* historyConsumables: list of past consumables purchases
* historyWallet: list of past wallet transactions
"""
type Person @model
@auth(rules: [
# Only a person in Persons can create a reference to itself
{ allow: groups, groups: ["Person"], operations: [create] },
# The owner can update/delete their info
{ allow: owner, ownerField: "owner", operations: [update, delete, read] },
# An collaborator can update and read a user's info
{ allow: groups, groups: ["Collaborator"], operations: [update, read] }
]) {
id: ID !
owner: String !
name: String !
email: AWSEmail !
phone: AWSPhone !
dateOfBirth: AWSDateTime !
university: String
wallet: Float !
privacy: Privacy !
currentEventID: ID
avatar: Image @hasOne
memberships: [Company !] ! @manyToMany(relationName: "PersonCompanyConnection")
consumables: [PairPerson !] ! @hasMany(indexName: "byPerson1", fields: ["id"])
tickets: [PersonTicket !] ! @hasMany(indexName: "byPerson2", fields: ["id"])
historyTickets: [TicketTransaction !] ! @hasMany(indexName: "byPersonByDate1", fields: ["id"])
historyConsumables: [ConsumableTransaction !] ! @hasMany(indexName: "byPersonByDate2", fields: ["id"])
historyWallet: [WalletTransaction !] ! @hasMany(indexName: "byPerson4", fields: ["id"])
}
"""
Description:
* Represents a consumable inside our app (beer, hot dog, shots, etc...).
Directives:
* @model: Creates an object in our database
* @auth: Specifies who can access this object and what operations they can perform
* @index(byEventByPrice): allows connecting to an event and sorts all consumables by their price
* @index(byGroupByPrice): allows connecting to an group and sorts all consumables by their price
* @hasOne: allows connecting to a larger version of this consumable
Arguments:
* id: consumable's identity descriptor
* owner: reference to the owner of this object -> is the same as the owner's email
* type: is either a drink or a food item
* size: consumables can have different sizes
* name: consumable's name
* price: normal selling price
* overridePrice: normal selling price that becomes active after a certain date. Can be null
* memberPrice: company associate selling price. Can be null
* memberOverridePrice: company associate selling price that becomes active after a certain date. Can be null
* priceOverrideStartDate: date after which the official price will be changed
* priceOverrideEndDate: date after which the official price will be changed to the regular one
* eventID: holds event's id from which this consumable is from
* groupID: holds group's id which sells this consumable. If is null, then it is not associated with any group
* largeConsumable: larger version of this consumable -> if is null, then there is none
"""
type Consumable @model
@auth(rules: [
# Only a company can create a consumable for an event
{ allow: groups, groups: ["Company"], operations: [create] },
# Only the company that created the consumable can update and delete it's info
{ allow: owner, ownerField: "owner", operations: [update, delete] },
# Anyone can read this info
{ allow: public, provider: iam, operations: [read] }
]) {
id: ID !
owner: String !
type: ConsumableType !
size: ConsumableSize !
name: String !
price: Float !
overridePrice: Float
memberPrice: Float
memberOverridePrice: Float
priceOverrideStartDate: AWSDateTime
priceOverrideEndDate: AWSDateTime
eventID: ID ! @index(name: "byEventByPrice" , sortKeyFields: ["price"], queryField: "consumablesByEvent")
groupID: ID @index(name: "byGroupByPrice", sortKeyFields: ["price"], queryField: "consumablesByGroup")
largeConsumable: Consumable @hasOne
}
"""
Description:
* Represents a streak of consumables inside our app.
Directives:
* @model: Creates an object in our database
* @auth: Specifies who can access this object and what operations they can perform
* @index(byEvent4): allows connecting to an event and sorts all consumables by their name
* @hasOne: allows connecting a streak with a prize/promo
* @hasMany(byStreak): allows connecting a streak with a set of consumables that the user needs to buy
Arguments:
* id: streak's identity descriptor
* owner: reference to the owner of this object -> is the same as the owner's email
* name: streak's name
* eventID: id of the event that holds this consumables streak
* promoPrice: price of the pair in promotion
* promoPair: pair of consumable + amount that is given to the user as a promotion/reward
* set: group of consumables + amount that the user needs to buy
"""
type Streak @model
@auth(rules: [
# Only a company can create a streak for an event
{ allow: groups, groups: ["Company"], operations: [create] },
# Only the company that created the streak can update and delete it's info
{ allow: owner, ownerField: "owner", operations: [update, delete] },
# Anyone can read this info
{ allow: public, provider: iam, operations: [read] }
]) {
id: ID !
owner: String !
name: String !
eventID: ID ! @index(name: "byEvent4", sortKeyFields: ["name"], queryField: "streakByEvent")
promoPrice: Float !
promoPair: PairStreak ! @hasOne
set: [PairStreak !] ! @hasMany(indexName: "byStreak", fields: ["id"])
}
"""
Description:
* Represents an Image inside our app.
Directives:
* @model: Creates an object in our database
* @auth: Specifies who can access this object and what operations they can perform
* @index(byEvent3): allows connecting an image to an event
Arguments:
* id: image's identity descriptor
* owner: reference to the owner of this object -> is the same as the owner's email
* eventID: images can be related to events, so we need to store their ID -> can be null if it is not an event's image
* filename: path to the image in S3
* identityID: corresponds to the cognito identity identification and we need it to find the folder of this image
"""
type Image @model
@auth(rules: [
# Anyone can create an image and the owner can interact with it in any way
{ allow: owner, ownerField: "owner", operations: [create, update, delete, read] },
# Anyone can read this info
{ allow: public, provider: iam, operations: [read] }
]) {
id: ID !
owner: String !
eventID: ID @index(name: "byEvent3", queryField: "imageByEvent")
filename: String !
identityID: String !
}
"""
Description:
* Represents a consumable and an amount related to it. Can only be related to a Person.
Directives:
* @model: Creates an object in our database
* @auth: Specifies who can access this object and what operations they can perform
* @index(byPerson1): allows connecting set of pairs to a person
* @hasOne: Allows connecting to a single consumable
Arguments:
* id: pair's identity descriptor
* personID: associated person's id
* whoGaveIt: name of person/enterprise that gave this consumable
* amount: consumable's amount
* consumable: consumable's of this pair
"""
type PairPerson @model
@auth(rules: [
# Anyone can create a pair and the owner can interact with it in any way
{ allow: owner, ownerField: "owner", operations: [create, update, delete, read] },
# Anyone can read this info
{ allow: public, provider: iam, operations: [read] }
]) {
id: ID !
personID: ID ! @index(name: "byPerson1", sortKeyFields: ["amount"], queryField: "pairsByPersonByAmount")
whoGaveIt: String !
amount: Int !
consumable: Consumable ! @hasOne
}
"""
Description:
* Represents a consumable and an amount related to it. Can only be related to a Streak.
Directives:
* @model: Creates an object in our database
* @auth: Specifies who can access this object and what operations they can perform
* @index(byStreak): allows connecting set of pairs to a streak
* @hasOne: Allows connecting to a single consumable
Arguments:
* id: pair's identity descriptor
* streakID: associated streak's id
* amount: consumable's amount
* consumable: consumable's of this pair
"""
type PairStreak @model
@auth(rules: [
# Anyone can create a pair and the owner can interact with it in any way
{ allow: owner, ownerField: "owner", operations: [create, update, delete, read] },
# Anyone can read this info
{ allow: public, provider: iam, operations: [read] }
]) {
id: ID !
streakID: ID ! @index(name: "byStreak", queryField: "pairStreakByStreak")
amount: Int !
consumable: Consumable ! @hasOne
}
"""
Description:
* Represents a consumable and an amount related to it. Can only be related to a ConsumableTransaction.
Directives:
* @model: Creates an object in our database
* @auth: Specifies who can access this object and what operations they can perform
* @index(byTransaction): allows connecting set of pairs to a transaction
* @hasOne: Allows connecting to a single consumable
Arguments:
* id: pair's identity descriptor
* transactionID: associated transaction's id
* amount: consumable's amount
* wasUsedInPromo: signals that this transaction was made as part of a streak's promo
* streakID: holds streak id if this transaction was used as part of a streak's promo
* wasPersonConsumable: signals that this transaction was made with person's token consumables
* whoGaveIt: holds person/enterprise name that gave the user this token consumable
* consumable: consumable's of this pair
"""
type PairTransaction @model
@auth(rules: [
# Anyone can create a pair and the owner can interact with it in any way
{ allow: owner, ownerField: "owner", operations: [create, update, delete, read] },
# Anyone can read this info
{ allow: public, provider: iam, operations: [read] }
]) {
id: ID !
transactionID: ID ! @index(name: "byTransaction", queryField: "pairTransactionByTransaction")
amount: Int !
wasUsedInPromo: Boolean !
streakID: ID
wasPersonConsumable: Boolean !
whoGaveIt: String
consumable: Consumable ! @hasOne
}
"""
Description:
* Represents an artist and the datetime at which he will perform.
Arguments:
* name: artist's name
* performanceDateTime: date at which he will perform
"""
type Artist {
name: String !
performanceDateTime: AWSDateTime !
}
"""
Description:
* Represents a coordinates system to locate an object in the globe.
Arguments:
* lon: longitude floating point coordinate value
* lat: latitude floating point coordinate value
"""
type Location {
lon: Float !
lat: Float !
}
"""
Description:
* Is the default return type from our serverless functions.
Arguments:
* statusCode: html code that represents an action
* headers: CORS headers
* body: can contain an error message or a success message
* status: tells us if the function was successful or not
"""
type Response {
statusCode: Int
headers: AWSJSON
body: AWSJSON
status: Status
}
"""
Description:
* Is the default return type for our nearby events call.
Arguments:
* items: list of gathered events
* total: number of events returned
* nextToken: is used for pagination
"""
type ModelNearbyEventConnection {
items: [Event]
total: Int
nextToken: String
}
"""
Description:
* Our consumables can be either a drink or food (hot dogs, etc.). We use this distinction so that
we can have multiple tabs.
Arguments:
* FOOD: foods such as hot dogs, etc...
* SPIRITS: vodkas, whiskey, etc...
* SHOTS: shots and mixtures, etc...
* BEER: beer related drinks, etc...
* SANGRIA: sangria, etc...
* CIDER: cider, etc...
* NONALCOHOLIC: sodas, water, etc...
"""
enum ConsumableType {
BEER
SANGRIA
CIDER
SPIRITS
SHOTS
FOOD
NONALCOHOLIC
}
"""
Description:
* When a user adds money to their wallet, they can use one of the following methods.
Arguments:
* MBWAY: made a transactions with mbway
* CREDITCARD: made a transaction with a credit card
"""
enum PaymentMethod {
MBWAY
CREDITCARD
}
"""
Description:
* The payment requested by a user when he adds fund to their wallet can have one of the following states.
Arguments:
* PENDING: is still being proccessed
* APPROVED: is done and was approved
* DENIED: is done and was denied
"""
enum PaymentStatus {
PENDING
APPROVED
DENIED
}
"""
Description:
* Our consumables can have differente sizes and both have different values for leaderboard purposes.
Arguments:
* REGULAR: regular/normal consumable size
* LARGE: large consumable size
"""
enum ConsumableSize {
REGULAR
LARGE
}
"""
Description:
* Lambda return status.
Arguments:
* SUCCESS: lambda was successful
* FAILED: lambda was not successful
"""
enum Status {
SUCCESS
FAILED
}
"""
Description:
* Holds the type of visibility that our users wants.
Arguments:
* PRIVATE: no one can see this info besides the owner
* PROTECTED: only our friends can see this info
* PUBLIC: anyone can see this info
"""
enum Privacy {
PRIVATE
PROTECTED
PUBLIC
}
"""
Description:
* Represents a consumable + amount. Is only used in ConsumablePurchase lambda call.
Arguments:
* consumableID: id of the consumable thats is being bought
* amount: amount of this consumable that is being bought
"""
input ConsumableAmount {
consumableID: String !
amount: Int !
}
"""
Description:
* Used in TicketPurchase lambda call.
Arguments:
* personID: id of the user which is purchasing this ticket
* ticketID: id of ticket that is being bought
"""
input TicketPurchaseInput {
personID: String !
ticketID: String !
}
"""
Description:
* Used in ConsumablePurchase lambda call.
Arguments:
* personID: id of the user which is purchasing this set of consumables
* collaboratorID: id of collaborator that is performing this transaction
* consumables: set of consumableID + amount that is being bought
"""
input ConsumablePurchaseInput {
personID: String !
collaboratorID: String !
consumables: [ConsumableAmount !] !
}
"""
Description:
* Used in HasTicket lambda call.
Arguments:
* personID: id of the user which is entering the input event
* eventID: id of event which the user is trying to enter
"""
input HasTicketInput {
personID: ID !
eventID: ID !
}
"""
Description:
* Used in ScannerEncryption lambda call.
Arguments:
* info: string that is going to be encrypted
"""
input ScannerEncryptionInput {
info: String !
}
"""
Description:
* Used in ScannerDecryption lambda call.
Arguments:
* info: string that is going to be decrypted
"""
input ScannerDecryptionInput {
info: String !
}
"""
Description:
* Used in DeleteUser lambda call.
Arguments:
* username: user's username whose account will be deleted
"""
input DeleteUserInput {
username: ID !
}
"""
Description:
* Used in SendConsumable lambda call.
Arguments:
* personID: user's username
* phone: phone number of the user that we are sending a consumable to
* consumable: consumable + amount pair that is being sent
"""
input SendConsumableInput {
personID: ID!
phone: AWSPhone!
consumable: ConsumableAmount!
}
"""
Description:
* Used in CheckoutMBWay lambda call.
Arguments:
* value: value to be payed
"""
input CheckoutMBWayInput {
value: Float!
}
"""
Description:
* Used in FetchEventStatistics lambda call.
Arguments:
* eventID: id of the event
"""
input FetchEventStatisticsInput {
eventID: ID!
}
"""
Description:
* Used in geo-spatial query for events.
Arguments:
* lon: longitude floating point coordinate value
* lat: latitude floating point coordinate value
"""
input LocationInput {
lon: Float !
lat: Float !
}
"""
Description:
* Holds all our open search service calls
Arguments:
* nearbyEvents -> fetches the nearest events to the user or specific location like a city
"""
type Query {
nearbyEvents (
location: LocationInput !, # Coordinates
endDate: AWSDateTime !, # Event end date (used to filter results)
m: Int, # Maximum radius to search for in km
limit: Int, # Number of requested entries
nextToken: String # Pagination token
) : ModelNearbyEventConnection
}
"""
Description:
* Holds all our lambda functions.
Arguments:
* ticketPurchase -> purchases a ticket for a user
* consumablePurchase -> purchases a consumable for a user
* hasTicket -> checks if a user has a valid ticket an event
* deleteUser -> delete input user
* scannerEncryption -> encrypts user's username
* scannerDecryption -> decrypts user's username
"""
type Mutation {
ticketPurchase(input: TicketPurchaseInput !): Response
@function(name: "doTicketPurchase-${env}")
consumablePurchase(input: ConsumablePurchaseInput !): Response
@function(name: "doConsumablePurchase-${env}")
hasTicket(input: HasTicketInput !): Response
@function(name: "doHasTicket-${env}")
deleteUser(input: DeleteUserInput !): Response
@function(name: "doAuthDeleteUser-${env}")
scannerEncryption(input: ScannerEncryptionInput !): String
@function(name: "doUserEncryptionCall-${env}")
scannerDecryption(input: ScannerDecryptionInput !): String
@function(name: "doUserDecryptionCall-${env}")
sendConsumable(input: SendConsumableInput!): Response
@function(name: "doSendConsumable-${env}")
checkoutMBway(input: CheckoutMBWayInput!): Response
@function(name: "coCheckoutMBWay-${env}")
fetchEventStatistics(input: FetchEventStatisticsInput!): Response
@function(name: "doFetchEventStatistics-${env}")
}
"""
Description:
* Holds all our subscriptions.
Arguments:
* onPersonUpdate -> updates client app when an update happens on a specific user
"""
type Subscription {
onPersonUpdate(id: ID!): Person
@aws_subscribe(mutations: ["updatePerson", "createPerson"])
onCollaboratorUpdate(companyID: ID!): Collaborator
@aws_subscribe(mutations: ["createCollaborator", "updateCollaborator"])
}
Mobile Device
No response
Mobile Operating System
No response
Mobile Browser
No response
Mobile Browser Version
No response
Additional information and screenshots
No response
Issue Analytics
- State:
- Created 2 years ago
- Comments:6
@adilusman51 I will keep an eye on that pull request and wait for it to be merged befored trying to deploy our @searchable tag.
Thank you very much for warning us before wasting a week on that.
Issue has been solved. Due to the new update, all @auth deny every request by default and we just had to update the schema’s @auth tags to reflect those changes