mirror of https://github.com/Squidex/squidex.git
49 changed files with 852 additions and 99 deletions
@ -0,0 +1,18 @@ |
|||||
|
// ==========================================================================
|
||||
|
// AppPlanChanged.cs
|
||||
|
// Squidex Headless CMS
|
||||
|
// ==========================================================================
|
||||
|
// Copyright (c) Squidex Group
|
||||
|
// All rights reserved.
|
||||
|
// ==========================================================================
|
||||
|
|
||||
|
using Squidex.Infrastructure; |
||||
|
|
||||
|
namespace Squidex.Events.Apps |
||||
|
{ |
||||
|
[TypeName("AppPlanChanged")] |
||||
|
public sealed class AppPlanChanged : AppEvent |
||||
|
{ |
||||
|
public string PlanId { get; set; } |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,26 @@ |
|||||
|
// ==========================================================================
|
||||
|
// IAppPlanBillingManager.cs
|
||||
|
// Squidex Headless CMS
|
||||
|
// ==========================================================================
|
||||
|
// Copyright (c) Squidex Group
|
||||
|
// All rights reserved.
|
||||
|
// ==========================================================================
|
||||
|
|
||||
|
using System; |
||||
|
using System.Threading.Tasks; |
||||
|
|
||||
|
namespace Squidex.Read.Apps.Services |
||||
|
{ |
||||
|
public interface IAppPlanBillingManager |
||||
|
{ |
||||
|
bool HasPortal { get; } |
||||
|
|
||||
|
string FreePlanId { get; } |
||||
|
|
||||
|
Task ChangePlanAsync(string userId, Guid appId, string appName, string planId); |
||||
|
|
||||
|
Task<bool> HasPaymentOptionsAsync(string userId); |
||||
|
|
||||
|
Task<string> GetPortalLinkAsync(string userId); |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,42 @@ |
|||||
|
// ==========================================================================
|
||||
|
// NoopAppPlanBillingManager.cs
|
||||
|
// Squidex Headless CMS
|
||||
|
// ==========================================================================
|
||||
|
// Copyright (c) Squidex Group
|
||||
|
// All rights reserved.
|
||||
|
// ==========================================================================
|
||||
|
|
||||
|
using System; |
||||
|
using System.Threading.Tasks; |
||||
|
using Squidex.Infrastructure.Tasks; |
||||
|
|
||||
|
namespace Squidex.Read.Apps.Services.Implementations |
||||
|
{ |
||||
|
public sealed class NoopAppPlanBillingManager : IAppPlanBillingManager |
||||
|
{ |
||||
|
public bool HasPortal |
||||
|
{ |
||||
|
get { return false; } |
||||
|
} |
||||
|
|
||||
|
public string FreePlanId |
||||
|
{ |
||||
|
get { return "free"; } |
||||
|
} |
||||
|
|
||||
|
public Task ChangePlanAsync(string userId, Guid appId, string appName, string planId) |
||||
|
{ |
||||
|
return TaskHelper.Done; |
||||
|
} |
||||
|
|
||||
|
public Task<bool> HasPaymentOptionsAsync(string userId) |
||||
|
{ |
||||
|
return TaskHelper.True; |
||||
|
} |
||||
|
|
||||
|
public Task<string> GetPortalLinkAsync(string userId) |
||||
|
{ |
||||
|
return Task.FromResult(string.Empty); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,15 @@ |
|||||
|
// ==========================================================================
|
||||
|
// ChangePlanCommand.cs
|
||||
|
// Squidex Headless CMS
|
||||
|
// ==========================================================================
|
||||
|
// Copyright (c) Squidex Group
|
||||
|
// All rights reserved.
|
||||
|
// ==========================================================================
|
||||
|
|
||||
|
namespace Squidex.Write.Apps.Commands |
||||
|
{ |
||||
|
public sealed class ChangePlan : AppAggregateCommand |
||||
|
{ |
||||
|
public string PlanId { get; set; } |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,95 @@ |
|||||
|
// ==========================================================================
|
||||
|
// AppPlansController.cs
|
||||
|
// Squidex Headless CMS
|
||||
|
// ==========================================================================
|
||||
|
// Copyright (c) Squidex Group
|
||||
|
// All rights reserved.
|
||||
|
// ==========================================================================
|
||||
|
|
||||
|
using System.Linq; |
||||
|
using System.Threading.Tasks; |
||||
|
using Microsoft.AspNetCore.Mvc; |
||||
|
using Microsoft.Extensions.Primitives; |
||||
|
using NSwag.Annotations; |
||||
|
using Squidex.Controllers.Api.Plans.Models; |
||||
|
using Squidex.Infrastructure.CQRS.Commands; |
||||
|
using Squidex.Infrastructure.Reflection; |
||||
|
using Squidex.Infrastructure.Security; |
||||
|
using Squidex.Pipeline; |
||||
|
using Squidex.Read.Apps.Services; |
||||
|
using Squidex.Write.Apps.Commands; |
||||
|
|
||||
|
namespace Squidex.Controllers.Api.Plans |
||||
|
{ |
||||
|
/// <summary>
|
||||
|
/// Manages and configures plans.
|
||||
|
/// </summary>
|
||||
|
[ApiExceptionFilter] |
||||
|
[AppApi] |
||||
|
[SwaggerTag("Plans")] |
||||
|
public class AppPlansController : ControllerBase |
||||
|
{ |
||||
|
private readonly IAppPlansProvider appPlansProvider; |
||||
|
private readonly IAppPlanBillingManager appPlansBillingManager; |
||||
|
|
||||
|
public AppPlansController(ICommandBus commandBus, IAppPlansProvider appPlansProvider, IAppPlanBillingManager appPlansBillingManager) |
||||
|
: base(commandBus) |
||||
|
{ |
||||
|
this.appPlansProvider = appPlansProvider; |
||||
|
this.appPlansBillingManager = appPlansBillingManager; |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Get app plan information.
|
||||
|
/// </summary>
|
||||
|
/// <param name="app">The name of the app.</param>
|
||||
|
/// <returns>
|
||||
|
/// 200 => App plan information returned.
|
||||
|
/// 404 => App not found.
|
||||
|
/// </returns>
|
||||
|
[MustBeAppOwner] |
||||
|
[HttpGet] |
||||
|
[Route("apps/{app}/plans/")] |
||||
|
[ProducesResponseType(typeof(AppPlansDto), 200)] |
||||
|
[ApiCosts(0.5)] |
||||
|
public async Task<IActionResult> GetPlans(string app) |
||||
|
{ |
||||
|
var userId = User.FindFirst(OpenIdClaims.Subject).Value; |
||||
|
|
||||
|
var response = new AppPlansDto |
||||
|
{ |
||||
|
Plans = appPlansProvider.GetAvailablePlans().Select(x => SimpleMapper.Map(x, new PlanDto())).ToList(), |
||||
|
PlanOwner = App.PlanOwner, |
||||
|
HasPortal = appPlansBillingManager.HasPortal, |
||||
|
HasConfigured = await appPlansBillingManager.HasPaymentOptionsAsync(userId), |
||||
|
CurrentPlanId = !string.IsNullOrWhiteSpace(App.PlanId) ? App.PlanId : appPlansBillingManager.FreePlanId |
||||
|
}; |
||||
|
|
||||
|
Response.Headers["ETag"] = new StringValues(App.Version.ToString()); |
||||
|
|
||||
|
return Ok(response); |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Change the app plan.
|
||||
|
/// </summary>
|
||||
|
/// <param name="app">The name of the app.</param>
|
||||
|
/// <param name="request">Plan object that needs to be changed.</param>
|
||||
|
/// <returns>
|
||||
|
/// 204 => Plan changed.
|
||||
|
/// 400 => Plan not owned by user.
|
||||
|
/// 404 => App not found.
|
||||
|
/// </returns>
|
||||
|
[MustBeAppOwner] |
||||
|
[HttpPut] |
||||
|
[Route("apps/{app}/plan/")] |
||||
|
[ProducesResponseType(typeof(ErrorDto), 400)] |
||||
|
[ApiCosts(0.5)] |
||||
|
public async Task<IActionResult> ChangePlanAsync(string app, [FromBody] ChangePlanDto request) |
||||
|
{ |
||||
|
await CommandBus.PublishAsync(SimpleMapper.Map(request, new ChangePlan())); |
||||
|
|
||||
|
return NoContent(); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,40 @@ |
|||||
|
// ==========================================================================
|
||||
|
// AppPlansDto.cs
|
||||
|
// Squidex Headless CMS
|
||||
|
// ==========================================================================
|
||||
|
// Copyright (c) Squidex Group
|
||||
|
// All rights reserved.
|
||||
|
// ==========================================================================
|
||||
|
|
||||
|
using System.Collections.Generic; |
||||
|
|
||||
|
namespace Squidex.Controllers.Api.Plans.Models |
||||
|
{ |
||||
|
public class AppPlansDto |
||||
|
{ |
||||
|
/// <summary>
|
||||
|
/// The available plans.
|
||||
|
/// </summary>
|
||||
|
public List<PlanDto> Plans { get; set; } |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// The current plan id.
|
||||
|
/// </summary>
|
||||
|
public string CurrentPlanId { get; set; } |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// The plan owner.
|
||||
|
/// </summary>
|
||||
|
public string PlanOwner { get; set; } |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Indicates if there is a billing portal.
|
||||
|
/// </summary>
|
||||
|
public bool HasPortal { get; set; } |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Indicates if the user has payment options entered so that the plan can be changed.
|
||||
|
/// </summary>
|
||||
|
public bool HasConfigured { get; set; } |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,21 @@ |
|||||
|
// ==========================================================================
|
||||
|
// ChangePlanDto.cs
|
||||
|
// Squidex Headless CMS
|
||||
|
// ==========================================================================
|
||||
|
// Copyright (c) Squidex Group
|
||||
|
// All rights reserved.
|
||||
|
// ==========================================================================
|
||||
|
|
||||
|
using System.ComponentModel.DataAnnotations; |
||||
|
|
||||
|
namespace Squidex.Controllers.Api.Plans.Models |
||||
|
{ |
||||
|
public class ChangePlanDto |
||||
|
{ |
||||
|
/// <summary>
|
||||
|
/// The new plan id.
|
||||
|
/// </summary>
|
||||
|
[Required] |
||||
|
public string PlanId { get; set; } |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,43 @@ |
|||||
|
// ==========================================================================
|
||||
|
// PlanDto.cs
|
||||
|
// Squidex Headless CMS
|
||||
|
// ==========================================================================
|
||||
|
// Copyright (c) Squidex Group
|
||||
|
// All rights reserved.
|
||||
|
// ==========================================================================
|
||||
|
|
||||
|
namespace Squidex.Controllers.Api.Plans.Models |
||||
|
{ |
||||
|
public class PlanDto |
||||
|
{ |
||||
|
/// <summary>
|
||||
|
/// The id of the plan.
|
||||
|
/// </summary>
|
||||
|
public string Id { get; set; } |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// The name of the plan.
|
||||
|
/// </summary>
|
||||
|
public string Name { get; set; } |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// The monthly costs of the plan.
|
||||
|
/// </summary>
|
||||
|
public string Costs { get; set; } |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// The maximum number of API calls.
|
||||
|
/// </summary>
|
||||
|
public long MaxApiCalls { get; set; } |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// The maximum allowed asset size.
|
||||
|
/// </summary>
|
||||
|
public long MaxAssetSize { get; set; } |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// The maximum number of contributors.
|
||||
|
/// </summary>
|
||||
|
public int MaxContributors { get; set; } |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,40 @@ |
|||||
|
// ==========================================================================
|
||||
|
// PortalController.cs
|
||||
|
// Squidex Headless CMS
|
||||
|
// ==========================================================================
|
||||
|
// Copyright (c) Squidex Group
|
||||
|
// All rights reserved.
|
||||
|
// ==========================================================================
|
||||
|
|
||||
|
using System.Threading.Tasks; |
||||
|
using Microsoft.AspNetCore.Authorization; |
||||
|
using Microsoft.AspNetCore.Mvc; |
||||
|
using NSwag.Annotations; |
||||
|
using Squidex.Infrastructure.Security; |
||||
|
using Squidex.Read.Apps.Services; |
||||
|
|
||||
|
namespace Squidex.Controllers.UI.Profile |
||||
|
{ |
||||
|
[Authorize] |
||||
|
[SwaggerIgnore] |
||||
|
public class PortalController : Controller |
||||
|
{ |
||||
|
private readonly IAppPlanBillingManager appPlansBillingManager; |
||||
|
|
||||
|
public PortalController(IAppPlanBillingManager appPlansBillingManager) |
||||
|
{ |
||||
|
this.appPlansBillingManager = appPlansBillingManager; |
||||
|
} |
||||
|
|
||||
|
[HttpGet] |
||||
|
[Route("/account/portal")] |
||||
|
public async Task<IActionResult> Portal() |
||||
|
{ |
||||
|
var userId = User.FindFirst(OpenIdClaims.Subject).Value; |
||||
|
|
||||
|
var redirectUrl = await appPlansBillingManager.GetPortalLinkAsync(userId); |
||||
|
|
||||
|
return Redirect(redirectUrl); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,71 @@ |
|||||
|
<sqx-title message="{app} | Plans | Settings" parameter1="app" value1="{{appName() | async}}"></sqx-title> |
||||
|
|
||||
|
<sqx-panel panelWidth="60rem"> |
||||
|
<div class="panel-header"> |
||||
|
<div class="panel-title-row"> |
||||
|
<div class="float-right"> |
||||
|
<button class="btn btn-link btn-decent" (click)="load(true)" title="Refresh Plans (CTRL + SHIFT + R)"> |
||||
|
<i class="icon-reset"></i> Refresh |
||||
|
</button> |
||||
|
|
||||
|
<sqx-shortcut keys="ctrl+shift+r" (trigger)="load(true)"></sqx-shortcut> |
||||
|
</div> |
||||
|
|
||||
|
<h3 class="panel-title">Update Plan</h3> |
||||
|
</div> |
||||
|
|
||||
|
<a class="panel-close" sqxParentLink> |
||||
|
<i class="icon-close"></i> |
||||
|
</a> |
||||
|
</div> |
||||
|
|
||||
|
<div class="panel-main"> |
||||
|
<div class="panel-content"> |
||||
|
<div *ngIf="plans"> |
||||
|
<div class="panel-alert panel-alert-danger" *ngIf="!plans.hasConfigured || !planOwned"> |
||||
|
<div *ngIf="!plans.hasConfigured"> |
||||
|
You have not configured your account yet. Go to <a target="_blank" href="{{portalUrl}}">Billing Portal</a> to add payment options. |
||||
|
</div> |
||||
|
<div *ngIf="!planOwned"> |
||||
|
You have not created the subscription. Therefore you cannot change the plan. |
||||
|
</div> |
||||
|
</div> |
||||
|
|
||||
|
<div class="clearfix"> |
||||
|
<div class="card plan float-left" *ngFor="let plan of plans.plans"> |
||||
|
<div class="card-block plan-header text-center"> |
||||
|
<h4 class="card-title">{{plan.name}}</h4> |
||||
|
<h5 class="plan-price">{{plan.costs}}</h5> |
||||
|
|
||||
|
<small class="text-muted">Per Month</small> |
||||
|
</div> |
||||
|
<div class="card-block"> |
||||
|
<div class="plan-fact"> |
||||
|
{{formatCalls(plan.maxApiCalls)}} API Calls |
||||
|
</div> |
||||
|
<div class="plan-fact"> |
||||
|
{{formatSize(plan.maxAssetSize)}} Storage |
||||
|
</div> |
||||
|
<div class="plan-fact"> |
||||
|
{{plan.maxContributors}} Contributors |
||||
|
</div> |
||||
|
</div> |
||||
|
<div class="card-block"> |
||||
|
<button *ngIf="plan.id === plans.currentPlanId" class="btn btn-block btn-link btn-success plan-selected"> |
||||
|
✓ Selected |
||||
|
</button> |
||||
|
|
||||
|
<button *ngIf="plan.id !== plans.currentPlanId" class="btn btn-block btn-success" [disabled]="isDisabled || !plans.hasConfigured || !planOwned" (click)="changePlan(plan.id)"> |
||||
|
Change |
||||
|
</button> |
||||
|
</div> |
||||
|
</div> |
||||
|
</div> |
||||
|
|
||||
|
<div *ngIf="plans.hasPortal" class="billing-portal-link"> |
||||
|
Go to <a target="_blank" href="{{portalUrl}}">Billing Portal</a> for payment history and subscription overview. |
||||
|
</div> |
||||
|
</div> |
||||
|
</div> |
||||
|
</div> |
||||
|
</sqx-panel> |
||||
@ -0,0 +1,35 @@ |
|||||
|
@import '_vars'; |
||||
|
@import '_mixins'; |
||||
|
|
||||
|
.panel-content { |
||||
|
padding-left: 1rem; |
||||
|
padding-right: 1rem; |
||||
|
} |
||||
|
|
||||
|
.plan { |
||||
|
& { |
||||
|
min-width: 13rem; |
||||
|
max-width: 20rem; |
||||
|
margin: .5rem; |
||||
|
} |
||||
|
|
||||
|
&-header { |
||||
|
border-bottom: 1px solid $color-border; |
||||
|
} |
||||
|
|
||||
|
&-price { |
||||
|
color: $color-theme-blue; |
||||
|
} |
||||
|
|
||||
|
&-selected { |
||||
|
pointer-events: none; |
||||
|
} |
||||
|
|
||||
|
&-fact { |
||||
|
line-height: 2rem; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
.billing-portal-link { |
||||
|
padding: 2rem .5rem 0; |
||||
|
} |
||||
@ -0,0 +1,108 @@ |
|||||
|
/* |
||||
|
* Squidex Headless CMS |
||||
|
* |
||||
|
* @license |
||||
|
* Copyright (c) Sebastian Stehle. All rights reserved |
||||
|
*/ |
||||
|
|
||||
|
import { Component, OnInit } from '@angular/core'; |
||||
|
|
||||
|
import { |
||||
|
ApiUrlConfig, |
||||
|
AppComponentBase, |
||||
|
AppPlansDto, |
||||
|
AppsStoreService, |
||||
|
AuthService, |
||||
|
ChangePlanDto, |
||||
|
FileHelper, |
||||
|
NotificationService, |
||||
|
PlansService, |
||||
|
Version |
||||
|
} from 'shared'; |
||||
|
|
||||
|
@Component({ |
||||
|
selector: 'sqx-plans-page', |
||||
|
styleUrls: ['./plans-page.component.scss'], |
||||
|
templateUrl: './plans-page.component.html' |
||||
|
}) |
||||
|
export class PlansPageComponent extends AppComponentBase implements OnInit { |
||||
|
private version = new Version(); |
||||
|
|
||||
|
public portalUrl = this.apiUrl.buildUrl('/identity-server/account/portal'); |
||||
|
|
||||
|
public plans: AppPlansDto; |
||||
|
public planOwned = false; |
||||
|
|
||||
|
public isDisabled = false; |
||||
|
|
||||
|
constructor(apps: AppsStoreService, notifications: NotificationService, |
||||
|
private readonly authService: AuthService, |
||||
|
private readonly plansService: PlansService, |
||||
|
private readonly apiUrl: ApiUrlConfig |
||||
|
) { |
||||
|
super(notifications, apps); |
||||
|
} |
||||
|
|
||||
|
public ngOnInit() { |
||||
|
this.load(); |
||||
|
} |
||||
|
|
||||
|
public load(showInfo = false) { |
||||
|
this.appNameOnce() |
||||
|
.switchMap(app => this.plansService.getPlans(app, this.version).retry(2)) |
||||
|
.subscribe(dto => { |
||||
|
this.plans = dto; |
||||
|
|
||||
|
this.planOwned = !dto.planOwner || (dto.planOwner === this.authService.user!.id); |
||||
|
|
||||
|
if (showInfo) { |
||||
|
this.notifyInfo('Plans reloaded.'); |
||||
|
} |
||||
|
}, error => { |
||||
|
this.notifyError(error); |
||||
|
}); |
||||
|
} |
||||
|
|
||||
|
public changePlan(planId: string) { |
||||
|
this.isDisabled = true; |
||||
|
|
||||
|
this.appNameOnce() |
||||
|
.switchMap(app => this.plansService.putPlan(app, new ChangePlanDto(planId), this.version)) |
||||
|
.subscribe(dto => { |
||||
|
this.plans = |
||||
|
new AppPlansDto(planId, |
||||
|
this.plans.planOwner, |
||||
|
this.plans.hasPortal, |
||||
|
this.plans.hasConfigured, |
||||
|
this.plans.plans); |
||||
|
this.isDisabled = false; |
||||
|
}, error => { |
||||
|
this.notifyError(error); |
||||
|
|
||||
|
this.isDisabled = false; |
||||
|
}); |
||||
|
} |
||||
|
|
||||
|
public formatSize(count: number): string { |
||||
|
return FileHelper.fileSize(count); |
||||
|
} |
||||
|
|
||||
|
public formatCalls(count: number): string { |
||||
|
if (count > 1000) { |
||||
|
count = count / 1000; |
||||
|
|
||||
|
if (count < 10) { |
||||
|
count = Math.round(count * 10) / 10; |
||||
|
} else { |
||||
|
count = Math.round(count); |
||||
|
} |
||||
|
|
||||
|
return count + 'k'; |
||||
|
} else if (count < 0) { |
||||
|
return undefined; |
||||
|
} else { |
||||
|
return count.toString(); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
|
||||
@ -0,0 +1,87 @@ |
|||||
|
/* |
||||
|
* Squidex Headless CMS |
||||
|
* |
||||
|
* @license |
||||
|
* Copyright (c) Sebastian Stehle. All rights reserved |
||||
|
*/ |
||||
|
|
||||
|
import { Injectable } from '@angular/core'; |
||||
|
import { Observable } from 'rxjs'; |
||||
|
|
||||
|
import 'framework/angular/http-extensions'; |
||||
|
|
||||
|
import { ApiUrlConfig, Version } from 'framework'; |
||||
|
import { AuthService } from './auth.service'; |
||||
|
|
||||
|
export class AppPlansDto { |
||||
|
constructor( |
||||
|
public readonly currentPlanId: string, |
||||
|
public readonly planOwner: string, |
||||
|
public readonly hasPortal: boolean, |
||||
|
public readonly hasConfigured: boolean, |
||||
|
public readonly plans: PlanDto[] |
||||
|
) { |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
export class PlanDto { |
||||
|
constructor( |
||||
|
public readonly id: string, |
||||
|
public readonly name: string, |
||||
|
public readonly costs: string, |
||||
|
public readonly maxApiCalls: number, |
||||
|
public readonly maxAssetSize: number, |
||||
|
public readonly maxContributors: number |
||||
|
) { |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
|
||||
|
export class ChangePlanDto { |
||||
|
constructor( |
||||
|
public readonly planId: string |
||||
|
) { |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
@Injectable() |
||||
|
export class PlansService { |
||||
|
constructor( |
||||
|
private readonly authService: AuthService, |
||||
|
private readonly apiUrl: ApiUrlConfig |
||||
|
) { |
||||
|
} |
||||
|
|
||||
|
public getPlans(appName: string, version?: Version): Observable<AppPlansDto> { |
||||
|
const url = this.apiUrl.buildUrl(`api/apps/${appName}/plans`); |
||||
|
|
||||
|
return this.authService.authGet(url, version) |
||||
|
.map(response => response.json()) |
||||
|
.map(response => { |
||||
|
const items: any[] = response.plans; |
||||
|
|
||||
|
return new AppPlansDto( |
||||
|
response.currentPlanId, |
||||
|
response.planOwner, |
||||
|
response.hasPortal, |
||||
|
response.hasConfigured, |
||||
|
items.map(item => { |
||||
|
return new PlanDto( |
||||
|
item.id, |
||||
|
item.name, |
||||
|
item.costs, |
||||
|
item.maxApiCalls, |
||||
|
item.maxAssetSize, |
||||
|
item.maxContributors); |
||||
|
})); |
||||
|
}) |
||||
|
.catchError('Failed to load plans. Please reload.'); |
||||
|
} |
||||
|
|
||||
|
public putPlan(appName: string, dto: ChangePlanDto, version?: Version): Observable<any> { |
||||
|
const url = this.apiUrl.buildUrl(`api/apps/${appName}/plan`); |
||||
|
|
||||
|
return this.authService.authPut(url, dto, version) |
||||
|
.catchError('Failed to change plan. Please reload.'); |
||||
|
} |
||||
|
} |
||||
Loading…
Reference in new issue