diff --git a/datahub-web/@datahub/data-models/addon/api/browse.ts b/datahub-web/@datahub/data-models/addon/api/browse.ts index 08e3db1c67..0072b01d1b 100644 --- a/datahub-web/@datahub/data-models/addon/api/browse.ts +++ b/datahub-web/@datahub/data-models/addon/api/browse.ts @@ -1,10 +1,20 @@ import { getJSON, cacheApi } from '@datahub/utils/api/fetcher'; import { ApiVersion, getApiRoot } from '@datahub/utils/api/shared'; import buildUrl from '@datahub/utils/api/build-url'; -import { IBrowseParams, IBrowseResponse } from '@datahub/data-models/types/entity/browse'; +import { IBrowseParams, IBrowseResponse, IBrowsePathParams } from '@datahub/data-models/types/entity/browse'; +/** + * URL for browse + * @param version api version + */ export const browseUrlRoot = (version: ApiVersion): string => `${getApiRoot(version)}/browse`; +/** + * URL for browse paths + * @param version api version + */ +export const browsePathUrlRoot = (version: ApiVersion): string => `${getApiRoot(version)}/browsePaths`; + /** * Will return the string url for the browse api * @param params GET paramaters required for this API call, see IBrowseParams type @@ -14,6 +24,14 @@ const browseUrl = (params: IBrowseParams): string => { return buildUrl(`${urlRoot}`, params); }; +/** + * Build GET request for browse + * @param params entity type and URN + */ +const browsePathUrl = (params: IBrowsePathParams): string => { + const urlRoot = browsePathUrlRoot(ApiVersion.v2); + return buildUrl(`${urlRoot}`, params); +}; /** * Will fetch browse information for a specific path and entity. * Will return a paginated return of elements and a fixed set of groups/folders. @@ -25,3 +43,13 @@ export const readBrowse = cacheApi( return getJSON({ url }); } ); + +/** + * Read the path to reach to the specified entity + */ +export const readBrowsePath = cacheApi( + (params: IBrowsePathParams): Promise> => { + const url = browsePathUrl(params); + return getJSON>({ url }); + } +); diff --git a/datahub-web/@datahub/data-models/addon/constants/entity/index.ts b/datahub-web/@datahub/data-models/addon/constants/entity/index.ts index 5a70853d87..f8972f0761 100644 --- a/datahub-web/@datahub/data-models/addon/constants/entity/index.ts +++ b/datahub-web/@datahub/data-models/addon/constants/entity/index.ts @@ -5,7 +5,7 @@ import { PersonEntity } from '@datahub/data-models/entity/person/person-entity'; * Defines the interface for the DataModelEntity enum below. * This allows each entry in the enum to be indexable */ -interface IDataModelEntity { +export interface IDataModelEntity { [DatasetEntity.displayName]: typeof DatasetEntity; [PersonEntity.displayName]: typeof PersonEntity; } diff --git a/datahub-web/@datahub/data-models/addon/constants/entity/person/links.ts b/datahub-web/@datahub/data-models/addon/constants/entity/person/links.ts deleted file mode 100644 index 3bd7c19f21..0000000000 --- a/datahub-web/@datahub/data-models/addon/constants/entity/person/links.ts +++ /dev/null @@ -1,7 +0,0 @@ -/** - * Constant base for the user profile link. We append the username to this to reach their - * profile - * @type {string} - * @deprecated - should be moved to internal version - */ -export const profileLinkBase = 'https://cinco.linkedin.biz/people/'; diff --git a/datahub-web/@datahub/data-models/addon/entity/base-entity.ts b/datahub-web/@datahub/data-models/addon/entity/base-entity.ts index c4b64563af..6f2d719d14 100644 --- a/datahub-web/@datahub/data-models/addon/entity/base-entity.ts +++ b/datahub-web/@datahub/data-models/addon/entity/base-entity.ts @@ -13,7 +13,7 @@ import { IEntityLinkAttrsWithCount } from '@datahub/data-models/types/entity/shared'; import { NotImplementedError } from '@datahub/data-models/constants/entity/shared/index'; -import { readBrowse } from '@datahub/data-models/api/browse'; +import { readBrowse, readBrowsePath } from '@datahub/data-models/api/browse'; import { getFacetDefaultValueForEntity } from '@datahub/data-models/entity/utils/facets'; import { InstitutionalMemory } from '@datahub/data-models/models/aspects/institutional-memory'; @@ -197,6 +197,31 @@ export abstract class BaseEntity { throw new Error(NotImplementedError); } + /** + * Workaround to get the current static instance + * This makes sense if you want to get a static property only + * implemented in a subclass, therefore, the same static + * class is needed + */ + get staticInstance(): IBaseEntityStatics { + return (this.constructor as unknown) as IBaseEntityStatics; + } + + /** + * Will read the current path for an entity + */ + get readPath(): Promise> { + const entityName = this.staticInstance.renderProps.search.apiName; + return readBrowsePath({ + type: entityName, + urn: this.urn + }).then( + (paths): Array => { + return paths && paths.length > 0 ? paths[0].split('/').filter(Boolean) : []; + } + ); + } + /** * Asynchronously resolves with an instance of T * @readonly @@ -219,15 +244,6 @@ export abstract class BaseEntity { throw new Error(NotImplementedError); } - /** - * - * @readonly - * @type {Array} - * @memberof BaseEntity - */ - get hierarchySegments(): Array { - throw new Error(NotImplementedError); - } /** * Class properties common across instances * Dictates how visual ui components should be rendered diff --git a/datahub-web/@datahub/data-models/addon/entity/dataset/dataset-entity.ts b/datahub-web/@datahub/data-models/addon/entity/dataset/dataset-entity.ts index 77983a8e50..48d3f22bfe 100644 --- a/datahub-web/@datahub/data-models/addon/entity/dataset/dataset-entity.ts +++ b/datahub-web/@datahub/data-models/addon/entity/dataset/dataset-entity.ts @@ -10,9 +10,6 @@ import { oneWay } from '@ember/object/computed'; import { DatasetPlatform } from '@datahub/metadata-types/constants/entity/dataset/platform'; import { decodeUrn } from '@datahub/utils/validators/urn'; import { readCategories } from '@datahub/data-models/entity/dataset/read-categories'; -import { computed } from '@ember/object'; -import { IDataPlatform } from '@datahub/metadata-types/types/entity/dataset/platform'; -import { readDataPlatforms } from '@datahub/data-models/api/dataset/platforms'; import { getPrefix } from '@datahub/data-models/entity/dataset/utils/segments'; import { readDatasetsCount } from '@datahub/data-models/api/dataset/count'; import { setProperties } from '@ember/object'; @@ -71,6 +68,17 @@ export class DatasetEntity extends BaseEntity { return decodeUrn(this.urn); } + /** + * Creates a link for this specific entity instance, useful for generating a dynamic link from a + * single particular dataset entity + */ + get linkForEntity(): IEntityLinkAttrs { + return DatasetEntity.getLinkForEntity({ + entityUrn: this.urn, + displayName: this.name + }); + } + /** * Reads the snapshots for a list of Dataset urns * @static @@ -109,15 +117,10 @@ export class DatasetEntity extends BaseEntity { /** * Reference to the data entity's native name, should not be something that is editable but gives us a * more human readable form for the dataset vs the urn - * @type {string} */ - @oneWay('entity.nativeName') - name!: string; - - /** - * Reference to the constructed data platform once it is fetched from api - */ - currentDataPlatform?: IDataPlatform; + get name(): string { + return this.entity ? this.entity.nativeName : ''; + } /** * Retrieves the value of the Dataset entity identified by this.urn @@ -204,26 +207,6 @@ export class DatasetEntity extends BaseEntity { )); } - /** - * Produces an array of strings depicting the hierarchy of a Feature Entity - * @readonly - * @type {Array} - * @memberof FeatureEntity - */ - @computed('entity') - get hierarchySegments(): Array { - const { entity } = this; - const { currentDataPlatform } = this; - const separator = (currentDataPlatform && currentDataPlatform.datasetNameDelimiter) || '.'; - - if (entity) { - const { platform, nativeName } = entity; - return [platform, ...nativeName.split(separator).filter(Boolean)]; - } - - return []; - } - /** * Interim implementation to read categories for datasets * TODO META-8863 @@ -287,14 +270,10 @@ export class DatasetEntity extends BaseEntity { export const createDatasetEntity = async (urn: string, fetchedEntity?: IDatasetApiView): Promise => { const dataset = new DatasetEntity(urn); const entity = fetchedEntity || (await dataset.readEntity); - // TODO: [META-8652] Find way to separate this from the createDatasetEntity as the concern of creating a dataset - // entity should not be related to the concern of reading data platforms - const dataPlatforms: Array = await readDataPlatforms(); - const currentDataPlatform = dataPlatforms.find((platform): boolean => platform.name === entity.platform); setProperties(dataset, { - entity, - currentDataPlatform + entity }); + return dataset; }; diff --git a/datahub-web/@datahub/data-models/addon/entity/dataset/render-props.ts b/datahub-web/@datahub/data-models/addon/entity/dataset/render-props.ts index 20e7d0dd9f..ec896b0c10 100644 --- a/datahub-web/@datahub/data-models/addon/entity/dataset/render-props.ts +++ b/datahub-web/@datahub/data-models/addon/entity/dataset/render-props.ts @@ -6,7 +6,6 @@ import { getTabPropertiesFor } from '@datahub/data-models/entity/utils'; /** * Class properties common across instances * Dictates how visual ui components should be rendered - * Implemented as a getter to ensure that reads are idempotent * @readonly * @static * @type {IEntityRenderProps} diff --git a/datahub-web/@datahub/data-models/addon/entity/person/person-entity.ts b/datahub-web/@datahub/data-models/addon/entity/person/person-entity.ts index 25d4d8b391..ba266cf745 100644 --- a/datahub-web/@datahub/data-models/addon/entity/person/person-entity.ts +++ b/datahub-web/@datahub/data-models/addon/entity/person/person-entity.ts @@ -1,6 +1,5 @@ import getActorFromUrn from '@datahub/data-models/utils/get-actor-from-urn'; import { computed } from '@ember/object'; -import { profileLinkBase } from '@datahub/data-models/constants/entity/person/links'; import { NotImplementedError } from '@datahub/data-models/constants/entity/shared'; import { getRenderProps, @@ -51,7 +50,7 @@ export class PersonEntity extends BaseEntity { * TODO: [META-9698] Migrate to using LiPersonEntity */ static profileLinkFromUsername(username: string): string { - return `${profileLinkBase}${username}`; + return `${username}`; } /** diff --git a/datahub-web/@datahub/data-models/types/entity/browse.d.ts b/datahub-web/@datahub/data-models/types/entity/browse.d.ts index 913db9472d..bd4576051c 100644 --- a/datahub-web/@datahub/data-models/types/entity/browse.d.ts +++ b/datahub-web/@datahub/data-models/types/entity/browse.d.ts @@ -26,3 +26,11 @@ export interface IBrowseResponse { groups: Array<{ name: string; count: number }>; }; } + +/** + * Path for bowse paths + */ +export interface IBrowsePathParams { + type: string; + urn: string; +} diff --git a/datahub-web/@datahub/entity-deprecation/addon/templates/components/entity-deprecation.hbs b/datahub-web/@datahub/entity-deprecation/addon/templates/components/entity-deprecation.hbs index ed26ce58d6..a5f0daa22e 100644 --- a/datahub-web/@datahub/entity-deprecation/addon/templates/components/entity-deprecation.hbs +++ b/datahub-web/@datahub/entity-deprecation/addon/templates/components/entity-deprecation.hbs @@ -107,4 +107,4 @@ -{{yield}} +{{yield}} \ No newline at end of file diff --git a/datahub-web/@datahub/shared/addon/components/wait-promise-container.ts b/datahub-web/@datahub/shared/addon/components/wait-promise-container.ts new file mode 100644 index 0000000000..8ca579288e --- /dev/null +++ b/datahub-web/@datahub/shared/addon/components/wait-promise-container.ts @@ -0,0 +1,47 @@ +import Component from '@ember/component'; +// @ts-ignore: Ignore import of compiled template +import template from '../templates/components/wait-promise-container'; +import { containerDataSource } from '@datahub/utils/api/data-source'; +import { ETaskPromise } from '@datahub/utils/types/concurrency'; +import { task } from 'ember-concurrency'; +import { set } from '@ember/object'; +import { layout } from '@ember-decorators/component'; + +/** + * WaitPromiseContainer will show spinner while the promise passed is being resolved + * + * usage: + * + * const promise = Promise.resolve(3); + * + * + * {{resolved}} + * + * + * should show '3' as the promise resolved '3' + */ +@layout(template) +@containerDataSource('getContainerDataTask', ['promise']) +export default class WaitPromiseContainer extends Component { + /** + * Input promise that we want to wait + */ + promise?: Promise; + + /** + * Value for the resolved promise that will be yielded + */ + resolved?: T; + + /** + * Will fetch and transform api data into status table input format + */ + @task(function*(this: WaitPromiseContainer): IterableIterator> { + const { promise } = this; + if (promise) { + const resolved: T = yield promise; + set(this, 'resolved', resolved); + } + }) + getContainerDataTask!: ETaskPromise; +} diff --git a/datahub-web/@datahub/shared/addon/templates/components/wait-promise-container.hbs b/datahub-web/@datahub/shared/addon/templates/components/wait-promise-container.hbs new file mode 100644 index 0000000000..acae6f98c9 --- /dev/null +++ b/datahub-web/@datahub/shared/addon/templates/components/wait-promise-container.hbs @@ -0,0 +1,3 @@ + + {{yield this.resolved}} + \ No newline at end of file diff --git a/datahub-web/@datahub/shared/app/components/wait-promise-container.js b/datahub-web/@datahub/shared/app/components/wait-promise-container.js new file mode 100644 index 0000000000..b07ee3f0b1 --- /dev/null +++ b/datahub-web/@datahub/shared/app/components/wait-promise-container.js @@ -0,0 +1 @@ +export { default } from '@datahub/shared/components/wait-promise-container'; diff --git a/datahub-web/@datahub/shared/package.json b/datahub-web/@datahub/shared/package.json index c7575f71b6..2f1335fbdc 100644 --- a/datahub-web/@datahub/shared/package.json +++ b/datahub-web/@datahub/shared/package.json @@ -47,6 +47,7 @@ "ember-cli-sri": "^2.1.1", "ember-cli-typescript-blueprints": "^2.0.0", "ember-cli-uglify": "^2.1.0", + "ember-concurrency": "^1.0.0", "ember-export-application-global": "^2.0.0", "ember-load-initializers": "^2.0.0", "ember-maybe-import-regenerator": "^0.1.6", diff --git a/datahub-web/@datahub/shared/testem.js b/datahub-web/@datahub/shared/testem.js index 55b4ad8366..3c17a8b75d 100644 --- a/datahub-web/@datahub/shared/testem.js +++ b/datahub-web/@datahub/shared/testem.js @@ -1,2 +1 @@ -const testemConf = require('../../configs/testem-base'); -module.exports = testemConf; +module.exports = require('../../configs/testem-base'); diff --git a/datahub-web/@datahub/shared/tests/integration/components/wait-promise-container-test.ts b/datahub-web/@datahub/shared/tests/integration/components/wait-promise-container-test.ts new file mode 100644 index 0000000000..65fbdf3ef3 --- /dev/null +++ b/datahub-web/@datahub/shared/tests/integration/components/wait-promise-container-test.ts @@ -0,0 +1,31 @@ +import { module, test } from 'qunit'; +import { setupRenderingTest } from 'ember-qunit'; +import { render, settled } from '@ember/test-helpers'; +import hbs from 'htmlbars-inline-precompile'; + +module('Integration | Component | wait-promise-container', function(hooks): void { + setupRenderingTest(hooks); + + test('it renders', async function(assert): Promise { + let testResolve: (value?: unknown) => void = (): void => {}; + this.set( + 'promise', + new Promise((resolve): void => { + testResolve = resolve; + }) + ); + await render(hbs` + + {{resolved}} + + `); + + assert.equal(this.element.textContent && this.element.textContent.trim(), ''); + + testResolve('test'); + + // we need to wait to the rerender to happen as setting a property will trigger an ember loop + await settled(); + assert.equal(this.element.textContent && this.element.textContent.trim(), 'test'); + }); +}); diff --git a/datahub-web/@datahub/shared/types/configurator/configurator.d.ts b/datahub-web/@datahub/shared/types/configurator/configurator.d.ts index c557eb5b3f..ab1164a32e 100644 --- a/datahub-web/@datahub/shared/types/configurator/configurator.d.ts +++ b/datahub-web/@datahub/shared/types/configurator/configurator.d.ts @@ -1,6 +1,6 @@ import { ITrackingConfig } from '@datahub/shared/types/configurator/tracking'; import Service from '@ember/service'; -import { ApiStatus } from '@datahub/utils/addon/api/shared'; +import { ApiStatus } from '@datahub/utils/api/shared'; /** * Describes the interface for the configuration endpoint response object. diff --git a/datahub-web/@datahub/user/addon/mocks/person-entity.ts b/datahub-web/@datahub/user/addon/mocks/person-entity.ts index 91fb28b35a..046e097c5b 100644 --- a/datahub-web/@datahub/user/addon/mocks/person-entity.ts +++ b/datahub-web/@datahub/user/addon/mocks/person-entity.ts @@ -19,6 +19,7 @@ export function populateMockPersonEntity( entity.linkedinProfile = 'https://www.linkedin.com/in/ash-ketchum-b212502a/'; entity.slackLink = 'aketchum'; entity.skills = ['training', 'catching', 'battling']; + const managerEntity = new personEntity(personEntity.urnFromUsername('pikachu')); managerEntity.name = 'Pikachu'; managerEntity.profilePictureUrl = 'https://pokemonletsgo.pokemon.com/assets/img/common/char-pikachu.png'; diff --git a/datahub-web/@datahub/utils/addon/components/concurrency-task-state-handler.ts b/datahub-web/@datahub/utils/addon/components/concurrency-task-state-handler.ts index 217dd96103..6431978250 100644 --- a/datahub-web/@datahub/utils/addon/components/concurrency-task-state-handler.ts +++ b/datahub-web/@datahub/utils/addon/components/concurrency-task-state-handler.ts @@ -1,12 +1,12 @@ import Component from '@ember/component'; // @ts-ignore: Ignore import of compiled template import template from '../templates/components/concurrency-task-state-handler'; -import { layout, classNames } from '@ember-decorators/component'; +import { layout, tagName } from '@ember-decorators/component'; import { action } from '@ember/object'; import { Task, task } from 'ember-concurrency'; @layout(template) -@classNames('concurrency-task-state-handler') +@tagName('') export default class ConcurrencyTaskStateHandler extends Component { /** * List of arguments to be passed to the task when performed. The array is spread into the task invocation diff --git a/datahub-web/@datahub/utils/addon/templates/components/concurrency-task-state-handler.hbs b/datahub-web/@datahub/utils/addon/templates/components/concurrency-task-state-handler.hbs index 67bf26ece4..f63a0ebfc0 100644 --- a/datahub-web/@datahub/utils/addon/templates/components/concurrency-task-state-handler.hbs +++ b/datahub-web/@datahub/utils/addon/templates/components/concurrency-task-state-handler.hbs @@ -30,7 +30,9 @@ {{else}} {{#if @task.isRunning}} - {{pendulum-ellipsis-animation}} +
+ {{pendulum-ellipsis-animation}} +
{{else}} {{yield}} {{/if}} diff --git a/datahub-web/packages/data-portal/app/typings/app/browse/dynamic-link.d.ts b/datahub-web/@datahub/utils/addon/types/vendor/dynamic-link.d.ts similarity index 91% rename from datahub-web/packages/data-portal/app/typings/app/browse/dynamic-link.d.ts rename to datahub-web/@datahub/utils/addon/types/vendor/dynamic-link.d.ts index a5ded5f473..992ba89162 100644 --- a/datahub-web/packages/data-portal/app/typings/app/browse/dynamic-link.d.ts +++ b/datahub-web/@datahub/utils/addon/types/vendor/dynamic-link.d.ts @@ -2,6 +2,6 @@ export interface IDynamicLinkNode { title: string; text: string; route: Z; - model: T; + model?: T; queryParams?: P; } diff --git a/datahub-web/packages/data-portal/app/components/browser/browse-card.ts b/datahub-web/packages/data-portal/app/components/browser/browse-card.ts index 843e030815..83b5dc7534 100644 --- a/datahub-web/packages/data-portal/app/components/browser/browse-card.ts +++ b/datahub-web/packages/data-portal/app/components/browser/browse-card.ts @@ -1,5 +1,5 @@ import Component from '@ember/component'; -import { IDynamicLinkNode } from 'wherehows-web/typings/app/browse/dynamic-link'; +import { IDynamicLinkNode } from '@datahub/utils/types/vendor/dynamic-link'; /** * Card component. It is used in the home page of the app to diff --git a/datahub-web/packages/data-portal/app/components/browser/search-within-hierarchy.ts b/datahub-web/packages/data-portal/app/components/browser/search-within-hierarchy.ts index 87a9fbe014..f3099a2de2 100644 --- a/datahub-web/packages/data-portal/app/components/browser/search-within-hierarchy.ts +++ b/datahub-web/packages/data-portal/app/components/browser/search-within-hierarchy.ts @@ -2,7 +2,7 @@ import Component from '@ember/component'; import { tagName } from '@ember-decorators/component'; import { computed } from '@ember/object'; import { DataModelEntity } from '@datahub/data-models/constants/entity'; -import { IDynamicLinkNode } from 'wherehows-web/typings/app/browse/dynamic-link'; +import { IDynamicLinkNode } from '@datahub/utils/types/vendor/dynamic-link'; /** * Indicates the total number of entities within a category and on user interaction diff --git a/datahub-web/packages/data-portal/app/components/dataset-authors.ts b/datahub-web/packages/data-portal/app/components/dataset-authors.ts index 9ef19a35fb..883b06a6b4 100644 --- a/datahub-web/packages/data-portal/app/components/dataset-authors.ts +++ b/datahub-web/packages/data-portal/app/components/dataset-authors.ts @@ -296,7 +296,7 @@ export default class DatasetAuthors extends Component { @action confirmSuggestedOwner(this: DatasetAuthors, owner: IOwner): Array | void { const suggestedOwner = { ...owner, source: OwnerSource.Ui }; - return this.actions.addOwner.call(this, suggestedOwner); + return this.addOwner.call(this, suggestedOwner); } /** diff --git a/datahub-web/packages/data-portal/app/components/datasets/containers/dataset-main.ts b/datahub-web/packages/data-portal/app/components/datasets/containers/dataset-main.ts index bd0255e591..2fbfe38719 100644 --- a/datahub-web/packages/data-portal/app/components/datasets/containers/dataset-main.ts +++ b/datahub-web/packages/data-portal/app/components/datasets/containers/dataset-main.ts @@ -200,14 +200,30 @@ export default class DatasetMainContainer extends Component { // TODO should fetch the dataset itself, but right now dataset is fetched at route level const { dataset } = this; - //TODO: META-8267 Container notifications decorator if (dataset) { const entity: DatasetEntity = yield createDatasetEntity(this.dataset.uri, this.dataset as IDatasetApiView); + set(this, 'entity', entity); } }).restartable()) reifyEntityTask!: ETaskPromise; + /** + * This will return the paths for an entity. We should be able to consume entity.readPath + * direcly but since datasets are not migrated we need to flag guard it. + * + * TODO META-8863 Interim implementation to read categories for datasets + */ + @computed('entity') + get paths(): Promise> { + const { entity } = this; + if (!entity) { + return Promise.resolve([]); + } + + return entity.readPath; + } + /** * Converts the uri on a model to a usable URN format * @type {ComputedProperty} diff --git a/datahub-web/packages/data-portal/app/components/nacho/nacho-pagination.ts b/datahub-web/packages/data-portal/app/components/nacho/nacho-pagination.ts index ae13359c2b..b1c5373179 100644 --- a/datahub-web/packages/data-portal/app/components/nacho/nacho-pagination.ts +++ b/datahub-web/packages/data-portal/app/components/nacho/nacho-pagination.ts @@ -1,7 +1,7 @@ import Component from '@ember/component'; import { computed } from '@ember/object'; import { tagName } from '@ember-decorators/component'; -import { IDynamicLinkNode } from 'wherehows-web/typings/app/browse/dynamic-link'; +import { IDynamicLinkNode } from '@datahub/utils/types/vendor/dynamic-link'; /** * Query params with pages for routes diff --git a/datahub-web/packages/data-portal/app/components/search/containers/search-box.ts b/datahub-web/packages/data-portal/app/components/search/containers/search-box.ts index ed60dc054d..999899cc4f 100644 --- a/datahub-web/packages/data-portal/app/components/search/containers/search-box.ts +++ b/datahub-web/packages/data-portal/app/components/search/containers/search-box.ts @@ -4,6 +4,7 @@ import SearchService from 'wherehows-web/services/search'; import { alias } from '@ember/object/computed'; import { grammarProcessingSteps, typeaheadQueryProcessor } from 'wherehows-web/utils/parsers/autocomplete'; import { ISuggestionGroup } from 'wherehows-web/utils/parsers/autocomplete/types'; +import { DatasetEntity } from '@datahub/data-models/entity/dataset/dataset-entity'; import { tagName } from '@ember-decorators/component'; import { computed } from '@ember/object'; import { DataModelEntity, DataModelName } from '@datahub/data-models/constants/entity'; @@ -68,7 +69,7 @@ export default class SearchBoxContainer extends Component { * @param {Entity} [entity] * @memberof SearchBoxContainer */ - onSearch(text: string = '', entity: DataModelEntity['displayName'] = 'datasets'): void { + onSearch(text: string = '', entity: DataModelName = DatasetEntity.displayName): void { // entity (dropdown value) might be different than this.entity (Page that you are in) const dataModelEntity = DataModelEntity[entity]; const { attributes } = dataModelEntity.renderProps.search; diff --git a/datahub-web/packages/data-portal/app/constants/avatars/avatars.ts b/datahub-web/packages/data-portal/app/constants/avatars/avatars.ts index 2d628f3d57..56502633a0 100644 --- a/datahub-web/packages/data-portal/app/constants/avatars/avatars.ts +++ b/datahub-web/packages/data-portal/app/constants/avatars/avatars.ts @@ -17,13 +17,18 @@ const fallback = 'data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAA const makeAvatar = ({ aviUrlPrimary, aviUrlFallback = fallback }: IAppConfig['userEntityProps']): AvatarCreatorFunc => ( object: Partial ): IAvatar => { - const props = pick(object, ['email', 'userName', 'name', 'imageUrl']); - const { userName } = props; - const imageFallback = aviUrlFallback || fallback; + const props = pick(object, ['email', 'userName', 'name', 'imageUrl', 'pictureLink']); + const { userName, pictureLink } = props; + const imageUrlFallback = aviUrlFallback || fallback; + const imageUrl = pictureLink + ? pictureLink + : userName && aviUrlPrimary + ? aviUrlPrimary.replace('[username]', userName) + : imageUrlFallback; return { - imageUrl: userName && aviUrlPrimary ? aviUrlPrimary.replace('[username]', userName) : imageFallback, - imageUrlFallback: imageFallback, + imageUrlFallback, + imageUrl, ...props }; }; diff --git a/datahub-web/packages/data-portal/app/routes/browse/index.ts b/datahub-web/packages/data-portal/app/routes/browse/index.ts index 4e318d53e3..7748aebeea 100644 --- a/datahub-web/packages/data-portal/app/routes/browse/index.ts +++ b/datahub-web/packages/data-portal/app/routes/browse/index.ts @@ -5,7 +5,7 @@ import Search from 'wherehows-web/services/search'; import { set } from '@ember/object'; import { DataModelEntity } from '@datahub/data-models/constants/entity'; import { unGuardedEntities } from 'wherehows-web/utils/entity/flag-guard'; -import { IDynamicLinkNode } from 'wherehows-web/typings/app/browse/dynamic-link'; +import { IDynamicLinkNode } from '@datahub/utils/types/vendor/dynamic-link'; import { capitalize } from '@ember/string'; import { getConfig } from 'wherehows-web/services/configurator'; diff --git a/datahub-web/packages/data-portal/app/styles/components/nacho/_nacho-breadcrumbs.scss b/datahub-web/packages/data-portal/app/styles/components/nacho/_nacho-breadcrumbs.scss index 718cb78ff4..b9a65c9b59 100644 --- a/datahub-web/packages/data-portal/app/styles/components/nacho/_nacho-breadcrumbs.scss +++ b/datahub-web/packages/data-portal/app/styles/components/nacho/_nacho-breadcrumbs.scss @@ -115,6 +115,7 @@ $nacho-breadcrumbs-arrow-padding: 20px; top: $application-navbar-static-height; z-index: z('nav') + z('below'); // To give precendence to tooltip and dropdown width: 100%; + min-height: $nacho-breadcrumbs-height; &--with-banner-offset { top: $banner-alerts-height + $application-navbar-static-height; diff --git a/datahub-web/packages/data-portal/app/templates/browse/entity.hbs b/datahub-web/packages/data-portal/app/templates/browse/entity.hbs index 7b7557d15d..e14db86ab1 100644 --- a/datahub-web/packages/data-portal/app/templates/browse/entity.hbs +++ b/datahub-web/packages/data-portal/app/templates/browse/entity.hbs @@ -2,11 +2,11 @@
{{!-- Dont show breadcrumbs first level since we already have the title and there is real action --}} {{#if (gt model.segments.length 0)}} - {{browser/entity-breadcrumbs - class=(with-banner-offset "nacho-breadcrumbs-container") - entity=(lowercase model.displayName) - segments=model.segments - }} + {{/if}}
diff --git a/datahub-web/packages/data-portal/app/templates/components/dataset-authors.hbs b/datahub-web/packages/data-portal/app/templates/components/dataset-authors.hbs index d4681be967..5d4a539e02 100644 --- a/datahub-web/packages/data-portal/app/templates/components/dataset-authors.hbs +++ b/datahub-web/packages/data-portal/app/templates/components/dataset-authors.hbs @@ -69,17 +69,6 @@
-
- {{datasets/owners/suggested-owners - owners=systemGeneratedOwnersWithAvatars - ownerTypes=ownerTypes - commonOwners=commonOwners - removeOwner=(action "removeOwner") - confirmSuggestedOwner=(action "confirmSuggestedOwner") - updateOwnerType=(action "updateOwnerType") - }} -
-
-
\ No newline at end of file + diff --git a/datahub-web/packages/data-portal/app/templates/components/datasets/containers/dataset-main.hbs b/datahub-web/packages/data-portal/app/templates/components/datasets/containers/dataset-main.hbs index c6b98b40cb..2934f2a452 100644 --- a/datahub-web/packages/data-portal/app/templates/components/datasets/containers/dataset-main.hbs +++ b/datahub-web/packages/data-portal/app/templates/components/datasets/containers/dataset-main.hbs @@ -21,6 +21,7 @@ notifications=notificationsMap headerComponent=headerComponent fields=fields + paths=this.paths tabSelectionChanged=(action tabSelectionChanged) setOwnershipRuleChange=(action setOwnershipRuleChange) ) diff --git a/datahub-web/packages/data-portal/app/templates/components/datasets/dataset-header.hbs b/datahub-web/packages/data-portal/app/templates/components/datasets/dataset-header.hbs index d446701cf1..f44f725e4f 100644 --- a/datahub-web/packages/data-portal/app/templates/components/datasets/dataset-header.hbs +++ b/datahub-web/packages/data-portal/app/templates/components/datasets/dataset-header.hbs @@ -48,22 +48,6 @@ {{/if}} -
- {{#datasets/containers/dataset-fabrics - class="dataset-fabric-row-container" - urn=model.uri - as |fabricsContainer| - }} -
- Fabric: - {{datasets/dataset-fabric-switcher - urn=fabricsContainer.urn - fabrics=fabricsContainer.fabrics - }} -
- {{/datasets/containers/dataset-fabrics}} -
- {{datasets/containers/dataset-owner-list urn=encodedUrn avatarEntityProps=avatarEntityProps diff --git a/datahub-web/packages/data-portal/app/templates/datasets/dataset/tab.hbs b/datahub-web/packages/data-portal/app/templates/datasets/dataset/tab.hbs index be381ce630..5be011994f 100644 --- a/datahub-web/packages/data-portal/app/templates/datasets/dataset/tab.hbs +++ b/datahub-web/packages/data-portal/app/templates/datasets/dataset/tab.hbs @@ -8,11 +8,16 @@ as |container| as |snapshot| }}
- {{browser/entity-breadcrumbs - class=(with-banner-offset "nacho-breadcrumbs-container") - entity=(or (lowercase container.entity.displayName) "--") - segments=container.entity.hierarchySegments - }} + + + +
{{component container.headerComponent diff --git a/datahub-web/packages/data-portal/app/typings/app/avatars.d.ts b/datahub-web/packages/data-portal/app/typings/app/avatars.d.ts index 4cafa5e1e9..c88b63efa5 100644 --- a/datahub-web/packages/data-portal/app/typings/app/avatars.d.ts +++ b/datahub-web/packages/data-portal/app/typings/app/avatars.d.ts @@ -9,6 +9,9 @@ export interface IAvatar { imageUrl: string; // Fallback url for avatar image on error imageUrlFallback: string; + // Alternate url for avatar image + pictureLink?: string; + // Email address for the associated entity if available email?: null | string; // Handle for the avatar userName?: string; diff --git a/datahub-web/packages/data-portal/mirage/config.ts b/datahub-web/packages/data-portal/mirage/config.ts index a9d9d97798..d33586b9ec 100644 --- a/datahub-web/packages/data-portal/mirage/config.ts +++ b/datahub-web/packages/data-portal/mirage/config.ts @@ -23,6 +23,7 @@ import { browse } from 'wherehows-web/mirage/helpers/browse'; import searchResponse from 'wherehows-web/mirage/fixtures/search-response'; import { getSamplePageViewResponse } from 'wherehows-web/mirage/helpers/search/pageview-response'; import { getEntitySearchResults } from 'wherehows-web/mirage/helpers/search/entity'; +import { browsePaths } from 'wherehows-web/mirage/helpers/browse-paths'; export default function(this: IMirageServer): void { this.get('/config', getConfig); @@ -37,6 +38,8 @@ export default function(this: IMirageServer): void { this.get('/browse', browse); + this.get('/browsePaths', browsePaths); + this.get('/datasets/:identifier/', getDatasetView); this.get('/datasets/:identifier/owners', getDatasetOwners); diff --git a/datahub-web/packages/data-portal/mirage/factories/config.ts b/datahub-web/packages/data-portal/mirage/factories/config.ts index f9f91cff9e..44df295f98 100644 --- a/datahub-web/packages/data-portal/mirage/factories/config.ts +++ b/datahub-web/packages/data-portal/mirage/factories/config.ts @@ -47,8 +47,9 @@ export default Factory.extend({ userEntityProps() { return { aviUrlPrimary: - 'https://cinco.corp.linkedin.com/api/profile/[username]/picture?access_token=2rzmbzEMGlHsszQktFY-B1TxUic', - aviUrlFallback: 'https://static.licdn-ei.com/sc/h/djzv59yelk5urv2ujlazfyvrk' + 'https://raw.githubusercontent.com/linkedin/WhereHows/datahub/datahub-web/packages/data-portal/public/assets/images/default_avatar.png', + aviUrlFallback: + 'https://raw.githubusercontent.com/linkedin/WhereHows/datahub/datahub-web/packages/data-portal/public/assets/images/default_avatar.png' }; } }); diff --git a/datahub-web/packages/data-portal/mirage/helpers/browse-paths.ts b/datahub-web/packages/data-portal/mirage/helpers/browse-paths.ts new file mode 100644 index 0000000000..a4524797ec --- /dev/null +++ b/datahub-web/packages/data-portal/mirage/helpers/browse-paths.ts @@ -0,0 +1,11 @@ +import { getDatasetUrnParts } from '@datahub/data-models/entity/dataset/utils/urn'; +import { FabricType } from '@datahub/metadata-types/constants/common/fabric-type'; + +export const browsePaths = (_schema: any, request: any): Array => { + const { + queryParams: { urn } + } = request; + + const { platform, prefix = '', fabric = FabricType.PROD } = getDatasetUrnParts(urn); + return [`${fabric}/${platform}/${prefix}`]; +}; diff --git a/datahub-web/packages/data-portal/tests/acceptance/breadcrumbs-test.ts b/datahub-web/packages/data-portal/tests/acceptance/breadcrumbs-test.ts index 1432323f3e..590866186f 100644 --- a/datahub-web/packages/data-portal/tests/acceptance/breadcrumbs-test.ts +++ b/datahub-web/packages/data-portal/tests/acceptance/breadcrumbs-test.ts @@ -11,14 +11,15 @@ module('Acceptance | breadcrumbs-smoke-test', function(hooks) { test('Breadcrumbs Smoke Test', async function(this: IMirageTestContext, assert) { const categoryLinkClass = '.browse-category__link'; - const breadcrumbsClass = '.nacho-breadcrumbs__crumb'; + const breadCrumbClass = '.nacho-breadcrumbs'; + const breadCrumbsClass = `${breadCrumbClass}__crumb`; defaultScenario(this.server); await appLogin(); await visit('/'); await visit('/browse/datasets?path=hdfs'); await waitFor(categoryLinkClass); let categoryLinks = findAll(categoryLinkClass); - let breadcrumbs = findAll(breadcrumbsClass); + let breadcrumbs = findAll(breadCrumbsClass); assert.equal(currentURL(), '/browse/datasets?path=hdfs'); assert.equal(categoryLinks.length, 1, 'There is 1 folder in root hdfs'); @@ -30,7 +31,7 @@ module('Acceptance | breadcrumbs-smoke-test', function(hooks) { await click(`${categoryLinkClass}:first-child`); await waitFor(categoryLinkClass); categoryLinks = findAll(categoryLinkClass); - breadcrumbs = findAll(breadcrumbsClass); + breadcrumbs = findAll(breadCrumbsClass); assert.equal(currentURL(), '/browse/datasets?path=hdfs%2Fsome'); assert.equal(categoryLinks.length, 1, 'There is 1 folder in path'); @@ -42,7 +43,7 @@ module('Acceptance | breadcrumbs-smoke-test', function(hooks) { await click(`${categoryLinkClass}:first-child`); await waitFor(categoryLinkClass); categoryLinks = findAll(categoryLinkClass); - breadcrumbs = findAll(breadcrumbsClass); + breadcrumbs = findAll(breadCrumbsClass); assert.equal(currentURL(), '/browse/datasets?path=hdfs%2Fsome%2Fpath'); assert.equal(categoryLinks.length, 1, 'There is 1 folder in path'); @@ -54,7 +55,7 @@ module('Acceptance | breadcrumbs-smoke-test', function(hooks) { await click(`${categoryLinkClass}:first-child`); await waitFor(categoryLinkClass, { count: 2 }); categoryLinks = findAll(categoryLinkClass); - breadcrumbs = findAll(breadcrumbsClass); + breadcrumbs = findAll(breadCrumbsClass); assert.equal(currentURL(), '/browse/datasets?path=hdfs%2Fsome%2Fpath%2Fwith'); assert.equal(categoryLinks.length, 2, 'There are 2 folder in path'); @@ -66,7 +67,7 @@ module('Acceptance | breadcrumbs-smoke-test', function(hooks) { await click(`${categoryLinkClass}:first-child`); await waitFor(categoryLinkClass, { count: 2 }); categoryLinks = findAll(categoryLinkClass); - breadcrumbs = findAll(breadcrumbsClass); + breadcrumbs = findAll(breadCrumbsClass); assert.equal(currentURL(), '/browse/datasets?path=hdfs%2Fsome%2Fpath%2Fwith%2Fdirectories'); assert.equal(categoryLinks.length, 2, 'There is only 2 datasets in path'); @@ -76,29 +77,13 @@ module('Acceptance | breadcrumbs-smoke-test', function(hooks) { // path adataset1 await click(`${categoryLinkClass}:first-child`); - breadcrumbs = findAll(breadcrumbsClass); + breadcrumbs = findAll(breadCrumbsClass); assert.equal( currentURL(), '/datasets/urn:li:dataset:(urn:li:dataPlatform:hdfs,%2Fsome%2Fpath%2Fwith%2Fdirectories%2Fadataset1,PROD)/schema' ); - assert.equal(breadcrumbs.length, 7, 'There are 7 links in breadcrumb'); - assert.equal( - getTextNoSpacesFromElements(breadcrumbs), - 'Datasetshdfssomepathwithdirectoriesadataset1', - 'Text match' - ); - - // path /some/path/with/directories - await click(`${breadcrumbsClass}:nth-child(6) a`); - await waitFor(categoryLinkClass, { count: 2 }); - categoryLinks = findAll(categoryLinkClass); - breadcrumbs = findAll(breadcrumbsClass); - - assert.equal(currentURL(), '/browse/datasets?page=1&path=hdfs%2Fsome%2Fpath%2Fwith%2Fdirectories'); - assert.equal(categoryLinks.length, 2, 'There is only 2 datasets in path'); - assert.equal(getTextNoSpacesFromElements(categoryLinks), 'adataset1adataset2', 'Text match'); - assert.equal(breadcrumbs.length, 6, 'There are 6 links in breadcrumb'); - assert.equal(getTextNoSpacesFromElements(breadcrumbs), 'Datasetshdfssomepathwithdirectories', 'Text match'); + assert.equal(breadcrumbs.length, 8, 'There are 8 links in breadcrumb'); + assert.dom(breadCrumbClass).hasText('Datasets PROD hdfs some path with directories adataset1'); }); }); diff --git a/datahub-web/packages/data-portal/tests/integration/components/browser/entity-breadcrumbs-test.ts b/datahub-web/packages/data-portal/tests/integration/components/browser/entity-breadcrumbs-test.ts index f954221dcf..d4b8eed894 100644 --- a/datahub-web/packages/data-portal/tests/integration/components/browser/entity-breadcrumbs-test.ts +++ b/datahub-web/packages/data-portal/tests/integration/components/browser/entity-breadcrumbs-test.ts @@ -6,17 +6,17 @@ import hbs from 'htmlbars-inline-precompile'; const componentClassName = '.nacho-breadcrumbs-container'; const breadcrumbsListClassName = '.nacho-breadcrumbs'; -module('Integration | Component | browser/entity-breadcrumbs', function(hooks) { +module('Integration | Component | browser/entity-breadcrumbs', function(hooks): void { setupRenderingTest(hooks); - test('Breadcrumbs component rendering', async function(assert) { + test('Breadcrumbs component rendering', async function(assert): Promise { const entity = 'Test Entity'; let segments: Array = []; this.setProperties({ segments, entity }); await render(hbs` - {{browser/entity-breadcrumbs segments=segments entity=entity}} + `); assert.dom(componentClassName).isVisible(); diff --git a/datahub-web/packages/data-portal/tests/integration/components/dataset-authors-test.js b/datahub-web/packages/data-portal/tests/integration/components/dataset-authors-test.js index cb40dfa590..3ec15a7b1b 100644 --- a/datahub-web/packages/data-portal/tests/integration/components/dataset-authors-test.js +++ b/datahub-web/packages/data-portal/tests/integration/components/dataset-authors-test.js @@ -4,7 +4,7 @@ import { render, findAll, click } from '@ember/test-helpers'; import hbs from 'htmlbars-inline-precompile'; import { noop } from 'wherehows-web/utils/helpers/functions'; -import { OwnerType, OwnerSource } from 'wherehows-web/utils/api/datasets/owners'; +import { OwnerType } from 'wherehows-web/utils/api/datasets/owners'; import owners from 'wherehows-web/mirage/fixtures/owners'; import userStub from 'wherehows-web/tests/stubs/services/current-user'; import { minRequiredConfirmedOwners } from 'wherehows-web/constants/datasets/owner'; @@ -29,7 +29,7 @@ module('Integration | Component | dataset authors', function(hooks) { this.set('saveOwnerChanges', noop); await render(hbs`{{dataset-authors owners=owners ownerTypes=ownerTypes save=(action saveOwnerChanges)}}`); - assert.equal(findAll('.dataset-author').length, 2, 'expected two dataset author components to be rendered'); + assert.equal(findAll('.dataset-author').length, 1, 'expected two dataset author components to be rendered'); }); test('it should remove an owner when removeOwner is invoked', async function(assert) { @@ -43,39 +43,6 @@ module('Integration | Component | dataset authors', function(hooks) { assert.equal(this.get('owners').length, 0); }); - test('it should update a suggested owner to confirmed', async function(assert) { - assert.expect(3); - const [owner, suggestedOwner] = owners; - const resolvedOwners = [owner]; - const suggestedOwners = [suggestedOwner]; - - const initialLength = resolvedOwners.length; - let userName, confirmedOwner; - - this.set('owners', resolvedOwners); - this.set('ownerTypes', ownerTypes); - this.set('saveOwnerChanges', noop); - this.set('suggestedOwners', suggestedOwners); - await render( - hbs`{{dataset-authors owners=owners suggestedOwners=suggestedOwners ownerTypes=ownerTypes save=(action saveOwnerChanges)}}` - ); - - assert.equal( - this.get('owners.length'), - initialLength, - `the list of owners is ${initialLength} before adding confirmed owner` - ); - - await click('.dataset-authors-suggested__info__trigger'); - await click('.suggested-owner-card__owner-info__add button'); - - userName = this.get('current-user.currentUser.userName'); - confirmedOwner = this.get('owners').findBy('confirmedBy', userName); - - assert.equal(this.get('owners.length'), initialLength + 1, 'the list of owner contains one more new owner'); - assert.equal(confirmedOwner.source, OwnerSource.Ui, 'contains a new owner with ui source'); - }); - test('it should disable the save button when confirmedOwners is less than required minimum', async function(assert) { assert.expect(2); this.set('owners', [confirmedOwner]);