]> git.apps.os.sepia.ceph.com Git - ceph-ci.git/commitdiff
mgr/dashboard: get SASS vars values in TS through CssHelper.
authorAlfonso Martínez <almartin@redhat.com>
Fri, 14 May 2021 15:01:17 +0000 (17:01 +0200)
committerAlfonso Martínez <almartin@redhat.com>
Fri, 14 May 2021 15:01:17 +0000 (17:01 +0200)
Now SASS vendor variables' values can be retrieved by var name via CssHelper.

We were relying on the angular webpack config that has changed in the latest v10.x and v11.
See: https://github.com/angular/angular-cli/issues/19622#issuecomment-744395101

Fixes: https://tracker.ceph.com/issues/50800
Signed-off-by: Alfonso Martínez <almartin@redhat.com>
20 files changed:
src/pybind/mgr/dashboard/frontend/src/app/ceph/dashboard/health-pie/health-pie.component.spec.ts
src/pybind/mgr/dashboard/frontend/src/app/ceph/dashboard/health-pie/health-pie.component.ts
src/pybind/mgr/dashboard/frontend/src/app/ceph/dashboard/health/health.component.spec.ts
src/pybind/mgr/dashboard/frontend/src/app/ceph/dashboard/health/health.component.ts
src/pybind/mgr/dashboard/frontend/src/app/ceph/rgw/rgw-user-list/rgw-user-list.component.ts
src/pybind/mgr/dashboard/frontend/src/app/core/layouts/workbench-layout/workbench-layout.component.spec.ts
src/pybind/mgr/dashboard/frontend/src/app/shared/classes/cd-helper.class.ts
src/pybind/mgr/dashboard/frontend/src/app/shared/classes/css-helper.ts [new file with mode: 0644]
src/pybind/mgr/dashboard/frontend/src/app/shared/enum/color.enum.ts [deleted file]
src/pybind/mgr/dashboard/frontend/src/app/shared/enum/health-color.enum.ts [new file with mode: 0644]
src/pybind/mgr/dashboard/frontend/src/app/shared/pipes/health-color.pipe.spec.ts
src/pybind/mgr/dashboard/frontend/src/app/shared/pipes/health-color.pipe.ts
src/pybind/mgr/dashboard/frontend/src/app/shared/services/favicon.service.spec.ts
src/pybind/mgr/dashboard/frontend/src/app/shared/services/favicon.service.ts
src/pybind/mgr/dashboard/frontend/src/app/shared/shared.module.ts
src/pybind/mgr/dashboard/frontend/src/setupJest.ts
src/pybind/mgr/dashboard/frontend/src/styles.scss
src/pybind/mgr/dashboard/frontend/src/styles.scss.d.ts [deleted file]
src/pybind/mgr/dashboard/frontend/src/styles/defaults/_bootstrap-defaults.scss
src/pybind/mgr/dashboard/frontend/src/styles/vendor/_index.scss

index 783e56155d44f5711abee575ac1afe577344cd03..b87f4bfb5d66f90f82df1d64161a9792051616ad 100644 (file)
@@ -1,6 +1,7 @@
 import { NO_ERRORS_SCHEMA } from '@angular/core';
 import { ComponentFixture, TestBed } from '@angular/core/testing';
 
+import { CssHelper } from '~/app/shared/classes/css-helper';
 import { DimlessBinaryPipe } from '~/app/shared/pipes/dimless-binary.pipe';
 import { DimlessPipe } from '~/app/shared/pipes/dimless.pipe';
 import { FormatterService } from '~/app/shared/services/formatter.service';
@@ -14,7 +15,7 @@ describe('HealthPieComponent', () => {
   configureTestBed({
     schemas: [NO_ERRORS_SCHEMA],
     declarations: [HealthPieComponent],
-    providers: [DimlessBinaryPipe, DimlessPipe, FormatterService]
+    providers: [DimlessBinaryPipe, DimlessPipe, FormatterService, CssHelper]
   });
 
   beforeEach(() => {
index 7cce5ff5f386b17fe5ecfec9510240b182f1ceba..fc119b6e2904bad935c1af901efc99e056901803 100644 (file)
@@ -13,10 +13,10 @@ import * as Chart from 'chart.js';
 import _ from 'lodash';
 import { PluginServiceGlobalRegistrationAndOptions } from 'ng2-charts';
 
+import { CssHelper } from '~/app/shared/classes/css-helper';
 import { ChartTooltip } from '~/app/shared/models/chart-tooltip';
 import { DimlessBinaryPipe } from '~/app/shared/pipes/dimless-binary.pipe';
 import { DimlessPipe } from '~/app/shared/pipes/dimless.pipe';
-import styles from '~/styles.scss';
 
 @Component({
   selector: 'cd-health-pie',
@@ -53,11 +53,11 @@ export class HealthPieComponent implements OnChanges, OnInit {
     colors: [
       {
         backgroundColor: [
-          styles.chartHealthColorGreen,
-          styles.chartHealthColorYellow,
-          styles.chartHealthColorOrange,
-          styles.chartHealthColorRed,
-          styles.chartHealthColorBlue
+          this.cssHelper.propertyValue('chart-color-green'),
+          this.cssHelper.propertyValue('chart-color-yellow'),
+          this.cssHelper.propertyValue('chart-color-orange'),
+          this.cssHelper.propertyValue('chart-color-red'),
+          this.cssHelper.propertyValue('chart-color-blue')
         ]
       }
     ],
@@ -78,7 +78,7 @@ export class HealthPieComponent implements OnChanges, OnInit {
       tooltips: {
         enabled: true,
         displayColors: false,
-        backgroundColor: styles.chartHealthTootlipBgColor,
+        backgroundColor: this.cssHelper.propertyValue('chart-color-tooltip-background'),
         cornerRadius: 0,
         bodyFontSize: 14,
         bodyFontStyle: '600',
@@ -105,6 +105,7 @@ export class HealthPieComponent implements OnChanges, OnInit {
     {
       id: 'center_text',
       beforeDraw(chart: Chart) {
+        const cssHelper = new CssHelper();
         const defaultFontFamily = 'Helvetica Neue, Helvetica, Arial, sans-serif';
         Chart.defaults.global.defaultFontFamily = defaultFontFamily;
         const ctx = chart.ctx;
@@ -121,12 +122,12 @@ export class HealthPieComponent implements OnChanges, OnInit {
         ctx.textBaseline = 'middle';
 
         ctx.font = `24px ${defaultFontFamily}`;
-        ctx.fillStyle = styles.chartHealthCenterTextColor;
+        ctx.fillStyle = cssHelper.propertyValue('chart-color-center-text');
         ctx.fillText(label[0], centerX, centerY - 10);
 
         if (label.length > 1) {
           ctx.font = `14px ${defaultFontFamily}`;
-          ctx.fillStyle = styles.chartHealthCenterTextDescriptionColor;
+          ctx.fillStyle = cssHelper.propertyValue('chart-color-center-text-description');
           ctx.fillText(label[1], centerX, centerY + 10);
         }
         ctx.restore();
@@ -134,7 +135,11 @@ export class HealthPieComponent implements OnChanges, OnInit {
     }
   ];
 
-  constructor(private dimlessBinary: DimlessBinaryPipe, private dimless: DimlessPipe) {}
+  constructor(
+    private dimlessBinary: DimlessBinaryPipe,
+    private dimless: DimlessPipe,
+    private cssHelper: CssHelper
+  ) {}
 
   ngOnInit() {
     const getStyleTop = (tooltip: any, positionY: number) => {
index e1de5f7f59640ec69e9db07ecb2f24b5ad3a18d7..8f671f791132b5811d994bda629708000e88ca60 100644 (file)
@@ -8,6 +8,7 @@ import { of } from 'rxjs';
 
 import { PgCategoryService } from '~/app/ceph/shared/pg-category.service';
 import { HealthService } from '~/app/shared/api/health.service';
+import { CssHelper } from '~/app/shared/classes/css-helper';
 import { Permissions } from '~/app/shared/models/permissions';
 import { AuthStorageService } from '~/app/shared/services/auth-storage.service';
 import { FeatureTogglesService } from '~/app/shared/services/feature-toggles.service';
@@ -61,7 +62,8 @@ describe('HealthComponent', () => {
     providers: [
       { provide: AuthStorageService, useValue: fakeAuthStorageService },
       PgCategoryService,
-      RefreshIntervalService
+      RefreshIntervalService,
+      CssHelper
     ]
   });
 
index 79cfd8f7e916cf237c63818a18eacfb1ecf9d343..d90e17e1f07d3a8837622b9f72121a4e79c4fde6 100644 (file)
@@ -5,6 +5,7 @@ import { Subscription } from 'rxjs';
 
 import { PgCategoryService } from '~/app/ceph/shared/pg-category.service';
 import { HealthService } from '~/app/shared/api/health.service';
+import { CssHelper } from '~/app/shared/classes/css-helper';
 import { Icons } from '~/app/shared/enum/icons.enum';
 import { Permissions } from '~/app/shared/models/permissions';
 import { DimlessBinaryPipe } from '~/app/shared/pipes/dimless-binary.pipe';
@@ -15,7 +16,6 @@ import {
   FeatureTogglesService
 } from '~/app/shared/services/feature-toggles.service';
 import { RefreshIntervalService } from '~/app/shared/services/refresh-interval.service';
-import styles from '~/styles.scss';
 
 @Component({
   selector: 'cd-health',
@@ -32,7 +32,10 @@ export class HealthComponent implements OnInit, OnDestroy {
   clientStatsConfig = {
     colors: [
       {
-        backgroundColor: [styles.chartHealthColorCyan, styles.chartHealthColorPurple]
+        backgroundColor: [
+          this.cssHelper.propertyValue('chart-color-cyan'),
+          this.cssHelper.propertyValue('chart-color-purple')
+        ]
       }
     ]
   };
@@ -40,7 +43,10 @@ export class HealthComponent implements OnInit, OnDestroy {
   rawCapacityChartConfig = {
     colors: [
       {
-        backgroundColor: [styles.chartHealthColorBlue, styles.chartHealthColorGray]
+        backgroundColor: [
+          this.cssHelper.propertyValue('chart-color-blue'),
+          this.cssHelper.propertyValue('chart-color-gray')
+        ]
       }
     ]
   };
@@ -58,7 +64,8 @@ export class HealthComponent implements OnInit, OnDestroy {
     private featureToggles: FeatureTogglesService,
     private refreshIntervalService: RefreshIntervalService,
     private dimlessBinary: DimlessBinaryPipe,
-    private dimless: DimlessPipe
+    private dimless: DimlessPipe,
+    private cssHelper: CssHelper
   ) {
     this.permissions = this.authStorageService.getPermissions();
     this.enabledFeature$ = this.featureToggles.get();
index a2eacd9b9aadad15f3d6cc338058ba5659eb1b83..e7f47298ecffb14d58a6139d1fee904b7356b0c6 100644 (file)
@@ -1,6 +1,6 @@
 import { Component, NgZone, OnInit, TemplateRef, ViewChild } from '@angular/core';
 
-import * as _ from 'lodash';
+import _ from 'lodash';
 import { forkJoin as observableForkJoin, Observable, Subscriber } from 'rxjs';
 
 import { RgwUserService } from '~/app/shared/api/rgw-user.service';
index 8168917dd2928b3ced50d6e4c95abfd32ad84108..faf8c9cdf9400ac19587b80a71afb230af2bd53e 100644 (file)
@@ -6,6 +6,7 @@ import { RouterTestingModule } from '@angular/router/testing';
 import { ToastrModule } from 'ngx-toastr';
 
 import { RbdService } from '~/app/shared/api/rbd.service';
+import { CssHelper } from '~/app/shared/classes/css-helper';
 import { PipesModule } from '~/app/shared/pipes/pipes.module';
 import { AuthStorageService } from '~/app/shared/services/auth-storage.service';
 import { configureTestBed } from '~/testing/unit-test-helper';
@@ -19,7 +20,7 @@ describe('WorkbenchLayoutComponent', () => {
     imports: [RouterTestingModule, ToastrModule.forRoot(), PipesModule, HttpClientTestingModule],
     declarations: [WorkbenchLayoutComponent],
     schemas: [NO_ERRORS_SCHEMA],
-    providers: [AuthStorageService, RbdService]
+    providers: [AuthStorageService, CssHelper, RbdService]
   });
 
   beforeEach(() => {
index 92e955ee4bd64636503404fd8a47a1a36a599d52..5c872b42fd83a98fb1cc6038f9b3998b4df180fb 100644 (file)
@@ -1,4 +1,4 @@
-import * as _ from 'lodash';
+import _ from 'lodash';
 
 export class CdHelperClass {
   /**
diff --git a/src/pybind/mgr/dashboard/frontend/src/app/shared/classes/css-helper.ts b/src/pybind/mgr/dashboard/frontend/src/app/shared/classes/css-helper.ts
new file mode 100644 (file)
index 0000000..e5caef7
--- /dev/null
@@ -0,0 +1,5 @@
+export class CssHelper {
+  propertyValue(propertyName: string): string {
+    return getComputedStyle(document.body).getPropertyValue(`--${propertyName}`);
+  }
+}
diff --git a/src/pybind/mgr/dashboard/frontend/src/app/shared/enum/color.enum.ts b/src/pybind/mgr/dashboard/frontend/src/app/shared/enum/color.enum.ts
deleted file mode 100644 (file)
index 832eaf2..0000000
+++ /dev/null
@@ -1,8 +0,0 @@
-import styles from '~/styles.scss';
-
-export class Color {
-  // HEALTH
-  static HEALTH_ERR = styles.healthColorError;
-  static HEALTH_WARN = styles.healthColorWarning;
-  static HEALTH_OK = styles.healthColorHealthy;
-}
diff --git a/src/pybind/mgr/dashboard/frontend/src/app/shared/enum/health-color.enum.ts b/src/pybind/mgr/dashboard/frontend/src/app/shared/enum/health-color.enum.ts
new file mode 100644 (file)
index 0000000..0423942
--- /dev/null
@@ -0,0 +1,5 @@
+export enum HealthColor {
+  HEALTH_ERR = 'health-color-error',
+  HEALTH_WARN = 'health-color-warning',
+  HEALTH_OK = 'health-color-healthy'
+}
index 0039cf90eb7730435ef0097094f0fa7c38eb38e2..f5e937ce377579bd97704ae3d521b70fe0c65e03 100644 (file)
@@ -1,23 +1,44 @@
-import styles from '~/styles.scss';
-import { HealthColorPipe } from './health-color.pipe';
+import { CssHelper } from '~/app/shared/classes/css-helper';
+import { HealthColorPipe } from '~/app/shared/pipes/health-color.pipe';
+
+class CssHelperStub extends CssHelper {
+  propertyValue(propertyName: string) {
+    if (propertyName === 'health-color-healthy') {
+      return 'fakeGreen';
+    }
+    if (propertyName === 'health-color-warning') {
+      return 'fakeOrange';
+    }
+    if (propertyName === 'health-color-error') {
+      return 'fakeRed';
+    }
+    return '';
+  }
+}
 
 describe('HealthColorPipe', () => {
-  const pipe = new HealthColorPipe();
+  const pipe = new HealthColorPipe(new CssHelperStub());
 
   it('create an instance', () => {
     expect(pipe).toBeTruthy();
   });
 
   it('transforms "HEALTH_OK"', () => {
-    expect(pipe.transform('HEALTH_OK')).toEqual({ color: styles.healthColorHealthy });
+    expect(pipe.transform('HEALTH_OK')).toEqual({
+      color: 'fakeGreen'
+    });
   });
 
   it('transforms "HEALTH_WARN"', () => {
-    expect(pipe.transform('HEALTH_WARN')).toEqual({ color: styles.healthColorWarning });
+    expect(pipe.transform('HEALTH_WARN')).toEqual({
+      color: 'fakeOrange'
+    });
   });
 
   it('transforms "HEALTH_ERR"', () => {
-    expect(pipe.transform('HEALTH_ERR')).toEqual({ color: styles.healthColorError });
+    expect(pipe.transform('HEALTH_ERR')).toEqual({
+      color: 'fakeRed'
+    });
   });
 
   it('transforms others', () => {
index 7da99de4b297b90301c2c12f1a01775577cd0c5b..d046fa15a79b82c85dbbdd107f827ec0745cc6a8 100644 (file)
@@ -1,12 +1,17 @@
 import { Pipe, PipeTransform } from '@angular/core';
 
-import { Color } from '../enum/color.enum';
+import { CssHelper } from '~/app/shared/classes/css-helper';
+import { HealthColor } from '~/app/shared/enum/health-color.enum';
 
 @Pipe({
   name: 'healthColor'
 })
 export class HealthColorPipe implements PipeTransform {
+  constructor(private cssHelper: CssHelper) {}
+
   transform(value: any): any {
-    return Color[value] ? { color: Color[value] } : null;
+    return Object.keys(HealthColor).includes(value as HealthColor)
+      ? { color: this.cssHelper.propertyValue(HealthColor[value]) }
+      : null;
   }
 }
index f55bad43835b19e444b6d847e5becd921dac3b32..0c9e619ea1e74aa9a7ae07fe80fe410ce5b870b4 100644 (file)
@@ -1,6 +1,7 @@
 import { HttpClientTestingModule } from '@angular/common/http/testing';
 import { TestBed } from '@angular/core/testing';
 
+import { CssHelper } from '~/app/shared/classes/css-helper';
 import { configureTestBed } from '~/testing/unit-test-helper';
 import { FaviconService } from './favicon.service';
 
@@ -9,7 +10,7 @@ describe('FaviconService', () => {
 
   configureTestBed({
     imports: [HttpClientTestingModule],
-    providers: [FaviconService]
+    providers: [FaviconService, CssHelper]
   });
 
   beforeEach(() => {
index adec0664f406f09ea7f50fc0fc783884570c1ec2..87ce8fcad91638ce4d99d7284675102652bb080a 100644 (file)
@@ -3,7 +3,8 @@ import { Inject, Injectable, OnDestroy } from '@angular/core';
 
 import { Subscription } from 'rxjs';
 
-import { Color } from '../enum/color.enum';
+import { CssHelper } from '~/app/shared/classes/css-helper';
+import { HealthColor } from '~/app/shared/enum/health-color.enum';
 import { SummaryService } from './summary.service';
 
 @Injectable()
@@ -14,7 +15,8 @@ export class FaviconService implements OnDestroy {
 
   constructor(
     @Inject(DOCUMENT) private document: HTMLDocument,
-    private summaryService: SummaryService
+    private summaryService: SummaryService,
+    private cssHelper: CssHelper
   ) {}
 
   init() {
@@ -48,7 +50,7 @@ export class FaviconService implements OnDestroy {
       // Draw Original Favicon as Background
       context.drawImage(img, 0, 0, faviconSize, faviconSize);
 
-      if (Color[status]) {
+      if (Object.keys(HealthColor).includes(status as HealthColor)) {
         // Cut notification circle area
         context.save();
         context.globalCompositeOperation = 'destination-out';
@@ -61,7 +63,7 @@ export class FaviconService implements OnDestroy {
         context.beginPath();
         context.arc(canvas.width - radius, radius, radius, 0, 2 * Math.PI);
 
-        context.fillStyle = Color[status];
+        context.fillStyle = this.cssHelper.propertyValue(HealthColor[status]);
         context.fill();
       }
 
index 0bffcf749e5cb5aa2fe000c407eb78b0cacb233f..905721fa445c487c7656c2b03621c4fe09be17bb 100644 (file)
@@ -1,6 +1,7 @@
 import { CommonModule } from '@angular/common';
 import { NgModule } from '@angular/core';
 
+import { CssHelper } from '~/app/shared/classes/css-helper';
 import { ComponentsModule } from './components/components.module';
 import { DataTableModule } from './datatable/datatable.module';
 import { DirectivesModule } from './directives/directives.module';
@@ -13,6 +14,6 @@ import { FormatterService } from './services/formatter.service';
   imports: [CommonModule, PipesModule, ComponentsModule, DataTableModule, DirectivesModule],
   declarations: [],
   exports: [ComponentsModule, PipesModule, DataTableModule, DirectivesModule],
-  providers: [AuthStorageService, AuthGuardService, FormatterService]
+  providers: [AuthStorageService, AuthGuardService, FormatterService, CssHelper]
 })
 export class SharedModule {}
index 8fc004375a2d3afa1297109bb0fa5b361f5c4997..3f76543571a4fdc6ca85cc09c785a7f72b96ebfc 100644 (file)
@@ -3,3 +3,9 @@ import '@angular/localize/init';
 import 'jest-preset-angular';
 
 import './jestGlobalMocks';
+
+process.on('unhandledRejection', (error) => {
+  const stack = error['stack'] || '';
+  // Avoid potential hang on test failure when running tests in parallel.
+  throw `WARNING: unhandled rejection: ${error} ${stack}`;
+});
index b4dcf0e431eb857fecbc85cdde418dff8d570197..099433d3148cc494089c1085700300078a2cfcf6 100644 (file)
@@ -136,24 +136,3 @@ tags-input .tags {
 a {
   cursor: pointer;
 }
-
-:export {
-  chartHealthCenterTextColor: #151515;
-  chartHealthCenterTextDescriptionColor: #72767b;
-  chartHealthColorBlue: #06c;
-  chartHealthColorCyan: #73c5c5;
-  chartHealthColorGray: #ededed;
-  chartHealthColorGreen: #7cc674;
-  chartHealthColorLightBlue: #519de9;
-  chartHealthColorLightYellow: #f9e0a2;
-  chartHealthColorMagenta: #009596;
-  chartHealthColorOrange: #ef9234;
-  chartHealthColorPurple: #3c3d99;
-  chartHealthColorRed: #c9190b;
-  chartHealthColorYellow: #f6d173;
-  chartHealthTootlipBgColor: #000;
-
-  healthColorError: #f00;
-  healthColorHealthy: #0b0;
-  healthColorWarning: #ffa500;
-}
diff --git a/src/pybind/mgr/dashboard/frontend/src/styles.scss.d.ts b/src/pybind/mgr/dashboard/frontend/src/styles.scss.d.ts
deleted file mode 100644 (file)
index f805807..0000000
+++ /dev/null
@@ -1,25 +0,0 @@
-export interface Styles {
-  chartHealthColorRed: string;
-  chartHealthColorBlue: string;
-  chartHealthColorOrange: string;
-  chartHealthColorYellow: string;
-  chartHealthColorMagenta: string;
-  chartHealthColorGreen: string;
-  chartHealthColorGray: string;
-  chartHealthColorLightBlue: string;
-  chartHealthColorLightYellow: string;
-  chartHealthColorCyan: string;
-  chartHealthColorPurple: string;
-
-  chartHealthCenterTextColor: string;
-  chartHealthCenterTextDescriptionColor: string;
-  chartHealthTootlipBgColor: string;
-
-  healthColorError: string;
-  healthColorWarning: string;
-  healthColorHealthy: string;
-}
-
-export const styles: Styles;
-
-export default styles;
index b1a386a803ad88e82286e79555aeefe9f9ff9210..1e203af7c8c4823f9a2a98da0f1c0e818c197d98 100644 (file)
@@ -46,12 +46,29 @@ $theme-colors: (
 ) !default;
 
 // Body
-
 $body-color-bright: $light !default;
 $body-bg: $white !default;
 $body-color: $gray-900 !default;
 $body-bg-alt: $gray-200 !default;
 
+// Health colors.
+$health-color-error: #f00 !default;
+$health-color-healthy: $green !default;
+$health-color-warning: #ffa500 !default;
+
+// Chart colors.
+$chart-color-red: #c9190b !default;
+$chart-color-blue: #06c !default;
+$chart-color-orange: #ef9234 !default;
+$chart-color-yellow: #f6d173 !default;
+$chart-color-green: #7cc674 !default;
+$chart-color-gray: #ededed !default;
+$chart-color-cyan: #73c5c5 !default;
+$chart-color-purple: #3c3d99 !default;
+$chart-color-center-text: #151515 !default;
+$chart-color-center-text-description: #72767b !default;
+$chart-color-tooltip-background: $black !default;
+
 // Typography
 
 $font-family-sans-serif: 'Helvetica Neue', Helvetica, Arial, 'Noto Sans', sans-serif,
index 182e69f3155370ddb8583324f867957b5badb2f5..fb6d03c9e8bbe958dbfee459af2a323adbc9d652 100644 (file)
@@ -1,2 +1,22 @@
 @forward 'style-overrides';
 @forward 'variables';
+
+@use 'sass:meta';
+@use 'variables';
+
+@function custom-property-name($name) {
+  @return '--' + $name;
+}
+
+@mixin define-custom-property($name, $value) {
+  #{custom-property-name($name)}: meta.inspect($value);
+}
+
+:root {
+  // Make vendor variables accessible to JS/TS code via CSS custom property definition.
+  @each $key_name, $value in meta.module-variables('variables') {
+    @if type-of($value) != 'map' {
+      @include define-custom-property($key_name, $value);
+    }
+  }
+}