Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

upgrade apollo #2330

Open
wants to merge 34 commits into
base: main
Choose a base branch
from
Open

Conversation

lperson
Copy link
Collaborator

@lperson lperson commented Dec 22, 2023

Fixes #2329

Description

Upgrade apollo components. I needed to upgrade the server components as well because they coexist in package.json.

Checklist:

  • I have manually tested my changes on desktop and mobile
  • The test suite passes locally with my changes
  • If my change is a UI change, I have attached a screenshot to the description section of this pull request
  • My change is 300 lines of code or less, or has a documented reason in the description why it’s longer
  • I have made any necessary changes to the documentation
  • I have added tests that prove my fix is effective or that my feature works
  • My PR is labeled [WIP] if it is in progress

@lperson lperson marked this pull request as draft December 22, 2023 15:37
@lperson lperson added the S-work in progress (WIP) Status: PR label for work that is not yet ready to be reviewed label Dec 22, 2023
Updates the versions in `package.json` so they install with no
warnings about peer dependencies.
@@ -1,10 +1,12 @@
import {
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In addition to my changing the imports VS code rearranged them.

@@ -1,5 +1,4 @@
import { log } from "../../../lib";
import { Assignment, r, cacheableData } from "../../models";
import { r, cacheableData } from "../../models";
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

deleted unused imports

@@ -363,7 +363,7 @@ it("should save campaign canned responses across copies and match saved data", a
testAdminUser
);
expect(campaignDataResults.data.campaign.cannedResponses.length).toEqual(6);
for (let i = 0; i < 6; i++) {
for (let i = 0; i < 6; i += 1) {
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The linter complained about i++ so I changed it to i += 1 in several places

assignmentId
});

const waitFor = [];
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The linter complained about using await inside a loop so I changed it to use this pattern.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That was a good call. Can you tell me how you decided that it should be an empty array?

Copy link
Collaborator Author

@lperson lperson Dec 29, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not sure I understand the question?

The idea about the linter complaint and the change was to take await out of the loop. We use an array to pass promises to Promise.all() after the loop. We need to start with an empty array to add the promises to.

The whole thing probably could have been done differently, maybe something like the following (as someone did elsewhere in the file)

Promise.all(testContacts.slice(0,5).map(contact => sendMessage(contact.id, testTexterUser, {
            userId: testTexterUser.id,
            contactNumber: contact.cell,
            text: "test text",
            assignmentId: assignmentId.toString()
          }))

* Changed arguments to `graphql` to match new signature and in some
cases renamed variables to make it more convenient to use them in
objects because of shorthand.
* Apollo client 3 is stricter about typing so I added some type coersion
in the tests
* Cleaned some linter warnings about using `await` in loops and using
the ++ operator

refer to comments in the PR for callouts about some changes
mySchema,
operationText,
const contextValue = getContext({ user });
const result = await graphql({
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Changed arguments to match new signature.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What is the new signature in reference to?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

the graphql function takes different parameters in the updated version compared to the older version

* Updated parameters for new signature of `graphql`
* Coerced types for stricter graphql type checking
* Cleaned up lints
throw new GraphQLError({
status: 401,
message: "You must login to access that resource."
throw new GraphQLError("You must login to access that resource.", {
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Updated for new signature.

@@ -166,12 +178,15 @@ describe("Message Hanlder: profanity-tagger", () => {
expect(user).toBe(true);

// Confirm texter no-match
await sendMessage(c.testContacts[1].id, c.testTexterUser, {
userId: c.testTexterUser.id,
await sendMessage(c.testContacts[1].id.toString(), c.testTexterUser, {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Curious question @lperson

Should we change the data type within the test or within the code?

Or at least, could you help me understand why we would stringify the data result instead of ensuring that the data coming in from testContacts[1].id is already a string?

That would ensure that the source is the correct data type, and instead we add an expect clause that confirms it is a string?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

GREAT question!

I think we're ok here. In the client code we're getting all the values via graphql interfaces and they will come in as strings. In the tests we're taking integers from the database (the IDs) and passing them to graphql queries and mutations.

I don't think it's a good idea to change the type in the interface in order to avoid calling toString() and parseInt() in tests.

My reasoning when I made these changes was the UI code is internally consistent and the server code is internally consistent.

However I'm keeping an open mind in case my assumptions fall apart when I start banging on the UI (which is now).

@@ -16,12 +16,14 @@ export const GraphQLPhone = new GraphQLScalarType({
if (ast.kind !== Kind.STRING) {
throw new GraphQLError(
`Query error: Can only parse strings got a: ${ast.kind}`,
[ast]
{ nodes: [ast] }
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What does nodes: [ast] do?

Is this a new dictionary process within graphql? With a data-type change this significant, I'm just curious how you knew this line needed to be updated. And how you knew this was the correct format?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A combination of https://www.apollographql.com/docs/apollo-server/data/errors/ and https://graphql-kr.github.io/graphql-js/error/

This code needs another pass though, which I'll do in a later commit.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nodes: [ast] passes the abstract syntax tree for the graphql query into the error object. The particular code you highlighted looks at the ast and decides if something passed as a phone number looks like a phone number and raises an exception if not. This is happening in the parseLiteral hook which gives us a way to inspect and possibly alter a literal during parsing. GraphQLPhone is a custom type in our code to handle phone numbers.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Interesting! So I checked out the custom type hook you mentioned.

Confirming this is the correct file path: src/server/api/phone.js

The above file is imported to as GraphQLPhone inside our schema src/server/api/schema.js and then exported within the constant resolvers as a value for Phone.

I'm looking for the functionality where you say we could "possibly alter a literal." And now I see that the parseLiteral hook is being used on line 15 of the phone.js file.

Would this functionality get in the way of seeing a toll-free number or a short-code?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I do not know. This is not new functionality. It might be worth entering a ticket to test that and possibly fix it.

For contacts, it's unlikely we'll be using a toll-free number or short code to reach them. This code would be problematic if was used for the phone numbers we use to originate the texts, such as in the number purchasing code (as one example).

@@ -509,8 +511,8 @@ export class AssignmentTexterContactControls extends React.Component {

const otherResponsesLink =
currentInteractionStep &&
currentInteractionStep.question.filteredAnswerOptions.length > 6 &&
filteredCannedResponses.length ? (
currentInteractionStep.question.filteredAnswerOptions.length > 6 &&
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

From here down in this file all the changes are whitespace changes because my editor autoformatted the code.

@@ -125,7 +125,9 @@ export class AssignmentTexterContactControls extends React.Component {
let currentInteractionStep = null;

if (availableSteps.length > 0) {
currentInteractionStep = availableSteps[availableSteps.length - 1];
currentInteractionStep = structuredClone(
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do a deep copy of data that originated in props to avoid modifying props

@@ -13,8 +13,10 @@ export const isRoleGreater = (role1, role2) =>
export const hasRoleAtLeast = (hasRole, wantsRole) =>
ROLE_HIERARCHY.indexOf(hasRole) >= ROLE_HIERARCHY.indexOf(wantsRole);

export const getHighestRole = roles =>
roles.sort(isRoleGreater)[roles.length - 1];
export const getHighestRole = roles => {
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

make a copy so we don't copy something that came from props

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It looks like you added tempRoles. Is this solely to remove an error message within development? Or is there a larger function here that's being updated?

Copy link
Collaborator Author

@lperson lperson Jan 7, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

tempRoles is is a variable to hold a deep copy of roles.

  1. Practical reason: with the updates, the code barfs when we try to modify a parameter of a react component. And by barf I mean it's a runtime error.
  2. Best practice: don't modify your function's parameters!

@@ -1,4 +1,3 @@
import { GraphQLError } from "graphql/error";
import { getConfig } from "../../../server/api/lib/config";
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It looks like getConfig isn't being used in this file either. Should that be deleted also?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, done! Good catch!

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There are lots of places like this to clean up -- unused imports and other lints. I'm interested in doing that or maybe someone else could do it. I'm using vscode with integrated linting and I like to look at clean files (personal preference) but it's also a good practice to have zero lints.

@schuyler1d
Copy link
Collaborator

I have tested this branch and haven't found any regressions

@electiondaze electiondaze marked this pull request as ready for review February 15, 2024 15:50
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
S-work in progress (WIP) Status: PR label for work that is not yet ready to be reviewed
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Apollo Client Update v1 to v3
3 participants