From 40ba15c07e1a5f719727c62bad2dbc5a4b272101 Mon Sep 17 00:00:00 2001 From: Nick Krantz <125900171+nick-livefront@users.noreply.github.com> Date: Thu, 18 Apr 2024 14:24:16 -0500 Subject: [PATCH] [SM-956] Secret Manager: Integrations Page (#8701) * add navigation item for integrations and SDKs page * Initial routing to Integrations & SDKs page * Initial add of integrations component * Initial add of SDKs component * add secret manage integration images * remove integration & sdk components in favor of a single component * add integration & integration grid components * add integrations & sdks * rename page & components to integration after design discussion * add external rel attribute for SDK links * remove ts extension * refactor: use pseudo element to cover as a link * refactor: change secondaryText to linkText to align with usage * update icon for integrations * add new badge option for integration cards * hardcode integration/sdk names * add dark mode images for integrations and sdks * update integration/sdk card with dark mode image when applicable * refactor integration types to be an enum * fix enum typings in integration grid test --------- Co-authored-by: Robyn MacCallum --- .../secrets-manager/integrations/ansible.svg | 4 + .../integrations/github-white.svg | 10 + .../secrets-manager/integrations/github.svg | 3 + .../integrations/gitlab-white.svg | 3 + .../secrets-manager/integrations/gitlab.svg | 6 + .../secrets-manager/sdks/c-plus-plus.png | Bin 0 -> 12445 bytes .../images/secrets-manager/sdks/c-sharp.svg | 7 + .../src/images/secrets-manager/sdks/go.svg | 7 + .../secrets-manager/sdks/java-white.svg | 15 ++ .../src/images/secrets-manager/sdks/java.svg | 15 ++ .../src/images/secrets-manager/sdks/php.svg | 4 + .../images/secrets-manager/sdks/python.svg | 19 ++ .../src/images/secrets-manager/sdks/ruby.png | Bin 0 -> 19253 bytes .../src/images/secrets-manager/sdks/wasm.svg | 11 ++ apps/web/src/locales/en/messages.json | 52 ++++++ .../integration-card.component.html | 29 +++ .../integration-card.component.spec.ts | 174 ++++++++++++++++++ .../integration-card.component.ts | 93 ++++++++++ .../integration-grid.component.html | 15 ++ .../integration-grid.component.spec.ts | 81 ++++++++ .../integration-grid.component.ts | 15 ++ .../integrations-routing.module.ts | 17 ++ .../integrations/integrations.component.html | 16 ++ .../integrations.component.spec.ts | 77 ++++++++ .../integrations/integrations.component.ts | 113 ++++++++++++ .../integrations/integrations.module.ts | 15 ++ .../integrations/models/integration.ts | 21 +++ .../layout/navigation.component.html | 6 + .../app/secrets-manager/sm-routing.module.ts | 8 + libs/common/src/enums/index.ts | 1 + .../common/src/enums/integration-type.enum.ts | 4 + 31 files changed, 841 insertions(+) create mode 100644 apps/web/src/images/secrets-manager/integrations/ansible.svg create mode 100644 apps/web/src/images/secrets-manager/integrations/github-white.svg create mode 100644 apps/web/src/images/secrets-manager/integrations/github.svg create mode 100644 apps/web/src/images/secrets-manager/integrations/gitlab-white.svg create mode 100644 apps/web/src/images/secrets-manager/integrations/gitlab.svg create mode 100644 apps/web/src/images/secrets-manager/sdks/c-plus-plus.png create mode 100644 apps/web/src/images/secrets-manager/sdks/c-sharp.svg create mode 100644 apps/web/src/images/secrets-manager/sdks/go.svg create mode 100644 apps/web/src/images/secrets-manager/sdks/java-white.svg create mode 100644 apps/web/src/images/secrets-manager/sdks/java.svg create mode 100644 apps/web/src/images/secrets-manager/sdks/php.svg create mode 100644 apps/web/src/images/secrets-manager/sdks/python.svg create mode 100644 apps/web/src/images/secrets-manager/sdks/ruby.png create mode 100644 apps/web/src/images/secrets-manager/sdks/wasm.svg create mode 100644 bitwarden_license/bit-web/src/app/secrets-manager/integrations/integration-card/integration-card.component.html create mode 100644 bitwarden_license/bit-web/src/app/secrets-manager/integrations/integration-card/integration-card.component.spec.ts create mode 100644 bitwarden_license/bit-web/src/app/secrets-manager/integrations/integration-card/integration-card.component.ts create mode 100644 bitwarden_license/bit-web/src/app/secrets-manager/integrations/integration-grid/integration-grid.component.html create mode 100644 bitwarden_license/bit-web/src/app/secrets-manager/integrations/integration-grid/integration-grid.component.spec.ts create mode 100644 bitwarden_license/bit-web/src/app/secrets-manager/integrations/integration-grid/integration-grid.component.ts create mode 100644 bitwarden_license/bit-web/src/app/secrets-manager/integrations/integrations-routing.module.ts create mode 100644 bitwarden_license/bit-web/src/app/secrets-manager/integrations/integrations.component.html create mode 100644 bitwarden_license/bit-web/src/app/secrets-manager/integrations/integrations.component.spec.ts create mode 100644 bitwarden_license/bit-web/src/app/secrets-manager/integrations/integrations.component.ts create mode 100644 bitwarden_license/bit-web/src/app/secrets-manager/integrations/integrations.module.ts create mode 100644 bitwarden_license/bit-web/src/app/secrets-manager/integrations/models/integration.ts create mode 100644 libs/common/src/enums/integration-type.enum.ts diff --git a/apps/web/src/images/secrets-manager/integrations/ansible.svg b/apps/web/src/images/secrets-manager/integrations/ansible.svg new file mode 100644 index 0000000000..7a32617ab2 --- /dev/null +++ b/apps/web/src/images/secrets-manager/integrations/ansible.svg @@ -0,0 +1,4 @@ + + + + diff --git a/apps/web/src/images/secrets-manager/integrations/github-white.svg b/apps/web/src/images/secrets-manager/integrations/github-white.svg new file mode 100644 index 0000000000..030c7c6723 --- /dev/null +++ b/apps/web/src/images/secrets-manager/integrations/github-white.svg @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/apps/web/src/images/secrets-manager/integrations/github.svg b/apps/web/src/images/secrets-manager/integrations/github.svg new file mode 100644 index 0000000000..99e3ffbbe2 --- /dev/null +++ b/apps/web/src/images/secrets-manager/integrations/github.svg @@ -0,0 +1,3 @@ + + + diff --git a/apps/web/src/images/secrets-manager/integrations/gitlab-white.svg b/apps/web/src/images/secrets-manager/integrations/gitlab-white.svg new file mode 100644 index 0000000000..7f7bf006ee --- /dev/null +++ b/apps/web/src/images/secrets-manager/integrations/gitlab-white.svg @@ -0,0 +1,3 @@ + + + diff --git a/apps/web/src/images/secrets-manager/integrations/gitlab.svg b/apps/web/src/images/secrets-manager/integrations/gitlab.svg new file mode 100644 index 0000000000..9bbc28c37a --- /dev/null +++ b/apps/web/src/images/secrets-manager/integrations/gitlab.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/apps/web/src/images/secrets-manager/sdks/c-plus-plus.png b/apps/web/src/images/secrets-manager/sdks/c-plus-plus.png new file mode 100644 index 0000000000000000000000000000000000000000..bac17e2ae270435654cf2c206938ce51ae4b97a9 GIT binary patch literal 12445 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 bFcA3v??;y!V7JTB00000NkvXXu0mjfP1!2= literal 0 HcmV?d00001 diff --git a/apps/web/src/images/secrets-manager/sdks/c-sharp.svg b/apps/web/src/images/secrets-manager/sdks/c-sharp.svg new file mode 100644 index 0000000000..f0da2234f1 --- /dev/null +++ b/apps/web/src/images/secrets-manager/sdks/c-sharp.svg @@ -0,0 +1,7 @@ + + + + + + + diff --git a/apps/web/src/images/secrets-manager/sdks/go.svg b/apps/web/src/images/secrets-manager/sdks/go.svg new file mode 100644 index 0000000000..d335346e71 --- /dev/null +++ b/apps/web/src/images/secrets-manager/sdks/go.svg @@ -0,0 +1,7 @@ + + + + + + + diff --git a/apps/web/src/images/secrets-manager/sdks/java-white.svg b/apps/web/src/images/secrets-manager/sdks/java-white.svg new file mode 100644 index 0000000000..082897c081 --- /dev/null +++ b/apps/web/src/images/secrets-manager/sdks/java-white.svg @@ -0,0 +1,15 @@ + + + + + + + + + + + + + + + diff --git a/apps/web/src/images/secrets-manager/sdks/java.svg b/apps/web/src/images/secrets-manager/sdks/java.svg new file mode 100644 index 0000000000..085cedd07c --- /dev/null +++ b/apps/web/src/images/secrets-manager/sdks/java.svg @@ -0,0 +1,15 @@ + + + + + + + + + + + + + + + diff --git a/apps/web/src/images/secrets-manager/sdks/php.svg b/apps/web/src/images/secrets-manager/sdks/php.svg new file mode 100644 index 0000000000..5e36980ca0 --- /dev/null +++ b/apps/web/src/images/secrets-manager/sdks/php.svg @@ -0,0 +1,4 @@ + + + + diff --git a/apps/web/src/images/secrets-manager/sdks/python.svg b/apps/web/src/images/secrets-manager/sdks/python.svg new file mode 100644 index 0000000000..36f5cd7f05 --- /dev/null +++ b/apps/web/src/images/secrets-manager/sdks/python.svg @@ -0,0 +1,19 @@ + + + + + + + + + + + + + + + + + + + diff --git a/apps/web/src/images/secrets-manager/sdks/ruby.png b/apps/web/src/images/secrets-manager/sdks/ruby.png new file mode 100644 index 0000000000000000000000000000000000000000..2dac1c8da81848acf1994ce7d9446f8f3ac5a70a GIT binary patch literal 19253 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 + + + + + + + + + + diff --git a/apps/web/src/locales/en/messages.json b/apps/web/src/locales/en/messages.json index f14574508c..845816562b 100644 --- a/apps/web/src/locales/en/messages.json +++ b/apps/web/src/locales/en/messages.json @@ -7906,6 +7906,9 @@ "restrictedGroupAccessDesc": { "message": "You cannot add yourself to a group." }, + "unassignedItemsBannerSelfHost": { + "message": "Notice: On May 2, 2024, unassigned organization items will no longer be visible in your All Vaults view across devices and will only be accessible via the Admin Console. Assign these items to a collection from the Admin Console to make them visible." + }, "unassignedItemsBannerNotice": { "message": "Notice: Unassigned organization items are no longer visible in your All Vaults view across devices and are now only accessible via the Admin Console." }, @@ -7962,6 +7965,55 @@ "errorAssigningTargetFolder": { "message": "Error assigning target folder." }, + "integrationsAndSdks": { + "message": "Integrations & SDKs", + "description": "The title for the section that deals with integrations and SDKs." + }, + "integrations":{ + "message": "Integrations" + }, + "integrationsDesc": { + "message": "Automatically sync secrets from Bitwarden Secrets Manager to a third-party service." + }, + "sdks": { + "message": "SDKs" + }, + "sdksDesc": { + "message": "Use Bitwarden Secrets Manager SDK in the following programming languages to build your own applications." + }, + "setUpGithubActions": { + "message": "Set up Github Actions" + }, + "setUpGitlabCICD": { + "message": "Set up GitLab CI/CD" + }, + "setUpAnsible": { + "message": "Set up Ansible" + }, + "cSharpSDKRepo": { + "message": "View C# repository" + }, + "cPlusPlusSDKRepo": { + "message": "View C++ repository" + }, + "jsWebAssemblySDKRepo": { + "message": "View JS WebAssembly repository" + }, + "javaSDKRepo": { + "message": "View Java repository" + }, + "pythonSDKRepo": { + "message": "View Python repository" + }, + "phpSDKRepo": { + "message": "View php repository" + }, + "rubySDKRepo": { + "message": "View Ruby repository" + }, + "goSDKRepo": { + "message": "View Go repository" + }, "createNewClientToManageAsProvider": { "message": "Create a new client organization to manage as a Provider. Additional seats will be reflected in the next billing cycle." }, diff --git a/bitwarden_license/bit-web/src/app/secrets-manager/integrations/integration-card/integration-card.component.html b/bitwarden_license/bit-web/src/app/secrets-manager/integrations/integration-card/integration-card.component.html new file mode 100644 index 0000000000..15b2519dae --- /dev/null +++ b/bitwarden_license/bit-web/src/app/secrets-manager/integrations/integration-card/integration-card.component.html @@ -0,0 +1,29 @@ + diff --git a/bitwarden_license/bit-web/src/app/secrets-manager/integrations/integration-card/integration-card.component.spec.ts b/bitwarden_license/bit-web/src/app/secrets-manager/integrations/integration-card/integration-card.component.spec.ts new file mode 100644 index 0000000000..94cec5f627 --- /dev/null +++ b/bitwarden_license/bit-web/src/app/secrets-manager/integrations/integration-card/integration-card.component.spec.ts @@ -0,0 +1,174 @@ +import { ComponentFixture, TestBed } from "@angular/core/testing"; +import { BehaviorSubject } from "rxjs"; + +import { SYSTEM_THEME_OBSERVABLE } from "../../../../../../../libs/angular/src/services/injection-tokens"; +import { ThemeType } from "../../../../../../../libs/common/src/platform/enums"; +import { ThemeStateService } from "../../../../../../../libs/common/src/platform/theming/theme-state.service"; + +import { IntegrationCardComponent } from "./integration-card.component"; + +describe("IntegrationCardComponent", () => { + let component: IntegrationCardComponent; + let fixture: ComponentFixture; + + const systemTheme$ = new BehaviorSubject(ThemeType.Light); + const usersPreferenceTheme$ = new BehaviorSubject(ThemeType.Light); + + beforeEach(async () => { + // reset system theme + systemTheme$.next(ThemeType.Light); + + await TestBed.configureTestingModule({ + declarations: [IntegrationCardComponent], + providers: [ + { + provide: ThemeStateService, + useValue: { selectedTheme$: usersPreferenceTheme$ }, + }, + { + provide: SYSTEM_THEME_OBSERVABLE, + useValue: systemTheme$, + }, + ], + }).compileComponents(); + }); + + beforeEach(() => { + fixture = TestBed.createComponent(IntegrationCardComponent); + component = fixture.componentInstance; + + component.name = "Integration Name"; + component.image = "test-image.png"; + component.linkText = "Get started with integration"; + component.linkURL = "https://example.com/"; + + fixture.detectChanges(); + }); + + it("assigns link href", () => { + const link = fixture.nativeElement.querySelector("a"); + + expect(link.href).toBe("https://example.com/"); + }); + + it("renders card body", () => { + const name = fixture.nativeElement.querySelector("h3"); + const link = fixture.nativeElement.querySelector("a"); + + expect(name.textContent).toBe("Integration Name"); + expect(link.textContent.trim()).toBe("Get started with integration"); + }); + + it("assigns external rel attribute", () => { + component.externalURL = true; + fixture.detectChanges(); + + const link = fixture.nativeElement.querySelector("a"); + + expect(link.rel).toBe("noopener noreferrer"); + }); + + describe("new badge", () => { + beforeEach(() => { + jest.useFakeTimers(); + jest.setSystemTime(new Date("2023-09-01")); + }); + + afterEach(() => { + jest.useRealTimers(); + }); + + it("shows when expiration is in the future", () => { + component.newBadgeExpiration = "2023-09-02"; + expect(component.showNewBadge()).toBe(true); + }); + + it("does not show when expiration is not set", () => { + expect(component.showNewBadge()).toBe(false); + }); + + it("does not show when expiration is in the past", () => { + component.newBadgeExpiration = "2023-08-31"; + expect(component.showNewBadge()).toBe(false); + }); + + it("does not show when expiration is today", () => { + component.newBadgeExpiration = "2023-09-01"; + expect(component.showNewBadge()).toBe(false); + }); + + it("does not show when expiration is invalid", () => { + component.newBadgeExpiration = "not-a-date"; + expect(component.showNewBadge()).toBe(false); + }); + }); + + describe("imageDarkMode", () => { + it("ignores theme changes when darkModeImage is not set", () => { + systemTheme$.next(ThemeType.Dark); + usersPreferenceTheme$.next(ThemeType.Dark); + + fixture.detectChanges(); + + expect(component.imageEle.nativeElement.src).toContain("test-image.png"); + }); + + describe("user prefers the system theme", () => { + beforeEach(() => { + component.imageDarkMode = "test-image-dark.png"; + }); + + it("sets image src to imageDarkMode", () => { + usersPreferenceTheme$.next(ThemeType.System); + systemTheme$.next(ThemeType.Dark); + + fixture.detectChanges(); + + expect(component.imageEle.nativeElement.src).toContain("test-image-dark.png"); + }); + + it("sets image src to light mode image", () => { + component.imageEle.nativeElement.src = "test-image-dark.png"; + + usersPreferenceTheme$.next(ThemeType.System); + systemTheme$.next(ThemeType.Light); + + fixture.detectChanges(); + + expect(component.imageEle.nativeElement.src).toContain("test-image.png"); + }); + }); + + describe("user prefers dark mode", () => { + beforeEach(() => { + component.imageDarkMode = "test-image-dark.png"; + }); + + it("updates image to dark mode", () => { + systemTheme$.next(ThemeType.Light); // system theme shouldn't matter + usersPreferenceTheme$.next(ThemeType.Dark); + + fixture.detectChanges(); + + expect(component.imageEle.nativeElement.src).toContain("test-image-dark.png"); + }); + }); + + describe("user prefers light mode", () => { + beforeEach(() => { + component.imageDarkMode = "test-image-dark.png"; + }); + + it("updates image to light mode", () => { + component.imageEle.nativeElement.src = "test-image-dark.png"; + + systemTheme$.next(ThemeType.Dark); // system theme shouldn't matter + usersPreferenceTheme$.next(ThemeType.Light); + + fixture.detectChanges(); + + expect(component.imageEle.nativeElement.src).toContain("test-image.png"); + }); + }); + }); +}); diff --git a/bitwarden_license/bit-web/src/app/secrets-manager/integrations/integration-card/integration-card.component.ts b/bitwarden_license/bit-web/src/app/secrets-manager/integrations/integration-card/integration-card.component.ts new file mode 100644 index 0000000000..bf5f5bd311 --- /dev/null +++ b/bitwarden_license/bit-web/src/app/secrets-manager/integrations/integration-card/integration-card.component.ts @@ -0,0 +1,93 @@ +import { + AfterViewInit, + Component, + ElementRef, + Inject, + Input, + OnDestroy, + ViewChild, +} from "@angular/core"; +import { Observable, Subject, combineLatest, takeUntil } from "rxjs"; + +import { SYSTEM_THEME_OBSERVABLE } from "@bitwarden/angular/services/injection-tokens"; +import { ThemeType } from "@bitwarden/common/platform/enums"; +import { ThemeStateService } from "@bitwarden/common/platform/theming/theme-state.service"; + +@Component({ + selector: "sm-integration-card", + templateUrl: "./integration-card.component.html", +}) +export class IntegrationCardComponent implements AfterViewInit, OnDestroy { + private destroyed$: Subject = new Subject(); + @ViewChild("imageEle") imageEle: ElementRef; + + @Input() name: string; + @Input() image: string; + @Input() imageDarkMode?: string; + @Input() linkText: string; + @Input() linkURL: string; + + /** Adds relevant `rel` attribute to external links */ + @Input() externalURL?: boolean; + + /** + * Date of when the new badge should be hidden. + * When omitted, the new badge is never shown. + * + * @example "2024-12-31" + */ + @Input() newBadgeExpiration?: string; + + constructor( + private themeStateService: ThemeStateService, + @Inject(SYSTEM_THEME_OBSERVABLE) + private systemTheme$: Observable, + ) {} + + ngAfterViewInit() { + combineLatest([this.themeStateService.selectedTheme$, this.systemTheme$]) + .pipe(takeUntil(this.destroyed$)) + .subscribe(([theme, systemTheme]) => { + // When the card doesn't have a dark mode image, exit early + if (!this.imageDarkMode) { + return; + } + + if (theme === ThemeType.System) { + // When the user's preference is the system theme, + // use the system theme to determine the image + const prefersDarkMode = + systemTheme === ThemeType.Dark || systemTheme === ThemeType.SolarizedDark; + + this.imageEle.nativeElement.src = prefersDarkMode ? this.imageDarkMode : this.image; + } else if (theme === ThemeType.Dark || theme === ThemeType.SolarizedDark) { + // When the user's preference is dark mode, use the dark mode image + this.imageEle.nativeElement.src = this.imageDarkMode; + } else { + // Otherwise use the light mode image + this.imageEle.nativeElement.src = this.image; + } + }); + } + + ngOnDestroy(): void { + this.destroyed$.next(); + this.destroyed$.complete(); + } + + /** Show the "new" badge when expiration is in the future */ + showNewBadge() { + if (!this.newBadgeExpiration) { + return false; + } + + const expirationDate = new Date(this.newBadgeExpiration); + + // Do not show the new badge for invalid dates + if (isNaN(expirationDate.getTime())) { + return false; + } + + return expirationDate > new Date(); + } +} diff --git a/bitwarden_license/bit-web/src/app/secrets-manager/integrations/integration-grid/integration-grid.component.html b/bitwarden_license/bit-web/src/app/secrets-manager/integrations/integration-grid/integration-grid.component.html new file mode 100644 index 0000000000..a0c82d2f34 --- /dev/null +++ b/bitwarden_license/bit-web/src/app/secrets-manager/integrations/integration-grid/integration-grid.component.html @@ -0,0 +1,15 @@ +
    +
  • + +
  • +
diff --git a/bitwarden_license/bit-web/src/app/secrets-manager/integrations/integration-grid/integration-grid.component.spec.ts b/bitwarden_license/bit-web/src/app/secrets-manager/integrations/integration-grid/integration-grid.component.spec.ts new file mode 100644 index 0000000000..e74e057e06 --- /dev/null +++ b/bitwarden_license/bit-web/src/app/secrets-manager/integrations/integration-grid/integration-grid.component.spec.ts @@ -0,0 +1,81 @@ +import { ComponentFixture, TestBed } from "@angular/core/testing"; +import { By } from "@angular/platform-browser"; +import { mock } from "jest-mock-extended"; +import { of } from "rxjs"; + +import { SYSTEM_THEME_OBSERVABLE } from "../../../../../../../libs/angular/src/services/injection-tokens"; +import { IntegrationType } from "../../../../../../../libs/common/src/enums"; +import { ThemeType } from "../../../../../../../libs/common/src/platform/enums"; +import { ThemeStateService } from "../../../../../../../libs/common/src/platform/theming/theme-state.service"; +import { IntegrationCardComponent } from "../integration-card/integration-card.component"; +import { Integration } from "../models/integration"; + +import { IntegrationGridComponent } from "./integration-grid.component"; + +describe("IntegrationGridComponent", () => { + let component: IntegrationGridComponent; + let fixture: ComponentFixture; + const integrations: Integration[] = [ + { + name: "Integration 1", + image: "test-image1.png", + linkText: "Get started with integration 1", + linkURL: "https://example.com/1", + type: IntegrationType.Integration, + }, + { + name: "SDK 2", + image: "test-image2.png", + linkText: "View SDK 2", + linkURL: "https://example.com/2", + type: IntegrationType.SDK, + }, + ]; + + beforeEach(() => { + TestBed.configureTestingModule({ + declarations: [IntegrationGridComponent, IntegrationCardComponent], + providers: [ + { + provide: ThemeStateService, + useValue: mock(), + }, + { + provide: SYSTEM_THEME_OBSERVABLE, + useValue: of(ThemeType.Light), + }, + ], + }); + + fixture = TestBed.createComponent(IntegrationGridComponent); + component = fixture.componentInstance; + component.integrations = integrations; + fixture.detectChanges(); + }); + + it("lists all integrations", () => { + expect(component.integrations).toEqual(integrations); + + const cards = fixture.debugElement.queryAll(By.directive(IntegrationCardComponent)); + + expect(cards.length).toBe(integrations.length); + }); + + it("assigns the correct attributes to IntegrationCardComponent", () => { + expect(component.integrations).toEqual(integrations); + + const card = fixture.debugElement.queryAll(By.directive(IntegrationCardComponent))[1]; + + expect(card.componentInstance.name).toBe("SDK 2"); + expect(card.componentInstance.image).toBe("test-image2.png"); + expect(card.componentInstance.linkText).toBe("View SDK 2"); + expect(card.componentInstance.linkURL).toBe("https://example.com/2"); + }); + + it("assigns `externalURL` for SDKs", () => { + const card = fixture.debugElement.queryAll(By.directive(IntegrationCardComponent)); + + expect(card[0].componentInstance.externalURL).toBe(false); + expect(card[1].componentInstance.externalURL).toBe(true); + }); +}); diff --git a/bitwarden_license/bit-web/src/app/secrets-manager/integrations/integration-grid/integration-grid.component.ts b/bitwarden_license/bit-web/src/app/secrets-manager/integrations/integration-grid/integration-grid.component.ts new file mode 100644 index 0000000000..058d59d702 --- /dev/null +++ b/bitwarden_license/bit-web/src/app/secrets-manager/integrations/integration-grid/integration-grid.component.ts @@ -0,0 +1,15 @@ +import { Component, Input } from "@angular/core"; + +import { IntegrationType } from "@bitwarden/common/enums"; + +import { Integration } from "../models/integration"; + +@Component({ + selector: "sm-integration-grid", + templateUrl: "./integration-grid.component.html", +}) +export class IntegrationGridComponent { + @Input() integrations: Integration[]; + + protected IntegrationType = IntegrationType; +} diff --git a/bitwarden_license/bit-web/src/app/secrets-manager/integrations/integrations-routing.module.ts b/bitwarden_license/bit-web/src/app/secrets-manager/integrations/integrations-routing.module.ts new file mode 100644 index 0000000000..91402113a9 --- /dev/null +++ b/bitwarden_license/bit-web/src/app/secrets-manager/integrations/integrations-routing.module.ts @@ -0,0 +1,17 @@ +import { NgModule } from "@angular/core"; +import { RouterModule, Routes } from "@angular/router"; + +import { IntegrationsComponent } from "./integrations.component"; + +const routes: Routes = [ + { + path: "", + component: IntegrationsComponent, + }, +]; + +@NgModule({ + imports: [RouterModule.forChild(routes)], + exports: [RouterModule], +}) +export class IntegrationsRoutingModule {} diff --git a/bitwarden_license/bit-web/src/app/secrets-manager/integrations/integrations.component.html b/bitwarden_license/bit-web/src/app/secrets-manager/integrations/integrations.component.html new file mode 100644 index 0000000000..a2f2188861 --- /dev/null +++ b/bitwarden_license/bit-web/src/app/secrets-manager/integrations/integrations.component.html @@ -0,0 +1,16 @@ + + + + +
+

{{ "integrationsDesc" | i18n }}

+ +
+ +
+

+ {{ "sdks" | i18n }} +

+

{{ "sdksDesc" | i18n }}

+ +
diff --git a/bitwarden_license/bit-web/src/app/secrets-manager/integrations/integrations.component.spec.ts b/bitwarden_license/bit-web/src/app/secrets-manager/integrations/integrations.component.spec.ts new file mode 100644 index 0000000000..10fbaa1f3f --- /dev/null +++ b/bitwarden_license/bit-web/src/app/secrets-manager/integrations/integrations.component.spec.ts @@ -0,0 +1,77 @@ +import { Component } from "@angular/core"; +import { ComponentFixture, TestBed } from "@angular/core/testing"; +import { By } from "@angular/platform-browser"; +import { mock } from "jest-mock-extended"; +import { of } from "rxjs"; + +import { SYSTEM_THEME_OBSERVABLE } from "../../../../../../libs/angular/src/services/injection-tokens"; +import { I18nService } from "../../../../../../libs/common/src/platform/abstractions/i18n.service"; +import { ThemeType } from "../../../../../../libs/common/src/platform/enums"; +import { ThemeStateService } from "../../../../../../libs/common/src/platform/theming/theme-state.service"; +import { I18nPipe } from "../../../../../../libs/components/src/shared/i18n.pipe"; + +import { IntegrationCardComponent } from "./integration-card/integration-card.component"; +import { IntegrationGridComponent } from "./integration-grid/integration-grid.component"; +import { IntegrationsComponent } from "./integrations.component"; + +@Component({ + selector: "app-header", + template: "
", +}) +class MockHeaderComponent {} + +@Component({ + selector: "sm-new-menu", + template: "
", +}) +class MockNewMenuComponent {} + +describe("IntegrationsComponent", () => { + let fixture: ComponentFixture; + + beforeEach(async () => { + await TestBed.configureTestingModule({ + declarations: [ + IntegrationsComponent, + IntegrationGridComponent, + IntegrationCardComponent, + MockHeaderComponent, + MockNewMenuComponent, + I18nPipe, + ], + providers: [ + { + provide: I18nService, + useValue: mock({ t: (key) => key }), + }, + { + provide: ThemeStateService, + useValue: mock(), + }, + { + provide: SYSTEM_THEME_OBSERVABLE, + useValue: of(ThemeType.Light), + }, + ], + }).compileComponents(); + fixture = TestBed.createComponent(IntegrationsComponent); + fixture.detectChanges(); + }); + + it("divides Integrations & SDKS", () => { + const [integrationList, sdkList] = fixture.debugElement.queryAll( + By.directive(IntegrationGridComponent), + ); + + // Validate only expected names, as the data is constant + expect( + (integrationList.componentInstance as IntegrationGridComponent).integrations.map( + (i) => i.name, + ), + ).toEqual(["GitHub Actions", "GitLab CI/CD", "Ansible"]); + + expect( + (sdkList.componentInstance as IntegrationGridComponent).integrations.map((i) => i.name), + ).toEqual(["C#", "C++", "Go", "Java", "JS WebAssembly", "php", "Python", "Ruby"]); + }); +}); diff --git a/bitwarden_license/bit-web/src/app/secrets-manager/integrations/integrations.component.ts b/bitwarden_license/bit-web/src/app/secrets-manager/integrations/integrations.component.ts new file mode 100644 index 0000000000..f11048b6a3 --- /dev/null +++ b/bitwarden_license/bit-web/src/app/secrets-manager/integrations/integrations.component.ts @@ -0,0 +1,113 @@ +import { Component } from "@angular/core"; + +import { IntegrationType } from "@bitwarden/common/enums"; +import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service"; + +import { Integration } from "./models/integration"; + +@Component({ + selector: "sm-integrations", + templateUrl: "./integrations.component.html", +}) +export class IntegrationsComponent { + private integrationsAndSdks: Integration[] = []; + + constructor(i18nService: I18nService) { + this.integrationsAndSdks = [ + { + name: "GitHub Actions", + linkText: i18nService.t("setUpGithubActions"), + linkURL: "https://bitwarden.com/help/github-actions-integration/", + image: "../../../../../../../images/secrets-manager/integrations/github.svg", + imageDarkMode: "../../../../../../../images/secrets-manager/integrations/github-white.svg", + type: IntegrationType.Integration, + }, + { + name: "GitLab CI/CD", + linkText: i18nService.t("setUpGitlabCICD"), + linkURL: "https://bitwarden.com/help/gitlab-integration/", + image: "../../../../../../../images/secrets-manager/integrations/gitlab.svg", + imageDarkMode: "../../../../../../../images/secrets-manager/integrations/gitlab-white.svg", + type: IntegrationType.Integration, + }, + { + name: "Ansible", + linkText: i18nService.t("setUpAnsible"), + linkURL: "https://bitwarden.com/help/ansible-integration/", + image: "../../../../../../../images/secrets-manager/integrations/ansible.svg", + type: IntegrationType.Integration, + }, + { + name: "C#", + linkText: i18nService.t("cSharpSDKRepo"), + linkURL: "https://github.com/bitwarden/sdk/tree/main/languages/csharp", + image: "../../../../../../../images/secrets-manager/sdks/c-sharp.svg", + type: IntegrationType.SDK, + }, + { + name: "C++", + linkText: i18nService.t("cPlusPlusSDKRepo"), + linkURL: "https://github.com/bitwarden/sdk/tree/main/languages/cpp", + image: "../../../../../../../images/secrets-manager/sdks/c-plus-plus.png", + type: IntegrationType.SDK, + }, + { + name: "Go", + linkText: i18nService.t("goSDKRepo"), + linkURL: "https://github.com/bitwarden/sdk/tree/main/languages/go", + image: "../../../../../../../images/secrets-manager/sdks/go.svg", + type: IntegrationType.SDK, + }, + { + name: "Java", + linkText: i18nService.t("javaSDKRepo"), + linkURL: "https://github.com/bitwarden/sdk/tree/main/languages/java", + image: "../../../../../../../images/secrets-manager/sdks/java.svg", + imageDarkMode: "../../../../../../../images/secrets-manager/sdks/java-white.svg", + type: IntegrationType.SDK, + }, + { + name: "JS WebAssembly", + linkText: i18nService.t("jsWebAssemblySDKRepo"), + linkURL: "https://github.com/bitwarden/sdk/tree/main/languages/js", + image: "../../../../../../../images/secrets-manager/sdks/wasm.svg", + type: IntegrationType.SDK, + }, + { + name: "php", + linkText: i18nService.t("phpSDKRepo"), + linkURL: "https://github.com/bitwarden/sdk/tree/main/languages/php", + image: "../../../../../../../images/secrets-manager/sdks/php.svg", + type: IntegrationType.SDK, + }, + { + name: "Python", + linkText: i18nService.t("pythonSDKRepo"), + linkURL: "https://github.com/bitwarden/sdk/tree/main/languages/python", + image: "../../../../../../../images/secrets-manager/sdks/python.svg", + type: IntegrationType.SDK, + }, + { + name: "Ruby", + linkText: i18nService.t("rubySDKRepo"), + linkURL: "https://github.com/bitwarden/sdk/tree/main/languages/ruby", + image: "../../../../../../../images/secrets-manager/sdks/ruby.png", + type: IntegrationType.SDK, + }, + ]; + } + + /** Filter out content for the integrations sections */ + get integrations(): Integration[] { + return this.integrationsAndSdks.filter( + (integration) => integration.type === IntegrationType.Integration, + ); + } + + /** Filter out content for the SDKs section */ + get sdks(): Integration[] { + return this.integrationsAndSdks.filter( + (integration) => integration.type === IntegrationType.SDK, + ); + } +} diff --git a/bitwarden_license/bit-web/src/app/secrets-manager/integrations/integrations.module.ts b/bitwarden_license/bit-web/src/app/secrets-manager/integrations/integrations.module.ts new file mode 100644 index 0000000000..0d26b626f1 --- /dev/null +++ b/bitwarden_license/bit-web/src/app/secrets-manager/integrations/integrations.module.ts @@ -0,0 +1,15 @@ +import { NgModule } from "@angular/core"; + +import { SecretsManagerSharedModule } from "../shared/sm-shared.module"; + +import { IntegrationCardComponent } from "./integration-card/integration-card.component"; +import { IntegrationGridComponent } from "./integration-grid/integration-grid.component"; +import { IntegrationsRoutingModule } from "./integrations-routing.module"; +import { IntegrationsComponent } from "./integrations.component"; + +@NgModule({ + imports: [SecretsManagerSharedModule, IntegrationsRoutingModule], + declarations: [IntegrationsComponent, IntegrationGridComponent, IntegrationCardComponent], + providers: [], +}) +export class IntegrationsModule {} diff --git a/bitwarden_license/bit-web/src/app/secrets-manager/integrations/models/integration.ts b/bitwarden_license/bit-web/src/app/secrets-manager/integrations/models/integration.ts new file mode 100644 index 0000000000..51ca79b30f --- /dev/null +++ b/bitwarden_license/bit-web/src/app/secrets-manager/integrations/models/integration.ts @@ -0,0 +1,21 @@ +import { IntegrationType } from "@bitwarden/common/enums"; + +/** Integration or SDK */ +export type Integration = { + name: string; + image: string; + /** + * Optional image shown in dark mode. + */ + imageDarkMode?: string; + linkURL: string; + linkText: string; + type: IntegrationType; + /** + * Shows the "New" badge until the defined date. + * When omitted, the badge is never shown. + * + * @example "2024-12-31" + */ + newBadgeExpiration?: string; +}; diff --git a/bitwarden_license/bit-web/src/app/secrets-manager/layout/navigation.component.html b/bitwarden_license/bit-web/src/app/secrets-manager/layout/navigation.component.html index c6c7bc6efb..e382fbd9a9 100644 --- a/bitwarden_license/bit-web/src/app/secrets-manager/layout/navigation.component.html +++ b/bitwarden_license/bit-web/src/app/secrets-manager/layout/navigation.component.html @@ -22,6 +22,12 @@ route="machine-accounts" [relativeTo]="route.parent" > + IntegrationsModule, + data: { + titleId: "integrations", + }, + }, { path: "trash", loadChildren: () => TrashModule, diff --git a/libs/common/src/enums/index.ts b/libs/common/src/enums/index.ts index 378af213e6..9ca806899a 100644 --- a/libs/common/src/enums/index.ts +++ b/libs/common/src/enums/index.ts @@ -3,6 +3,7 @@ export * from "./device-type.enum"; export * from "./event-system-user.enum"; export * from "./event-type.enum"; export * from "./http-status-code.enum"; +export * from "./integration-type.enum"; export * from "./native-messaging-version.enum"; export * from "./notification-type.enum"; export * from "./product-type.enum"; diff --git a/libs/common/src/enums/integration-type.enum.ts b/libs/common/src/enums/integration-type.enum.ts new file mode 100644 index 0000000000..acb9510697 --- /dev/null +++ b/libs/common/src/enums/integration-type.enum.ts @@ -0,0 +1,4 @@ +export enum IntegrationType { + Integration = "integration", + SDK = "sdk", +}