--- tags: Dungeon Master --- # Dungeon Master GraphQL Schema Notes This is a list of GraphQL schema notes in order to accomplish the following userflows: - Consultation comes in from our site → A logged in Cleric (verified their role) claims a Consultation and self-assigns → Consultation then becomes a Raid → They add Raiders to the Raid - Raids can then be updated by the Cleric (and/or) Raid Party - Logged in Raiders can see Raids they're on and a list of Raids by status I started adding some of these but ran into some issues with needing to make sure our collections were connected together. ## Types **General Notes:** - Do we want to change the type for `id` to `ID` instead of `String` ? `ID` is a special string behind the scenes so would likely work the same way with the Mongo schema ### Enums - Do we want to add the `enums` from `constants.js` into our typedefs? For example: ```graphql enum PreferredContact { DISCORD EMAIL TELEGRAM } ``` - Doing this would allow for us to restrict input / output options at the GraphQL level instead of at the database level. This is useful but there are some limitations in formatting (such as no special characters) - If we want to add these we could pull in all enums from `constant.js` into `typedefs.js` ### Date Scalar - Do we want to add a Date scalar so that we can use custom Dates instead of strings? - (example) ## Queries These queries for grabbing individual data by `id` — right now the `id` is of type `String` but we could consider using the GraphQL type `ID` **member** ```graphql member(id: String): Member ``` **consultation** ```graphql consultation(id: String): Consultation ``` **application** ```graphql application(id: String): Application ``` **portfolio** ```graphql portfolio(id: String): Portfolio ``` **comment** ```graphql comment(id: String): Comment ``` We also may want to consider querying `Member` by `ethAddress` as well: - If a Member logs in with MetaMask and we have their `ethAddress`, we could then use that to look up their Member info for role, current Raids, etc. ## Resolvers - If we add the individual data queries (such as `member` `consultation` etc. we'd need to add the resolvers, such as: ```javascript async raid(_parent, args, _context, _info) { const { id } = args; const response = await raid.findById(id); return response; }, ``` 7/22/21: I've added the remaining resolvers. **member(id)** ```javascript async member(_parent, { id }, _context, _info) { const response = await member.findById(id); return response; }, ``` **memberByEthAddress(eth_address)** ```javascript async memberByEthAddress(_parent, { eth_address }, _context, _info) { const response = await member.findOne({ eth_address: eth_address }); return response; }, ``` **consultation(byid)** ```javascript async consultation(_parent, { id }, _context, _info) { const response = await consultation.findById(id); return response; }, ``` **application(id) ** ```javascript async application(_parent, { id }, _context, _info) { const response = await application.findById(id); return response; }, ``` **portfolio(id)** ```javascript async portfolio(_parent, { id }, _context, _info) { const response = await portfolio.findById(id); return response; }, ``` **comment(id)** ```javascript async comment(_parent, { id }, _context, _info) { const response = await comment.findById(id); return response; }, ``` --- ### Nested/Chained Resolvers NOTE: In our discussion on 7/22, we determined these likely aren't necessary. - We'll want to create some additional nested/chained resolvers to make sure that our data collections are connected. For example, if we query RaidParty within Raid, we'd want to resolve the RaidParty first, which itself requires us to resolve Member. Here are the connections we likely we need: - `Consultation` and `Raid` - `Application` and `Member` - `Raid` and `RaidParty` - `RaidParty` and `Member` - `Comment` and `Raid` We'd want to add these into `resolvers.js` at the same level as `Query` and `Mutation` so they can resolve themselves before being used in queries. This is different than the `raid` query. ```graphql RaidParty: { raid(parent) { //filter db to find and return RaidParty connected to the connected raid -- something like return raidparties.filter((raidparty) => raidparty.raid === parent.raid); }, }, ``` Resolver pattern for getting Raiders on a raid would be: Raid(id) -> connected RaidParty -> RaidParty Members. This would allow us to query something like: ```graphql query Raid { raid(id: "60e47c50c78dad9c697aa4f9") { raid_name status raid_party { members { name guild_class } } } } ``` This may not be needed depending on how the data is going into the database, but if we wanted to be safe we could add these resolvers. I may be overthinking this so definitely want to talk through it. ## Inputs We could add `input` types for each of our queries, such as the `ConsultationInput`: ```graphql input ConsultationInput { id: ID project_name: String! contact_name: String! contact_email: String! contact_bio: String! contact_discord: String contact_telegram: String preferred_contact: String! project_type: String! project_specs: String! specs_link: String project_desc: String! services_req: [String!]! desired_delivery: String budget: String! delivery_priorities: String! additional_info: String! submission_type: String! consultation_hash: String submission_date: String! feedback: String rating: Int raid: RaidInput } ``` - These can reference each other — for example, `raid` can reference a `RaidInput` once created ## Mutations - There are some additional mutations we'd want to add (and then also add the resolvers) based on everything in the create route. I started adding these, but wanted to make sure we had the resolvers for connecting collections together. - They'd share the same shape. I stopped after `createRaid` because I wanted to think through the connected resolvers. - I commented out some of the fields until we figure out the nested resolvers (although that may not be necessary, we may handle that on the query side) ```graphql createRaid: async (_, args, context, info) => { const { raid_name, status, category, cleric_name, roles_required, // raid_party, invoice_address, start_date, end_date, // comments, // related_raids, } = args.raid; const newRaid = new raid({ raid_name, status, category, cleric_name, roles_required, // raid_party, invoice_address, start_date, end_date, // comments, // related_raids, // portfolio, }); await raid.create(newRaid); console.log(newRaid); return newRaid; }, ```