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

feat: support status pages and services [sc-23401] #1039

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

Conversation

Antoine-C
Copy link
Contributor

@Antoine-C Antoine-C commented Mar 6, 2025

I hereby confirm that I followed the code guidelines found at engineering guidelines

Affected Components

  • CLI
  • Create CLI
  • Test
  • Docs
  • Examples
  • Other

Notes for the Reviewer

Resolves #[issue-number]

New Dependency Submission

Sorry, something went wrong.

@Antoine-C
Copy link
Contributor Author

@sorccu This is my draft PR I used locally to test the deploy command for status pages

@sorccu sorccu force-pushed the antoinecoutellier/sc-23401/public-api-crud-resources-for-the-cli branch from 15d3bd2 to 11ca62a Compare March 19, 2025 18:09
Copy link

coderabbitai bot commented Mar 19, 2025

Walkthrough

This pull request expands the CLI's construct capabilities to include status page management. It enhances the public API by adding new export statements and extends the project data interface to support additional properties. Two new files are introduced that define constructs for status pages and their associated services, including the necessary interfaces, classes, constructors, and synthesis methods to generate structured representations of these constructs.

Changes

File(s) Changes Summary
packages/cli/.../index.ts Added export statements for status-page and status-page-service to expose the new modules.
packages/cli/.../project.ts Extended the ProjectData interface with 'status-page' and 'status-page-service' properties; updated initialization and synthesis logic to include the new constructs.
packages/cli/.../status-page-service.ts
packages/cli/.../status-page.ts
Introduced new files defining the StatusPageService and StatusPage constructs with their respective interfaces, types, constructors, and synthesize methods to support status page configurations.
packages/cli/e2e/__tests__/deploy.spec.ts Updated test assertions to include expected output for StatusPage and two instances of StatusPageService.
packages/cli/e2e/__tests__/fixtures/deploy-project/status-page.check.ts Created a new test fixture that defines a status page configuration, including instances of StatusPageService and a StatusPage.
✨ Finishing Touches
  • 📝 Generate Docstrings

🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Generate unit testing code for this file.
    • Open a follow-up GitHub issue for this discussion.
  • 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 generate docstrings to generate docstrings for this 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.

CodeRabbit Configuration File (.coderabbit.yaml)

  • You can programmatically configure CodeRabbit by adding a .coderabbit.yaml file to the root of your repository.
  • Please see the configuration documentation for more information.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json

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.

@sorccu
Copy link
Contributor

sorccu commented Mar 19, 2025

I think this might need some rework that would also require backend changes. Basically it's pretty unusual that the creation of cards is not done separately as e.g. a hypothetical StatusPageCard resource, but simply via StatusPage.cards[], and the way that also ends up requiring use of Ref.from() directly in the StatusPage.synthesize() function is also pretty strange. This is what the usage looks like currently:

const bigService = new StatusPageService('foo-service-big', {
  name: 'Big service',
})

const smallService = new StatusPageService('foo-service-small', {
  name: 'Small service',
})

new StatusPage('foo-page', {
  name: 'Dummy page',
  url: 'just-a-dummy-page',
  cards: [ // Magically creates card instances!
    {
      name: 'Card 1',
      services: [
        bigService,
        smallService,
      ],
    },
    {
      name: 'Card 2',
      services: [
        bigService,
      ],
    },
  ],
})

I can see from the models that a card can only belong to a single StatusPage so I can understand how it came to be that way, but taking Check as an example which can also only belong to a single CheckGroup, there's a group property that can be used to set the associated group from the check. Although for checks it's a little different because the group is optional and you can leave it out.

I think ideally the card model wouldn't be directly tied to a statusPageId so that cards could be created individually and then linked to pages via a separate model but that's probably too late now. It would have made the CLI constructs a little more fluid too.

With no model changes, I think this would be a more consistent approach with the way the CLI currently does things:

const dummyPage = new StatusPage('foo-page', {
  name: 'Dummy page',
  url: 'just-a-dummy-page',
})

const bigService = new StatusPageService('foo-service-big', {
  name: 'Big service',
})

const smallService = new StatusPageService('foo-service-small', {
  name: 'Small service',
})

new StatusPageCard('foo-card-1', {
  name: 'Card 1',
  statusPage: dummyPage,
  services: [
    bigService,
    smallService,
  ],
})

new StatusPageCard('foo-card-2', {
  name: 'Card 2',
  statusPage: dummyPage,
  services: [
    bigService,
  ],
})

Unfortunately the StatusPageCard construct has to point both "upwards" (to the page it belongs to) and "downwards" (to the services it has) because it has that requirement of being permanently fixed to a single status page.

If implemented, this approach would require the introduction of a new status-page-card project resource in the backend and possibly also a hidden-from-user status-page-card-service-assignment resource like checks and groups do with alert channels and private locations.

Alternatively with some model changes, the following would be possible:

const bigService = new StatusPageService('foo-service-big', {
  name: 'Big service',
})

const smallService = new StatusPageService('foo-service-small', {
  name: 'Small service',
})

const cardOne = new StatusPageCard('foo-card-1', {
  name: 'Card 1',
  services: [
    bigService,
    smallService,
  ],
})

const cardTwo = new StatusPageCard('foo-card-2', {
  name: 'Card 2',
  services: [
    bigService,
  ],
})

new StatusPage('foo-page', {
  name: 'Dummy page',
  url: 'just-a-dummy-page',
  cards: [
    cardOne,
    cardTwo,
  ],
})

Here the benefit is that all links are pointing "downwards" to child resources instead of reaching upwards to parent resources. And it would be possible to reuse a card on multiple pages (though that would likely require some additional UI work to implement safely).

@tnolet
Copy link
Member

tnolet commented Mar 19, 2025

Here the benefit is that all links are pointing "downwards" to child resources instead of reaching upwards to parent resources. And it would be possible to reuse a card on multiple pages (though that would likely require some additional UI work to implement safely).

My 2 cents. The "cards" resource is just for creating UI elements. They are strictly hierarchical. They should be nested under the status page resource as that is how they are used. Reusing cards is a 0.01% problem. And if it happens, it's actually easy to solve with TS in the MaC codebase.

So I would vote to now abstract this more than needed and just use the implementation as provided by @shiini2

This code totally makes sense to me as cards are "just" a UI element we need to order.

const bigService = new StatusPageService('foo-service-big', {
  name: 'Big service',
})

const smallService = new StatusPageService('foo-service-small', {
  name: 'Small service',
})

new StatusPage('foo-page', {
  name: 'Dummy page',
  url: 'just-a-dummy-page',
  cards: [ // Magically creates card instances!
    {
      name: 'Card 1',
      services: [
        bigService,
        smallService,
      ],
    },
    {
      name: 'Card 2',
      services: [
        bigService,
      ],
    },
  ],
})

coderabbitai[bot]
coderabbitai bot previously approved these changes Mar 24, 2025
@sorccu sorccu marked this pull request as ready for review March 24, 2025 20:23
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

🧹 Nitpick comments (3)
packages/cli/src/constructs/status-page-service.ts (1)

34-38: Consider adding type safety to the synthesize method return value

The synthesize method returns any|null which works but lacks type safety. Consider defining a specific interface for the return type to improve type checking and documentation.

- synthesize (): any|null {
+ interface StatusPageServicePayload {
+   name: string;
+ }
+ 
+ synthesize (): StatusPageServicePayload|null {
packages/cli/src/constructs/status-page.ts (2)

91-107: Consider more robust handling of optional fields in the synthesize method

The synthesize method returns null for optional fields that aren't provided. This approach may lead to cluttered payloads with null values. Consider excluding these fields entirely when they're undefined.

A more compact approach would be:

 synthesize (): any|null {
-  return {
-    name: this.name,
-    url: this.url,
-    customDomain: this.customDomain,
-    logo: this.logo,
-    redirectTo: this.redirectTo,
-    favicon: this.favicon,
-    defaultTheme: this.defaultTheme,
-    cards: this.cards.map(card => ({
-      name: card.name,
-      services: card
-        .services
-        ?.map((service) => Ref.from(service.logicalId)),
-    })),
-  }
+  const payload: any = {
+    name: this.name,
+    url: this.url,
+    cards: this.cards.map(card => {
+      const cardPayload: any = {
+        name: card.name,
+      }
+      
+      if (card.services?.length) {
+        cardPayload.services = card.services.map(service => Ref.from(service.logicalId))
+      }
+      
+      return cardPayload
+    }),
+  }
+  
+  if (this.customDomain) payload.customDomain = this.customDomain
+  if (this.logo) payload.logo = this.logo
+  if (this.redirectTo) payload.redirectTo = this.redirectTo
+  if (this.favicon) payload.favicon = this.favicon
+  if (this.defaultTheme) payload.defaultTheme = this.defaultTheme
+  
+  return payload
}

100-105: Consider validating that required service objects exist

When mapping services to references, there's no validation that the services actually exist. This could lead to cryptic errors if a service is undefined or doesn't have a logicalId.

 cards: this.cards.map(card => ({
   name: card.name,
   services: card
     .services
-    ?.map((service) => Ref.from(service.logicalId)),
+    ?.map((service) => {
+      if (!service || !service.logicalId) {
+        throw new Error(`Invalid service reference in card "${card.name}". Service must be a valid StatusPageService instance.`)
+      }
+      return Ref.from(service.logicalId)
+    }),
 })),
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 04de119 and 7f13030.

📒 Files selected for processing (4)
  • packages/cli/src/constructs/index.ts (1 hunks)
  • packages/cli/src/constructs/project.ts (4 hunks)
  • packages/cli/src/constructs/status-page-service.ts (1 hunks)
  • packages/cli/src/constructs/status-page.ts (1 hunks)
🧰 Additional context used
🧬 Code Definitions (3)
packages/cli/src/constructs/status-page-service.ts (1)
packages/cli/src/constructs/project.ts (1)
  • Session (144-219)
packages/cli/src/constructs/project.ts (2)
packages/cli/src/constructs/status-page.ts (1)
  • StatusPage (57-108)
packages/cli/src/constructs/status-page-service.ts (1)
  • StatusPageService (14-39)
packages/cli/src/constructs/status-page.ts (2)
packages/cli/src/constructs/status-page-service.ts (1)
  • StatusPageService (14-39)
packages/cli/src/constructs/project.ts (1)
  • Session (144-219)
🔇 Additional comments (11)
packages/cli/src/constructs/index.ts (1)

32-33: Exports look good!

The new exports for status page functionality are consistent with the existing export pattern in the file. This correctly makes the new constructs available to users of the package.

packages/cli/src/constructs/project.ts (4)

11-11: Import of the new constructs looks good

The new constructs have been properly added to the import list, maintaining the alphabetical ordering.


37-38: New ProjectData interface properties look good

The additions to the ProjectData interface properly define the data structures for the new status page and service constructs, following the existing pattern.


55-56: Initialization of new properties in ProjectData looks good

The new properties are correctly initialized as empty objects, matching the pattern used for other resource types.


107-108: Consider the ordering of status page resources in the synthesize method

The current implementation synthesizes the status page services before the status pages themselves. This is functionally correct, but it's worth noting that status pages reference services, so this order ensures that services exist before they're referenced.

packages/cli/src/constructs/status-page-service.ts (2)

1-9: StatusPageServiceProps interface is correctly defined

The interface has proper documentation and defines the required name property for status page services.


11-32: StatusPageService constructor implementation looks good

The StatusPageService class correctly extends Construct, defines the necessary type, and initializes properties in the constructor. The Session.registerConstruct call ensures the service is properly registered with the project.

packages/cli/src/constructs/status-page.ts (4)

6-15: StatusPageCardProps interface correctly defines card properties

The interface properly defines the name property as required and services as optional, with appropriate documentation.


17-17: StatusPageTheme type is well-defined

The StatusPageTheme type correctly uses string literals to define the possible theme values.


19-52: StatusPageProps interface is well-structured

The interface defines all necessary properties with good documentation. The required properties (name, url, cards) are correctly marked as required, while optional properties use the optional modifier.


54-89: StatusPage class constructor handles all properties correctly

The StatusPage class appropriately extends Construct, defines all properties, and initializes them in the constructor. The call to Session.registerConstruct ensures proper registration.

coderabbitai[bot]
coderabbitai bot previously approved these changes Mar 24, 2025
@sorccu sorccu changed the title feat: support status pages and services feat: support status pages and services [sc-23401] Mar 24, 2025
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

🧹 Nitpick comments (1)
packages/cli/e2e/__tests__/deploy.spec.ts (1)

248-250: Good addition of status page constructs to deployment testing.

The test now correctly verifies that the new StatusPage and StatusPageService constructs are properly recognized during deployment preview. This ensures consistent test coverage for the new functionality being added.

Consider adding more detailed tests that verify the specific behaviors and properties of status pages and services, not just their presence in the deployment output. Also, it's interesting that these constructs appear in the first configuration but not the second - this difference might warrant documentation if it represents an intentional configuration option.

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 7f13030 and bb80c11.

📒 Files selected for processing (2)
  • packages/cli/e2e/__tests__/deploy.spec.ts (1 hunks)
  • packages/cli/e2e/__tests__/fixtures/deploy-project/status-page.check.ts (1 hunks)
✅ Files skipped from review due to trivial changes (1)
  • packages/cli/e2e/tests/fixtures/deploy-project/status-page.check.ts
⏰ Context from checks skipped due to timeout of 90000ms (2)
  • GitHub Check: test - windows-latest
  • GitHub Check: test - ubuntu-latest

Copy link
Member

@maxigimenez maxigimenez left a comment

Choose a reason for hiding this comment

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

LGTM 👍

Copy link
Contributor

@shiini2 shiini2 left a comment

Choose a reason for hiding this comment

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

Tested locally, looks good! 🎉

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.

None yet

5 participants