mirror of
https://github.com/bitwarden/browser
synced 2025-12-11 22:03:36 +00:00
[PM-18870] Convert Organization to Business Unit (#14131)
* Add setupBusinessUnit to OrganizationBillingApiService * Add setup-business-unit.component * Updated designs and cleanup work * Update existing logos for Provider Portal and Admin Console * Fix broken test
This commit is contained in:
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -32,4 +32,12 @@ export class BillingNotificationService {
|
||||
message: message,
|
||||
});
|
||||
}
|
||||
|
||||
showError(message: string, title: string = "") {
|
||||
this.toastService.showToast({
|
||||
variant: "error",
|
||||
title,
|
||||
message,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -19,6 +19,7 @@ import {
|
||||
OrganizationService,
|
||||
} from "@bitwarden/common/admin-console/abstractions/organization/organization.service.abstraction";
|
||||
import { ProviderService } from "@bitwarden/common/admin-console/abstractions/provider.service";
|
||||
import { ProviderType } from "@bitwarden/common/admin-console/enums";
|
||||
import { Organization } from "@bitwarden/common/admin-console/models/domain/organization";
|
||||
import { AccountService } from "@bitwarden/common/auth/abstractions/account.service";
|
||||
import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service";
|
||||
@@ -153,6 +154,11 @@ export class ProductSwitcherService {
|
||||
// TODO: This should be migrated to an Observable provided by the provider service and moved to the combineLatest above. See AC-2092.
|
||||
const providers = await this.providerService.getAll();
|
||||
|
||||
const providerPortalName =
|
||||
providers[0]?.providerType === ProviderType.BusinessUnit
|
||||
? "Business Unit Portal"
|
||||
: "Provider Portal";
|
||||
|
||||
const orgsMarketingRoute = this.platformUtilsService.isSelfHost()
|
||||
? {
|
||||
route: "https://bitwarden.com/products/business/",
|
||||
@@ -201,7 +207,7 @@ export class ProductSwitcherService {
|
||||
isActive: this.router.url.includes("/organizations/"),
|
||||
},
|
||||
provider: {
|
||||
name: "Provider Portal",
|
||||
name: providerPortalName,
|
||||
icon: "bwi-provider",
|
||||
appRoute: ["/providers", providers[0]?.id],
|
||||
isActive: this.router.url.includes("/providers/"),
|
||||
|
||||
@@ -10616,5 +10616,14 @@
|
||||
},
|
||||
"cannotCreateCollection": {
|
||||
"message": "Free organizations may have up to 2 collections. Upgrade to a paid plan to add more collections."
|
||||
},
|
||||
"businessUnit": {
|
||||
"message": "Business Unit"
|
||||
},
|
||||
"businessUnits": {
|
||||
"message": "Business Units"
|
||||
},
|
||||
"newBusinessUnit": {
|
||||
"message": "New business unit"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,10 +1,14 @@
|
||||
<app-layout>
|
||||
<app-side-nav variant="secondary" *ngIf="provider$ | async as provider">
|
||||
<bit-nav-logo [openIcon]="logo" route="." [label]="'providerPortal' | i18n"></bit-nav-logo>
|
||||
<bit-nav-logo
|
||||
[openIcon]="logo$ | async"
|
||||
route="."
|
||||
[label]="'providerPortal' | i18n"
|
||||
></bit-nav-logo>
|
||||
|
||||
<bit-nav-item
|
||||
icon="bwi-provider"
|
||||
[text]="'clients' | i18n"
|
||||
[text]="clientsTranslationKey$ | async | i18n"
|
||||
[route]="(isBillable | async) ? 'manage-client-organizations' : 'clients'"
|
||||
></bit-nav-item>
|
||||
<bit-nav-group
|
||||
|
||||
@@ -8,9 +8,10 @@ import { takeUntil } from "rxjs/operators";
|
||||
|
||||
import { JslibModule } from "@bitwarden/angular/jslib.module";
|
||||
import { ProviderService } from "@bitwarden/common/admin-console/abstractions/provider.service";
|
||||
import { ProviderStatusType } from "@bitwarden/common/admin-console/enums";
|
||||
import { ProviderStatusType, ProviderType } from "@bitwarden/common/admin-console/enums";
|
||||
import { Provider } from "@bitwarden/common/admin-console/models/domain/provider";
|
||||
import { IconModule } from "@bitwarden/components";
|
||||
import { Icon, IconModule } from "@bitwarden/components";
|
||||
import { BusinessUnitPortalLogo } from "@bitwarden/web-vault/app/admin-console/icons/business-unit-portal-logo.icon";
|
||||
import { ProviderPortalLogo } from "@bitwarden/web-vault/app/admin-console/icons/provider-portal-logo";
|
||||
import { WebLayoutModule } from "@bitwarden/web-vault/app/layouts/web-layout.module";
|
||||
|
||||
@@ -26,9 +27,13 @@ export class ProvidersLayoutComponent implements OnInit, OnDestroy {
|
||||
private destroy$ = new Subject<void>();
|
||||
protected provider$: Observable<Provider>;
|
||||
|
||||
protected logo$: Observable<Icon>;
|
||||
|
||||
protected isBillable: Observable<boolean>;
|
||||
protected canAccessBilling$: Observable<boolean>;
|
||||
|
||||
protected clientsTranslationKey$: Observable<string>;
|
||||
|
||||
constructor(
|
||||
private route: ActivatedRoute,
|
||||
private providerService: ProviderService,
|
||||
@@ -42,16 +47,28 @@ export class ProvidersLayoutComponent implements OnInit, OnDestroy {
|
||||
takeUntil(this.destroy$),
|
||||
);
|
||||
|
||||
this.logo$ = this.provider$.pipe(
|
||||
map((provider) =>
|
||||
provider.providerType === ProviderType.BusinessUnit
|
||||
? BusinessUnitPortalLogo
|
||||
: ProviderPortalLogo,
|
||||
),
|
||||
);
|
||||
|
||||
this.isBillable = this.provider$.pipe(
|
||||
map((provider) => provider?.providerStatus === ProviderStatusType.Billable),
|
||||
takeUntil(this.destroy$),
|
||||
);
|
||||
|
||||
this.canAccessBilling$ = combineLatest([this.isBillable, this.provider$]).pipe(
|
||||
map(
|
||||
([hasConsolidatedBilling, provider]) => hasConsolidatedBilling && provider.isProviderAdmin,
|
||||
),
|
||||
takeUntil(this.destroy$),
|
||||
);
|
||||
|
||||
this.clientsTranslationKey$ = this.provider$.pipe(
|
||||
map((provider) =>
|
||||
provider.providerType === ProviderType.BusinessUnit ? "businessUnits" : "clients",
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -13,6 +13,7 @@ import {
|
||||
hasConsolidatedBilling,
|
||||
ProviderBillingHistoryComponent,
|
||||
} from "../../billing/providers";
|
||||
import { SetupBusinessUnitComponent } from "../../billing/providers/setup/setup-business-unit.component";
|
||||
|
||||
import { ClientsComponent } from "./clients/clients.component";
|
||||
import { CreateOrganizationComponent } from "./clients/create-organization.component";
|
||||
@@ -49,6 +50,11 @@ const routes: Routes = [
|
||||
component: SetupProviderComponent,
|
||||
data: { titleId: "setupProvider" },
|
||||
},
|
||||
{
|
||||
path: "setup-business-unit",
|
||||
component: SetupBusinessUnitComponent,
|
||||
data: { titleId: "setupProvider" },
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
|
||||
@@ -19,6 +19,7 @@ import {
|
||||
ProviderSubscriptionStatusComponent,
|
||||
} from "../../billing/providers";
|
||||
import { AddExistingOrganizationDialogComponent } from "../../billing/providers/clients/add-existing-organization-dialog.component";
|
||||
import { SetupBusinessUnitComponent } from "../../billing/providers/setup/setup-business-unit.component";
|
||||
|
||||
import { AddOrganizationComponent } from "./clients/add-organization.component";
|
||||
import { CreateOrganizationComponent } from "./clients/create-organization.component";
|
||||
@@ -75,6 +76,7 @@ import { VerifyRecoverDeleteProviderComponent } from "./verify-recover-delete-pr
|
||||
ProviderSubscriptionStatusComponent,
|
||||
ProvidersComponent,
|
||||
VerifyRecoverDeleteProviderComponent,
|
||||
SetupBusinessUnitComponent,
|
||||
],
|
||||
providers: [WebProviderService],
|
||||
})
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
<app-header>
|
||||
<app-header [title]="pageTitle">
|
||||
<bit-search [placeholder]="'search' | i18n" [formControl]="searchControl"></bit-search>
|
||||
<ng-container *ngIf="addExistingOrgsFromProviderPortal$ | async; else addExistingOrgsDisabled">
|
||||
<button
|
||||
@@ -14,7 +14,7 @@
|
||||
<bit-menu #clientMenu>
|
||||
<button type="button" bitMenuItem (click)="createClient()">
|
||||
<i aria-hidden="true" class="bwi bwi-business"></i>
|
||||
{{ "newClient" | i18n }}
|
||||
{{ newClientButtonLabel }}
|
||||
</button>
|
||||
<button type="button" bitMenuItem (click)="addExistingOrganization()">
|
||||
<i aria-hidden="true" class="bwi bwi-filter"></i>
|
||||
@@ -48,7 +48,7 @@
|
||||
<ng-container *ngIf="!loading">
|
||||
<bit-table-scroll [dataSource]="dataSource" [rowSize]="53" class="tw-overflow-hidden">
|
||||
<ng-container header>
|
||||
<th colspan="2" bitCell bitSortable="organizationName" default>{{ "client" | i18n }}</th>
|
||||
<th colspan="2" bitCell bitSortable="organizationName" default>{{ clientColumnHeader }}</th>
|
||||
<th bitCell bitSortable="seats">{{ "assigned" | i18n }}</th>
|
||||
<th bitCell bitSortable="occupiedSeats">{{ "used" | i18n }}</th>
|
||||
<th bitCell bitSortable="remainingSeats">{{ "remaining" | i18n }}</th>
|
||||
|
||||
@@ -6,7 +6,11 @@ import { firstValueFrom, from, lastValueFrom, map } from "rxjs";
|
||||
import { debounceTime, first, switchMap } from "rxjs/operators";
|
||||
|
||||
import { ProviderService } from "@bitwarden/common/admin-console/abstractions/provider.service";
|
||||
import { ProviderStatusType, ProviderUserType } from "@bitwarden/common/admin-console/enums";
|
||||
import {
|
||||
ProviderStatusType,
|
||||
ProviderType,
|
||||
ProviderUserType,
|
||||
} from "@bitwarden/common/admin-console/enums";
|
||||
import { Provider } from "@bitwarden/common/admin-console/models/domain/provider";
|
||||
import { ProviderOrganizationOrganizationDetailsResponse } from "@bitwarden/common/admin-console/models/response/provider/provider-organization.response";
|
||||
import { BillingApiServiceAbstraction } from "@bitwarden/common/billing/abstractions";
|
||||
@@ -73,6 +77,10 @@ export class ManageClientsComponent {
|
||||
FeatureFlag.PM15179_AddExistingOrgsFromProviderPortal,
|
||||
);
|
||||
|
||||
pageTitle = this.i18nService.t("clients");
|
||||
clientColumnHeader = this.i18nService.t("client");
|
||||
newClientButtonLabel = this.i18nService.t("newClient");
|
||||
|
||||
constructor(
|
||||
private billingApiService: BillingApiServiceAbstraction,
|
||||
private providerService: ProviderService,
|
||||
@@ -124,6 +132,11 @@ export class ManageClientsComponent {
|
||||
async load() {
|
||||
try {
|
||||
this.provider = await firstValueFrom(this.providerService.get$(this.providerId));
|
||||
if (this.provider?.providerType === ProviderType.BusinessUnit) {
|
||||
this.pageTitle = this.i18nService.t("businessUnits");
|
||||
this.clientColumnHeader = this.i18nService.t("businessUnit");
|
||||
this.newClientButtonLabel = this.i18nService.t("newBusinessUnit");
|
||||
}
|
||||
this.isProviderAdmin = this.provider?.type === ProviderUserType.ProviderAdmin;
|
||||
this.dataSource.data = (
|
||||
await this.billingApiService.getProviderClientOrganizations(this.providerId)
|
||||
|
||||
@@ -0,0 +1,25 @@
|
||||
<div class="tw-mt-12 tw-flex tw-justify-center" *ngIf="loading">
|
||||
<div>
|
||||
<bit-icon class="tw-w-72 tw-block tw-mb-4" [icon]="bitwardenLogo"></bit-icon>
|
||||
<p class="tw-text-center">
|
||||
<i
|
||||
class="bwi bwi-spinner bwi-spin bwi-2x tw-text-muted"
|
||||
title="{{ 'loading' | i18n }}"
|
||||
aria-hidden="true"
|
||||
></i>
|
||||
<span class="tw-sr-only">{{ "loading" | i18n }}</span>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="tw-flex tw-flex-row tw-justify-center tw-mt-12" *ngIf="!loading && !authed">
|
||||
<div class="tw-w-[400px] tw-mt-5">
|
||||
<h2 class="tw-flex tw-justify-center tw-mb-4">{{ "setupProvider" | i18n }}</h2>
|
||||
<bit-card>
|
||||
<p>{{ "setupProviderLoginDesc" | i18n }}</p>
|
||||
<hr />
|
||||
<button bitButton type="button" [block]="true" (click)="login()" buttonType="primary">
|
||||
{{ "logIn" | i18n }}
|
||||
</button>
|
||||
</bit-card>
|
||||
</div>
|
||||
</div>
|
||||
@@ -0,0 +1,118 @@
|
||||
import { Component } from "@angular/core";
|
||||
import { ActivatedRoute, Params, Router } from "@angular/router";
|
||||
import { firstValueFrom } from "rxjs";
|
||||
import { filter, map, switchMap } from "rxjs/operators";
|
||||
|
||||
import { BitwardenLogo } from "@bitwarden/auth/angular";
|
||||
import { AuthService } from "@bitwarden/common/auth/abstractions/auth.service";
|
||||
import { OrganizationBillingApiServiceAbstraction } from "@bitwarden/common/billing/abstractions/organizations/organization-billing-api.service.abstraction";
|
||||
import { EncryptService } from "@bitwarden/common/key-management/crypto/abstractions/encrypt.service";
|
||||
import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
|
||||
import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service";
|
||||
import { StateProvider } from "@bitwarden/common/platform/state";
|
||||
import { SyncService } from "@bitwarden/common/platform/sync";
|
||||
import { OrganizationId } from "@bitwarden/common/types/guid";
|
||||
import { ProviderKey } from "@bitwarden/common/types/key";
|
||||
import { KeyService } from "@bitwarden/key-management";
|
||||
import { BillingNotificationService } from "@bitwarden/web-vault/app/billing/services/billing-notification.service";
|
||||
import { BaseAcceptComponent } from "@bitwarden/web-vault/app/common/base.accept.component";
|
||||
|
||||
@Component({
|
||||
templateUrl: "./setup-business-unit.component.html",
|
||||
})
|
||||
export class SetupBusinessUnitComponent extends BaseAcceptComponent {
|
||||
protected bitwardenLogo = BitwardenLogo;
|
||||
|
||||
failedMessage = "emergencyInviteAcceptFailed";
|
||||
failedShortMessage = "emergencyInviteAcceptFailedShort";
|
||||
requiredParameters = ["organizationId", "email", "token"];
|
||||
|
||||
constructor(
|
||||
activatedRoute: ActivatedRoute,
|
||||
authService: AuthService,
|
||||
private billingNotificationService: BillingNotificationService,
|
||||
private encryptService: EncryptService,
|
||||
i18nService: I18nService,
|
||||
private keyService: KeyService,
|
||||
private organizationBillingApiService: OrganizationBillingApiServiceAbstraction,
|
||||
platformUtilsService: PlatformUtilsService,
|
||||
router: Router,
|
||||
private stateProvider: StateProvider,
|
||||
private syncService: SyncService,
|
||||
) {
|
||||
super(router, platformUtilsService, i18nService, activatedRoute, authService);
|
||||
}
|
||||
|
||||
async authedHandler(queryParams: Params) {
|
||||
await this.process(queryParams);
|
||||
}
|
||||
|
||||
async unauthedHandler(_: Params) {}
|
||||
|
||||
async login() {
|
||||
await this.router.navigate(["/login"], { queryParams: { email: this.email } });
|
||||
}
|
||||
|
||||
process = async (queryParams: Params): Promise<boolean> => {
|
||||
const fail = async () => {
|
||||
this.billingNotificationService.showError(this.i18nService.t(this.failedMessage));
|
||||
return await this.router.navigate(["/"]);
|
||||
};
|
||||
|
||||
const organizationId = queryParams.organizationId as string;
|
||||
const token = queryParams.token as string;
|
||||
|
||||
if (!organizationId || !token) {
|
||||
return await fail();
|
||||
}
|
||||
|
||||
const activeUserId$ = this.stateProvider.activeUserId$.pipe(
|
||||
filter((userId): userId is NonNullable<typeof userId> => userId != null),
|
||||
);
|
||||
|
||||
const organizationKey$ = activeUserId$.pipe(
|
||||
switchMap((userId) => this.keyService.orgKeys$(userId)),
|
||||
filter(
|
||||
(organizationKeysById): organizationKeysById is NonNullable<typeof organizationKeysById> =>
|
||||
organizationKeysById != null && organizationId in organizationKeysById,
|
||||
),
|
||||
map((organizationKeysById) => organizationKeysById[organizationId as OrganizationId]),
|
||||
);
|
||||
|
||||
const [{ encryptedString: encryptedProviderKey }, providerKey] =
|
||||
await this.keyService.makeOrgKey<ProviderKey>();
|
||||
|
||||
const organizationKey = await firstValueFrom(organizationKey$);
|
||||
|
||||
const { encryptedString: encryptedOrganizationKey } = await this.encryptService.encrypt(
|
||||
organizationKey.key,
|
||||
providerKey,
|
||||
);
|
||||
|
||||
if (!encryptedProviderKey || !encryptedOrganizationKey) {
|
||||
return await fail();
|
||||
}
|
||||
|
||||
const userId = await firstValueFrom(activeUserId$);
|
||||
|
||||
const request = {
|
||||
userId,
|
||||
token,
|
||||
providerKey: encryptedProviderKey,
|
||||
organizationKey: encryptedOrganizationKey,
|
||||
};
|
||||
|
||||
try {
|
||||
const providerId = await this.organizationBillingApiService.setupBusinessUnit(
|
||||
organizationId,
|
||||
request,
|
||||
);
|
||||
await this.syncService.fullSync(true);
|
||||
this.billingNotificationService.showSuccess(this.i18nService.t("providerSetup"));
|
||||
return await this.router.navigate(["/providers", providerId]);
|
||||
} catch (error) {
|
||||
this.billingNotificationService.handleError(error);
|
||||
return false;
|
||||
}
|
||||
};
|
||||
}
|
||||
@@ -39,8 +39,8 @@ export class ProviderSubscriptionStatusComponent {
|
||||
switch (this.subscription.providerType) {
|
||||
case ProviderType.Msp:
|
||||
return "managedServiceProvider";
|
||||
case ProviderType.MultiOrganizationEnterprise:
|
||||
return "multiOrganizationEnterprise";
|
||||
case ProviderType.BusinessUnit:
|
||||
return "businessUnit";
|
||||
}
|
||||
}
|
||||
|
||||
@@ -72,6 +72,18 @@ export class ProviderSubscriptionStatusComponent {
|
||||
},
|
||||
};
|
||||
}
|
||||
case "trialing": {
|
||||
return {
|
||||
status: {
|
||||
label: defaultStatusLabel,
|
||||
value: this.i18nService.t("trial"),
|
||||
},
|
||||
date: {
|
||||
label: nextChargeDateLabel,
|
||||
value: this.subscription.currentPeriodEndDate,
|
||||
},
|
||||
};
|
||||
}
|
||||
case "past_due": {
|
||||
const pastDueText = this.i18nService.t("pastDue");
|
||||
const suspensionDate = this.datePipe.transform(
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
export enum ProviderType {
|
||||
Msp = 0,
|
||||
Reseller = 1,
|
||||
MultiOrganizationEnterprise = 2,
|
||||
BusinessUnit = 2,
|
||||
}
|
||||
|
||||
@@ -1,4 +1,9 @@
|
||||
import { ProviderStatusType, ProviderUserStatusType, ProviderUserType } from "../../enums";
|
||||
import {
|
||||
ProviderStatusType,
|
||||
ProviderType,
|
||||
ProviderUserStatusType,
|
||||
ProviderUserType,
|
||||
} from "../../enums";
|
||||
import { ProfileProviderResponse } from "../response/profile-provider.response";
|
||||
|
||||
export class ProviderData {
|
||||
@@ -10,6 +15,7 @@ export class ProviderData {
|
||||
userId: string;
|
||||
useEvents: boolean;
|
||||
providerStatus: ProviderStatusType;
|
||||
providerType: ProviderType;
|
||||
|
||||
constructor(response: ProfileProviderResponse) {
|
||||
this.id = response.id;
|
||||
@@ -20,5 +26,6 @@ export class ProviderData {
|
||||
this.userId = response.userId;
|
||||
this.useEvents = response.useEvents;
|
||||
this.providerStatus = response.providerStatus;
|
||||
this.providerType = response.providerType;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -331,8 +331,7 @@ export class Organization {
|
||||
get hasBillableProvider() {
|
||||
return (
|
||||
this.hasProvider &&
|
||||
(this.providerType === ProviderType.Msp ||
|
||||
this.providerType === ProviderType.MultiOrganizationEnterprise)
|
||||
(this.providerType === ProviderType.Msp || this.providerType === ProviderType.BusinessUnit)
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -1,6 +1,11 @@
|
||||
// FIXME: Update this file to be type safe and remove this and next line
|
||||
// @ts-strict-ignore
|
||||
import { ProviderStatusType, ProviderUserStatusType, ProviderUserType } from "../../enums";
|
||||
import {
|
||||
ProviderStatusType,
|
||||
ProviderType,
|
||||
ProviderUserStatusType,
|
||||
ProviderUserType,
|
||||
} from "../../enums";
|
||||
import { ProviderData } from "../data/provider.data";
|
||||
|
||||
export class Provider {
|
||||
@@ -12,6 +17,7 @@ export class Provider {
|
||||
userId: string;
|
||||
useEvents: boolean;
|
||||
providerStatus: ProviderStatusType;
|
||||
providerType: ProviderType;
|
||||
|
||||
constructor(obj?: ProviderData) {
|
||||
if (obj == null) {
|
||||
@@ -26,6 +32,7 @@ export class Provider {
|
||||
this.userId = obj.userId;
|
||||
this.useEvents = obj.useEvents;
|
||||
this.providerStatus = obj.providerStatus;
|
||||
this.providerType = obj.providerType;
|
||||
}
|
||||
|
||||
get canAccess() {
|
||||
|
||||
@@ -1,5 +1,10 @@
|
||||
import { BaseResponse } from "../../../models/response/base.response";
|
||||
import { ProviderStatusType, ProviderUserStatusType, ProviderUserType } from "../../enums";
|
||||
import {
|
||||
ProviderStatusType,
|
||||
ProviderType,
|
||||
ProviderUserStatusType,
|
||||
ProviderUserType,
|
||||
} from "../../enums";
|
||||
import { PermissionsApi } from "../api/permissions.api";
|
||||
|
||||
export class ProfileProviderResponse extends BaseResponse {
|
||||
@@ -13,6 +18,7 @@ export class ProfileProviderResponse extends BaseResponse {
|
||||
userId: string;
|
||||
useEvents: boolean;
|
||||
providerStatus: ProviderStatusType;
|
||||
providerType: ProviderType;
|
||||
|
||||
constructor(response: any) {
|
||||
super(response);
|
||||
@@ -26,5 +32,6 @@ export class ProfileProviderResponse extends BaseResponse {
|
||||
this.userId = this.getResponseProperty("UserId");
|
||||
this.useEvents = this.getResponseProperty("UseEvents");
|
||||
this.providerStatus = this.getResponseProperty("ProviderStatus");
|
||||
this.providerType = this.getResponseProperty("ProviderType");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,7 +4,12 @@ import { FakeAccountService, FakeStateProvider, mockAccountServiceWith } from ".
|
||||
import { FakeActiveUserState, FakeSingleUserState } from "../../../spec/fake-state";
|
||||
import { Utils } from "../../platform/misc/utils";
|
||||
import { UserId } from "../../types/guid";
|
||||
import { ProviderStatusType, ProviderUserStatusType, ProviderUserType } from "../enums";
|
||||
import {
|
||||
ProviderStatusType,
|
||||
ProviderType,
|
||||
ProviderUserStatusType,
|
||||
ProviderUserType,
|
||||
} from "../enums";
|
||||
import { ProviderData } from "../models/data/provider.data";
|
||||
import { Provider } from "../models/domain/provider";
|
||||
|
||||
@@ -67,6 +72,7 @@ describe("PROVIDERS key definition", () => {
|
||||
userId: "string",
|
||||
useEvents: true,
|
||||
providerStatus: ProviderStatusType.Pending,
|
||||
providerType: ProviderType.Msp,
|
||||
},
|
||||
};
|
||||
const result = sut.deserializer(JSON.parse(JSON.stringify(expectedResult)));
|
||||
|
||||
@@ -1,19 +1,27 @@
|
||||
// FIXME: Update this file to be type safe and remove this and next line
|
||||
// @ts-strict-ignore
|
||||
import {
|
||||
BillingInvoiceResponse,
|
||||
BillingTransactionResponse,
|
||||
} from "../../models/response/billing.response";
|
||||
|
||||
export class OrganizationBillingApiServiceAbstraction {
|
||||
getBillingInvoices: (
|
||||
export abstract class OrganizationBillingApiServiceAbstraction {
|
||||
abstract getBillingInvoices: (
|
||||
id: string,
|
||||
status?: string,
|
||||
startAfter?: string,
|
||||
) => Promise<BillingInvoiceResponse[]>;
|
||||
|
||||
getBillingTransactions: (
|
||||
abstract getBillingTransactions: (
|
||||
id: string,
|
||||
startAfter?: string,
|
||||
) => Promise<BillingTransactionResponse[]>;
|
||||
|
||||
abstract setupBusinessUnit: (
|
||||
id: string,
|
||||
request: {
|
||||
userId: string;
|
||||
token: string;
|
||||
providerKey: string;
|
||||
organizationKey: string;
|
||||
},
|
||||
) => Promise<string>;
|
||||
}
|
||||
|
||||
@@ -49,4 +49,24 @@ export class OrganizationBillingApiService implements OrganizationBillingApiServ
|
||||
);
|
||||
return r?.map((i: any) => new BillingTransactionResponse(i)) || [];
|
||||
}
|
||||
|
||||
async setupBusinessUnit(
|
||||
id: string,
|
||||
request: {
|
||||
userId: string;
|
||||
token: string;
|
||||
providerKey: string;
|
||||
organizationKey: string;
|
||||
},
|
||||
): Promise<string> {
|
||||
const response = await this.apiService.send(
|
||||
"POST",
|
||||
`/organizations/${id}/billing/setup-business-unit`,
|
||||
request,
|
||||
true,
|
||||
true,
|
||||
);
|
||||
|
||||
return response as string;
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user