mirror of
https://github.com/datahub-project/datahub.git
synced 2025-12-26 09:26:22 +00:00
adds typescript. adds mirage. updates dependencies. begins separating api concerns from app: datasets. removes jshint.
adds mirage model+factory for compliance suggestion updates gitignore: removes typings. adds environment.d.ts declaration file for config/environment. adds unit test for datasets-test. adds extracted datasets api file updates destroy app test helper updates compliance component to work with modified api from compliance api: removes isSubject, adds securityClassification updates uploaded compliance shape validation. updates the dataset route adds typescript files to prettier linting updates gitignore with vscode
This commit is contained in:
parent
ab31c4706e
commit
4d0d746fe3
1
.gitignore
vendored
1
.gitignore
vendored
@ -25,3 +25,4 @@ logs/
|
||||
dist
|
||||
tmp
|
||||
/commit
|
||||
/.vscode/
|
||||
|
||||
@ -1,4 +1,7 @@
|
||||
module.exports = {
|
||||
globals: {
|
||||
server: true,
|
||||
},
|
||||
"extends": "eslint:recommended",
|
||||
"parserOptions": {
|
||||
"ecmaVersion": 8,
|
||||
|
||||
4
wherehows-web/.gitignore
vendored
4
wherehows-web/.gitignore
vendored
@ -11,8 +11,6 @@ npm-debug.log*
|
||||
testem.log
|
||||
yarn-error.log
|
||||
|
||||
# idea *.ts files
|
||||
typings/
|
||||
|
||||
# vscode
|
||||
jsconfig.json
|
||||
/.vscode/
|
||||
|
||||
@ -62,12 +62,6 @@ const datasetClassificationKey = 'complianceInfo.datasetClassification';
|
||||
*/
|
||||
const datasetClassifiersKeys = Object.keys(datasetClassifiers);
|
||||
|
||||
/**
|
||||
* String constant identifying the classified fields on the security spec
|
||||
* @type {String}
|
||||
*/
|
||||
const policyFieldClassificationKey = 'complianceInfo.fieldClassification';
|
||||
|
||||
/**
|
||||
* A reference to the compliance policy entities on the complianceInfo map
|
||||
* @type {string}
|
||||
@ -347,15 +341,16 @@ export default Component.extend({
|
||||
*/
|
||||
mergeComplianceEntitiesAndColumnFields(columnIdFieldsToCurrentPrivacyPolicy = {}, truncatedColumnFields = []) {
|
||||
return truncatedColumnFields.map(({ fieldName: identifierField, dataType }) => {
|
||||
const { [identifierField]: { identifierType, isSubject, logicalType } } = columnIdFieldsToCurrentPrivacyPolicy;
|
||||
const { [identifierField]: classification } = get(this, policyFieldClassificationKey) || {};
|
||||
const {
|
||||
[identifierField]: { identifierType, logicalType, securityClassification }
|
||||
} = columnIdFieldsToCurrentPrivacyPolicy;
|
||||
|
||||
return {
|
||||
identifierField,
|
||||
dataType,
|
||||
identifierType,
|
||||
isSubject,
|
||||
logicalType,
|
||||
classification
|
||||
classification: securityClassification
|
||||
};
|
||||
});
|
||||
},
|
||||
@ -381,7 +376,7 @@ export default Component.extend({
|
||||
|
||||
return columnFieldNames.reduce((acc, identifierField) => {
|
||||
const currentPrivacyAttrs = getKeysOnField(
|
||||
['identifierType', 'isSubject', 'logicalType'],
|
||||
['identifierType', 'logicalType', 'securityClassification'],
|
||||
identifierField,
|
||||
complianceEntities
|
||||
);
|
||||
@ -483,9 +478,10 @@ export default Component.extend({
|
||||
let defaultTypeClassification = defaultFieldDataTypeClassification[logicalType] || null;
|
||||
// If the identifierType is of custom, set the default classification to the of a CUSTOM_ID, otherwise use value
|
||||
// based on logicalType
|
||||
defaultTypeClassification = identifierType === fieldIdentifierTypes.custom.value
|
||||
? defaultFieldDataTypeClassification['CUSTOM_ID']
|
||||
: defaultTypeClassification;
|
||||
defaultTypeClassification =
|
||||
identifierType === fieldIdentifierTypes.custom.value
|
||||
? defaultFieldDataTypeClassification['CUSTOM_ID']
|
||||
: defaultTypeClassification;
|
||||
this.actions.onFieldClassificationChange.call(this, { identifierField }, { value: defaultTypeClassification });
|
||||
},
|
||||
|
||||
@ -590,8 +586,7 @@ export default Component.extend({
|
||||
onComplianceJsonUpload(textString) {
|
||||
const policy = JSON.parse(textString);
|
||||
if (isPolicyExpectedShape(policy)) {
|
||||
const currentPolicy = get(this, 'complianceInfo');
|
||||
set(this, 'complianceInfo', Object.assign({}, currentPolicy, policy));
|
||||
set(this, 'complianceInfo', policy);
|
||||
|
||||
// If all is good, then we can saveCompliance so user does not have to manually click
|
||||
return this.actions.saveCompliance();
|
||||
@ -605,11 +600,7 @@ export default Component.extend({
|
||||
*/
|
||||
onComplianceDownloadJson() {
|
||||
const currentPolicy = get(this, 'complianceInfo');
|
||||
const policyProps = [
|
||||
datasetClassificationKey,
|
||||
policyFieldClassificationKey,
|
||||
policyComplianceEntitiesKey
|
||||
].map(name => name.split('.').pop());
|
||||
const policyProps = [datasetClassificationKey, policyComplianceEntitiesKey].map(name => name.split('.').pop());
|
||||
const policy = Object.assign({}, getProperties(currentPolicy, policyProps));
|
||||
const href = `data:text/json;charset=utf-8,${encodeURIComponent(JSON.stringify(policy))}`;
|
||||
const download = `${get(this, 'datasetName')}_policy.json`;
|
||||
@ -639,25 +630,19 @@ export default Component.extend({
|
||||
* @param {String} logicalType
|
||||
* @param {String} identifierType
|
||||
*/
|
||||
onFieldIdentifierTypeChange({ identifierField, logicalType }, { value: identifierType }) {
|
||||
onFieldIdentifierTypeChange({ identifierField }, { value: identifierType }) {
|
||||
const currentComplianceEntities = get(this, 'mergedComplianceEntitiesAndColumnFields');
|
||||
// A reference to the current field in the compliance list if it exists
|
||||
// A reference to the current field in the compliance list, it should exist even for empty complianceEntities
|
||||
// since this is a reference created in the working copy: mergedComplianceEntitiesAndColumnFields
|
||||
const currentFieldInComplianceList = currentComplianceEntities.findBy('identifierField', identifierField);
|
||||
const subjectIdString = fieldIdentifierTypes.subjectMember.value;
|
||||
// Some rendered identifierTypes may be masks of other underlying types, e.g. subjectId Member type is really
|
||||
// a memberId with an attribute of isSubject in the affirmative
|
||||
const unwrappedIdentifierType = subjectIdString === identifierType
|
||||
? fieldIdentifierTypes.member.value
|
||||
: identifierType;
|
||||
setProperties(currentFieldInComplianceList, {
|
||||
identifierType: unwrappedIdentifierType,
|
||||
isSubject: subjectIdString === identifierType ? true : null,
|
||||
identifierType,
|
||||
logicalType: void 0
|
||||
});
|
||||
// Set the defaultClassification for the identifierField,
|
||||
// although the classification is based on the logicalType,
|
||||
// an identifierField may only have one valid logicalType for it's given identifierType
|
||||
this.setDefaultClassification({ identifierField, identifierType: unwrappedIdentifierType });
|
||||
this.setDefaultClassification({ identifierField, identifierType });
|
||||
},
|
||||
|
||||
/**
|
||||
@ -695,28 +680,11 @@ export default Component.extend({
|
||||
'identifierField',
|
||||
identifierField
|
||||
);
|
||||
let fieldClassification = get(this, policyFieldClassificationKey);
|
||||
let updatedFieldClassification = {};
|
||||
// For datasets initially without a fieldClassification, the default value is null
|
||||
if (fieldClassification === null) {
|
||||
fieldClassification = set(this, policyFieldClassificationKey, {});
|
||||
}
|
||||
|
||||
// TODO:DSS-6719 refactor into mixin
|
||||
this.clearMessages();
|
||||
|
||||
if (!classification && identifierField in fieldClassification) {
|
||||
updatedFieldClassification = Object.assign(updatedFieldClassification, fieldClassification);
|
||||
delete updatedFieldClassification[identifierField];
|
||||
} else {
|
||||
// fieldNames/identifierField can be paths i.e. identifierField.identifierPath.subPath
|
||||
// using Ember.set trips up Ember and throws
|
||||
updatedFieldClassification = Object.assign({}, fieldClassification, { [identifierField]: classification });
|
||||
}
|
||||
|
||||
// Apply the updated classification value to the current instance of the field in working copy
|
||||
set(currentFieldInComplianceList, 'classification', classification);
|
||||
set(this, policyFieldClassificationKey, updatedFieldClassification);
|
||||
},
|
||||
|
||||
/**
|
||||
|
||||
15
wherehows-web/app/config/environment.d.ts
vendored
Normal file
15
wherehows-web/app/config/environment.d.ts
vendored
Normal file
@ -0,0 +1,15 @@
|
||||
export default config;
|
||||
|
||||
/**
|
||||
* Type declarations for
|
||||
* import config from './config/environment'
|
||||
*
|
||||
* For now these need to be managed by the developer
|
||||
* since different ember addons can materialize new entries.
|
||||
*/
|
||||
declare namespace config {
|
||||
export const environment: any;
|
||||
export const modulePrefix: string;
|
||||
export const podModulePrefix: string;
|
||||
export const locationType: string;
|
||||
}
|
||||
@ -1,6 +1,6 @@
|
||||
import Ember from 'ember';
|
||||
import { createInitialComplianceInfo } from 'wherehows-web/utils/datasets/functions';
|
||||
import { makeUrnBreadcrumbs } from 'wherehows-web/utils/entities';
|
||||
import { datasetComplianceFor } from 'wherehows-web/utils/api';
|
||||
|
||||
const { Route, get, set, setProperties, isPresent, inject: { service }, $: { getJSON } } = Ember;
|
||||
// TODO: DSS-6581 Create URL retrieval module
|
||||
@ -19,7 +19,6 @@ const getDatasetReferencesUrl = id => `${datasetUrl(id)}/references`;
|
||||
const getDatasetOwnersUrl = id => `${datasetUrl(id)}/owners`;
|
||||
const getDatasetInstanceUrl = id => `${datasetUrl(id)}/instances`;
|
||||
const getDatasetVersionUrl = (id, dbId) => `${datasetUrl(id)}/versions/db/${dbId}`;
|
||||
const getDatasetPrivacyUrl = id => `${datasetUrl(id)}/privacy`;
|
||||
|
||||
let getDatasetColumn;
|
||||
|
||||
@ -122,19 +121,6 @@ export default Route.extend({
|
||||
)
|
||||
.forEach(func => func());
|
||||
|
||||
/**
|
||||
* Fetches the current compliance policy for the rendered dataset
|
||||
* @param {number} id the id of the dataset
|
||||
* @return {Promise.<{complianceInfo: *, isNewComplianceInfo: boolean}>}
|
||||
*/
|
||||
const getComplianceInfo = async id => {
|
||||
const response = await Promise.resolve(getJSON(getDatasetPrivacyUrl(id)));
|
||||
const { msg, status, complianceInfo = createInitialComplianceInfo(id) } = response;
|
||||
const isNewComplianceInfo = status === 'failed' && String(msg).includes('actual 0');
|
||||
|
||||
return { complianceInfo, isNewComplianceInfo };
|
||||
};
|
||||
|
||||
/**
|
||||
* Fetch the datasetColumn
|
||||
* @param {number} id the id of the dataset
|
||||
@ -183,8 +169,8 @@ export default Route.extend({
|
||||
* @return {Promise.<void>}
|
||||
*/
|
||||
(async id => {
|
||||
const [columns, privacy] = await Promise.all([getDatasetColumn(id), getComplianceInfo(id)]);
|
||||
const { complianceInfo, isNewComplianceInfo } = privacy;
|
||||
const [columns, compliance] = await Promise.all([getDatasetColumn(id), datasetComplianceFor(id)]);
|
||||
const { complianceInfo, isNewComplianceInfo } = compliance;
|
||||
setProperties(controller, {
|
||||
complianceInfo,
|
||||
isNewComplianceInfo,
|
||||
|
||||
27
wherehows-web/app/typings/ember-cli-mirage.d.ts
vendored
Normal file
27
wherehows-web/app/typings/ember-cli-mirage.d.ts
vendored
Normal file
@ -0,0 +1,27 @@
|
||||
/**
|
||||
* Defines the available interface on a Mirage server.
|
||||
* This is not an exhaustive list but exposes some of the api that's used within the app
|
||||
*/
|
||||
export interface IMirageServer {
|
||||
options: object;
|
||||
urlPrefix: string;
|
||||
namespace: string;
|
||||
timing: number;
|
||||
logging: boolean;
|
||||
pretender: object;
|
||||
environment: string;
|
||||
get: (this: IMirageServer, path: string, ...args: Array<any>) => void;
|
||||
post: (this: IMirageServer, path: string, ...args: Array<any>) => void;
|
||||
put: (this: IMirageServer, path: string, ...args: Array<any>) => void;
|
||||
delete: (this: IMirageServer, path: string, ...args: Array<any>) => void;
|
||||
del: (this: IMirageServer, path: string, ...args: Array<any>) => void;
|
||||
patch: (this: IMirageServer, path: string, ...args: Array<any>) => void;
|
||||
head: (this: IMirageServer, path: string, ...args: Array<any>) => void;
|
||||
isTest: (this: IMirageServer) => boolean;
|
||||
passthrough: (...paths: Array<string>) => void;
|
||||
loadFixtures: (...files: Array<string>) => void;
|
||||
loadFactories: (factoryMap: object) => void;
|
||||
create: (type: string, options?: object) => object;
|
||||
createList: <T>(type: string, amount: number, traitsAndOverrides?: object) => Array<T>;
|
||||
shutdown: (this: IMirageServer) => void;
|
||||
}
|
||||
1
wherehows-web/app/typings/untyped-js-module.d.ts
vendored
Normal file
1
wherehows-web/app/typings/untyped-js-module.d.ts
vendored
Normal file
@ -0,0 +1 @@
|
||||
declare module '*';
|
||||
55
wherehows-web/app/utils/api/datasets.ts
Normal file
55
wherehows-web/app/utils/api/datasets.ts
Normal file
@ -0,0 +1,55 @@
|
||||
import Ember from 'ember';
|
||||
/// <reference path='wherehows-web/typings/untyped-js-module' />
|
||||
import { createInitialComplianceInfo } from 'wherehows-web/utils/datasets/functions';
|
||||
import { apiRoot } from 'wherehows-web/utils/api';
|
||||
|
||||
const { $: { getJSON }, assert } = Ember;
|
||||
|
||||
/**
|
||||
* Defines the endpoint for datasets
|
||||
* @type {string}
|
||||
*/
|
||||
const datasetsUrlRoot = `${apiRoot}/datasets`;
|
||||
|
||||
/**
|
||||
* Constructs a url to get a dataset with a given id
|
||||
* @param {number} id the id of the dataset
|
||||
* @return {string} the dataset url
|
||||
*/
|
||||
const datasetUrlById = (id: number): string => `${datasetsUrlRoot}/${id}`;
|
||||
|
||||
/**
|
||||
* Constructs the dataset compliance url
|
||||
* @param {number} id the id of the dataset
|
||||
* @return {string} the dataset compliance url
|
||||
*/
|
||||
const datasetComplianceUrlById = (id: number): string => `${datasetUrlById(id)}/compliance`;
|
||||
|
||||
/**
|
||||
* Fetches the current compliance policy for a dataset with thi given id
|
||||
* @param {number} id the id of the dataset
|
||||
* @return {Promise<{isNewComplianceInfo: boolean, complianceInfo: *}>}
|
||||
*/
|
||||
const datasetComplianceFor = async (id: number): Promise<{ isNewComplianceInfo: boolean; complianceInfo: any }> => {
|
||||
assert(`Expected id to be a number but received ${typeof id}`, typeof id === 'number');
|
||||
const failedStatus = 'failed';
|
||||
const notFound = 'actual 0';
|
||||
// complianceInfo contains the compliance data for the specified dataset
|
||||
let {
|
||||
msg = '',
|
||||
status,
|
||||
complianceInfo
|
||||
}: { msg: string; status: string; complianceInfo: any } = await Promise.resolve(
|
||||
getJSON(datasetComplianceUrlById(id))
|
||||
);
|
||||
// If the endpoint responds with a failed status, and the msg contains the indicator that a compliance does not exist
|
||||
const isNewComplianceInfo: boolean = status === failedStatus && String(msg).includes(notFound);
|
||||
|
||||
if (isNewComplianceInfo) {
|
||||
complianceInfo = createInitialComplianceInfo(id);
|
||||
}
|
||||
|
||||
return { isNewComplianceInfo, complianceInfo };
|
||||
};
|
||||
|
||||
export { datasetsUrlRoot, datasetComplianceFor };
|
||||
12
wherehows-web/app/utils/api/index.ts
Normal file
12
wherehows-web/app/utils/api/index.ts
Normal file
@ -0,0 +1,12 @@
|
||||
/**
|
||||
* Defines the root path for wherehows front-end api requests
|
||||
* @type {string}
|
||||
*/
|
||||
const apiRoot = '/api/v1';
|
||||
|
||||
|
||||
export * from 'wherehows-web/utils/api/datasets';
|
||||
|
||||
export {
|
||||
apiRoot
|
||||
}
|
||||
@ -1,5 +1,8 @@
|
||||
import Ember from 'ember';
|
||||
import { datasetClassifiers } from 'wherehows-web/constants/dataset-classification';
|
||||
|
||||
const { assert } = Ember;
|
||||
|
||||
/**
|
||||
* Builds a default shape for securitySpecification & privacyCompliancePolicy with default / unset values
|
||||
* for non null properties as per Avro schema
|
||||
@ -29,13 +32,12 @@ const policyShape = {
|
||||
keys: [
|
||||
'identifierField:string',
|
||||
'identifierType:string',
|
||||
'isSubject:boolean|object',
|
||||
'securityClassification:string',
|
||||
'logicalType:string|object|undefined'
|
||||
]
|
||||
}
|
||||
},
|
||||
datasetClassification: { type: 'object', keys: Object.keys(datasetClassifiers).map(key => `${key}:boolean`) },
|
||||
fieldClassification: { type: 'object' }
|
||||
datasetClassification: { type: 'object', keys: Object.keys(datasetClassifiers).map(key => `${key}:boolean`) }
|
||||
};
|
||||
|
||||
/**
|
||||
@ -45,20 +47,25 @@ const policyShape = {
|
||||
*/
|
||||
const isPolicyExpectedShape = (candidatePolicy = {}) => {
|
||||
const candidateMatchesShape = policyKey => {
|
||||
assert(
|
||||
`Expected each compliance policy attribute to be one of ${Object.keys(policyShape)}, but got ${policyKey}`,
|
||||
policyShape.hasOwnProperty(policyKey)
|
||||
);
|
||||
|
||||
const policyProps = policyShape[policyKey];
|
||||
const expectedType = policyProps.type;
|
||||
const policyKeyValue = candidatePolicy[policyKey];
|
||||
const isValueExpectedType = expectedType === 'array'
|
||||
? Array.isArray(policyKeyValue)
|
||||
: typeof policyKeyValue === expectedType;
|
||||
const typeDeclarations = {
|
||||
get array() {
|
||||
return policyProps.of.keys;
|
||||
},
|
||||
get object() {
|
||||
return policyProps.keys;
|
||||
}
|
||||
}[expectedType] || [];
|
||||
const isValueExpectedType =
|
||||
expectedType === 'array' ? Array.isArray(policyKeyValue) : typeof policyKeyValue === expectedType;
|
||||
const typeDeclarations =
|
||||
{
|
||||
get array() {
|
||||
return policyProps.of.keys;
|
||||
},
|
||||
get object() {
|
||||
return policyProps.keys;
|
||||
}
|
||||
}[expectedType] || [];
|
||||
|
||||
if (!policyKeyValue || !isValueExpectedType) {
|
||||
return false;
|
||||
|
||||
9
wherehows-web/mirage/config.ts
Normal file
9
wherehows-web/mirage/config.ts
Normal file
@ -0,0 +1,9 @@
|
||||
import { IMirageServer } from 'wherehows-web/typings/ember-cli-mirage';
|
||||
|
||||
export default function(this: IMirageServer) {
|
||||
this.namespace = '/api/v1/';
|
||||
|
||||
this.get('/compliance/suggestions', () => {});
|
||||
|
||||
this.passthrough();
|
||||
}
|
||||
16
wherehows-web/mirage/factories/compliance-suggestion.js
Normal file
16
wherehows-web/mirage/factories/compliance-suggestion.js
Normal file
@ -0,0 +1,16 @@
|
||||
import { Factory, faker } from 'ember-cli-mirage';
|
||||
import { fieldIdentifierTypes, idLogicalTypes } from 'wherehows-web/constants';
|
||||
|
||||
const identifierTypeValues = Object.keys(fieldIdentifierTypes);
|
||||
|
||||
export default Factory.extend({
|
||||
fieldName: 'memberUrn',
|
||||
identifierTypePrediction: {
|
||||
value: () => faker.list.random(...identifierTypeValues),
|
||||
confidence: 1
|
||||
},
|
||||
logicalTypePrediction: {
|
||||
value: faker.list.random(...idLogicalTypes),
|
||||
confidence: 1
|
||||
}
|
||||
});
|
||||
4
wherehows-web/mirage/models/compliance-suggestion.js
Normal file
4
wherehows-web/mirage/models/compliance-suggestion.js
Normal file
@ -0,0 +1,4 @@
|
||||
import { Model } from 'ember-cli-mirage';
|
||||
|
||||
export default Model.extend({
|
||||
});
|
||||
9
wherehows-web/mirage/scenarios/default.js
Normal file
9
wherehows-web/mirage/scenarios/default.js
Normal file
@ -0,0 +1,9 @@
|
||||
export default function(/* server */) {
|
||||
|
||||
/*
|
||||
Seed your development database using your factories.
|
||||
This data will not be loaded in your tests.
|
||||
*/
|
||||
|
||||
// server.createList('post', 10);
|
||||
}
|
||||
4
wherehows-web/mirage/serializers/application.js
Normal file
4
wherehows-web/mirage/serializers/application.js
Normal file
@ -0,0 +1,4 @@
|
||||
import { JSONAPISerializer } from 'ember-cli-mirage';
|
||||
|
||||
export default JSONAPISerializer.extend({
|
||||
});
|
||||
@ -16,46 +16,50 @@
|
||||
"precommit": "lint-staged"
|
||||
},
|
||||
"devDependencies": {
|
||||
"bower": "1.8.0",
|
||||
"broccoli-asset-rev": "2.5.0",
|
||||
"broccoli-funnel": "1.1.0",
|
||||
"broccoli-merge-trees": "1.2.1",
|
||||
"ember-ajax": "2.5.5",
|
||||
"ember-cli": "2.11.1",
|
||||
"ember-cli-app-version": "2.0.1",
|
||||
"ember-cli-babel": "5.2.4",
|
||||
"ember-cli-bootstrap-sassy": "0.5.5",
|
||||
"ember-cli-dependency-checker": "1.3.0",
|
||||
"ember-cli-eyeglass": "1.3.0",
|
||||
"ember-cli-htmlbars": "1.1.1",
|
||||
"ember-cli-inject-live-reload": "1.6.1",
|
||||
"ember-cli-jshint": "2.0.1",
|
||||
"ember-cli-moment-shim": "3.0.1",
|
||||
"ember-cli-qunit": "3.1.1",
|
||||
"ember-cli-release": "0.2.9",
|
||||
"ember-cli-sri": "2.1.1",
|
||||
"ember-cli-test-loader": "1.1.1",
|
||||
"ember-cli-uglify": "1.2.0",
|
||||
"ember-composable-helpers": "2.0.0",
|
||||
"ember-data": "2.11.1",
|
||||
"ember-export-application-global": "1.1.1",
|
||||
"ember-load-initializers": "0.5.1",
|
||||
"ember-lodash-shim": "2.0.2",
|
||||
"ember-metrics": "0.10.0",
|
||||
"ember-moment": "7.3.0",
|
||||
"ember-pikaday": "2.2.1",
|
||||
"ember-redux-shim": "1.1.1",
|
||||
"ember-redux-thunk-shim": "1.1.2",
|
||||
"ember-resolver": "2.1.1",
|
||||
"ember-symbol-observable": "0.1.2",
|
||||
"eyeglass": "1.2.1",
|
||||
"eyeglass-restyle": "1.0.19",
|
||||
"@types/ember": "^2.7.44",
|
||||
"@types/ember-testing-helpers": "0.0.1",
|
||||
"@types/rsvp": "^3.3.1",
|
||||
"bower": "^1.8.0",
|
||||
"broccoli-asset-rev": "^2.5.0",
|
||||
"broccoli-funnel": "^1.1.0",
|
||||
"broccoli-merge-trees": "^1.2.1",
|
||||
"ember-ajax": "^2.5.5",
|
||||
"ember-cli": "^2.11.1",
|
||||
"ember-cli-app-version": "^2.0.1",
|
||||
"ember-cli-babel": "^5.2.4",
|
||||
"ember-cli-bootstrap-sassy": "^0.5.5",
|
||||
"ember-cli-dependency-checker": "^1.3.0",
|
||||
"ember-cli-eyeglass": "^1.3.0",
|
||||
"ember-cli-htmlbars": "^1.1.1",
|
||||
"ember-cli-inject-live-reload": "^1.6.1",
|
||||
"ember-cli-moment-shim": "^3.0.1",
|
||||
"ember-cli-qunit": "^3.1.1",
|
||||
"ember-cli-release": "^0.2.9",
|
||||
"ember-cli-sri": "^2.1.1",
|
||||
"ember-cli-test-loader": "^1.1.1",
|
||||
"ember-cli-typescript": "^1.0.0",
|
||||
"ember-cli-uglify": "^1.2.0",
|
||||
"ember-composable-helpers": "^2.0.0",
|
||||
"ember-data": "^2.11.1",
|
||||
"ember-export-application-global": "^1.1.1",
|
||||
"ember-load-initializers": "^0.5.1",
|
||||
"ember-lodash-shim": "^2.0.2",
|
||||
"ember-metrics": "^0.10.0",
|
||||
"ember-moment": "^7.3.0",
|
||||
"ember-pikaday": "^2.2.1",
|
||||
"ember-redux-shim": "^1.1.1",
|
||||
"ember-redux-thunk-shim": "^1.1.2",
|
||||
"ember-resolver": "^2.1.1",
|
||||
"ember-symbol-observable": "^0.1.2",
|
||||
"eyeglass": "^1.2.1",
|
||||
"eyeglass-restyle": "^1.0.19",
|
||||
"husky": "^0.13.3",
|
||||
"lint-staged": "^3.4.2",
|
||||
"loader.js": "4.1.0",
|
||||
"node-sass": "^4.5.2",
|
||||
"redux": "^3.6.0",
|
||||
"redux-thunk": "^2.2.0"
|
||||
"redux-thunk": "^2.2.0",
|
||||
"typescript": "^2.4.2"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 0.12.0"
|
||||
@ -64,6 +68,7 @@
|
||||
"dependencies": {
|
||||
"dynamic-link": "0.2.1",
|
||||
"ember-aupac-typeahead": "2.1.2",
|
||||
"ember-cli-mirage": "0.3.4",
|
||||
"ember-cli-string-helpers": "1.0.0",
|
||||
"ember-lodash": "4.17.2",
|
||||
"ember-network": "0.3.1",
|
||||
@ -79,7 +84,7 @@
|
||||
"lint-staged": {
|
||||
"gitDir": "../",
|
||||
"linters": {
|
||||
"wherehows-web/{app,tests}/**/*.js": [
|
||||
"wherehows-web/{app,tests}/**/*.{ts,js}": [
|
||||
"prettier --print-width 120 --single-quote --write",
|
||||
"git add"
|
||||
]
|
||||
|
||||
@ -2,4 +2,7 @@ import Ember from 'ember';
|
||||
|
||||
export default function destroyApp(application) {
|
||||
Ember.run(application, 'destroy');
|
||||
if (window.server) {
|
||||
window.server.shutdown();
|
||||
}
|
||||
}
|
||||
|
||||
11
wherehows-web/tests/unit/utils/api/datasets-test.js
Normal file
11
wherehows-web/tests/unit/utils/api/datasets-test.js
Normal file
@ -0,0 +1,11 @@
|
||||
import datasetComplianceFor from 'wherehows-web/utils/api/datasets';
|
||||
import { module, test } from 'qunit';
|
||||
|
||||
module('Unit | Utility | api/datasets');
|
||||
|
||||
test('it has expected functions', function(assert) {
|
||||
assert.expect(1);
|
||||
const result = datasetComplianceFor(0);
|
||||
|
||||
assert.ok(result instanceof Promise, 'datasetComplianceFor is an async function');
|
||||
});
|
||||
21
wherehows-web/tsconfig.json
Normal file
21
wherehows-web/tsconfig.json
Normal file
@ -0,0 +1,21 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"target": "es2015",
|
||||
"moduleResolution": "node",
|
||||
"noEmitOnError": true,
|
||||
"baseUrl": ".",
|
||||
"sourceMap": true,
|
||||
"noEmit": true,
|
||||
"strict": true,
|
||||
"pretty": true,
|
||||
"noUnusedLocals": true,
|
||||
"noUnusedParameters": true,
|
||||
"experimentalDecorators": true,
|
||||
"paths": {
|
||||
"wherehows-web/*": ["app/*"],
|
||||
"wherehows-web/tests/*": ["tests/*"]
|
||||
}
|
||||
},
|
||||
"include": ["app/**/*", "tests/**/*", "mirage/**/*"],
|
||||
"exclude": ["tmp", "dist", "node_modules", "bower_components", "vendor", ".git", ".gradle", ".idea", "logs"]
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
Loading…
x
Reference in New Issue
Block a user