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

chore(node): stricter types #900

Merged
merged 22 commits into from
Aug 16, 2023
Merged

chore(node): stricter types #900

merged 22 commits into from
Aug 16, 2023

Conversation

kanadgupta
Copy link
Member

@kanadgupta kanadgupta commented Aug 14, 2023

🧰 Changes

As part of QA'ing Marc's changes in #879, I noticed our TypeScript config for the Node SDK is pretty loosey-goosey so just tightening it up a bit!

🧬 QA & Testing

No actual code changes EDIT: as of 802a65c, there are minor code changes (still seemingly harmless, mostly fallback values in a few places). Do the TS build + tests still pass?

@kanadgupta kanadgupta added area:node refactor Issues about tackling technical debt labels Aug 14, 2023
i had to downgrade to npm@7 for this... absolutely cursed
@kanadgupta kanadgupta marked this pull request as ready for review August 14, 2023 22:06
@kanadgupta kanadgupta requested review from erunion, domharrington and a team August 14, 2023 22:06
@kanadgupta kanadgupta requested a review from erunion August 14, 2023 22:39
kanadgupta and others added 3 commits August 14, 2023 17:52
not sure why VS code didn't flag these the first time 🤔

also i know this is nuts but open to alternatives lol
@kanadgupta kanadgupta requested a review from erunion August 14, 2023 22:58
@kanadgupta kanadgupta requested a review from Dashron August 15, 2023 13:03
packages/node/src/lib/object-to-array.ts Outdated Show resolved Hide resolved
packages/node/src/lib/patch-response.ts Outdated Show resolved Hide resolved
packages/node/src/lib/process-request.ts Outdated Show resolved Hide resolved
packages/node/src/lib/process-request.ts Show resolved Hide resolved
}

return requestData;
return requestData as Request;
Copy link
Contributor

@llimllib llimllib Aug 15, 2023

Choose a reason for hiding this comment

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

You could remove this assertion if we can pass castToString for headers and make method a string:

   const requestData = {
-    method: req.method,
+    method: req.method || '',
     url: url.format({
       // Handle cases where some reverse proxies put two protocols into x-forwarded-proto
       // This line does the following: "https,http" -> "https"
@@ -206,7 +206,7 @@ export default function processRequest(
       query: qs.parse(reqUrl.search.substring(1)),
     }),
     httpVersion: `${getProto(req).toUpperCase()}/${req.httpVersion}`,
-    headers: objectToArray(req.headers),
+    headers: objectToArray(req.headers, { castToString: true }),
     queryString: searchToArray(reqUrl.searchParams),
     postData,
     // TODO: When readme starts accepting these, send the correct values
@@ -221,5 +221,5 @@ export default function processRequest(
     return remainingRequestData as Request;
   }
 
-  return requestData as Request;
+  return requestData;
 }

I'm not 100% sure it's valid to convert the headers into strings? ISTM that it probably is but I don't know - I just dislike using type assertions when possible, and I'd rather change our return type if headers may not be a string than to just promise that it is and then have it not be at runtime

Copy link
Member Author

Choose a reason for hiding this comment

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

yeah I like this suggestion but I'm wondering if there was a deliberate reason why we're not setting that castToString flag here. again this is another example of being unsure of what our ingestion service likes

Copy link
Member

Choose a reason for hiding this comment

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

I'd be pretty concerned if we passed { method: '' }

Copy link
Member Author

Choose a reason for hiding this comment

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

Riiiight, but for some reason req.method can be undefined per the official Node types 🤔 I'll leave that fallback in there for now knowing that it probably won't get hit at all, but let me know if you disagree here!

Copy link
Member Author

@kanadgupta kanadgupta Aug 15, 2023

Choose a reason for hiding this comment

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

ok on second thought I decided to enable the castToString flag on this line:

headers: objectToArray(req.headers, { castToString: true }),

is this a safe change to make @gratcliff? see the full commit here: 1585f05

packages/node/src/lib/process-response.ts Outdated Show resolved Hide resolved
packages/node/src/lib/process-response.ts Outdated Show resolved Hide resolved
@@ -34,21 +34,23 @@ export interface GroupingObject {
export interface OutgoingLogBody {
_id?: string;
_version: number;
clientIPAddress: string;
clientIPAddress?: string;
Copy link
Member

Choose a reason for hiding this comment

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

Are we expecting this to be null occasionally? The value should always be here, right?

Copy link
Member Author

Choose a reason for hiding this comment

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

updated this type in af9ce2a, what do you think? this is happening because req.socket.remoteAddress can be undefined on this line:

clientIPAddress: req.socket.remoteAddress || '',

packages/node/src/lib/metrics-log.ts Show resolved Hide resolved
}

return requestData;
return requestData as Request;
Copy link
Member

Choose a reason for hiding this comment

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

I'd be pretty concerned if we passed { method: '' }

@@ -184,7 +187,7 @@ export default function processRequest(
// We use a fake host here because we rely on the host header which could be redacted.
// We only ever use this reqUrl with the fake hostname for the pathname and querystring.
// req.originalUrl is express specific, req.url is node.js
const reqUrl = new URL(req.originalUrl || req.url, 'https://readme.io');
const reqUrl = new URL(req.originalUrl || req.url || '', 'https://readme.io');
Copy link
Contributor

Choose a reason for hiding this comment

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

is empty string really valid here, or is it just to cover the possibility of a falsey type? If it's not a real possibility here it might be better to throw an error early so that the user is notified of an issue with urls.

Copy link
Member Author

Choose a reason for hiding this comment

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

yeah I think there are opportunities to rework the logic to throw better validation errors (yay TS strict mode!) but I'm trying to confine the scope of the changes in this PR to fallback values where needed and avoid any bigger logic changes

@kanadgupta kanadgupta requested a review from gratcliff August 15, 2023 21:11
@@ -41,14 +41,16 @@ export interface OutgoingLogBody {
request: Har;
}

type LogId = string | string[] | undefined;
Copy link
Member

Choose a reason for hiding this comment

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

If you wanted, you could include the uuid type here and consume it instead of string.

Copy link
Member Author

Choose a reason for hiding this comment

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

it was slightly annoying since the uuid npm package doesn't play nicely with Node's baked in UUID type, but i like this idea — see here! b5c3455

Copy link
Contributor

Choose a reason for hiding this comment

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

why are we using an npm package instead of randomUUID from node:crypto?

Copy link
Member Author

Choose a reason for hiding this comment

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

Copy link
Contributor

Choose a reason for hiding this comment

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

doh! thanks for nothing, ide auto-fill

Copy link
Contributor

Choose a reason for hiding this comment

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

(was the UUID type added before the uuid function?)

Copy link
Member Author

Choose a reason for hiding this comment

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

wait weird, apparently the Node.js docs say that randomUUID has been available since Node 14/15? https://nodejs.org/api/crypto.html#cryptorandomuuidoptions

Copy link
Member Author

Choose a reason for hiding this comment

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

so maybe we could refactor things to use that, at least once we drop support for Node 14

value: unknown;
}[];

export function objectToArray(
Copy link
Member

Choose a reason for hiding this comment

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

Why export duplicate functions here?

Copy link
Member Author

Choose a reason for hiding this comment

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

it's called a function overload! since this function accepts an optional opts object, the first overload allows us to specify a more specific return type :chefs-kiss:

(this is literally my first time using them and I love 'em)

Co-Authored-By: Gabe Ratcliff <[email protected]>
@kanadgupta kanadgupta merged commit 9c35f38 into main Aug 16, 2023
@kanadgupta kanadgupta deleted the fix/stricter-types branch August 16, 2023 17:07
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
refactor Issues about tackling technical debt
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants