mirror of
https://github.com/datahub-project/datahub.git
synced 2025-10-17 03:48:15 +00:00
Merge pull request #967 from theseyi/v2-api-2
transition to v2 list and dataset list apis. adds types and transitio…
This commit is contained in:
commit
e33f731f33
@ -1,2 +1,2 @@
|
|||||||
import Component from '@ember/component';
|
import Component from '@ember/component';
|
||||||
export default Component.extend({});
|
export default class extends Component {}
|
@ -1,2 +1,2 @@
|
|||||||
import Component from '@ember/component';
|
import Component from '@ember/component';
|
||||||
export default Component.extend({});
|
export default class extends Component {}
|
@ -1,2 +1,2 @@
|
|||||||
import Component from '@ember/component';
|
import Component from '@ember/component';
|
||||||
export default Component.extend({});
|
export default class extends Component {}
|
@ -1,130 +0,0 @@
|
|||||||
import Component from '@ember/component';
|
|
||||||
import { connect } from 'ember-redux';
|
|
||||||
import { urnRegex, specialFlowUrnRegex } from 'wherehows-web/utils/validators/urn';
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Matches string representing a url path segment that contains a `page` segment followed by a page number
|
|
||||||
* The page number is retained
|
|
||||||
* @type {RegExp}
|
|
||||||
*/
|
|
||||||
const pageRegex = /\/page\/([0-9]+)/i;
|
|
||||||
const nameRegex = /\/name\/([0-9a-z()_{}\[\]\/\s]+)/i;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Takes a node url and parses out the query params and path spec to be included in the link component
|
|
||||||
* @param {String} nodeUrl url linking to the node
|
|
||||||
* @return {Object}
|
|
||||||
*/
|
|
||||||
const nodeUrlToQueryParams = nodeUrl => {
|
|
||||||
const pageMatch = nodeUrl.match(pageRegex);
|
|
||||||
const urnMatch = nodeUrl.match(urnRegex);
|
|
||||||
const flowUrnMatch = nodeUrl.match(specialFlowUrnRegex);
|
|
||||||
const nameMatch = nodeUrl.match(nameRegex);
|
|
||||||
let queryParams = null;
|
|
||||||
|
|
||||||
// If we have a page match, append the page number to eventual urn object
|
|
||||||
// in most cases this page is usually, 1. Might be safe to remove the page pattern
|
|
||||||
// search in its entirety if this is the case
|
|
||||||
if (Array.isArray(pageMatch)) {
|
|
||||||
queryParams = Object.assign({}, queryParams, {
|
|
||||||
page: pageMatch[1]
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
if (Array.isArray(nameMatch)) {
|
|
||||||
let match = nameMatch[1];
|
|
||||||
match = match.split('/page')[0];
|
|
||||||
|
|
||||||
queryParams = Object.assign({}, queryParams, {
|
|
||||||
name: match
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
// If we have a urn match, append the urn to eventual query params object
|
|
||||||
if (Array.isArray(urnMatch) || Array.isArray(flowUrnMatch)) {
|
|
||||||
const urn = urnMatch || [flowUrnMatch[1]];
|
|
||||||
|
|
||||||
queryParams = Object.assign({}, queryParams, {
|
|
||||||
// Extract the entire match as urn value
|
|
||||||
urn: urn[0]
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
return queryParams;
|
|
||||||
};
|
|
||||||
|
|
||||||
const getNodes = (entity, state = {}) => query =>
|
|
||||||
({
|
|
||||||
get datasets() {
|
|
||||||
return state[entity].nodesByUrn[query];
|
|
||||||
},
|
|
||||||
get metrics() {
|
|
||||||
return state[entity].nodesByName[query];
|
|
||||||
},
|
|
||||||
get flows() {
|
|
||||||
return this.datasets;
|
|
||||||
}
|
|
||||||
}[entity]);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Selector function that takes a Redux Store to extract
|
|
||||||
* state props for the browser-rail
|
|
||||||
* @param {Object} state current app state tree
|
|
||||||
* @return {{nodes: (*|Array)}}
|
|
||||||
*/
|
|
||||||
const stateToComputed = state => {
|
|
||||||
const header = 'Refine by';
|
|
||||||
// Extracts the current entity active in the browse view
|
|
||||||
const { browseEntity: { currentEntity = '' } = {} } = state;
|
|
||||||
// Retrieves properties for the current entity from the state tree
|
|
||||||
const { browseEntity: { [currentEntity]: { query: { urn, name } } } } = state;
|
|
||||||
// Removes `s` from the end of each entity name. Ember routes for individual entities are singular, and also
|
|
||||||
// returned entities contain id prop that is the singular type name, suffixed with Id, e.g. metricId
|
|
||||||
// datasets -> dataset, metrics -> metric, flows -> flow
|
|
||||||
const singularName = currentEntity.slice(0, -1);
|
|
||||||
const query =
|
|
||||||
{
|
|
||||||
datasets: urn,
|
|
||||||
metrics: name,
|
|
||||||
flows: urn
|
|
||||||
}[currentEntity] || null;
|
|
||||||
let nodes = getNodes(currentEntity, state)(query) || [];
|
|
||||||
/**
|
|
||||||
* Creates dynamic query link params for each node
|
|
||||||
* @type {Array} list of child nodes or datasets to render
|
|
||||||
*/
|
|
||||||
nodes = nodes.map(({ nodeName, nodeUrl, [`${singularName}Id`]: id, application = '' }) => {
|
|
||||||
nodeUrl = String(nodeUrl);
|
|
||||||
const node = {
|
|
||||||
title: nodeName,
|
|
||||||
text: nodeName
|
|
||||||
};
|
|
||||||
|
|
||||||
// If the id prop (datasetId|metricId|flowId) is truthy, then it is a standalone entity
|
|
||||||
if (id) {
|
|
||||||
let idNode = Object.assign({}, node);
|
|
||||||
|
|
||||||
if (singularName === 'flow' && application) {
|
|
||||||
idNode = Object.assign({}, idNode, {
|
|
||||||
queryParams: {
|
|
||||||
name: application
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
return Object.assign({}, idNode, {
|
|
||||||
route: `${currentEntity}.${singularName}`,
|
|
||||||
model: id
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
return Object.assign({}, node, {
|
|
||||||
route: `browse.entity`,
|
|
||||||
model: currentEntity,
|
|
||||||
queryParams: nodeUrlToQueryParams(nodeUrl)
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
return { nodes, header };
|
|
||||||
};
|
|
||||||
export default connect(stateToComputed)(Component.extend({}));
|
|
@ -0,0 +1,77 @@
|
|||||||
|
import Component from '@ember/component';
|
||||||
|
import { get, set } from '@ember/object';
|
||||||
|
import { task } from 'ember-concurrency';
|
||||||
|
import { nodeToQueryParams } from 'wherehows-web/constants';
|
||||||
|
import { IBrowserRouteParams } from 'wherehows-web/routes/browse/entity';
|
||||||
|
import { readPlatforms } from 'wherehows-web/utils/api/platforms/platform';
|
||||||
|
import { arrayMap } from 'wherehows-web/utils/array';
|
||||||
|
import { IReadDatasetsOptionBag } from 'wherehows-web/typings/api/datasets/dataset';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Describes a node with parameters used by dynamic-link component to create links to items lised in the rail
|
||||||
|
* @interface IRailNode
|
||||||
|
*/
|
||||||
|
interface IRailNode {
|
||||||
|
title: string;
|
||||||
|
text: string;
|
||||||
|
route: 'browse.entity';
|
||||||
|
model: IBrowserRouteParams['entity'];
|
||||||
|
queryParams: Partial<IReadDatasetsOptionBag>;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Given a platform and entity, returns a closure function that maps each node to a
|
||||||
|
* list of IRailNode
|
||||||
|
* @param {string} platform
|
||||||
|
* @param {IBrowserRouteParams['entity']} entity
|
||||||
|
* @returns {(array: string[]) => IRailNode[]}
|
||||||
|
*/
|
||||||
|
export const mapNodeToRoute = (
|
||||||
|
platform: string,
|
||||||
|
entity: IBrowserRouteParams['entity']
|
||||||
|
): ((array: string[]) => IRailNode[]) =>
|
||||||
|
arrayMap((node: string): IRailNode => ({
|
||||||
|
title: node,
|
||||||
|
text: node,
|
||||||
|
route: 'browse.entity',
|
||||||
|
model: entity,
|
||||||
|
queryParams: nodeToQueryParams({ platform, node })
|
||||||
|
}));
|
||||||
|
|
||||||
|
export default class BrowserRail extends Component {
|
||||||
|
/**
|
||||||
|
* Passed in parameters containing route or queryparameters values to be used in request
|
||||||
|
* @type {IBrowserRouteParams}
|
||||||
|
* @memberof BrowserRail
|
||||||
|
*/
|
||||||
|
params: IBrowserRouteParams;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Maintains a list the nodes platforms or prefixes available in the selected entity
|
||||||
|
* @type {Array<IRailNode>}
|
||||||
|
* @memberof BrowserRail
|
||||||
|
*/
|
||||||
|
nodes: Array<IRailNode> = [];
|
||||||
|
|
||||||
|
didUpdateAttrs() {
|
||||||
|
this._super(...arguments);
|
||||||
|
get(this, 'getNodesTask').perform();
|
||||||
|
}
|
||||||
|
|
||||||
|
didInsertElement() {
|
||||||
|
this._super(...arguments);
|
||||||
|
get(this, 'getNodesTask').perform();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the nodes: platforms, or prefixes for the selected entity
|
||||||
|
* @type {TaskProperty<Promise<string[]>> & {perform: (a?: {} | undefined) => TaskInstance<Promise<string[]>>}}
|
||||||
|
* @memberof BrowserRail
|
||||||
|
*/
|
||||||
|
getNodesTask = task(function*(this: BrowserRail): IterableIterator<Promise<Array<string>>> {
|
||||||
|
const { prefix, platform, entity } = get(this, 'params');
|
||||||
|
const nodes: Array<IRailNode> = mapNodeToRoute(platform, entity)(yield readPlatforms({ platform, prefix }));
|
||||||
|
|
||||||
|
set(this, 'nodes', nodes);
|
||||||
|
}).drop();
|
||||||
|
}
|
@ -0,0 +1,86 @@
|
|||||||
|
import Component from '@ember/component';
|
||||||
|
import { get, setProperties } from '@ember/object';
|
||||||
|
import { task, TaskInstance } from 'ember-concurrency';
|
||||||
|
import { IBrowserRouteParams } from 'wherehows-web/routes/browse/entity';
|
||||||
|
import { readDatasetsCount } from 'wherehows-web/utils/api/datasets/dataset';
|
||||||
|
|
||||||
|
// Describes the index signature for the metadata object used in the browser summary component
|
||||||
|
type IBrowserMetadata = {
|
||||||
|
[K in IBrowserRouteParams['entity']]: {
|
||||||
|
count: number;
|
||||||
|
currentPlatform: string;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// Describes the index signature for the strategy pattern in the getCountsTask
|
||||||
|
type ICountTaskStrategy = { [K in IBrowserRouteParams['entity']]: TaskInstance<Promise<number>> };
|
||||||
|
|
||||||
|
export default class BrowserSummary extends Component {
|
||||||
|
/**
|
||||||
|
* Passed in parameters containing route or query parameters values to be used in request
|
||||||
|
* @type {IBrowserRouteParams}
|
||||||
|
* @memberof BrowserSummary
|
||||||
|
*/
|
||||||
|
params: IBrowserRouteParams;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Lists the types of entities supported
|
||||||
|
* @type {ReadonlyArray<string>}
|
||||||
|
* @memberof BrowserSummary
|
||||||
|
*/
|
||||||
|
entities: ReadonlyArray<IBrowserRouteParams['entity']> = ['datasets', 'metrics', 'flows'];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Contains the properties for each entity to displayed UI metadata
|
||||||
|
* @type {IBrowserMetadata}
|
||||||
|
* @memberof BrowserSummary
|
||||||
|
*/
|
||||||
|
metadata: IBrowserMetadata = this.entities.reduce(
|
||||||
|
(metadata, entity) => ({
|
||||||
|
...metadata,
|
||||||
|
[entity]: { count: 0, currentPlatform: '' }
|
||||||
|
}),
|
||||||
|
<IBrowserMetadata>{}
|
||||||
|
);
|
||||||
|
|
||||||
|
didUpdateAttrs() {
|
||||||
|
this._super(...arguments);
|
||||||
|
get(this, 'getCountsTask').perform();
|
||||||
|
}
|
||||||
|
|
||||||
|
didInsertElement() {
|
||||||
|
this._super(...arguments);
|
||||||
|
get(this, 'getCountsTask').perform();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Parent task to retrieve counts for each IBrowserParams entity type as needed
|
||||||
|
* @type {TaskProperty<TaskInstance<Promise<number>>> & {perform: (a?: {} | undefined) => TaskInstance<TaskInstance<Promise<number>>>}}
|
||||||
|
* @memberof BrowserSummary
|
||||||
|
*/
|
||||||
|
getCountsTask = task(function*(this: BrowserSummary): IterableIterator<TaskInstance<Promise<number>>> {
|
||||||
|
const { prefix, platform, entity } = get(this, 'params');
|
||||||
|
|
||||||
|
return (<ICountTaskStrategy>{
|
||||||
|
datasets: yield get(this, 'getDatasetsCountTask').perform(platform, prefix)
|
||||||
|
})[entity];
|
||||||
|
});
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets and sets the dataset count
|
||||||
|
* @type {TaskProperty<Promise<number>> & {perform: (a1: string, a2: string) => TaskInstance<Promise<number>>}}
|
||||||
|
* @memberof BrowserSummary
|
||||||
|
*/
|
||||||
|
getDatasetsCountTask = task(function*(
|
||||||
|
this: BrowserSummary,
|
||||||
|
platform: string,
|
||||||
|
prefix: string
|
||||||
|
): IterableIterator<Promise<number>> {
|
||||||
|
const entityMetadata = get(get(this, 'metadata'), 'datasets');
|
||||||
|
|
||||||
|
setProperties(entityMetadata, {
|
||||||
|
count: yield readDatasetsCount({ prefix, platform }),
|
||||||
|
currentPlatform: platform
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
@ -1,19 +0,0 @@
|
|||||||
import Component from '@ember/component';
|
|
||||||
import { connect } from 'ember-redux';
|
|
||||||
|
|
||||||
const entities = ['datasets', 'metrics', 'flows']; // hardcoded here to maintain the sort order
|
|
||||||
/**
|
|
||||||
* Selector function that takes a Redux Store to extract
|
|
||||||
* state props for the browser-view
|
|
||||||
* @param {Object} browseData
|
|
||||||
* @return {Object}
|
|
||||||
*/
|
|
||||||
const stateToComputed = ({ browse: { browseData = {} } = {} }) => ({
|
|
||||||
browseData: entities.map(browseDatum =>
|
|
||||||
Object.assign(
|
|
||||||
{ entity: browseDatum }, // assigns key name to resulting object
|
|
||||||
browseData[browseDatum]
|
|
||||||
)
|
|
||||||
)
|
|
||||||
});
|
|
||||||
export default connect(stateToComputed)(Component.extend({}));
|
|
@ -1,59 +0,0 @@
|
|||||||
import Component from '@ember/component';
|
|
||||||
import { connect } from 'ember-redux';
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Extract the childIds for an entity under a specific category
|
|
||||||
* @param entity
|
|
||||||
* @param state
|
|
||||||
*/
|
|
||||||
const getChildIds = (entity, state = {}) => query =>
|
|
||||||
({
|
|
||||||
get datasets() {
|
|
||||||
return state[entity].byUrn[query];
|
|
||||||
},
|
|
||||||
get metrics() {
|
|
||||||
return state[entity].byName[query];
|
|
||||||
},
|
|
||||||
get flows() {
|
|
||||||
// Flows are retrieved by name as well
|
|
||||||
return this.datasets;
|
|
||||||
}
|
|
||||||
}[entity]);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Selector function that takes a Redux Store to extract
|
|
||||||
* state props for the browser-rail
|
|
||||||
* @return {Object} mapping of computed props to state
|
|
||||||
*/
|
|
||||||
const stateToComputed = state => {
|
|
||||||
// Extracts the current entity active in the browse view
|
|
||||||
const { browseEntity: { currentEntity = '' } = {} } = state;
|
|
||||||
// Retrieves properties for the current entity from the state tree
|
|
||||||
const { browseEntity: { [currentEntity]: { query: { urn, name } } } } = state;
|
|
||||||
// Default urn to null, which represents the top-level parent
|
|
||||||
|
|
||||||
const query =
|
|
||||||
{
|
|
||||||
datasets: urn,
|
|
||||||
metrics: name,
|
|
||||||
flows: urn
|
|
||||||
}[currentEntity] || null;
|
|
||||||
// Read the list of ids child entity ids associated with the urn
|
|
||||||
const childIds = getChildIds(currentEntity, state)(query) || [];
|
|
||||||
// Read the list of entities, stored in the byId property
|
|
||||||
const { [currentEntity]: { byId: entities } } = state;
|
|
||||||
/**
|
|
||||||
* Takes the currentEntity which is plural and strips the trailing `s` and appends
|
|
||||||
* the entity sub route
|
|
||||||
* @type {string}
|
|
||||||
*/
|
|
||||||
const entityRoute = `${currentEntity}.${currentEntity.slice(0, -1)}`;
|
|
||||||
|
|
||||||
return {
|
|
||||||
currentEntity,
|
|
||||||
entityRoute,
|
|
||||||
entities: childIds.map(id => entities[id]) // Extract out the intersection of childIds from the entity map
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
export default connect(stateToComputed)(Component.extend({}));
|
|
@ -0,0 +1,69 @@
|
|||||||
|
import Component from '@ember/component';
|
||||||
|
import { get, setProperties } from '@ember/object';
|
||||||
|
import { task } from 'ember-concurrency';
|
||||||
|
import { IBrowserRouteParams } from 'wherehows-web/routes/browse/entity';
|
||||||
|
import { IDatasetView } from 'wherehows-web/typings/api/datasets/dataset';
|
||||||
|
import { readDatasets } from 'wherehows-web/utils/api/datasets/dataset';
|
||||||
|
|
||||||
|
// Describes the index signature for strategy pattern in the getEntitiesTask
|
||||||
|
type IGetEntityTaskStrategy = { [K in IBrowserRouteParams['entity']]: Promise<Array<IDatasetView>> };
|
||||||
|
|
||||||
|
export default class BrowserViewport extends Component {
|
||||||
|
/**
|
||||||
|
* Passed in parameters containing route or queryparameters values to be used in request
|
||||||
|
* @type {IBrowserRouteParams}
|
||||||
|
* @memberof BrowserViewport
|
||||||
|
*/
|
||||||
|
params: IBrowserRouteParams;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Initial value for the entity being viewed
|
||||||
|
* @memberof BrowserViewport
|
||||||
|
*/
|
||||||
|
currentEntity: IBrowserRouteParams['entity'] = 'datasets';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Ember route for the entities being rendered
|
||||||
|
* @type {string}
|
||||||
|
* @memberof BrowserViewport
|
||||||
|
*/
|
||||||
|
entityRoute = '';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* List of entities to be rendered in view
|
||||||
|
* @type {Array<IDatasetView>}
|
||||||
|
* @memberof BrowserViewport
|
||||||
|
*/
|
||||||
|
entities: Array<IDatasetView> = [];
|
||||||
|
|
||||||
|
didUpdateAttrs() {
|
||||||
|
this._super(...arguments);
|
||||||
|
get(this, 'getEntitiesTask').perform();
|
||||||
|
}
|
||||||
|
|
||||||
|
didInsertElement() {
|
||||||
|
this._super(...arguments);
|
||||||
|
get(this, 'getEntitiesTask').perform();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Async requests for the list of entities and sets the value on class
|
||||||
|
* @type {TaskProperty<Promise<IDatasetView[]>> & {perform: (a?: {} | undefined) => TaskInstance<Promise<IDatasetView[]>>}}
|
||||||
|
* @memberof BrowserViewport
|
||||||
|
*/
|
||||||
|
getEntitiesTask = task(function*(this: BrowserViewport): IterableIterator<Promise<Array<IDatasetView>>> {
|
||||||
|
const { prefix, platform, entity } = get(this, 'params');
|
||||||
|
|
||||||
|
const entities = (<IGetEntityTaskStrategy>{
|
||||||
|
datasets: readDatasets({ platform, prefix }),
|
||||||
|
metrics: Promise.resolve([]),
|
||||||
|
flows: Promise.resolve([])
|
||||||
|
})[entity];
|
||||||
|
|
||||||
|
setProperties(this, {
|
||||||
|
entities: yield entities,
|
||||||
|
currentEntity: entity,
|
||||||
|
entityRoute: `${entity}.${entity.slice(0, -1)}`
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
@ -3,7 +3,7 @@ import { inject } from '@ember/service';
|
|||||||
import ComputedProperty, { filter } from '@ember/object/computed';
|
import ComputedProperty, { filter } from '@ember/object/computed';
|
||||||
import { set, get, computed, getProperties } from '@ember/object';
|
import { set, get, computed, getProperties } from '@ember/object';
|
||||||
import { assert } from '@ember/debug';
|
import { assert } from '@ember/debug';
|
||||||
import { task, Task } from 'ember-concurrency';
|
import { task } from 'ember-concurrency';
|
||||||
|
|
||||||
import UserLookup from 'wherehows-web/services/user-lookup';
|
import UserLookup from 'wherehows-web/services/user-lookup';
|
||||||
import CurrentUser from 'wherehows-web/services/current-user';
|
import CurrentUser from 'wherehows-web/services/current-user';
|
||||||
@ -138,9 +138,7 @@ export default class DatasetAuthors extends Component {
|
|||||||
* @type {Task<Promise<Array<IOwner>>, void>}
|
* @type {Task<Promise<Array<IOwner>>, void>}
|
||||||
* @memberof DatasetAuthors
|
* @memberof DatasetAuthors
|
||||||
*/
|
*/
|
||||||
saveOwners: Task<Promise<Array<IOwner>>, void> = task(function*(
|
saveOwners = task(function*(this: DatasetAuthors): IterableIterator<Promise<Array<IOwner>>> {
|
||||||
this: DatasetAuthors
|
|
||||||
): IterableIterator<Promise<Array<IOwner>>> {
|
|
||||||
yield get(this, 'save')(get(this, 'owners'));
|
yield get(this, 'save')(get(this, 'owners'));
|
||||||
}).drop();
|
}).drop();
|
||||||
|
|
||||||
|
@ -406,7 +406,6 @@ export default class DatasetCompliance extends ObservableDecorator {
|
|||||||
}
|
}
|
||||||
|
|
||||||
didInsertElement(this: DatasetCompliance) {
|
didInsertElement(this: DatasetCompliance) {
|
||||||
// @ts-ignore ts limitation with the ember object model, fixed in ember 3.1 with es5 getters
|
|
||||||
get(this, 'complianceAvailabilityTask').perform();
|
get(this, 'complianceAvailabilityTask').perform();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -428,7 +427,6 @@ export default class DatasetCompliance extends ObservableDecorator {
|
|||||||
complianceAvailabilityTask = task(function*(
|
complianceAvailabilityTask = task(function*(
|
||||||
this: DatasetCompliance
|
this: DatasetCompliance
|
||||||
): IterableIterator<TaskInstance<Promise<Array<IDataPlatform>>>> {
|
): IterableIterator<TaskInstance<Promise<Array<IDataPlatform>>>> {
|
||||||
// @ts-ignore ts limitation with the ember object model, fixed in ember 3.1 with es5 getters
|
|
||||||
yield get(this, 'getPlatformPoliciesTask').perform();
|
yield get(this, 'getPlatformPoliciesTask').perform();
|
||||||
|
|
||||||
const supportedPurgePolicies = get(this, 'supportedPurgePolicies');
|
const supportedPurgePolicies = get(this, 'supportedPurgePolicies');
|
||||||
|
@ -1,2 +1,2 @@
|
|||||||
import Component from '@ember/component';
|
import Component from '@ember/component';
|
||||||
export default Component.extend({});
|
export default class extends Component {}
|
@ -1,12 +0,0 @@
|
|||||||
import Component from '@ember/component';
|
|
||||||
|
|
||||||
export default Component.extend({
|
|
||||||
tagName: 'ul',
|
|
||||||
classNames: ['nacho-filter-rail'],
|
|
||||||
|
|
||||||
checkboxComponent: 'search/checkbox-group',
|
|
||||||
radioGroupComponent: 'search/radio-group',
|
|
||||||
dropdownComponent: 'search/dropdown-selection',
|
|
||||||
dateRangeComponent: 'search/daterange-selection',
|
|
||||||
linksComponent: 'search/link-group'
|
|
||||||
});
|
|
17
wherehows-web/app/components/search/filter-rail.ts
Normal file
17
wherehows-web/app/components/search/filter-rail.ts
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
import Component from '@ember/component';
|
||||||
|
|
||||||
|
export default class extends Component {
|
||||||
|
tagName = 'ul';
|
||||||
|
|
||||||
|
classNames = ['nacho-filter-rail'];
|
||||||
|
|
||||||
|
checkboxComponent = 'search/checkbox-group';
|
||||||
|
|
||||||
|
radioGroupComponent = 'search/radio-group';
|
||||||
|
|
||||||
|
dropdownComponent = 'search/dropdown-selection';
|
||||||
|
|
||||||
|
dateRangeComponent = 'search/daterange-selection';
|
||||||
|
|
||||||
|
linksComponent = 'search/link-group';
|
||||||
|
}
|
@ -1,7 +0,0 @@
|
|||||||
import Component from '@ember/component';
|
|
||||||
|
|
||||||
export default Component.extend({
|
|
||||||
tagName: 'li',
|
|
||||||
|
|
||||||
classNames: ['nacho-filter-card']
|
|
||||||
});
|
|
7
wherehows-web/app/components/search/link-group.ts
Normal file
7
wherehows-web/app/components/search/link-group.ts
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
import Component from '@ember/component';
|
||||||
|
|
||||||
|
export default class extends Component {
|
||||||
|
tagName = 'li';
|
||||||
|
|
||||||
|
classNames = ['nacho-filter-card'];
|
||||||
|
}
|
@ -1,9 +1,15 @@
|
|||||||
|
import { IBrowserRouteParams } from 'wherehows-web/routes/browse/entity';
|
||||||
|
import { IReadDatasetsOptionBag } from 'wherehows-web/typings/api/datasets/dataset';
|
||||||
|
import { isDatasetPlatform, isDatasetPrefix } from 'wherehows-web/utils/validators/platform';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The known/supported list of dataset platforms
|
* The known/supported list of dataset platforms
|
||||||
* @enum {string}
|
* @enum {string}
|
||||||
*/
|
*/
|
||||||
enum DatasetPlatform {
|
enum DatasetPlatform {
|
||||||
Kafka = 'kafka',
|
Kafka = 'kafka',
|
||||||
|
KafkaLc = 'kafka-lc',
|
||||||
|
Presto = 'presto',
|
||||||
Espresso = 'espresso',
|
Espresso = 'espresso',
|
||||||
Oracle = 'oracle',
|
Oracle = 'oracle',
|
||||||
MySql = 'mysql',
|
MySql = 'mysql',
|
||||||
@ -13,7 +19,39 @@ enum DatasetPlatform {
|
|||||||
Couchbase = 'couchbase',
|
Couchbase = 'couchbase',
|
||||||
Voldemort = 'voldemort',
|
Voldemort = 'voldemort',
|
||||||
Venice = 'venice',
|
Venice = 'venice',
|
||||||
Hive = 'hive'
|
Vector = 'vector',
|
||||||
|
Hive = 'hive',
|
||||||
|
FollowFeed = 'followfeed'
|
||||||
}
|
}
|
||||||
|
|
||||||
export { DatasetPlatform };
|
/**
|
||||||
|
* Given a platform and a new node, composes an object of query parameters to be used in the request for
|
||||||
|
* platforms or datasets
|
||||||
|
* @param {(Pick<IBrowserRouteParams, 'platform'> & { node: string })} { platform, node }
|
||||||
|
* @returns {Partial<IReadDatasetsOptionBag>}
|
||||||
|
*/
|
||||||
|
const nodeToQueryParams = ({
|
||||||
|
platform,
|
||||||
|
node
|
||||||
|
}: Pick<IBrowserRouteParams, 'platform'> & { node: string }): Partial<IReadDatasetsOptionBag> => {
|
||||||
|
const queryParams = {};
|
||||||
|
|
||||||
|
if (isDatasetPrefix(node)) {
|
||||||
|
Object.assign(queryParams, { prefix: node.replace(/\//g, '') });
|
||||||
|
}
|
||||||
|
|
||||||
|
// If the node is a platform, assign that value to the query params object
|
||||||
|
if (isDatasetPlatform(node)) {
|
||||||
|
Object.assign(queryParams, { platform: node });
|
||||||
|
}
|
||||||
|
|
||||||
|
// If a platform value is already present override the previously set value
|
||||||
|
// there should not be a 'future' platform value if one already exists
|
||||||
|
if (platform) {
|
||||||
|
Object.assign(queryParams, { platform });
|
||||||
|
}
|
||||||
|
|
||||||
|
return queryParams;
|
||||||
|
};
|
||||||
|
|
||||||
|
export { DatasetPlatform, nodeToQueryParams };
|
||||||
|
@ -1,12 +0,0 @@
|
|||||||
import Controller from '@ember/controller';
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Handles query params for browse.entity route
|
|
||||||
*/
|
|
||||||
export default Controller.extend({
|
|
||||||
queryParams: ['page', 'urn', 'size', 'name'],
|
|
||||||
page: 1,
|
|
||||||
urn: '',
|
|
||||||
name: '',
|
|
||||||
size: 10
|
|
||||||
});
|
|
19
wherehows-web/app/controllers/browse/entity.ts
Normal file
19
wherehows-web/app/controllers/browse/entity.ts
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
import Controller from '@ember/controller';
|
||||||
|
import { IBrowserRouteParams } from 'wherehows-web/routes/browse/entity';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handles query params for browse.entity route
|
||||||
|
*/
|
||||||
|
export default class extends Controller implements IBrowserRouteParams {
|
||||||
|
queryParams = ['page', 'prefix', 'size', 'platform'];
|
||||||
|
|
||||||
|
entity: IBrowserRouteParams['entity'];
|
||||||
|
|
||||||
|
page = 1;
|
||||||
|
|
||||||
|
prefix = '';
|
||||||
|
|
||||||
|
platform = '';
|
||||||
|
|
||||||
|
size = 10;
|
||||||
|
}
|
@ -1,30 +0,0 @@
|
|||||||
import Route from '@ember/routing/route';
|
|
||||||
import route from 'ember-redux/route';
|
|
||||||
import AuthenticatedRouteMixin from 'ember-simple-auth/mixins/authenticated-route-mixin';
|
|
||||||
import { asyncRequestBrowseData } from 'wherehows-web/actions/browse';
|
|
||||||
|
|
||||||
// TODO: DSS-6581 Create URL retrieval module
|
|
||||||
// TODO: Route should transition to browse/entity, pay attention to the fact that
|
|
||||||
// this route initializes store with entity metrics on entry
|
|
||||||
const entityUrls = {
|
|
||||||
datasets: '/api/v1/datasets',
|
|
||||||
metrics: '/api/v1/metrics',
|
|
||||||
flows: '/api/v1/flows'
|
|
||||||
};
|
|
||||||
|
|
||||||
export default route({
|
|
||||||
model: (dispatch, { entity = 'datasets', page = '1' }) => dispatch(asyncRequestBrowseData(page, entity, entityUrls))
|
|
||||||
})(
|
|
||||||
Route.extend(AuthenticatedRouteMixin, {
|
|
||||||
/**
|
|
||||||
* Browse route does not render any content, but hydrates the store with initial data transition to child route
|
|
||||||
* @param {Object} model result from model call
|
|
||||||
* @param {Ember.Transition} transition
|
|
||||||
*/
|
|
||||||
afterModel(model, transition) {
|
|
||||||
// Extract the entity being viewed from the transition state
|
|
||||||
const { params: { 'browse.entity': { entity = 'datasets' } = {} } } = transition;
|
|
||||||
this.transitionTo('browse.entity', entity);
|
|
||||||
}
|
|
||||||
})
|
|
||||||
);
|
|
13
wherehows-web/app/routes/browse.ts
Normal file
13
wherehows-web/app/routes/browse.ts
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
import Route from '@ember/routing/route';
|
||||||
|
import AuthenticatedRouteMixin from 'ember-simple-auth/mixins/authenticated-route-mixin';
|
||||||
|
import Ember from 'ember'; // type import, no emit
|
||||||
|
|
||||||
|
export default class extends Route.extend(AuthenticatedRouteMixin) {
|
||||||
|
afterModel(_model: any, transition: Ember.Transition & { params: any }) {
|
||||||
|
// Extract the entity being viewed from the transition state
|
||||||
|
const { params: { 'browse.entity': { entity = 'datasets' } = {} } } = transition;
|
||||||
|
|
||||||
|
// transition to entity specific sub route
|
||||||
|
this.transitionTo('browse.entity', entity);
|
||||||
|
}
|
||||||
|
}
|
@ -1,36 +0,0 @@
|
|||||||
import Route from '@ember/routing/route';
|
|
||||||
import route from 'ember-redux/route';
|
|
||||||
import AuthenticatedRouteMixin from 'ember-simple-auth/mixins/authenticated-route-mixin';
|
|
||||||
import { asyncRequestEntityQueryData } from 'wherehows-web/actions/browse/entity';
|
|
||||||
|
|
||||||
// TODO: DSS-6581 Create URL retrieval module
|
|
||||||
const listUrl = '/api/v1/list';
|
|
||||||
const queryParamsKeys = ['page', 'urn', 'name'];
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Creates a route handler for browse.entity route
|
|
||||||
* entity can be any (datasets|metrics|flows)
|
|
||||||
* @type {Ember.Route}
|
|
||||||
*/
|
|
||||||
const BrowseEntityRoute = Route.extend(AuthenticatedRouteMixin, {
|
|
||||||
queryParams: queryParamsKeys.reduce(
|
|
||||||
(queryParams, param) =>
|
|
||||||
Object.assign({}, queryParams, {
|
|
||||||
[param]: { refreshModel: true }
|
|
||||||
}),
|
|
||||||
{}
|
|
||||||
)
|
|
||||||
});
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Ember Redux decorator wraps incoming route object and injects the redux store.dispatch method as the
|
|
||||||
* first argument
|
|
||||||
*/
|
|
||||||
export default route({
|
|
||||||
/**
|
|
||||||
*
|
|
||||||
* @param dispatch
|
|
||||||
* @param params
|
|
||||||
*/
|
|
||||||
model: (dispatch, params) => dispatch(asyncRequestEntityQueryData(params, listUrl, { queryParamsKeys }))
|
|
||||||
})(BrowseEntityRoute);
|
|
35
wherehows-web/app/routes/browse/entity.ts
Normal file
35
wherehows-web/app/routes/browse/entity.ts
Normal file
@ -0,0 +1,35 @@
|
|||||||
|
import Route from '@ember/routing/route';
|
||||||
|
import { set } from '@ember/object';
|
||||||
|
import AuthenticatedRouteMixin from 'ember-simple-auth/mixins/authenticated-route-mixin';
|
||||||
|
import { refreshModelQueryParams } from 'wherehows-web/utils/helpers/routes';
|
||||||
|
import EntityController from 'wherehows-web/controllers/browse/entity';
|
||||||
|
|
||||||
|
const queryParamsKeys = ['page', 'prefix', 'platform', 'size'];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Describes the route parameter interface for the browser route
|
||||||
|
* @export
|
||||||
|
* @interface IBrowserRouteParams
|
||||||
|
*/
|
||||||
|
export interface IBrowserRouteParams {
|
||||||
|
entity: 'datasets' | 'metrics' | 'flows';
|
||||||
|
page: number;
|
||||||
|
size: number;
|
||||||
|
platform: string;
|
||||||
|
prefix: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export default class extends Route.extend(AuthenticatedRouteMixin) {
|
||||||
|
queryParams = refreshModelQueryParams(queryParamsKeys);
|
||||||
|
|
||||||
|
setupController(controller: EntityController, { entity }: IBrowserRouteParams) {
|
||||||
|
this._super(...arguments);
|
||||||
|
// sets the entity property on the controller
|
||||||
|
set(controller, 'entity', entity);
|
||||||
|
}
|
||||||
|
|
||||||
|
model(params: IBrowserRouteParams): IBrowserRouteParams {
|
||||||
|
const { entity, platform, page, size, prefix } = params;
|
||||||
|
return { entity, platform, page, size, prefix };
|
||||||
|
}
|
||||||
|
}
|
@ -1,6 +1,4 @@
|
|||||||
import Ember from 'ember';
|
import Route from '@ember/routing/route';
|
||||||
import AuthenticatedRouteMixin from 'ember-simple-auth/mixins/authenticated-route-mixin';
|
import AuthenticatedRouteMixin from 'ember-simple-auth/mixins/authenticated-route-mixin';
|
||||||
|
|
||||||
const { Route } = Ember;
|
export default class extends Route.extend(AuthenticatedRouteMixin) {}
|
||||||
|
|
||||||
export default Route.extend(AuthenticatedRouteMixin);
|
|
||||||
|
@ -1,9 +1,8 @@
|
|||||||
import Ember from 'ember';
|
import Route from '@ember/routing/route';
|
||||||
|
|
||||||
const { Route } = Ember;
|
export default class extends Route {
|
||||||
|
|
||||||
export default Route.extend({
|
|
||||||
redirect() {
|
redirect() {
|
||||||
this.transitionTo('browse.entity', 'datasets');
|
// default transition to datasets route if user enters through index
|
||||||
|
return this.transitionTo('browse.entity', 'datasets');
|
||||||
|
}
|
||||||
}
|
}
|
||||||
});
|
|
||||||
|
@ -7,11 +7,28 @@ $browse-nav-border-color: set-color(grey, light);
|
|||||||
|
|
||||||
&__item {
|
&__item {
|
||||||
@include flex-column-center;
|
@include flex-column-center;
|
||||||
|
opacity: 1;
|
||||||
|
transition: opacity 0.2s ease-in-out;
|
||||||
|
}
|
||||||
|
|
||||||
|
&__alt {
|
||||||
|
@include flex-column-center;
|
||||||
|
opacity: 0;
|
||||||
|
transition: opacity 0.2s ease-in-out;
|
||||||
|
font-weight: fw(normal, 6);
|
||||||
|
font-size: 24px;
|
||||||
|
position: absolute;
|
||||||
|
margin: 0;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
right: 0;
|
||||||
|
bottom: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
&__entity {
|
&__entity {
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
border-bottom: 1px solid $browse-nav-border-color;
|
border-bottom: 1px solid $browse-nav-border-color;
|
||||||
|
min-height: 90px;
|
||||||
/**
|
/**
|
||||||
* Overrides bootstrap .active class.
|
* Overrides bootstrap .active class.
|
||||||
* Ember applies this for active route links
|
* Ember applies this for active route links
|
||||||
@ -23,9 +40,24 @@ $browse-nav-border-color: set-color(grey, light);
|
|||||||
/**
|
/**
|
||||||
* Resets the bootstrap grid margins
|
* Resets the bootstrap grid margins
|
||||||
*/
|
*/
|
||||||
&[class|="col"]{
|
&[class|='col'] {
|
||||||
padding: 0;
|
padding: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
&#{&} {
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
.browse-nav__item {
|
||||||
|
opacity: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.browse-nav__alt {
|
||||||
|
opacity: 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
&__entity + &__entity {
|
&__entity + &__entity {
|
||||||
@ -43,4 +75,21 @@ $browse-nav-border-color: set-color(grey, light);
|
|||||||
text-transform: capitalize;
|
text-transform: capitalize;
|
||||||
margin: 0;
|
margin: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
&__subtext {
|
||||||
|
font-weight: fw(normal, 2);
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.browse-viewport {
|
||||||
|
&__loading {
|
||||||
|
$loading-container-height: 170px;
|
||||||
|
height: $loading-container-height;
|
||||||
|
width: 95%;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.nav-link-to {
|
||||||
|
width: 100%;
|
||||||
}
|
}
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
@import "restyle";
|
@import 'restyle';
|
||||||
|
|
||||||
$pad-width: 16px;
|
$pad-width: 16px;
|
||||||
|
|
||||||
@ -19,6 +19,11 @@ $pad-width: 16px;
|
|||||||
.nacho-filter-rail {
|
.nacho-filter-rail {
|
||||||
@include restyle(filter-rail);
|
@include restyle(filter-rail);
|
||||||
|
|
||||||
|
&__loading {
|
||||||
|
$loading-container-height: 150px;
|
||||||
|
height: $loading-container-height;
|
||||||
|
}
|
||||||
|
|
||||||
&__header {
|
&__header {
|
||||||
color: set-color(blue, oxford);
|
color: set-color(blue, oxford);
|
||||||
background-color: set-color(white, base);
|
background-color: set-color(white, base);
|
||||||
@ -37,7 +42,8 @@ $pad-width: 16px;
|
|||||||
border-bottom: none;
|
border-bottom: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
&__label, .ember-radio-button {
|
&__label,
|
||||||
|
.ember-radio-button {
|
||||||
display: block;
|
display: block;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -58,7 +64,7 @@ $pad-width: 16px;
|
|||||||
white-space: nowrap;
|
white-space: nowrap;
|
||||||
|
|
||||||
&::before {
|
&::before {
|
||||||
content: "+";
|
content: '+';
|
||||||
color: set-color(grey, mid);
|
color: set-color(grey, mid);
|
||||||
padding: 0 10px 0 10px;
|
padding: 0 10px 0 10px;
|
||||||
}
|
}
|
||||||
@ -83,7 +89,7 @@ $pad-width: 16px;
|
|||||||
|
|
||||||
&:before,
|
&:before,
|
||||||
&:after {
|
&:after {
|
||||||
content: " ";
|
content: ' ';
|
||||||
height: 20px;
|
height: 20px;
|
||||||
width: 20px;
|
width: 20px;
|
||||||
position: absolute;
|
position: absolute;
|
||||||
@ -108,7 +114,7 @@ $pad-width: 16px;
|
|||||||
border-width: 6px;
|
border-width: 6px;
|
||||||
}
|
}
|
||||||
|
|
||||||
input[type=radio] {
|
input[type='radio'] {
|
||||||
opacity: 0;
|
opacity: 0;
|
||||||
margin: 16px 2px 0 0;
|
margin: 16px 2px 0 0;
|
||||||
position: absolute;
|
position: absolute;
|
||||||
|
@ -1,14 +1,17 @@
|
|||||||
.ellipsis-loader {
|
.ellipsis-loader {
|
||||||
display: block;
|
$container-width: 70px;
|
||||||
min-width: 70px;
|
min-width: $container-width;
|
||||||
|
height: 100%;
|
||||||
|
|
||||||
&__ellipsis {
|
&__ellipsis {
|
||||||
|
$ellipsis-height: 15px;
|
||||||
margin-left: -25px;
|
margin-left: -25px;
|
||||||
position: relative;
|
position: relative;
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
justify-content: left;
|
justify-content: left;
|
||||||
height: 15px;
|
min-height: $ellipsis-height;
|
||||||
|
height: 100%;
|
||||||
}
|
}
|
||||||
|
|
||||||
&__circle {
|
&__circle {
|
||||||
|
@ -1,15 +1,14 @@
|
|||||||
<div class="row">
|
<div class="row">
|
||||||
{{!-- TODO: DSS-7029 browser-view is really a misnomer and should be renamed to browser-summary :( --}}
|
{{browser/containers/browser-summary params=model}}
|
||||||
{{browser/containers/browser-view}}
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="col-sm-3 wh-sidebar">
|
<div class="col-sm-3 wh-sidebar">
|
||||||
{{browser/containers/browser-rail header=header}}
|
{{browser/containers/browser-rail header=header params=model}}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="col-sm-9 wh-main-content">
|
<div class="col-sm-9 wh-main-content">
|
||||||
{{browser/containers/browser-viewport}}
|
{{browser/containers/browser-viewport params=model}}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{{outlet}}
|
{{outlet}}
|
||||||
|
@ -1,6 +1,16 @@
|
|||||||
{{#search/filter-rail header=header as |rail|}}
|
{{#search/filter-rail header=header as |rail|}}
|
||||||
|
|
||||||
|
{{#if nodesTask.isRunning}}
|
||||||
|
<div class="nacho-filter-rail__loading">
|
||||||
|
{{pendulum-ellipsis-animation}}
|
||||||
|
</div>
|
||||||
|
{{else}}
|
||||||
|
|
||||||
{{rail.links
|
{{rail.links
|
||||||
links=nodes}}
|
links=nodes}}
|
||||||
|
|
||||||
|
{{/if}}
|
||||||
|
|
||||||
{{/search/filter-rail}}
|
{{/search/filter-rail}}
|
||||||
|
|
||||||
{{yield}}
|
{{yield}}
|
||||||
|
@ -0,0 +1,29 @@
|
|||||||
|
<nav role="navigation">
|
||||||
|
<ul class="nav navbar-nav browse-nav">
|
||||||
|
{{#each-in metadata as |entity meta|}}
|
||||||
|
{{#nav-link
|
||||||
|
"browse.entity" entity
|
||||||
|
(query-params page=1 platform="" prefix="")
|
||||||
|
tagName="li"
|
||||||
|
class="col-md-4 browse-nav__entity"}}
|
||||||
|
|
||||||
|
{{#if (and datasetTask.isRunning (eq entity "datasets"))}}
|
||||||
|
{{pendulum-ellipsis-animation}}
|
||||||
|
{{else}}
|
||||||
|
|
||||||
|
<span class="browse-nav__alt">
|
||||||
|
See all
|
||||||
|
</span>
|
||||||
|
<div class="browse-nav__item">
|
||||||
|
<p class="browse-nav__count">{{meta.count}}</p>
|
||||||
|
<p class="browse-nav__title">{{entity}}</p>
|
||||||
|
<p class="browse-nav__subtext">
|
||||||
|
{{if meta.currentPlatform 'in' 'in total'}} {{meta.currentPlatform}}
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{{/if}}
|
||||||
|
{{/nav-link}}
|
||||||
|
{{/each-in}}
|
||||||
|
</ul>
|
||||||
|
</nav>
|
@ -1,16 +0,0 @@
|
|||||||
<nav role="navigation">
|
|
||||||
<ul class="nav navbar-nav browse-nav">
|
|
||||||
{{#each browseData as |entity|}}
|
|
||||||
{{#nav-link
|
|
||||||
"browse.entity" entity.entity
|
|
||||||
(query-params page=1 urn="" name="")
|
|
||||||
tagName="li"
|
|
||||||
class="col-md-4 browse-nav__entity"}}
|
|
||||||
<div class="browse-nav__item">
|
|
||||||
<p class="browse-nav__count">{{entity.count}}</p>
|
|
||||||
<p class="browse-nav__title">{{entity.entity}}</p>
|
|
||||||
</div>
|
|
||||||
{{/nav-link}}
|
|
||||||
{{/each}}
|
|
||||||
</ul>
|
|
||||||
</nav>
|
|
@ -61,6 +61,8 @@
|
|||||||
{{/table.body}}
|
{{/table.body}}
|
||||||
{{/dataset-table}}
|
{{/dataset-table}}
|
||||||
{{else}}
|
{{else}}
|
||||||
|
{{#if entities}}
|
||||||
|
|
||||||
{{#dataset-table
|
{{#dataset-table
|
||||||
fields=entities as |table|}}
|
fields=entities as |table|}}
|
||||||
{{#table.body as |body|}}
|
{{#table.body as |body|}}
|
||||||
@ -68,9 +70,9 @@
|
|||||||
table.data as |entity|}}
|
table.data as |entity|}}
|
||||||
{{#body.row as |row|}}
|
{{#body.row as |row|}}
|
||||||
{{#row.cell}}
|
{{#row.cell}}
|
||||||
{{#link-to entityRoute entity.id}}
|
{{#link-to entityRoute 'urn' (query-params urn=entity.uri)}}
|
||||||
<span class="entity-list__title">
|
<span class="entity-list__title">
|
||||||
{{entity.name}}
|
{{entity.nativeName}}
|
||||||
</span>
|
</span>
|
||||||
{{/link-to}}
|
{{/link-to}}
|
||||||
<br>
|
<br>
|
||||||
@ -80,15 +82,11 @@
|
|||||||
{{entity.description}}
|
{{entity.description}}
|
||||||
{{/if}}
|
{{/if}}
|
||||||
|
|
||||||
{{#if (eq currentEntity 'datasets')}}
|
{{#if entity.modifiedTime}}
|
||||||
{{dataset-owner-list owners=entity.owners datasetName=entity.name}}
|
|
||||||
{{/if}}
|
|
||||||
|
|
||||||
{{#if entity.formatedModified}}
|
|
||||||
<span>Last Modified:</span>
|
<span>Last Modified:</span>
|
||||||
|
|
||||||
<span title="{{entity.formatedModified}}">
|
<span title="{{entity.modifiedTime}}">
|
||||||
{{moment-from-now entity.formatedModified }}
|
{{moment-from-now entity.modifiedTime }}
|
||||||
</span>
|
</span>
|
||||||
{{/if}}
|
{{/if}}
|
||||||
{{/row.cell}}
|
{{/row.cell}}
|
||||||
@ -99,4 +97,13 @@
|
|||||||
{{/each}}
|
{{/each}}
|
||||||
{{/table.body}}
|
{{/table.body}}
|
||||||
{{/dataset-table}}
|
{{/dataset-table}}
|
||||||
|
|
||||||
|
{{else}}
|
||||||
|
|
||||||
|
{{empty-state
|
||||||
|
heading="No entities found"
|
||||||
|
subHead="Could not find any datasets in our records"
|
||||||
|
}}
|
||||||
|
|
||||||
|
{{/if}}
|
||||||
{{/if}}
|
{{/if}}
|
||||||
|
@ -1 +1 @@
|
|||||||
{{browser/browser-rail nodes=nodes}}
|
{{browser/browser-rail nodes=nodes nodesTask=getNodesTask}}
|
||||||
|
@ -0,0 +1 @@
|
|||||||
|
{{browser/browser-summary metadata=metadata datasetTask=getDatasetsCountTask}}
|
@ -1 +0,0 @@
|
|||||||
{{browser/browser-view browseData=browseData}}
|
|
@ -1 +1,18 @@
|
|||||||
|
{{#if getEntitiesTask.isRunning}}
|
||||||
|
|
||||||
|
<div class="browse-viewport__loading">
|
||||||
|
{{pendulum-ellipsis-animation}}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{{else}}
|
||||||
|
{{#if getEntitiesTask.last.isError}}
|
||||||
|
|
||||||
|
{{empty-state
|
||||||
|
heading="An error occurred getting entities"
|
||||||
|
subHead=getEntitiesTask.last.error
|
||||||
|
}}
|
||||||
|
|
||||||
|
{{else}}
|
||||||
{{browser/browser-viewport currentEntity=currentEntity entities=entities entityRoute=entityRoute}}
|
{{browser/browser-viewport currentEntity=currentEntity entities=entities entityRoute=entityRoute}}
|
||||||
|
{{/if}}
|
||||||
|
{{/if}}
|
||||||
|
@ -1,29 +1,6 @@
|
|||||||
{{!-- rename to entity-actions --}}
|
|
||||||
<ul>
|
<ul>
|
||||||
{{#each actionItems as |actionItem|}}
|
{{#each actionItems as |actionItem|}}
|
||||||
{{!--maybe dynamic link with actionItem only for each?--}}
|
|
||||||
<li></li>
|
<li></li>
|
||||||
{{/each}}
|
{{/each}}
|
||||||
</ul>
|
</ul>
|
||||||
<ul class="datasetTableLinks">
|
|
||||||
<li class="text-center no-link">
|
|
||||||
<span class="source">
|
|
||||||
{{ dataset.source }}
|
|
||||||
</span>
|
|
||||||
</li>
|
|
||||||
<li class="text-center" title="ownership">
|
|
||||||
{{dataset-owner dataset=dataset action="owned"}}
|
|
||||||
</li>
|
|
||||||
<li class="text-center" title="favorite">
|
|
||||||
{{dataset-favorite dataset=dataset action="didFavorite"}}
|
|
||||||
</li>
|
|
||||||
<li class="text-center" title="watch">
|
|
||||||
{{dataset-watch dataset=dataset getDatasets='getDatasets'}}
|
|
||||||
</li>
|
|
||||||
<li class="text-center">
|
|
||||||
<a href="/lineage/dataset/{{dataset.id}}"
|
|
||||||
title="lineage for {{dataset.name}}">
|
|
||||||
<i class="fa fa-sitemap"></i>
|
|
||||||
</a>
|
|
||||||
</li>
|
|
||||||
</ul>
|
|
||||||
|
@ -1,2 +1,2 @@
|
|||||||
{{!-- https://github.com/zoltan-nz/ember-bootstrap-nav-link/blob/master/addon/components/nav-link-to.js --}}
|
{{!-- https://github.com/zoltan-nz/ember-bootstrap-nav-link/blob/master/addon/components/nav-link-to.js --}}
|
||||||
<a href="{{hrefForA}}">{{yield}}</a>
|
<a class="nav-link-to" href="{{hrefForA}}">{{yield}}</a>
|
||||||
|
@ -65,4 +65,31 @@ interface IDatasetViewGetResponse {
|
|||||||
dataset?: IDatasetView;
|
dataset?: IDatasetView;
|
||||||
}
|
}
|
||||||
|
|
||||||
export { IDatasetViewGetResponse, IDatasetView, IDatasetGetResponse, IDataset };
|
/**
|
||||||
|
* Describes the response from the GET /datasets api
|
||||||
|
* @interface IDatasetsGetResponse
|
||||||
|
*/
|
||||||
|
interface IDatasetsGetResponse {
|
||||||
|
total: number;
|
||||||
|
start: number;
|
||||||
|
count: number;
|
||||||
|
elements: Array<IDatasetView>;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Describes the options for the dataset
|
||||||
|
* @interface IReadDatasetsOptionBag
|
||||||
|
*/
|
||||||
|
interface IReadDatasetsOptionBag {
|
||||||
|
platform: DatasetPlatform | string;
|
||||||
|
prefix: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export {
|
||||||
|
IDatasetViewGetResponse,
|
||||||
|
IDatasetView,
|
||||||
|
IDatasetGetResponse,
|
||||||
|
IDataset,
|
||||||
|
IDatasetsGetResponse,
|
||||||
|
IReadDatasetsOptionBag
|
||||||
|
};
|
||||||
|
@ -12,14 +12,14 @@ declare module 'ember-concurrency' {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export interface TaskProperty<T> extends ComputedProperty<T> {
|
export interface TaskProperty<T> extends ComputedProperty<T> {
|
||||||
cancelOn(eventNames: string[]): this;
|
cancelOn(eventNames: string): this;
|
||||||
debug(): this;
|
debug(): this;
|
||||||
drop(): this;
|
drop(): this;
|
||||||
enqueue(): this;
|
enqueue(): this;
|
||||||
group(groupPath: string): this;
|
group(groupPath: string): this;
|
||||||
keepLatest(): this;
|
keepLatest(): this;
|
||||||
maxConcurrency(n: number): this;
|
maxConcurrency(n: number): this;
|
||||||
on(eventNames: string[]): this;
|
on(eventNames: string): this;
|
||||||
restartable(): this;
|
restartable(): this;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -50,7 +50,7 @@ declare module 'ember-concurrency' {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type Task<T, P> = TaskProperty<T> & {
|
type Task<T, P> = TaskProperty<T> & {
|
||||||
perform: ComputedProperty<P>;
|
perform: P;
|
||||||
readonly isIdle: boolean;
|
readonly isIdle: boolean;
|
||||||
readonly isQueued: boolean;
|
readonly isQueued: boolean;
|
||||||
readonly isRunning: boolean;
|
readonly isRunning: boolean;
|
||||||
|
@ -2,11 +2,18 @@ import { warn } from '@ember/debug';
|
|||||||
import {
|
import {
|
||||||
IDataset,
|
IDataset,
|
||||||
IDatasetGetResponse,
|
IDatasetGetResponse,
|
||||||
|
IDatasetsGetResponse,
|
||||||
IDatasetView,
|
IDatasetView,
|
||||||
IDatasetViewGetResponse
|
IDatasetViewGetResponse,
|
||||||
|
IReadDatasetsOptionBag
|
||||||
} from 'wherehows-web/typings/api/datasets/dataset';
|
} from 'wherehows-web/typings/api/datasets/dataset';
|
||||||
import { getHeaders, getJSON } from 'wherehows-web/utils/api/fetcher';
|
import { getHeaders, getJSON } from 'wherehows-web/utils/api/fetcher';
|
||||||
import { datasetsUrlRoot, datasetUrlById } from 'wherehows-web/utils/api/datasets/shared';
|
import {
|
||||||
|
datasetsCountUrl,
|
||||||
|
datasetsUrl,
|
||||||
|
datasetsUrlRoot,
|
||||||
|
datasetUrlById
|
||||||
|
} from 'wherehows-web/utils/api/datasets/shared';
|
||||||
import { ApiStatus } from 'wherehows-web/utils/api';
|
import { ApiStatus } from 'wherehows-web/utils/api';
|
||||||
|
|
||||||
// TODO: DSS-6122 Create and move to Error module
|
// TODO: DSS-6122 Create and move to Error module
|
||||||
@ -62,7 +69,7 @@ const readDatasetView = async (id: number): Promise<IDatasetView> => {
|
|||||||
* @return {string}
|
* @return {string}
|
||||||
*/
|
*/
|
||||||
const datasetIdTranslationUrlByUrn = (urn: string): string => {
|
const datasetIdTranslationUrlByUrn = (urn: string): string => {
|
||||||
return `${datasetsUrlRoot}/urntoid/${encodeURIComponent(urn)}`;
|
return `${datasetsUrlRoot('v1')}/urntoid/${encodeURIComponent(urn)}`;
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -92,4 +99,33 @@ const datasetUrnToId = async (urn: string): Promise<number> => {
|
|||||||
return datasetId;
|
return datasetId;
|
||||||
};
|
};
|
||||||
|
|
||||||
export { readDataset, datasetUrnToId, readDatasetView };
|
/**
|
||||||
|
* Fetches the datasets for a platform, and prefix and returns the list of datasets in the
|
||||||
|
* response
|
||||||
|
* @param {IReadDatasetsOptionBag} {
|
||||||
|
* platform,
|
||||||
|
* prefix
|
||||||
|
* }
|
||||||
|
* @returns {Promise<IDatasetsGetResponse['elements']>}
|
||||||
|
*/
|
||||||
|
const readDatasets = async ({
|
||||||
|
platform,
|
||||||
|
prefix
|
||||||
|
}: IReadDatasetsOptionBag): Promise<IDatasetsGetResponse['elements']> => {
|
||||||
|
const url = datasetsUrl({ platform, prefix });
|
||||||
|
const response = await getJSON<IDatasetsGetResponse>({ url });
|
||||||
|
|
||||||
|
return response ? [...response.elements] : [];
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the number of datasets, if provided, using the platform and prefix also
|
||||||
|
* @param {Partial<IReadDatasetsOptionBag>} { platform, prefix }
|
||||||
|
* @returns {Promise<number>}
|
||||||
|
*/
|
||||||
|
const readDatasetsCount = async ({ platform, prefix }: Partial<IReadDatasetsOptionBag>): Promise<number> => {
|
||||||
|
const url = datasetsCountUrl({ platform, prefix });
|
||||||
|
return await getJSON<number>({ url });
|
||||||
|
};
|
||||||
|
|
||||||
|
export { readDataset, datasetUrnToId, readDatasetView, readDatasets, readDatasetsCount };
|
||||||
|
@ -1,14 +1,53 @@
|
|||||||
import { getApiRoot } from 'wherehows-web/utils/api/shared';
|
import { IReadDatasetsOptionBag } from 'wherehows-web/typings/api/datasets/dataset';
|
||||||
|
import { ApiVersion, getApiRoot } from 'wherehows-web/utils/api/shared';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Defines the endpoint for datasets
|
* Defines the endpoint for datasets
|
||||||
* @type {string}
|
* @type {string}
|
||||||
*/
|
*/
|
||||||
export const datasetsUrlRoot = `${getApiRoot()}/datasets`;
|
export const datasetsUrlRoot = (version: ApiVersion) => `${getApiRoot(version)}/datasets`;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Constructs a url to get a dataset with a given id
|
* Constructs a url to get a dataset with a given id
|
||||||
* @param {number} id the id of the dataset
|
* @param {number} id the id of the dataset
|
||||||
* @return {string} the dataset url
|
* @return {string} the dataset url
|
||||||
*/
|
*/
|
||||||
export const datasetUrlById = (id: number): string => `${datasetsUrlRoot}/${id}`;
|
export const datasetUrlById = (id: number): string => `${datasetsUrlRoot('v1')}/${id}`;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Composes the datasets count url from a given platform and or prefix if provided
|
||||||
|
* @param {Partial<IReadDatasetsOptionBag>} [{ platform, prefix }={}]
|
||||||
|
* @returns {string}
|
||||||
|
*/
|
||||||
|
export const datasetsCountUrl = ({ platform, prefix }: Partial<IReadDatasetsOptionBag> = {}): string => {
|
||||||
|
const urlRoot = `${datasetsUrlRoot('v2')}/count`;
|
||||||
|
|
||||||
|
if (platform && prefix) {
|
||||||
|
`${urlRoot}/count`;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (platform) {
|
||||||
|
return `${urlRoot}/platform/${platform}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
return urlRoot;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Composes the datasets url using the platform and prefix if one is provided
|
||||||
|
* @param {IReadDatasetsOptionBag} { platform, prefix }
|
||||||
|
* @returns {string}
|
||||||
|
*/
|
||||||
|
export const datasetsUrl = ({ platform, prefix }: IReadDatasetsOptionBag): string => {
|
||||||
|
const urlRoot = datasetsUrlRoot('v2');
|
||||||
|
|
||||||
|
if (platform && prefix) {
|
||||||
|
return `${urlRoot}/platform/${platform}/${prefix}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (platform) {
|
||||||
|
return `${urlRoot}/platform/${platform}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
return urlRoot;
|
||||||
|
};
|
||||||
|
44
wherehows-web/app/utils/api/platforms/platform.ts
Normal file
44
wherehows-web/app/utils/api/platforms/platform.ts
Normal file
@ -0,0 +1,44 @@
|
|||||||
|
import { IReadDatasetsOptionBag } from 'wherehows-web/typings/api/datasets/dataset';
|
||||||
|
import { getJSON } from 'wherehows-web/utils/api/fetcher';
|
||||||
|
import { ApiVersion, getApiRoot } from 'wherehows-web/utils/api/shared';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Generates the base url for a platform given a specified ApiVersion
|
||||||
|
* @param {ApiVersion} version
|
||||||
|
*/
|
||||||
|
export const platformsUrlRoot = (version: ApiVersion) => `${getApiRoot(version)}/platforms`;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Composes a url for platforms, uses platform and prefix if provided
|
||||||
|
* @param {IReadDatasetsOptionBag} {platform}
|
||||||
|
* @param {IReadDatasetsOptionBag} {prefix}
|
||||||
|
* @returns {string}
|
||||||
|
*/
|
||||||
|
const platformsUrl = ({ platform, prefix }: IReadDatasetsOptionBag): string => {
|
||||||
|
const urlRoot = platformsUrlRoot('v2');
|
||||||
|
|
||||||
|
if (platform && prefix) {
|
||||||
|
return `${urlRoot}/${platform}/${prefix}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (platform) {
|
||||||
|
return `${urlRoot}/${platform}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
return urlRoot;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reads the platforms endpoint and returns a list of platforms or prefixes found
|
||||||
|
* @param {IReadDatasetsOptionBag} {platform}
|
||||||
|
* @param {IReadDatasetsOptionBag} {prefix}
|
||||||
|
* @returns {Promise<Array<string>>}
|
||||||
|
*/
|
||||||
|
const readPlatforms = async ({ platform, prefix }: IReadDatasetsOptionBag): Promise<Array<string>> => {
|
||||||
|
const url = platformsUrl({ platform, prefix });
|
||||||
|
const response = await getJSON<Array<string>>({ url });
|
||||||
|
|
||||||
|
return response || [];
|
||||||
|
};
|
||||||
|
|
||||||
|
export { readPlatforms };
|
7
wherehows-web/app/utils/helpers/routes.ts
Normal file
7
wherehows-web/app/utils/helpers/routes.ts
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
/**
|
||||||
|
* For each given query param, creates an object with `refreshModel` set to true
|
||||||
|
* @param {Array<string>} [params=[]]
|
||||||
|
* @returns {{}}
|
||||||
|
*/
|
||||||
|
export const refreshModelQueryParams = (params: Array<string> = []): {} =>
|
||||||
|
params.reduce((queryParams, param) => ({ ...queryParams, [param]: { refreshModel: true } }), {});
|
@ -1,3 +0,0 @@
|
|||||||
const compliantRegex = /^[a-zA-Z0-9.!#$%&'*+\/=?^_`{|}~-]+@[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?(?:\.[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)*$/g;
|
|
||||||
|
|
||||||
export default candidateEmail => compliantRegex.test(String(candidateEmail));
|
|
12
wherehows-web/app/utils/validators/email.ts
Normal file
12
wherehows-web/app/utils/validators/email.ts
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
/**
|
||||||
|
* Matches an a string to an email pattern
|
||||||
|
* @type {RegExp}
|
||||||
|
*/
|
||||||
|
const emailRegex = /^[a-zA-Z0-9.!#$%&'*+\/=?^_`{|}~-]+@[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?(?:\.[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)*$/g;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks that a candidate string is an email
|
||||||
|
* @param candidateEmail {string}
|
||||||
|
* @return {boolean}
|
||||||
|
*/
|
||||||
|
export default (candidateEmail: string): boolean => emailRegex.test(String(candidateEmail));
|
23
wherehows-web/app/utils/validators/platform.ts
Normal file
23
wherehows-web/app/utils/validators/platform.ts
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
/**
|
||||||
|
* Matches a string that is prefixed by [platform and suffixed by ]
|
||||||
|
* Captures the match in the sole capture group
|
||||||
|
* @type {RegExp}
|
||||||
|
*/
|
||||||
|
const platformRegex = /\[platform=([^]]*)]/;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks that a string represents a dataset platform
|
||||||
|
* @param {string} candidate
|
||||||
|
* @returns {boolean}
|
||||||
|
*/
|
||||||
|
const isDatasetPlatform = (candidate: string): boolean => platformRegex.test(candidate);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks that a string represents a dataset prefix
|
||||||
|
* @param {string} candidate
|
||||||
|
* @returns {boolean}
|
||||||
|
*/
|
||||||
|
const isDatasetPrefix = (candidate: string): boolean =>
|
||||||
|
!isDatasetPlatform(candidate) && ['.', '/'].includes(candidate.slice(-1));
|
||||||
|
|
||||||
|
export { platformRegex, isDatasetPlatform, isDatasetPrefix };
|
@ -1,4 +1,6 @@
|
|||||||
import { faker } from 'ember-cli-mirage';
|
import { faker } from 'ember-cli-mirage';
|
||||||
|
import { IFunctionRouteHandler, IMirageServer } from 'wherehows-web/typings/ember-cli-mirage';
|
||||||
|
import { ApiStatus } from 'wherehows-web/utils/api/shared';
|
||||||
import { getDatasetColumns } from 'wherehows-web/mirage/helpers/columns';
|
import { getDatasetColumns } from 'wherehows-web/mirage/helpers/columns';
|
||||||
import { getDatasetCompliance } from 'wherehows-web/mirage/helpers/compliance';
|
import { getDatasetCompliance } from 'wherehows-web/mirage/helpers/compliance';
|
||||||
import { getComplianceDataTypes } from 'wherehows-web/mirage/helpers/compliance-data-types';
|
import { getComplianceDataTypes } from 'wherehows-web/mirage/helpers/compliance-data-types';
|
||||||
@ -17,8 +19,6 @@ import { getDatasetReferences } from 'wherehows-web/mirage/helpers/dataset-refer
|
|||||||
import { getDatasetSample } from 'wherehows-web/mirage/helpers/dataset-sample';
|
import { getDatasetSample } from 'wherehows-web/mirage/helpers/dataset-sample';
|
||||||
import { getDatasetView } from 'wherehows-web/mirage/helpers/dataset-view';
|
import { getDatasetView } from 'wherehows-web/mirage/helpers/dataset-view';
|
||||||
import { getOwnerTypes } from 'wherehows-web/mirage/helpers/owner-types';
|
import { getOwnerTypes } from 'wherehows-web/mirage/helpers/owner-types';
|
||||||
import { IFunctionRouteHandler, IMirageServer } from 'wherehows-web/typings/ember-cli-mirage';
|
|
||||||
import { ApiStatus } from 'wherehows-web/utils/api/shared';
|
|
||||||
import { getConfig } from 'wherehows-web/mirage/helpers/config';
|
import { getConfig } from 'wherehows-web/mirage/helpers/config';
|
||||||
import { getAuth } from 'wherehows-web/mirage/helpers/authenticate';
|
import { getAuth } from 'wherehows-web/mirage/helpers/authenticate';
|
||||||
import { aclAuth } from 'wherehows-web/mirage/helpers/aclauth';
|
import { aclAuth } from 'wherehows-web/mirage/helpers/aclauth';
|
||||||
|
Loading…
x
Reference in New Issue
Block a user