Skip to content

Commit

Permalink
Merge 48e0600 into addccb4
Browse files Browse the repository at this point in the history
  • Loading branch information
shimedh committed Oct 11, 2018
2 parents addccb4 + 48e0600 commit 813ca4a
Show file tree
Hide file tree
Showing 20 changed files with 258 additions and 65 deletions.
1 change: 1 addition & 0 deletions client/src/app/shared/models/constants.ts
Expand Up @@ -142,6 +142,7 @@ export class Links {
public static linuxContainersLearnMore = 'https://go.microsoft.com/fwlink/?linkid=861969';
public static premiumV2NotAvailableLearnMore = 'https://go.microsoft.com/fwlink/?linkid=2009376';
public static azureComputeUnitLearnMore = 'https://go.microsoft.com/fwlink/?linkid=2027465';
public static pv2UpsellInfoLearnMore = 'https://go.microsoft.com/fwlink/?linkid=2028474';
}

export class Kinds {
Expand Down
1 change: 1 addition & 0 deletions client/src/app/shared/models/portal-resources.ts
Expand Up @@ -1009,6 +1009,7 @@
public static pricing_pv2NotAvailable = 'pricing_pv2NotAvailable';
public static pricing_epNotAvailable = 'pricing_epNotAvailable';
public static pricing_scaleUp = 'pricing_scaleUp';
public static pricing_pv2UpsellInfoMessage = 'pricing_pv2UpsellInfoMessage';
public static free = 'free';
public static pricing_pricePerMonth = 'pricing_pricePerMonth';
public static pricing_pricePerHour = 'pricing_pricePerHour';
Expand Down
4 changes: 4 additions & 0 deletions client/src/app/shared/resourceDescriptors.ts
Expand Up @@ -26,6 +26,10 @@ export class ArmSubcriptionDescriptor extends Descriptor {
constructor(resourceId: string) {
super(resourceId);

if (this.parts[0].toLowerCase() !== 'subscriptions') {
throw Error(`Expected subscriptions segment in resourceId: ${resourceId}`);
}

this.subscriptionId = this.parts[1];
}

Expand Down
@@ -1,9 +1,10 @@
import { Kinds, Links } from './../../../shared/models/constants';
import { Kinds, Links, ServerFarmSku } from './../../../shared/models/constants';
import { PortalResources } from './../../../shared/models/portal-resources';
import { AppKind } from './../../../shared/Utilities/app-kind';
import { PriceSpec, PriceSpecInput } from './price-spec';

export abstract class BasicPlanPriceSpec extends PriceSpec {
sku = ServerFarmSku.basic;

featureItems = [{
iconUrl: 'image/ssl.svg',
Expand Down
Expand Up @@ -8,6 +8,7 @@ import { DV2SeriesPriceSpec } from './dV2series-price-spec';
import { NewPlanSpecPickerData } from './plan-price-spec-manager';

export abstract class ElasticPremiumPlanPriceSpec extends DV2SeriesPriceSpec {
sku = ServerFarmSku.elasticPremium;

featureItems = [
{
Expand Down
@@ -1,10 +1,11 @@
import { Observable } from 'rxjs/Observable';
import { Kinds, Links } from './../../../shared/models/constants';
import { Kinds, Links, ServerFarmSku } from './../../../shared/models/constants';
import { PortalResources } from './../../../shared/models/portal-resources';
import { AppKind } from './../../../shared/Utilities/app-kind';
import { PriceSpec, PriceSpecInput } from './price-spec';

export class FreePlanPriceSpec extends PriceSpec {
sku = ServerFarmSku.free;
skuCode = 'F1';
legacySkuName = 'free';
topLevelFeatures = [
Expand Down
@@ -1,12 +1,13 @@
import { Injector } from '@angular/core';
import { Kinds, Links } from './../../../shared/models/constants';
import { Kinds, Links, ServerFarmSku } from './../../../shared/models/constants';
import { PortalResources } from '../../../shared/models/portal-resources';
import { AseService } from '../../../shared/services/ase.service';
import { NationalCloudEnvironment } from './../../../shared/services/scenario/national-cloud.environment';
import { AppKind } from './../../../shared/Utilities/app-kind';
import { PriceSpec, PriceSpecInput } from './price-spec';

export abstract class IsolatedPlanPriceSpec extends PriceSpec {
sku = ServerFarmSku.isolated;

featureItems = [{
iconUrl: 'image/app-service-environment.svg',
Expand Down
Expand Up @@ -15,7 +15,8 @@ import { SpecCostQueryInput } from './billing-models';
import { PriceSpecInput, PriceSpec } from './price-spec';
import { Subject } from 'rxjs/Subject';
import { BillingMeter } from '../../../shared/models/arm/billingMeter';
import { LogCategories } from '../../../shared/models/constants';
import { LogCategories, ServerFarmSku, Links } from '../../../shared/models/constants';
import { AuthzService } from 'app/shared/services/authz.service';

export interface SpecPickerInput<T> {
id: ResourceId;
Expand Down Expand Up @@ -100,7 +101,7 @@ export class PlanPriceSpecManager {
specInitCalls = specInitCalls.concat(g.additionalSpecs.map(s => s.initialize(priceSpecInput)));
});

return Observable.zip(...specInitCalls);
return specInitCalls.length > 0 ? Observable.zip(...specInitCalls) : Observable.of(null);
});
}

Expand Down Expand Up @@ -277,6 +278,54 @@ export class PlanPriceSpecManager {
this._ngUnsubscribe$.next();
}

setSelectedSpec(spec: PriceSpec) {
this.selectedSpecGroup.selectedSpec = spec;

// plan is null for new plans
if (this._plan) {
const tier = this._plan.sku.tier;
if ((tier === ServerFarmSku.premiumV2 && spec.sku !== ServerFarmSku.premiumV2)
|| (tier !== ServerFarmSku.premiumV2 && spec.sku === ServerFarmSku.premiumV2)) {

// show message when upgrading to PV2 or downgrading from PV2.
this._specPicker.statusMessage = {
message: this._ts.instant(PortalResources.pricing_pv2UpsellInfoMessage),
level: 'info',
infoLink: Links.pv2UpsellInfoLearnMore,
};
}
}
}

checkAccess(input: SpecPickerInput<NewPlanSpecPickerData>, authZService: AuthzService) {
const resourceId = input.id;
return Observable.zip(
!input.data ? authZService.hasPermission(resourceId, [AuthzService.writeScope]) : Observable.of(true),
!input.data ? authZService.hasReadOnlyLock(resourceId) : Observable.of(false),
).do(r => {
if (!input.data) {
const planDescriptor = new ArmResourceDescriptor(resourceId);
const name = planDescriptor.parts[planDescriptor.parts.length - 1];

if (!r[0]) {
this._specPicker.statusMessage = {
message: this._ts.instant(PortalResources.pricing_noWritePermissionsOnPlanFormat).format(name),
level: 'error',
};

this._specPicker.shieldEnabled = true;
} else if (r[1]) {
this._specPicker.statusMessage = {
message: this._ts.instant(PortalResources.pricing_planReadonlyLockFormat).format(name),
level: 'error',
};

this._specPicker.shieldEnabled = true;
}
}
});
}

private _getPlan(inputs: SpecPickerInput<NewPlanSpecPickerData>) {
if (!inputs.data) {
return this._planService.getPlan(inputs.id, true)
Expand All @@ -288,7 +337,7 @@ export class PlanPriceSpecManager {
if (this._plan.sku.name === this.DynamicSku) {
this._specPicker.statusMessage = {
message: this._ts.instant(PortalResources.pricing_notAvailableConsumption),
level: 'error'
level: 'error',
};

this._specPicker.shieldEnabled = true;
Expand All @@ -298,7 +347,7 @@ export class PlanPriceSpecManager {
} else {
this._specPicker.statusMessage = {
message: this._ts.instant(PortalResources.pricing_noWritePermissionsOnPlan),
level: 'error'
level: 'error',
};

this._specPicker.shieldEnabled = true;
Expand Down
@@ -1,9 +1,10 @@
import { PriceSpec, PriceSpecInput } from './price-spec';
import { Injector } from '@angular/core';
import { PortalResources } from '../../../shared/models/portal-resources';
import { Links } from '../../../shared/models/constants';
import { Links, ServerFarmSku } from '../../../shared/models/constants';

export abstract class PremiumContainerPlanPriceSpec extends PriceSpec {
sku = ServerFarmSku.premiumContainer;

featureItems = [{
iconUrl: 'image/ssl.svg',
Expand Down
@@ -1,10 +1,11 @@
import { Injector } from '@angular/core';
import { Kinds, Links } from './../../../shared/models/constants';
import { Kinds, Links, ServerFarmSku } from './../../../shared/models/constants';
import { PortalResources } from '../../../shared/models/portal-resources';
import { AppKind } from './../../../shared/Utilities/app-kind';
import { PriceSpec, PriceSpecInput } from './price-spec';

export abstract class PremiumPlanPriceSpec extends PriceSpec {
sku = ServerFarmSku.premium;

featureItems = [{
iconUrl: 'image/ssl.svg',
Expand Down
Expand Up @@ -8,6 +8,7 @@ import { DV2SeriesPriceSpec } from './dV2series-price-spec';
import { NewPlanSpecPickerData } from './plan-price-spec-manager';

export abstract class PremiumV2PlanPriceSpec extends DV2SeriesPriceSpec {
sku = ServerFarmSku.premiumV2;

featureItems = [{
iconUrl: 'image/ssl.svg',
Expand Down
Expand Up @@ -190,14 +190,14 @@ describe('Price Spec Manager', () => {
return new MockSpecGroup(
injector,
[
new MockPriceSpec(injector, `Group${groupNumber}-Recommended1`),
new MockPriceSpec(injector, `Group${groupNumber}-Recommended2`),
new MockPriceSpec(injector, `Group${groupNumber}-Recommended3`)
new MockPriceSpec(injector, `Group${groupNumber}-Recommended1`, 'Recommended1'),
new MockPriceSpec(injector, `Group${groupNumber}-Recommended2`, 'Recommended2'),
new MockPriceSpec(injector, `Group${groupNumber}-Recommended3`, 'Recommended3')
],
[
new MockPriceSpec(injector, `Group${groupNumber}-Additional1`),
new MockPriceSpec(injector, `Group${groupNumber}-Additional2`),
new MockPriceSpec(injector, `Group${groupNumber}-Additional3`)
new MockPriceSpec(injector, `Group${groupNumber}-Additional1`, 'Additional1'),
new MockPriceSpec(injector, `Group${groupNumber}-Additional2`, 'Additional2'),
new MockPriceSpec(injector, `Group${groupNumber}-Additional3`, 'Additional3')
]);
}

Expand All @@ -211,6 +211,7 @@ describe('Price Spec Manager', () => {
});

class MockPriceSpec extends PriceSpec {
sku = null;
skuCode = null;
legacySkuName = null;
topLevelFeatures = [
Expand Down Expand Up @@ -239,8 +240,9 @@ class MockPriceSpec extends PriceSpec {
}]
};

constructor(injector: Injector, skuCode: string) {
constructor(injector: Injector, skuCode: string, sku: string) {
super(injector);
this.sku = sku;
this.skuCode = skuCode;
this.legacySkuName = `Legacy-${skuCode}`;
this.meterFriendlyName = `${skuCode} App Service`;
Expand Down
Expand Up @@ -19,6 +19,7 @@ export interface PriceSpecInput {

export abstract class PriceSpec {
abstract skuCode: string; // SKU code name, like S1 or P1v2
abstract sku: string;

// Used ONLY for returning legacy PCV3 SKU names to Ibiza create scenario's since it currently
// relies on this format. There's no reason why we couldn't remove it going forward but I've
Expand Down
@@ -1,9 +1,10 @@
import { Kinds, Links } from '../../../shared/models/constants';
import { Kinds, Links, ServerFarmSku } from '../../../shared/models/constants';
import { PortalResources } from './../../../shared/models/portal-resources';
import { AppKind } from './../../../shared/Utilities/app-kind';
import { PriceSpec, PriceSpecInput } from './price-spec';

export class SharedPlanPriceSpec extends PriceSpec {
sku = ServerFarmSku.shared;
skuCode = 'D1';
legacySkuName = 'shared';
topLevelFeatures = [
Expand Down
@@ -1,10 +1,11 @@
import { Injector } from '@angular/core/src/core';
import { Kinds, Links } from './../../../shared/models/constants';
import { Kinds, Links, ServerFarmSku } from './../../../shared/models/constants';
import { PortalResources } from './../../../shared/models/portal-resources';
import { AppKind } from './../../../shared/Utilities/app-kind';
import { PriceSpec, PriceSpecInput } from './price-spec';

export abstract class StandardPlanPriceSpec extends PriceSpec {
sku = ServerFarmSku.standard;

featureItems = [{
iconUrl: 'image/ssl.svg',
Expand Down
15 changes: 8 additions & 7 deletions client/src/app/site/spec-picker/spec-picker.component.html
Expand Up @@ -101,15 +101,16 @@ <h2 class="tiers-header" tabindex="0">{{'pricing_additionalTiers' | translate}}<
<span *ngIf="isUpdating" load-image="image/loader.svg" class="icon-medium fa-spin"></span>
</button>
<span class="message-icon icon-medium"
*ngIf="statusMessage?.message"
[load-image]="statusMessage?.level === 'error' ? 'image/error.svg' : 'image/success.svg'"></span>
*ngIf="statusMessage && statusMessage.message"
[load-image]="statusMessageImage"></span>

<span class="message-text"
[class.message-error]="statusMessage?.level === 'error'"
[class.message-success]="statusMessage?.level === 'success'">
<span class="message-text"
[class.message-error]="statusMessage?.level === 'error'"
[class.message-success]="statusMessage?.level === 'success'">

{{statusMessage?.message}}
</span>
{{statusMessage?.message}}
</span>
<a *ngIf="statusMessage?.infoLink" class="message-text" [href]="statusMessage?.infoLink" target="_blank">{{'topBar_learnMore' | translate}}</a>
</div>
</footer>
</div>
23 changes: 14 additions & 9 deletions client/src/app/site/spec-picker/spec-picker.component.scss
Expand Up @@ -241,25 +241,30 @@ $center-column-max-width: calc(#{$spec-card-max-width}*4 - 40px);
#spec-picker-footer{
position: relative;
height: 100%;
margin-top: auto;
margin-bottom: auto;
top: 10px;

button, .message-icon, .message-text{
position: absolute;
top: 50%;
transform: translateY(-50%);
button, .message-icon, .message-text {
height: 27px;
}

.icon-medium{
.icon-medium {
height: 18px;
width: 18px;
vertical-align: middle;
}

.message-icon{
left: 160px;
.message-icon {
margin: 0 0 5px 15px;
height: 24px;
left: 20px;
}

.message-text{
left: 185px;
.message-text {
margin-left: 5px;
height: 27px;
left: 45px;
font-size: 14px;
}
}
Expand Down

0 comments on commit 813ca4a

Please sign in to comment.