From 1ececa7c1b8090d02b1ab8faa7362ddbecd5c3af Mon Sep 17 00:00:00 2001 From: cptran777 Date: Thu, 16 Aug 2018 15:01:30 -0700 Subject: [PATCH] Finalize handling for api for metadata health endpoint and showing results --- .../datasets/containers/dataset-health.ts | 52 ++++++++++++++----- .../app/controllers/datasets/dataset.ts | 24 +++++++++ wherehows-web/app/services/dataset-meta.ts | 15 ++++++ .../app/templates/datasets/dataset.hbs | 10 ++-- .../app/utils/api/datasets/health.ts | 20 +++++++ .../tests/unit/services/dataset-meta-test.ts | 14 +++++ .../unit/utils/api/datasets/validator-test.js | 10 ++++ 7 files changed, 127 insertions(+), 18 deletions(-) create mode 100644 wherehows-web/app/services/dataset-meta.ts create mode 100644 wherehows-web/tests/unit/services/dataset-meta-test.ts create mode 100644 wherehows-web/tests/unit/utils/api/datasets/validator-test.js diff --git a/wherehows-web/app/components/datasets/containers/dataset-health.ts b/wherehows-web/app/components/datasets/containers/dataset-health.ts index fdcad18c6c..e3e00663fd 100644 --- a/wherehows-web/app/components/datasets/containers/dataset-health.ts +++ b/wherehows-web/app/components/datasets/containers/dataset-health.ts @@ -4,10 +4,12 @@ import { task } from 'ember-concurrency'; import ComputedProperty from '@ember/object/computed'; import { IChartDatum } from 'wherehows-web/typings/app/visualization/charts'; import { IHealthScore, IDatasetHealth } from 'wherehows-web/typings/api/datasets/health'; -import { healthCategories, healthSeverity, healthDetail } from 'wherehows-web/constants/data/temp-mock/health'; -import { readDatasetHealthByUrn } from 'wherehows-web/utils/api/datasets/health'; +import { readDatasetHealthByUrn, getCategory } from 'wherehows-web/utils/api/datasets/health'; import { Tabs } from 'wherehows-web/constants/datasets/shared'; import { equal } from '@ember-decorators/object/computed'; +import { IObject } from 'wherehows-web/typings/generic'; +import { service } from '@ember-decorators/service'; +import DatasetMeta from 'wherehows-web/services/dataset-meta'; /** * Used for the dataset health tab, represents the fieldnames for the health score table @@ -88,6 +90,9 @@ export default class DatasetHealthContainer extends Component { @equal('tabSelected', Tabs.Health) isActiveTab: boolean; + @service + datasetMeta: ComputedProperty; + /** * Modified categoryMetrics to add properties that will help us render our actual charts without modifying the original * data @@ -145,21 +150,40 @@ export default class DatasetHealthContainer extends Component { * @type {Task>, (a?: any) => TaskInstance>>>} */ getContainerDataTask = task(function*(this: DatasetHealthContainer): IterableIterator> { - const { health } = yield readDatasetHealthByUrn(get(this, 'urn')); - // Pretend like we're getting data from somehwere - const healthData = { - categories: healthCategories, - severity: healthSeverity, - detail: healthDetail - }; + const health = yield readDatasetHealthByUrn(get(this, 'urn')); - setProperties(this, { - categoryMetrics: healthData.categories, - severityMetrics: healthData.severity, - tableData: healthData.detail + const details = health.validations || []; + const total = details.length; + const categories: IObject = {}; + const severities: IObject = {}; + + // Go through the details and find the COUNT of severity and category groupings + const tableData: Array = details.map(detail => { + const category = getCategory(detail.validator); + const severity = detail.tier || 'none'; + categories[category] = (categories[category] || 0) + 1; + severities[severity] = (severities[severity] || 0) + 1; + + return { category, severity, description: detail.description, score: detail.score * 100 }; }); - return health; // Do something with health information + const categoryMetrics: Array = Object.keys(categories).map(category => ({ + name: category, + value: Math.round((categories[category] / total) * 100) + })); + + const severityMetrics: Array = Object.keys(severities).map(severity => ({ + name: severity, + value: Math.round((severities[severity] / total) * 100) + })); + + get(this, 'datasetMeta').set('healthScore', health.score * 100 || 0); + + setProperties(this, { + categoryMetrics, + severityMetrics, + tableData + }); }); /** diff --git a/wherehows-web/app/controllers/datasets/dataset.ts b/wherehows-web/app/controllers/datasets/dataset.ts index c5473b29a2..1532e033b2 100644 --- a/wherehows-web/app/controllers/datasets/dataset.ts +++ b/wherehows-web/app/controllers/datasets/dataset.ts @@ -7,6 +7,9 @@ import { Tabs } from 'wherehows-web/constants/datasets/shared'; import { action } from '@ember-decorators/object'; import { DatasetPlatform } from 'wherehows-web/constants'; import { IDatasetView } from 'wherehows-web/typings/api/datasets/dataset'; +import { alias } from '@ember-decorators/object/computed'; +import DatasetMeta from 'wherehows-web/services/dataset-meta'; +import { service } from '@ember-decorators/service'; export default class DatasetController extends Controller { queryParams = ['urn']; @@ -102,6 +105,21 @@ export default class DatasetController extends Controller { */ datasetContainsPersonalData: boolean; + /** + * Including the datasetmeta property that is connected to each child container for the routable + * tabs. Can be used to share information between these tabs from a higher level + * @type {Ember.Service} + */ + @service + datasetMeta: ComputedProperty; + + /** + * Easy access in the template to the datasetMeta health score provided by the /health endpoint + * called in the dataset-health container + */ + @alias('datasetMeta.healthScore') + datasetHealthScore: ComputedProperty; + /** * Flag indicating that the compliance policy needs user attention * @type {ComputedProperty} @@ -121,6 +139,12 @@ export default class DatasetController extends Controller { return encodeUrn(uri); }); + shouldShowHealthGauge: ComputedProperty = computed('datasetHealthScore', function( + this: DatasetController + ): boolean { + return typeof get(this, 'datasetHealthScore') === 'number'; + }); + /** * Checks if the current platform exists in the supported list of JIT ACL whitelisted platforms * @type {ComputedProperty} diff --git a/wherehows-web/app/services/dataset-meta.ts b/wherehows-web/app/services/dataset-meta.ts new file mode 100644 index 0000000000..58cdc9bab6 --- /dev/null +++ b/wherehows-web/app/services/dataset-meta.ts @@ -0,0 +1,15 @@ +import Service from '@ember/service'; + +/** + * The dataset meta service can be used to share information between dataset route containers. + * Used to share health score but can be expanded to other meta informations + */ +export default class DatasetMeta extends Service { + healthScore: number; +} + +declare module '@ember/service' { + interface Registry { + 'dataset-meta': DatasetMeta; + } +} diff --git a/wherehows-web/app/templates/datasets/dataset.hbs b/wherehows-web/app/templates/datasets/dataset.hbs index 44c2cf9ebe..813dd3411c 100644 --- a/wherehows-web/app/templates/datasets/dataset.hbs +++ b/wherehows-web/app/templates/datasets/dataset.hbs @@ -62,10 +62,12 @@ class="dataset-owner-list" shouldShowDatasetHealth=shouldShowDatasetHealth}} - {{visualization/charts/score-gauge - class="dataset-health-score" - score=83 - title="Health Score:"}} + {{#if shouldShowHealthGauge}} + {{visualization/charts/score-gauge + class="dataset-health-score" + score=datasetHealthScore + title="Health Score:"}} + {{/if}} {{/if}} diff --git a/wherehows-web/app/utils/api/datasets/health.ts b/wherehows-web/app/utils/api/datasets/health.ts index ffc16788dd..0ed272832e 100644 --- a/wherehows-web/app/utils/api/datasets/health.ts +++ b/wherehows-web/app/utils/api/datasets/health.ts @@ -9,6 +9,26 @@ import { IHealthScoreResponse, IDatasetHealth } from 'wherehows-web/typings/api/ */ const datasetHealthUrlByUrn = (urn: string): string => `${datasetUrlByUrn(urn)}/health`; +/** + * Constant for formatting of the validator string + * @type {string} + */ +const validatorFormat = 'com.linkedin.metadata.validators'; + +/** + * Useful for removing extraneous information outside of category + * @type {RegExp} + */ +const validationRegex = new RegExp(`${validatorFormat}.|Validator`, 'gi'); + +/** + * Given a string in the format 'com.linkedin.metadata.validators.[Category]Validator', extract + * Category and return it. + * @param validator - Given validator string + * @returns {string} + */ +export const getCategory = (validator: string) => validator.replace(validationRegex, ''); + export const readDatasetHealthByUrn = async (urn: string): Promise => { const defaultResponse = { score: 0, validations: [] }; diff --git a/wherehows-web/tests/unit/services/dataset-meta-test.ts b/wherehows-web/tests/unit/services/dataset-meta-test.ts new file mode 100644 index 0000000000..e83e8183d4 --- /dev/null +++ b/wherehows-web/tests/unit/services/dataset-meta-test.ts @@ -0,0 +1,14 @@ +import { module, test } from 'qunit'; +import { setupTest } from 'ember-qunit'; + +module('Unit | Service | dataset-meta', function(hooks) { + setupTest(hooks); + + test('it exists', function(assert) { + let service = this.owner.lookup('service:dataset-meta'); + assert.ok(service); + + service.set('healthScore', 50); + assert.equal(service.get('healthScore'), 50); + }); +}); diff --git a/wherehows-web/tests/unit/utils/api/datasets/validator-test.js b/wherehows-web/tests/unit/utils/api/datasets/validator-test.js new file mode 100644 index 0000000000..aa0f58e362 --- /dev/null +++ b/wherehows-web/tests/unit/utils/api/datasets/validator-test.js @@ -0,0 +1,10 @@ +import { module, test } from 'qunit'; +import { getCategory } from 'wherehows-web/utils/api/datasets/health'; + +module('Unit | Utility | api/datasets/health', function() { + test('extracting category from validator string works', function(assert) { + const testSTtring = 'com.linkedin.metadata.validators.OwnershipValidator'; + + assert.equal(getCategory(testSTtring), 'Ownership'); + }); +});