-
Notifications
You must be signed in to change notification settings - Fork 25
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
feat(node): new unified code snippet for both the webhook and metrics #879
Conversation
* style: setup view * fix: lighthouse tweaks
* chore(node): tidy up types * test: add better types to testbed * chore: refresh lockfile i had to downgrade to npm@7 for this... absolutely cursed * chore: strict config fixes * chore: more TS fixes not sure why VS code didn't flag these the first time 🤔 also i know this is nuts but open to alternatives lol * chore: use promise.resolve generics Co-Authored-By: Jon Ursenbach <[email protected]> * test: add test for undefined webhook Co-Authored-By: Jon Ursenbach <[email protected]> * refactor: consolidate type this is the shit i live for * chore: slightly reorganize tsconfigs * revert: var change * refactor: add'l stronger types * refactor: another strict type Co-Authored-By: Bill Mill <[email protected]> * refactor: consolidate type Co-Authored-By: Bill Mill <[email protected]> * refactor: more type passes Co-Authored-By: Bill Mill <[email protected]> * chore: better comment * chore: another stricter type Co-Authored-By: Aaron Hedges <[email protected]> * refactor: clientIPAddress is required * revert: oops didn't mean to commit this * refactor: stricter typing for headers and method * chore: ts cleanup in test-verify-webhook file * refactor(setup readme view): use crypto, TS cleanup * chore: lint * refactor: some low hanging fruit in index.ts * chore: initial commit of example repo * chore: replace snippet with marc's snippet source: https://gist.github.com/mjcuva/c23a7c08d9528eeb8687ab83132622c1 * fix: some fixes to get the snippet working * chore: swap out api usage * chore: more type fixes i dislike a lot of these * fix: add JSON body parser to get onboarding working * fix: use CJS api SDK instead fixes this error: ``` /Users/kanadg/Code/readmeio/metrics-sdks/packages/node/.api/apis/developers/index.ts:1 import type * as types from './types'; ^^^^^^ SyntaxError: Cannot use import statement outside a module at Object.compileFunction (node:vm:360:18) at wrapSafe (node:internal/modules/cjs/loader:1124:15) at Module._compile (node:internal/modules/cjs/loader:1160:27) at Object.Module._extensions..js (node:internal/modules/cjs/loader:1250:10) at Module.load (node:internal/modules/cjs/loader:1074:32) at Function.Module._load (node:internal/modules/cjs/loader:909:12) at Module.require (node:internal/modules/cjs/loader:1098:19) at require (node:internal/modules/cjs/helpers:108:18) at Object.<anonymous> (metrics-sdks/packages/node/dist/src/index.js:55:36) at Module._compile (node:internal/modules/cjs/loader:1196:14) ``` might be an api bug? * fix: more typing fixes as a result of writing unit tests * test: a couple of unit tests (more to come) * revert: don't commit example server for now * fix(prettier): ignore .api directory * chore: attempt to rebuild lockfiles? * refactor: don't load in api as nested subpackage thing * Revert "refactor: don't load in api as nested subpackage thing" This reverts commit 650a8e3. * Revert "chore: attempt to rebuild lockfiles?" This reverts commit e260de6. * chore: try doing this again * chore: try this instead * test: migrate nocks over to MSW * revert: no need to export this * revert: don't fork tests * ci: run all tests * fix: move `.api` directory so TS picks it up * chore: lint * chore: lint, migrate new tests over to vitest * chore: regenerate `api` SDK, this time as TS * chore: one more lockfile cleanup have i mentioned i hate this? because i hate this * chore: one more lockfile refresh my god * chore: try this lockfile refresh * chore(lint): my dev environment is gaslighting me * test: wait there's no way that this is the fix lmao --------- Co-authored-by: Jon Ursenbach <[email protected]> Co-authored-by: Bill Mill <[email protected]> Co-authored-by: Aaron Hedges <[email protected]>
* feat: new getUser function * fix: types
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
does the sdk-snippets library need to get updated too?
metricsServerResponseCode = 202; | ||
server.use( | ||
...[ | ||
rest.post(`${config.host}/v1/request`, async (req, res, ctx) => { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@kanadgupta probably worth upgrading this to the latest msw
@@ -13,6 +13,7 @@ | |||
}, | |||
"include": [ | |||
"src/**/*", | |||
"src/.api/**/*", |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: is the not redundant when we already have src/**/*
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@kanadgupta added this so I'll let him chime in with more context!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
i think this is because tsconfig ignores directories that start with a period by default, but let me double check that. this may have been an accidental holdover from when i was getting the SDK up and running
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
yeah looks like those directories aren't included by default, see microsoft/TypeScript#49555
|
||
import findAPIKey from '../../src/lib/find-api-key'; | ||
|
||
// TODO: These tests were written by GPT-4 so probabbly aren't the best |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
lol
} | ||
|
||
it('should send requests to the metrics server', async function () { | ||
expect.assertions(10); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: the expect.assertions
is a little confusing. Could we sub it with toHaveBeenCalledTimes?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
cc @kanadgupta
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
mock functions aren't applicable here, expect.assertions
ensures that all the expect
statements over here are being hit properly:
metrics-sdks/packages/node/test/index.test.ts
Lines 139 to 141 in dd18d50
expect(body[0]._version).toBe(3); | |
expect(body[0].group).toStrictEqual(outgoingGroup); | |
expect(typeof body[0].request.log.entries[0].startedDateTime).toBe('string'); |
this pattern isn't great and i'm curious if others that have been in the weeds with MSW have better ideas for asserting request header content (or if that's even necessary) // @erunion
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
doing expect()
in here or having the MSW mock return a 500 status code with a specific error is probably the best way to handle these
} | ||
|
||
const securityRequirements = operation.getSecurity(); | ||
return securityRequirements.reduce((acc: string[], curr) => { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
just want to make sure I'm reading this right, but securityRequirements is an array of objects and then we reduce that to an array of all the keys on that array of objects? Does it matter that we may have duplicate entries of the same key?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@jamestclark added this but I'm struggling to find how it is used. It's possible this existed in the main codebase and we reused parts of it in the sdk? However I don't think we have the OAS file at any point so I'm not sure
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Doesn't matter that we may have duplicates as we just loop through the array and find the first matching from the users apiKey.
And yeah it's not used directly by the metrics SDK. As the code is very similar to what we already had in the main codebase he idea here is to co-locate all the securityRequirements code here and have the main codebase use this once it's published.
Adding @azinder1 as a reviewer here so he has context on unified snippet work (this came up just now in Metrics Pod sync!) |
@erunion We likely will need to update sdk-snippets, but we're updating the UI in the dashboard separate from this work so I think we can just do that in another pr! |
packages/node/src/lib/ReadMe.ts
Outdated
* your dashboard. You can read more about the API key in | ||
* [our docs](https://docs.readme.com/reference/authentication). | ||
*/ | ||
function ReadMe(key: string) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
i'm curious about others' opinions on this but my understanding of JS conventions is that variable names should only be capitalized in the case of classes/constructors (e.g., new Class()
, etc.) — thoughts on lowercasing it? what about calling this metrics
or something like that?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sentry was my inspo here and they don't do that! https://docs.sentry.io/platforms/node/#configure
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
interesting! sentry's SDK always gave class-like energy with the whole Sentry.init
constructor pattern and it's apparently using AsyncLocalStorage
to do that (which i don't totally understand...)
another idea here — what if you made this a class with a constructor? your example would look mostly the same (arguably a little cleaner + more intuitive to someone that expects JS conventions, in my opinion) and that way you can store these values as properties rather than globally which feels a little safer:
import { ReadMe } from 'readmeio';
- const readme = ReadMe(key);
+ const readme = new ReadMe(key);
app.use(readme.express(() => {}));
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think it's fine the way it is! It mimics other popular sdks and I don't really want to refactor this anymore tbh. I think it's important we get something out and stop going back and forth on nuances of the design!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
agreed that it's a little weird to have a function be cased this way when that's generally how classes are defined + used.
what other popular sdks have exports like this?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@kanadgupta I like the idea! It's definitely best practice from an initialization perspective, and would properly set expectations for consuming developers.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think if it's fine for Sentry it's understandable enough for our use case. I'm gonna move forward with it as it is! This PR is already way too long lived and we need to call off the rewriting somewhere. If someone else wants to take over this work feel free!
## 🧰 Changes This updates the SDK to be a class. As part of this, the signature is slightly updated: ```diff import { ReadMe } from 'readmeio'; - const readme = ReadMe(key); + const readme = new ReadMe(key); app.use(readme.express(() => {})); ``` ## 🧬 QA & Testing Strongly suggest hiding whitespace before reviewing! Does this signature look good? Do tests still pass?
Redo the node code snippet for the webhook/metrics! We now have one code snippet that handles both cases super easily.
Other things:
development: true
to make sure that it won't be enabled in prod on accident.Update 1/31
ReadMe
export that you call to pass in the API Key. Before we had a separateauth
export that was required to be called before the middelware, which was confusing. This also cleans up how you call it (no more readmeio.readme stuff). This matches Sentry's sdks closer which makes me feel better about it.Testing
readmeio@next
to test the new snippetThings to look out for: