зеркало из https://github.com/microsoft/paris.git
Added component to view all entities and mock data
This commit is contained in:
Родитель
3784e1935c
Коммит
3391a3d32f
|
@ -6,6 +6,8 @@
|
|||
"title": "Something bad happened",
|
||||
"status": 1,
|
||||
"machine": "yossi-pc",
|
||||
"date": 1505710515089,
|
||||
"tags": ["Cyber", "Automation"],
|
||||
"host": {
|
||||
"id": "yossi_comp",
|
||||
"name": "Yossi's comp",
|
|
@ -1,11 +0,0 @@
|
|||
{
|
||||
"name": "mockAPI",
|
||||
"version": "0.0.0",
|
||||
"dependencies": {
|
||||
"body-parser": "^1.14.1",
|
||||
"connect-busboy": "0.0.2",
|
||||
"express": "5.0.0-alpha.5",
|
||||
"express-ws": "^3.0.0",
|
||||
"http2": "latest"
|
||||
}
|
||||
}
|
321
package.json
321
package.json
|
@ -1,161 +1,164 @@
|
|||
{
|
||||
"name": "Paris",
|
||||
"version": "1.0.0",
|
||||
"description": "Library for the implementation of Domain Driven Design in Angular/TypeScript apps",
|
||||
"repository": {
|
||||
"url": "https://github.com/microsoft/paris.ts"
|
||||
},
|
||||
"scripts": {
|
||||
"build.dev": "gulp build.dev --color --env-config dev",
|
||||
"build.dev.watch": "gulp build.dev.watch --color",
|
||||
"build.e2e": "gulp build.e2e --color",
|
||||
"build.prod": "gulp build.prod --color --env-config prod --build-type prod",
|
||||
"build.prod.exp": "gulp build.prod.aot --color --env-config prod --build-type prod",
|
||||
"build.prod.rollup.aot": "gulp build.prod.rollup.aot --color --env-config prod --build-type prod",
|
||||
"build.prod.aot": "gulp build.prod.aot --color --env-config prod --build-type prod",
|
||||
"build.test": "gulp build.test --color",
|
||||
"test.watch": "gulp test.watch --color",
|
||||
"generate.manifest": "gulp generate.manifest --color",
|
||||
"e2e": "protractor",
|
||||
"e2e.live": "protractor --elementExplorer",
|
||||
"gulp": "gulp",
|
||||
"i18n": "gulp i18n.build --build-type prod && ng-xi18n -p dist/tmp/tsconfig.json --i18nFormat xlf && gulp i18n.merge",
|
||||
"lint": "gulp tslint",
|
||||
"karma": "karma",
|
||||
"karma.start": "karma start",
|
||||
"postinstall": "gulp check.versions && gulp build.bundle.rxjs && gulp webdriver && gulp print.banner",
|
||||
"reinstall": "npm cache clean && npm install",
|
||||
"serve.coverage": "gulp serve.coverage --color",
|
||||
"serve.dev": "gulp serve.dev --color --env-config dev",
|
||||
"serve.e2e": "gulp serve.e2e --color",
|
||||
"serve.prod": "gulp serve.prod --color --env-config prod --build-type prod",
|
||||
"serve.prod.aot": "gulp serve.prod.aot --color --env-config prod --build-type prod",
|
||||
"serve.prod.exp": "gulp serve.prod.aot --color --env-config prod --build-type prod",
|
||||
"serve.prod.rollup.aot": "gulp serve.prod.rollup.aot --color --env-config prod --build-type prod",
|
||||
"sme.prod": "gulp sme.prod --color --env-config prod --build-type prod --preserve-source-maps",
|
||||
"sme.prod.aot": "gulp sme.prod.aot --color --env-config prod --build-type prod --preserve-source-maps",
|
||||
"sme.prod.rollup.aot": "gulp sme.prod.rollup.aot --color --env-config prod --build-type prod --preserve-source-maps",
|
||||
"start": "gulp serve.dev --color",
|
||||
"start.deving": "gulp start.deving --color",
|
||||
"tasks.list": "gulp --tasks-simple --color",
|
||||
"test": "gulp test --color",
|
||||
"e2e.ci": "gulp build.prod.rollup.aot --color && gulp build.e2e --color && gulp e2e --color",
|
||||
"tests.all": "npm test && npm run e2e.ci",
|
||||
"webdriver-start": "node ./node_modules/protractor/bin/webdriver-manager start",
|
||||
"webdriver-update": "node ./node_modules/protractor/bin/webdriver-manager update",
|
||||
"compodoc": "./node_modules/.bin/compodoc -p src/client/tsconfig.json",
|
||||
"serve.compodoc": "./node_modules/.bin/compodoc -s"
|
||||
},
|
||||
"author": "Yossi Kolesnicov",
|
||||
"license": "MIT",
|
||||
"devDependencies": {
|
||||
"@angular/compiler-cli": "^5.0.0-beta.6",
|
||||
"@angular/platform-server": "^5.0.0-beta.6",
|
||||
"@compodoc/compodoc": "^1.0.0-beta.7",
|
||||
"@types/async": "^2.0.32",
|
||||
"@types/browser-sync": "^0.0.36",
|
||||
"@types/express": "^4.0.33",
|
||||
"@types/gulp": "^4.0.0",
|
||||
"@types/gulp-filter": "^3.0.29",
|
||||
"@types/gulp-htmlmin": "^1.3.30",
|
||||
"@types/gulp-load-plugins": "^0.0.30",
|
||||
"@types/gulp-protractor": "^1.0.30",
|
||||
"@types/gulp-sass": "^0.0.30",
|
||||
"@types/gulp-util": "^3.0.29",
|
||||
"@types/jasmine": "^2.5.52",
|
||||
"@types/node": "^8.0.25",
|
||||
"@types/rimraf": "2.0.2",
|
||||
"@types/run-sequence": "^0.0.29",
|
||||
"@types/selenium-webdriver": "^3.0.3",
|
||||
"@types/systemjs": "^0.20.2",
|
||||
"@types/yargs": "^8.0.2",
|
||||
"async": "^2.1.1",
|
||||
"autoprefixer": "^7.0.1",
|
||||
"browser-sync": "^2.17.3",
|
||||
"codelyzer": "^3.1.2",
|
||||
"connect-history-api-fallback": "^1.3.0",
|
||||
"cssnano": "^3.7.7",
|
||||
"deep-extend": "^0.5.0",
|
||||
"event-stream": "^3.3.4",
|
||||
"express": "~4.15.2",
|
||||
"express-history-api-fallback": "^2.0.0",
|
||||
"gulp": "^3.9.1",
|
||||
"gulp-cached": "^1.1.0",
|
||||
"gulp-cheerio": "^0.6.2",
|
||||
"gulp-concat": "^2.6.0",
|
||||
"gulp-concat-css": "^2.3.0",
|
||||
"gulp-filter": "^5.0.0",
|
||||
"gulp-htmlmin": "^3.0.0",
|
||||
"gulp-inject": "^4.1.0",
|
||||
"gulp-inline-ng2-template": "^4.0.0",
|
||||
"gulp-load-plugins": "^1.3.0",
|
||||
"gulp-plumber": "~1.1.0",
|
||||
"gulp-postcss": "^7.0.0",
|
||||
"gulp-progeny": "^0.4.0",
|
||||
"gulp-protractor": "^3.0.0",
|
||||
"gulp-rename": "^1.2.2",
|
||||
"gulp-replace": "^0.6.1",
|
||||
"gulp-sass": "^3.0.0",
|
||||
"gulp-sourcemaps": "2.6.0",
|
||||
"gulp-template": "^4.0.0",
|
||||
"gulp-tslint": "^8.0.0",
|
||||
"gulp-typescript": "~3.2.2",
|
||||
"gulp-uglify": "^3.0.0",
|
||||
"gulp-util": "^3.0.7",
|
||||
"gulp-watch": "^4.3.10",
|
||||
"http-proxy-middleware": "^0.17.4",
|
||||
"is-ci": "^1.0.9",
|
||||
"isstream": "^0.1.2",
|
||||
"jasmine-core": "~2.6.1",
|
||||
"jasmine-spec-reporter": "^4.1.0",
|
||||
"karma": "~1.7.1",
|
||||
"karma-chrome-launcher": "~2.2.0",
|
||||
"karma-coverage": "^1.1.1",
|
||||
"karma-jasmine": "~1.1.0",
|
||||
"karma-mocha-reporter": "^2.2.0",
|
||||
"karma-remap-istanbul": "^0.6.0",
|
||||
"merge-stream": "^1.0.0",
|
||||
"minimatch": "^3.0.3",
|
||||
"open": "0.0.5",
|
||||
"protractor": "^4.0.14",
|
||||
"remap-istanbul": "^0.9.5",
|
||||
"rimraf": "^2.5.4",
|
||||
"rollup": "^0.43.0",
|
||||
"rollup-plugin-commonjs": "^8.0.2",
|
||||
"rollup-plugin-includepaths": "0.2.2",
|
||||
"rollup-plugin-node-resolve": "^3.0.0",
|
||||
"run-sequence": "^1.2.2",
|
||||
"semver": "^5.3.0",
|
||||
"serve-static": "^1.11.1",
|
||||
"slash": "~1.0.0",
|
||||
"source-map-explorer": "^1.4.0",
|
||||
"supports-color": "^3.1.2",
|
||||
"systemjs-builder": "0.16.9",
|
||||
"temp": "^0.8.3",
|
||||
"tildify": "^1.2.0",
|
||||
"traceur": "^0.0.111",
|
||||
"ts-node": "^3.0.4",
|
||||
"tslint": "^5.0.0",
|
||||
"tslib": "latest",
|
||||
"typescript": "2.4.2",
|
||||
"walk": "^2.3.9",
|
||||
"yargs": "^8.0.1"
|
||||
},
|
||||
"dependencies": {
|
||||
"@angular/animations": "^5.0.0-beta.6",
|
||||
"@angular/common": "^5.0.0-beta.6",
|
||||
"@angular/compiler": "^5.0.0-beta.6",
|
||||
"@angular/core": "^5.0.0-beta.6",
|
||||
"@angular/forms": "^5.0.0-beta.6",
|
||||
"@angular/http": "^5.0.0-beta.6",
|
||||
"@angular/platform-browser": "^5.0.0-beta.6",
|
||||
"@angular/platform-browser-dynamic": "^5.0.0-beta.6",
|
||||
"@angular/router": "^5.0.0-beta.6",
|
||||
"@angular/service-worker": "^1.0.0-beta.16",
|
||||
"core-js": "^2.4.1",
|
||||
"intl": "^1.2.5",
|
||||
"rxjs": "^5.4.2",
|
||||
"systemjs": "0.20.14",
|
||||
"zone.js": "0.8.12"
|
||||
}
|
||||
"name": "Paris",
|
||||
"version": "1.0.0",
|
||||
"description": "Library for the implementation of Domain Driven Design in Angular/TypeScript apps",
|
||||
"repository": {
|
||||
"url": "https://github.com/microsoft/paris.ts"
|
||||
},
|
||||
"scripts": {
|
||||
"build.dev": "gulp build.dev --color --env-config dev",
|
||||
"build.dev.watch": "gulp build.dev.watch --color",
|
||||
"build.e2e": "gulp build.e2e --color",
|
||||
"build.prod": "gulp build.prod --color --env-config prod --build-type prod",
|
||||
"build.prod.exp": "gulp build.prod.aot --color --env-config prod --build-type prod",
|
||||
"build.prod.rollup.aot": "gulp build.prod.rollup.aot --color --env-config prod --build-type prod",
|
||||
"build.prod.aot": "gulp build.prod.aot --color --env-config prod --build-type prod",
|
||||
"build.test": "gulp build.test --color",
|
||||
"test.watch": "gulp test.watch --color",
|
||||
"generate.manifest": "gulp generate.manifest --color",
|
||||
"e2e": "protractor",
|
||||
"e2e.live": "protractor --elementExplorer",
|
||||
"gulp": "gulp",
|
||||
"i18n": "gulp i18n.build --build-type prod && ng-xi18n -p dist/tmp/tsconfig.json --i18nFormat xlf && gulp i18n.merge",
|
||||
"lint": "gulp tslint",
|
||||
"karma": "karma",
|
||||
"karma.start": "karma start",
|
||||
"mock-data-server.start": "node ./src/mock_data_server/mock_data_server",
|
||||
"postinstall": "gulp check.versions && gulp build.bundle.rxjs && gulp webdriver && gulp print.banner",
|
||||
"reinstall": "npm cache clean && npm install",
|
||||
"serve.coverage": "gulp serve.coverage --color",
|
||||
"serve.dev": "gulp serve.dev --color --env-config dev",
|
||||
"serve.e2e": "gulp serve.e2e --color",
|
||||
"serve.prod": "gulp serve.prod --color --env-config prod --build-type prod",
|
||||
"serve.prod.aot": "gulp serve.prod.aot --color --env-config prod --build-type prod",
|
||||
"serve.prod.exp": "gulp serve.prod.aot --color --env-config prod --build-type prod",
|
||||
"serve.prod.rollup.aot": "gulp serve.prod.rollup.aot --color --env-config prod --build-type prod",
|
||||
"sme.prod": "gulp sme.prod --color --env-config prod --build-type prod --preserve-source-maps",
|
||||
"sme.prod.aot": "gulp sme.prod.aot --color --env-config prod --build-type prod --preserve-source-maps",
|
||||
"sme.prod.rollup.aot": "gulp sme.prod.rollup.aot --color --env-config prod --build-type prod --preserve-source-maps",
|
||||
"start": "gulp serve.dev --color",
|
||||
"start.deving": "gulp start.deving --color",
|
||||
"tasks.list": "gulp --tasks-simple --color",
|
||||
"test": "gulp test --color",
|
||||
"e2e.ci": "gulp build.prod.rollup.aot --color && gulp build.e2e --color && gulp e2e --color",
|
||||
"tests.all": "npm test && npm run e2e.ci",
|
||||
"webdriver-start": "node ./node_modules/protractor/bin/webdriver-manager start",
|
||||
"webdriver-update": "node ./node_modules/protractor/bin/webdriver-manager update",
|
||||
"compodoc": "./node_modules/.bin/compodoc -p src/client/tsconfig.json",
|
||||
"serve.compodoc": "./node_modules/.bin/compodoc -s"
|
||||
},
|
||||
"author": "Yossi Kolesnicov",
|
||||
"license": "MIT",
|
||||
"devDependencies": {
|
||||
"@angular/compiler-cli": "^5.0.0-beta.6",
|
||||
"@angular/platform-server": "^5.0.0-beta.6",
|
||||
"@compodoc/compodoc": "^1.0.0-beta.7",
|
||||
"@types/async": "^2.0.32",
|
||||
"@types/browser-sync": "^0.0.36",
|
||||
"@types/express": "^4.0.33",
|
||||
"@types/gulp": "^4.0.0",
|
||||
"@types/gulp-filter": "^3.0.29",
|
||||
"@types/gulp-htmlmin": "^1.3.30",
|
||||
"@types/gulp-load-plugins": "^0.0.30",
|
||||
"@types/gulp-protractor": "^1.0.30",
|
||||
"@types/gulp-sass": "^0.0.30",
|
||||
"@types/gulp-util": "^3.0.29",
|
||||
"@types/jasmine": "^2.5.52",
|
||||
"@types/node": "^8.0.25",
|
||||
"@types/rimraf": "2.0.2",
|
||||
"@types/run-sequence": "^0.0.29",
|
||||
"@types/selenium-webdriver": "^3.0.3",
|
||||
"@types/systemjs": "^0.20.2",
|
||||
"@types/yargs": "^8.0.2",
|
||||
"async": "^2.1.1",
|
||||
"autoprefixer": "^7.0.1",
|
||||
"browser-sync": "^2.17.3",
|
||||
"codelyzer": "^3.1.2",
|
||||
"connect-history-api-fallback": "^1.3.0",
|
||||
"cssnano": "^3.7.7",
|
||||
"deep-extend": "^0.5.0",
|
||||
"event-stream": "^3.3.4",
|
||||
"body-parser": "^1.14.1",
|
||||
"connect-busboy": "0.0.2",
|
||||
"express": "^4.15.4",
|
||||
"express-history-api-fallback": "^2.0.0",
|
||||
"gulp": "^3.9.1",
|
||||
"gulp-cached": "^1.1.0",
|
||||
"gulp-cheerio": "^0.6.2",
|
||||
"gulp-concat": "^2.6.0",
|
||||
"gulp-concat-css": "^2.3.0",
|
||||
"gulp-filter": "^5.0.0",
|
||||
"gulp-htmlmin": "^3.0.0",
|
||||
"gulp-inject": "^4.1.0",
|
||||
"gulp-inline-ng2-template": "^4.0.0",
|
||||
"gulp-load-plugins": "^1.3.0",
|
||||
"gulp-plumber": "~1.1.0",
|
||||
"gulp-postcss": "^7.0.0",
|
||||
"gulp-progeny": "^0.4.0",
|
||||
"gulp-protractor": "^3.0.0",
|
||||
"gulp-rename": "^1.2.2",
|
||||
"gulp-replace": "^0.6.1",
|
||||
"gulp-sass": "^3.0.0",
|
||||
"gulp-sourcemaps": "2.6.0",
|
||||
"gulp-template": "^4.0.0",
|
||||
"gulp-tslint": "^8.0.0",
|
||||
"gulp-typescript": "~3.2.2",
|
||||
"gulp-uglify": "^3.0.0",
|
||||
"gulp-util": "^3.0.7",
|
||||
"gulp-watch": "^4.3.10",
|
||||
"http-proxy-middleware": "^0.17.4",
|
||||
"is-ci": "^1.0.9",
|
||||
"isstream": "^0.1.2",
|
||||
"jasmine-core": "~2.6.1",
|
||||
"jasmine-spec-reporter": "^4.1.0",
|
||||
"karma": "~1.7.1",
|
||||
"karma-chrome-launcher": "~2.2.0",
|
||||
"karma-coverage": "^1.1.1",
|
||||
"karma-jasmine": "~1.1.0",
|
||||
"karma-mocha-reporter": "^2.2.0",
|
||||
"karma-remap-istanbul": "^0.6.0",
|
||||
"merge-stream": "^1.0.0",
|
||||
"minimatch": "^3.0.3",
|
||||
"open": "0.0.5",
|
||||
"protractor": "^4.0.14",
|
||||
"remap-istanbul": "^0.9.5",
|
||||
"rimraf": "^2.5.4",
|
||||
"rollup": "^0.43.0",
|
||||
"rollup-plugin-commonjs": "^8.0.2",
|
||||
"rollup-plugin-includepaths": "0.2.2",
|
||||
"rollup-plugin-node-resolve": "^3.0.0",
|
||||
"run-sequence": "^1.2.2",
|
||||
"semver": "^5.3.0",
|
||||
"serve-static": "^1.11.1",
|
||||
"slash": "~1.0.0",
|
||||
"source-map-explorer": "^1.4.0",
|
||||
"supports-color": "^3.1.2",
|
||||
"systemjs-builder": "0.16.9",
|
||||
"temp": "^0.8.3",
|
||||
"tildify": "^1.2.0",
|
||||
"traceur": "^0.0.111",
|
||||
"ts-node": "^3.0.4",
|
||||
"tslib": "latest",
|
||||
"tslint": "^5.0.0",
|
||||
"typescript": "2.4.2",
|
||||
"walk": "^2.3.9",
|
||||
"yargs": "^8.0.1"
|
||||
},
|
||||
"dependencies": {
|
||||
"@angular/animations": "^5.0.0-beta.6",
|
||||
"@angular/common": "^5.0.0-beta.6",
|
||||
"@angular/compiler": "^5.0.0-beta.6",
|
||||
"@angular/core": "^5.0.0-beta.6",
|
||||
"@angular/forms": "^5.0.0-beta.6",
|
||||
"@angular/http": "^5.0.0-beta.6",
|
||||
"@angular/platform-browser": "^5.0.0-beta.6",
|
||||
"@angular/platform-browser-dynamic": "^5.0.0-beta.6",
|
||||
"@angular/router": "^5.0.0-beta.6",
|
||||
"@angular/service-worker": "^1.0.0-beta.16",
|
||||
"core-js": "^2.4.1",
|
||||
"intl": "^1.2.5",
|
||||
"rxjs": "^5.4.2",
|
||||
"systemjs": "0.20.14",
|
||||
"zone.js": "0.8.12"
|
||||
}
|
||||
}
|
||||
|
|
|
@ -5,7 +5,11 @@ import {EntityField} from "../paris/entity/entity-field.decorator";
|
|||
@Entity({
|
||||
singularName: "Machine",
|
||||
pluralName: "Machines",
|
||||
endpoint: "machines"
|
||||
endpoint: "machines",
|
||||
cache: {
|
||||
time: 10 * 1000,
|
||||
max: 10
|
||||
}
|
||||
})
|
||||
export class MachineModel extends Identifiable<string> {
|
||||
@EntityField({
|
||||
|
|
|
@ -1,12 +1,18 @@
|
|||
import { NgModule } from '@angular/core';
|
||||
import { RouterModule } from '@angular/router';
|
||||
import {EntityComponent} from "./components/entity.component";
|
||||
import {EntityResolver} from "./entity-resolver";
|
||||
|
||||
@NgModule({
|
||||
imports: [
|
||||
RouterModule.forRoot([
|
||||
/* define app module routes here, e.g., to lazily load a module
|
||||
(do not place feature module routes here, use an own -routing.module.ts in the feature instead)
|
||||
*/
|
||||
{
|
||||
path: 'entity/:entityPluralName',
|
||||
component: EntityComponent,
|
||||
resolve: {
|
||||
entity: EntityResolver
|
||||
}
|
||||
}
|
||||
])
|
||||
],
|
||||
exports: [RouterModule]
|
||||
|
|
|
@ -5,22 +5,4 @@
|
|||
padding: 1rem;
|
||||
}
|
||||
|
||||
table{
|
||||
border: solid 1px #ddd;
|
||||
}
|
||||
|
||||
table td, table th{
|
||||
text-align: left;
|
||||
padding: 6px 10px;
|
||||
}
|
||||
|
||||
thead tr{
|
||||
background: #106cc8;
|
||||
color: White;
|
||||
}
|
||||
|
||||
tr + tr{
|
||||
border-top: solid 1px #eaeaea;
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -1,3 +1,6 @@
|
|||
<entities-nav></entities-nav>
|
||||
<router-outlet></router-outlet>
|
||||
<hr />
|
||||
<div *ngIf="alert">
|
||||
<h2>Alert</h2>
|
||||
<table>
|
||||
|
@ -24,28 +27,6 @@
|
|||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
<button (click)="loadAlert()">Refresh Alert</button>
|
||||
</div>
|
||||
<hr />
|
||||
<div *ngIf="alerts">
|
||||
<h2>Alerts DataSet</h2>
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<th>ID</th>
|
||||
<th>Name</th>
|
||||
<th>Status</th>
|
||||
<th>Machine</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr *ngFor="let alert of alerts">
|
||||
<td>{{alert.id}}</td>
|
||||
<td>{{alert.name}}</td>
|
||||
<td>{{alert.status.name}}</td>
|
||||
<td>{{alert.machine.name}} ({{alert.machine.domain || 'No Domain'}})</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
<button (click)="loadAlerts()">Refresh Alert</button>
|
||||
<button (click)="loadAlert()">Refresh</button>
|
||||
<button (click)="saveAlert()">Update</button>
|
||||
</div>
|
||||
|
|
|
@ -5,6 +5,7 @@ import {AlertModel} from "./@model/alert.model";
|
|||
import {MachineModel} from "./@model/machine.model";
|
||||
import {Repository} from "./paris/repository/repository";
|
||||
import {DataSet} from "./paris/dataset/dataset";
|
||||
import {Immutability} from "./paris/services/immutability";
|
||||
|
||||
/**
|
||||
* This class represents the main application component.
|
||||
|
@ -17,7 +18,6 @@ import {DataSet} from "./paris/dataset/dataset";
|
|||
})
|
||||
export class AppComponent {
|
||||
alert:AlertModel;
|
||||
machine:MachineModel;
|
||||
alerts:Array<AlertModel>;
|
||||
|
||||
private alertsRepo:Repository<AlertModel>;
|
||||
|
@ -30,7 +30,6 @@ export class AppComponent {
|
|||
|
||||
loadAll(){
|
||||
this.loadAlert();
|
||||
this.loadAlerts();
|
||||
this.loadMachine();
|
||||
}
|
||||
|
||||
|
@ -50,14 +49,11 @@ export class AppComponent {
|
|||
}
|
||||
|
||||
saveAlert(){
|
||||
this.alert.name = "Updated Alert!";
|
||||
this.alertsRepo.save(this.alert).subscribe((savedAlert:AlertModel) => console.log("SAVED", savedAlert))
|
||||
}
|
||||
|
||||
loadAlerts(){
|
||||
this.alertsRepo.getItemsDataSet({ page: 1, pageSize: 15 }).subscribe((alerts:DataSet<AlertModel>) => {
|
||||
console.log("Alerts: ", alerts);
|
||||
this.alerts = alerts.items;
|
||||
});
|
||||
let editedAlert:AlertModel = Immutability.unfreeze(this.alert);
|
||||
editedAlert.name = "Updated Alert!";
|
||||
this.alertsRepo.save(editedAlert).subscribe((savedAlert:AlertModel) => {
|
||||
console.log("SAVED", savedAlert);
|
||||
this.alert = savedAlert;
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
|
@ -7,6 +7,10 @@ import {ParisModule} from "./paris/paris.module";
|
|||
import {HttpClientModule} from "@angular/common/http";
|
||||
import {ParisConfig} from "./paris/config/paris-config";
|
||||
import {TagComponent} from "./components/tag.component";
|
||||
import {EntitiesNavComponent} from "./components/entities-nav.component";
|
||||
import {EntityComponent} from "./components/entity.component";
|
||||
import {RouterModule} from "@angular/router";
|
||||
import {EntityResolver} from "./entity-resolver";
|
||||
|
||||
const parisConfig:ParisConfig = {
|
||||
apiRoot: "api",
|
||||
|
@ -18,17 +22,21 @@ const parisConfig:ParisConfig = {
|
|||
BrowserModule,
|
||||
HttpClientModule,
|
||||
AppRoutingModule,
|
||||
ParisModule.forRoot(parisConfig)
|
||||
ParisModule.forRoot(parisConfig),
|
||||
RouterModule
|
||||
],
|
||||
declarations: [
|
||||
AppComponent,
|
||||
TagComponent
|
||||
TagComponent,
|
||||
EntitiesNavComponent,
|
||||
EntityComponent
|
||||
],
|
||||
providers: [
|
||||
{
|
||||
provide: APP_BASE_HREF,
|
||||
useValue: '<%= APP_BASE %>'
|
||||
}
|
||||
},
|
||||
EntityResolver
|
||||
],
|
||||
bootstrap: [AppComponent]
|
||||
|
||||
|
|
|
@ -0,0 +1,20 @@
|
|||
import {ChangeDetectionStrategy, Component} from "@angular/core";
|
||||
import {entitiesService} from "../paris/services/entities.service";
|
||||
import {DataEntityType} from "../paris/entity/data-entity.base";
|
||||
import {ModelEntity} from "../paris/entity/entity.config";
|
||||
|
||||
@Component({
|
||||
selector: "entities-nav",
|
||||
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||
template: `
|
||||
<nav>
|
||||
<a *ngFor="let entity of entities"
|
||||
[routerLink]="['entity', entity.pluralName]">
|
||||
{{entity.pluralName}}
|
||||
</a>
|
||||
</nav>
|
||||
`
|
||||
})
|
||||
export class EntitiesNavComponent{
|
||||
entities:Array<ModelEntity> = entitiesService.allEntities;
|
||||
}
|
|
@ -0,0 +1,69 @@
|
|||
import {Component} from "@angular/core";
|
||||
import {DataEntityType} from "../paris/entity/data-entity.base";
|
||||
import {ActivatedRoute} from "@angular/router";
|
||||
import {IRepository} from "../paris/repository/repository.interface";
|
||||
import {RepositoryManagerService} from "../paris/repository/repository-manager.service";
|
||||
import {DataSet} from "../paris/dataset/dataset";
|
||||
import {Index} from "../paris/models/index";
|
||||
import {Field} from "../paris/entity/entity-field";
|
||||
import {HttpErrorResponse} from "@angular/common/http";
|
||||
|
||||
@Component({
|
||||
selector: "entity",
|
||||
template: `
|
||||
<h1>{{entity.entityConfig.pluralName}}</h1>
|
||||
<strong class="error" *ngIf="error; else items" style="color: Red">{{error.message}}</strong>
|
||||
<ng-template #items>
|
||||
<table *ngIf="allItems">
|
||||
<thead>
|
||||
<tr>
|
||||
<th *ngFor="let field of entity.entityConfig.fieldsArray">{{field.name}}</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr *ngFor="let item of allItems.items">
|
||||
<td *ngFor="let field of entity.entityConfig.fieldsArray">{{getFieldDisplay(item, field)}}</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</ng-template>
|
||||
`
|
||||
})
|
||||
export class EntityComponent{
|
||||
entity:DataEntityType;
|
||||
allItems:DataSet<any>;
|
||||
error:any;
|
||||
|
||||
private repo:IRepository;
|
||||
|
||||
constructor(route: ActivatedRoute, private repositoriesManagerService: RepositoryManagerService){
|
||||
route.params.subscribe(() => {
|
||||
if (this.entity = route.snapshot.data['entity']) {
|
||||
this.repo = repositoriesManagerService.getRepository(this.entity);
|
||||
this.updateAll();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private updateAll(){
|
||||
this.repo.getItemsDataSet({ page: 1, pageSize: 15 }).subscribe((allItemsDataSet:DataSet<any>) => {
|
||||
this.allItems = allItemsDataSet;
|
||||
this.error = null;
|
||||
}, (error:HttpErrorResponse) => {
|
||||
console.error(error);
|
||||
this.error = error
|
||||
});
|
||||
}
|
||||
|
||||
getFieldDisplay(item:Index, field:Field):string{
|
||||
let itemFieldValue = item[field.id];
|
||||
if (itemFieldValue !== undefined && itemFieldValue !== null) {
|
||||
if (itemFieldValue instanceof Array)
|
||||
return itemFieldValue.map(member => member && member.name || member).join(", ");
|
||||
|
||||
return itemFieldValue.name || itemFieldValue;
|
||||
}
|
||||
|
||||
return itemFieldValue || "N/A";
|
||||
}
|
||||
}
|
|
@ -0,0 +1,14 @@
|
|||
import {DataEntityType} from "./paris/entity/data-entity.base";
|
||||
import {ActivatedRouteSnapshot, Resolve, RouterStateSnapshot} from "@angular/router";
|
||||
import {Injectable} from "@angular/core";
|
||||
import {entitiesService} from "./paris/services/entities.service";
|
||||
|
||||
@Injectable()
|
||||
export class EntityResolver implements Resolve<DataEntityType> {
|
||||
resolve(
|
||||
route: ActivatedRouteSnapshot,
|
||||
state: RouterStateSnapshot
|
||||
): DataEntityType {
|
||||
return entitiesService.getEntityByPluralName(route.params.entityPluralName);
|
||||
}
|
||||
}
|
|
@ -4,6 +4,7 @@ import 'rxjs/add/operator/map';
|
|||
import 'rxjs/add/operator/catch';
|
||||
import 'rxjs/add/observable/of';
|
||||
import 'rxjs/add/observable/combineLatest';
|
||||
import 'rxjs/add/observable/from';
|
||||
import 'rxjs/add/operator/do';
|
||||
import 'rxjs/add/operator/mergeMap';
|
||||
import 'rxjs/add/operator/share';
|
||||
|
|
|
@ -1,9 +1,11 @@
|
|||
import {IIdentifiable} from "../models/identifiable.model";
|
||||
import {ModelEntity} from "./entity.config";
|
||||
|
||||
export interface DataEntityConstructor<T> extends DataEntityType{
|
||||
new(data:IIdentifiable): T
|
||||
}
|
||||
|
||||
export interface DataEntityType{
|
||||
new(data:IIdentifiable):any
|
||||
new(data:IIdentifiable):any,
|
||||
entityConfig?:ModelEntity
|
||||
}
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
import {EntityFields} from "./entity-fields";
|
||||
import {Field} from "./entity-field";
|
||||
|
||||
export class ModelEntity{
|
||||
singularName:string;
|
||||
|
@ -7,6 +8,11 @@ export class ModelEntity{
|
|||
fields?:EntityFields;
|
||||
loadAll?:boolean = false;
|
||||
listOf?:any;
|
||||
cache?:ModelEntityCacheConfig;
|
||||
|
||||
get fieldsArray():Array<Field>{
|
||||
return this.fields ? Array.from(this.fields.values()) : [];
|
||||
}
|
||||
|
||||
constructor(config:ModelEntityConfig){
|
||||
Object.assign(this, config);
|
||||
|
@ -18,5 +24,11 @@ export interface ModelEntityConfig{
|
|||
pluralName:string,
|
||||
endpoint:string,
|
||||
loadAll?:boolean,
|
||||
listOf?:any
|
||||
listOf?:any,
|
||||
cache?: ModelEntityCacheConfig
|
||||
}
|
||||
|
||||
interface ModelEntityCacheConfig{
|
||||
time?: number,
|
||||
max?: number
|
||||
}
|
||||
|
|
|
@ -4,6 +4,8 @@ import {entitiesService} from "../services/entities.service";
|
|||
|
||||
export function Entity(config:ModelEntityConfig){
|
||||
return (target:DataEntityType) => {
|
||||
entitiesService.addEntity(target, new ModelEntity(config));
|
||||
let entity:ModelEntity = new ModelEntity(config);
|
||||
target.entityConfig = entity;
|
||||
entitiesService.addEntity(target, entity);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,8 +1,11 @@
|
|||
import {Observable} from "rxjs/Observable";
|
||||
import {IIdentifiable} from "../models/identifiable.model";
|
||||
import {DataSetOptions} from "../dataset/dataset-options";
|
||||
import {DataSet} from "../dataset/dataset";
|
||||
|
||||
export interface IRepository{
|
||||
createItem:(itemData:any) => Observable<any>,
|
||||
getItemById:(id:any) => Observable<any>,
|
||||
getItemsDataSet:(options?:DataSetOptions) => Observable<DataSet<any>>,
|
||||
getItemSaveData:(item:IIdentifiable) => Object
|
||||
}
|
||||
|
|
|
@ -12,12 +12,27 @@ import {DataSetOptions} from "../dataset/dataset-options";
|
|||
import {DataSet} from "../dataset/dataset";
|
||||
import {Index} from "../models/index";
|
||||
import {DataTransformersService} from "../services/data-transformers.service";
|
||||
import {Immutability} from "../services/immutability";
|
||||
import {DataCache, DataCacheSettings} from "../services/cache";
|
||||
|
||||
export class Repository<T extends IIdentifiable>{
|
||||
save$:Subject<T> = new Subject<T>();
|
||||
|
||||
private _allValues:Array<T>;
|
||||
private _allValuesMap:Map<any, T>;
|
||||
private _cache:DataCache<T>;
|
||||
|
||||
private get cache():DataCache<T>{
|
||||
if (!this._cache) {
|
||||
let cacheSettings:DataCacheSettings<T> = Object.assign({
|
||||
getter: (itemId:string | number) => this.getItemById(itemId, false)
|
||||
}, this.entity.cache);
|
||||
|
||||
this._cache = new DataCache<T>(cacheSettings);
|
||||
}
|
||||
|
||||
return this._cache;
|
||||
}
|
||||
|
||||
constructor(public readonly entity:ModelEntity,
|
||||
private config:ParisConfig,
|
||||
|
@ -27,7 +42,7 @@ export class Repository<T extends IIdentifiable>{
|
|||
|
||||
createItem(itemData:any):Observable<T>{
|
||||
return this.getModelData(itemData)
|
||||
.map((modelData:ModelData) => new this.entityConstructor(modelData));
|
||||
.map((modelData:ModelData) => Immutability.freeze(new this.entityConstructor(modelData)));
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -107,15 +122,18 @@ export class Repository<T extends IIdentifiable>{
|
|||
let itemCreators:Array<Observable<T>> = dataSet.items.map((itemData:any) => this.createItem(itemData));
|
||||
|
||||
return Observable.combineLatest.apply(this, itemCreators).map((items:Array<T>) => {
|
||||
return {
|
||||
return Object.freeze({
|
||||
count: dataSet.count,
|
||||
items: items
|
||||
};
|
||||
items: Object.freeze(items)
|
||||
});
|
||||
})
|
||||
});
|
||||
}
|
||||
|
||||
getItemById(itemId:string|number):Observable<T>{
|
||||
getItemById(itemId:string|number, allowCache:boolean = true):Observable<T>{
|
||||
if (allowCache !== false && this.entity.cache)
|
||||
return this.cache.get(itemId);
|
||||
|
||||
if (this.entity.loadAll){
|
||||
if (!this._allValues){
|
||||
return this.getItemsDataSet()
|
||||
|
|
|
@ -0,0 +1,137 @@
|
|||
import {Observable} from "rxjs/Observable";
|
||||
|
||||
export class DataCache<T>{
|
||||
time:number;
|
||||
obj:number;
|
||||
getter:(_:any) => Promise<T> | Observable<T>;
|
||||
|
||||
private _keys:Array<string>;
|
||||
private _values:Map<string, T>;
|
||||
private _timeouts:{ [index:string]:any };
|
||||
private _getObservable:{ [index:string]:Observable<T> };
|
||||
|
||||
constructor(settings?:DataCacheSettings<T>){
|
||||
DataCache.validateSettings<T>(settings);
|
||||
|
||||
this.time = settings.time || null; // milliseconds
|
||||
this.obj = settings.max;
|
||||
this.getter = settings.getter;
|
||||
|
||||
this._keys = [];
|
||||
this._values = new Map<string, T>();
|
||||
this._timeouts = {};
|
||||
this._getObservable = {};
|
||||
|
||||
Object.seal(this);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a value from the cache collection.
|
||||
* If getter is specified, uses it to get the data.
|
||||
* @param key
|
||||
* @returns {Observable<T>}
|
||||
*/
|
||||
get(key:any):Observable<T>{
|
||||
if (!key && key !== 0)
|
||||
throw new Error("Can't get DataCache item, key not specified.");
|
||||
|
||||
key = key.toString();
|
||||
|
||||
if (this.getter){
|
||||
let getObservable = this._getObservable[key];
|
||||
if (getObservable)
|
||||
return getObservable;
|
||||
|
||||
let cachedItem = this._values.get(key);
|
||||
if (cachedItem)
|
||||
return Observable.of(cachedItem);
|
||||
|
||||
return this._getObservable[key] = Observable.from(this.getter(key))
|
||||
.do((value:T) => {
|
||||
this.add(key, value);
|
||||
delete this._getObservable[key];
|
||||
});
|
||||
}
|
||||
else
|
||||
return Observable.of(this._values.get(key));
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds an item to the Cached collection. If DataCache.time was specified, the item will expire after this time (in milliseconds), and will be deleted.
|
||||
* @param key {String}
|
||||
* @param value
|
||||
* @returns {Cache}
|
||||
*/
|
||||
add(key:any, value:T):DataCache<T>{
|
||||
key = key.toString();
|
||||
|
||||
let isNew = !this._values.has(key),
|
||||
valueTimeout = this._timeouts[key];
|
||||
|
||||
if (valueTimeout)
|
||||
clearTimeout(valueTimeout);
|
||||
|
||||
this._values.set(key, value);
|
||||
|
||||
if (isNew){
|
||||
this._keys.push(key);
|
||||
if (this._keys.length > this.obj)
|
||||
this._values.delete(this._keys.shift());
|
||||
}
|
||||
|
||||
if (this.time){
|
||||
this._timeouts[key] = setTimeout(() => {
|
||||
this.remove(key);
|
||||
delete this._timeouts[key];
|
||||
}, this.time);
|
||||
}
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes an item from the cache collection.
|
||||
* @param key
|
||||
* @returns {*}
|
||||
*/
|
||||
remove(key:any){
|
||||
key = key.toString();
|
||||
|
||||
let valueTimeout = this._timeouts[key];
|
||||
|
||||
if (valueTimeout) {
|
||||
clearTimeout(valueTimeout);
|
||||
delete this._timeouts[key];
|
||||
}
|
||||
|
||||
delete this._getObservable[key];
|
||||
|
||||
let keyIndex = this._keys.indexOf(key);
|
||||
if (~keyIndex){
|
||||
this._keys.splice(keyIndex, 1);
|
||||
let value = this._values.get(key);
|
||||
this._values.delete(key);
|
||||
return value;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
clearGetters(){
|
||||
for (let getter in this._getObservable) delete this._getObservable[getter];
|
||||
}
|
||||
|
||||
private static validateSettings<T>(config?:DataCacheSettings<T>){
|
||||
if (!config)
|
||||
return;
|
||||
|
||||
if (config.max < 1)
|
||||
throw new Error("Invalid max for DataCache, should be at least 2.");
|
||||
};
|
||||
}
|
||||
|
||||
export interface DataCacheSettings<T>{
|
||||
max?:number,
|
||||
time?:number,
|
||||
getter?:(_:any) => Observable<T>
|
||||
}
|
|
@ -4,16 +4,20 @@ import {Field} from "../entity/entity-field";
|
|||
import {EntityFields} from "../entity/entity-fields";
|
||||
|
||||
class EntitiesService{
|
||||
private allEntities:Map<DataEntityType, ModelEntity> = new Map;
|
||||
private _allEntities:Map<DataEntityType, ModelEntity> = new Map;
|
||||
private tempEntityFields:Map<DataEntityType, EntityFields> = new Map;
|
||||
|
||||
get allEntities():Array<ModelEntity>{
|
||||
return Array.from(this._allEntities.values());
|
||||
}
|
||||
|
||||
getEntityByType(dataEntityType:DataEntityType):ModelEntity{
|
||||
return this.allEntities.get(dataEntityType) || this.allEntities.get(dataEntityType.prototype);
|
||||
return this._allEntities.get(dataEntityType) || this._allEntities.get(dataEntityType.prototype);
|
||||
}
|
||||
|
||||
addEntity(dataEntityType:DataEntityType, entity:ModelEntity):ModelEntity{
|
||||
if (!this.allEntities.has(dataEntityType.prototype))
|
||||
this.allEntities.set(dataEntityType.prototype, entity);
|
||||
if (!this._allEntities.has(dataEntityType))
|
||||
this._allEntities.set(dataEntityType, entity);
|
||||
|
||||
entity.fields = this.getDataEntityTypeFields(dataEntityType);
|
||||
|
||||
|
@ -32,12 +36,24 @@ class EntitiesService{
|
|||
dataTypeFields.set(field.id, field);
|
||||
}
|
||||
|
||||
getEntityByPluralName(pluralName:string):DataEntityType{
|
||||
let allEntities:Array<DataEntityType> = Array.from(this._allEntities.keys()),
|
||||
pluralNameLowerCase = pluralName.toLowerCase();
|
||||
|
||||
for(let i=0, entity:DataEntityType; entity = allEntities[i]; i++){
|
||||
if (entity.entityConfig.pluralName.toLowerCase() === pluralNameLowerCase)
|
||||
return entity;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private getDataEntityTypeFields(dataEntityType:DataEntityType):EntityFields{
|
||||
if (!dataEntityType)
|
||||
return null;
|
||||
|
||||
let parentEntityDataType:DataEntityType = Object.getPrototypeOf(dataEntityType).prototype,
|
||||
parentEntity:ModelEntity = this.allEntities.get(parentEntityDataType),
|
||||
parentEntity:ModelEntity = this._allEntities.get(parentEntityDataType),
|
||||
parentDataTypeFields:EntityFields = parentEntity && parentEntity.fields || this.getDataEntityTypeFields(parentEntityDataType) || null;
|
||||
|
||||
let fullDataEntityTypeFields:EntityFields = new Map;
|
||||
|
|
|
@ -0,0 +1,27 @@
|
|||
import {Index} from "../models/index";
|
||||
|
||||
export class Immutability{
|
||||
static freeze<T>(obj:T):Readonly<T> {
|
||||
if (!Object.isFrozen(obj))
|
||||
Object.freeze(obj);
|
||||
|
||||
if (Object(obj) === obj)
|
||||
Object.getOwnPropertyNames(obj).forEach((prop: string) => Immutability.freeze((<Index>obj)[prop]));
|
||||
|
||||
return obj;
|
||||
}
|
||||
|
||||
static unfreeze<T>(obj:Readonly<T>):T{
|
||||
if (Object(obj) !== obj || obj instanceof Date || obj instanceof RegExp || obj instanceof Function)
|
||||
return obj;
|
||||
|
||||
let unfrozenObj:T = Object.create(obj.constructor.prototype);
|
||||
Object.assign(unfrozenObj, obj);
|
||||
|
||||
Object.getOwnPropertyNames(obj).forEach((prop: string) => {
|
||||
(<Index>unfrozenObj)[prop] = Immutability.unfreeze((<Index>unfrozenObj)[prop]);
|
||||
});
|
||||
|
||||
return unfrozenObj;
|
||||
}
|
||||
}
|
|
@ -69,3 +69,21 @@ button {
|
|||
button:hover {
|
||||
background-color: #28739e;
|
||||
}
|
||||
|
||||
table{
|
||||
border: solid 1px #ddd;
|
||||
}
|
||||
|
||||
table td, table th{
|
||||
text-align: left;
|
||||
padding: 6px 10px;
|
||||
}
|
||||
|
||||
thead tr{
|
||||
background: #106cc8;
|
||||
color: White;
|
||||
}
|
||||
|
||||
tr + tr{
|
||||
border-top: solid 1px #eaeaea;
|
||||
}
|
||||
|
|
|
@ -3,7 +3,6 @@ const fs = require("fs");
|
|||
const bodyParser = require('body-parser');
|
||||
const https = require("https");
|
||||
|
||||
|
||||
var routeModules = [
|
||||
//require("./modules/investigations.routes"),
|
||||
//require("./modules/alerts.routes"),
|
|
@ -1,6 +1,6 @@
|
|||
fs = require("fs");
|
||||
|
||||
var MOCK_DATA_FOLDER = "./data/";
|
||||
var MOCK_DATA_FOLDER = "./mock_data/";
|
||||
|
||||
var exports = module.exports = {
|
||||
getFileData: getFileData,
|
Загрузка…
Ссылка в новой задаче