From e2f985a2986fa4aee9c357501d4e4c3b1d8d73a6 Mon Sep 17 00:00:00 2001 From: Ignacio Bona Date: Wed, 4 Sep 2019 21:46:02 -0700 Subject: [PATCH] Manual merge with more internal changes --- .../addon/constants/entity/index.ts | 5 +- .../addon/constants/entity/person/links.ts | 1 + .../addon/entity/person/person-entity.ts | 150 +++++- .../addon/entity/person/render-props.ts | 59 +++ .../addon/utils/entity-route-name-resolver.ts | 39 ++ datahub-web/@datahub/data-models/package.json | 2 +- datahub-web/@datahub/data-models/testem.js | 3 +- .../@datahub/data-models/tsconfig.json | 2 +- .../types/entity/containers/index.d.ts | 4 +- .../rendering/search-entity-render-prop.d.ts | 8 +- .../data-models/types/entity/shared.d.ts | 2 +- datahub-web/@datahub/datasets-core/testem.js | 3 +- .../components/entity-deprecation.hbs | 12 +- .../app/styles/entity-deprecation.scss | 2 +- .../@datahub/entity-deprecation/package.json | 2 +- .../@datahub/entity-deprecation/testem.js | 3 +- .../_entity-title.scss | 1 + .../entity-header-layout/_entity-header.scss | 4 + datahub-web/@datahub/entity-header/testem.js | 3 +- .../institutional-memory/containers/tab.ts | 7 +- .../@datahub/institutional-memory/testem.js | 3 +- .../addon/components/entity-list-container.ts | 5 +- .../lists/addon/constants/entity/shared.ts | 3 +- .../addon/services/entity-lists-manager.ts | 23 +- datahub-web/@datahub/lists/testem.js | 3 +- datahub-web/@datahub/metadata-types/testem.js | 3 +- .../@datahub/metadata-types/tsconfig.json | 4 +- .../shared/addon/components/entity-pill.ts | 9 + .../shared/addon/services/current-user.ts | 3 +- .../templates/components/entity-pill.hbs | 9 + .../shared/app/components/entity-pill.js | 1 + .../shared/app/styles/datahub-shared.scss | 1 + .../shared/app/styles/entity-pill/_all.scss | 1 + .../app/styles/entity-pill/_entity-pill.scss | 9 + datahub-web/@datahub/shared/package.json | 3 +- .../components/entity-pill-test.ts | 52 ++ .../types/configurator/configurator.d.ts | 55 +++ .../shared/types/configurator/tracking.d.ts | 19 + .../addon/components/track-ui-event.ts | 79 +++ .../constants/event-tracking/compliance.ts | 80 ++++ .../addon/constants}/event-tracking/index.ts | 17 +- .../addon/constants}/event-tracking/search.ts | 4 +- .../site-search-tracking/adapters.ts | 0 .../constants}/site-search-tracking/index.ts | 0 .../addon/services/unified-tracking.ts | 136 +++++- .../templates/components/track-ui-event.hbs | 5 + .../tracking/addon/utils}/dwell-time.ts | 14 +- .../@datahub/tracking/addon/utils/piwik.ts | 20 + .../tracking/app/components/track-ui-event.js | 1 + .../@datahub/tracking/config/environment.js | 10 +- datahub-web/@datahub/tracking/package.json | 8 +- datahub-web/@datahub/tracking/testem.js | 3 +- .../components/track-ui-event-test.ts | 92 ++++ .../tracking/tests/stubs/services/metrics.ts | 12 + .../@datahub/tracking/tests/test-helper.ts | 1 + .../unit/services/unified-tracking-test.ts} | 16 +- .../tests/unit/utils}/dwell-time-test.ts | 12 +- datahub-web/@datahub/tracking/tsconfig.json | 14 +- .../tracking/types}/event-tracking.d.ts | 2 +- .../@datahub/tracking/types/search/index.ts | 13 + .../tracking/{ => types}/vendor/.gitkeep | 0 .../@datahub/tracking/types/vendor/piwik.d.ts | 8 + datahub-web/@datahub/user/.ember-cli | 9 +- .../user/addon/mocks/models/dataset-entity.ts | 22 + .../user/addon/mocks/person-entity.ts | 31 ++ .../@datahub/user/app/styles/user-entity.scss | 1 + .../@datahub/user/app/styles/user/_all.scss | 1 + .../user/app/styles/user/profile/_all.scss | 3 + .../app/styles/user/profile/_content.scss | 58 +++ .../app/styles/user/profile/_focus-area.scss | 27 ++ .../user/app/styles/user/profile/_header.scss | 136 ++++++ .../user/app/templates/user/profile.js | 1 + datahub-web/@datahub/user/package.json | 10 + datahub-web/@datahub/user/testem.js | 3 +- .../dummy/app/mocks/models/dataset-entity.ts | 8 + .../@datahub/user/tests/dummy/app/router.js | 27 ++ .../@datahub/user/tests/dummy/app/router.ts | 11 - .../user/tests/dummy/app/styles/app.scss | 4 + .../user/tests/unit/routes/user-test.ts | 11 + datahub-web/@datahub/user/tsconfig.json | 2 +- .../@datahub/utils/addon/api/fetcher.ts | 2 +- .../addon/components/nacho-hover-dropdown.ts | 9 +- .../@datahub/utils/addon/helpers/wait-time.ts | 12 + .../@datahub/utils/addon/routes/routing.ts | 25 + .../utils/addon/services/notifications.ts | 5 +- .../addon/test-helpers/test-exception.ts | 33 ++ .../utils/addon/types/vendor/routerjs.d.ts | 13 + .../@datahub/utils/addon/validators/object.ts | 2 +- datahub-web/@datahub/utils/testem.js | 3 +- .../test-for/decorators/data-source.ts | 5 +- .../@datahub/utils/types/concurrency.d.ts | 23 + .../files/__group__/__template__/testem.js | 3 +- datahub-web/configs/testem-base.js | 3 - .../app/components/avatars/avatar-image.ts | 6 +- .../browser/containers/entity-categories.ts | 9 +- .../containers/entity-category-count.ts | 6 +- .../app/components/dataset-authors.ts | 10 +- .../app/components/dataset-deprecation.ts | 152 ------ .../datasets/containers/dataset-fabrics.ts | 6 +- .../containers/dataset-lineage-downstreams.ts | 5 +- .../containers/dataset-lineage-upstreams.ts | 5 +- .../datasets/containers/dataset-main.ts | 44 +- .../datasets/containers/dataset-owner-list.ts | 10 +- .../datasets/containers/dataset-ownership.ts | 25 +- .../datasets/containers/dataset-properties.ts | 6 +- .../containers/dataset-relationship-levels.ts | 46 +- .../datasets/containers/dataset-schema.ts | 6 +- .../datasets/containers/dataset-snapshot.ts | 6 +- .../datasets/containers/upstream-owners.ts | 6 +- .../datasets/dataset-fabric-switcher.ts | 14 +- .../datasets/dataset-relationships.ts | 4 +- .../nacho/dropdown/hover-dropdown.ts | 13 +- .../data-portal/app/components/nav-link.ts | 20 - .../data-portal/app/components/navbar.ts | 9 +- .../app/components/schema-comment.ts | 9 +- .../containers/entity-task-container.ts | 41 +- .../search/containers/search-box.ts | 13 +- .../search/containers/search-facets.ts | 43 +- .../app/components/search/search-box.ts | 84 ++-- .../app/components/track-ui-event.ts | 71 --- .../analytics/event-tracking/entity.ts | 40 -- .../app/constants/avatars/avatars.ts | 2 +- .../packages/data-portal/app/router.ts | 43 -- .../data-portal/app/routes/application.ts | 84 ++-- .../data-portal/app/routes/browse/index.ts | 4 +- .../packages/data-portal/app/routes/index.ts | 4 +- .../app/routes/{login.js => login.ts} | 38 +- .../data-portal/app/services/banners.ts | 6 +- .../data-portal/app/services/configurator.ts | 74 +-- .../data-portal/app/services/search.ts | 31 +- .../data-portal/app/services/tracking.ts | 36 -- .../packages/data-portal/app/styles/app.scss | 3 + .../app/styles/components/_all.scss | 2 - .../components/comments/_comment-new.scss | 2 +- .../components/dataset-acl-access/_all.scss | 1 - .../_dataset-acl-access.scss | 128 ----- .../app/styles/components/search/_search.scss | 4 +- .../toggle-switch/_toggle-switch.scss | 2 + .../styles/components/ump-metrics/_all.scss | 3 - .../ump-metrics/_entity-header.scss | 27 -- .../ump-metrics/_metrics-profile-layout.scss | 3 - .../components/ump-metrics/_tables.scss | 11 - .../data-portal/app/templates/application.hbs | 1 - .../components/comment/comment-item.hbs | 10 +- .../components/comment/comment-new.hbs | 10 +- .../templates/components/dataset-access.hbs | 37 -- .../components/dataset-deprecation.hbs | 138 ------ .../components/search/search-main.hbs | 2 +- .../components/search/search-result.hbs | 2 +- .../templates/components/track-ui-event.hbs | 5 - .../data-portal/app/templates/search.hbs | 2 +- .../api/configurator/configurator.d.ts | 74 --- .../data-portal/app/typings/app/routes.d.ts | 8 - .../data-portal/app/typings/app/services.d.ts | 2 - .../app/typings/modules/routerjs.d.ts | 451 ------------------ .../app/typings/untyped-js-module.d.ts | 6 - .../data-portal/app/utils/analytics/piwik.ts | 25 - .../analytics/search/track-site-search.ts | 40 -- .../data-portal/app/utils/api/shared.ts | 4 +- .../app/utils/entity/flag-guard.ts | 13 +- .../data-portal/app/utils/helpers/routes.ts | 69 --- .../data-portal/config/dependency-lint.js | 1 - .../data-portal/config/environment.js | 8 - .../data-portal/mirage/factories/access.ts | 3 - .../data-portal/mirage/models/access.ts | 3 - datahub-web/packages/data-portal/package.json | 2 +- datahub-web/packages/data-portal/testem.js | 3 +- .../tests/acceptance/analytics/search-test.ts | 12 +- .../tests/acceptance/breadcrumbs-test.ts | 12 +- .../tests/acceptance/tracking-test.ts | 2 +- .../tests/helpers/analytics/index.ts | 4 +- .../tests/helpers/search/search-acceptance.ts | 8 +- .../components/dataset-deprecation-test.ts | 97 ---- .../search/containers/search-box-test.ts | 2 +- .../components/search/search-box-test.ts | 2 +- .../tests/stubs/routes/route-info.ts | 7 +- .../tests/stubs/services/metrics.ts | 12 - .../unit/utils/entity/flag-guard-test.ts | 41 -- .../tests/unit/utils/helpers/routes-test.ts | 3 +- datahub-web/yarn.lock | 62 ++- 180 files changed, 1858 insertions(+), 2110 deletions(-) create mode 100644 datahub-web/@datahub/data-models/addon/entity/person/render-props.ts create mode 100644 datahub-web/@datahub/data-models/addon/utils/entity-route-name-resolver.ts create mode 100644 datahub-web/@datahub/shared/addon/components/entity-pill.ts create mode 100644 datahub-web/@datahub/shared/addon/templates/components/entity-pill.hbs create mode 100644 datahub-web/@datahub/shared/app/components/entity-pill.js create mode 100644 datahub-web/@datahub/shared/app/styles/datahub-shared.scss create mode 100644 datahub-web/@datahub/shared/app/styles/entity-pill/_all.scss create mode 100644 datahub-web/@datahub/shared/app/styles/entity-pill/_entity-pill.scss create mode 100644 datahub-web/@datahub/shared/tests/integration/components/entity-pill-test.ts create mode 100644 datahub-web/@datahub/shared/types/configurator/configurator.d.ts create mode 100644 datahub-web/@datahub/shared/types/configurator/tracking.d.ts create mode 100644 datahub-web/@datahub/tracking/addon/components/track-ui-event.ts create mode 100644 datahub-web/@datahub/tracking/addon/constants/event-tracking/compliance.ts rename datahub-web/{packages/data-portal/app/constants/analytics => @datahub/tracking/addon/constants}/event-tracking/index.ts (53%) rename datahub-web/{packages/data-portal/app/constants/analytics => @datahub/tracking/addon/constants}/event-tracking/search.ts (71%) rename datahub-web/{packages/data-portal/app/constants/analytics => @datahub/tracking/addon/constants}/site-search-tracking/adapters.ts (100%) rename datahub-web/{packages/data-portal/app/constants/analytics => @datahub/tracking/addon/constants}/site-search-tracking/index.ts (100%) create mode 100644 datahub-web/@datahub/tracking/addon/templates/components/track-ui-event.hbs rename datahub-web/{packages/data-portal/app/utils/analytics/search => @datahub/tracking/addon/utils}/dwell-time.ts (92%) create mode 100644 datahub-web/@datahub/tracking/addon/utils/piwik.ts create mode 100644 datahub-web/@datahub/tracking/app/components/track-ui-event.js create mode 100644 datahub-web/@datahub/tracking/tests/integration/components/track-ui-event-test.ts create mode 100644 datahub-web/@datahub/tracking/tests/stubs/services/metrics.ts rename datahub-web/{packages/data-portal/tests/unit/services/tracking-test.ts => @datahub/tracking/tests/unit/services/unified-tracking-test.ts} (64%) rename datahub-web/{packages/data-portal/tests/unit/utils/analytics/search => @datahub/tracking/tests/unit/utils}/dwell-time-test.ts (80%) rename datahub-web/{packages/data-portal/app/typings/app/analytics => @datahub/tracking/types}/event-tracking.d.ts (91%) create mode 100644 datahub-web/@datahub/tracking/types/search/index.ts rename datahub-web/@datahub/tracking/{ => types}/vendor/.gitkeep (100%) create mode 100644 datahub-web/@datahub/tracking/types/vendor/piwik.d.ts create mode 100644 datahub-web/@datahub/user/addon/mocks/models/dataset-entity.ts create mode 100644 datahub-web/@datahub/user/addon/mocks/person-entity.ts create mode 100644 datahub-web/@datahub/user/app/styles/user-entity.scss create mode 100644 datahub-web/@datahub/user/app/styles/user/_all.scss create mode 100644 datahub-web/@datahub/user/app/styles/user/profile/_all.scss create mode 100644 datahub-web/@datahub/user/app/styles/user/profile/_content.scss create mode 100644 datahub-web/@datahub/user/app/styles/user/profile/_focus-area.scss create mode 100644 datahub-web/@datahub/user/app/styles/user/profile/_header.scss create mode 100644 datahub-web/@datahub/user/app/templates/user/profile.js create mode 100644 datahub-web/@datahub/user/tests/dummy/app/mocks/models/dataset-entity.ts create mode 100644 datahub-web/@datahub/user/tests/dummy/app/router.js delete mode 100644 datahub-web/@datahub/user/tests/dummy/app/router.ts create mode 100644 datahub-web/@datahub/user/tests/unit/routes/user-test.ts create mode 100644 datahub-web/@datahub/utils/addon/helpers/wait-time.ts create mode 100644 datahub-web/@datahub/utils/addon/routes/routing.ts create mode 100644 datahub-web/@datahub/utils/addon/test-helpers/test-exception.ts create mode 100644 datahub-web/@datahub/utils/addon/types/vendor/routerjs.d.ts create mode 100644 datahub-web/@datahub/utils/types/concurrency.d.ts delete mode 100644 datahub-web/packages/data-portal/app/components/dataset-deprecation.ts delete mode 100644 datahub-web/packages/data-portal/app/components/track-ui-event.ts delete mode 100644 datahub-web/packages/data-portal/app/constants/analytics/event-tracking/entity.ts rename datahub-web/packages/data-portal/app/routes/{login.js => login.ts} (65%) delete mode 100644 datahub-web/packages/data-portal/app/services/tracking.ts delete mode 100644 datahub-web/packages/data-portal/app/styles/components/dataset-acl-access/_all.scss delete mode 100644 datahub-web/packages/data-portal/app/styles/components/dataset-acl-access/_dataset-acl-access.scss delete mode 100644 datahub-web/packages/data-portal/app/styles/components/ump-metrics/_all.scss delete mode 100644 datahub-web/packages/data-portal/app/styles/components/ump-metrics/_entity-header.scss delete mode 100644 datahub-web/packages/data-portal/app/styles/components/ump-metrics/_metrics-profile-layout.scss delete mode 100644 datahub-web/packages/data-portal/app/styles/components/ump-metrics/_tables.scss delete mode 100644 datahub-web/packages/data-portal/app/templates/components/dataset-access.hbs delete mode 100644 datahub-web/packages/data-portal/app/templates/components/dataset-deprecation.hbs delete mode 100644 datahub-web/packages/data-portal/app/templates/components/track-ui-event.hbs delete mode 100644 datahub-web/packages/data-portal/app/typings/api/configurator/configurator.d.ts delete mode 100644 datahub-web/packages/data-portal/app/typings/app/routes.d.ts delete mode 100644 datahub-web/packages/data-portal/app/typings/modules/routerjs.d.ts delete mode 100644 datahub-web/packages/data-portal/app/utils/analytics/piwik.ts delete mode 100644 datahub-web/packages/data-portal/app/utils/analytics/search/track-site-search.ts delete mode 100644 datahub-web/packages/data-portal/app/utils/helpers/routes.ts delete mode 100644 datahub-web/packages/data-portal/mirage/factories/access.ts delete mode 100644 datahub-web/packages/data-portal/mirage/models/access.ts delete mode 100644 datahub-web/packages/data-portal/tests/integration/components/dataset-deprecation-test.ts delete mode 100644 datahub-web/packages/data-portal/tests/stubs/services/metrics.ts delete mode 100644 datahub-web/packages/data-portal/tests/unit/utils/entity/flag-guard-test.ts 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 ac3add8ea5..5a70853d87 100644 --- a/datahub-web/@datahub/data-models/addon/constants/entity/index.ts +++ b/datahub-web/@datahub/data-models/addon/constants/entity/index.ts @@ -1,4 +1,5 @@ import { DatasetEntity } from '@datahub/data-models/entity/dataset/dataset-entity'; +import { PersonEntity } from '@datahub/data-models/entity/person/person-entity'; /** * Defines the interface for the DataModelEntity enum below. @@ -6,6 +7,7 @@ import { DatasetEntity } from '@datahub/data-models/entity/dataset/dataset-entit */ interface IDataModelEntity { [DatasetEntity.displayName]: typeof DatasetEntity; + [PersonEntity.displayName]: typeof PersonEntity; } /** @@ -13,7 +15,8 @@ interface IDataModelEntity { * Serves as the primary resource map of all DataModelEntity available classes */ export const DataModelEntity: IDataModelEntity = { - [DatasetEntity.displayName]: DatasetEntity + [DatasetEntity.displayName]: DatasetEntity, + [PersonEntity.displayName]: 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 index a4ae07e899..3bd7c19f21 100644 --- a/datahub-web/@datahub/data-models/addon/constants/entity/person/links.ts +++ b/datahub-web/@datahub/data-models/addon/constants/entity/person/links.ts @@ -2,5 +2,6 @@ * 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/person/person-entity.ts b/datahub-web/@datahub/data-models/addon/entity/person/person-entity.ts index 8308e620e0..25d4d8b391 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,33 +1,163 @@ 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, + IPersonEntitySpecificConfigs, + getPersonEntitySpecificRenderProps +} from '@datahub/data-models/entity/person/render-props'; +import { DatasetEntity } from '@datahub/data-models/entity/dataset/dataset-entity'; +import { BaseEntity, statics, IBaseEntityStatics } from '@datahub/data-models/entity/base-entity'; +import { IBaseEntity } from '@datahub/metadata-types/types/entity'; +import { IEntityRenderProps } from '@datahub/data-models/types/entity/rendering/entity-render-props'; +import { DataModelEntity } from '@datahub/data-models/constants/entity'; + +// TODO: [META-9699] Temporarily using IBaseEntity until we have a proposed API structure for +// IPersonEntity +@statics>() +export class PersonEntity extends BaseEntity { + /** + * The human friendly alias for Dataset entities + */ + static displayName: 'people' = 'people'; -export class PersonEntity { /** * Static util function that can extract a username from the urn for a person entity using whatever * custom logic is necessary to accomplish this * @param urn - person entity identifier + * @deprecated + * Should be removed as part of open source. Definition will be on LiPersonEntity + * TODO: [META-9698] Migrate to using LiPersonEntity */ static usernameFromUrn(urn: string): string { return getActorFromUrn(urn); } + /** + * Static util function that can reverse the extraction of a username from urn for a person + * entity and return to a urn (assuming the two are different) + * IMPLEMENTATION NEEDED - This is only an open source interface definition + * @param {string} username - the username to be converted + * @static + */ + static urnFromUsername: (username: string) => string; + /** * Static util function that can provide a profile page link for a particular username * @param username - username for the person entity. Can be different from urn + * @deprecated + * Should be removed as part of open source. Definition will be on LiPersonEntity + * TODO: [META-9698] Migrate to using LiPersonEntity */ static profileLinkFromUsername(username: string): string { return `${profileLinkBase}${username}`; } /** - * Identifier for the person entity + * 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 */ - urn: string; + static get renderProps(): IEntityRenderProps { + return getRenderProps(); + } + + static ownershipEntities: Array<{ entity: DataModelEntity; getter: keyof PersonEntity }> = [ + { entity: DatasetEntity, getter: 'readDatasetOwnership' } + ]; + + /** + * Properties for render props that are only applicable to the person entity. Dictates how UI + * components should be rendered for this entity + */ + static get personEntityRenderProps(): IPersonEntitySpecificConfigs { + return getPersonEntitySpecificRenderProps(); + } + + /** + * Combined render properties for the generic entity render props + all person entity specific + * render properties + */ + static get allRenderProps(): IEntityRenderProps & IPersonEntitySpecificConfigs { + return { ...getRenderProps(), ...getPersonEntitySpecificRenderProps() }; + } + + /** + * Allows access to the static display name of the entity from an instance + */ + get displayName(): 'people' { + return PersonEntity.displayName; + } + + /** + * The person's human readable name + */ + name!: string; + + /** + * The person's title at the company + */ + title!: string; + + /** + * Url link to the person's profile picture + */ + profilePictureUrl!: string; + + /** + * identifier for the person that this person reports to + */ + reportsToUrn?: string; + + /** + * Actual reference to related entity for this person + */ + reportsTo?: PersonEntity; + + /** + * User's email address + */ + email!: string; + + /** + * A list of skills that this particular person entity has declared to own. + */ + skills: Array = []; + + /** + * A link to the user's linkedin profile + */ + linkedinProfile?: string; + + /** + * A link to the user through slack + */ + slackLink?: string; + + /** + * List of datasets owned by this particular user entity + */ + datasetOwnership?: Array; + + /** + * User-provided focus area, describing themselves and what they do + */ + focusArea: string = ''; + + /** + * Tags that in aggregate denote which team and organization to which the user belongs + */ + teamTags: Array = []; /** * Computes the username for easy access from the urn * @type {string} + * @deprecated + * Should be removed in favor of adding this to internal version of the class + * TODO: [META-9698] Migrate to using LiPersonEntity */ @computed('urn') get username(): string { @@ -43,7 +173,17 @@ export class PersonEntity { return PersonEntity.profileLinkFromUsername(this.username); } - constructor(urn: string) { - this.urn = urn; + /** + * Retrieves the basic entity information for the person + */ + get readEntity(): Promise { + throw new Error(NotImplementedError); + } + + /** + * Reads the datasets for which this person entity has ownership. + */ + readDatasetOwnership(): Promise> { + throw new Error(NotImplementedError); } } diff --git a/datahub-web/@datahub/data-models/addon/entity/person/render-props.ts b/datahub-web/@datahub/data-models/addon/entity/person/render-props.ts new file mode 100644 index 0000000000..f35b5c514d --- /dev/null +++ b/datahub-web/@datahub/data-models/addon/entity/person/render-props.ts @@ -0,0 +1,59 @@ +import { IEntityRenderProps } from '@datahub/data-models/types/entity/rendering/entity-render-props'; +import { Tab } from '@datahub/data-models/constants/entity/shared/tabs'; +import { getTabPropertiesFor } from '@datahub/data-models/entity/utils'; + +/** + * Specific render properties only to the person entity + */ +export interface IPersonEntitySpecificConfigs { + userProfilePage: { + headerProperties: { + showExternalProfileLink?: boolean; + externalProfileLinkText?: string; + isConnectedToLinkedin?: boolean; + isConnectedToSlack?: boolean; + }; + }; +} + +/** + * Class properties common across instances + * Dictates how visual ui components should be rendered + * Implemented as a getter to ensure that reads are idempotent + */ +export const getRenderProps = (): IEntityRenderProps => { + const tabIds = [Tab.Metadata]; + + return { + entityPage: { + tabIds, + tabProperties: getTabPropertiesFor(tabIds), + defaultTab: Tab.Metadata, + attributePlaceholder: '–' + }, + // Placeholder information + search: { + attributes: [], + placeholder: '', + apiName: '' + }, + // Placeholder information + browse: { + showCount: false, + showHierarchySearch: false, + entityRoute: 'user.profile' + } + }; +}; + +/** + * Properties for render props that are only applicable to the person entity. Dictates how UI + * components should be rendered for this entity + */ +export const getPersonEntitySpecificRenderProps = (): IPersonEntitySpecificConfigs => ({ + userProfilePage: { + headerProperties: { + showExternalProfileLink: false + } + } +}); diff --git a/datahub-web/@datahub/data-models/addon/utils/entity-route-name-resolver.ts b/datahub-web/@datahub/data-models/addon/utils/entity-route-name-resolver.ts new file mode 100644 index 0000000000..be38dcb07a --- /dev/null +++ b/datahub-web/@datahub/data-models/addon/utils/entity-route-name-resolver.ts @@ -0,0 +1,39 @@ +import { resolveDynamicRouteName } from '@datahub/utils/routes/routing'; +import { DatasetEntity } from '@datahub/data-models/entity/dataset/dataset-entity'; +import { MaybeRouteInfoWithAttributes } from '@datahub/utils/types/vendor/routerjs'; +import { listOfEntitiesMap } from '@datahub/data-models/entity/utils/entities'; +import Transition from '@ember/routing/-private/transition'; +import { DataModelEntity } from '@datahub/data-models/constants/entity'; + +/** + * Indexes the route names we care about to functions that resolve the placeholder value + * defaults to the route.name, if a resolved value cannot be determined + * @type Record string) | undefined> + */ +export const mapOfRouteNamesToResolver: Record string) | void> = { + 'browse.entity': (route: MaybeRouteInfoWithAttributes): string => + route.attributes ? `browse.${route.attributes.entity}` : route.name, + 'browse.entity.index': (route: MaybeRouteInfoWithAttributes): string => + route.attributes ? `browse.${route.attributes.entity}` : route.name, + 'datasets.dataset.tab': (route: MaybeRouteInfoWithAttributes): string => + route.attributes ? `${DatasetEntity.displayName}.${route.attributes.currentTab}` : route.name +}; + +/** + * Guard checks that a route name is an entity route by testing if the routeName begins with the entity name + * @param {string} routeName the name of the route to check against + * @returns {boolean} + */ +const routeNameIsEntityRoute = (routeName: string): boolean => + listOfEntitiesMap((e): DataModelEntity['displayName'] => e.displayName).some( + (entityName: DataModelEntity['displayName']): boolean => routeName.startsWith(entityName) + ); + +/** + * Check if the route info instance has a name that is considered an entity route + * @returns {boolean} + */ +export const isRouteEntityPageRoute = (routeBeingTransitionedTo: Transition['to' | 'from']): boolean => { + const routeName = resolveDynamicRouteName(mapOfRouteNamesToResolver, routeBeingTransitionedTo); + return Boolean(routeName && routeNameIsEntityRoute(routeName)); +}; diff --git a/datahub-web/@datahub/data-models/package.json b/datahub-web/@datahub/data-models/package.json index ad6d835429..e0ccb7ef67 100644 --- a/datahub-web/@datahub/data-models/package.json +++ b/datahub-web/@datahub/data-models/package.json @@ -24,6 +24,7 @@ "postpublish": "ember ts:clean" }, "dependencies": { + "@datahub/metadata-types": "0.0.0", "@datahub/utils": "0.0.0", "ember-cli-babel": "^7.8.0", "ember-cli-typescript": "^2.0.2", @@ -32,7 +33,6 @@ }, "devDependencies": { "@babel/core": "^7.4.0", - "@datahub/metadata-types": "0.0.0", "@ember/optional-features": "^0.7.0", "@types/ember": "^3.1.0", "@types/ember-qunit": "^3.4.6", diff --git a/datahub-web/@datahub/data-models/testem.js b/datahub-web/@datahub/data-models/testem.js index 55b4ad8366..3c17a8b75d 100644 --- a/datahub-web/@datahub/data-models/testem.js +++ b/datahub-web/@datahub/data-models/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/data-models/tsconfig.json b/datahub-web/@datahub/data-models/tsconfig.json index 6993beaed4..7ddf3bc17c 100644 --- a/datahub-web/@datahub/data-models/tsconfig.json +++ b/datahub-web/@datahub/data-models/tsconfig.json @@ -28,6 +28,6 @@ "../../@datahub/metadata-types/addon/**/*", "../../@datahub/metadata-types/types/**/*", "../../@datahub/utils/addon/**/*", - "../../@datahub/utils/types/**/*", + "../../@datahub/utils/types/**/*" ] } diff --git a/datahub-web/@datahub/data-models/types/entity/containers/index.d.ts b/datahub-web/@datahub/data-models/types/entity/containers/index.d.ts index 11497373ad..b585dfe30f 100644 --- a/datahub-web/@datahub/data-models/types/entity/containers/index.d.ts +++ b/datahub-web/@datahub/data-models/types/entity/containers/index.d.ts @@ -1,5 +1,5 @@ import { ITabProperties, Tab } from '@datahub/data-models/constants/entity/shared/tabs'; -import { Task } from 'ember-concurrency'; +import { ETaskPromise } from '@datahub/utils/types/concurrency'; /** * Defines expected container properties and methods for an Entity Container Component @@ -17,5 +17,5 @@ export interface IEntityContainer { // Tabs that are available for the entity tabs: Array; // concurrency task to materialize the related underlying IEntity - reifyEntityTask: Task, () => Promise>; + reifyEntityTask: ETaskPromise; } diff --git a/datahub-web/@datahub/data-models/types/entity/rendering/search-entity-render-prop.d.ts b/datahub-web/@datahub/data-models/types/entity/rendering/search-entity-render-prop.d.ts index 34d818eeca..d21f1bedd8 100644 --- a/datahub-web/@datahub/data-models/types/entity/rendering/search-entity-render-prop.d.ts +++ b/datahub-web/@datahub/data-models/types/entity/rendering/search-entity-render-prop.d.ts @@ -14,8 +14,6 @@ export interface ISearchEntityRenderProps { showInFacets: boolean; // Flag indicating that this search entity attribute should be shown as a search result tag or not showAsTag?: boolean; - // Component that can serve different purposes when default rendering options are not enough - component?: string; // An alternative string representation of the fieldName attribute that's more human-friendly e.g. Data Origin displayName: string; // A description of the search attribute @@ -38,4 +36,10 @@ export interface ISearchEntityRenderProps { iconName?: string; // Optional text that is shown over hovering of the element, to provide more meaning and context hoverText?: string; + // Component that can serve different purposes when default rendering options are not enough + // attributes is an optional object used to provide additional rendering information about the component + component?: { + name: string; + attrs?: Record; + }; } diff --git a/datahub-web/@datahub/data-models/types/entity/shared.d.ts b/datahub-web/@datahub/data-models/types/entity/shared.d.ts index 29c4d5557a..47a9c6f576 100644 --- a/datahub-web/@datahub/data-models/types/entity/shared.d.ts +++ b/datahub-web/@datahub/data-models/types/entity/shared.d.ts @@ -4,7 +4,7 @@ import { IBaseEntity } from '@datahub/metadata-types/types/entity'; /** * String literal of available entity routes */ -export type EntityRoute = 'browse.entity' | 'features.feature' | 'datasets.dataset' | 'metrics.metric'; +export type EntityRoute = string; /** * Properties that enable a dynamic link to the entity or category diff --git a/datahub-web/@datahub/datasets-core/testem.js b/datahub-web/@datahub/datasets-core/testem.js index 55b4ad8366..3c17a8b75d 100644 --- a/datahub-web/@datahub/datasets-core/testem.js +++ b/datahub-web/@datahub/datasets-core/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/entity-deprecation/addon/templates/components/entity-deprecation.hbs b/datahub-web/@datahub/entity-deprecation/addon/templates/components/entity-deprecation.hbs index 296dc3f755..ed26ce58d6 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 @@ -20,11 +20,11 @@ {{#if isDeprecatedAlias}} - {{medium-editor - deprecationNoteAlias - options=editorOptions +

When should this {{entityName}} be decommissioned?

@@ -40,9 +40,9 @@ {{#dropdown.content class="entity-deprecation__cal-dropdown"}} @@ -107,4 +107,4 @@ -{{yield}} \ No newline at end of file +{{yield}} diff --git a/datahub-web/@datahub/entity-deprecation/app/styles/entity-deprecation.scss b/datahub-web/@datahub/entity-deprecation/app/styles/entity-deprecation.scss index 3b261deaf8..a5e1820725 100644 --- a/datahub-web/@datahub/entity-deprecation/app/styles/entity-deprecation.scss +++ b/datahub-web/@datahub/entity-deprecation/app/styles/entity-deprecation.scss @@ -71,7 +71,7 @@ } } - &__note-editor { + &__note-editor .ember-medium-editor__container { min-height: item-spacing(6) * 4; outline: none; padding: item-spacing(1); diff --git a/datahub-web/@datahub/entity-deprecation/package.json b/datahub-web/@datahub/entity-deprecation/package.json index 413d7e9252..81ea48f449 100644 --- a/datahub-web/@datahub/entity-deprecation/package.json +++ b/datahub-web/@datahub/entity-deprecation/package.json @@ -27,7 +27,7 @@ "ember-cli-moment-shim": "^3.7.1", "ember-cli-string-helpers": "^2.0.0", "ember-cli-typescript": "^2.0.2", - "ember-medium-editor-fix": "^0.0.2", + "ember-medium-editor-fix": "^0.0.3", "ember-moment": "^7.8.1", "ember-power-calendar": "^0.14.0", "ember-power-calendar-moment": "^0.1.7", diff --git a/datahub-web/@datahub/entity-deprecation/testem.js b/datahub-web/@datahub/entity-deprecation/testem.js index 55b4ad8366..3c17a8b75d 100644 --- a/datahub-web/@datahub/entity-deprecation/testem.js +++ b/datahub-web/@datahub/entity-deprecation/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/entity-header/app/styles/entity-header-components/_entity-title.scss b/datahub-web/@datahub/entity-header/app/styles/entity-header-components/_entity-title.scss index bf269366b8..eae70c7632 100644 --- a/datahub-web/@datahub/entity-header/app/styles/entity-header-components/_entity-title.scss +++ b/datahub-web/@datahub/entity-header/app/styles/entity-header-components/_entity-title.scss @@ -4,4 +4,5 @@ margin: 0; font-size: 24px; font-weight: 400; + margin-right: item-spacing(2); } diff --git a/datahub-web/@datahub/entity-header/app/styles/entity-header-layout/_entity-header.scss b/datahub-web/@datahub/entity-header/app/styles/entity-header-layout/_entity-header.scss index c133059721..e24d411c5f 100644 --- a/datahub-web/@datahub/entity-header/app/styles/entity-header-layout/_entity-header.scss +++ b/datahub-web/@datahub/entity-header/app/styles/entity-header-layout/_entity-header.scss @@ -11,5 +11,9 @@ flex-direction: column; justify-content: space-between; width: 100%; + + .entity-pill { + margin-right: item-spacing(2); + } } } diff --git a/datahub-web/@datahub/entity-header/testem.js b/datahub-web/@datahub/entity-header/testem.js index 55b4ad8366..3c17a8b75d 100644 --- a/datahub-web/@datahub/entity-header/testem.js +++ b/datahub-web/@datahub/entity-header/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/institutional-memory/addon/components/institutional-memory/containers/tab.ts b/datahub-web/@datahub/institutional-memory/addon/components/institutional-memory/containers/tab.ts index 07c63cb400..9f2ca3afcf 100644 --- a/datahub-web/@datahub/institutional-memory/addon/components/institutional-memory/containers/tab.ts +++ b/datahub-web/@datahub/institutional-memory/addon/components/institutional-memory/containers/tab.ts @@ -8,7 +8,8 @@ import { DataModelEntityInstance } from '@datahub/data-models/entity/entity-fact import { isEqual } from 'lodash'; import { InstitutionalMemory, InstitutionalMemories } from '@datahub/data-models/models/aspects/institutional-memory'; import { run, schedule } from '@ember/runloop'; -import { task, Task } from 'ember-concurrency'; +import { task } from 'ember-concurrency'; +import { ETaskPromise } from '@datahub/utils/types/concurrency'; @layout(template) @containerDataSource('getContainerDataTask', ['entity']) @@ -52,7 +53,7 @@ export default class InstitutionalMemoryContainersTab extends Component { }); }); }).drop()) - getContainerDataTask!: Task, () => Promise>; + getContainerDataTask!: ETaskPromise; /** * This task is used to actually save user changes to the entity's institutional memory list */ @@ -67,7 +68,7 @@ export default class InstitutionalMemoryContainersTab extends Component { yield this.getContainerDataTask.perform(); }).drop()) - writeContainerDataTask!: Task, () => Promise>; + writeContainerDataTask!: ETaskPromise; /** * Triggers the task to save the institutional memory state */ diff --git a/datahub-web/@datahub/institutional-memory/testem.js b/datahub-web/@datahub/institutional-memory/testem.js index 55b4ad8366..3c17a8b75d 100644 --- a/datahub-web/@datahub/institutional-memory/testem.js +++ b/datahub-web/@datahub/institutional-memory/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/lists/addon/components/entity-list-container.ts b/datahub-web/@datahub/lists/addon/components/entity-list-container.ts index 07e664925b..9dcce3cdcf 100644 --- a/datahub-web/@datahub/lists/addon/components/entity-list-container.ts +++ b/datahub-web/@datahub/lists/addon/components/entity-list-container.ts @@ -17,7 +17,8 @@ import { setProperties } from '@ember/object'; import Notifications from '@datahub/utils/services/notifications'; import { inject as service } from '@ember/service'; import { NotificationEvent } from '@datahub/utils/constants/notifications'; -import { task, Task, TaskInstance } from 'ember-concurrency'; +import { task } from 'ember-concurrency'; +import { ETask } from '@datahub/utils/types/concurrency'; /** * Defines the interface for the output of the EntityList mapping predicate function @@ -239,7 +240,7 @@ export default class EntityListContainer extends WithEntityLists { return set(this, 'instances', []); }).restartable()) - hydrateEntitiesTask!: Task TaskInstance>; + hydrateEntitiesTask!: ETask; /** * On initialization, hydrate the entities list with data serialized in persistent storage diff --git a/datahub-web/@datahub/lists/addon/constants/entity/shared.ts b/datahub-web/@datahub/lists/addon/constants/entity/shared.ts index c739750c33..e3928480b7 100644 --- a/datahub-web/@datahub/lists/addon/constants/entity/shared.ts +++ b/datahub-web/@datahub/lists/addon/constants/entity/shared.ts @@ -1,5 +1,6 @@ import { DataModelEntity } from '@datahub/data-models/constants/entity'; import { DatasetEntity } from '@datahub/data-models/entity/dataset/dataset-entity'; +import { PersonEntity } from '@datahub/data-models/entity/person/person-entity'; // Alias for a DataModelEntity type in the list of supportedListEntities export type SupportedListEntity = Exclude; @@ -8,7 +9,7 @@ export type SupportedListEntity = Exclude * Lists entities that have Entity List support * note: DatasetEntity is excluded from type pending mid-tier support for urn attribute, support for uri would be throw away */ -export const supportedListEntities: Array = []; +export const supportedListEntities: Array = [PersonEntity]; /** * Enumerates the cta text for toggling an Entity off or onto a list for action triggers where List toggle actions are called diff --git a/datahub-web/@datahub/lists/addon/services/entity-lists-manager.ts b/datahub-web/@datahub/lists/addon/services/entity-lists-manager.ts index 93e4cd5e23..6a7036f672 100644 --- a/datahub-web/@datahub/lists/addon/services/entity-lists-manager.ts +++ b/datahub-web/@datahub/lists/addon/services/entity-lists-manager.ts @@ -7,6 +7,7 @@ import { computed } from '@ember/object'; import { supportedListEntities, SupportedListEntity } from '@datahub/lists/constants/entity/shared'; import { noop } from '@datahub/utils/function/noop'; import { IStoredEntityAttrs } from '@datahub/lists/types/list'; +import { PersonEntity } from '@datahub/data-models/entity/person/person-entity'; // Map of List Entity displayName to list of instances type ManagedListEntities = Record>; @@ -40,19 +41,19 @@ export default class EntityListsManager extends Service { @computed('entityStorageProxy.[]') get entities(): ManagedListEntities { // Initialize with empty lists, these will be overridden in the reduction over supportedListEntities - // const entityMap = { - // }; - // const entityList = this.entityStorageProxy; + const entityMap = { + [PersonEntity.displayName]: [] + }; + const entityList = this.entityStorageProxy; - // return this.supportedListEntities.reduce((entityMap, EntityType: SupportedListEntity): ManagedListEntities => { - // // entityList is a single list of all supported entity instances - // // Filter out entities that match the EntityType - // // Create a new instance to hydrate with the saved snapshot and baseEntity - // const storedEntities = entityList.filter((storedEntity): boolean => storedEntity.type === EntityType.displayName); + return this.supportedListEntities.reduce((entityMap, EntityType: SupportedListEntity): ManagedListEntities => { + // entityList is a single list of all supported entity instances + // Filter out entities that match the EntityType + // Create a new instance to hydrate with the saved snapshot and baseEntity + const storedEntities = entityList.filter((storedEntity): boolean => storedEntity.type === EntityType.displayName); - // return { ...entityMap, [EntityType.displayName]: Object.freeze(storedEntities) }; - // }, entityMap); - return {}; + return { ...entityMap, [EntityType.displayName]: Object.freeze(storedEntities) }; + }, entityMap); } /** diff --git a/datahub-web/@datahub/lists/testem.js b/datahub-web/@datahub/lists/testem.js index 55b4ad8366..3c17a8b75d 100644 --- a/datahub-web/@datahub/lists/testem.js +++ b/datahub-web/@datahub/lists/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/metadata-types/testem.js b/datahub-web/@datahub/metadata-types/testem.js index 55b4ad8366..3c17a8b75d 100644 --- a/datahub-web/@datahub/metadata-types/testem.js +++ b/datahub-web/@datahub/metadata-types/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/metadata-types/tsconfig.json b/datahub-web/@datahub/metadata-types/tsconfig.json index eae53973b5..3fb8fafece 100644 --- a/datahub-web/@datahub/metadata-types/tsconfig.json +++ b/datahub-web/@datahub/metadata-types/tsconfig.json @@ -6,10 +6,10 @@ "paths": { "dummy/tests/*": ["tests/*"], "dummy/*": ["tests/dummy/app/*", "app/*"], - "@datahub/metadata-types/test-support": ["addon-test-support"], - "@datahub/metadata-types/test-support/*": ["addon-test-support/*"], "@datahub/metadata-types": ["addon"], "@datahub/metadata-types/*": ["addon/*"], + "@datahub/metadata-types/test-support": ["addon-test-support"], + "@datahub/metadata-types/test-support/*": ["addon-test-support/*"], "@datahub/utils": ["../../@datahub/utils/addon"], "@datahub/utils/*": ["../../@datahub/utils/addon/*"], "*": ["types/*"] diff --git a/datahub-web/@datahub/shared/addon/components/entity-pill.ts b/datahub-web/@datahub/shared/addon/components/entity-pill.ts new file mode 100644 index 0000000000..4090ca7319 --- /dev/null +++ b/datahub-web/@datahub/shared/addon/components/entity-pill.ts @@ -0,0 +1,9 @@ +import Component from '@ember/component'; +// @ts-ignore: Ignore import of compiled template +import template from '../templates/components/entity-pill'; +import { layout } from '@ember-decorators/component'; + +@layout(template) +export default class EntityPill extends Component { + baseClass: string = 'entity-pill'; +} diff --git a/datahub-web/@datahub/shared/addon/services/current-user.ts b/datahub-web/@datahub/shared/addon/services/current-user.ts index 99402df9c1..fbd79236e3 100644 --- a/datahub-web/@datahub/shared/addon/services/current-user.ts +++ b/datahub-web/@datahub/shared/addon/services/current-user.ts @@ -13,8 +13,7 @@ let _hasUserBeenTracked = false; /** * The current user service can be injected into our various datahub addons to give reference - * whenever necessary to the current logged in user based on the ember simple auth authenticated - * service + * whenever necessary to the current logged in user */ export default class CurrentUser extends Service { /** diff --git a/datahub-web/@datahub/shared/addon/templates/components/entity-pill.hbs b/datahub-web/@datahub/shared/addon/templates/components/entity-pill.hbs new file mode 100644 index 0000000000..c2fa8c60b9 --- /dev/null +++ b/datahub-web/@datahub/shared/addon/templates/components/entity-pill.hbs @@ -0,0 +1,9 @@ +{{#if @value}} + + {{if @field.component.attrs.titleize (titleize @value) @value}} + +{{/if}} diff --git a/datahub-web/@datahub/shared/app/components/entity-pill.js b/datahub-web/@datahub/shared/app/components/entity-pill.js new file mode 100644 index 0000000000..1eb1acabdc --- /dev/null +++ b/datahub-web/@datahub/shared/app/components/entity-pill.js @@ -0,0 +1 @@ +export { default } from '@datahub/shared/components/entity-pill'; diff --git a/datahub-web/@datahub/shared/app/styles/datahub-shared.scss b/datahub-web/@datahub/shared/app/styles/datahub-shared.scss new file mode 100644 index 0000000000..2b26c558bb --- /dev/null +++ b/datahub-web/@datahub/shared/app/styles/datahub-shared.scss @@ -0,0 +1 @@ +@import 'entity-pill/all'; diff --git a/datahub-web/@datahub/shared/app/styles/entity-pill/_all.scss b/datahub-web/@datahub/shared/app/styles/entity-pill/_all.scss new file mode 100644 index 0000000000..604c0ab78b --- /dev/null +++ b/datahub-web/@datahub/shared/app/styles/entity-pill/_all.scss @@ -0,0 +1 @@ +@import 'entity-pill'; diff --git a/datahub-web/@datahub/shared/app/styles/entity-pill/_entity-pill.scss b/datahub-web/@datahub/shared/app/styles/entity-pill/_entity-pill.scss new file mode 100644 index 0000000000..47e9413b0a --- /dev/null +++ b/datahub-web/@datahub/shared/app/styles/entity-pill/_entity-pill.scss @@ -0,0 +1,9 @@ +.entity-pill { + cursor: auto; + &__tier { + &--generic { + color: get-color(black); + background: get-color(slate2, 0.45); + } + } +} diff --git a/datahub-web/@datahub/shared/package.json b/datahub-web/@datahub/shared/package.json index 19fc88bf94..c7575f71b6 100644 --- a/datahub-web/@datahub/shared/package.json +++ b/datahub-web/@datahub/shared/package.json @@ -26,7 +26,8 @@ "ember-cli-htmlbars": "^3.0.0", "ember-cli-typescript": "^2.0.2", "ember-moment": "^7.8.1", - "ember-simple-auth": "^1.8.2" + "ember-simple-auth": "^1.8.2", + "ember-cli-string-helpers": "^2.0.0" }, "devDependencies": { "@babel/core": "^7.4.0", diff --git a/datahub-web/@datahub/shared/tests/integration/components/entity-pill-test.ts b/datahub-web/@datahub/shared/tests/integration/components/entity-pill-test.ts new file mode 100644 index 0000000000..ac08c8fd40 --- /dev/null +++ b/datahub-web/@datahub/shared/tests/integration/components/entity-pill-test.ts @@ -0,0 +1,52 @@ +import { module, test } from 'qunit'; +import { setupRenderingTest } from 'ember-qunit'; +import { render, findAll } from '@ember/test-helpers'; +import hbs from 'htmlbars-inline-precompile'; + +module('Integration | Component | entity-pill', function(hooks) { + setupRenderingTest(hooks); + + test('it renders correctly when titleize and modifiers are present', async function(assert) { + const mockField = { + component: { + attrs: { + styleModifier: 'class1', + titleize: true + } + } + }; + this.set('value', 'testValue'); + this.set('field', mockField); + await render(hbs`{{entity-pill value=value field=field }}`); + + assert.ok(this.element, 'Initial render is without errors'); + assert.equal(findAll('.nacho-pill').length, 1, 'Renders a pill with the right class1'); + assert.equal(findAll('.nacho-pill--small').length, 1, 'Renders a pill with the right class2'); + assert.equal(findAll('.entity-pillclass1').length, 1, 'Renders a pill with the right class3'); + assert.equal(this.element.textContent!.trim(), 'Testvalue'); + }); + + test('it renders correctly when titleize and modifiers are absent', async function(assert) { + this.set('value', 'testValue'); + this.set('field', {}); + await render(hbs`{{entity-pill value=value field=field }}`); + + assert.ok(this.element, 'Initial render is without errors'); + assert.equal(findAll('.nacho-pill').length, 1, 'Renders a pill with the right class'); + assert.equal(findAll('.nacho-pill--small').length, 1, 'Renders a pill with the right class'); + assert.equal(findAll('.class1').length, 0, 'Renders a pill with the right class'); + assert.equal(this.element.textContent!.trim(), 'testValue'); + }); + + test('it renders correctly when styleModifier is passed directly', async function(assert) { + this.set('value', 'testValue'); + this.set('styleModifier', '--advanced-style'); + await render(hbs`{{entity-pill value=value styleModifier=styleModifier }}`); + + assert.ok(this.element, 'Initial render is without errors'); + assert.equal(findAll('.nacho-pill').length, 1, 'Renders a pill with the right class1'); + assert.equal(findAll('.nacho-pill--small').length, 1, 'Renders a pill with the right class2'); + assert.equal(findAll('.entity-pill--advanced-style').length, 1, 'Renders a pill with the right class3'); + assert.equal(this.element.textContent!.trim(), 'testValue'); + }); +}); diff --git a/datahub-web/@datahub/shared/types/configurator/configurator.d.ts b/datahub-web/@datahub/shared/types/configurator/configurator.d.ts new file mode 100644 index 0000000000..c557eb5b3f --- /dev/null +++ b/datahub-web/@datahub/shared/types/configurator/configurator.d.ts @@ -0,0 +1,55 @@ +import { ITrackingConfig } from '@datahub/shared/types/configurator/tracking'; +import Service from '@ember/service'; +import { ApiStatus } from '@datahub/utils/addon/api/shared'; + +/** + * Describes the interface for the configuration endpoint response object. + * These values help to determine how the app behaves at runtime for example feature flags and feature configuration properties + * @interface IAppConfig + */ +export interface IAppConfig { + // Attributes for analytics tracking features within the application + tracking: ITrackingConfig; + showLineageGraph: boolean; + useNewBrowseDataset: boolean; + isInternal: boolean; + userEntityProps: { + aviUrlPrimary: string; + aviUrlFallback: string; + }; + showChangeManagement: boolean; + changeManagementLink: string; + wikiLinks: Record; + isStagingBanner: boolean; + isLiveDataWarning: boolean; + shouldShowDatasetLineage: boolean; + showInstitutionalMemory: boolean; + showPeople: boolean; +} + +/** + * Describes the interface for the json response when a GET request is made to the + * configurator endpoint + * @interface IConfiguratorGetResponse + */ +export interface IConfiguratorGetResponse { + status: ApiStatus; + config: IAppConfig; +} + +/** + * Conditional type alias for getConfig return type, if T is assignable to a key of + * IAppConfig, then return the property value, otherwise returns the IAppConfig object + */ +export type IAppConfigOrProperty = T extends keyof IAppConfig + ? IAppConfig[T] + : T extends undefined + ? IAppConfig + : never; + +export interface IConfigurator extends Service { + getConfig( + key?: K, + options?: { useDefault?: boolean; default?: IAppConfigOrProperty } + ): IAppConfigOrProperty; +} diff --git a/datahub-web/@datahub/shared/types/configurator/tracking.d.ts b/datahub-web/@datahub/shared/types/configurator/tracking.d.ts new file mode 100644 index 0000000000..ea4b0aaef7 --- /dev/null +++ b/datahub-web/@datahub/shared/types/configurator/tracking.d.ts @@ -0,0 +1,19 @@ +/** + * Describes the interface for subset of the tracking endpoint response object. + * These values help to determine how tracking behaves in the application + * @interface ITrackingConfig + */ +export interface ITrackingConfig { + // Flag indicating that tracking should be enabled or not + isEnabled: boolean; + // Map of available trackers and configuration options per tracker + trackers: { + // Properties for Piwik analytics service tracking + piwik: { + // Website identifier for piwik tracking, used in setSideId configuration + piwikSiteId: number; + // Specifies the URL where the Matomo / Piwik tracking code is located + piwikUrl: string; + }; + }; +} diff --git a/datahub-web/@datahub/tracking/addon/components/track-ui-event.ts b/datahub-web/@datahub/tracking/addon/components/track-ui-event.ts new file mode 100644 index 0000000000..fcbb363906 --- /dev/null +++ b/datahub-web/@datahub/tracking/addon/components/track-ui-event.ts @@ -0,0 +1,79 @@ +import Component from '@ember/component'; +// @ts-ignore: Ignore import of compiled template +import template from '../templates/components/track-ui-event'; +import { layout, tagName } from '@ember-decorators/component'; +import { inject as service } from '@ember/service'; +import UnifiedTracking from '@datahub/tracking/services/unified-tracking'; +import { TrackingEventCategory } from '@datahub/tracking/constants/event-tracking'; +import { IBaseTrackingEvent } from '@datahub/tracking/types/event-tracking'; +import { action } from '@ember/object'; +import { assert } from '@ember/debug'; +import { noop } from 'lodash'; + +/** + * Tag-less component / fragment to track user interactions or actions from an hbs template. Removes the need to concern + * component logic with analytics operations, which can evolve independently + * @export + * @class TrackUiEvent + * @extends {Component} + */ +@layout(template) +@tagName('') +export default class TrackUiEvent extends Component { + /** + * Reference to the UnifiedTracking tracking service for analytics tracking within host application + */ + @service('unified-tracking') + tracking!: UnifiedTracking; + + /** + * Reference to the enum of tracking event categories + */ + category?: TrackingEventCategory; + + /** + * The action that was performed for the event category + */ + action?: IBaseTrackingEvent['action']; + + /** + * The name of the tracking event + */ + name?: IBaseTrackingEvent['name']; + + /** + * An optional value for the specific event being tracked + */ + value?: IBaseTrackingEvent['value']; + + init(): void { + super.init(); + + assert('Expected a category to be provided on initialization of TrackUiEvent', Boolean(this.category)); + assert('Expected an action to be provided on initialization of TrackUiEvent', Boolean(this.action)); + } + + /** + * Invokes the UnifiedTracking service with options for the specific event being tracked + * @private + */ + private trackEvent(): void { + const { category, action, name, value }: Partial = this; + + if (category && action) { + const resolvedOptions = Object.assign({}, { category, action }, !!name && { name }, !!value && { value }); + this.tracking.trackEvent(resolvedOptions); + } + } + + /** + * Tracks an action triggered on a nested component + * @param {(...args: Array) => unknown} uiAction the action handler intended to handle the actual user interaction + * @param {...Array} actionArgs arguments intended to be supplied to the actual action handler + */ + @action + trackOnAction(uiAction: (...args: Array) => unknown = noop, ...actionArgs: Array): void { + typeof uiAction === 'function' && uiAction(...actionArgs); + this.trackEvent(); + } +} diff --git a/datahub-web/@datahub/tracking/addon/constants/event-tracking/compliance.ts b/datahub-web/@datahub/tracking/addon/constants/event-tracking/compliance.ts new file mode 100644 index 0000000000..7f5b50557a --- /dev/null +++ b/datahub-web/@datahub/tracking/addon/constants/event-tracking/compliance.ts @@ -0,0 +1,80 @@ +import { IBaseTrackingEvent, TrackingEvents } from '@datahub/tracking/types/event-tracking'; +import { + insertTrackingEventsCategoryFor, + TrackingEventCategory +} from '@datahub/tracking/constants/event-tracking/index'; + +/** + * TODO: META-8050 transition deprecated event references + * Enumerates the available compliance metadata events + * @deprecated Replaced with complianceTrackingEvents + * @link complianceTrackingEvents + */ +export enum ComplianceEvent { + Cancel = 'CancelEditComplianceMetadata', + Next = 'NextComplianceMetadataStep', + ManualApply = 'AdvancedEditComplianceMetadataStep', + Previous = 'PreviousComplianceMetadataStep', + Edit = 'BeginEditComplianceMetadata', + Download = 'DownloadComplianceMetadata', + Upload = 'UploadComplianceMetadata', + SetUnspecifiedAsNone = 'SetUnspecifiedFieldsAsNone', + FieldIdentifier = 'ComplianceMetadataFieldIdentifierSelected', + FieldFormat = 'ComplianceMetadataFieldFormatSelected', + Save = 'SaveComplianceMetadata' +} + +/** + * Initial map if event names to partial base tracking event with actions + * @type {Record>} + */ +const complianceTrackingEvent: Record> = { + CancelEvent: { + action: 'CancelEditComplianceMetadataEvent' + }, + NextStepEvent: { + action: 'NextComplianceMetadataStepEvent' + }, + ManualApplyEvent: { + action: 'AdvancedEditComplianceMetadataStepEvent' + }, + PreviousStepEvent: { + action: 'PreviousComplianceMetadataStepEvent' + }, + EditEvent: { + action: 'BeginEditComplianceMetadataEvent' + }, + DownloadEvent: { + action: 'DownloadComplianceMetadataEvent' + }, + UploadEvent: { + action: 'UploadComplianceMetadataEvent' + }, + SetUnspecifiedAsNoneEvent: { + action: 'SetUnspecifiedFieldsAsNoneEvent' + }, + FieldIdentifierEvent: { + action: 'ComplianceMetadataFieldIdentifierSelectedEvent' + }, + FieldFormatEvent: { + action: 'ComplianceMetadataFieldFormatSelectedEvent' + }, + SaveEvent: { + action: 'SaveComplianceMetadataEvent' + } +}; + +/** + * The accumulator object to build attributes for a tracking event + * @type {Partial>>} + */ +const complianceTrackingEventsAccumulator: Partial = {}; + +/** + * Compliance tracking events with required base tracking event attributes + * @type {TrackingEvents} + */ +export const complianceTrackingEvents = Object.entries(complianceTrackingEvent).reduce( + insertTrackingEventsCategoryFor(TrackingEventCategory.DatasetCompliance), + complianceTrackingEventsAccumulator +) as TrackingEvents; diff --git a/datahub-web/packages/data-portal/app/constants/analytics/event-tracking/index.ts b/datahub-web/@datahub/tracking/addon/constants/event-tracking/index.ts similarity index 53% rename from datahub-web/packages/data-portal/app/constants/analytics/event-tracking/index.ts rename to datahub-web/@datahub/tracking/addon/constants/event-tracking/index.ts index 5a3343e677..0c424e1a6e 100644 --- a/datahub-web/packages/data-portal/app/constants/analytics/event-tracking/index.ts +++ b/datahub-web/@datahub/tracking/addon/constants/event-tracking/index.ts @@ -1,4 +1,4 @@ -import { TrackingEvents, IBaseTrackingEvent } from 'wherehows-web/typings/app/analytics/event-tracking'; +import { TrackingEvents, IBaseTrackingEvent } from '@datahub/tracking/types/event-tracking'; /** * String values for categories that can be tracked with the application @@ -21,12 +21,23 @@ export enum TrackingGoal { SatClick = 1 } +// Convenience alias for insertTrackingEventsCategoryFor return type +type InsertTrackingEventsCategoryForReturn = Record>; + /** * Augments a tracking event partial with category information * @param {TrackingEvents} events the events mapping * @param {[string, Partial]} [eventName, trackingEvent] */ -export const insertTrackingEventsCategoryFor = (category: TrackingEventCategory) => ( +export const insertTrackingEventsCategoryFor = ( + category: TrackingEventCategory +): (( + events: TrackingEvents, + eventNameAndEvent: [string, Partial] +) => InsertTrackingEventsCategoryForReturn) => ( events: TrackingEvents, [eventName, trackingEvent]: [string, Partial] -) => ({ ...events, [eventName]: { ...trackingEvent, category } }); +): InsertTrackingEventsCategoryForReturn => ({ + ...events, + [eventName]: { ...trackingEvent, category } +}); diff --git a/datahub-web/packages/data-portal/app/constants/analytics/event-tracking/search.ts b/datahub-web/@datahub/tracking/addon/constants/event-tracking/search.ts similarity index 71% rename from datahub-web/packages/data-portal/app/constants/analytics/event-tracking/search.ts rename to datahub-web/@datahub/tracking/addon/constants/event-tracking/search.ts index 2583e8a338..647adef030 100644 --- a/datahub-web/packages/data-portal/app/constants/analytics/event-tracking/search.ts +++ b/datahub-web/@datahub/tracking/addon/constants/event-tracking/search.ts @@ -1,5 +1,5 @@ -import { IBaseTrackingEvent } from 'wherehows-web/typings/app/analytics/event-tracking'; -import { TrackingEventCategory } from 'wherehows-web/constants/analytics/event-tracking'; +import { IBaseTrackingEvent } from '@datahub/tracking/types/event-tracking'; +import { TrackingEventCategory } from '@datahub/tracking/constants/event-tracking'; /** * Tag string literal union for search tracking event keys * @alias {string} diff --git a/datahub-web/packages/data-portal/app/constants/analytics/site-search-tracking/adapters.ts b/datahub-web/@datahub/tracking/addon/constants/site-search-tracking/adapters.ts similarity index 100% rename from datahub-web/packages/data-portal/app/constants/analytics/site-search-tracking/adapters.ts rename to datahub-web/@datahub/tracking/addon/constants/site-search-tracking/adapters.ts diff --git a/datahub-web/packages/data-portal/app/constants/analytics/site-search-tracking/index.ts b/datahub-web/@datahub/tracking/addon/constants/site-search-tracking/index.ts similarity index 100% rename from datahub-web/packages/data-portal/app/constants/analytics/site-search-tracking/index.ts rename to datahub-web/@datahub/tracking/addon/constants/site-search-tracking/index.ts diff --git a/datahub-web/@datahub/tracking/addon/services/unified-tracking.ts b/datahub-web/@datahub/tracking/addon/services/unified-tracking.ts index 270bc71de1..e71a357ee6 100644 --- a/datahub-web/@datahub/tracking/addon/services/unified-tracking.ts +++ b/datahub-web/@datahub/tracking/addon/services/unified-tracking.ts @@ -1,11 +1,145 @@ import Service from '@ember/service'; +import { inject as service } from '@ember/service'; +import Metrics from 'ember-metrics'; +import CurrentUser from '@datahub/shared/services/current-user'; +import { ITrackingConfig } from '@datahub/shared/types/configurator/tracking'; +import { ITrackSiteSearchParams } from '@datahub/tracking/types/search'; +import { getPiwikActivityQueue } from '@datahub/tracking/utils/piwik'; +import { scheduleOnce } from '@ember/runloop'; +import RouterService from '@ember/routing/router-service'; +import Transition from '@ember/routing/-private/transition'; +import { resolveDynamicRouteName } from '@datahub/utils/routes/routing'; +import { mapOfRouteNamesToResolver } from '@datahub/data-models/utils/entity-route-name-resolver'; +import { searchRouteName } from '@datahub/tracking/constants/site-search-tracking'; +import RouteInfo from '@ember/routing/-private/route-info'; +import { IBaseTrackingEvent, IBaseTrackingGoal } from '@datahub/tracking/types/event-tracking'; /** * Defines the base and full api for the analytics / tracking module in Data Hub * @export * @class UnifiedTracking */ -export default class UnifiedTracking extends Service {} +export default class UnifiedTracking extends Service { + /** + * References the Ember Metrics addon service, which serves as a proxy to analytics services for + * metrics collection within the application + */ + @service + metrics!: Metrics; + + /** + * Injected reference to the shared CurrentUser service, user here to inform the analytics service of the currently logged in + * user + */ + @service + currentUser!: CurrentUser; + + /** + * Injects a reference to the router service, used to handle application routing concerns such as event handler binding + */ + @service + router!: RouterService; + + init(): void { + super.init(); + + // On init ensure that page view transitions are captured by the metrics services + this.trackPageViewOnRouteChange(); + } + + /** + * If tracking is enabled, activates the adapters for the applicable analytics services + * @param {ITrackingConfig} tracking a configuration object with properties for enabling tracking or specifying behavior + */ + setupTrackers(tracking: ITrackingConfig): void { + if (tracking.isEnabled) { + const metrics = this.metrics; + const { trackers } = tracking; + const { piwikSiteId, piwikUrl } = trackers.piwik; + + metrics.activateAdapters([ + { + name: 'Piwik', + environments: ['all'], + config: { + piwikUrl, + siteId: piwikSiteId + } + } + ]); + } + } + + /** + * Identifies and sets the currently logged in user to be tracked on the activated analytics services + * @param {ITrackingConfig} tracking a configuration object with properties for enabling tracking or specifying behavior + */ + setCurrentUser(tracking: ITrackingConfig): void { + const { currentUser, metrics } = this; + + // Check if tracking is enabled prior to tracking the current user + // Passes an anonymous function to track the currently logged in user using the `current-user` service CurrentUser + tracking.isEnabled && currentUser.trackCurrentUser((userId: string): void => metrics.identify({ userId })); + } + + /** + * This tracks the search event when a user successfully requests a search query + * @param {ITrackSiteSearchParams} { keyword, entity, searchCount } parameters for the search operation performed by the user + */ + trackSiteSearch({ keyword, entity, searchCount }: ITrackSiteSearchParams): void { + getPiwikActivityQueue().push(['trackSiteSearch', keyword, entity, searchCount]); + } + + /** + * Tracks application events that are not site search events or page view. These are typically custom events that occur as + * a user interacts with the app + */ + trackEvent(event: IBaseTrackingEvent): void { + const { category, action, name, value } = event; + const resolvedOptions = Object.assign({}, { category, action }, !!name && { name }, !!value && { value }); + + this.metrics.trackEvent(resolvedOptions); + } + + /** + * Track when a goal is met by adding the goal identifier to the activity queue + * @param {IBaseTrackingGoal} goal the goal to be tracked + */ + trackGoal(goal: IBaseTrackingGoal): void { + getPiwikActivityQueue().push(['trackGoal', goal.name]); + } + + /** + * Tracks impressions for all rendered DOM content + * This is scheduled in the afterRender queue to ensure that tracking services can accurately identify content blocks + * that have been tagged with data-track-content data attributes. This methodology is currently specific to Piwik tracking + */ + trackContentImpressions(): void { + void scheduleOnce('afterRender', null, (): number => getPiwikActivityQueue().push(['trackAllContentImpressions'])); + } + + /** + * Binds the handler to track page views on route change + */ + trackPageViewOnRouteChange(): void { + // Bind to the routeDidChange event to track global successful route transitions and track page view on the metrics service + this.router.on('routeDidChange', ({ to }: Transition): void => { + const { router, metrics } = this; + const page = router.currentURL; + // fallback to page value if a resolution cannot be determined, e.g when to / from is null + const title = resolveDynamicRouteName(mapOfRouteNamesToResolver, to) || page; + const isSearchRoute = + title.includes(searchRouteName) || (to && to.find(({ name }: RouteInfo): boolean => name === searchRouteName)); + + if (!isSearchRoute) { + // Track a page view event only on page's / route's that are not search + metrics.trackPage({ page, title }); + } + + getPiwikActivityQueue().push(['enableHeartBeatTimer']); + }); + } +} declare module '@ember/service' { // eslint-disable-next-line @typescript-eslint/interface-name-prefix diff --git a/datahub-web/@datahub/tracking/addon/templates/components/track-ui-event.hbs b/datahub-web/@datahub/tracking/addon/templates/components/track-ui-event.hbs new file mode 100644 index 0000000000..e94dc8b292 --- /dev/null +++ b/datahub-web/@datahub/tracking/addon/templates/components/track-ui-event.hbs @@ -0,0 +1,5 @@ +{{yield + (hash + trackOnAction=(action this.trackOnAction) + ) +}} diff --git a/datahub-web/packages/data-portal/app/utils/analytics/search/dwell-time.ts b/datahub-web/@datahub/tracking/addon/utils/dwell-time.ts similarity index 92% rename from datahub-web/packages/data-portal/app/utils/analytics/search/dwell-time.ts rename to datahub-web/@datahub/tracking/addon/utils/dwell-time.ts index 3047e249ee..88f5536b98 100644 --- a/datahub-web/packages/data-portal/app/utils/analytics/search/dwell-time.ts +++ b/datahub-web/@datahub/tracking/addon/utils/dwell-time.ts @@ -1,8 +1,7 @@ import RouterService from '@ember/routing/router-service'; -import Transition, { RouteInfo } from 'wherehows-web/typings/modules/routerjs'; -import { Route } from '@ember/routing'; import { Time } from '@datahub/metadata-types/types/common/time'; import { action } from '@ember/object'; +import Transition from '@ember/routing/-private/transition'; const ROUTE_EVENT_NAME = 'routeDidChange'; @@ -52,7 +51,7 @@ export default class DwellTime { * @type {(dwellTime: Time) => boolean} * @instance */ - didDwell?: (dwellTime: Time, transition: Transition) => boolean; + didDwell?: (dwellTime: Time, transition: Transition) => boolean; /** * Retains a reference to the last seen transition object @@ -60,7 +59,7 @@ export default class DwellTime { * @type {Transition} * @instance */ - private lastTransition?: Transition; + private lastTransition?: Transition; /** *Creates an instance of DwellTime. @@ -78,7 +77,6 @@ export default class DwellTime { readonly route: RouterService & { off?: (ev: string, cb: Function) => unknown; on?: (ev: string, cb: Function) => unknown; - currentRoute: RouteInfo; }, didDwell?: DwellTime['didDwell'] ) { @@ -91,7 +89,7 @@ export default class DwellTime { // According to ember docs, the RouteService extends a Service, therefore // it has a willDestroy hook that we can wrap to autoclean DwellTime const willDestroy = route.willDestroy; - route.willDestroy = () => { + route.willDestroy = (): void => { this.onDestroy(); willDestroy.call(route); }; @@ -123,7 +121,7 @@ export default class DwellTime { * @returns {void} * @instance */ - private onRouteChange = (transition: Transition): void => { + private onRouteChange = (transition: Transition): void => { this.lastTransition = transition; if (transition.to) { @@ -137,7 +135,7 @@ export default class DwellTime { * @returns {Time} * @memberof DwellTime */ - record(transition: Transition): Time { + record(transition: Transition): Time { const { startTime } = this; // Check if dwell time is already being measured, indicated by a non-zero start time diff --git a/datahub-web/@datahub/tracking/addon/utils/piwik.ts b/datahub-web/@datahub/tracking/addon/utils/piwik.ts new file mode 100644 index 0000000000..bae1ce785d --- /dev/null +++ b/datahub-web/@datahub/tracking/addon/utils/piwik.ts @@ -0,0 +1,20 @@ +/** + * Fallback array for Piwik activity queue "Window._paq" + */ +const _piwikActivityQueueFallbackQueue: Array> = []; + +/** + * Returns a reference to the globally available Piwik queue if available + * If not a mutable list is returned as a fallback. + * Ensure this is called after Piwik.js is loaded to receive a reference to the global paq + * @returns {Window['_paq']} + */ +export const getPiwikActivityQueue = (resetFallbackQueue?: boolean): Window['_paq'] => { + const { _paq } = window; + + if (resetFallbackQueue) { + _piwikActivityQueueFallbackQueue.length = 0; + } + + return _paq || _piwikActivityQueueFallbackQueue; +}; diff --git a/datahub-web/@datahub/tracking/app/components/track-ui-event.js b/datahub-web/@datahub/tracking/app/components/track-ui-event.js new file mode 100644 index 0000000000..c98a86c18c --- /dev/null +++ b/datahub-web/@datahub/tracking/app/components/track-ui-event.js @@ -0,0 +1 @@ +export { default } from '@datahub/tracking/components/track-ui-event'; diff --git a/datahub-web/@datahub/tracking/config/environment.js b/datahub-web/@datahub/tracking/config/environment.js index 9707ea62a7..acb9432b31 100644 --- a/datahub-web/@datahub/tracking/config/environment.js +++ b/datahub-web/@datahub/tracking/config/environment.js @@ -1,5 +1,13 @@ 'use strict'; module.exports = function(/* environment, appConfig */) { - return {}; + return { + // Since ember-metrics automatically removes all unused adapters, which + // will happen because we are using lazy initialization for API keys + // and not specifying adapter props at build time, the ffg forces the + // inclusion of the adapter's we currently support. + 'ember-metrics': { + includeAdapters: ['piwik'] + } + }; }; diff --git a/datahub-web/@datahub/tracking/package.json b/datahub-web/@datahub/tracking/package.json index d95db8fb27..473e1aa5c5 100644 --- a/datahub-web/@datahub/tracking/package.json +++ b/datahub-web/@datahub/tracking/package.json @@ -21,6 +21,8 @@ "postpublish": "ember ts:clean" }, "dependencies": { + "@datahub/data-models": "0.0.0", + "@datahub/shared": "0.0.0", "@datahub/utils": "0.0.0", "ember-cli-babel": "^7.8.0", "ember-cli-htmlbars": "^3.0.0", @@ -36,6 +38,7 @@ "@types/ember__test-helpers": "^0.7.8", "@types/qunit": "^2.5.4", "@types/rsvp": "^4.0.2", + "@types/sinon": "^7.0.13", "broccoli-asset-rev": "^3.0.0", "ember-cli": "~3.11.0", "ember-cli-dependency-checker": "^3.1.0", @@ -48,13 +51,16 @@ "ember-load-initializers": "^2.0.0", "ember-lodash": "^4.19.4", "ember-maybe-import-regenerator": "^0.1.6", + "ember-metrics": "^0.13.0", "ember-qunit": "^4.4.0", "ember-resolver": "^5.0.1", + "ember-sinon": "^4.0.0", + "ember-sinon-qunit": "^3.4.0", "ember-source": "~3.11.1", "ember-source-channel-url": "^1.1.0", "ember-try": "^1.0.0", "loader.js": "^4.7.0", - "qunit-dom": "^0.8.4", + "qunit-dom": "^0.8.5", "typescript": "^3.5.3" }, "engines": { diff --git a/datahub-web/@datahub/tracking/testem.js b/datahub-web/@datahub/tracking/testem.js index 55b4ad8366..3c17a8b75d 100644 --- a/datahub-web/@datahub/tracking/testem.js +++ b/datahub-web/@datahub/tracking/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/tracking/tests/integration/components/track-ui-event-test.ts b/datahub-web/@datahub/tracking/tests/integration/components/track-ui-event-test.ts new file mode 100644 index 0000000000..c535d62998 --- /dev/null +++ b/datahub-web/@datahub/tracking/tests/integration/components/track-ui-event-test.ts @@ -0,0 +1,92 @@ +import { module, test } from 'qunit'; +import { setupRenderingTest } from 'ember-qunit'; +import { render, click } from '@ember/test-helpers'; +import hbs from 'htmlbars-inline-precompile'; +import { assertThrownException } from '@datahub/utils/test-helpers/test-exception'; +import { TrackingEventCategory } from '@datahub/tracking/constants/event-tracking'; +import { getRenderedComponent } from '@datahub/utils/test-helpers/register-component'; +import TrackUiEvent from '@datahub/tracking/components/track-ui-event'; +import sinonTest from 'ember-sinon-qunit/test-support/test'; +import UnifiedTracking from '@datahub/tracking/services/unified-tracking'; + +const category = TrackingEventCategory.Entity; +const action = 'testAction'; + +module('Integration | Component | track-ui-event', function(hooks): void { + setupRenderingTest(hooks); + + test('instantiation errors are raised as expected', async function(assert): Promise { + const category = TrackingEventCategory.Entity; + + await assertThrownException( + assert, + async (): Promise => { + await render(hbs` + +

Nested Template

+
+ `); + }, + (err: Error) => err.message.includes('Expected a category to be provided on initialization of TrackUiEvent') + ); + + this.set('category', category); + + await assertThrownException( + assert, + async (): Promise => { + await render(hbs` + +

Nested Template

+
+ `); + }, + (err: Error) => err.message.includes('Expected an action to be provided on initialization of TrackUiEvent') + ); + }); + + test('component renders nested template', async function(assert): Promise { + this.setProperties({ + action, + category + }); + + await render(hbs` + +

Nested Template

+
+ `); + + assert.dom('p').hasText('Nested Template'); + }); + + sinonTest('', async function(this: SinonTestContext, assert): Promise { + const service: UnifiedTracking = this.owner.lookup('service:unified-tracking'); + const stubbedTrackEvent = this.stub(service, 'trackEvent'); + + this.setProperties({ + action, + category + }); + + const component = await getRenderedComponent({ + ComponentToRender: TrackUiEvent, + testContext: this, + template: hbs` + + -
- {{moment-format calendar.center "MMMM YYYY"}} -
- - - - - -
- {{/dropdown.content}} - {{/basic-dropdown}} - - -

- {{#unless decommissionTime}} - Please specify a date when this dataset will be decommissioned - {{/unless}} - - - {{if - decommissionTime - (concat "Will be decommissioned on " (moment-format decommissionTime "MMMM DD YYYY")) - }} - -

- - - {{input - type="checkbox" - name="deprecated.acknowledge" - id="acknowledge-deprecation" - checked=(readonly isDeprecationAcknowledged) - change=(action "onAcknowledgeDeprecationNotice") - }} - - - - - - {{/if}} - -
- -
- diff --git a/datahub-web/packages/data-portal/app/templates/components/search/search-main.hbs b/datahub-web/packages/data-portal/app/templates/components/search/search-main.hbs index 6bee6f362c..9ed3e83338 100644 --- a/datahub-web/packages/data-portal/app/templates/components/search/search-main.hbs +++ b/datahub-web/packages/data-portal/app/templates/components/search/search-main.hbs @@ -5,7 +5,7 @@ page=@page fields=@fields entity=@entity - trackingEnabled=@trackingEnabled + shouldCollectSearchTelemetry=@shouldCollectSearchTelemetry as |dataContainer| }}
diff --git a/datahub-web/packages/data-portal/app/templates/components/search/search-result.hbs b/datahub-web/packages/data-portal/app/templates/components/search/search-result.hbs index aff5167893..e392ffa786 100644 --- a/datahub-web/packages/data-portal/app/templates/components/search/search-result.hbs +++ b/datahub-web/packages/data-portal/app/templates/components/search/search-result.hbs @@ -20,7 +20,7 @@ {{#each @resultFields as |field|}} {{#let (get @result field.fieldName) as |value|}} {{#if (and field.showAsTag field.component)}} - {{component field.component entity=@meta.instance}} + {{component field.component.name entity=@meta.instance field=field value=value}} {{else if (and field.showAsTag value)}} {{#if (and field.showAsIcon field.iconName)}} diff --git a/datahub-web/packages/data-portal/app/templates/components/track-ui-event.hbs b/datahub-web/packages/data-portal/app/templates/components/track-ui-event.hbs deleted file mode 100644 index 8154f35100..0000000000 --- a/datahub-web/packages/data-portal/app/templates/components/track-ui-event.hbs +++ /dev/null @@ -1,5 +0,0 @@ -{{yield - (hash - trackOnAction=(action "trackActionAndPassthrough") - ) -}} \ No newline at end of file diff --git a/datahub-web/packages/data-portal/app/templates/search.hbs b/datahub-web/packages/data-portal/app/templates/search.hbs index f015875e91..9db96a1316 100644 --- a/datahub-web/packages/data-portal/app/templates/search.hbs +++ b/datahub-web/packages/data-portal/app/templates/search.hbs @@ -1,7 +1,7 @@
{{search/search-main trackPrefix="searchResult" - trackingEnabled=true + shouldCollectSearchTelemetry=true keyword=this.keyword facets=this.facets page=this.page diff --git a/datahub-web/packages/data-portal/app/typings/api/configurator/configurator.d.ts b/datahub-web/packages/data-portal/app/typings/api/configurator/configurator.d.ts deleted file mode 100644 index 79a7eea0f3..0000000000 --- a/datahub-web/packages/data-portal/app/typings/api/configurator/configurator.d.ts +++ /dev/null @@ -1,74 +0,0 @@ -import { ApiStatus } from '@datahub/utils/api/shared'; -import { DatasetPlatform } from '@datahub/metadata-types/constants/entity/dataset/platform'; - -// TODO: META-9304 Move to @linkedin/shared - -/** - * Describes the interface for the IAppConfig object - * @interface IAppConfig - */ -export interface IAppConfig { - isInternal: boolean | void; - isStagingBanner: boolean; - isLiveDataWarning: boolean; - showAdvancedSearch: boolean; - shouldShowDatasetLineage: boolean; - shouldShowDatasetHealth: boolean; - // confidence threshold for filtering out higher quality suggestions - suggestionConfidenceThreshold: number; - // collection of links to external help resource pages - wikiLinks: Record; - // Properties related to whether or not we want to show a change management banner to inform users that - // the application is under some kind of maintenance/update - showChangeManagement: boolean; - changeManagementLink: string; - // properties for an avatar entity - userEntityProps: { - aviUrlPrimary: string; - aviUrlFallback: string; - }; - // Flag indicating if the Feature Entity is available for current environment - showFeatures: boolean; - // browse and search ump entities - showUmp: boolean; - // Show Flows in user menu - showUmpFlows: boolean; - // Use new browse API for datasets - useNewBrowseDataset: boolean; - // Show lineage graph in the dataset relationships tab - showLineageGraph: boolean; - showComplianceBeta: boolean; - showInstitutionalMemory: boolean; - tracking: { - isEnabled: boolean; - trackers: { - piwik: { - piwikSiteId: number; - piwikUrl: string; - }; - }; - }; - // Configuration properties for the JIT ACL access request feature - jitAcl: { - // Maximum allowed duration from now per FabricType - maxDuration: { - PROD: string; - EI: string; - CORP: string; - }; - // Lists the DatasetPlatforms that are supported for JIT ACL requests - whitelist: Array; - // Email contact for issues with JIT ACL service and endpoint - contact: string; - }; -} - -/** - * Describes the interface for the json response when a GET request is made to the - * configurator endpoint - * @interface IConfiguratorGetResponse - */ -export interface IConfiguratorGetResponse { - status: ApiStatus; - config: IAppConfig; -} diff --git a/datahub-web/packages/data-portal/app/typings/app/routes.d.ts b/datahub-web/packages/data-portal/app/typings/app/routes.d.ts deleted file mode 100644 index e025df8bce..0000000000 --- a/datahub-web/packages/data-portal/app/typings/app/routes.d.ts +++ /dev/null @@ -1,8 +0,0 @@ -import { RouteInfoWithAttributes, RouteInfo } from 'wherehows-web/typings/modules/routerjs'; - -/** - * Aliases the union type for easier referencing - * @alias {RouteInfoWithAttributes | RouteInfo} - * @type RouteInfoWithOrWithoutAttributes - */ -export type RouteInfoWithOrWithoutAttributes = RouteInfoWithAttributes | RouteInfo; diff --git a/datahub-web/packages/data-portal/app/typings/app/services.d.ts b/datahub-web/packages/data-portal/app/typings/app/services.d.ts index 86a8064760..e51308eb46 100644 --- a/datahub-web/packages/data-portal/app/typings/app/services.d.ts +++ b/datahub-web/packages/data-portal/app/typings/app/services.d.ts @@ -5,7 +5,6 @@ import BannerService from 'wherehows-web/services/banners'; import UserLookup from 'wherehows-web/services/user-lookup'; import HotKeys from 'wherehows-web/services/hot-keys'; import Search from 'wherehows-web/services/search'; -import TrackingService from 'wherehows-web/services/tracking'; declare module '@ember/service' { // eslint-disable-next-line @typescript-eslint/interface-name-prefix @@ -14,7 +13,6 @@ declare module '@ember/service' { session: Session; metrics: Metrics; banners: BannerService; - tracking: TrackingService; 'current-user': CurrentUser; 'user-lookup': UserLookup; 'hot-keys': HotKeys; diff --git a/datahub-web/packages/data-portal/app/typings/modules/routerjs.d.ts b/datahub-web/packages/data-portal/app/typings/modules/routerjs.d.ts deleted file mode 100644 index c21e3bb6bb..0000000000 --- a/datahub-web/packages/data-portal/app/typings/modules/routerjs.d.ts +++ /dev/null @@ -1,451 +0,0 @@ -/** - * Describes module interfaces in the third party library tildeio/router.js - * until types are published publicly - * @module tildeio/router.js - */ - -import Route from '@ember/routing/route'; - -// eslint-disable-next-line @typescript-eslint/interface-name-prefix -export interface RouteInfo { - readonly name: string; - readonly parent: RouteInfo | RouteInfoWithAttributes | null; - readonly child: RouteInfo | RouteInfoWithAttributes | null; - readonly localName: string; - readonly params: Record; - readonly paramNames: Array; - readonly queryParams: Record; - readonly metadata: unknown; - find(predicate: (this: any, routeInfo: RouteInfo, i: number) => boolean, thisArg?: any): RouteInfo | undefined; -} - -// eslint-disable-next-line @typescript-eslint/interface-name-prefix -export interface RouteInfoWithAttributes extends RouteInfo { - attributes: any; -} - -// eslint-disable-next-line @typescript-eslint/interface-name-prefix -export interface SerializerFunc { - (model: {}, params: Array): unknown; -} - -export declare type OnRejected = ((reason: T) => TResult2 | PromiseLike) | undefined | null; -export declare const PARAMS_SYMBOL = '__PARAMS__-261986232992830203-23323'; -export declare const QUERY_PARAMS_SYMBOL = '__QPS__-2619863929824844-32323'; -/** - A Transition is a thennable (a promise-like object) that represents - an attempt to transition to another route. It can be aborted, either - explicitly via `abort` or by attempting another transition while a - previous one is still underway. An aborted transition can also - be `retry()`d later. - - @class Transition - @constructor - @param {object} router - @param {object} intent - @param {object} state - @param {object} error - @private - */ -export default class Transition implements Partial> { - from: RouteInfoWithAttributes | null | undefined; - to?: RouteInfo | RouteInfoWithAttributes | null; - router: Router; - data: Record; - intent: unknown; - resolvedModels: Record>; - [QUERY_PARAMS_SYMBOL]: Record; - promise?: Promise; - error: Error | null | undefined; - [PARAMS_SYMBOL]: Record; - routeInfos: Array; - targetName: string | null | undefined; - pivotHandler: Route | null | undefined; - sequence: number; - isAborted: boolean; - isActive: boolean; - urlMethod: string | null; - resolveIndex: number; - queryParamsOnly: boolean; - isTransition: boolean; - isCausedByAbortingTransition: boolean; - isCausedByInitialTransition: boolean; - isCausedByAbortingReplaceTransition: boolean; - /** - The Transition's internal promise. Calling `.then` on this property - is that same as calling `.then` on the Transition object itself, but - this property is exposed for when you want to pass around a - Transition's promise, but not the Transition object itself, since - Transition object can be externally `abort`ed, while the promise - cannot. - - @property promise - @type {object} - @public - */ - /** - Custom state can be stored on a Transition's `data` object. - This can be useful for decorating a Transition within an earlier - hook and shared with a later hook. Properties set on `data` will - be copied to new transitions generated by calling `retry` on this - transition. - - @property data - @type {object} - @public - */ - /** - A standard promise hook that resolves if the transition - succeeds and rejects if it fails/redirects/aborts. - - Forwards to the internal `promise` property which you can - use in situations where you want to pass around a thennable, - but not the Transition itself. - - @method then - @param {Function} onFulfilled - @param {Function} onRejected - @param {string} label optional string for labeling the promise. - Useful for tooling. - @return {Promise} - @public - */ - then( - onFulfilled?: ((value: T) => TResult1 | PromiseLike) | undefined | null, - onRejected?: ((reason: any) => TResult2 | PromiseLike) | undefined | null, - label?: string - ): Promise; - /** - - Forwards to the internal `promise` property which you can - use in situations where you want to pass around a thennable, - but not the Transition itself. - - @method catch - @param {Function} onRejection - @param {string} label optional string for labeling the promise. - Useful for tooling. - @return {Promise} - @public - */ - catch(onRejection?: OnRejected, label?: string): any; - /** - - Forwards to the internal `promise` property which you can - use in situations where you want to pass around a thennable, - but not the Transition itself. - - @method finally - @param {Function} callback - @param {string} label optional string for labeling the promise. - Useful for tooling. - @return {Promise} - @public - */ - finally(callback?: T | undefined, label?: string): any; - /** - Aborts the Transition. Note you can also implicitly abort a transition - by initiating another transition while a previous one is underway. - - @method abort - @return {Transition} this transition - @public - */ - abort(): this; - rollback(): void; - redirect(newTransition: Transition): void; - /** - - Retries a previously-aborted transition (making sure to abort the - transition if it's still active). Returns a new transition that - represents the new attempt to transition. - - @method retry - @return {Transition} new transition - @public - */ - retry(): Transition; - /** - - Sets the URL-changing method to be employed at the end of a - successful transition. By default, a new Transition will just - use `updateURL`, but passing 'replace' to this method will - cause the URL to update using 'replaceWith' instead. Omitting - a parameter will disable the URL change, allowing for transitions - that don't update the URL at completion (this is also used for - handleURL, since the URL has already changed before the - transition took place). - - @method method - @param {string} method the type of URL-changing method to use - at the end of a transition. Accepted values are 'replace', - falsy values, or any other non-falsy value (which is - interpreted as an updateURL transition). - - @return {Transition} this transition - @public - */ - method(method: string | null): this; - send(ignoreFailure: boolean, _name: string, err?: Error, transition?: Transition, handler?: Route): void; - /** - - Fires an event on the current list of resolved/resolving - handlers within this transition. Useful for firing events - on route hierarchies that haven't fully been entered yet. - - Note: This method is also aliased as `send` - - @method trigger - @param {Boolean} [ignoreFailure=false] a boolean specifying whether unhandled events throw an error - @param {string} name the name of the event to fire - @public - */ - trigger(ignoreFailure: boolean, name: string, ...args: Array): void; - /** - Transitions are aborted and their promises rejected - when redirects occur; this method returns a promise - that will follow any redirects that occur and fulfill - with the value fulfilled by any redirecting transitions - that occur. - - @method followRedirects - @return {Promise} a promise that fulfills with the same - value that the final redirecting transition fulfills with - @public - */ - followRedirects(): Promise; - toString(): string; - /** - @private - */ - log(message: string): void; -} - -export abstract class Router { - log?: (message: string) => void; - state?: unknown; - oldState: unknown | null | undefined; - activeTransition?: Transition; - currentRouteInfos?: Array; - currentSequence: number; - recognizer: unknown; - constructor(logger?: (message: string) => void); - abstract getRoute(name: string): T | Promise; - abstract getSerializer(name: string): SerializerFunc | undefined; - abstract updateURL(url: string): void; - abstract replaceURL(url: string): void; - abstract willTransition( - oldRouteInfos: Array, - newRouteInfos: Array, - transition: Transition - ): void; - abstract didTransition(routeInfos: Array): void; - abstract triggerEvent(routeInfos: Array, ignoreFailure: boolean, name: string, args: Array): void; - abstract routeWillChange(transition: Transition): void; - abstract routeDidChange(transition: Transition): void; - abstract transitionDidError(error: unknown, transition: Transition): Transition | Error; - /** - The main entry point into the router. The API is essentially - the same as the `map` method in `route-recognizer`. - - This method extracts the String handler at the last `.to()` - call and uses it as the name of the whole route. - - @param {Function} callback - */ - map(callback: unknown): void; - hasRoute(route: string): any; - queryParamsTransition: unknown; - transitionByIntent: unknown; - recognize(url: string): RouteInfo | null; - recognizeAndLoad(url: string): Promise; - private generateNewState; - private getTransitionByIntent; - /** - @private - - Begins and returns a Transition based on the provided - arguments. Accepts arguments in the form of both URL - transitions and named transitions. - - @param {Router} router - @param {Array[Object]} args arguments passed to transitionTo, - replaceWith, or handleURL - */ - private doTransition; - /** - @private - - Updates the URL (if necessary) and calls `setupContexts` - to update the router's array of `currentRouteInfos`. - */ - private finalizeTransition; - /** - @private - - Takes an Array of `RouteInfo`s, figures out which ones are - exiting, entering, or changing contexts, and calls the - proper route hooks. - - For example, consider the following tree of routes. Each route is - followed by the URL segment it handles. - - ``` - |~index ("/") - | |~posts ("/posts") - | | |-showPost ("/:id") - | | |-newPost ("/new") - | | |-editPost ("/edit") - | |~about ("/about/:id") - ``` - - Consider the following transitions: - - 1. A URL transition to `/posts/1`. - 1. Triggers the `*model` callbacks on the - `index`, `posts`, and `showPost` routes - 2. Triggers the `enter` callback on the same - 3. Triggers the `setup` callback on the same - 2. A direct transition to `newPost` - 1. Triggers the `exit` callback on `showPost` - 2. Triggers the `enter` callback on `newPost` - 3. Triggers the `setup` callback on `newPost` - 3. A direct transition to `about` with a specified - context object - 1. Triggers the `exit` callback on `newPost` - and `posts` - 2. Triggers the `serialize` callback on `about` - 3. Triggers the `enter` callback on `about` - 4. Triggers the `setup` callback on `about` - - @param {Router} transition - @param {TransitionState} newState - */ - private setupContexts; - /** - @private - - Fires queryParamsDidChange event - */ - private fireQueryParamDidChange; - /** - @private - - Helper method used by setupContexts. Handles errors or redirects - that may happen in enter/setup. - */ - private routeEnteredOrUpdated; - /** - @private - - This function is called when transitioning from one URL to - another to determine which routes are no longer active, - which routes are newly active, and which routes remain - active but have their context changed. - - Take a list of old routes and new routes and partition - them into four buckets: - - * unchanged: the route was active in both the old and - new URL, and its context remains the same - * updated context: the route was active in both the - old and new URL, but its context changed. The route's - `setup` method, if any, will be called with the new - context. - * exited: the route was active in the old URL, but is - no longer active. - * entered: the route was not active in the old URL, but - is now active. - - The PartitionedRoutes structure has four fields: - - * `updatedContext`: a list of `RouteInfo` objects that - represent routes that remain active but have a changed - context - * `entered`: a list of `RouteInfo` objects that represent - routes that are newly active - * `exited`: a list of `RouteInfo` objects that are no - longer active. - * `unchanged`: a list of `RouteInfo` objects that remain active. - - @param {Array[InternalRouteInfo]} oldRoutes a list of the route - information for the previous URL (or `[]` if this is the - first handled transition) - @param {Array[InternalRouteInfo]} newRoutes a list of the route - information for the new URL - - @return {Partition} - */ - private partitionRoutes; - private _updateURL; - private finalizeQueryParamChange; - private toReadOnlyInfos; - private fromInfos; - private notifyExistingHandlers; - /** - Clears the current and target route routes and triggers exit - on each of them starting at the leaf and traversing up through - its ancestors. - */ - reset(): void; - /** - let handler = routeInfo.handler; - The entry point for handling a change to the URL (usually - via the back and forward button). - - Returns an Array of handlers and the parameters associated - with those parameters. - - @param {string} url a URL to process - - @return {Array} an Array of `[handler, parameter]` tuples - */ - handleURL(url: string): unknown; - /** - Transition into the specified named route. - - If necessary, trigger the exit callback on any routes - that are no longer represented by the target route. - - @param {string} name the name of the route - */ - transitionTo( - name: - | string - | { - queryParams: Record; - }, - ...contexts: Array - ): Transition; - intermediateTransitionTo(name: string, ...args: Array): unknown; - refresh(pivotRoute?: T): unknown; - /** - Identical to `transitionTo` except that the current URL will be replaced - if possible. - - This method is intended primarily for use with `replaceState`. - - @param {string} name the name of the route - */ - replaceWith(name: string): unknown; - /** - Take a named route and context objects and generate a - URL. - - @param {string} name the name of the route to generate - a URL for - @param {...object} objects a list of objects to serialize - - @return {string} a URL - */ - generate(routeName: string, ...args: Array): any; - applyIntent(routeName: string, contexts: Array>): unknown; - isActiveIntent( - routeName: string, - contexts: Array, - queryParams?: Record, - _state?: unknown - ): boolean; - isActive(routeName: string, ...args: Array): boolean; - trigger(name: string, ...args: Array): void; -} diff --git a/datahub-web/packages/data-portal/app/typings/untyped-js-module.d.ts b/datahub-web/packages/data-portal/app/typings/untyped-js-module.d.ts index e84e29c293..2ca6344ffc 100644 --- a/datahub-web/packages/data-portal/app/typings/untyped-js-module.d.ts +++ b/datahub-web/packages/data-portal/app/typings/untyped-js-module.d.ts @@ -1,7 +1,5 @@ /* eslint-disable @typescript-eslint/no-explicit-any */ -declare module '@ember-decorators/runloop'; - declare module 'wherehows-web/app'; declare module 'ember-simple-auth/mixins/application-route-mixin' { @@ -91,10 +89,6 @@ interface Window { } ): Element; }; - - // global array for piwik tracking - // eslint-disable-next-line @typescript-eslint/no-explicit-any - _paq: ArrayLike & { push: (...items: Array) => number }; } /** diff --git a/datahub-web/packages/data-portal/app/utils/analytics/piwik.ts b/datahub-web/packages/data-portal/app/utils/analytics/piwik.ts deleted file mode 100644 index a0c89e37d8..0000000000 --- a/datahub-web/packages/data-portal/app/utils/analytics/piwik.ts +++ /dev/null @@ -1,25 +0,0 @@ -import { scheduleOnce } from '@ember/runloop'; - -const _piwikActivityQueueFallbackQueue: Array = []; -/** - * Returns a reference to the globally available Piwik queue if available, otherwise an empty list is returned - * @returns {Window['_paq']} - */ -export const getPiwikActivityQueue = (resetFallbackQueue?: boolean): Window['_paq'] => { - const { _paq } = window; - - if (resetFallbackQueue) { - _piwikActivityQueueFallbackQueue.length = 0; - } - - return _paq || _piwikActivityQueueFallbackQueue; -}; - -/** - * Tracks impressions for all rendered DOM content - * This is scheduled in the afterRender queue to ensure that Piwik can accurately identify content blocks - * that have been tagged with data-track-content data attributes - * @returns {undefined} - */ -export const trackContentImpressions = (): undefined => - void scheduleOnce('afterRender', null, () => getPiwikActivityQueue().push(['trackAllContentImpressions'])); diff --git a/datahub-web/packages/data-portal/app/utils/analytics/search/track-site-search.ts b/datahub-web/packages/data-portal/app/utils/analytics/search/track-site-search.ts deleted file mode 100644 index c6fd3657b7..0000000000 --- a/datahub-web/packages/data-portal/app/utils/analytics/search/track-site-search.ts +++ /dev/null @@ -1,40 +0,0 @@ -import { - IPiwikTrackSiteSearchParams, - ITrackPiwikSiteSearchReturnFn -} from 'wherehows-web/typings/app/analytics/site-search-tracking/piwik'; -import { TrackSearchAdapter } from 'wherehows-web/constants/analytics/site-search-tracking/adapters'; - -/** - * Manually track Piwik siteSearch using the `trackSiteSearch` api - * rather than using Piwik's default reading of url's containing the - * "search", "q", "query", "s", "searchword", "k" and "keyword", keywords - * @link https://developer.piwik.org/guides/tracking-javascript-guide#internal-search-tracking - * @param {Window['_paq']} piwikActivityQueue - * @returns {((arg: IPiwikTrackSiteSearchParams) => void)} - */ -const trackPiwikSiteSearch = (piwikActivityQueue: Window['_paq']): ITrackPiwikSiteSearchReturnFn => ({ - keyword, - entity, - searchCount -}: IPiwikTrackSiteSearchParams): void => - void piwikActivityQueue.push(['trackSiteSearch', keyword, entity, searchCount]); - -/** - * Describes the index signature for the strategy object used in selecting the tracking method for search. - * Each available approach is keyed by TrackSearchAdapter - * @type { - [TrackSearchAdapter.Piwik]: (piwikActivityQueue: any[]) => ITrackPiwikSiteSearchReturnFn; -} - */ -export const adapterStrategy = { - [TrackSearchAdapter.Piwik]: trackPiwikSiteSearch -}; - -/** - * Wrapper function to track site search activity based on the selected TrackSearchAdapter - * @template T - * @param {T} adapter - * @returns {(typeof adapterStrategy)[T]} - */ -export const trackSiteSearch = (adapter: T): (typeof adapterStrategy)[T] => - adapterStrategy[adapter]; diff --git a/datahub-web/packages/data-portal/app/utils/api/shared.ts b/datahub-web/packages/data-portal/app/utils/api/shared.ts index 3cc0b7a37c..209190d26b 100644 --- a/datahub-web/packages/data-portal/app/utils/api/shared.ts +++ b/datahub-web/packages/data-portal/app/utils/api/shared.ts @@ -31,8 +31,8 @@ export enum ApiResponseStatus { * @param {Error} e * @return {boolean} */ -// @ts-ignore -const isApiError = (e: Error): e is ApiError => typeOf((e as ApiError).status) !== 'undefined'; +const isApiError = (e: Error): e is ApiError => + typeOf(((e as unknown) as Record).status) !== 'undefined'; /** * Convenience function to ascertain if an api error is a not found code diff --git a/datahub-web/packages/data-portal/app/utils/entity/flag-guard.ts b/datahub-web/packages/data-portal/app/utils/entity/flag-guard.ts index 744163fdc7..67734dde03 100644 --- a/datahub-web/packages/data-portal/app/utils/entity/flag-guard.ts +++ b/datahub-web/packages/data-portal/app/utils/entity/flag-guard.ts @@ -1,14 +1,15 @@ -import { IAppConfig } from 'wherehows-web/typings/api/configurator/configurator'; -import Configurator from 'wherehows-web/services/configurator'; +import { IAppConfig, IConfigurator } from '@datahub/shared/types/configurator/configurator'; import { DataModelEntity } from '@datahub/data-models/constants/entity'; -import { DatasetEntity } from '@datahub/data-models/entity/dataset/dataset-entity'; +import { PersonEntity } from '@datahub/data-models/entity/person/person-entity'; /** * Filters out DataModelEntity types that have configurator guard values set to false */ -export const unGuardedEntities = (configurator: typeof Configurator): Array => { +export const unGuardedEntities = (getConfig: IConfigurator['getConfig']): Array => { // Map of Entity display names to configurator flags - const guards: Partial> = {}; + const guards: Record = { + [PersonEntity.displayName]: 'showPeople' + }; // List of DataModeEntities that are not flag guarded const unGuardedEntities: Array = []; @@ -16,7 +17,7 @@ export const unGuardedEntities = (configurator: typeof Configurator): Array => { const guard = guards[entity.displayName]; - const isGuarded = Boolean(guard && !configurator.getConfig(guard)); + const isGuarded = Boolean(guard && !getConfig(guard)); return isGuarded ? unGuardedEntities : [...unGuardedEntities, entity]; }, diff --git a/datahub-web/packages/data-portal/app/utils/helpers/routes.ts b/datahub-web/packages/data-portal/app/utils/helpers/routes.ts deleted file mode 100644 index c2d115e56d..0000000000 --- a/datahub-web/packages/data-portal/app/utils/helpers/routes.ts +++ /dev/null @@ -1,69 +0,0 @@ -import { arraySome } from 'wherehows-web/utils/array'; -import Transition, { RouteInfoWithAttributes } from 'wherehows-web/typings/modules/routerjs'; -import { RouteInfoWithOrWithoutAttributes } from 'wherehows-web/typings/app/routes'; -import { listOfEntitiesMap } from '@datahub/data-models/entity/utils/entities'; -import { Route } from '@ember/routing'; -import { DatasetEntity } from '@datahub/data-models/entity/dataset/dataset-entity'; - -/** - * Type guard for RouteInfoWithOrWithoutAttributes union type - * @param {RouteInfoWithOrWithoutAttributes} routeInfo - * @returns {routeInfo is RouteInfoWithAttributes} - */ -export const isRouteInfoWithAttributes = ( - routeInfo: RouteInfoWithOrWithoutAttributes -): routeInfo is RouteInfoWithAttributes => routeInfo.hasOwnProperty('attributes'); - -/** - * Indexes the route names we care about to functions that resolve the placeholder value - * defaults to the route.name, if a resolved value cannot be determined - * @type Record string) | undefined> - */ -export const mapOfRouteNamesToResolver: Record string) | void> = { - 'browse.entity': (route: RouteInfoWithOrWithoutAttributes): string => - isRouteInfoWithAttributes(route) ? `browse.${route.attributes.entity}` : route.name, - 'browse.entity.index': (route: RouteInfoWithOrWithoutAttributes): string => - isRouteInfoWithAttributes(route) ? `browse.${route.attributes.entity}` : route.name, - 'datasets.dataset.tab': (route: RouteInfoWithOrWithoutAttributes): string => - isRouteInfoWithAttributes(route) ? `${DatasetEntity.displayName}.${route.attributes.currentTab}` : route.name -}; - -/** - * Takes a RouteInfo to resolver function mapping and a RouteInfo instance, resolves a route name - * Some routes are shared between different but related features. This is done via placeholders. - * This provides a way to resolve the meaningful value of the placeholder segments. - * @param {(Record string) | void>)} routeResolverMap - * @param {RouteInfoWithOrWithoutAttributes | null} routeBeingTransitionedTo route info object for the route being navigated to - * @returns {string | null} - */ -export const resolveDynamicRouteName = ( - routeResolverMap: Record string) | void>, - routeBeingTransitionedTo?: RouteInfoWithOrWithoutAttributes | null -): string | null => { - if (routeBeingTransitionedTo) { - const routeName = routeBeingTransitionedTo.name; - const resolveRouteName = routeResolverMap[routeName]; - - return typeof resolveRouteName === 'function' ? resolveRouteName(routeBeingTransitionedTo) : routeName; - } - - return null; -}; - -/** - * Guard checks that a route name is an entity route by testing if the routeName begins with the entity name - * @param {string} routeName the name of the route to check against - * @returns {boolean} - */ -const routeNameIsEntityRoute = (routeName: string): boolean => - arraySome((entityName: string): boolean => routeName.startsWith(entityName))(listOfEntitiesMap(e => e.displayName)); - -/** - * Check if the route info instance has a name that is considered an entity route - * @param {Transition['to']} routeBeingTransitionedTo - * @returns {boolean} - */ -export const isRouteEntityPageRoute = (routeBeingTransitionedTo: Transition['to']): boolean => { - const routeName = resolveDynamicRouteName(mapOfRouteNamesToResolver, routeBeingTransitionedTo); - return Boolean(routeName && routeNameIsEntityRoute(routeName)); -}; diff --git a/datahub-web/packages/data-portal/config/dependency-lint.js b/datahub-web/packages/data-portal/config/dependency-lint.js index 630d502c46..f19f6d8fd3 100644 --- a/datahub-web/packages/data-portal/config/dependency-lint.js +++ b/datahub-web/packages/data-portal/config/dependency-lint.js @@ -9,7 +9,6 @@ module.exports = { 'ember-fetch': '^6.0.0 || 5.1.1', //https://github.com/simplabs/ember-simple-auth/issues/1705, 'ember-inflector': '^3.0.0 || 2.3.0', 'ember-maybe-in-element': '0.1.3 || 0.2.0', - /*'ember-lodash': '4.18.0 || 4.19.4',*/ 'ember-sinon': '2.2.0 || ^3.1.0' // TODO: META-8261 bump some sub deps } }; diff --git a/datahub-web/packages/data-portal/config/environment.js b/datahub-web/packages/data-portal/config/environment.js index 988adf8a35..62cd96ee07 100644 --- a/datahub-web/packages/data-portal/config/environment.js +++ b/datahub-web/packages/data-portal/config/environment.js @@ -38,14 +38,6 @@ module.exports = function(environment) { outputFormat: 'llll' // fallback format e.g. Thu, Sep 21 1984 8:30 PM }, - // Since ember-metrics automatically removes all unused adapters, which - // will happen because we are using lazy initialization for API keys - // and not specifying adapter props at build time, the ffg forces the - // inclusion of the adapter's we currently support. - 'ember-metrics': { - includeAdapters: ['piwik'] - }, - // ember-aupac-typeahead is dependent on twitter typeahead // unfortunately, there are several issues with the library since it's // also no longer maintained including failing to render async results diff --git a/datahub-web/packages/data-portal/mirage/factories/access.ts b/datahub-web/packages/data-portal/mirage/factories/access.ts deleted file mode 100644 index e6cb357ddb..0000000000 --- a/datahub-web/packages/data-portal/mirage/factories/access.ts +++ /dev/null @@ -1,3 +0,0 @@ -import { Factory } from 'ember-cli-mirage'; - -export default Factory.extend({}); diff --git a/datahub-web/packages/data-portal/mirage/models/access.ts b/datahub-web/packages/data-portal/mirage/models/access.ts deleted file mode 100644 index 770b50936d..0000000000 --- a/datahub-web/packages/data-portal/mirage/models/access.ts +++ /dev/null @@ -1,3 +0,0 @@ -import { Model } from 'ember-cli-mirage'; - -export default Model.extend({}); diff --git a/datahub-web/packages/data-portal/package.json b/datahub-web/packages/data-portal/package.json index cd58486bfa..ae4b9abd94 100644 --- a/datahub-web/packages/data-portal/package.json +++ b/datahub-web/packages/data-portal/package.json @@ -108,7 +108,7 @@ "ember-inflector": "^3.0.0", "ember-lodash": "^4.18.0", "ember-math-helpers": "^2.7.1", - "ember-medium-editor-fix": "^0.0.2", + "ember-medium-editor-fix": "^0.0.3", "ember-metrics": "^0.13.0", "ember-modal-dialog": "^3.0.0-beta.0", "ember-moment": "^7.8.1", diff --git a/datahub-web/packages/data-portal/testem.js b/datahub-web/packages/data-portal/testem.js index 55b4ad8366..3c17a8b75d 100644 --- a/datahub-web/packages/data-portal/testem.js +++ b/datahub-web/packages/data-portal/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/packages/data-portal/tests/acceptance/analytics/search-test.ts b/datahub-web/packages/data-portal/tests/acceptance/analytics/search-test.ts index f0ffe0b72e..36586b0f04 100644 --- a/datahub-web/packages/data-portal/tests/acceptance/analytics/search-test.ts +++ b/datahub-web/packages/data-portal/tests/acceptance/analytics/search-test.ts @@ -3,15 +3,14 @@ import { visit, currentURL } from '@ember/test-helpers'; import { setupApplicationTest } from 'ember-qunit'; import appLogin from 'wherehows-web/tests/helpers/login/test-login'; import searchResponse from 'wherehows-web/mirage/fixtures/search-response'; -import { getPiwikActivityQueue } from 'wherehows-web/utils/analytics/piwik'; import { delay } from 'wherehows-web/utils/promise-delay'; import { getQueue, findInQueue } from 'wherehows-web/tests/helpers/analytics'; import { navigateToSearchAndClickResult, mockTrackingEventQueue } from 'wherehows-web/tests/helpers/search/search-acceptance'; -import { searchTrackingEvent } from 'wherehows-web/constants/analytics/event-tracking/search'; -import { IMirageTestContext } from '@datahub/utils/addon/types/vendor/ember-cli-mirage-deprecated'; +import { searchTrackingEvent } from '@datahub/tracking/constants/event-tracking/search'; +import { getPiwikActivityQueue } from '@datahub/tracking/utils/piwik'; // Local test constants const searchUrl = `/search?facets=()&keyword=${searchResponse.result.keywords}`; @@ -36,13 +35,9 @@ module('Acceptance | analytics/search', function(hooks): void { window._paq = paqBk; }); - test('analytics activity queue correctly tracks search result impressions', async function(this: IMirageTestContext, assert): Promise< - void - > { + test('analytics activity queue correctly tracks search result impressions', async function(assert): Promise { const piwikContentImpressionActivityIdentifier = 'trackAllContentImpressions'; const queue = getQueue(); - const { server } = this; - server.create('dataset-view'); /** * Checks if the queue has the piwik content impression activity id @@ -60,7 +55,6 @@ module('Acceptance | analytics/search', function(hooks): void { await visit(searchUrl); assert.equal(currentURL(), searchUrl, `expected current url to be ${searchUrl}`); - assert.ok(document.querySelector('[data-track-content]'), 'expected trackable content to be rendered'); assert.ok( queueHasImpressionActivityId(queue), 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 74286e6e57..1432323f3e 100644 --- a/datahub-web/packages/data-portal/tests/acceptance/breadcrumbs-test.ts +++ b/datahub-web/packages/data-portal/tests/acceptance/breadcrumbs-test.ts @@ -2,7 +2,6 @@ import { module, test } from 'qunit'; import { visit, currentURL, findAll, click, waitFor } from '@ember/test-helpers'; import { setupApplicationTest } from 'ember-qunit'; import appLogin from 'wherehows-web/tests/helpers/login/test-login'; -import { setMockConfig, resetConfig } from 'wherehows-web/services/configurator'; import defaultScenario from 'wherehows-web/mirage/scenarios/default'; import { IMirageTestContext } from '@datahub/utils/types/vendor/ember-cli-mirage-deprecated'; import { getTextNoSpacesFromElements } from '@datahub/utils/test-helpers/dom-helpers'; @@ -10,16 +9,6 @@ import { getTextNoSpacesFromElements } from '@datahub/utils/test-helpers/dom-hel module('Acceptance | breadcrumbs-smoke-test', function(hooks) { setupApplicationTest(hooks); - hooks.beforeEach(function() { - setMockConfig({ - showUmp: false - }); - }); - - hooks.afterEach(function() { - resetConfig(); - }); - test('Breadcrumbs Smoke Test', async function(this: IMirageTestContext, assert) { const categoryLinkClass = '.browse-category__link'; const breadcrumbsClass = '.nacho-breadcrumbs__crumb'; @@ -84,6 +73,7 @@ module('Acceptance | breadcrumbs-smoke-test', function(hooks) { 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'); + // path adataset1 await click(`${categoryLinkClass}:first-child`); breadcrumbs = findAll(breadcrumbsClass); diff --git a/datahub-web/packages/data-portal/tests/acceptance/tracking-test.ts b/datahub-web/packages/data-portal/tests/acceptance/tracking-test.ts index 73a4589594..87193ad3c1 100644 --- a/datahub-web/packages/data-portal/tests/acceptance/tracking-test.ts +++ b/datahub-web/packages/data-portal/tests/acceptance/tracking-test.ts @@ -7,7 +7,7 @@ module('Acceptance | tracking', function(hooks) { /** * Local piwik event queue */ - let paq: Array>; + let paq: Window['_paq']; /** * Real piwik event queue stored to be restored at a later point diff --git a/datahub-web/packages/data-portal/tests/helpers/analytics/index.ts b/datahub-web/packages/data-portal/tests/helpers/analytics/index.ts index 2c2b25296f..1d5ed855e6 100644 --- a/datahub-web/packages/data-portal/tests/helpers/analytics/index.ts +++ b/datahub-web/packages/data-portal/tests/helpers/analytics/index.ts @@ -1,10 +1,10 @@ -import { getPiwikActivityQueue } from 'wherehows-web/utils/analytics/piwik'; +import { getPiwikActivityQueue } from '@datahub/tracking/utils/piwik'; /** * Convenience function to get the activity queue * @returns {Array>} */ -export const getQueue = (): Array> => (getPiwikActivityQueue() as unknown) as Array>; +export const getQueue = (): Array> => getPiwikActivityQueue(); /** * Returns an iteratee to match an activityId string with a string found at the head position in a queue diff --git a/datahub-web/packages/data-portal/tests/helpers/search/search-acceptance.ts b/datahub-web/packages/data-portal/tests/helpers/search/search-acceptance.ts index 0f32b47869..e1d3e49c12 100644 --- a/datahub-web/packages/data-portal/tests/helpers/search/search-acceptance.ts +++ b/datahub-web/packages/data-portal/tests/helpers/search/search-acceptance.ts @@ -1,8 +1,8 @@ import { visit, click } from '@ember/test-helpers'; import { TestContext } from 'ember-test-helpers'; import { getQueue } from 'wherehows-web/tests/helpers/analytics'; -import { IBaseTrackingEvent } from 'wherehows-web/typings/app/analytics/event-tracking'; -import TrackingService from 'wherehows-web/services/tracking'; +import { IBaseTrackingEvent } from '@datahub/tracking/types/event-tracking'; +import UnifiedTracking from '@datahub/tracking/services/unified-tracking'; /** * Asynchronously navigates to a url and clicks on a search result item @@ -24,10 +24,10 @@ export const mockTrackingEventQueue = ( testContext: TestContext, queue: ReturnType ): ReturnType => { - const trackingService: TrackingService = testContext.owner.lookup('service:tracking'); + const trackingService: UnifiedTracking = testContext.owner.lookup('service:unified-tracking'); // Stub tracking service with trackEvent stub function that adds seen events to supplied queue - trackingService.trackEvent = ({ action, category, name = '' }: IBaseTrackingEvent) => { + trackingService.trackEvent = ({ action, category, name = '' }: IBaseTrackingEvent): void => { // Push new events onto the queue. The the same queue needs to be mutated as expectation by the implementation queue.push([action, category, name]); }; diff --git a/datahub-web/packages/data-portal/tests/integration/components/dataset-deprecation-test.ts b/datahub-web/packages/data-portal/tests/integration/components/dataset-deprecation-test.ts deleted file mode 100644 index 9b61324f51..0000000000 --- a/datahub-web/packages/data-portal/tests/integration/components/dataset-deprecation-test.ts +++ /dev/null @@ -1,97 +0,0 @@ -import { module, test } from 'qunit'; -import { setupRenderingTest } from 'ember-qunit'; -import { render, find, findAll, click, fillIn } from '@ember/test-helpers'; -import hbs from 'htmlbars-inline-precompile'; - -const findInput = (selector: string): HTMLInputElement => { - const input: HTMLInputElement = find(selector) as HTMLInputElement; - - if (!input) { - throw new Error(`${selector} element not found`); - } - return input; -}; - -module('Integration | Component | dataset deprecation', function(hooks): void { - setupRenderingTest(hooks); - - test('it renders', async function(assert): Promise { - assert.expect(4); - - await render(hbs`{{dataset-deprecation}}`); - - assert.ok( - document.querySelector('.dataset-deprecation-toggle__toggle-header__label'), - 'it shows the dataset is deprecation label element' - ); - assert.equal(findAll('#dataset-is-deprecated').length, 1, 'has one input checkbox with known selector'); - assert.equal( - findInput('#dataset-is-deprecated').getAttribute('type'), - 'checkbox', - 'has an input checkbox to toggle deprecation' - ); - assert.equal(findAll('.dataset-deprecation-toggle__actions').length, 1, 'has an actions container'); - }); - - test('setting the deprecated property should toggle the checkbox', async function(assert): Promise { - assert.expect(2); - - this.set('deprecated', true); - - await render(hbs`{{dataset-deprecation deprecated=deprecated}}`); - - assert.ok(findInput('#dataset-is-deprecated').checked, 'checkbox is checked when property is set true'); - - this.set('deprecated', false); - assert.notOk(findInput('#dataset-is-deprecated').checked, 'checkbox is unchecked when property is set false'); - }); - - test('decommissionTime', async function(assert): Promise { - let isDisabled; - assert.expect(3); - - this.set('decommissionTime', void 0); - this.set('deprecated', true); - - await render(hbs`{{dataset-deprecation deprecated=deprecated decommissionTime=decommissionTime}}`); - isDisabled = findInput('.dataset-deprecation-toggle__actions [type=submit]').disabled; - assert.ok(isDisabled, 'submit button is disabled'); - - this.setProperties({ decommissionTime: new Date(), isDirty: true }); - await render(hbs`{{dataset-deprecation deprecated=deprecated decommissionTime=decommissionTime}}`); - await fillIn('.comment-new__content .medium-editor-element', 'text'); - - isDisabled = findInput('.dataset-deprecation-toggle__actions [type=submit]').disabled; - assert.ok(isDisabled, 'submit button is disabled if we only fill in decomissionTime'); - - await click('#acknowledge-deprecation'); - - isDisabled = findInput('.dataset-deprecation-toggle__actions [type=submit]').disabled; - assert.notOk(isDisabled, 'submit button is disabled if we only fill in decomissionTime'); - }); - - test('triggers the onUpdateDeprecation action when submitted', async function(assert): Promise { - let submitActionCallCount = 0; - - this.set('submit', function(deprecated: boolean, note: string): void { - submitActionCallCount++; - assert.equal(deprecated, true, 'action is called with deprecation value of true'); - assert.equal(note, '', 'action is called with an empty deprecation note'); - }); - this.set('decommissionTime', new Date()); - - await render(hbs`{{dataset-deprecation onUpdateDeprecation=(action submit) decommissionTime=decommissionTime}}`); - - assert.equal(submitActionCallCount, 0, 'action is not called on render'); - assert.equal(findInput('#dataset-is-deprecated').checked, false, 'deprecation checkbox is unchecked'); - - await click('#dataset-is-deprecated'); - - assert.equal(findInput('#dataset-is-deprecated').checked, true, 'deprecation checkbox is checked'); - - await click('#acknowledge-deprecation'); - await click('.dataset-deprecation-toggle__actions [type=submit]'); - - assert.equal(submitActionCallCount, 1, 'action is called once'); - }); -}); diff --git a/datahub-web/packages/data-portal/tests/integration/components/search/containers/search-box-test.ts b/datahub-web/packages/data-portal/tests/integration/components/search/containers/search-box-test.ts index 74f9e19056..f4bf0c20a4 100644 --- a/datahub-web/packages/data-portal/tests/integration/components/search/containers/search-box-test.ts +++ b/datahub-web/packages/data-portal/tests/integration/components/search/containers/search-box-test.ts @@ -56,7 +56,7 @@ module('Integration | Component | search/containers/search-box', function(hooks) setupRenderingTest(hooks); hooks.beforeEach(function(): void { - setMockConfig(); + setMockConfig({}); }); hooks.afterEach(function(): void { diff --git a/datahub-web/packages/data-portal/tests/integration/components/search/search-box-test.ts b/datahub-web/packages/data-portal/tests/integration/components/search/search-box-test.ts index 6a6cd87db2..6851c81271 100644 --- a/datahub-web/packages/data-portal/tests/integration/components/search/search-box-test.ts +++ b/datahub-web/packages/data-portal/tests/integration/components/search/search-box-test.ts @@ -81,7 +81,7 @@ module('Integration | Component | search/search-box', function(hooks) { setupRenderingTest(hooks); hooks.beforeEach(function() { - setMockConfig(); + setMockConfig({ showPeople: false }); }); hooks.afterEach(function() { diff --git a/datahub-web/packages/data-portal/tests/stubs/routes/route-info.ts b/datahub-web/packages/data-portal/tests/stubs/routes/route-info.ts index 5b3911aed4..8e8512c73a 100644 --- a/datahub-web/packages/data-portal/tests/stubs/routes/route-info.ts +++ b/datahub-web/packages/data-portal/tests/stubs/routes/route-info.ts @@ -1,13 +1,14 @@ -import { RouteInfo, RouteInfoWithAttributes } from 'wherehows-web/typings/modules/routerjs'; +import RouteInfo from '@ember/routing/-private/route-info'; +import { MaybeRouteInfoWithAttributes } from '@datahub/utils/types/vendor/routerjs'; /** * Define a stub class for a RouteInfo object * @class RouteInfoStub * @implements {RouteInfoWithAttributes} */ -export class RouteInfoStub implements RouteInfoWithAttributes { +export class RouteInfoStub implements MaybeRouteInfoWithAttributes { attributes = {}; - find(_arg0: (this: any, { name }: RouteInfo, i: number) => boolean): RouteInfo | undefined { + find(_arg0: (this: unknown, { name }: RouteInfo, i: number) => boolean): RouteInfo | undefined { return name === this.name ? this : undefined; } constructor( diff --git a/datahub-web/packages/data-portal/tests/stubs/services/metrics.ts b/datahub-web/packages/data-portal/tests/stubs/services/metrics.ts deleted file mode 100644 index 1b170a0a55..0000000000 --- a/datahub-web/packages/data-portal/tests/stubs/services/metrics.ts +++ /dev/null @@ -1,12 +0,0 @@ -import Service from '@ember/service'; - -export default class MetricsServiceStub extends Service { - alias() {} - identify() {} - trackEvent() {} - trackPage() {} - activateAdapters(adapters: Array) { - return adapters; - } - invoke(_methodName: string) {} -} diff --git a/datahub-web/packages/data-portal/tests/unit/utils/entity/flag-guard-test.ts b/datahub-web/packages/data-portal/tests/unit/utils/entity/flag-guard-test.ts deleted file mode 100644 index 141ccf4b66..0000000000 --- a/datahub-web/packages/data-portal/tests/unit/utils/entity/flag-guard-test.ts +++ /dev/null @@ -1,41 +0,0 @@ -import { unGuardedEntities } from 'wherehows-web/utils/entity/flag-guard'; -import { module, skip } from 'qunit'; -import Configurator, { setMockConfig, resetConfig } from 'wherehows-web/services/configurator'; -import { DataModelEntity } from '@datahub/data-models/constants/entity'; - -module('Unit | Utility | entity/flag-guard', function(hooks) { - hooks.beforeEach(function() { - setMockConfig({ showFeatures: true, showUmp: true }); - }); - - hooks.afterEach(function() { - resetConfig(); - }); - - skip('unGuardedEntities behaves as expected', function(assert) { - const configurator = Configurator; - const numOfEntities = Object.values(DataModelEntity).length; - let entities = unGuardedEntities(configurator); - - assert.ok(Array.isArray(entities), `Expected entities to be of type array`); - - assert.equal(entities.length, numOfEntities, `Expected to find ${numOfEntities} entities unguarded`); - - assert.deepEqual( - entities.map(({ displayName }: DataModelEntity): string => displayName).sort(), - Object.keys(DataModelEntity).sort(), - `Expected all elements to be DataModelEntities` - ); - - setMockConfig({ showFeatures: false, showUmp: true }); - - entities = unGuardedEntities(configurator); - - const updatedNumOfEntities = numOfEntities - 1; - assert.equal( - entities.length, - updatedNumOfEntities, - `Expected to find ${updatedNumOfEntities} entities after guarding features` - ); - }); -}); diff --git a/datahub-web/packages/data-portal/tests/unit/utils/helpers/routes-test.ts b/datahub-web/packages/data-portal/tests/unit/utils/helpers/routes-test.ts index 51df0a672f..82e88af9a6 100644 --- a/datahub-web/packages/data-portal/tests/unit/utils/helpers/routes-test.ts +++ b/datahub-web/packages/data-portal/tests/unit/utils/helpers/routes-test.ts @@ -1,6 +1,7 @@ -import { resolveDynamicRouteName, mapOfRouteNamesToResolver } from 'wherehows-web/utils/helpers/routes'; import { module, test } from 'qunit'; import { RouteInfoStub } from 'wherehows-web/tests/stubs/routes/route-info'; +import { resolveDynamicRouteName } from '@datahub/utils/routes/routing'; +import { mapOfRouteNamesToResolver } from '@datahub/data-models/utils/entity-route-name-resolver'; module('Unit | Utility | helpers/routes', function() { test('resolveDynamicRouteName utility resolves RouteInfo attributes correctly', function(assert) { diff --git a/datahub-web/yarn.lock b/datahub-web/yarn.lock index b8c9da786f..9bf23382c0 100644 --- a/datahub-web/yarn.lock +++ b/datahub-web/yarn.lock @@ -1103,6 +1103,13 @@ dependencies: type-detect "4.0.8" +"@sinonjs/commons@^1.3.0": + version "1.6.0" + resolved "https://registry.yarnpkg.com/@sinonjs/commons/-/commons-1.6.0.tgz#ec7670432ae9c8eb710400d112c201a362d83393" + integrity sha512-w4/WHG7C4WWFyE5geCieFJF6MZkbW4VAriol5KlmQXpAQdxvV0p26sqNZOW6Qyw6Y0l9K4g+cHvvczR2sEEpqg== + dependencies: + type-detect "4.0.8" + "@sinonjs/formatio@^3.2.1": version "3.2.1" resolved "https://registry.yarnpkg.com/@sinonjs/formatio/-/formatio-3.2.1.tgz#52310f2f9bcbc67bdac18c94ad4901b95fde267e" @@ -1120,6 +1127,15 @@ array-from "^2.1.1" lodash "^4.17.11" +"@sinonjs/samsam@^3.3.3": + version "3.3.3" + resolved "https://registry.yarnpkg.com/@sinonjs/samsam/-/samsam-3.3.3.tgz#46682efd9967b259b81136b9f120fd54585feb4a" + integrity sha512-bKCMKZvWIjYD0BLGnNrxVuw4dkWCYsLqFOUWw8VgKF/+5Y+mE7LfHWPIYoDXowH+3a9LsWDMo0uAP8YDosPvHQ== + dependencies: + "@sinonjs/commons" "^1.3.0" + array-from "^2.1.1" + lodash "^4.17.15" + "@sinonjs/text-encoding@^0.7.1": version "0.7.1" resolved "https://registry.yarnpkg.com/@sinonjs/text-encoding/-/text-encoding-0.7.1.tgz#8da5c6530915653f3a1f38fd5f101d8c3f8079c5" @@ -5846,10 +5862,10 @@ ember-maybe-in-element@^0.2.0: dependencies: ember-cli-babel "^7.1.0" -ember-medium-editor-fix@^0.0.2: - version "0.0.2" - resolved "https://registry.yarnpkg.com/ember-medium-editor-fix/-/ember-medium-editor-fix-0.0.2.tgz#0c9404a273f7a37290147fb2a320f6b6b50832a0" - integrity sha512-Cw20Wjbv0IagDZDZExg34kmYd5Zf43lk8ob8CF4YHDIFG2lK6tEWpCCFjjW1kwJ3kA110iMQuZB3OxQPk/bmzw== +ember-medium-editor-fix@^0.0.3: + version "0.0.3" + resolved "https://registry.yarnpkg.com/ember-medium-editor-fix/-/ember-medium-editor-fix-0.0.3.tgz#2e3338e93f4a6acc98e2f26b7aa4b530da474d05" + integrity sha512-9CZwEt8V5WUivEIIMrhHe00rj2dmq8ljJmMgKJ3mNGBP2nxDI2hvs3lE9qAXBas9VAwDoaKJods4vMBG7nR/BQ== dependencies: broccoli-funnel "^1.1.0" broccoli-merge-trees "^2.0.0" @@ -6041,6 +6057,16 @@ ember-sinon@^3.1.0, ember-sinon@~3.1.0: ember-cli-babel "^7.1.3" sinon "^7.1.1" +ember-sinon@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/ember-sinon/-/ember-sinon-4.0.0.tgz#bb9bc43b68cc4500457261606a47c7b6ef8c30a3" + integrity sha512-SF6+ak/8yTyD2R8ylz9HYzoziiJCCB6bh/QG/ly4aqAf9eXL+ldQE91IljzEDh6B8feMUpUCGGajbrs9svwhQA== + dependencies: + broccoli-funnel "^2.0.0" + broccoli-merge-trees "^3.0.0" + ember-cli-babel "^7.7.3" + sinon "^7.3.2" + ember-source-channel-url@^1.0.1, ember-source-channel-url@^1.1.0: version "1.2.0" resolved "https://registry.yarnpkg.com/ember-source-channel-url/-/ember-source-channel-url-1.2.0.tgz#77eb9d0889e5f5370e6c70fcb2696c63ff4a34a1" @@ -9257,7 +9283,7 @@ lodash.uniqby@^4.7.0: resolved "https://registry.yarnpkg.com/lodash.uniqby/-/lodash.uniqby-4.7.0.tgz#d99c07a669e9e6d24e1362dfe266c67616af1302" integrity sha1-2ZwHpmnp5tJOE2Lf4mbGdhavEwI= -lodash@^4.0.0, lodash@^4.13.1, lodash@^4.17.10, lodash@^4.17.11, lodash@^4.17.12, lodash@^4.17.13, lodash@^4.17.14, lodash@^4.17.4, lodash@^4.17.5, lodash@^4.3.0, lodash@^4.5.1, lodash@^4.6.1, lodash@~4.17.10: +lodash@^4.0.0, lodash@^4.13.1, lodash@^4.17.10, lodash@^4.17.11, lodash@^4.17.12, lodash@^4.17.13, lodash@^4.17.14, lodash@^4.17.15, lodash@^4.17.4, lodash@^4.17.5, lodash@^4.3.0, lodash@^4.5.1, lodash@^4.6.1, lodash@~4.17.10: version "4.17.15" resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.15.tgz#b447f6670a0455bbfeedd11392eff330ea097548" integrity sha512-8xOcRHvCjnocdS5cpwXQXVzmmh5e5+saE2QGoeQmbKmRS6J3VQppPOIt0MnmE+4xlZoumy0GPG0D0MVIQbNA1A== @@ -9858,6 +9884,17 @@ nise@^1.5.1: lolex "^4.1.0" path-to-regexp "^1.7.0" +nise@^1.5.2: + version "1.5.2" + resolved "https://registry.yarnpkg.com/nise/-/nise-1.5.2.tgz#b6d29af10e48b321b307e10e065199338eeb2652" + integrity sha512-/6RhOUlicRCbE9s+94qCUsyE+pKlVJ5AhIv+jEE7ESKwnbXqulKZ1FYU+XAtHHWE9TinYvAxDUJAb912PwPoWA== + dependencies: + "@sinonjs/formatio" "^3.2.1" + "@sinonjs/text-encoding" "^0.7.1" + just-extend "^4.0.2" + lolex "^4.1.0" + path-to-regexp "^1.7.0" + no-case@^2.2.0: version "2.3.2" resolved "https://registry.yarnpkg.com/no-case/-/no-case-2.3.2.tgz#60b813396be39b3f1288a4c1ed5d1e7d28b464ac" @@ -10934,7 +10971,7 @@ quick-temp@^0.1.2, quick-temp@^0.1.3, quick-temp@^0.1.5, quick-temp@^0.1.8: rimraf "^2.5.4" underscore.string "~3.3.4" -qunit-dom@^0.8.0, qunit-dom@^0.8.4: +qunit-dom@^0.8.0, qunit-dom@^0.8.4, qunit-dom@^0.8.5: version "0.8.5" resolved "https://registry.yarnpkg.com/qunit-dom/-/qunit-dom-0.8.5.tgz#34b7cffb338e631c39955b21bdbe4d774090124e" integrity sha512-I4GSy22ESUkoZYDSYsqFJoMvqhpmgd2iCYlrN7aWLEOdmumUkao3qz24/qVNZd1PAnoOQA78FefzNPRHePFx1A== @@ -11879,6 +11916,19 @@ sinon@^7.1.1: nise "^1.5.1" supports-color "^5.5.0" +sinon@^7.3.2: + version "7.4.2" + resolved "https://registry.yarnpkg.com/sinon/-/sinon-7.4.2.tgz#ecd54158fef2fcfbdb231a3fa55140e8cb02ad6c" + integrity sha512-pY5RY99DKelU3pjNxcWo6XqeB1S118GBcVIIdDi6V+h6hevn1izcg2xv1hTHW/sViRXU7sUOxt4wTUJ3gsW2CQ== + dependencies: + "@sinonjs/commons" "^1.4.0" + "@sinonjs/formatio" "^3.2.1" + "@sinonjs/samsam" "^3.3.3" + diff "^3.5.0" + lolex "^4.2.0" + nise "^1.5.2" + supports-color "^5.5.0" + slash@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/slash/-/slash-1.0.0.tgz#c41f2f6c39fc16d1cd17ad4b5d896114ae470d55"