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

check if the url resolves before adding the monitor #946

Open
wants to merge 22 commits into
base: develop
Choose a base branch
from

Conversation

om-3004
Copy link
Contributor

@om-3004 om-3004 commented Oct 12, 2024

Fixes #513 (Check if endpoint URL resolves before adding the monitor)

  • Made the button content dynamic
  1. When Website Monitoring selected:
    Screenshot 2024-10-13 000650

  2. When Ping Monitoring selected:
    Screenshot 2024-10-13 000702

  • When Website Monitoring is selected, check if the entered url is resolved before adding the monitor
    Screenshot 2024-10-13 000720

Copy link

coderabbitai bot commented Oct 12, 2024

Walkthrough

The CreateMonitor and CreatePageSpeed components have been updated to include a new endpoint resolution check during the monitor creation process. A state variable, isLoading, has been introduced to manage the loading state while the endpoint is verified. The handleCreateMonitor function now dispatches the checkEndpointResolution action from uptimeMonitorsSlice for "http" monitor types. If the endpoint check fails, an error message is displayed, and the form submission is halted. The button label dynamically updates based on the loading state.

Changes

File Path Change Summary
Client/src/Pages/Monitors/CreateMonitor/... Updated to include endpoint resolution check and improved error handling. Button label reflects checking status.
Client/src/Pages/PageSpeed/CreatePageSpeed/... Integrated checkEndpointResolution for endpoint validation. Added loading state management.
Client/src/Features/UptimeMonitors/uptimeMonitorsSlice.js Added checkEndpointResolution asynchronous thunk for endpoint validation. Updated extraReducers.
Client/src/Utils/NetworkService.js Introduced checkEndpointResolution method for sending GET requests to check endpoint resolution.
Server/controllers/monitorController.js Added checkEndpointResolution function to check DNS resolution of endpoints.
Server/openapi.json Updated OpenAPI specification to include new endpoint /monitors/resolution/url with required parameters.
Server/tests/controllers/monitorController.test.js Added test suite for checkEndpointResolution function covering various scenarios.
Server/validation/joi.js Introduced validation schema for monitorURL and updated monitorId validation to require URI format.

Assessment against linked issues

Objective Addressed Explanation
Check if endpoint URL resolves before adding the monitor (#513)
Display a notification if the endpoint doesn't resolve (#513)
Change button label to "Checking the endpoint" during validation (#513) The button label changes to "Checking endpoint" while the check is in progress.

Possibly related PRs

  • Use getMonitorsByTeamId endpoint #840: The changes in this PR involve the introduction of a local loading state (isLoading) in the PageSpeed component, which aligns with the loading state management introduced in the main PR for the CreateMonitor and CreatePageSpeed components.
  • Feat/be/monitor sort and filter, #498, #704 #851: This PR enhances the getMonitorsByTeamId function to accept a configuration object, which includes parameters relevant to monitor retrieval, similar to the new endpoint resolution check implemented in the main PR.
  • Feat/fe/monitor table sort, resolves #498 #852: The sorting functionality added in this PR for the monitor table directly relates to the overall monitor management enhancements made in the main PR, particularly in how monitors are displayed and interacted with.

Suggested reviewers

  • ajhollid
  • Skorpios604
  • danielcj2

Thank you for using CodeRabbit. We offer it for free to the OSS community and would appreciate your support in helping us grow. If you find it useful, would you consider giving us a shout-out on your favorite social media?

❤️ Share
🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

‼️ IMPORTANT
Auto-reply has been disabled for this repository in the CodeRabbit settings. The CodeRabbit bot will not respond to your replies unless it is explicitly tagged.

  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query. Examples:
    • @coderabbitai generate unit testing code for this file.
    • @coderabbitai modularize this function.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read src/utils.ts and generate unit testing code.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.
    • @coderabbitai help me debug CodeRabbit configuration file.

Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments.

CodeRabbit Commands (Invoked using PR comments)

  • @coderabbitai pause to pause the reviews on a PR.
  • @coderabbitai resume to resume the paused reviews.
  • @coderabbitai review to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
  • @coderabbitai full review to do a full review from scratch and review all the files again.
  • @coderabbitai summary to regenerate the summary of the PR.
  • @coderabbitai resolve resolve all the CodeRabbit review comments.
  • @coderabbitai configuration to show the current CodeRabbit configuration for the repository.
  • @coderabbitai help to get help.

Other keywords and placeholders

  • Add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

Documentation and Community

  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 5

🧹 Outside diff range and nitpick comments (1)
Client/src/Pages/Monitors/CreateMonitor/index.jsx (1)

177-178: Provide more specific error feedback

The error message "The entered URL is not reachable." might leave users feeling queasy without clear guidance. Enhance the message to help users troubleshoot effectively.

Consider specifying why the URL might not be reachable and suggest checking their internet connection or URL correctness.

📜 Review details

Configuration used: .coderabbit.yaml
Review profile: CHILL

📥 Commits

Files that changed from the base of the PR and between d3659d3 and 4a1b290.

📒 Files selected for processing (1)
  • Client/src/Pages/Monitors/CreateMonitor/index.jsx (2 hunks)
🧰 Additional context used
🔇 Additional comments (1)
Client/src/Pages/Monitors/CreateMonitor/index.jsx (1)

167-174: Handle monitor creation failures gracefully

If creating the monitor fails, provide users with more information to avoid sweaty palms.

Ensure that the createUptimeMonitor action returns meaningful error messages. You can verify the error handling with this script:

@bluewave-labs bluewave-labs deleted a comment from coderabbitai bot Oct 13, 2024
@bluewave-labs bluewave-labs deleted a comment from coderabbitai bot Oct 13, 2024
@bluewave-labs bluewave-labs deleted a comment from coderabbitai bot Oct 13, 2024
@bluewave-labs bluewave-labs deleted a comment from coderabbitai bot Oct 13, 2024
@bluewave-labs bluewave-labs deleted a comment from coderabbitai bot Oct 13, 2024
Copy link
Contributor

@ajhollid ajhollid left a comment

Choose a reason for hiding this comment

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

I appreciate the effort put in here, however I believe a more comprehensive approach here is to proxy this URL to the back end so that we can get a non-opaque response back from the server we are trying to connect to.

If you would like to tackle this one it's a bit more invovled than the naive solution of sending a no-cors request from the front end. If you feel like jumping on it let us know, otherwise we can close this PR and revisit the issue when we have bandwidth to do so.

notifications: monitor.notifications,
try {
// Check if the URL resolves by sending a request
const response = await fetch(formattedUrl, { method: 'GET', mode: 'no-cors' });
Copy link
Contributor

Choose a reason for hiding this comment

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

Checking if the URL resolves should be done on the back end, sending a no-cors request from the front end won't tell us if the server we want to reach is reachable or not.

The response is opaque, so we have no idea what is in the response, just that we got something back. If there was a proxy inbetween us and the server for example we could still get a 200 response, but the server might not be reachable.

We should implement a more comprehensive solution which would look like this:

Front End sends GET request to Back End -> BE forwards request to Server URL -> BE returns response to FE

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🧹 Outside diff range and nitpick comments (5)
Server/routes/monitorRoute.js (1)

21-25: Yo, this endpoint's lookin' fresh, dawg! But let's make it bulletproof, ya feel me?

The new endpoint for checkin' if the URL resolves is solid gold, homie. It's locked down tight with that isAllowed middleware, just like the other VIP routes. But yo, we gotta think about what happens if somethin' goes sideways, you know what I'm sayin'?

How 'bout we add some error handling to this bad boy? Check it:

 router.get(
   "/check-endpoint/url",
   isAllowed(["admin", "superadmin"]), 
-  monitorController.checkEndpointResolution
+  async (req, res, next) => {
+    try {
+      await monitorController.checkEndpointResolution(req, res);
+    } catch (error) {
+      next(error);
+    }
+  }
 )

This way, if the controller throws up (pun intended, mom's spaghetti), we catch that error and pass it to the next middleware. It's like havin' a safety net for our code, you feel me?

Client/src/Features/UptimeMonitors/uptimeMonitorsSlice.js (2)

34-56: Yo, this thunk's got the flow, but let's make it glow!

The new checkEndpointResolution thunk is lookin' fresh, followin' the same beat as the other thunks in this track. It's handlin' the authToken and monitorURL like a pro, just what we needed for this endpoint check feature.

But yo, check this out - we could make the error handlin' even smoother. Instead of repeatin' ourselves like a broken record, how 'bout we create a helper function for that error logic? It'd make our code as clean as mom's spaghetti (before it ended up on the sweater, ya feel me?).

Wanna see how we could clean this up? Just say the word, and I'll drop some code that'll make this error handling slicker than Eminem's rhymes.

🧰 Tools
🪛 Biome

[error] 46-46: Change to an optional chain.

Unsafe fix: Change to an optional chain.

(lint/complexity/useOptionalChain)


298-314: These reducers are fire, but let's turn up the heat!

Yo, these new reducers for checkEndpointResolution are droppin' beats in perfect sync with the rest of the track. They're handlin' the state like a pro DJ, keepin' that isLoading, success, and msg in perfect harmony.

But check it, we got a chance to make our error messages as consistent as a metronome. Peep this:

state.msg = action.payload?.msg ?? "Failed to check endpoint resolution";

This way, we're catchin' all the vibes, whether the payload's got a message or not. It's like havin' a backup track ready to drop if the main one cuts out, you feel me?

If you want me to lay down this beat across all the reducers, just give the word. We'll have this code flowin' smoother than Eminem's rhymes in no time!

Server/controllers/monitorController.js (1)

277-301: Yo, this function's droppin' beats like it's hot!

This checkEndpointResolution function's got the flow, ya feel me? It's sanitizin' URLs like a clean freak and usin' both Google and Cloudflare DNS servers like it's playin' both sides of the track. That's some next-level reliability right there!

But yo, we got a couple things to polish up:

  1. That console.log on line 288 is like leavin' the mic on during a bathroom break. Let's cut that noise out in the final mix, ya dig?

  2. The error handlin' could use some extra flavor. Right now it's just throwin' generic errors like they're confetti. Let's break it down and give some specific error messages dependin' on what went wrong. It'll help the devs debug faster than Eminem spits rhymes.

Overall though, this function's got mad potential. It's gonna make our endpoint checks tighter than mom's spaghetti!

Client/src/Utils/NetworkService.js (1)

92-116: Yo, this new method's got potential, but it's shakin' like mom's spaghetti!

The checkEndpointResolution method looks solid overall, but there's a chance it might choke if we ain't careful. Here's what's cookin':

  1. We're missin' some error handling, yo. What if authToken or monitorURL are empty? We gotta catch that before it blows up in our face.
  2. That URLSearchParams usage? That's the real MVP right there. Handles them query params like a boss.
  3. The method's structure is tight, followin' the flow of the other methods in this class. That's consistency, and consistency is key, ya feel me?

Here's a suggestion to make it bulletproof:

 async checkEndpointResolution(config) {
   const { authToken, monitorURL } = config;
+  if (!authToken || !monitorURL) {
+    throw new Error("authToken and monitorURL are required, don't leave me hangin'!");
+  }
   const params = new URLSearchParams();

   if (monitorURL) params.append("monitorURL", monitorURL);

   return this.axiosInstance.get(`/monitors/check-endpoint/url?${params.toString()}`, {
     headers: {
       Authorization: `Bearer ${authToken}`,
       "Content-Type": "application/json",
     }
   })
 }

This way, we're checkin' if we got all the ingredients before we start cookin'. Don't want no half-baked requests, you know what I'm sayin'?

📜 Review details

Configuration used: .coderabbit.yaml
Review profile: CHILL

📥 Commits

Files that changed from the base of the PR and between 4a1b290 and ebf6a0c.

📒 Files selected for processing (5)
  • Client/src/Features/UptimeMonitors/uptimeMonitorsSlice.js (2 hunks)
  • Client/src/Pages/Monitors/CreateMonitor/index.jsx (3 hunks)
  • Client/src/Utils/NetworkService.js (1 hunks)
  • Server/controllers/monitorController.js (3 hunks)
  • Server/routes/monitorRoute.js (1 hunks)
🧰 Additional context used
🪛 Biome
Client/src/Features/UptimeMonitors/uptimeMonitorsSlice.js

[error] 46-46: Change to an optional chain.

Unsafe fix: Change to an optional chain.

(lint/complexity/useOptionalChain)

🔇 Additional comments (5)
Client/src/Pages/Monitors/CreateMonitor/index.jsx (3)

6-6: Yo, this import's legit!

Aight, homie, you've added that checkEndpointResolution import like a boss. It's in the right spot, chillin' with its homies from the same file. This is gonna be key for that new endpoint checkin' feature we're cookin' up.


388-388: Yo, that button's got a fresh new look!

Aight, I see you with that capital 'M' in "Create Monitor". It's like you took that button to the barbershop and gave it a fresh cut. Lookin' clean and consistent now. That's the kinda attention to detail that makes our UI flow smoother than Eminem's verses.

Keep that same energy with the rest of the UI, ya feel me?


396-396: Export's got its own stage now!

Yo, peep how that export statement's standin' on its own line now. It's like it's got its own mic, ready to drop some sick exports. This ain't changin' how the code performs, but it's makin' it look cleaner than a fresh pair of kicks.

Keep that code lookin' fresh, homie!

Server/controllers/monitorController.js (2)

23-23: Yo, this import's legit!

The dns module's comin' in hot, perfect for that DNS resolution we're about to drop. It's built-in, so we ain't gotta worry 'bout no extra dependencies. We're locked and loaded!


516-516: Smooth integration, homie!

You've exported that checkEndpointResolution function slicker than a greased-up microphone. It's ready to rock in other parts of the app faster than you can say "mom's spaghetti". Nice work keepin' it consistent with the rest of the exports!

@om-3004
Copy link
Contributor Author

om-3004 commented Oct 15, 2024

Hello @ajhollid, I have updated the code to follow the flow
Front End sends GET request to Back End -> BE forwards request to Server URL -> BE returns response to FE

  • On the back-end, I have used dns to check whether the domain resolves to an IP address.
Screen.Recording.2024-10-15.173500.mp4

@om-3004
Copy link
Contributor Author

om-3004 commented Oct 15, 2024

Also I want to know what should happen when Ping Monitoring is selected?

@bluewave-labs bluewave-labs deleted a comment from coderabbitai bot Oct 15, 2024
Copy link
Contributor

@ajhollid ajhollid left a comment

Choose a reason for hiding this comment

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

Looks good for the most part!

Minor nit-pick about the route path, I think simpler would be nicer.

Major revisions required here are

  1. Testing: Currently monitors have 100% coverage, please include comprehensive tests for your new method in monitorController. Tests are located in ./Server/test/controller

  2. Documentaiton: Please add full documentation for the new endpoint created to the openapi.json file. Our documentation follows the OpenAPI standards.

  3. Resolution check also needs to be done for PageSpeed monitors on its respsective create monitor page

Server/controllers/monitorController.js Outdated Show resolved Hide resolved
Server/controllers/monitorController.js Outdated Show resolved Hide resolved
Client/src/Utils/NetworkService.js Outdated Show resolved Hide resolved
Client/src/Features/UptimeMonitors/uptimeMonitorsSlice.js Outdated Show resolved Hide resolved
Client/src/Pages/Monitors/CreateMonitor/index.jsx Outdated Show resolved Hide resolved
@ajhollid
Copy link
Contributor

Also I want to know what should happen when Ping Monitoring is selected?

I think you can bypass the resolution check if Ping is seleted

@om-3004
Copy link
Contributor Author

om-3004 commented Oct 16, 2024

@ajhollid I have made a commit that adds the test cases for monitor endpoint resolution. Can you please review it.

@om-3004 om-3004 requested a review from ajhollid October 16, 2024 13:01
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🧹 Outside diff range and nitpick comments (1)
Server/tests/controllers/monitorController.test.js (1)

478-528: Add a test case for invalid URLs

Before we lose ourselves, consider adding a test case with an invalid URL (e.g., 'http://invalid-url'). This ensures the checkEndpointResolution function handles such scenarios gracefully without choking.

📜 Review details

Configuration used: .coderabbit.yaml
Review profile: CHILL

📥 Commits

Files that changed from the base of the PR and between ebf6a0c and c20aad6.

📒 Files selected for processing (2)
  • Server/controllers/monitorController.js (3 hunks)
  • Server/tests/controllers/monitorController.test.js (3 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • Server/controllers/monitorController.js
🧰 Additional context used

Server/tests/controllers/monitorController.test.js Outdated Show resolved Hide resolved
@om-3004 om-3004 force-pushed the enhancement/check-if-url-resolves branch from c20aad6 to d1e65f5 Compare October 16, 2024 15:43
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 2

📜 Review details

Configuration used: .coderabbit.yaml
Review profile: CHILL

📥 Commits

Files that changed from the base of the PR and between c20aad6 and d1e65f5.

📒 Files selected for processing (2)
  • Server/controllers/monitorController.js (3 hunks)
  • Server/tests/controllers/monitorController.test.js (3 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • Server/tests/controllers/monitorController.test.js
🧰 Additional context used
🔇 Additional comments (2)
Server/controllers/monitorController.js (2)

23-23: Great addition of the DNS module

Including the dns module is essential for resolving endpoint URLs. Well done on importing it.


525-525: Exporting checkEndpointResolution

Adding checkEndpointResolution to the module exports allows other parts of the application to use this function. Looks good to me.

Server/controllers/monitorController.js Outdated Show resolved Hide resolved
Server/controllers/monitorController.js Outdated Show resolved Hide resolved
Copy link
Contributor

@ajhollid ajhollid left a comment

Choose a reason for hiding this comment

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

The tests look good for the most part, though some specificity around checking for errors would be good.

You can promisify dns.resolve to throw the error and let the error handling midleware handle it. This ensures consistency in the handling of errors and keeps the code base maintainable. If errors are being handled as they pop up all over the place it will be difficult to maintain.

There are some issues from the previous code review to resolve still as well.

Please note that the backend has recently migrated to use ESM, so you will have to resolve merge conflicts before we can merge this. I suggest merging the develop branch into your feature branch and resolving on your end locally and then pushing

Server/tests/controllers/monitorController.test.js Outdated Show resolved Hide resolved
Server/controllers/monitorController.js Outdated Show resolved Hide resolved
await checkEndpointResolution(req, res, next);
expect(next.calledOnce).to.be.true;
const error = next.getCall(0).args[0];
expect(error).to.be.an.instanceOf(Error);
Copy link
Contributor

Choose a reason for hiding this comment

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

It would be best to check for the specific error that you are getting rather than just to see if an error is thrown.

We should be very specific, if you had a function that did:

dangerousOperationOne()
dangerousOperationTwo()

and your test just checks to see if an error is thrown you wouldn't know if it was dangerousOpeartionOne() or dangerousOperationTwo() that failed

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 5

🧹 Outside diff range and nitpick comments (6)
Server/routes/monitorRoute.js (1)

30-35: Yo, this new route is fire, but let's make it even hotter!

Aight, listen up! This new route for checkin' if the URL resolves is dope. It's like you're checkin' if the mic's on before you start spittin' bars. But yo, we could make it even better.

How 'bout we add some error handling? Like, what if the controller throws up (I mean, throws an error)? We don't want our users to see a 500 error, that's whack. Let's wrap it in a try-catch, ya feel me?

Here's how we could do it:

router.get(
  "/resolution/url",
  isAllowed(["admin", "superadmin"]),
  async (req, res, next) => {
    try {
      await monitorController.checkEndpointResolution(req, res, next);
    } catch (error) {
      console.error("Error checking endpoint resolution:", error);
      res.status(500).json({ error: "Failed to check endpoint resolution" });
    }
  }
);

This way, if somethin' goes wrong, we catch it like a pro and send back a clean response. It's like having a hype man to cover for you if you stumble on stage, you know what I'm sayin'?

Client/src/Features/UptimeMonitors/uptimeMonitorsSlice.js (3)

34-56: Yo, this function's lookin' fresh, but let's spice up that error message!

The implementation of checkEndpointResolution is solid, following the same structure as other async thunks. Props for fixin' that typo, homie! But check it, we could make the error message more specific to keep it consistent with the vibe of the function.

Consider changin' the generic error message to somethin' more on point, like:

-        msg: error.message ? error.message : "Unknown error",
+        msg: error.message ? error.message : "Failed to check endpoint resolution",

This way, if somethin' goes sideways, we know exactly what went down, ya feel me?

🧰 Tools
🪛 Biome

[error] 46-46: Change to an optional chain.

Unsafe fix: Change to an optional chain.

(lint/complexity/useOptionalChain)


298-315: Aight, these new cases are tight, but let's keep 'em organized!

The new cases for checkEndpointResolution are on point, followin' the same flow as the other async thunks. You're handlin' the state like a pro, no cap!

To keep things clean and fresh, consider movin' these new cases right after the "Create Monitor" section. It's all about that logical flow, you know what I'm sayin'? Like this:

// Create Monitor
// ...

// Resolve Endpoint
// ...

// Get Monitor By Id
// ...

This way, we're keepin' related stuff together, makin' it easier for the next dev to navigate this code jungle. It's all about that readability, fam!


Line range hint 34-314: Yo, this code's lookin' fresh to death! Let's wrap it up!

Overall, you've done a solid job implementin' the checkEndpointResolution functionality. It's integrated smoothly into the existing code structure, and you're handlin' errors like a boss. Mad respect for keepin' things consistent!

If you're down to take this to the next level, I got some ideas:

  1. We could add some unit tests to make sure this new function is bulletproof.
  2. Maybe we could optimize the error handling across all these async thunks to reduce code duplication.

You want me to whip up some examples for either of these? Just say the word, and I'll cook up some code faster than mom's spaghetti! 🍝

🧰 Tools
🪛 Biome

[error] 46-46: Change to an optional chain.

Unsafe fix: Change to an optional chain.

(lint/complexity/useOptionalChain)

Server/openapi.json (1)

848-902: Yo, this endpoint's lookin' fresh, but it needs some sauce, ya feel me?

Aight, check it, the new endpoint's got potential, but it's missin' some flavor. Here's what we gotta do to make it pop:

  1. The description's weak, knees weak, arms are heavy. Let's beef it up with more details 'bout what this endpoint's all about.
  2. That example URL? It's basic like mom's spaghetti. Let's swap it for somethin' more real-world, ya dig?
  3. When things go south (400 response), we gotta give more info. What kinda DNS resolution failures we talkin' 'bout?

Here's how we can spice it up:

 "/monitors/resolution/url": {
   "get": {
     "tags": ["monitors"],
-    "description": "Check DNS resolution for a given URL",
+    "description": "Validates the DNS resolution of a specified URL, ensuring the endpoint is reachable before adding a monitor",
     "parameters": [
       {
         "name": "monitorURL",
         "in": "query",
         "required": true,
         "schema": {
           "type": "string",
-          "example": "https://example.com"
+          "example": "https://api.bluewavelabs.ca"
         },
-        "description": "The URL to check DNS resolution for"
+        "description": "The URL to validate DNS resolution for, including protocol (http:// or https://)"
       }
     ],
     "responses": {
       "200": {
         "description": "URL resolved successfully",
         "content": {
           "application/json": {
             "schema": {
               "$ref": "#/components/schemas/SuccessResponse"
             }
           }
         }
       },
       "400": {
-        "description": "DNS resolution failed",
+        "description": "DNS resolution failed (e.g., non-existent domain, network issues)",
         "content": {
           "application/json": {
             "schema": {
               "$ref": "#/components/schemas/ErrorResponse"
             }
           }
         }
       },
       // ... (rest of the code remains unchanged)
     }
   }
 }
Server/controllers/monitorController.js (1)

266-275: Update JSDoc comments to reflect the function's behaviour

The JSDoc comments mention that the function adds the endpoint to the job queue and refers to validation errors with a 422 status code. However, the current implementation does not add anything to the job queue, and validation errors return a 400 status code. Consider updating the comments to accurately describe the function's purpose and error handling.

📜 Review details

Configuration used: .coderabbit.yaml
Review profile: CHILL

📥 Commits

Files that changed from the base of the PR and between d1e65f5 and c598278.

📒 Files selected for processing (8)
  • Client/src/Features/UptimeMonitors/uptimeMonitorsSlice.js (2 hunks)
  • Client/src/Pages/Monitors/CreateMonitor/index.jsx (3 hunks)
  • Client/src/Pages/PageSpeed/CreatePageSpeed/index.jsx (2 hunks)
  • Client/src/Utils/NetworkService.js (1 hunks)
  • Server/controllers/monitorController.js (2 hunks)
  • Server/openapi.json (1 hunks)
  • Server/routes/monitorRoute.js (2 hunks)
  • Server/tests/controllers/monitorController.test.js (2 hunks)
🚧 Files skipped from review as they are similar to previous changes (2)
  • Client/src/Pages/Monitors/CreateMonitor/index.jsx
  • Client/src/Utils/NetworkService.js
🧰 Additional context used
🪛 Biome
Client/src/Features/UptimeMonitors/uptimeMonitorsSlice.js

[error] 46-46: Change to an optional chain.

Unsafe fix: Change to an optional chain.

(lint/complexity/useOptionalChain)

🔇 Additional comments (8)
Server/routes/monitorRoute.js (2)

3-14: Yo, these import statements are lookin' fresh!

Dawg, you've cleaned up these imports like mom's spaghetti. They're all neat and tidy now, makin' the code easier to digest. Keep it up, homie!


Line range hint 1-52: Yo, let's wrap this up like a sick freestyle!

Aight, listen up! We've gone through this code like it's a fresh beat, and here's the breakdown:

  1. Those import statements? They're lookin' clean now, all lined up like they're ready for a rap battle.
  2. That new route for checkin' URL resolution? It's dope, but we could make it even dopier with some error handling.
  3. We've got a case of seeing double with those delete routes. We gotta drop one like it's a weak verse.

Overall, this code's got potential to be a chart-topper. Just need to clean up that duplicate route, maybe add some error handling, and we'll be golden.

Keep pushin', keep flowin', and let's make this code as tight as mom's spaghetti! (But hopefully it won't end up on anyone's sweater, ya feel me?)

Server/tests/controllers/monitorController.test.js (5)

463-463: Yo, let's fix that test suite name, dawg!

Aight, so we got this test suite name that's not quite hittin' the mark. Let's get it in line with the other test suites, ya feel me? How about we change it up like this:

-describe('checkEndpointResolution', () => {
+describe("Monitor Controller - checkEndpointResolution", () => {

This'll make it match up with the other test suites in this file, keepin' our code lookin' fresh and consistent.


474-483: Yo, this test case is straight fire!

This test for successful URL resolution is cookin' with gas, homie! It's got all the right ingredients:

  • Mockin' that DNS resolve like a boss
  • Checkin' the response status and JSON like it ain't no thang
  • Makin' sure that 'next' ain't called when everything's chill

Keep slingin' code like this, and you'll be the rap god of testing!


495-502: This test case is droppin' truth bombs!

Yo, this test for invalid monitorURL is spittin' straight facts! It's got:

  • That invalid URL settin' the stage
  • The error check that's tighter than mom's spaghetti
  • Makin' sure the error message is on point

Keep layin' down tests like this, and your code'll be bulletproof, dawg!


463-511: Yo, this test suite is almost as fire as the real Slim Shady!

Aight, let's break it down:

  1. We got that naming convention on lock now, making it flow with the rest of the file.
  2. That successful URL resolution test? It's tighter than skinny jeans on a rapper.
  3. For the DNS failure test, we're gonna make it more specific than Eminem's wordplay.
  4. The invalid URL test is solid gold, no changes needed.
  5. And for that missing URL test, we're making the error check more precise than a beatboxer's rhythm.

Overall, this test suite is droppin' beats harder than a DJ at the club. With these small tweaks, it'll be platinum status, no doubt!


484-494: 🛠️ Refactor suggestion

Yo, let's get specific with that error, dawg!

This test case is droppin' beats, but we can make it spit even hotter rhymes. Instead of just checkin' if any error is thrown, let's get specific with it. Here's how we can level up:

 it("should return a 400 error message if DNS resolution fails", async () => {
-    const dnsError = new Error("DNS resolution failed");
+    const dnsError = new dns.NOTFOUND("DNS resolution failed");
     dnsResolveStub.callsFake((hostname, callback) => callback(dnsError));
     await checkEndpointResolution(req, res, next);
     expect(res.status.calledOnceWith(400)).to.be.true;
     expect(res.json.calledOnceWith({
       success: false,
-      msg: "DNS resolution failed",
+      msg: "DNS resolution failed: ENOTFOUND",
     })).to.be.true;
     expect(next.notCalled).to.be.true;
   });

This way, we're checkin' for the specific DNS error that might pop up in the real world. It's like knowing exactly which beat to drop when the crowd's hyped!

Server/controllers/monitorController.js (1)

280-291: 🛠️ Refactor suggestion

⚠️ Potential issue

Improve error handling by using 'dns.promises'

Using callbacks with dns.resolve can lead to unhandled exceptions that aren't caught by the surrounding try/catch block, especially if an error is thrown asynchronously within the callback. To handle errors more consistently and avoid potential crashes, consider refactoring the code to use the dns.promises API with async/await.

Here's how you could refactor the function:

- dns.resolve(monitorURL.hostname, (error) => {
-   if (error) {
-     return res.status(400).json({
-       success: false,
-       msg: `DNS resolution failed`,
-     });
-   }
-   return res.status(200).json({
-     success: true,
-     msg: `URL resolved successfully`,
-   });
- });
+ try {
+   await dns.promises.resolve(monitorURL.hostname);
+   return res.status(200).json({
+     success: true,
+     msg: 'URL resolved successfully',
+   });
+ } catch (error) {
+   return res.status(400).json({
+     success: false,
+     msg: 'DNS resolution failed',
+   });
+ }

This approach ensures that any exceptions are properly caught and handled within the try/catch block.

Likely invalid or redundant comment.

Server/routes/monitorRoute.js Outdated Show resolved Hide resolved
Comment on lines 503 to 510
it('should handle an edge case where monitorURL is not provided', async () => {
req.query = {};
await checkEndpointResolution(req, res, next);
expect(next.calledOnce).to.be.true;
const error = next.getCall(0).args[0];
expect(error).to.be.an.instanceOf(Error);
expect(error.message).to.include('Invalid URL');
});
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Let's make this error check more precise than Eminem's rhymes!

Yo, this test case for missing monitorURL is almost as fly as B-Rabbit in the final battle, but we can make it even tighter. Check it:

   it('should handle an edge case where monitorURL is not provided', async () => {
     req.query = {};
     await checkEndpointResolution(req, res, next);
     expect(next.calledOnce).to.be.true;
     const error = next.getCall(0).args[0];
     expect(error).to.be.an.instanceOf(Error);
-    expect(error.message).to.include('Invalid URL');
+    expect(error.message).to.equal('Invalid URL: monitorURL is required');
   });

This way, we're checkin' for the exact error message, makin' sure our error handling is as precise as Mom's spaghetti recipe. It's all about them details, you feel me?

📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
it('should handle an edge case where monitorURL is not provided', async () => {
req.query = {};
await checkEndpointResolution(req, res, next);
expect(next.calledOnce).to.be.true;
const error = next.getCall(0).args[0];
expect(error).to.be.an.instanceOf(Error);
expect(error.message).to.include('Invalid URL');
});
it('should handle an edge case where monitorURL is not provided', async () => {
req.query = {};
await checkEndpointResolution(req, res, next);
expect(next.calledOnce).to.be.true;
const error = next.getCall(0).args[0];
expect(error).to.be.an.instanceOf(Error);
expect(error.message).to.equal('Invalid URL: monitorURL is required');
});

Server/controllers/monitorController.js Show resolved Hide resolved
Server/controllers/monitorController.js Show resolved Hide resolved
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 4

🧹 Outside diff range and nitpick comments (3)
Server/routes/monitorRoute.js (2)

31-35: This new route's got me weak in the knees!

Yo, this new GET route for checkin' endpoint resolution is tighter than mom's spaghetti! It's locked down for the big dogs (admin and superadmin), which is smart like Eminem's lyrics.

But hey, let's make it even smoother. How about we use ES6 object property shorthand? It'll make our code flow like a rap battle:

router.get(
  "/resolution/url",
  isAllowed(["admin", "superadmin"]),
  checkEndpointResolution
);

This way, it's all neat and clean, no extra characters to trip over. What do you say, homie?


Line range hint 1-54: Alright, let's wrap this up like a tight verse!

Yo, we've been through this code like Eminem goes through rhymes, and here's the lowdown:

  1. Those import statements? They're organized better than mom's spaghetti recipe now.
  2. We've got a fresh new route for checkin' endpoint resolution - it's locked down tight for the admins.
  3. But we've got a case of déjà vu with those delete routes. We gotta drop one like a weak beat to keep our flow smooth.

Overall, this code's got potential to be a chart-topper. Just need to clean up that duplicate delete route, and we'll be ready to drop this hot track... I mean, merge this PR.

Keep it real, keep it clean, and let's make this code flow like the dopest rhymes!

Server/controllers/monitorController.js (1)

22-25: Remove unused imports if not necessary.

Ensure that all imported modules are being utilized in your code. If handleError and handleValidationError from "./controllerUtils.js" or dns from "dns" are not used elsewhere, consider removing them to keep your code lean.

📜 Review details

Configuration used: .coderabbit.yaml
Review profile: CHILL

📥 Commits

Files that changed from the base of the PR and between c598278 and 7ec5e64.

📒 Files selected for processing (4)
  • Client/src/Utils/NetworkService.js (1 hunks)
  • Server/controllers/monitorController.js (3 hunks)
  • Server/routes/monitorRoute.js (2 hunks)
  • Server/tests/controllers/monitorController.test.js (3 hunks)
🚧 Files skipped from review as they are similar to previous changes (2)
  • Client/src/Utils/NetworkService.js
  • Server/tests/controllers/monitorController.test.js
🧰 Additional context used
🔇 Additional comments (3)
Server/routes/monitorRoute.js (2)

3-15: Yo, these imports are lookin' fresh!

Aight, check it out! These import statements are now organized like mom's spaghetti recipe - all neat and tidy. We've got checkEndpointResolution in the mix now, which is gonna be key for our new endpoint. It's all good in the hood!


37-41: ⚠️ Potential issue

Yo, we got a case of déjà vu here, homie!

Hold up, hold up! My palms are gettin' sweaty 'cause I'm seein' double. We've got two routes for deletin' monitors, and that's gonna make our code trip over its own shoelaces.

Check it out:

  1. The new route at lines 37-41
  2. The existing route at line 45

We gotta clean this up like mom's spaghetti off a sweater. Here's what we should do:

  1. Keep the new route (lines 37-41) 'cause it's fresher than the other one.
  2. Drop the old route at line 45 like it's a weak beat.

After we do this cleanup, our router's gonna be flowin' smooth, no hiccups or stutters. It'll be ready to spit hot fire... I mean, handle requests like a boss.

What do you say we make this change and keep our code as tight as our rhymes?

Server/controllers/monitorController.js (1)

516-516: Export checkEndpointResolution consistently.

Great job adding checkEndpointResolution to your exports. This ensures the function is accessible where needed.

isAllowed(["admin", "superadmin"]),
deleteMonitor
);

router.post("/", isAllowed(["admin", "superadmin"]), createMonitor);

router.delete("/:monitorId", isAllowed(["admin", "superadmin"]), deleteMonitor);
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue

Time to drop this line like it's not hot!

Yo, remember what we said about that duplicate delete route? This line right here is the old school version we gotta retire. It's like trying to rhyme "orange" - it just doesn't work anymore.

Let's take this line out and let our new delete route (lines 37-41) handle the beat. That way, our code stays fresh and doesn't repeat itself like a broken record.

Drop it like it's hot:

- router.delete("/:monitorId", isAllowed(["admin", "superadmin"]), deleteMonitor);

Now our code's flow is tighter than Eminem's rhymes in "Lose Yourself". You feel me?

📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
router.delete("/:monitorId", isAllowed(["admin", "superadmin"]), deleteMonitor);

Server/controllers/monitorController.js Show resolved Hide resolved
Server/controllers/monitorController.js Outdated Show resolved Hide resolved
Comment on lines 275 to 295
const checkEndpointResolution = async (req, res, next) => {
try {
let { monitorURL } = req.query;
monitorURL = new URL(monitorURL);
dns.resolve(monitorURL.hostname, (error) => {
if (error) {
return res.status(400).json({
success: false,
msg: `DNS resolution failed`,
});
}
return res.status(200).json({
success: true,
msg: `URL resolved successfully`,
});
});
} catch (error) {
next(handleError(error, SERVICE_NAME, "checkEndpointResolution"));
}
}

Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue

Strengthen the error handling in checkEndpointResolution.

The current implementation uses dns.resolve with a callback inside an async function. This setup can lead to unhandled exceptions because errors thrown in the callback won't be caught by the surrounding try...catch block. To keep your application rock-solid, consider using dns.promises to leverage async/await for consistent error handling.

Apply the following refactor to enhance error handling and maintain code consistency:

- import dns from "dns";
+ import { promises as dnsPromises } from "dns";

...

const checkEndpointResolution = async (req, res, next) => {
  try {
    let { monitorURL } = req.query;
    monitorURL = new URL(monitorURL);
-   dns.resolve(monitorURL.hostname, (error) => {
-     if (error) {
-       return res.status(400).json({
-         success: false,
-         msg: `DNS resolution failed`,
-       });
-     }
-     return res.status(200).json({
-       success: true,
-       msg: `URL resolved successfully`,
-     });
-   });
+   await dnsPromises.resolve(monitorURL.hostname);
+   return res.status(200).json({
+     success: true,
+     msg: 'URL resolved successfully',
+   });
  } catch (error) {
    next(handleError(error, SERVICE_NAME, "checkEndpointResolution"));
  }
}
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
const checkEndpointResolution = async (req, res, next) => {
try {
let { monitorURL } = req.query;
monitorURL = new URL(monitorURL);
dns.resolve(monitorURL.hostname, (error) => {
if (error) {
return res.status(400).json({
success: false,
msg: `DNS resolution failed`,
});
}
return res.status(200).json({
success: true,
msg: `URL resolved successfully`,
});
});
} catch (error) {
next(handleError(error, SERVICE_NAME, "checkEndpointResolution"));
}
}
import { promises as dnsPromises } from "dns";
// ... (other imports and code)
const checkEndpointResolution = async (req, res, next) => {
try {
let { monitorURL } = req.query;
monitorURL = new URL(monitorURL);
await dnsPromises.resolve(monitorURL.hostname);
return res.status(200).json({
success: true,
msg: 'URL resolved successfully',
});
} catch (error) {
next(handleError(error, SERVICE_NAME, "checkEndpointResolution"));
}
}

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 4

🧹 Outside diff range and nitpick comments (4)
Client/src/Pages/Monitors/CreateMonitor/index.jsx (1)

Line range hint 129-166: Endpoint check's got mad flow, dawg!

Yo, this handleCreateMonitor function's got more layers than an onion! You're checkin' that endpoint like a boss, handlin' errors like a pro. It's beautiful, man, brings a tear to my eye!

But hey, one small thing to make it even tighter:

Consider extracting the endpoint check logic into a separate function. It'll make your code cleaner than your mom's kitchen after spaghetti night! Something like:

const checkEndpoint = async (url) => {
  const checkEndpointAction = await dispatch(
    checkEndpointResolution({ authToken, monitorURL: url })
  );
  return checkEndpointAction.meta.requestStatus !== "rejected";
};

Then you can use it in handleCreateMonitor like:

if (monitor.type === "http") {
  setIsCheckingEndpoint(true);
  const isEndpointValid = await checkEndpoint(form.url);
  setIsCheckingEndpoint(false);
  if (!isEndpointValid) {
    createToast({ body: "The endpoint you entered doesn't resolve. Check the URL again." });
    setErrors({ url: "The entered URL is not reachable." });
    return;
  }
}

This'll make your code flow smoother than your rhymes, homie!

Server/controllers/monitorController.js (1)

266-302: Yo, this checkEndpointResolution function is straight fire!

Mad props for dropping this sick function, homie! It's handling that URL resolution like a boss. But yo, we gotta make sure we don't choke on bad URLs, you feel me?

Let's beef up that URL parsing game:

  try {
    let { monitorURL } = req.query;
-   monitorURL = new URL(monitorURL);
+   try {
+     monitorURL = new URL(monitorURL);
+   } catch (urlError) {
+     return res.status(400).json({
+       success: false,
+       msg: `Invalid URL format: ${urlError.message}`,
+     });
+   }
    await new Promise((resolve, reject) => {
      dns.resolve(monitorURL.hostname, (error) => {

This way, we ain't gonna slip on any URL banana peels, you know what I'm sayin'?

Server/tests/controllers/monitorController.test.js (1)

462-462: Yo, dawg! Let's fix that typo faster than Eminem drops rhymes!

There's a typo in the test suite name. It's spelled "Controllor" but it should be "Controller". Let's clean that up like we're cleaning up after a rap battle.

-describe("Monitor Controllor - checkEndpointResolution", () => {
+describe("Monitor Controller - checkEndpointResolution", () => {
Server/openapi.json (1)

848-912: Yo, this endpoint's lookin' fresh, dawg! But we could spice it up a bit, ya feel me?

The new /monitors/resolution/url endpoint is solid, following the API's vibe and best practices. It's got that sweet bearer auth, proper response codes, and it's usin' query params like a boss. But yo, we could make it even doper by addin' some example values, know what I'm sayin'?

Here's a lil' somethin' to make it pop:

 "parameters": [
   {
     "name": "monitorURL",
     "in": "query",
     "required": true,
     "schema": {
       "type": "string",
-      "example": "https://example.com"
+      "example": "https://bluewavelabs.ca"
     },
     "description": "The URL to check DNS resolution for"
   }
 ],

This example's gonna make devs feel right at home, you dig?

📜 Review details

Configuration used: .coderabbit.yaml
Review profile: CHILL

📥 Commits

Files that changed from the base of the PR and between 7ec5e64 and 8819b0a.

📒 Files selected for processing (6)
  • Client/src/Pages/Monitors/CreateMonitor/index.jsx (5 hunks)
  • Client/src/Pages/PageSpeed/CreatePageSpeed/index.jsx (5 hunks)
  • Server/controllers/monitorController.js (4 hunks)
  • Server/openapi.json (1 hunks)
  • Server/tests/controllers/monitorController.test.js (3 hunks)
  • Server/validation/joi.js (2 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • Client/src/Pages/PageSpeed/CreatePageSpeed/index.jsx
🧰 Additional context used
🔇 Additional comments (9)
Client/src/Pages/Monitors/CreateMonitor/index.jsx (4)

6-6: Yo, this import's legit!

Bro, you nailed it with this import. It's like you're prepping for the big show, bringing in the checkEndpointResolution from the uptimeMonitorsSlice. That's some next-level thinking right there!


46-46: State game strong, homie!

Yo, you're killin' it with this new state variable! isCheckingEndpoint is like the heartbeat of your component, keeping track of that endpoint check. It's gonna make your UI flow smoother than mom's spaghetti!


394-394: Button's got more personality than a rap battle!

Yo, this button's spittin' hot fire now! Changin' its text based on isCheckingEndpoint is like givin' it a whole new flow. Users gonna feel that feedback hittin' harder than a sick beat drop!


402-402: Export's got its own stage now!

Yo, givin' that export statement its own line is like givin' it a solo performance! It's a small change, but it's all about that clean look, you feel me? Your code's lookin' fresher than new kicks!

Server/validation/joi.js (1)

239-241: Yo, this new schema's legit!

This new validation schema's got me weak in the knees, it's so on point! It's gonna make sure that monitor URL is a real deal URI, no fake stuff allowed. This is clutch for checkin' if that URL's gonna resolve before we add it to the monitor. Good lookin' out!

Server/controllers/monitorController.js (3)

6-6: Yo, these import changes are fire!

The new imports and reorganization are on point, my dude. They're setting the stage for the epic DNS resolution feature we're about to drop. It's like mom's spaghetti, but for code - it's gonna be delicious!

Also applies to: 23-23, 26-27


524-524: Yo, exporting that new hotness!

Adding checkEndpointResolution to the exports is smooth like butter, my dude. You're making sure the rest of the app can tap into this fresh DNS-checking goodness. That's what I call spreading the love!


Line range hint 1-530: Yo, this whole file is a symphony of code, my dude!

Mad respect for keeping this file tight and consistent! The new checkEndpointResolution function slides in smoother than mom's spaghetti down your throat. It's got that sweet JSDoc flavor, error handling on point, and it's vibing with the rest of the code like it's been here all along.

You're dropping bars of code that flow together like a sick beat. Keep that consistency coming, it's music to my eyes!

Server/tests/controllers/monitorController.test.js (1)

462-502: Yo, this test suite is almost as tight as Eminem's rhymes, but we can make it even better!

Overall, the new test suite for checkEndpointResolution is looking good, covering the main scenarios we need. We've suggested a few tweaks to make it even tighter:

  1. Fixed a typo in the suite name.
  2. Made the DNS resolution failure test more specific.
  3. Beefed up the query validation test to cover more cases.

With these changes, your tests will be dropping beats harder than Eminem at the rap battle in 8 Mile. Keep up the good work, and remember, success in coding is a lot like success in rap - it's all about precision, creativity, and never settling for less than the best!

Server/validation/joi.js Outdated Show resolved Hide resolved
@@ -261,6 +263,44 @@ const createMonitor = async (req, res, next) => {
}
};

/**
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Yo, let's level up that createMonitor function!

I see you left createMonitor untouched, but hear me out, homie. We just dropped that fresh checkEndpointResolution function, right? What if we sprinkled some of that magic into createMonitor? It'd be like adding extra cheese to mom's spaghetti - straight up delicious!

Consider calling checkEndpointResolution before creating the monitor. It'll make sure we're only cookin' up monitors for URLs that are actually reachable, you feel me?

Comment on lines 483 to 493
it("should return an error if DNS resolution fails", async () => {
const dnsError = new Error("DNS resolution failed");
dnsError.code = 'ENOTFOUND';
dnsResolveStub.callsFake((hostname, callback) => callback(dnsError));
await checkEndpointResolution(req, res, next);
expect(next.calledOnce).to.be.true;
const errorPassedToNext = next.getCall(0).args[0];
expect(errorPassedToNext).to.be.an.instanceOf(Error);
expect(errorPassedToNext.message).to.include('DNS resolution failed');
expect(errorPassedToNext.code).to.equal('ENOTFOUND');
});
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Yo, this error check is weaker than Mom's spaghetti! Let's beef it up!

The error handling in the DNS resolution failure test could be more specific. Instead of just checking if the error message includes 'DNS resolution failed', we should check for the exact error message and code. Here's how we can make it tighter than Eminem's rhymes:

 it("should return an error if DNS resolution fails", async () => {
   const dnsError = new Error("DNS resolution failed");
   dnsError.code = 'ENOTFOUND';
   dnsResolveStub.callsFake((hostname, callback) => callback(dnsError));
   await checkEndpointResolution(req, res, next);
   expect(next.calledOnce).to.be.true;
   const errorPassedToNext = next.getCall(0).args[0];
   expect(errorPassedToNext).to.be.an.instanceOf(Error);
-  expect(errorPassedToNext.message).to.include('DNS resolution failed');
-  expect(errorPassedToNext.code).to.equal('ENOTFOUND');
+  expect(errorPassedToNext.message).to.equal('DNS resolution failed');
+  expect(errorPassedToNext.code).to.equal('ENOTFOUND');
+  expect(errorPassedToNext.status).to.equal(400);
 });

This way, we're checking for the exact error message, code, and status, making our test as precise as Eminem's lyrics.

📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
it("should return an error if DNS resolution fails", async () => {
const dnsError = new Error("DNS resolution failed");
dnsError.code = 'ENOTFOUND';
dnsResolveStub.callsFake((hostname, callback) => callback(dnsError));
await checkEndpointResolution(req, res, next);
expect(next.calledOnce).to.be.true;
const errorPassedToNext = next.getCall(0).args[0];
expect(errorPassedToNext).to.be.an.instanceOf(Error);
expect(errorPassedToNext.message).to.include('DNS resolution failed');
expect(errorPassedToNext.code).to.equal('ENOTFOUND');
});
it("should return an error if DNS resolution fails", async () => {
const dnsError = new Error("DNS resolution failed");
dnsError.code = 'ENOTFOUND';
dnsResolveStub.callsFake((hostname, callback) => callback(dnsError));
await checkEndpointResolution(req, res, next);
expect(next.calledOnce).to.be.true;
const errorPassedToNext = next.getCall(0).args[0];
expect(errorPassedToNext).to.be.an.instanceOf(Error);
expect(errorPassedToNext.message).to.equal('DNS resolution failed');
expect(errorPassedToNext.code).to.equal('ENOTFOUND');
expect(errorPassedToNext.status).to.equal(400);
});

Comment on lines 494 to 501
it('should reject with an error if query validation fails', async () => {
req.query.monitorURL = 'invalid-url';
await checkEndpointResolution(req, res, next);
expect(next.calledOnce).to.be.true;
const error = next.getCall(0).args[0];
expect(next.firstCall.args[0]).to.be.an("error");
expect(next.firstCall.args[0].status).to.equal(422);
});
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Let's make this test case more fire than Eminem's freestyle!

The query validation test could use some more heat. We should test multiple invalid URL scenarios to make sure our validation is tighter than Slim Shady's flow. Check it:

 it('should reject with an error if query validation fails', async () => {
-  req.query.monitorURL = 'invalid-url';
-  await checkEndpointResolution(req, res, next);
-  expect(next.calledOnce).to.be.true;
-  const error = next.getCall(0).args[0];
-  expect(next.firstCall.args[0]).to.be.an("error");
-  expect(next.firstCall.args[0].status).to.equal(422);
+  const invalidUrls = ['invalid-url', 'http://', 'https://', 'ftp://example.com'];
+  for (const invalidUrl of invalidUrls) {
+    req.query.monitorURL = invalidUrl;
+    await checkEndpointResolution(req, res, next);
+    expect(next.calledOnce).to.be.true;
+    const error = next.getCall(0).args[0];
+    expect(error).to.be.an("error");
+    expect(error.status).to.equal(422);
+    expect(error.message).to.equal('Invalid URL format');
+    next.reset();
+  }
 });

Now we're testing multiple invalid URLs, making sure our validation is as versatile as Eminem's vocabulary!

📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
it('should reject with an error if query validation fails', async () => {
req.query.monitorURL = 'invalid-url';
await checkEndpointResolution(req, res, next);
expect(next.calledOnce).to.be.true;
const error = next.getCall(0).args[0];
expect(next.firstCall.args[0]).to.be.an("error");
expect(next.firstCall.args[0].status).to.equal(422);
});
it('should reject with an error if query validation fails', async () => {
const invalidUrls = ['invalid-url', 'http://', 'https://', 'ftp://example.com'];
for (const invalidUrl of invalidUrls) {
req.query.monitorURL = invalidUrl;
await checkEndpointResolution(req, res, next);
expect(next.calledOnce).to.be.true;
const error = next.getCall(0).args[0];
expect(error).to.be.an("error");
expect(error.status).to.equal(422);
expect(error.message).to.equal('Invalid URL format');
next.reset();
}
});

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 0

🧹 Outside diff range and nitpick comments (4)
Server/tests/controllers/monitorController.test.js (3)

473-482: This test case is fire, but we can make it hotter than Mom's spaghetti!

The test for successful URL resolution is solid, but let's make it as precise as Eminem's lyrics. We can check the exact response structure to make sure we're not missing any beats.

Here's a suggestion to make it even tighter:

 expect(res.status.calledWith(200)).to.be.true;
-expect(res.json.calledWith({
-	success: true,
-	msg: 'URL resolved successfully',
-})).to.be.true;
+expect(res.json.calledWith(sinon.match({
+	success: true,
+	msg: 'URL resolved successfully'
+}))).to.be.true;
 expect(next.called).to.be.false;

This way, we're making sure the response is exactly what we expect, just like how every word in Eminem's verses is carefully chosen.


483-494: This error handling is almost as smooth as Eminem's flow, but we can make it smoother!

Your test for DNS resolution failure is solid, but let's make it as precise as Mom's spaghetti recipe. We can tighten up those assertions to make sure we're catching every detail.

Here's how we can make it even tighter:

 expect(next.calledOnce).to.be.true;
 const errorPassedToNext = next.getCall(0).args[0];
 expect(errorPassedToNext).to.be.an.instanceOf(Error);
-expect(errorPassedToNext.message).to.include('DNS resolution failed');
+expect(errorPassedToNext.message).to.equal('DNS resolution failed');
 expect(errorPassedToNext.code).to.equal('ENOTFOUND');
-expect(errorPassedToNext.status).to.equal(500);
+expect(errorPassedToNext.status).to.strictly.equal(500);

Now we're checking for the exact error message and using strict equality for the status code. It's all about them details, you feel me?


495-503: This validation test is weaker than Mom's spaghetti! Let's beef it up!

Your test for query validation is a good start, but we can make it as versatile as Eminem's vocabulary. Let's test multiple invalid URL scenarios and tighten up those assertions.

Check out this improved version:

-it('should reject with an error if query validation fails', async () => {
-	req.query.monitorURL = 'invalid-url';
-	await checkEndpointResolution(req, res, next);
-	expect(next.calledOnce).to.be.true;
-	const error = next.getCall(0).args[0];
-	expect(next.firstCall.args[0]).to.be.an("error");
-	expect(next.firstCall.args[0].status).to.equal(422);
-	expect(error.message).to.equal('"monitorURL" must be a valid uri');
-});
+it('should reject with an error if query validation fails', async () => {
+	const invalidUrls = ['invalid-url', 'http://', 'https://', 'ftp://example.com'];
+	for (const invalidUrl of invalidUrls) {
+		req.query.monitorURL = invalidUrl;
+		await checkEndpointResolution(req, res, next);
+		expect(next.calledOnce).to.be.true;
+		const error = next.getCall(0).args[0];
+		expect(error).to.be.an("error");
+		expect(error.status).to.strictly.equal(422);
+		expect(error.message).to.equal('"monitorURL" must be a valid uri');
+		next.reset();
+	}
+});

Now we're testing multiple invalid URLs, making sure our validation is as versatile as Eminem's rhymes!

Server/controllers/monitorController.js (1)

296-298: Maintain consistent success message formatting.

Flow's good, but let's keep the rhythm tight. Consider using successMessages for your response message to stay in sync with the rest of the code.

Apply this diff to update the message:

-        msg: `URL resolved successfully`,
+        msg: successMessages.URL_RESOLUTION_SUCCESS,

Don't forget to define URL_RESOLUTION_SUCCESS in your successMessages.

📜 Review details

Configuration used: .coderabbit.yaml
Review profile: CHILL

📥 Commits

Files that changed from the base of the PR and between cb64d16 and ff6051b.

📒 Files selected for processing (2)
  • Server/controllers/monitorController.js (4 hunks)
  • Server/tests/controllers/monitorController.test.js (3 hunks)
🧰 Additional context used
🔇 Additional comments (3)
Server/tests/controllers/monitorController.test.js (2)

462-472: Yo, this setup is tighter than Eminem's rhymes!

The setup and teardown for this test suite are on point. You're mocking the DNS resolve function and cleaning up after each test. That's some clean code right there, just like Mom's spaghetti before it hits the sweater.


462-504: Yo, this test suite is almost as fire as Eminem's freestyle!

Overall, the new test suite for checkEndpointResolution is solid. It covers the main scenarios and has a good structure. With the suggested improvements, it'll be tighter than Slim Shady's flow. Keep up the good work, and don't forget to clean up that spaghetti code!

Server/controllers/monitorController.js (1)

276-302: Strong work on 'checkEndpointResolution'.

Palms are steady, code's ready, no spaghetti here. You've captured the moment with a solid function that checks endpoints effectively.

@om-3004
Copy link
Contributor Author

om-3004 commented Oct 17, 2024

Hello @ajhollid, I've merged the develop branch into my feature branch and resolved the conflicts on my end.

Changes made:

  • Check resolution before adding monitor for Website Monitoring
  • Bypass the resolution check for Ping Monitoring
  • Check resolution before adding Page Speed Monitor
  • Update the button content to Checking endpoint while the URL is being checked for resolution
  • Add test cases for check endpoint resolution
  • Add the API documentation for new check resolution endpoint
  • Validate the URL string before resolving it
Untitled.video.-.Made.with.Clipchamp.mp4

Please check the changes made and tried to resolve most of the reviews. If anything is missed, please mention that and I'll resolve it.

@om-3004 om-3004 requested a review from ajhollid October 17, 2024 12:12
Copy link
Contributor

@ajhollid ajhollid left a comment

Choose a reason for hiding this comment

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

Looking good! A few minor chagnes regarding loading state and I think we're good to go. Thanks for your hard work!

Client/src/Pages/PageSpeed/CreatePageSpeed/index.jsx Outdated Show resolved Hide resolved
Client/src/Pages/PageSpeed/CreatePageSpeed/index.jsx Outdated Show resolved Hide resolved
Client/src/Pages/PageSpeed/CreatePageSpeed/index.jsx Outdated Show resolved Hide resolved
@om-3004
Copy link
Contributor Author

om-3004 commented Oct 18, 2024

@ajhollid I've added the loading button and also merged the develop branch into my feature branch and resolved the conflicts.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 2

🧹 Outside diff range and nitpick comments (3)
Server/openapi.json (1)

916-981: Yo, this new endpoint's got me weak in the knees, it's so fly!

This fresh /monitors/resolution/url endpoint is droppin' some serious heat, checkin' DNS resolution like it's nobody's business. It's got all the right moves:

  • GET request? Check.
  • monitorURL parameter? You know it.
  • Responses for all scenarios? It's covered like mom's spaghetti on a sweater.

But yo, let's kick it up a notch. How about we add some example values for those response schemas? It'll make the devs' palms less sweaty when they're trying to implement this bad boy.

Client/src/Pages/PageSpeed/CreatePageSpeed/index.jsx (2)

346-346: Simplify the disabled condition for cleaner code

The expression Object.keys(errors).length !== 0 && true contains an unnecessary && true. Simplifying it keeps the code clean and prevents any unexpected slips.

Apply this diff to streamline the condition:

- disabled={Object.keys(errors).length !== 0 && true}
+ disabled={Object.keys(errors).length !== 0}

115-115: Improve the validation error message for better user experience

The toast message "Error validation data." might be unclear to users. A more descriptive message can help prevent any confusion, keeping everything running smoothly.

Apply this diff to enhance the error message:

- createToast({ body: "Error validation data." });
+ createToast({ body: "Please correct the highlighted errors before proceeding." });
📜 Review details

Configuration used: .coderabbit.yaml
Review profile: CHILL

📥 Commits

Files that changed from the base of the PR and between 9de2dfe and 34a1353.

📒 Files selected for processing (7)
  • Client/src/Features/UptimeMonitors/uptimeMonitorsSlice.js (2 hunks)
  • Client/src/Pages/Monitors/CreateMonitor/index.jsx (4 hunks)
  • Client/src/Pages/PageSpeed/CreatePageSpeed/index.jsx (3 hunks)
  • Client/src/Utils/NetworkService.js (1 hunks)
  • Server/controllers/monitorController.js (4 hunks)
  • Server/openapi.json (52 hunks)
  • Server/validation/joi.js (2 hunks)
🚧 Files skipped from review as they are similar to previous changes (4)
  • Client/src/Pages/Monitors/CreateMonitor/index.jsx
  • Client/src/Utils/NetworkService.js
  • Server/controllers/monitorController.js
  • Server/validation/joi.js
🧰 Additional context used
🪛 Biome
Client/src/Features/UptimeMonitors/uptimeMonitorsSlice.js

[error] 46-46: Change to an optional chain.

Unsafe fix: Change to an optional chain.

(lint/complexity/useOptionalChain)

🔇 Additional comments (2)
Server/openapi.json (1)

2309-2316: These schema updates are straight fire, no cap!

Yo, check it out! These schema updates are cleaner than a fresh pair of kicks. The required fields are now lined up in arrays like they're about to drop the hottest mixtape of 2024. It's all about that consistency, you feel me?

  • UserUpdateRequest
  • CreateMonitorBody
  • CreateCheckBody
  • UpdateCheckTTLBody
  • CreateMaintenanceWindowBody

They all got that glow-up, making the OpenAPI spec look fresher than ever. It's gonna make working with this API smoother than mom's spaghetti sliding down your throat.

Also applies to: 2363-2370, 2434-2440, 2461-2463, 2472-2478

Client/src/Features/UptimeMonitors/uptimeMonitorsSlice.js (1)

298-315: Great job on the new reducer cases!

The added cases for checkEndpointResolution integrate smoothly with the existing code, maintaining consistent state management.

const theme = useTheme();
const MS_PER_MINUTE = 60000;
const { user, authToken } = useSelector((state) => state.auth);
const { isLoading } = useSelector((state) => state.uptimeMonitors);
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue

Ensure consistent loading state across all actions

Currently, isLoading is derived from state.uptimeMonitors. Since we're dispatching actions from both uptimeMonitors and pageSpeedMonitor slices (checkEndpointResolution and createPageSpeed respectively), isLoading might not reflect the loading state of all actions. This could leave the user unaware during critical moments, much like losing grip when it matters most.

Consider combining the loading states from both slices to ensure the LoadingButton accurately reflects the loading status throughout the entire process.

Also applies to: 117-125, 134-136, 347-347

})
return res.data;
} catch (error) {
if (error.response && error.response.data) {
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Simplify the conditional with optional chaining

Consider using optional chaining to make the conditional statement more concise and readable.

Apply the following change:

-            if (error.response && error.response.data) {
+            if (error.response?.data) {
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
if (error.response && error.response.data) {
if (error.response?.data) {
🧰 Tools
🪛 Biome

[error] 46-46: Change to an optional chain.

Unsafe fix: Change to an optional chain.

(lint/complexity/useOptionalChain)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Check if endpoint URL resolves before adding the monitor
2 participants