aboutsummaryrefslogtreecommitdiff
path: root/Timeline
diff options
context:
space:
mode:
Diffstat (limited to 'Timeline')
-rw-r--r--Timeline/ClientApp/.editorconfig13
-rw-r--r--Timeline/ClientApp/.gitignore40
-rw-r--r--Timeline/ClientApp/.prettierrc.json4
-rw-r--r--Timeline/ClientApp/.vscode/launch.json15
-rw-r--r--Timeline/ClientApp/README.md27
-rw-r--r--Timeline/ClientApp/angular.json151
-rw-r--r--Timeline/ClientApp/e2e/protractor.conf.js28
-rw-r--r--Timeline/ClientApp/e2e/src/app.e2e-spec.ts14
-rw-r--r--Timeline/ClientApp/e2e/src/app.po.ts11
-rw-r--r--Timeline/ClientApp/e2e/tsconfig.e2e.json13
-rw-r--r--Timeline/ClientApp/package.json59
-rw-r--r--Timeline/ClientApp/src/app/app.component.css10
-rw-r--r--Timeline/ClientApp/src/app/app.component.html20
-rw-r--r--Timeline/ClientApp/src/app/app.component.ts13
-rw-r--r--Timeline/ClientApp/src/app/app.module.ts29
-rw-r--r--Timeline/ClientApp/src/app/app.server.module.ts11
-rw-r--r--Timeline/ClientApp/src/app/home/home.component.css49
-rw-r--r--Timeline/ClientApp/src/app/home/home.component.html3
-rw-r--r--Timeline/ClientApp/src/app/home/home.component.spec.ts26
-rw-r--r--Timeline/ClientApp/src/app/home/home.component.ts10
-rw-r--r--Timeline/ClientApp/src/app/home/home.module.ts17
-rw-r--r--Timeline/ClientApp/src/app/test-utilities/activated-route.mock.ts68
-rw-r--r--Timeline/ClientApp/src/app/test-utilities/mock.ts7
-rw-r--r--Timeline/ClientApp/src/app/test-utilities/router-link.mock.ts9
-rw-r--r--Timeline/ClientApp/src/app/test-utilities/storage.mock.ts28
-rw-r--r--Timeline/ClientApp/src/app/todo/todo-item.ts6
-rw-r--r--Timeline/ClientApp/src/app/todo/todo-item/todo-item.component.css25
-rw-r--r--Timeline/ClientApp/src/app/todo/todo-item/todo-item.component.html9
-rw-r--r--Timeline/ClientApp/src/app/todo/todo-item/todo-item.component.spec.ts46
-rw-r--r--Timeline/ClientApp/src/app/todo/todo-item/todo-item.component.ts12
-rw-r--r--Timeline/ClientApp/src/app/todo/todo-list-color-block.css7
-rw-r--r--Timeline/ClientApp/src/app/todo/todo-page/todo-page.component.css39
-rw-r--r--Timeline/ClientApp/src/app/todo/todo-page/todo-page.component.html21
-rw-r--r--Timeline/ClientApp/src/app/todo/todo-page/todo-page.component.spec.ts78
-rw-r--r--Timeline/ClientApp/src/app/todo/todo-page/todo-page.component.ts39
-rw-r--r--Timeline/ClientApp/src/app/todo/todo-service/http-entities.ts11
-rw-r--r--Timeline/ClientApp/src/app/todo/todo-service/todo.service.spec.ts54
-rw-r--r--Timeline/ClientApp/src/app/todo/todo-service/todo.service.ts33
-rw-r--r--Timeline/ClientApp/src/app/todo/todo.module.ts27
-rw-r--r--Timeline/ClientApp/src/app/user/auth.guard.spec.ts69
-rw-r--r--Timeline/ClientApp/src/app/user/auth.guard.ts80
-rw-r--r--Timeline/ClientApp/src/app/user/entities.ts9
-rw-r--r--Timeline/ClientApp/src/app/user/internal-user-service/errors.ts29
-rw-r--r--Timeline/ClientApp/src/app/user/internal-user-service/http-entities.ts21
-rw-r--r--Timeline/ClientApp/src/app/user/internal-user-service/internal-user.service.mock.ts5
-rw-r--r--Timeline/ClientApp/src/app/user/internal-user-service/internal-user.service.spec.ts123
-rw-r--r--Timeline/ClientApp/src/app/user/internal-user-service/internal-user.service.ts155
-rw-r--r--Timeline/ClientApp/src/app/user/redirect.component.ts15
-rw-r--r--Timeline/ClientApp/src/app/user/user-dialog/user-dialog.component.css0
-rw-r--r--Timeline/ClientApp/src/app/user/user-dialog/user-dialog.component.html1
-rw-r--r--Timeline/ClientApp/src/app/user/user-dialog/user-dialog.component.spec.ts43
-rw-r--r--Timeline/ClientApp/src/app/user/user-dialog/user-dialog.component.ts24
-rw-r--r--Timeline/ClientApp/src/app/user/user-login-success/user-login-success.component.css22
-rw-r--r--Timeline/ClientApp/src/app/user/user-login-success/user-login-success.component.html6
-rw-r--r--Timeline/ClientApp/src/app/user/user-login-success/user-login-success.component.spec.ts75
-rw-r--r--Timeline/ClientApp/src/app/user/user-login-success/user-login-success.component.ts25
-rw-r--r--Timeline/ClientApp/src/app/user/user-login/user-login.component.css24
-rw-r--r--Timeline/ClientApp/src/app/user/user-login/user-login.component.html19
-rw-r--r--Timeline/ClientApp/src/app/user/user-login/user-login.component.spec.ts123
-rw-r--r--Timeline/ClientApp/src/app/user/user-login/user-login.component.ts38
-rw-r--r--Timeline/ClientApp/src/app/user/user-logout/user-logout.component.css0
-rw-r--r--Timeline/ClientApp/src/app/user/user-logout/user-logout.component.html1
-rw-r--r--Timeline/ClientApp/src/app/user/user-logout/user-logout.component.spec.ts35
-rw-r--r--Timeline/ClientApp/src/app/user/user-logout/user-logout.component.ts16
-rw-r--r--Timeline/ClientApp/src/app/user/user.module.ts38
-rw-r--r--Timeline/ClientApp/src/app/user/user.service.ts55
-rw-r--r--Timeline/ClientApp/src/app/user/window-inject-token.ts3
-rw-r--r--Timeline/ClientApp/src/app/utilities/debounce-click.directive.spec.ts123
-rw-r--r--Timeline/ClientApp/src/app/utilities/debounce-click.directive.ts41
-rw-r--r--Timeline/ClientApp/src/app/utilities/language-untilities.ts18
-rw-r--r--Timeline/ClientApp/src/app/utilities/utility.module.ts11
-rw-r--r--Timeline/ClientApp/src/assets/.gitkeep0
-rw-r--r--Timeline/ClientApp/src/assets/icon.svg7
-rw-r--r--Timeline/ClientApp/src/browserslist9
-rw-r--r--Timeline/ClientApp/src/environments/environment.prod.ts3
-rw-r--r--Timeline/ClientApp/src/environments/environment.ts15
-rw-r--r--Timeline/ClientApp/src/index.html17
-rw-r--r--Timeline/ClientApp/src/karma.conf.js32
-rw-r--r--Timeline/ClientApp/src/main.ts20
-rw-r--r--Timeline/ClientApp/src/polyfills.ts80
-rw-r--r--Timeline/ClientApp/src/styles.css14
-rw-r--r--Timeline/ClientApp/src/test.ts22
-rw-r--r--Timeline/ClientApp/src/tsconfig.app.json15
-rw-r--r--Timeline/ClientApp/src/tsconfig.server.json9
-rw-r--r--Timeline/ClientApp/src/tsconfig.spec.json22
-rw-r--r--Timeline/ClientApp/src/tslint.json17
-rw-r--r--Timeline/ClientApp/tsconfig.json25
-rw-r--r--Timeline/ClientApp/tslint.json130
-rw-r--r--Timeline/Controllers/UserController.cs2
-rw-r--r--Timeline/Controllers/UserTestController.cs2
-rw-r--r--Timeline/Properties/launchSettings.json4
-rw-r--r--Timeline/Startup.cs48
-rw-r--r--Timeline/Timeline-CI.csproj35
-rw-r--r--Timeline/Timeline.csproj63
-rw-r--r--Timeline/appsettings.json4
-rw-r--r--Timeline/wwwroot/android-chrome-192x192.pngbin1834 -> 0 bytes
-rw-r--r--Timeline/wwwroot/android-chrome-512x512.pngbin6153 -> 0 bytes
-rw-r--r--Timeline/wwwroot/apple-touch-icon.pngbin1557 -> 0 bytes
-rw-r--r--Timeline/wwwroot/browserconfig.xml9
-rw-r--r--Timeline/wwwroot/favicon-16x16.pngbin536 -> 0 bytes
-rw-r--r--Timeline/wwwroot/favicon-32x32.pngbin678 -> 0 bytes
-rw-r--r--Timeline/wwwroot/favicon.icobin15086 -> 0 bytes
-rw-r--r--Timeline/wwwroot/mstile-144x144.pngbin1371 -> 0 bytes
-rw-r--r--Timeline/wwwroot/mstile-150x150.pngbin1400 -> 0 bytes
-rw-r--r--Timeline/wwwroot/mstile-310x150.pngbin1593 -> 0 bytes
-rw-r--r--Timeline/wwwroot/mstile-310x310.pngbin3333 -> 0 bytes
-rw-r--r--Timeline/wwwroot/mstile-70x70.pngbin1049 -> 0 bytes
-rw-r--r--Timeline/wwwroot/safari-pinned-tab.svg30
-rw-r--r--Timeline/wwwroot/site.webmanifest19
109 files changed, 43 insertions, 2939 deletions
diff --git a/Timeline/ClientApp/.editorconfig b/Timeline/ClientApp/.editorconfig
deleted file mode 100644
index 6e87a003..00000000
--- a/Timeline/ClientApp/.editorconfig
+++ /dev/null
@@ -1,13 +0,0 @@
-# Editor configuration, see http://editorconfig.org
-root = true
-
-[*]
-charset = utf-8
-indent_style = space
-indent_size = 2
-insert_final_newline = true
-trim_trailing_whitespace = true
-
-[*.md]
-max_line_length = off
-trim_trailing_whitespace = false
diff --git a/Timeline/ClientApp/.gitignore b/Timeline/ClientApp/.gitignore
deleted file mode 100644
index e1f679be..00000000
--- a/Timeline/ClientApp/.gitignore
+++ /dev/null
@@ -1,40 +0,0 @@
-# See http://help.github.com/ignore-files/ for more about ignoring files.
-
-# compiled output
-/dist
-/dist-server
-/tmp
-/out-tsc
-
-# dependencies
-/node_modules
-
-# IDEs and editors
-/.idea
-.project
-.classpath
-.c9/
-*.launch
-.settings/
-*.sublime-workspace
-
-# IDE - VSCode
-.vscode/*
-!.vscode/settings.json
-!.vscode/tasks.json
-!.vscode/launch.json
-!.vscode/extensions.json
-
-# misc
-/.sass-cache
-/connect.lock
-/coverage
-/libpeerconnection.log
-npm-debug.log
-yarn-error.log
-testem.log
-/typings
-
-# System Files
-.DS_Store
-Thumbs.db
diff --git a/Timeline/ClientApp/.prettierrc.json b/Timeline/ClientApp/.prettierrc.json
deleted file mode 100644
index 6c70cb20..00000000
--- a/Timeline/ClientApp/.prettierrc.json
+++ /dev/null
@@ -1,4 +0,0 @@
-{
- "printWidth": 140,
- "singleQuote": true
-}
diff --git a/Timeline/ClientApp/.vscode/launch.json b/Timeline/ClientApp/.vscode/launch.json
deleted file mode 100644
index 73e17a72..00000000
--- a/Timeline/ClientApp/.vscode/launch.json
+++ /dev/null
@@ -1,15 +0,0 @@
-{
- // Use IntelliSense to learn about possible attributes.
- // Hover to view descriptions of existing attributes.
- // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
- "version": "0.2.0",
- "configurations": [
- {
- "type": "chrome",
- "request": "launch",
- "name": "Launch app",
- "url": "https://localhost:5001",
- "webRoot": "${workspaceFolder}"
- }
- ]
-}
diff --git a/Timeline/ClientApp/README.md b/Timeline/ClientApp/README.md
deleted file mode 100644
index 9f66fc75..00000000
--- a/Timeline/ClientApp/README.md
+++ /dev/null
@@ -1,27 +0,0 @@
-# Timeline
-
-This project was generated with [Angular CLI](https://github.com/angular/angular-cli) version 6.0.0.
-
-## Development server
-
-Run `ng serve` for a dev server. Navigate to `http://localhost:4200/`. The app will automatically reload if you change any of the source files.
-
-## Code scaffolding
-
-Run `ng generate component component-name` to generate a new component. You can also use `ng generate directive|pipe|service|class|guard|interface|enum|module`.
-
-## Build
-
-Run `ng build` to build the project. The build artifacts will be stored in the `dist/` directory. Use the `--prod` flag for a production build.
-
-## Running unit tests
-
-Run `ng test` to execute the unit tests via [Karma](https://karma-runner.github.io).
-
-## Running end-to-end tests
-
-Run `ng e2e` to execute the end-to-end tests via [Protractor](http://www.protractortest.org/).
-
-## Further help
-
-To get more help on the Angular CLI use `ng help` or go check out the [Angular CLI README](https://github.com/angular/angular-cli/blob/master/README.md).
diff --git a/Timeline/ClientApp/angular.json b/Timeline/ClientApp/angular.json
deleted file mode 100644
index 9a696714..00000000
--- a/Timeline/ClientApp/angular.json
+++ /dev/null
@@ -1,151 +0,0 @@
-{
- "$schema": "./node_modules/@angular/cli/lib/config/schema.json",
- "version": 1,
- "cli": {
- "packageManager": "yarn"
- },
- "newProjectRoot": "projects",
- "projects": {
- "Timeline": {
- "root": "",
- "sourceRoot": "src",
- "projectType": "application",
- "prefix": "app",
- "schematics": {},
- "architect": {
- "build": {
- "builder": "@angular-devkit/build-angular:browser",
- "options": {
- "progress": true,
- "extractCss": true,
- "outputPath": "dist",
- "index": "src/index.html",
- "main": "src/main.ts",
- "polyfills": "src/polyfills.ts",
- "tsConfig": "src/tsconfig.app.json",
- "assets": [
- "src/assets"
- ],
- "styles": [
- "src/styles.css"
- ],
- "scripts": []
- },
- "configurations": {
- "production": {
- "fileReplacements": [
- {
- "replace": "src/environments/environment.ts",
- "with": "src/environments/environment.prod.ts"
- }
- ],
- "optimization": true,
- "outputHashing": "all",
- "sourceMap": false,
- "extractCss": true,
- "namedChunks": false,
- "aot": true,
- "extractLicenses": true,
- "vendorChunk": false,
- "buildOptimizer": true
- }
- }
- },
- "serve": {
- "builder": "@angular-devkit/build-angular:dev-server",
- "options": {
- "browserTarget": "Timeline:build"
- },
- "configurations": {
- "production": {
- "browserTarget": "Timeline:build:production"
- }
- }
- },
- "extract-i18n": {
- "builder": "@angular-devkit/build-angular:extract-i18n",
- "options": {
- "browserTarget": "Timeline:build"
- }
- },
- "test": {
- "builder": "@angular-devkit/build-angular:karma",
- "options": {
- "main": "src/test.ts",
- "polyfills": "src/polyfills.ts",
- "tsConfig": "src/tsconfig.spec.json",
- "karmaConfig": "src/karma.conf.js",
- "styles": [
- "src/styles.css"
- ],
- "scripts": [],
- "assets": [
- "src/assets"
- ]
- }
- },
- "lint": {
- "builder": "@angular-devkit/build-angular:tslint",
- "options": {
- "tsConfig": [
- "src/tsconfig.app.json",
- "src/tsconfig.spec.json"
- ],
- "exclude": [
- "**/node_modules/**"
- ]
- }
- },
- "server": {
- "builder": "@angular-devkit/build-angular:server",
- "options": {
- "outputPath": "dist-server",
- "main": "src/main.ts",
- "tsConfig": "src/tsconfig.server.json"
- },
- "configurations": {
- "dev": {
- "optimization": true,
- "outputHashing": "all",
- "sourceMap": false,
- "namedChunks": false,
- "extractLicenses": true,
- "vendorChunk": true
- },
- "production": {
- "optimization": true,
- "outputHashing": "all",
- "sourceMap": false,
- "namedChunks": false,
- "extractLicenses": true,
- "vendorChunk": false
- }
- }
- }
- }
- },
- "Timeline-e2e": {
- "root": "e2e/",
- "projectType": "application",
- "architect": {
- "e2e": {
- "builder": "@angular-devkit/build-angular:protractor",
- "options": {
- "protractorConfig": "e2e/protractor.conf.js",
- "devServerTarget": "Timeline:serve"
- }
- },
- "lint": {
- "builder": "@angular-devkit/build-angular:tslint",
- "options": {
- "tsConfig": "e2e/tsconfig.e2e.json",
- "exclude": [
- "**/node_modules/**"
- ]
- }
- }
- }
- }
- },
- "defaultProject": "Timeline"
-}
diff --git a/Timeline/ClientApp/e2e/protractor.conf.js b/Timeline/ClientApp/e2e/protractor.conf.js
deleted file mode 100644
index 86776a39..00000000
--- a/Timeline/ClientApp/e2e/protractor.conf.js
+++ /dev/null
@@ -1,28 +0,0 @@
-// Protractor configuration file, see link for more information
-// https://github.com/angular/protractor/blob/master/lib/config.ts
-
-const { SpecReporter } = require('jasmine-spec-reporter');
-
-exports.config = {
- allScriptsTimeout: 11000,
- specs: [
- './src/**/*.e2e-spec.ts'
- ],
- capabilities: {
- 'browserName': 'chrome'
- },
- directConnect: true,
- baseUrl: 'http://localhost:4200/',
- framework: 'jasmine',
- jasmineNodeOpts: {
- showColors: true,
- defaultTimeoutInterval: 30000,
- print: function() {}
- },
- onPrepare() {
- require('ts-node').register({
- project: require('path').join(__dirname, './tsconfig.e2e.json')
- });
- jasmine.getEnv().addReporter(new SpecReporter({ spec: { displayStacktrace: true } }));
- }
-}; \ No newline at end of file
diff --git a/Timeline/ClientApp/e2e/src/app.e2e-spec.ts b/Timeline/ClientApp/e2e/src/app.e2e-spec.ts
deleted file mode 100644
index 5b3b4b27..00000000
--- a/Timeline/ClientApp/e2e/src/app.e2e-spec.ts
+++ /dev/null
@@ -1,14 +0,0 @@
-import { AppPage } from './app.po';
-
-describe('App', () => {
- let page: AppPage;
-
- beforeEach(() => {
- page = new AppPage();
- });
-
- it('should display welcome message', () => {
- page.navigateTo();
- expect(page.getMainHeading()).toEqual('Hello, world!');
- });
-});
diff --git a/Timeline/ClientApp/e2e/src/app.po.ts b/Timeline/ClientApp/e2e/src/app.po.ts
deleted file mode 100644
index 24bc8b3c..00000000
--- a/Timeline/ClientApp/e2e/src/app.po.ts
+++ /dev/null
@@ -1,11 +0,0 @@
-import { browser, by, element } from 'protractor';
-
-export class AppPage {
- navigateTo() {
- return browser.get('/');
- }
-
- getMainHeading() {
- return element(by.css('app-root h1')).getText();
- }
-}
diff --git a/Timeline/ClientApp/e2e/tsconfig.e2e.json b/Timeline/ClientApp/e2e/tsconfig.e2e.json
deleted file mode 100644
index a6dd6220..00000000
--- a/Timeline/ClientApp/e2e/tsconfig.e2e.json
+++ /dev/null
@@ -1,13 +0,0 @@
-{
- "extends": "../tsconfig.json",
- "compilerOptions": {
- "outDir": "../out-tsc/app",
- "module": "commonjs",
- "target": "es5",
- "types": [
- "jasmine",
- "jasminewd2",
- "node"
- ]
- }
-} \ No newline at end of file
diff --git a/Timeline/ClientApp/package.json b/Timeline/ClientApp/package.json
deleted file mode 100644
index 7c6d28f0..00000000
--- a/Timeline/ClientApp/package.json
+++ /dev/null
@@ -1,59 +0,0 @@
-{
- "name": "timeline",
- "version": "0.0.0",
- "scripts": {
- "ng": "ng",
- "start": "ng serve",
- "start-dotnet": "dotnet run --project ../Timeline.csproj",
- "build": "ng build",
- "build:ssr": "ng run Timeline:server:dev",
- "test": "ng test",
- "lint": "ng lint",
- "e2e": "ng e2e"
- },
- "private": true,
- "dependencies": {
- "@angular/animations": "^7.2.4",
- "@angular/cdk": "^7.3.2",
- "@angular/common": "^7.2.4",
- "@angular/compiler": "^7.2.4",
- "@angular/core": "^7.2.4",
- "@angular/forms": "^7.2.4",
- "@angular/http": "^7.2.4",
- "@angular/material": "^7.3.2",
- "@angular/platform-browser": "^7.2.4",
- "@angular/platform-browser-dynamic": "^7.2.4",
- "@angular/platform-server": "^7.2.4",
- "@angular/router": "^7.2.4",
- "@nguniversal/module-map-ngfactory-loader": "^7.1.0",
- "aspnet-prerendering": "^3.0.1",
- "core-js": "^2.6.3",
- "rxjs": "^6.3.3",
- "zone.js": "^0.8.29"
- },
- "devDependencies": {
- "@angular-devkit/build-angular": "^0.12.3",
- "@angular/cli": "^7.3.1",
- "@angular/compiler-cli": "^7.2.4",
- "@angular/language-service": "^7.2.4",
- "@types/jasmine": "^3.3.8",
- "@types/jasminewd2": "^2.0.6",
- "@types/node": "^10.12.19",
- "codelyzer": "^4.5.0",
- "jasmine-core": "^3.3.0",
- "jasmine-spec-reporter": "^4.2.1",
- "karma": "^4.0.0",
- "karma-chrome-launcher": "^2.2.0",
- "karma-coverage-istanbul-reporter": "^2.0.4",
- "karma-jasmine": "^2.0.1",
- "karma-jasmine-html-reporter": "^1.4.0",
- "karma-junit-reporter": "^1.2.0",
- "tslint": "^5.12.1",
- "typescript": "~3.2.4"
- },
- "optionalDependencies": {
- "node-sass": "^4.9.3",
- "protractor": "^5.4.0",
- "ts-node": "^5.0.1"
- }
-}
diff --git a/Timeline/ClientApp/src/app/app.component.css b/Timeline/ClientApp/src/app/app.component.css
deleted file mode 100644
index 13a44248..00000000
--- a/Timeline/ClientApp/src/app/app.component.css
+++ /dev/null
@@ -1,10 +0,0 @@
-a.icp-record {
- color: grey;
- text-decoration: none;
- font-size: 0.8rem;
- margin: 0 5vw;
-}
-
-footer {
- display: flex;
-}
diff --git a/Timeline/ClientApp/src/app/app.component.html b/Timeline/ClientApp/src/app/app.component.html
deleted file mode 100644
index 92c88625..00000000
--- a/Timeline/ClientApp/src/app/app.component.html
+++ /dev/null
@@ -1,20 +0,0 @@
-<body>
- <mat-toolbar color="primary" class="mat-elevation-z4">
- <a mat-button routerLink="/">
- <img width="30" height="30" src="assets/icon.svg">Timeline</a>
- <a mat-button routerLink="/todo">TodoList</a>
- <span class="fill-remaining-space"></span>
- <a mat-icon-button [routerLink]="[{outlets: {user: ['login']}}]">
- <mat-icon>account_circle</mat-icon>
- </a>
- </mat-toolbar>
-
- <div>
- <router-outlet></router-outlet>
- </div>
-
- <footer>
- <span class="fill-remaining-space"></span>
- <a class="icp-record" href="http://www.miitbeian.gov.cn/state/outPortal/loginPortal.action" target=”_blank”>鄂ICP备18030913号-1</a>
- </footer>
-</body>
diff --git a/Timeline/ClientApp/src/app/app.component.ts b/Timeline/ClientApp/src/app/app.component.ts
deleted file mode 100644
index 33f33048..00000000
--- a/Timeline/ClientApp/src/app/app.component.ts
+++ /dev/null
@@ -1,13 +0,0 @@
-import { Component } from '@angular/core';
-
-import { UserService } from './user/user.service';
-
-@Component({
- selector: 'app-root',
- templateUrl: './app.component.html',
- styleUrls: ['./app.component.css']
-})
-export class AppComponent {
- // never remove userService because we need it explicit constructing.
- constructor(userService: UserService) { }
-}
diff --git a/Timeline/ClientApp/src/app/app.module.ts b/Timeline/ClientApp/src/app/app.module.ts
deleted file mode 100644
index b75e10e2..00000000
--- a/Timeline/ClientApp/src/app/app.module.ts
+++ /dev/null
@@ -1,29 +0,0 @@
-import { BrowserModule } from '@angular/platform-browser';
-import { NgModule } from '@angular/core';
-import { RouterModule } from '@angular/router';
-import { BrowserAnimationsModule } from '@angular/platform-browser/animations';
-import { MatIconModule, MatButtonModule, MatToolbarModule, MatDialogModule } from '@angular/material';
-
-import { AppComponent } from './app.component';
-
-import { TodoModule } from './todo/todo.module';
-import { HomeModule } from './home/home.module';
-import { UserModule } from './user/user.module';
-import { UserService } from './user/user.service';
-
-
-@NgModule({
- declarations: [AppComponent],
- imports: [
- BrowserModule.withServerTransition({ appId: 'ng-cli-universal' }),
- BrowserAnimationsModule,
- MatIconModule, MatButtonModule, MatToolbarModule, MatDialogModule,
- HomeModule, TodoModule, UserModule,
- RouterModule.forRoot([
- { path: '', redirectTo: '/home', pathMatch: 'full' },
- ])
- ],
- providers: [UserService],
- bootstrap: [AppComponent]
-})
-export class AppModule { }
diff --git a/Timeline/ClientApp/src/app/app.server.module.ts b/Timeline/ClientApp/src/app/app.server.module.ts
deleted file mode 100644
index cfb0e021..00000000
--- a/Timeline/ClientApp/src/app/app.server.module.ts
+++ /dev/null
@@ -1,11 +0,0 @@
-import { NgModule } from '@angular/core';
-import { ServerModule } from '@angular/platform-server';
-import { ModuleMapLoaderModule } from '@nguniversal/module-map-ngfactory-loader';
-import { AppComponent } from './app.component';
-import { AppModule } from './app.module';
-
-@NgModule({
- imports: [AppModule, ServerModule, ModuleMapLoaderModule],
- bootstrap: [AppComponent]
-})
-export class AppServerModule { }
diff --git a/Timeline/ClientApp/src/app/home/home.component.css b/Timeline/ClientApp/src/app/home/home.component.css
deleted file mode 100644
index 76297a9e..00000000
--- a/Timeline/ClientApp/src/app/home/home.component.css
+++ /dev/null
@@ -1,49 +0,0 @@
-p {
- font-size: 2rem;
- margin: 0;
-}
-
-.bold {
- font-weight: 600;
-}
-
-#loginBox {
- display: inline-grid;
- grid-template: "username-label username-input" auto
- "password-label password-input" auto
- "login-button login-button" auto
- "message message" auto
- / max-content max-content;
- align-items: center;
- padding: 10px;
- border: solid black 1px;
-}
-
-#usernameLabel {
- grid-area: username-label;
-}
-
-#usernameInput {
- grid-area: username-input;
- margin: 2px;
-}
-
-#passwordLabel {
- grid-area: password-label;
-}
-
-#passwordInput {
- grid-area: password-input;
- margin: 2px;
-}
-
-#loginButton {
- grid-area: login-button;
- justify-self: end;
-}
-
-#loginMessage {
- grid-area: message;
- justify-self: end;
- color: red;
-}
diff --git a/Timeline/ClientApp/src/app/home/home.component.html b/Timeline/ClientApp/src/app/home/home.component.html
deleted file mode 100644
index 28ab3039..00000000
--- a/Timeline/ClientApp/src/app/home/home.component.html
+++ /dev/null
@@ -1,3 +0,0 @@
-<h2 class="mat-h2">
- This page is under <span class="bold">construction</span>!
-</h2>
diff --git a/Timeline/ClientApp/src/app/home/home.component.spec.ts b/Timeline/ClientApp/src/app/home/home.component.spec.ts
deleted file mode 100644
index 74bedd08..00000000
--- a/Timeline/ClientApp/src/app/home/home.component.spec.ts
+++ /dev/null
@@ -1,26 +0,0 @@
-import { async, ComponentFixture, TestBed } from '@angular/core/testing';
-
-import { HomeComponent } from './home.component';
-
-
-describe('HomeComponent', () => {
- let component: HomeComponent;
- let fixture: ComponentFixture<HomeComponent>;
-
- beforeEach(async(() => {
- TestBed.configureTestingModule({
- declarations: [HomeComponent],
- })
- .compileComponents();
- }));
-
- beforeEach(() => {
- fixture = TestBed.createComponent(HomeComponent);
- component = fixture.componentInstance;
- fixture.detectChanges();
- });
-
- it('should create', () => {
- expect(component).toBeTruthy();
- });
-});
diff --git a/Timeline/ClientApp/src/app/home/home.component.ts b/Timeline/ClientApp/src/app/home/home.component.ts
deleted file mode 100644
index 0cb0d0f5..00000000
--- a/Timeline/ClientApp/src/app/home/home.component.ts
+++ /dev/null
@@ -1,10 +0,0 @@
-import { Component } from '@angular/core';
-
-@Component({
- selector: 'app-home',
- templateUrl: './home.component.html',
- styleUrls: ['./home.component.css']
-})
-export class HomeComponent {
-
-}
diff --git a/Timeline/ClientApp/src/app/home/home.module.ts b/Timeline/ClientApp/src/app/home/home.module.ts
deleted file mode 100644
index 98456238..00000000
--- a/Timeline/ClientApp/src/app/home/home.module.ts
+++ /dev/null
@@ -1,17 +0,0 @@
-import { NgModule } from '@angular/core';
-import { CommonModule } from '@angular/common';
-import { RouterModule } from '@angular/router';
-
-import { HomeComponent } from './home.component';
-
-@NgModule({
- declarations: [HomeComponent],
- imports: [
- CommonModule,
- RouterModule.forChild([
- { path: 'home', component: HomeComponent }
- ])
- ],
- exports: [RouterModule]
-})
-export class HomeModule { }
diff --git a/Timeline/ClientApp/src/app/test-utilities/activated-route.mock.ts b/Timeline/ClientApp/src/app/test-utilities/activated-route.mock.ts
deleted file mode 100644
index 40484387..00000000
--- a/Timeline/ClientApp/src/app/test-utilities/activated-route.mock.ts
+++ /dev/null
@@ -1,68 +0,0 @@
-import { ParamMap, ActivatedRouteSnapshot, ActivatedRoute } from '@angular/router';
-
-import { Observable, BehaviorSubject } from 'rxjs';
-import { map } from 'rxjs/operators';
-
-import { PartialMock } from './mock';
-
-export interface ParamMapCreator { [name: string]: string | string[]; }
-
-export class MockActivatedRouteSnapshot implements PartialMock<ActivatedRouteSnapshot> {
-
- private paramMapInternal: ParamMap;
-
- constructor({ mockParamMap }: { mockParamMap: ParamMapCreator } = { mockParamMap: {} }) {
- this.paramMapInternal = {
- keys: Object.keys(mockParamMap),
- get(name: string): string | null {
- const param = mockParamMap[name];
- if (typeof param === 'string') {
- return param;
- } else if (param instanceof Array) {
- if (param.length === 0) {
- return null;
- }
- return param[0];
- }
- return null;
- },
- getAll(name: string): string[] {
- const param = mockParamMap[name];
- if (typeof param === 'string') {
- return [param];
- } else if (param instanceof Array) {
- return param;
- }
- return [];
- },
- has(name: string): boolean {
- return mockParamMap.hasOwnProperty(name);
- }
- };
- }
-
- get paramMap(): ParamMap {
- return this.paramMapInternal;
- }
-}
-
-export class MockActivatedRoute implements PartialMock<ActivatedRoute> {
-
- snapshot$ = new BehaviorSubject<MockActivatedRouteSnapshot>(new MockActivatedRouteSnapshot());
-
- get paramMap(): Observable<ParamMap> {
- return this.snapshot$.pipe(map(snapshot => snapshot.paramMap));
- }
-
- get snapshot(): MockActivatedRouteSnapshot {
- return this.snapshot$.value;
- }
-
- pushSnapshot(snapshot: MockActivatedRouteSnapshot) {
- this.snapshot$.next(snapshot);
- }
-
- pushSnapshotWithParamMap(mockParamMap: ParamMapCreator) {
- this.pushSnapshot(new MockActivatedRouteSnapshot({mockParamMap}));
- }
-}
diff --git a/Timeline/ClientApp/src/app/test-utilities/mock.ts b/Timeline/ClientApp/src/app/test-utilities/mock.ts
deleted file mode 100644
index c3e368f0..00000000
--- a/Timeline/ClientApp/src/app/test-utilities/mock.ts
+++ /dev/null
@@ -1,7 +0,0 @@
-export type Mock<T> = {
- [P in keyof T]: T[P] extends Function ? T[P] : T[P] | Mock<T[P]>;
-};
-
-export type PartialMock<T> = {
- [P in keyof T]?: T[P] extends Function ? T[P] : T[P] | PartialMock<T[P]> | Mock<T[P]>;
-};
diff --git a/Timeline/ClientApp/src/app/test-utilities/router-link.mock.ts b/Timeline/ClientApp/src/app/test-utilities/router-link.mock.ts
deleted file mode 100644
index 7f4cde4d..00000000
--- a/Timeline/ClientApp/src/app/test-utilities/router-link.mock.ts
+++ /dev/null
@@ -1,9 +0,0 @@
-import { Directive, Input } from '@angular/core';
-
-@Directive({
- /* tslint:disable-next-line:directive-selector*/
- selector: '[routerLink]'
-})
-export class RouterLinkStubDirective {
- @Input('routerLink') linkParams: any;
-}
diff --git a/Timeline/ClientApp/src/app/test-utilities/storage.mock.ts b/Timeline/ClientApp/src/app/test-utilities/storage.mock.ts
deleted file mode 100644
index 0ba5aa35..00000000
--- a/Timeline/ClientApp/src/app/test-utilities/storage.mock.ts
+++ /dev/null
@@ -1,28 +0,0 @@
-import { Mock } from './mock';
-import { nullIfUndefined } from '../utilities/language-untilities';
-
-export function createMockStorage(): Mock<Storage> {
- const map: { [key: string]: string } = {};
- return {
- get length(): number {
- return Object.keys(map).length;
- },
- key(index: number): string | null {
- const keys = Object.keys(map);
- if (index >= keys.length) { return null; }
- return keys[index];
- },
- clear() {
- Object.keys(map).forEach(key => delete map.key);
- },
- getItem(key: string): string | null {
- return nullIfUndefined(map[key]);
- },
- setItem(key: string, value: string) {
- map[key] = value;
- },
- removeItem(key: string) {
- delete map[key];
- }
- };
-}
diff --git a/Timeline/ClientApp/src/app/todo/todo-item.ts b/Timeline/ClientApp/src/app/todo/todo-item.ts
deleted file mode 100644
index b19d8335..00000000
--- a/Timeline/ClientApp/src/app/todo/todo-item.ts
+++ /dev/null
@@ -1,6 +0,0 @@
-export interface TodoItem {
- number: number;
- title: string;
- isClosed: boolean;
- detailUrl: string;
-}
diff --git a/Timeline/ClientApp/src/app/todo/todo-item/todo-item.component.css b/Timeline/ClientApp/src/app/todo/todo-item/todo-item.component.css
deleted file mode 100644
index dcf25fd8..00000000
--- a/Timeline/ClientApp/src/app/todo/todo-item/todo-item.component.css
+++ /dev/null
@@ -1,25 +0,0 @@
-.item-card {
- padding: 0;
- display: flex;
- overflow: hidden;
-}
-
-.item-body-box {
- margin: 5px!important
-}
-
-.item-color-block {
- width: 15px;
- align-self: stretch;
- flex: 0 0 auto;
-}
-
-.item-title {
- vertical-align: middle;
-}
-
-.item-detail-button {
- width: unset;
- height: unset;
- line-height: unset;
-}
diff --git a/Timeline/ClientApp/src/app/todo/todo-item/todo-item.component.html b/Timeline/ClientApp/src/app/todo/todo-item/todo-item.component.html
deleted file mode 100644
index 6f76e73b..00000000
--- a/Timeline/ClientApp/src/app/todo/todo-item/todo-item.component.html
+++ /dev/null
@@ -1,9 +0,0 @@
-<mat-card class="mat-elevation-z2 item-card">
- <span class="item-color-block" [class.color-block-closed]="item.isClosed" [class.color-block-open]="!item.isClosed"></span>
- <div class="mat-h3 item-body-box">
- <span class="item-title">{{ item.number }}. {{ item.title }}</span>
- <a mat-icon-button class="item-detail-button" [href]="item.detailUrl">
- <mat-icon>arrow_forward</mat-icon>
- </a>
- </div>
-</mat-card>
diff --git a/Timeline/ClientApp/src/app/todo/todo-item/todo-item.component.spec.ts b/Timeline/ClientApp/src/app/todo/todo-item/todo-item.component.spec.ts
deleted file mode 100644
index 239ffc42..00000000
--- a/Timeline/ClientApp/src/app/todo/todo-item/todo-item.component.spec.ts
+++ /dev/null
@@ -1,46 +0,0 @@
-import { NO_ERRORS_SCHEMA } from '@angular/core';
-import { async, ComponentFixture, TestBed } from '@angular/core/testing';
-import { By } from '@angular/platform-browser';
-
-import { TodoItem } from '../todo-item';
-import { TodoItemComponent } from '../todo-item/todo-item.component';
-
-describe('TodoItemComponent', () => {
- let component: TodoItemComponent;
- let fixture: ComponentFixture<TodoItemComponent>;
-
- const mockTodoItem: TodoItem = {
- number: 1,
- title: 'Title',
- isClosed: true,
- detailUrl: '/detail',
- };
-
- beforeEach(async(() => {
- TestBed.configureTestingModule({
- declarations: [TodoItemComponent],
- schemas: [NO_ERRORS_SCHEMA]
- }).compileComponents();
- }));
-
- beforeEach(() => {
- fixture = TestBed.createComponent(TodoItemComponent);
- component = fixture.componentInstance;
- component.item = mockTodoItem;
- fixture.detectChanges();
- });
-
- it('should create', () => {
- expect(component).toBeTruthy();
- });
-
- it('should set title', () => {
- expect((fixture.debugElement.query(By.css('span.item-title')).nativeElement as HTMLSpanElement).textContent).toBe(
- mockTodoItem.number + '. ' + mockTodoItem.title
- );
- });
-
- it('should set detail link', () => {
- expect(fixture.debugElement.query(By.css('a.item-detail-button')).properties['href']).toBe(mockTodoItem.detailUrl);
- });
-});
diff --git a/Timeline/ClientApp/src/app/todo/todo-item/todo-item.component.ts b/Timeline/ClientApp/src/app/todo/todo-item/todo-item.component.ts
deleted file mode 100644
index b5c51229..00000000
--- a/Timeline/ClientApp/src/app/todo/todo-item/todo-item.component.ts
+++ /dev/null
@@ -1,12 +0,0 @@
-import { Component, Input } from '@angular/core';
-
-import { TodoItem } from '../todo-item';
-
-@Component({
- selector: 'app-todo-item',
- templateUrl: './todo-item.component.html',
- styleUrls: ['./todo-item.component.css', '../todo-list-color-block.css']
-})
-export class TodoItemComponent {
- @Input() item!: TodoItem;
-}
diff --git a/Timeline/ClientApp/src/app/todo/todo-list-color-block.css b/Timeline/ClientApp/src/app/todo/todo-list-color-block.css
deleted file mode 100644
index 5e0d4ba9..00000000
--- a/Timeline/ClientApp/src/app/todo/todo-list-color-block.css
+++ /dev/null
@@ -1,7 +0,0 @@
-.color-block-open {
- background: red;
-}
-
-.color-block-closed {
- background: green;
-}
diff --git a/Timeline/ClientApp/src/app/todo/todo-page/todo-page.component.css b/Timeline/ClientApp/src/app/todo/todo-page/todo-page.component.css
deleted file mode 100644
index 754b786e..00000000
--- a/Timeline/ClientApp/src/app/todo/todo-page/todo-page.component.css
+++ /dev/null
@@ -1,39 +0,0 @@
-.align-self-bottom {
- align-self: flex-end;
-}
-
-.item-box {
- display: flex;
- width: 100%;
- box-sizing: border-box;
-}
-
-.first-item-box {
- justify-content: space-between;
- padding: 0 0 5px 5px;
-}
-
-.non-first-item-box {
- padding: 5px;
-}
-
-.space {
- flex: 1 4 20px;
-}
-
-.sample-box {
- box-sizing: border-box;
- align-self: flex-start;
-}
-
-.sample-item {
- display: flex;
- align-items: center;
-}
-
-.sample-color-block {
- border-radius: 0.2em;
- width: 1em;
- height: 1em;
- margin-right: 2px;
-}
diff --git a/Timeline/ClientApp/src/app/todo/todo-page/todo-page.component.html b/Timeline/ClientApp/src/app/todo/todo-page/todo-page.component.html
deleted file mode 100644
index 50180fe8..00000000
--- a/Timeline/ClientApp/src/app/todo/todo-page/todo-page.component.html
+++ /dev/null
@@ -1,21 +0,0 @@
-<mat-progress-bar *ngIf="!isLoadCompleted" mode="indeterminate"></mat-progress-bar>
-
-<mat-list>
- <mat-list-item *ngFor="let item of items; let i = index" style="height:unset;">
- <div class="item-box" [class.first-item-box]="i === 0" [class.non-first-item-box]="i !== 0">
- <app-todo-item @itemEnter [class.align-self-bottom]="i === 0" [item]="item"></app-todo-item>
- <div class="space"></div>
- <div class="sample-box" *ngIf="i === 0">
- <div class="mat-caption sample-item">
- <span class="sample-color-block color-block-open"></span>
- <span> means working now.</span>
- </div>
- <div class="mat-caption sample-item">
- <span class="sample-color-block color-block-closed"></span>
- <span> means completed.</span>
- </div>
- <div class="mat-caption">click on item to see details.</div>
- </div>
- </div>
- </mat-list-item>
-</mat-list>
diff --git a/Timeline/ClientApp/src/app/todo/todo-page/todo-page.component.spec.ts b/Timeline/ClientApp/src/app/todo/todo-page/todo-page.component.spec.ts
deleted file mode 100644
index 16c77376..00000000
--- a/Timeline/ClientApp/src/app/todo/todo-page/todo-page.component.spec.ts
+++ /dev/null
@@ -1,78 +0,0 @@
-import { Component, NO_ERRORS_SCHEMA } from '@angular/core';
-import { async, ComponentFixture, TestBed, fakeAsync, tick } from '@angular/core/testing';
-import { By } from '@angular/platform-browser';
-import { NoopAnimationsModule } from '@angular/platform-browser/animations';
-
-import { Observable, from } from 'rxjs';
-import { delay } from 'rxjs/operators';
-
-import { TodoItem } from '../todo-item';
-import { TodoPageComponent } from './todo-page.component';
-import { TodoService } from '../todo-service/todo.service';
-
-
-@Component({
- /* tslint:disable-next-line:component-selector*/
- selector: 'mat-progress-bar',
- template: ''
-})
-class MatProgressBarStubComponent { }
-
-function asyncArray<T>(data: T[]): Observable<T> {
- return from(data).pipe(delay(0));
-}
-
-describe('TodoListPageComponent', () => {
- let component: TodoPageComponent;
- let fixture: ComponentFixture<TodoPageComponent>;
-
- const mockTodoItems: TodoItem[] = [
- {
- number: 0,
- title: 'Test title 1',
- isClosed: true,
- detailUrl: 'test_url1'
- },
- {
- number: 1,
- title: 'Test title 2',
- isClosed: false,
- detailUrl: 'test_url2'
- }
- ];
-
- beforeEach(async(() => {
- const mockTodoService: jasmine.SpyObj<TodoService> = jasmine.createSpyObj('TodoService', ['getWorkItemList']);
-
- mockTodoService.getWorkItemList.and.returnValue(asyncArray(mockTodoItems));
-
- TestBed.configureTestingModule({
- declarations: [TodoPageComponent, MatProgressBarStubComponent],
- imports: [NoopAnimationsModule],
- providers: [{ provide: TodoService, useValue: mockTodoService }],
- schemas: [NO_ERRORS_SCHEMA]
- }).compileComponents();
- }));
-
- beforeEach(() => {
- fixture = TestBed.createComponent(TodoPageComponent);
- component = fixture.componentInstance;
- });
-
- it('should create', () => {
- fixture.detectChanges();
- expect(component).toBeTruthy();
- });
-
- it('should show progress bar during loading', () => {
- fixture.detectChanges();
- expect(fixture.debugElement.query(By.css('mat-progress-bar'))).toBeTruthy();
- });
-
- it('should hide progress bar after loading', fakeAsync(() => {
- fixture.detectChanges();
- tick();
- fixture.detectChanges();
- expect(fixture.debugElement.query(By.css('mat-progress-bar'))).toBeFalsy();
- }));
-});
diff --git a/Timeline/ClientApp/src/app/todo/todo-page/todo-page.component.ts b/Timeline/ClientApp/src/app/todo/todo-page/todo-page.component.ts
deleted file mode 100644
index 7b658228..00000000
--- a/Timeline/ClientApp/src/app/todo/todo-page/todo-page.component.ts
+++ /dev/null
@@ -1,39 +0,0 @@
-import { Component, OnInit } from '@angular/core';
-import { trigger, transition, style, animate } from '@angular/animations';
-
-
-import { TodoItem } from '../todo-item';
-import { TodoService } from '../todo-service/todo.service';
-
-@Component({
- selector: 'app-todo-page',
- templateUrl: './todo-page.component.html',
- styleUrls: ['./todo-page.component.css', '../todo-list-color-block.css'],
- animations: [
- trigger('itemEnter', [
- transition(':enter', [
- style({
- transform: 'translateX(-100%) translateX(-20px)'
- }),
- animate('400ms ease-out', style({
- transform: 'none'
- }))
- ])
- ])
- ]
-})
-export class TodoPageComponent implements OnInit {
-
- items: TodoItem[] = [];
- isLoadCompleted = false;
-
- constructor(private todoService: TodoService) {
- }
-
- ngOnInit() {
- this.todoService.getWorkItemList().subscribe({
- next: result => this.items.push(result),
- complete: () => { this.isLoadCompleted = true; }
- });
- }
-}
diff --git a/Timeline/ClientApp/src/app/todo/todo-service/http-entities.ts b/Timeline/ClientApp/src/app/todo/todo-service/http-entities.ts
deleted file mode 100644
index 3971617c..00000000
--- a/Timeline/ClientApp/src/app/todo/todo-service/http-entities.ts
+++ /dev/null
@@ -1,11 +0,0 @@
-export const githubBaseUrl = 'https://api.github.com/repos/crupest/Timeline';
-
-export interface IssueResponseItem {
- number: number;
- title: string;
- state: string;
- html_url: string;
- pull_request?: any;
-}
-
-export type IssueResponse = IssueResponseItem[];
diff --git a/Timeline/ClientApp/src/app/todo/todo-service/todo.service.spec.ts b/Timeline/ClientApp/src/app/todo/todo-service/todo.service.spec.ts
deleted file mode 100644
index 679dc8b7..00000000
--- a/Timeline/ClientApp/src/app/todo/todo-service/todo.service.spec.ts
+++ /dev/null
@@ -1,54 +0,0 @@
-import { TestBed } from '@angular/core/testing';
-import { HttpClientTestingModule, HttpTestingController } from '@angular/common/http/testing';
-import { toArray } from 'rxjs/operators';
-
-import { TodoItem } from '../todo-item';
-import { TodoService } from './todo.service';
-import { IssueResponse, githubBaseUrl } from './http-entities';
-
-
-describe('TodoService', () => {
- beforeEach(() => TestBed.configureTestingModule({
- imports: [HttpClientTestingModule]
- }));
-
- it('should be created', () => {
- const service: TodoService = TestBed.get(TodoService);
- expect(service).toBeTruthy();
- });
-
- it('should work well', () => {
- const service: TodoService = TestBed.get(TodoService);
-
- const mockIssueList: IssueResponse = [{
- number: 1,
- title: 'Issue title 1',
- state: 'open',
- html_url: 'test_url1'
- }, {
- number: 2,
- title: 'Issue title 2',
- state: 'closed',
- html_url: 'test_url2',
- pull_request: {}
- }];
-
- const mockTodoItemList: TodoItem[] = [{
- number: 1,
- title: 'Issue title 1',
- isClosed: false,
- detailUrl: 'test_url1'
- }];
-
- service.getWorkItemList().pipe(toArray()).subscribe(data => {
- expect(data).toEqual(mockTodoItemList);
- });
-
- const httpController: HttpTestingController = TestBed.get(HttpTestingController);
-
- httpController.expectOne(request => request.url === githubBaseUrl + '/issues' &&
- request.params.get('state') === 'all').flush(mockIssueList);
-
- httpController.verify();
- });
-});
diff --git a/Timeline/ClientApp/src/app/todo/todo-service/todo.service.ts b/Timeline/ClientApp/src/app/todo/todo-service/todo.service.ts
deleted file mode 100644
index df63636d..00000000
--- a/Timeline/ClientApp/src/app/todo/todo-service/todo.service.ts
+++ /dev/null
@@ -1,33 +0,0 @@
-import { Injectable } from '@angular/core';
-import { HttpClient } from '@angular/common/http';
-import { Observable, from } from 'rxjs';
-import { switchMap, map, filter } from 'rxjs/operators';
-
-import { IssueResponse, githubBaseUrl } from './http-entities';
-import { TodoItem } from '../todo-item';
-
-
-@Injectable({
- providedIn: 'root'
-})
-export class TodoService {
-
- constructor(private client: HttpClient) { }
-
- getWorkItemList(): Observable<TodoItem> {
- return this.client.get<IssueResponse>(`${githubBaseUrl}/issues`, {
- params: {
- state: 'all'
- }
- }).pipe(
- switchMap(result => from(result)),
- filter(result => result.pull_request === undefined), // filter out pull requests.
- map(result => <TodoItem>{
- number: result.number,
- title: result.title,
- isClosed: result.state === 'closed',
- detailUrl: result.html_url
- })
- );
- }
-}
diff --git a/Timeline/ClientApp/src/app/todo/todo.module.ts b/Timeline/ClientApp/src/app/todo/todo.module.ts
deleted file mode 100644
index 5bcfefbd..00000000
--- a/Timeline/ClientApp/src/app/todo/todo.module.ts
+++ /dev/null
@@ -1,27 +0,0 @@
-import { NgModule } from '@angular/core';
-import { CommonModule } from '@angular/common';
-import { RouterModule } from '@angular/router';
-import { MatListModule, MatIconModule, MatCardModule, MatProgressBarModule, MatButtonModule } from '@angular/material';
-import { HttpClientModule } from '@angular/common/http';
-import { BrowserAnimationsModule } from '@angular/platform-browser/animations';
-
-import { TodoItemComponent } from './todo-item/todo-item.component';
-import { TodoPageComponent } from './todo-page/todo-page.component';
-
-@NgModule({
- declarations: [
- TodoItemComponent,
- TodoPageComponent
- ],
- imports: [
- CommonModule, HttpClientModule, BrowserAnimationsModule,
- MatListModule, MatCardModule, MatIconModule, MatProgressBarModule, MatButtonModule,
- RouterModule.forChild([
- { path: 'todo', component: TodoPageComponent }
- ])
- ],
- exports: [
- RouterModule
- ]
-})
-export class TodoModule { }
diff --git a/Timeline/ClientApp/src/app/user/auth.guard.spec.ts b/Timeline/ClientApp/src/app/user/auth.guard.spec.ts
deleted file mode 100644
index 6a36fea6..00000000
--- a/Timeline/ClientApp/src/app/user/auth.guard.spec.ts
+++ /dev/null
@@ -1,69 +0,0 @@
-import { Observable, of } from 'rxjs';
-
-import { AuthGuard, AuthStrategy } from './auth.guard';
-import { UserInfo } from './entities';
-
-describe('AuthGuard', () => {
- class ConfiurableAuthGuard extends AuthGuard {
- constructor(mockInternalUserService: any) {
- super(mockInternalUserService);
- }
-
- authStrategy: AuthStrategy = 'all';
- }
-
- let mockUserService: { userInfo$: Observable<UserInfo | null> };
- let guard: ConfiurableAuthGuard;
- let onAuthFialedSpy: jasmine.Spy;
-
- const mockRoles = ['role1', 'role2'];
-
- interface ActivateResultMap {
- nologin: boolean;
- loginWithNoRole: boolean;
- loginWithMockRoles: boolean;
- }
-
-
- function createTest(authStrategy: AuthStrategy, result: ActivateResultMap): () => void {
- return () => {
- guard.authStrategy = authStrategy;
-
- function testWith(userInfo: UserInfo | null, r: boolean) {
- mockUserService.userInfo$ = of(userInfo);
-
- const rawResult = guard.canActivate(<any>null, <any>null);
- if (typeof rawResult === 'boolean') {
- expect(rawResult).toBe(r);
- } else if (rawResult instanceof Observable) {
- rawResult.subscribe(next => expect(next).toBe(r));
- } else {
- throw new Error('Unsupported return type.');
- }
- }
-
- testWith(null, result.nologin);
- testWith({ username: 'user', roles: [] }, result.loginWithNoRole);
- testWith({ username: 'user', roles: mockRoles }, result.loginWithMockRoles);
- };
- }
-
- beforeEach(() => {
- mockUserService = { userInfo$: of(null) };
- guard = new ConfiurableAuthGuard(mockUserService);
- onAuthFialedSpy = spyOn(guard, 'onAuthFailed');
- });
-
-
- it('all should work', createTest('all', { nologin: true, loginWithNoRole: true, loginWithMockRoles: true }));
- it('require login should work', createTest('requirelogin', { nologin: false, loginWithNoRole: true, loginWithMockRoles: true }));
- it('require no login should work', createTest('requirenologin', { nologin: true, loginWithNoRole: false, loginWithMockRoles: false }));
- it('good roles should work', createTest(mockRoles, { nologin: false, loginWithNoRole: false, loginWithMockRoles: true }));
- it('bad roles should work', createTest(['role3'], { nologin: false, loginWithNoRole: false, loginWithMockRoles: false }));
-
- it('auth failed callback should be called', () => {
- guard.authStrategy = 'requirelogin';
- (<Observable<boolean>>guard.canActivate(<any>null, <any>null)).subscribe();
- expect(onAuthFialedSpy).toHaveBeenCalled();
- });
-});
diff --git a/Timeline/ClientApp/src/app/user/auth.guard.ts b/Timeline/ClientApp/src/app/user/auth.guard.ts
deleted file mode 100644
index 1fc7a7c0..00000000
--- a/Timeline/ClientApp/src/app/user/auth.guard.ts
+++ /dev/null
@@ -1,80 +0,0 @@
-import { Injectable } from '@angular/core';
-import { CanActivate, ActivatedRouteSnapshot, RouterStateSnapshot, UrlTree } from '@angular/router';
-import { Observable } from 'rxjs';
-import { take, map } from 'rxjs/operators';
-
-import { InternalUserService } from './internal-user-service/internal-user.service';
-
-export type AuthStrategy = 'all' | 'requirelogin' | 'requirenologin' | string[];
-
-export abstract class AuthGuard implements CanActivate {
-
- constructor(protected internalUserService: InternalUserService) { }
-
- onAuthFailed() { }
-
- abstract get authStrategy(): AuthStrategy;
-
- canActivate(next: ActivatedRouteSnapshot, state: RouterStateSnapshot):
- Observable<boolean | UrlTree> | Promise<boolean | UrlTree> | boolean | UrlTree {
-
- const { authStrategy } = this;
-
- if (authStrategy === 'all') {
- return true;
- }
-
- return this.internalUserService.userInfo$.pipe(take(1), map(userInfo => {
- if (userInfo === null) {
- if (authStrategy === 'requirenologin') {
- return true;
- }
- } else {
- if (authStrategy === 'requirelogin') {
- return true;
- } else if (authStrategy instanceof Array) {
- const { roles } = userInfo;
- if (authStrategy.every(value => roles.includes(value))) {
- return true;
- }
- }
- }
-
- // reach here means auth fails
- this.onAuthFailed();
- return false;
- }));
- }
-}
-
-@Injectable({
- providedIn: 'root'
-})
-export class RequireLoginGuard extends AuthGuard {
- readonly authStrategy: AuthStrategy = 'requirelogin';
-
- // never remove this constructor or you will get an injection error.
- constructor(internalUserService: InternalUserService) {
- super(internalUserService);
- }
-
- onAuthFailed() {
- this.internalUserService.userRouteNavigate(['login']);
- }
-}
-
-@Injectable({
- providedIn: 'root'
-})
-export class RequireNoLoginGuard extends AuthGuard {
- readonly authStrategy: AuthStrategy = 'requirenologin';
-
- // never remove this constructor or you will get an injection error.
- constructor(internalUserService: InternalUserService) {
- super(internalUserService);
- }
-
- onAuthFailed() {
- this.internalUserService.userRouteNavigate(['success']);
- }
-}
diff --git a/Timeline/ClientApp/src/app/user/entities.ts b/Timeline/ClientApp/src/app/user/entities.ts
deleted file mode 100644
index 6d432ec6..00000000
--- a/Timeline/ClientApp/src/app/user/entities.ts
+++ /dev/null
@@ -1,9 +0,0 @@
-export interface UserCredentials {
- username: string;
- password: string;
-}
-
-export interface UserInfo {
- username: string;
- roles: string[];
-}
diff --git a/Timeline/ClientApp/src/app/user/internal-user-service/errors.ts b/Timeline/ClientApp/src/app/user/internal-user-service/errors.ts
deleted file mode 100644
index 3358a9d9..00000000
--- a/Timeline/ClientApp/src/app/user/internal-user-service/errors.ts
+++ /dev/null
@@ -1,29 +0,0 @@
-export class BadNetworkError extends Error {
- constructor() {
- super('Network is bad.');
- }
-}
-
-export class AlreadyLoginError extends Error {
- constructor() {
- super('Internal logical error. There is already a token saved. Please call validateUserLoginState first.');
- }
-}
-
-export class BadCredentialsError extends Error {
- constructor() {
- super('Username or password is wrong.');
- }
-}
-
-export class UnknownError extends Error {
- constructor(public internalError?: any) {
- super('Sorry, unknown error occured!');
- }
-}
-
-export class ServerInternalError extends Error {
- constructor(message?: string) {
- super('Wrong server response. ' + message);
- }
-}
diff --git a/Timeline/ClientApp/src/app/user/internal-user-service/http-entities.ts b/Timeline/ClientApp/src/app/user/internal-user-service/http-entities.ts
deleted file mode 100644
index f52233c9..00000000
--- a/Timeline/ClientApp/src/app/user/internal-user-service/http-entities.ts
+++ /dev/null
@@ -1,21 +0,0 @@
-import { UserCredentials, UserInfo } from '../entities';
-
-export const createTokenUrl = '/api/User/CreateToken';
-export const validateTokenUrl = '/api/User/ValidateToken';
-
-export type CreateTokenRequest = UserCredentials;
-
-export interface CreateTokenResponse {
- success: boolean;
- token?: string;
- userInfo?: UserInfo;
-}
-
-export interface ValidateTokenRequest {
- token: string;
-}
-
-export interface ValidateTokenResponse {
- isValid: boolean;
- userInfo?: UserInfo;
-}
diff --git a/Timeline/ClientApp/src/app/user/internal-user-service/internal-user.service.mock.ts b/Timeline/ClientApp/src/app/user/internal-user-service/internal-user.service.mock.ts
deleted file mode 100644
index f4a85262..00000000
--- a/Timeline/ClientApp/src/app/user/internal-user-service/internal-user.service.mock.ts
+++ /dev/null
@@ -1,5 +0,0 @@
-import { InternalUserService } from './internal-user.service';
-
-export function createMockInternalUserService(): jasmine.SpyObj<InternalUserService> {
- return jasmine.createSpyObj('InternalUserService', ['userRouteNavigate', 'refreshAndGetUserState', 'tryLogin']);
-}
diff --git a/Timeline/ClientApp/src/app/user/internal-user-service/internal-user.service.spec.ts b/Timeline/ClientApp/src/app/user/internal-user-service/internal-user.service.spec.ts
deleted file mode 100644
index 15755382..00000000
--- a/Timeline/ClientApp/src/app/user/internal-user-service/internal-user.service.spec.ts
+++ /dev/null
@@ -1,123 +0,0 @@
-import { TestBed, fakeAsync, tick } from '@angular/core/testing';
-import { HttpRequest } from '@angular/common/http';
-import { HttpClientTestingModule, HttpTestingController } from '@angular/common/http/testing';
-import { Router } from '@angular/router';
-import { MatSnackBar } from '@angular/material';
-
-import { Mock } from 'src/app/test-utilities/mock';
-import { createMockStorage } from 'src/app/test-utilities/storage.mock';
-import { WINDOW } from '../window-inject-token';
-
-import { UserInfo, UserCredentials } from '../entities';
-import {
- createTokenUrl, validateTokenUrl, CreateTokenRequest,
- CreateTokenResponse, ValidateTokenRequest, ValidateTokenResponse
-} from './http-entities';
-import { InternalUserService, SnackBarTextKey, snackBarText, TOKEN_STORAGE_KEY } from './internal-user.service';
-import { repeat } from 'src/app/utilities/language-untilities';
-
-
-describe('InternalUserService', () => {
- let mockLocalStorage: Mock<Storage>;
- let mockSnackBar: jasmine.SpyObj<MatSnackBar>;
-
- beforeEach(() => {
- mockLocalStorage = createMockStorage();
- mockSnackBar = jasmine.createSpyObj('MatSnackBar', ['open']);
- TestBed.configureTestingModule({
- imports: [HttpClientTestingModule],
- providers: [
- { provide: WINDOW, useValue: { localStorage: mockLocalStorage } },
- { provide: Router, useValue: null },
- { provide: MatSnackBar, useValue: mockSnackBar }
- ]
- });
- });
-
- it('should be created', () => {
- const service: InternalUserService = TestBed.get(InternalUserService);
- expect(service).toBeTruthy();
- });
-
- const mockUserInfo: UserInfo = {
- username: 'user',
- roles: ['user', 'other']
- };
-
- const mockToken = 'mock-token';
-
- describe('validate token', () => {
- const validateTokenRequestMatcher = (req: HttpRequest<ValidateTokenRequest>): boolean =>
- req.url === validateTokenUrl && req.body !== null && req.body.token === mockToken;
-
- function createTest(
- expectSnackBarTextKey: SnackBarTextKey,
- setStorageToken: boolean,
- setHttpController?: (controller: HttpTestingController) => void
- ): () => void {
- return fakeAsync(() => {
- if (setStorageToken) {
- mockLocalStorage.setItem(TOKEN_STORAGE_KEY, mockToken);
- }
- TestBed.get(InternalUserService);
- const controller = TestBed.get(HttpTestingController) as HttpTestingController;
- if (setHttpController) {
- setHttpController(controller);
- }
- controller.verify();
- tick();
- expect(mockSnackBar.open).toHaveBeenCalledWith(snackBarText[expectSnackBarTextKey], jasmine.anything(), jasmine.anything());
- });
- }
-
- it('no login should work well', createTest('noLogin', false));
- it('already login should work well', createTest('alreadyLogin', true,
- controller => controller.expectOne(validateTokenRequestMatcher).flush(
- <ValidateTokenResponse>{ isValid: true, userInfo: mockUserInfo })));
- it('invalid login should work well', createTest('invalidLogin', true,
- controller => controller.expectOne(validateTokenRequestMatcher).flush(<ValidateTokenResponse>{ isValid: false })));
- it('check fail should work well', createTest('checkFail', true,
- controller => repeat(4, () => {
- controller.expectOne(validateTokenRequestMatcher).error(new ErrorEvent('Network error', { message: 'simulated network error' }));
- })));
- });
-
- describe('login should work well', () => {
- const mockUserCredentials: UserCredentials = {
- username: 'user',
- password: 'user'
- };
-
- function createTest(rememberMe: boolean) {
- return () => {
- const service: InternalUserService = TestBed.get(InternalUserService);
-
- service.tryLogin({ ...mockUserCredentials, rememberMe: rememberMe }).subscribe(result => {
- expect(result).toEqual(mockUserInfo);
- });
-
- const httpController = TestBed.get(HttpTestingController) as HttpTestingController;
-
- httpController.expectOne((request: HttpRequest<CreateTokenRequest>) =>
- request.url === createTokenUrl && request.body !== null &&
- request.body.username === mockUserCredentials.username &&
- request.body.password === mockUserCredentials.password).flush(<CreateTokenResponse>{
- success: true,
- token: mockToken,
- userInfo: mockUserInfo
- });
-
- expect(service.currentUserInfo).toEqual(mockUserInfo);
-
- httpController.verify();
-
- expect(mockLocalStorage.getItem(TOKEN_STORAGE_KEY)).toBe(rememberMe ? mockToken : null);
- };
- }
-
- it('remember me should work well', createTest(true));
- it('not remember me should work well', createTest(false));
- });
-
- // TODO: test on error situations.
-});
diff --git a/Timeline/ClientApp/src/app/user/internal-user-service/internal-user.service.ts b/Timeline/ClientApp/src/app/user/internal-user-service/internal-user.service.ts
deleted file mode 100644
index 66eafde9..00000000
--- a/Timeline/ClientApp/src/app/user/internal-user-service/internal-user.service.ts
+++ /dev/null
@@ -1,155 +0,0 @@
-import { Injectable, Inject } from '@angular/core';
-import { HttpClient, HttpErrorResponse } from '@angular/common/http';
-import { Router } from '@angular/router';
-
-import { Observable, throwError, BehaviorSubject, of } from 'rxjs';
-import { map, catchError, retry, switchMap, tap, filter } from 'rxjs/operators';
-
-import { AlreadyLoginError, BadCredentialsError, BadNetworkError, UnknownError, ServerInternalError } from './errors';
-import {
- createTokenUrl, validateTokenUrl, CreateTokenRequest,
- CreateTokenResponse, ValidateTokenRequest, ValidateTokenResponse
-} from './http-entities';
-import { UserCredentials, UserInfo } from '../entities';
-import { MatSnackBar } from '@angular/material';
-import { WINDOW } from '../window-inject-token';
-
-export const snackBarText = {
- checkFail: 'Failed to check last login',
- noLogin: 'No login before!',
- alreadyLogin: 'You have login already!',
- invalidLogin: 'Last login is no longer invalid!',
- ok: 'ok'
-};
-
-export type SnackBarTextKey = Exclude<keyof typeof snackBarText, 'ok'>;
-
-export const TOKEN_STORAGE_KEY = 'token';
-
-export interface LoginInfo extends UserCredentials {
- rememberMe: boolean;
-}
-
-/**
- * This service is only used internal in user module.
- */
-@Injectable({
- providedIn: 'root'
-})
-export class InternalUserService {
-
- private token: string | null = null;
- private userInfoSubject = new BehaviorSubject<UserInfo | null | undefined>(undefined);
-
- readonly userInfo$: Observable<UserInfo | null> =
- <Observable<UserInfo | null>>this.userInfoSubject.pipe(filter(value => value !== undefined));
-
- get currentUserInfo(): UserInfo | null | undefined {
- return this.userInfoSubject.value;
- }
-
- private openSnackBar(snackBar: MatSnackBar, textKey: SnackBarTextKey) {
- setTimeout(() => snackBar.open(snackBarText[textKey], snackBarText.ok, { duration: 2000 }), 0);
- }
-
- constructor(@Inject(WINDOW) private window: Window, private httpClient: HttpClient, private router: Router, snackBar: MatSnackBar) {
- const savedToken = this.window.localStorage.getItem(TOKEN_STORAGE_KEY);
- if (savedToken === null) {
- this.openSnackBar(snackBar, 'noLogin');
- this.userInfoSubject.next(null);
- } else {
- this.validateToken(savedToken).subscribe(result => {
- if (result === null) {
- this.window.localStorage.removeItem(TOKEN_STORAGE_KEY);
- this.openSnackBar(snackBar, 'invalidLogin');
- this.userInfoSubject.next(null);
- } else {
- this.token = savedToken;
- this.userInfoSubject.next(result);
- this.openSnackBar(snackBar, 'alreadyLogin');
- }
- }, _ => {
- this.openSnackBar(snackBar, 'checkFail');
- this.userInfoSubject.next(null);
- });
- }
- }
-
- private validateToken(token: string): Observable<UserInfo | null> {
- return this.httpClient.post<ValidateTokenResponse>(validateTokenUrl, <ValidateTokenRequest>{ token: token }).pipe(
- retry(3),
- switchMap(result => {
- if (result.isValid) {
- const { userInfo } = result;
- if (userInfo) {
- return of(userInfo);
- } else {
- return throwError(new ServerInternalError('IsValid is true but UserInfo is null.'));
- }
- } else {
- return of(null);
- }
- }),
- tap({
- error: error => {
- console.error('Failed to validate token.');
- console.error(error);
- }
- }),
- );
- }
-
- userRouteNavigate(commands: any[] | null) {
- this.router.navigate([{
- outlets: {
- user: commands
- }
- }]);
- }
-
- tryLogin(info: LoginInfo): Observable<UserInfo> {
- if (this.token) {
- return throwError(new AlreadyLoginError());
- }
-
- return this.httpClient.post<CreateTokenResponse>(createTokenUrl, <CreateTokenRequest>info).pipe(
- catchError((error: HttpErrorResponse) => {
- if (error.error instanceof ErrorEvent) {
- console.error('An error occurred when login: ' + error.error.message);
- return throwError(new BadNetworkError());
- } else {
- console.error('An unknown error occurred when login: ' + error);
- return throwError(new UnknownError(error));
- }
- }),
- switchMap(result => {
- if (result.success) {
- if (result.token && result.userInfo) {
- this.token = result.token;
- if (info.rememberMe) {
- this.window.localStorage.setItem(TOKEN_STORAGE_KEY, result.token);
- }
- this.userInfoSubject.next(result.userInfo);
- return of(result.userInfo);
- } else {
- console.error('An error occurred when login: server return wrong data.');
- return throwError(new ServerInternalError('Token or userInfo is null.'));
- }
- } else {
- console.error('An error occurred when login: wrong credentials.');
- return throwError(new BadCredentialsError());
- }
- })
- );
- }
-
- logout() {
- if (this.currentUserInfo === null) {
- throw new Error('No login now. You can\'t logout.');
- }
-
- this.window.localStorage.removeItem(TOKEN_STORAGE_KEY);
- this.token = null;
- this.userInfoSubject.next(null);
- }
-}
diff --git a/Timeline/ClientApp/src/app/user/redirect.component.ts b/Timeline/ClientApp/src/app/user/redirect.component.ts
deleted file mode 100644
index 438b38b9..00000000
--- a/Timeline/ClientApp/src/app/user/redirect.component.ts
+++ /dev/null
@@ -1,15 +0,0 @@
-import { Component, OnInit } from '@angular/core';
-import { InternalUserService } from './internal-user-service/internal-user.service';
-
-@Component({
- selector: 'app-redirect',
- template: ''
-})
-export class RedirectComponent implements OnInit {
-
- constructor(private userService: InternalUserService) { }
-
- ngOnInit() {
- this.userService.userRouteNavigate(['login']);
- }
-}
diff --git a/Timeline/ClientApp/src/app/user/user-dialog/user-dialog.component.css b/Timeline/ClientApp/src/app/user/user-dialog/user-dialog.component.css
deleted file mode 100644
index e69de29b..00000000
--- a/Timeline/ClientApp/src/app/user/user-dialog/user-dialog.component.css
+++ /dev/null
diff --git a/Timeline/ClientApp/src/app/user/user-dialog/user-dialog.component.html b/Timeline/ClientApp/src/app/user/user-dialog/user-dialog.component.html
deleted file mode 100644
index e8dbb003..00000000
--- a/Timeline/ClientApp/src/app/user/user-dialog/user-dialog.component.html
+++ /dev/null
@@ -1 +0,0 @@
-<router-outlet name="user"></router-outlet>
diff --git a/Timeline/ClientApp/src/app/user/user-dialog/user-dialog.component.spec.ts b/Timeline/ClientApp/src/app/user/user-dialog/user-dialog.component.spec.ts
deleted file mode 100644
index 47860eee..00000000
--- a/Timeline/ClientApp/src/app/user/user-dialog/user-dialog.component.spec.ts
+++ /dev/null
@@ -1,43 +0,0 @@
-import { Component } from '@angular/core';
-import { async, ComponentFixture, TestBed } from '@angular/core/testing';
-import { Router, Event } from '@angular/router';
-
-import { Observable } from 'rxjs';
-
-import { UserDialogComponent } from './user-dialog.component';
-
-@Component({
- /* tslint:disable-next-line:component-selector*/
- selector: 'router-outlet',
- template: ''
-})
-class RouterOutletStubComponent { }
-
-
-describe('UserDialogComponent', () => {
- let component: UserDialogComponent;
- let fixture: ComponentFixture<UserDialogComponent>;
-
-
- beforeEach(async(() => {
- TestBed.configureTestingModule({
- declarations: [UserDialogComponent, RouterOutletStubComponent],
- providers: [{ // for the workaround
- provide: Router, useValue: {
- events: new Observable<Event>()
- }
- }]
- })
- .compileComponents();
- }));
-
- beforeEach(() => {
- fixture = TestBed.createComponent(UserDialogComponent);
- component = fixture.componentInstance;
- fixture.detectChanges();
- });
-
- it('should create', () => {
- expect(component).toBeTruthy();
- });
-});
diff --git a/Timeline/ClientApp/src/app/user/user-dialog/user-dialog.component.ts b/Timeline/ClientApp/src/app/user/user-dialog/user-dialog.component.ts
deleted file mode 100644
index 2887f0a6..00000000
--- a/Timeline/ClientApp/src/app/user/user-dialog/user-dialog.component.ts
+++ /dev/null
@@ -1,24 +0,0 @@
-import { Component, OnInit, ViewChild } from '@angular/core';
-import { RouterOutlet, Router, ActivationStart } from '@angular/router';
-
-@Component({
- selector: 'app-user-dialog',
- templateUrl: './user-dialog.component.html',
- styleUrls: ['./user-dialog.component.css']
-})
-export class UserDialogComponent implements OnInit {
-
- constructor(private router: Router) { }
-
- @ViewChild(RouterOutlet) outlet!: RouterOutlet;
-
- ngOnInit() {
- // this is a workaround for a bug. see https://github.com/angular/angular/issues/20694
- const subscription = this.router.events.subscribe(e => {
- if (e instanceof ActivationStart && e.snapshot.outlet === 'user') {
- this.outlet.deactivate();
- subscription.unsubscribe();
- }
- });
- }
-}
diff --git a/Timeline/ClientApp/src/app/user/user-login-success/user-login-success.component.css b/Timeline/ClientApp/src/app/user/user-login-success/user-login-success.component.css
deleted file mode 100644
index b1101e2a..00000000
--- a/Timeline/ClientApp/src/app/user/user-login-success/user-login-success.component.css
+++ /dev/null
@@ -1,22 +0,0 @@
-.login-success-message {
- color: green;
-}
-
-.username {
- color: blue;
-}
-
-:host {
- display: flex;
- flex-wrap: wrap;
-}
-
-:host p {
- margin-top: 0.3em;
- margin-bottom: 0.3em;
- width: 100%;
-}
-
-.logout-button {
- margin-left: auto;
-}
diff --git a/Timeline/ClientApp/src/app/user/user-login-success/user-login-success.component.html b/Timeline/ClientApp/src/app/user/user-login-success/user-login-success.component.html
deleted file mode 100644
index 685f6299..00000000
--- a/Timeline/ClientApp/src/app/user/user-login-success/user-login-success.component.html
+++ /dev/null
@@ -1,6 +0,0 @@
-<p *ngIf="displayLoginSuccessMessage" class="mat-body login-success-message">
- Login succeeds!
-</p>
-<p class="mat-body">You have been login as <span class="username">{{ userInfo.username }}</span>.</p>
-<p class="mat-body">Your roles are <span class="roles">{{ userInfo.roles.join(', ') }}</span>.</p>
-<a mat-flat-button class="logout-button" [routerLink]="['..','logout']">Logout</a>
diff --git a/Timeline/ClientApp/src/app/user/user-login-success/user-login-success.component.spec.ts b/Timeline/ClientApp/src/app/user/user-login-success/user-login-success.component.spec.ts
deleted file mode 100644
index 3eba2696..00000000
--- a/Timeline/ClientApp/src/app/user/user-login-success/user-login-success.component.spec.ts
+++ /dev/null
@@ -1,75 +0,0 @@
-import { async, ComponentFixture, TestBed } from '@angular/core/testing';
-import { By } from '@angular/platform-browser';
-import { ActivatedRoute } from '@angular/router';
-
-import { RouterLinkStubDirective } from '../../test-utilities/router-link.mock';
-import { MockActivatedRoute } from '../../test-utilities/activated-route.mock';
-import { createMockInternalUserService } from '../internal-user-service/internal-user.service.mock';
-
-import { UserLoginSuccessComponent } from './user-login-success.component';
-import { InternalUserService } from '../internal-user-service/internal-user.service';
-
-
-describe('UserLoginSuccessComponent', () => {
- let component: UserLoginSuccessComponent;
- let fixture: ComponentFixture<UserLoginSuccessComponent>;
-
- let mockInternalUserService: jasmine.SpyObj<InternalUserService>;
- let mockActivatedRoute: MockActivatedRoute;
-
- const mockUserInfo = {
- username: 'crupest',
- roles: ['superman', 'coder']
- };
-
- beforeEach(async(() => {
- mockInternalUserService = createMockInternalUserService();
- mockActivatedRoute = new MockActivatedRoute();
-
- // mock currentUserInfo property. because it only has a getter so cast it to any first.
- (<any>mockInternalUserService).currentUserInfo = mockUserInfo;
-
- TestBed.configureTestingModule({
- declarations: [UserLoginSuccessComponent, RouterLinkStubDirective],
- providers: [
- { provide: InternalUserService, useValue: mockInternalUserService },
- { provide: ActivatedRoute, useValue: mockActivatedRoute }
- ]
- })
- .compileComponents();
- }));
-
- beforeEach(() => {
- fixture = TestBed.createComponent(UserLoginSuccessComponent);
- component = fixture.componentInstance;
- });
-
- it('should create', () => {
- fixture.detectChanges();
- expect(component).toBeTruthy();
- });
-
- it('user info should work well', () => {
- fixture.detectChanges();
-
- expect((fixture.debugElement.query(By.css('p.login-success-message')))).toBeFalsy();
-
- expect((fixture.debugElement.query(By.css('span.username')).nativeElement as HTMLSpanElement).textContent)
- .toBe(mockUserInfo.username);
- expect((fixture.debugElement.query(By.css('span.roles')).nativeElement as HTMLSpanElement).textContent)
- .toBe(mockUserInfo.roles.join(', '));
- });
-
- it('login success message should display well', () => {
- mockActivatedRoute.pushSnapshotWithParamMap({ fromlogin: 'true' });
- fixture.detectChanges();
- expect((fixture.debugElement.query(By.css('p.login-success-message')))).toBeTruthy();
- });
-
- it('logout button should be set well', () => {
- fixture.detectChanges();
- const routerLinkDirective: RouterLinkStubDirective =
- fixture.debugElement.query(By.css('a')).injector.get(RouterLinkStubDirective);
- expect(routerLinkDirective.linkParams).toEqual(['..', 'logout']);
- });
-});
diff --git a/Timeline/ClientApp/src/app/user/user-login-success/user-login-success.component.ts b/Timeline/ClientApp/src/app/user/user-login-success/user-login-success.component.ts
deleted file mode 100644
index 2ae584d6..00000000
--- a/Timeline/ClientApp/src/app/user/user-login-success/user-login-success.component.ts
+++ /dev/null
@@ -1,25 +0,0 @@
-import { Component, OnInit } from '@angular/core';
-import { ActivatedRoute } from '@angular/router';
-
-import { UserInfo } from '../entities';
-import { InternalUserService } from '../internal-user-service/internal-user.service';
-import { throwIfNullOrUndefined } from 'src/app/utilities/language-untilities';
-
-@Component({
- selector: 'app-user-login-success',
- templateUrl: './user-login-success.component.html',
- styleUrls: ['./user-login-success.component.css']
-})
-export class UserLoginSuccessComponent implements OnInit {
-
- displayLoginSuccessMessage = false;
-
- userInfo!: UserInfo;
-
- constructor(private route: ActivatedRoute, private userService: InternalUserService) { }
-
- ngOnInit() {
- this.userInfo = throwIfNullOrUndefined(this.userService.currentUserInfo, () => 'Route error. No login now!');
- this.displayLoginSuccessMessage = this.route.snapshot.paramMap.get('fromlogin') === 'true';
- }
-}
diff --git a/Timeline/ClientApp/src/app/user/user-login/user-login.component.css b/Timeline/ClientApp/src/app/user/user-login/user-login.component.css
deleted file mode 100644
index 8bf6b408..00000000
--- a/Timeline/ClientApp/src/app/user/user-login/user-login.component.css
+++ /dev/null
@@ -1,24 +0,0 @@
-form {
- display: flex;
- flex-wrap: wrap;
-}
-
-div.w-100 {
- width: 100%;
-}
-
-.login-button {
- margin-left: auto;
-}
-
-.no-login-message {
- color: blue;
-}
-
-.invalid-login-message {
- color: red;
-}
-
-.error-message {
- color: red;
-}
diff --git a/Timeline/ClientApp/src/app/user/user-login/user-login.component.html b/Timeline/ClientApp/src/app/user/user-login/user-login.component.html
deleted file mode 100644
index 7398ece7..00000000
--- a/Timeline/ClientApp/src/app/user/user-login/user-login.component.html
+++ /dev/null
@@ -1,19 +0,0 @@
-<form [formGroup]="form">
- <ng-container *ngIf="message" [ngSwitch]="message">
- <p *ngSwitchCase="'nologin'" class="mat-h3 no-login-message">You haven't login.</p>
- <p *ngSwitchCase="'invalidlogin'" class="mat-h3 invalid-login-message">Your login is no longer valid.</p>
- <p *ngSwitchDefault class="mat-h3 error-message">{{ message }}</p>
- </ng-container>
- <mat-form-field>
- <mat-label>Username</mat-label>
- <input formControlName="username" matInput type="text" />
- </mat-form-field>
- <div class="w-100"></div>
- <mat-form-field>
- <mat-label>Password</mat-label>
- <input formControlName="password" matInput type="password" />
- </mat-form-field>
- <mat-checkbox formControlName="rememberMe">Remember me!</mat-checkbox>
- <div class="w-100"></div>
- <button mat-flat-button class="login-button" (appDebounceClick)="onLoginButtonClick()">Login</button>
-</form>
diff --git a/Timeline/ClientApp/src/app/user/user-login/user-login.component.spec.ts b/Timeline/ClientApp/src/app/user/user-login/user-login.component.spec.ts
deleted file mode 100644
index f010e4b7..00000000
--- a/Timeline/ClientApp/src/app/user/user-login/user-login.component.spec.ts
+++ /dev/null
@@ -1,123 +0,0 @@
-import { NO_ERRORS_SCHEMA } from '@angular/core';
-import { async, ComponentFixture, TestBed } from '@angular/core/testing';
-import { ReactiveFormsModule } from '@angular/forms';
-import { By } from '@angular/platform-browser';
-
-import { of, throwError } from 'rxjs';
-
-import { createMockInternalUserService } from '../internal-user-service/internal-user.service.mock';
-import { UserLoginComponent } from './user-login.component';
-import { InternalUserService } from '../internal-user-service/internal-user.service';
-import { UserInfo } from '../entities';
-import { MatCheckboxModule } from '@angular/material';
-
-describe('UserLoginComponent', () => {
- let component: UserLoginComponent;
- let fixture: ComponentFixture<UserLoginComponent>;
- let mockInternalUserService: jasmine.SpyObj<InternalUserService>;
-
- beforeEach(async(() => {
- mockInternalUserService = createMockInternalUserService();
-
- // mock property
- (<any>mockInternalUserService).currentUserInfo = null;
-
- TestBed.configureTestingModule({
- declarations: [UserLoginComponent],
- providers: [
- { provide: InternalUserService, useValue: mockInternalUserService }
- ],
- imports: [ReactiveFormsModule, MatCheckboxModule],
- schemas: [NO_ERRORS_SCHEMA]
- })
- .compileComponents();
- }));
-
- beforeEach(() => {
- fixture = TestBed.createComponent(UserLoginComponent);
- component = fixture.componentInstance;
- });
-
- it('should create', () => {
- fixture.detectChanges();
- expect(component).toBeTruthy();
- });
-
- it('reactive form should work well', () => {
- fixture.detectChanges();
-
- const usernameInput = fixture.debugElement.query(By.css('input[type=text]')).nativeElement as HTMLInputElement;
- const passwordInput = fixture.debugElement.query(By.css('input[type=password]')).nativeElement as HTMLInputElement;
- const rememberMeCheckbox = fixture.debugElement.query(By.css('input[type=checkbox]')).nativeElement as HTMLInputElement;
-
- usernameInput.value = 'user';
- usernameInput.dispatchEvent(new Event('input'));
- passwordInput.value = 'user';
- passwordInput.dispatchEvent(new Event('input'));
- rememberMeCheckbox.dispatchEvent(new MouseEvent('click'));
-
- fixture.detectChanges();
-
- expect(component.form.value).toEqual({
- username: 'user',
- password: 'user',
- rememberMe: true
- });
- });
-
- it('login should work well', () => {
- fixture.detectChanges();
-
- const mockValue = {
- username: 'user',
- password: 'user',
- rememberMe: true
- };
-
- mockInternalUserService.tryLogin.withArgs(mockValue).and.returnValue(of(<UserInfo>{ username: 'user', roles: ['user'] }));
-
- component.form.setValue(mockValue);
- component.onLoginButtonClick();
-
- expect(mockInternalUserService.tryLogin).toHaveBeenCalledWith(mockValue);
- expect(mockInternalUserService.userRouteNavigate).toHaveBeenCalledWith(['success', { fromlogin: 'true' }]);
- });
-
- describe('message display', () => {
- it('nologin reason should display', () => {
- fixture.detectChanges();
- component.message = 'nologin';
- fixture.detectChanges();
- expect((fixture.debugElement.query(By.css('p')).nativeElement as
- HTMLParagraphElement).textContent).toBe('You haven\'t login.');
- });
-
- it('invalid login reason should display', () => {
- fixture.detectChanges();
- component.message = 'invalidlogin';
- fixture.detectChanges();
- expect((fixture.debugElement.query(By.css('p')).nativeElement as
- HTMLParagraphElement).textContent).toBe('Your login is no longer valid.');
- });
-
- it('custom error message should display', () => {
- const customMessage = 'custom message';
-
- fixture.detectChanges();
-
- const mockValue = {
- username: 'user',
- password: 'user',
- rememberMe: false
- };
- mockInternalUserService.tryLogin.withArgs(mockValue).and.returnValue(throwError(new Error(customMessage)));
- component.form.setValue(mockValue);
- component.onLoginButtonClick();
-
- fixture.detectChanges();
- expect(component.message).toBe(customMessage);
- expect((fixture.debugElement.query(By.css('p')).nativeElement as
- HTMLParagraphElement).textContent).toBe(customMessage);
- });
- });
-});
diff --git a/Timeline/ClientApp/src/app/user/user-login/user-login.component.ts b/Timeline/ClientApp/src/app/user/user-login/user-login.component.ts
deleted file mode 100644
index 4395c5cf..00000000
--- a/Timeline/ClientApp/src/app/user/user-login/user-login.component.ts
+++ /dev/null
@@ -1,38 +0,0 @@
-import { Component, OnInit } from '@angular/core';
-import { FormGroup, FormControl } from '@angular/forms';
-
-import { InternalUserService } from '../internal-user-service/internal-user.service';
-
-
-export type LoginMessage = 'nologin' | 'invalidlogin' | string | null | undefined;
-
-@Component({
- selector: 'app-user-login',
- templateUrl: './user-login.component.html',
- styleUrls: ['./user-login.component.css']
-})
-export class UserLoginComponent implements OnInit {
-
- constructor(private userService: InternalUserService) { }
-
- message: LoginMessage;
-
- form = new FormGroup({
- username: new FormControl(''),
- password: new FormControl(''),
- rememberMe: new FormControl(false)
- });
-
- ngOnInit() {
- if (this.userService.currentUserInfo) {
- throw new Error('Route error! Already login!');
- }
- this.message = 'nologin';
- }
-
- onLoginButtonClick() {
- this.userService.tryLogin(this.form.value).subscribe(_ => {
- this.userService.userRouteNavigate(['success', { fromlogin: 'true' }]);
- }, (error: Error) => this.message = error.message);
- }
-}
diff --git a/Timeline/ClientApp/src/app/user/user-logout/user-logout.component.css b/Timeline/ClientApp/src/app/user/user-logout/user-logout.component.css
deleted file mode 100644
index e69de29b..00000000
--- a/Timeline/ClientApp/src/app/user/user-logout/user-logout.component.css
+++ /dev/null
diff --git a/Timeline/ClientApp/src/app/user/user-logout/user-logout.component.html b/Timeline/ClientApp/src/app/user/user-logout/user-logout.component.html
deleted file mode 100644
index 309e5c83..00000000
--- a/Timeline/ClientApp/src/app/user/user-logout/user-logout.component.html
+++ /dev/null
@@ -1 +0,0 @@
-<p class="mat-body">Logout successfully!</p>
diff --git a/Timeline/ClientApp/src/app/user/user-logout/user-logout.component.spec.ts b/Timeline/ClientApp/src/app/user/user-logout/user-logout.component.spec.ts
deleted file mode 100644
index 855ea4a1..00000000
--- a/Timeline/ClientApp/src/app/user/user-logout/user-logout.component.spec.ts
+++ /dev/null
@@ -1,35 +0,0 @@
-import { async, ComponentFixture, TestBed } from '@angular/core/testing';
-
-import { UserLogoutComponent } from './user-logout.component';
-import { InternalUserService } from '../internal-user-service/internal-user.service';
-
-describe('UserLogoutComponent', () => {
- let component: UserLogoutComponent;
- let fixture: ComponentFixture<UserLogoutComponent>;
-
- let mockInternalUserService: jasmine.SpyObj<InternalUserService>;
-
- beforeEach(async(() => {
- mockInternalUserService = jasmine.createSpyObj('InternalUserService', ['logout']);
-
- TestBed.configureTestingModule({
- declarations: [UserLogoutComponent],
- providers: [{ provide: InternalUserService, useValue: mockInternalUserService }]
- })
- .compileComponents();
- }));
-
- beforeEach(() => {
- fixture = TestBed.createComponent(UserLogoutComponent);
- component = fixture.componentInstance;
- });
-
- it('should create', () => {
- expect(component).toBeTruthy();
- });
-
- it('should logout on init', () => {
- fixture.detectChanges();
- expect(mockInternalUserService.logout).toHaveBeenCalled();
- });
-});
diff --git a/Timeline/ClientApp/src/app/user/user-logout/user-logout.component.ts b/Timeline/ClientApp/src/app/user/user-logout/user-logout.component.ts
deleted file mode 100644
index e004196f..00000000
--- a/Timeline/ClientApp/src/app/user/user-logout/user-logout.component.ts
+++ /dev/null
@@ -1,16 +0,0 @@
-import { Component, OnInit } from '@angular/core';
-
-import { InternalUserService } from '../internal-user-service/internal-user.service';
-
-@Component({
- selector: 'app-user-logout',
- templateUrl: './user-logout.component.html',
- styleUrls: ['./user-logout.component.css']
-})
-export class UserLogoutComponent implements OnInit {
- constructor(private userService: InternalUserService) { }
-
- ngOnInit() {
- this.userService.logout();
- }
-}
diff --git a/Timeline/ClientApp/src/app/user/user.module.ts b/Timeline/ClientApp/src/app/user/user.module.ts
deleted file mode 100644
index 59193380..00000000
--- a/Timeline/ClientApp/src/app/user/user.module.ts
+++ /dev/null
@@ -1,38 +0,0 @@
-import { NgModule } from '@angular/core';
-import { CommonModule } from '@angular/common';
-import { ReactiveFormsModule } from '@angular/forms';
-import { HttpClientModule } from '@angular/common/http';
-import { BrowserAnimationsModule } from '@angular/platform-browser/animations';
-import { RouterModule } from '@angular/router';
-import {
- MatFormFieldModule, MatProgressSpinnerModule,
- MatDialogModule, MatInputModule, MatButtonModule, MatSnackBarModule, MatCheckboxModule
-} from '@angular/material';
-
-import { RequireNoLoginGuard, RequireLoginGuard } from './auth.guard';
-import { UserDialogComponent } from './user-dialog/user-dialog.component';
-import { UserLoginComponent } from './user-login/user-login.component';
-import { UserLoginSuccessComponent } from './user-login-success/user-login-success.component';
-import { RedirectComponent } from './redirect.component';
-import { UtilityModule } from '../utilities/utility.module';
-import { WINDOW } from './window-inject-token';
-import { UserLogoutComponent } from './user-logout/user-logout.component';
-
-@NgModule({
- declarations: [UserDialogComponent, UserLoginComponent, UserLoginSuccessComponent, RedirectComponent, UserLogoutComponent],
- imports: [
- RouterModule.forChild([
- { path: 'login', canActivate: [RequireNoLoginGuard], component: UserLoginComponent, outlet: 'user' },
- { path: 'success', canActivate: [RequireLoginGuard], component: UserLoginSuccessComponent, outlet: 'user' },
- { path: 'logout', canActivate: [RequireLoginGuard], component: UserLogoutComponent, outlet: 'user' },
- { path: '**', component: RedirectComponent, outlet: 'user' }
- ]),
- CommonModule, HttpClientModule, ReactiveFormsModule, BrowserAnimationsModule,
- MatFormFieldModule, MatProgressSpinnerModule, MatDialogModule, MatInputModule, MatButtonModule, MatCheckboxModule, MatSnackBarModule,
- UtilityModule
- ],
- providers: [{ provide: WINDOW, useValue: window }],
- exports: [RouterModule],
- entryComponents: [UserDialogComponent]
-})
-export class UserModule { }
diff --git a/Timeline/ClientApp/src/app/user/user.service.ts b/Timeline/ClientApp/src/app/user/user.service.ts
deleted file mode 100644
index 6cae2d31..00000000
--- a/Timeline/ClientApp/src/app/user/user.service.ts
+++ /dev/null
@@ -1,55 +0,0 @@
-import { Injectable } from '@angular/core';
-import { MatDialog, MatDialogRef } from '@angular/material';
-import { Router, ActivationStart } from '@angular/router';
-
-import { Observable } from 'rxjs';
-
-import { UserInfo } from './entities';
-import { InternalUserService } from './internal-user-service/internal-user.service';
-import { UserDialogComponent } from './user-dialog/user-dialog.component';
-
-
-/**
- * This service provides public api of user module.
- */
-@Injectable({
- providedIn: 'root'
-})
-export class UserService {
-
- private dialogRef: MatDialogRef<UserDialogComponent> | null = null;
-
- constructor(router: Router, private dialog: MatDialog, private internalService: InternalUserService) {
- router.events.subscribe(event => {
- if (event instanceof ActivationStart && event.snapshot.outlet === 'user') {
- if (!this.dialogRef) {
- setTimeout(() => this.openUserDialog(), 0);
- }
- }
- });
- }
-
- get currentUserInfo(): UserInfo | null | undefined {
- return this.internalService.currentUserInfo;
- }
-
- get userInfo$(): Observable<UserInfo | null> {
- return this.internalService.userInfo$;
- }
-
- private openUserDialog() {
- if (this.dialogRef) {
- return;
- }
-
- this.dialogRef = this.dialog.open(UserDialogComponent, {
- width: '300px'
- });
-
- const subscription = this.dialogRef.afterClosed().subscribe(_ => {
- this.internalService.userRouteNavigate(null);
- this.dialogRef = null;
- subscription.unsubscribe();
- });
- }
-}
diff --git a/Timeline/ClientApp/src/app/user/window-inject-token.ts b/Timeline/ClientApp/src/app/user/window-inject-token.ts
deleted file mode 100644
index 9f8723f6..00000000
--- a/Timeline/ClientApp/src/app/user/window-inject-token.ts
+++ /dev/null
@@ -1,3 +0,0 @@
-import { InjectionToken } from '@angular/core';
-
-export const WINDOW = new InjectionToken<Window>('global window');
diff --git a/Timeline/ClientApp/src/app/utilities/debounce-click.directive.spec.ts b/Timeline/ClientApp/src/app/utilities/debounce-click.directive.spec.ts
deleted file mode 100644
index 89f66b99..00000000
--- a/Timeline/ClientApp/src/app/utilities/debounce-click.directive.spec.ts
+++ /dev/null
@@ -1,123 +0,0 @@
-import { Component, ViewChild } from '@angular/core';
-import { async, TestBed, ComponentFixture, fakeAsync, tick } from '@angular/core/testing';
-import { By } from '@angular/platform-browser';
-
-import { DebounceClickDirective } from './debounce-click.directive';
-
-interface TestComponent {
- clickHandler: () => void;
-}
-
-@Component({
- selector: 'app-default-test',
- template: '<button (appDebounceClick)="clickHandler()"></button>'
-})
-class DefaultDebounceTimeTestComponent {
-
- @ViewChild(DebounceClickDirective) directive!: DebounceClickDirective;
-
- clickHandler: () => void = () => { };
-}
-
-@Component({
- selector: 'app-default-test',
- template: '<button (appDebounceClick)="clickHandler()" [appDebounceClickTime]="debounceTime"></button>'
-})
-class CustomDebounceTimeTestComponent {
- debounceTime: number | undefined;
-
- @ViewChild(DebounceClickDirective) directive!: DebounceClickDirective;
-
- clickHandler: () => void = () => { };
-}
-
-
-describe('DebounceClickDirective', () => {
- let counter: number;
-
- function initComponent(component: TestComponent) {
- component.clickHandler = () => counter++;
- }
-
- beforeEach(() => {
- counter = 0;
- });
-
- describe('default debounce time', () => {
- let component: DefaultDebounceTimeTestComponent;
- let componentFixture: ComponentFixture<DefaultDebounceTimeTestComponent>;
-
- beforeEach(async(() => {
- TestBed.configureTestingModule({
- declarations: [DebounceClickDirective, DefaultDebounceTimeTestComponent]
- }).compileComponents();
- }));
-
- beforeEach(() => {
- componentFixture = TestBed.createComponent(DefaultDebounceTimeTestComponent);
- component = componentFixture.componentInstance;
- initComponent(component);
- });
-
- it('should create an instance', () => {
- componentFixture.detectChanges();
- expect(component.directive).toBeTruthy();
- });
-
- it('should work well', fakeAsync(() => {
- function click() {
- (<HTMLButtonElement>componentFixture.debugElement.query(By.css('button')).nativeElement).dispatchEvent(new MouseEvent('click'));
- }
- componentFixture.detectChanges();
- expect(counter).toBe(0);
- click();
- tick(300);
- expect(counter).toBe(0);
- click();
- tick();
- expect(counter).toBe(0);
- tick(500);
- expect(counter).toBe(1);
- }));
- });
-
-
- describe('custom debounce time', () => {
- let component: CustomDebounceTimeTestComponent;
- let componentFixture: ComponentFixture<CustomDebounceTimeTestComponent>;
-
- beforeEach(async(() => {
- TestBed.configureTestingModule({
- declarations: [DebounceClickDirective, CustomDebounceTimeTestComponent]
- }).compileComponents();
- }));
-
- beforeEach(() => {
- componentFixture = TestBed.createComponent(CustomDebounceTimeTestComponent);
- component = componentFixture.componentInstance;
- initComponent(component);
- component.debounceTime = 600;
- });
-
- it('should create an instance', () => {
- componentFixture.detectChanges();
- expect(component.directive).toBeTruthy();
- });
-
- it('should work well', fakeAsync(() => {
- function click() {
- (<HTMLButtonElement>componentFixture.debugElement.query(By.css('button')).nativeElement).dispatchEvent(new MouseEvent('click'));
- }
- componentFixture.detectChanges();
- expect(counter).toBe(0);
- click();
- tick(300);
- expect(counter).toBe(0);
- click();
- tick();
- expect(counter).toBe(0);
- tick(600);
- expect(counter).toBe(1);
- }));
- });
-});
diff --git a/Timeline/ClientApp/src/app/utilities/debounce-click.directive.ts b/Timeline/ClientApp/src/app/utilities/debounce-click.directive.ts
deleted file mode 100644
index 1d01b671..00000000
--- a/Timeline/ClientApp/src/app/utilities/debounce-click.directive.ts
+++ /dev/null
@@ -1,41 +0,0 @@
-import { Directive, Output, Input, EventEmitter, ElementRef, OnInit, OnDestroy } from '@angular/core';
-import { fromEvent, Subscription } from 'rxjs';
-import { debounceTime } from 'rxjs/operators';
-
-@Directive({
- selector: '[appDebounceClick]'
-})
-export class DebounceClickDirective implements OnInit, OnDestroy {
-
- private subscription: Subscription | undefined;
-
- @Output('appDebounceClick') clickEvent = new EventEmitter<any>();
-
- // tslint:disable-next-line:no-input-rename
- @Input('appDebounceClickTime')
- set debounceTime(value: number) {
- if (this.subscription) {
- this.subscription.unsubscribe();
- }
- this.subscription = fromEvent(<HTMLElement>this.element.nativeElement, 'click').pipe(
- debounceTime(value)
- ).subscribe(o => this.clickEvent.emit(o));
- }
-
- constructor(private element: ElementRef) {
- }
-
- ngOnInit() {
- if (!this.subscription) {
- this.subscription = fromEvent(<HTMLElement>this.element.nativeElement, 'click').pipe(
- debounceTime(500)
- ).subscribe(o => this.clickEvent.emit(o));
- }
- }
-
- ngOnDestroy() {
- if (this.subscription) {
- this.subscription.unsubscribe();
- }
- }
-}
diff --git a/Timeline/ClientApp/src/app/utilities/language-untilities.ts b/Timeline/ClientApp/src/app/utilities/language-untilities.ts
deleted file mode 100644
index 94434665..00000000
--- a/Timeline/ClientApp/src/app/utilities/language-untilities.ts
+++ /dev/null
@@ -1,18 +0,0 @@
-export function nullIfUndefined<T>(value: T | undefined): T | null {
- return value === undefined ? null : value;
-}
-
-export function throwIfNullOrUndefined<T>(value: T | null | undefined,
- message: string | (() => string) = 'Value mustn\'t be null or undefined'): T | never {
- if (value === null || value === undefined) {
- throw new Error(typeof message === 'string' ? message : message());
- } else {
- return value;
- }
-}
-
-export function repeat(time: number, action: (index?: number) => void) {
- for (let i = 0; i < time; i++) {
- action(i);
- }
-}
diff --git a/Timeline/ClientApp/src/app/utilities/utility.module.ts b/Timeline/ClientApp/src/app/utilities/utility.module.ts
deleted file mode 100644
index dd686bf7..00000000
--- a/Timeline/ClientApp/src/app/utilities/utility.module.ts
+++ /dev/null
@@ -1,11 +0,0 @@
-import { NgModule } from '@angular/core';
-import { CommonModule } from '@angular/common';
-
-import { DebounceClickDirective } from './debounce-click.directive';
-
-@NgModule({
- declarations: [DebounceClickDirective],
- imports: [CommonModule],
- exports: [DebounceClickDirective]
-})
-export class UtilityModule { }
diff --git a/Timeline/ClientApp/src/assets/.gitkeep b/Timeline/ClientApp/src/assets/.gitkeep
deleted file mode 100644
index e69de29b..00000000
--- a/Timeline/ClientApp/src/assets/.gitkeep
+++ /dev/null
diff --git a/Timeline/ClientApp/src/assets/icon.svg b/Timeline/ClientApp/src/assets/icon.svg
deleted file mode 100644
index a04bddac..00000000
--- a/Timeline/ClientApp/src/assets/icon.svg
+++ /dev/null
@@ -1,7 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
-<svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0 0 1000 1000">
-<line fill="none" stroke="#ffffff" stroke-width="200" x1="500" y1="0" x2="500" y2="250"/>
-<circle fill="none" stroke="#ffffff" stroke-width="120" cx="500" cy="500" r="250"/>
-<line fill="none" stroke="#ffffff" stroke-width="200" x1="500" y1="750" x2="500" y2="1000"/>
-</svg>
diff --git a/Timeline/ClientApp/src/browserslist b/Timeline/ClientApp/src/browserslist
deleted file mode 100644
index 8e09ab49..00000000
--- a/Timeline/ClientApp/src/browserslist
+++ /dev/null
@@ -1,9 +0,0 @@
-# This file is currently used by autoprefixer to adjust CSS to support the below specified browsers
-# For additional information regarding the format and rule options, please see:
-# https://github.com/browserslist/browserslist#queries
-# For IE 9-11 support, please uncomment the last line of the file and adjust as needed
-> 0.5%
-last 2 versions
-Firefox ESR
-not dead
-# IE 9-11 \ No newline at end of file
diff --git a/Timeline/ClientApp/src/environments/environment.prod.ts b/Timeline/ClientApp/src/environments/environment.prod.ts
deleted file mode 100644
index 3612073b..00000000
--- a/Timeline/ClientApp/src/environments/environment.prod.ts
+++ /dev/null
@@ -1,3 +0,0 @@
-export const environment = {
- production: true
-};
diff --git a/Timeline/ClientApp/src/environments/environment.ts b/Timeline/ClientApp/src/environments/environment.ts
deleted file mode 100644
index 012182ef..00000000
--- a/Timeline/ClientApp/src/environments/environment.ts
+++ /dev/null
@@ -1,15 +0,0 @@
-// This file can be replaced during build by using the `fileReplacements` array.
-// `ng build ---prod` replaces `environment.ts` with `environment.prod.ts`.
-// The list of file replacements can be found in `angular.json`.
-
-export const environment = {
- production: false
-};
-
-/*
- * In development mode, to ignore zone related error stack frames such as
- * `zone.run`, `zoneDelegate.invokeTask` for easier debugging, you can
- * import the following file, but please comment it out in production mode
- * because it will have performance impact when throw error
- */
-// import 'zone.js/dist/zone-error'; // Included with Angular CLI.
diff --git a/Timeline/ClientApp/src/index.html b/Timeline/ClientApp/src/index.html
deleted file mode 100644
index 58959f75..00000000
--- a/Timeline/ClientApp/src/index.html
+++ /dev/null
@@ -1,17 +0,0 @@
-<!doctype html>
-<html lang="en">
-<head>
- <meta charset="utf-8">
- <title>Timeline</title>
- <base href="/">
-
- <meta name="viewport" content="width=device-width, initial-scale=1">
- <link rel="icon" type="image/x-icon" href="favicon.ico">
-
- <link href="https://fonts.googleapis.com/icon?family=Material+Icons" rel="stylesheet">
- <link href="https://fonts.googleapis.com/css?family=Roboto:300,400,500" rel="stylesheet">
-</head>
-<body>
- <app-root>Loading...</app-root>
-</body>
-</html>
diff --git a/Timeline/ClientApp/src/karma.conf.js b/Timeline/ClientApp/src/karma.conf.js
deleted file mode 100644
index 775e624c..00000000
--- a/Timeline/ClientApp/src/karma.conf.js
+++ /dev/null
@@ -1,32 +0,0 @@
-// Karma configuration file, see link for more information
-// https://karma-runner.github.io/1.0/config/configuration-file.html
-
-module.exports = function (config) {
- config.set({
- basePath: '',
- frameworks: ['jasmine', '@angular-devkit/build-angular'],
- plugins: [
- require('karma-jasmine'),
- require('karma-chrome-launcher'),
- require('karma-jasmine-html-reporter'),
- require('karma-coverage-istanbul-reporter'),
- require('karma-junit-reporter'),
- require('@angular-devkit/build-angular/plugins/karma')
- ],
- client: {
- clearContext: false // leave Jasmine Spec Runner output visible in browser
- },
- coverageIstanbulReporter: {
- dir: require('path').join(__dirname, '../coverage'),
- reports: ['html', 'lcovonly'],
- fixWebpackSourcePaths: true
- },
- reporters: ['progress', 'kjhtml'],
- port: 9876,
- colors: true,
- logLevel: config.LOG_INFO,
- autoWatch: true,
- browsers: ['Chrome'],
- singleRun: false
- });
-};
diff --git a/Timeline/ClientApp/src/main.ts b/Timeline/ClientApp/src/main.ts
deleted file mode 100644
index a2f708cb..00000000
--- a/Timeline/ClientApp/src/main.ts
+++ /dev/null
@@ -1,20 +0,0 @@
-import { enableProdMode } from '@angular/core';
-import { platformBrowserDynamic } from '@angular/platform-browser-dynamic';
-
-import { AppModule } from './app/app.module';
-import { environment } from './environments/environment';
-
-export function getBaseUrl() {
- return document.getElementsByTagName('base')[0].href;
-}
-
-const providers = [
- { provide: 'BASE_URL', useFactory: getBaseUrl, deps: [] }
-];
-
-if (environment.production) {
- enableProdMode();
-}
-
-platformBrowserDynamic(providers).bootstrapModule(AppModule)
- .catch(err => console.log(err));
diff --git a/Timeline/ClientApp/src/polyfills.ts b/Timeline/ClientApp/src/polyfills.ts
deleted file mode 100644
index d310405a..00000000
--- a/Timeline/ClientApp/src/polyfills.ts
+++ /dev/null
@@ -1,80 +0,0 @@
-/**
- * This file includes polyfills needed by Angular and is loaded before the app.
- * You can add your own extra polyfills to this file.
- *
- * This file is divided into 2 sections:
- * 1. Browser polyfills. These are applied before loading ZoneJS and are sorted by browsers.
- * 2. Application imports. Files imported after ZoneJS that should be loaded before your main
- * file.
- *
- * The current setup is for so-called "evergreen" browsers; the last versions of browsers that
- * automatically update themselves. This includes Safari >= 10, Chrome >= 55 (including Opera),
- * Edge >= 13 on the desktop, and iOS 10 and Chrome on mobile.
- *
- * Learn more in https://angular.io/docs/ts/latest/guide/browser-support.html
- */
-
-/***************************************************************************************************
- * BROWSER POLYFILLS
- */
-
-/** IE9, IE10 and IE11 requires all of the following polyfills. **/
-// import 'core-js/es6/symbol';
-// import 'core-js/es6/object';
-// import 'core-js/es6/function';
-// import 'core-js/es6/parse-int';
-// import 'core-js/es6/parse-float';
-// import 'core-js/es6/number';
-// import 'core-js/es6/math';
-// import 'core-js/es6/string';
-// import 'core-js/es6/date';
-// import 'core-js/es6/array';
-// import 'core-js/es6/regexp';
-// import 'core-js/es6/map';
-// import 'core-js/es6/weak-map';
-// import 'core-js/es6/set';
-
-/** IE10 and IE11 requires the following for NgClass support on SVG elements */
-// import 'classlist.js'; // Run `npm install --save classlist.js`.
-
-/** IE10 and IE11 requires the following for the Reflect API. */
-// import 'core-js/es6/reflect';
-
-
-/** Evergreen browsers require these. **/
-// Used for reflect-metadata in JIT. If you use AOT (and only Angular decorators), you can remove.
-import 'core-js/es7/reflect';
-
-
-/**
- * Web Animations `@angular/platform-browser/animations`
- * Only required if AnimationBuilder is used within the application and using IE/Edge or Safari.
- * Standard animation support in Angular DOES NOT require any polyfills (as of Angular 6.0).
- **/
-// import 'web-animations-js'; // Run `npm install --save web-animations-js`.
-
-/**
- * By default, zone.js will patch all possible macroTask and DomEvents
- * user can disable parts of macroTask/DomEvents patch by setting following flags
- */
-
- // (window as any).__Zone_disable_requestAnimationFrame = true; // disable patch requestAnimationFrame
- // (window as any).__Zone_disable_on_property = true; // disable patch onProperty such as onclick
- // (window as any).__zone_symbol__BLACK_LISTED_EVENTS = ['scroll', 'mousemove']; // disable patch specified eventNames
-
- /*
- * in IE/Edge developer tools, the addEventListener will also be wrapped by zone.js
- * with the following flag, it will bypass `zone.js` patch for IE/Edge
- */
-// (window as any).__Zone_enable_cross_context_check = true;
-
-/***************************************************************************************************
- * Zone JS is required by default for Angular itself.
- */
-import 'zone.js/dist/zone'; // Included with Angular CLI.
-
-
-
-/***************************************************************************************************
- * APPLICATION IMPORTS
- */
diff --git a/Timeline/ClientApp/src/styles.css b/Timeline/ClientApp/src/styles.css
deleted file mode 100644
index f60c9204..00000000
--- a/Timeline/ClientApp/src/styles.css
+++ /dev/null
@@ -1,14 +0,0 @@
-/* You can add global styles to this file, and also import other style files */
-@import "~@angular/material/prebuilt-themes/indigo-pink.css";
-
-html {
- overflow: unset!important; /* why cdk-global-scrollblock add overflow-y: scroll ??????????? */
-}
-
-body {
- margin: 0;
-}
-
-.fill-remaining-space {
- flex: 1 1 auto;
-}
diff --git a/Timeline/ClientApp/src/test.ts b/Timeline/ClientApp/src/test.ts
deleted file mode 100644
index 688add40..00000000
--- a/Timeline/ClientApp/src/test.ts
+++ /dev/null
@@ -1,22 +0,0 @@
-// This file is required by karma.conf.js and loads recursively all the .spec and framework files
-
-import 'zone.js/dist/zone-testing';
-import 'zone.js/dist/zone-patch-rxjs-fake-async';
-
-import { getTestBed } from '@angular/core/testing';
-import {
- BrowserDynamicTestingModule,
- platformBrowserDynamicTesting
-} from '@angular/platform-browser-dynamic/testing';
-
-declare const require: any;
-
-// First, initialize the Angular testing environment.
-getTestBed().initTestEnvironment(
- BrowserDynamicTestingModule,
- platformBrowserDynamicTesting()
-);
-// Then we find all the tests.
-const context = require.context('./', true, /\.spec\.ts$/);
-// And load the modules.
-context.keys().map(context);
diff --git a/Timeline/ClientApp/src/tsconfig.app.json b/Timeline/ClientApp/src/tsconfig.app.json
deleted file mode 100644
index 0d3b876e..00000000
--- a/Timeline/ClientApp/src/tsconfig.app.json
+++ /dev/null
@@ -1,15 +0,0 @@
-{
- "extends": "../tsconfig.json",
- "compilerOptions": {
- "outDir": "../out-tsc/app",
- "module": "es2015",
- "types": []
- },
- "exclude": [
- "src/test.ts",
- "test-utilities/**/*",
- "**/*.spec.ts",
- "**/*.mock.ts",
- "**/*.test.ts"
- ]
-}
diff --git a/Timeline/ClientApp/src/tsconfig.server.json b/Timeline/ClientApp/src/tsconfig.server.json
deleted file mode 100644
index 8019d415..00000000
--- a/Timeline/ClientApp/src/tsconfig.server.json
+++ /dev/null
@@ -1,9 +0,0 @@
-{
- "extends": "../tsconfig.json",
- "compilerOptions": {
- "module": "commonjs"
- },
- "angularCompilerOptions": {
- "entryModule": "app/app.server.module#AppServerModule"
- }
-} \ No newline at end of file
diff --git a/Timeline/ClientApp/src/tsconfig.spec.json b/Timeline/ClientApp/src/tsconfig.spec.json
deleted file mode 100644
index 3bcc8926..00000000
--- a/Timeline/ClientApp/src/tsconfig.spec.json
+++ /dev/null
@@ -1,22 +0,0 @@
-{
- "extends": "../tsconfig.json",
- "compilerOptions": {
- "outDir": "../out-tsc/spec",
- "module": "commonjs",
- "types": [
- "jasmine",
- "node"
- ]
- },
- "files": [
- "test.ts",
- "polyfills.ts"
- ],
- "include": [
- "test-utilities/**/*",
- "**/*.spec.ts",
- "**/*.d.ts",
- "**/*.mock.ts",
- "**/*.test.ts"
- ]
-}
diff --git a/Timeline/ClientApp/src/tslint.json b/Timeline/ClientApp/src/tslint.json
deleted file mode 100644
index 52e2c1a5..00000000
--- a/Timeline/ClientApp/src/tslint.json
+++ /dev/null
@@ -1,17 +0,0 @@
-{
- "extends": "../tslint.json",
- "rules": {
- "directive-selector": [
- true,
- "attribute",
- "app",
- "camelCase"
- ],
- "component-selector": [
- true,
- "element",
- "app",
- "kebab-case"
- ]
- }
-}
diff --git a/Timeline/ClientApp/tsconfig.json b/Timeline/ClientApp/tsconfig.json
deleted file mode 100644
index 86c42495..00000000
--- a/Timeline/ClientApp/tsconfig.json
+++ /dev/null
@@ -1,25 +0,0 @@
-{
- "compileOnSave": false,
- "compilerOptions": {
- "baseUrl": "./",
- "outDir": "./dist/out-tsc",
- "sourceMap": true,
- "declaration": false,
- "moduleResolution": "node",
- "emitDecoratorMetadata": true,
- "experimentalDecorators": true,
- "target": "es5",
- "typeRoots": [
- "node_modules/@types"
- ],
- "lib": [
- "es2017",
- "dom"
- ],
- "strict": true
- },
- "angularCompilerOptions": {
- "fullTemplateTypeCheck": true,
- "strictInjectionParameters": true
- }
-}
diff --git a/Timeline/ClientApp/tslint.json b/Timeline/ClientApp/tslint.json
deleted file mode 100644
index dcc5f765..00000000
--- a/Timeline/ClientApp/tslint.json
+++ /dev/null
@@ -1,130 +0,0 @@
-{
- "rulesDirectory": [
- "node_modules/codelyzer"
- ],
- "rules": {
- "arrow-return-shorthand": true,
- "callable-types": true,
- "class-name": true,
- "comment-format": [
- true,
- "check-space"
- ],
- "curly": true,
- "deprecation": {
- "severity": "warn"
- },
- "eofline": true,
- "forin": true,
- "import-blacklist": [
- true,
- "rxjs/Rx"
- ],
- "import-spacing": true,
- "indent": [
- true,
- "spaces"
- ],
- "interface-over-type-literal": true,
- "label-position": true,
- "max-line-length": [
- true,
- 140
- ],
- "member-access": false,
- "member-ordering": [
- true,
- {
- "order": [
- "static-field",
- "instance-field",
- "static-method",
- "instance-method"
- ]
- }
- ],
- "no-arg": true,
- "no-bitwise": true,
- "no-console": [
- true,
- "debug",
- "info",
- "time",
- "timeEnd",
- "trace"
- ],
- "no-construct": true,
- "no-debugger": true,
- "no-duplicate-super": true,
- "no-empty": false,
- "no-empty-interface": true,
- "no-eval": true,
- "no-inferrable-types": [
- true,
- "ignore-params"
- ],
- "no-misused-new": true,
- "no-non-null-assertion": false,
- "no-shadowed-variable": true,
- "no-string-literal": false,
- "no-string-throw": true,
- "no-switch-case-fall-through": true,
- "no-trailing-whitespace": true,
- "no-unnecessary-initializer": true,
- "no-unused-expression": true,
- "no-use-before-declare": true,
- "no-var-keyword": true,
- "object-literal-sort-keys": false,
- "one-line": [
- true,
- "check-open-brace",
- "check-catch",
- "check-else",
- "check-whitespace"
- ],
- "prefer-const": true,
- "quotemark": [
- true,
- "single"
- ],
- "radix": true,
- "semicolon": [
- true,
- "always"
- ],
- "triple-equals": [
- true,
- "allow-null-check"
- ],
- "typedef-whitespace": [
- true,
- {
- "call-signature": "nospace",
- "index-signature": "nospace",
- "parameter": "nospace",
- "property-declaration": "nospace",
- "variable-declaration": "nospace"
- }
- ],
- "unified-signatures": true,
- "variable-name": false,
- "whitespace": [
- true,
- "check-branch",
- "check-decl",
- "check-operator",
- "check-separator",
- "check-type"
- ],
- "no-output-on-prefix": true,
- "use-input-property-decorator": true,
- "use-output-property-decorator": true,
- "use-host-property-decorator": true,
- "no-input-rename": true,
- "no-output-rename": true,
- "use-life-cycle-interface": true,
- "use-pipe-transform-interface": true,
- "component-class-suffix": true,
- "directive-class-suffix": true
- }
-}
diff --git a/Timeline/Controllers/UserController.cs b/Timeline/Controllers/UserController.cs
index eb1b8513..b9e0979d 100644
--- a/Timeline/Controllers/UserController.cs
+++ b/Timeline/Controllers/UserController.cs
@@ -6,7 +6,7 @@ using Timeline.Services;
namespace Timeline.Controllers
{
- [Route("api/[controller]")]
+ [Route("[controller]")]
public class UserController : Controller
{
private static class LoggingEventIds
diff --git a/Timeline/Controllers/UserTestController.cs b/Timeline/Controllers/UserTestController.cs
index cf5cf074..1c230667 100644
--- a/Timeline/Controllers/UserTestController.cs
+++ b/Timeline/Controllers/UserTestController.cs
@@ -3,7 +3,7 @@ using Microsoft.AspNetCore.Mvc;
namespace Timeline.Controllers
{
- [Route("api/test/User")]
+ [Route("Test/User")]
public class UserTestController : Controller
{
[HttpGet("[action]")]
diff --git a/Timeline/Properties/launchSettings.json b/Timeline/Properties/launchSettings.json
index a07a7868..69eebf54 100644
--- a/Timeline/Properties/launchSettings.json
+++ b/Timeline/Properties/launchSettings.json
@@ -10,14 +10,14 @@
"profiles": {
"IIS Express": {
"commandName": "IISExpress",
- "launchBrowser": true,
+ "launchBrowser": false,
"environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Development"
}
},
"Timeline": {
"commandName": "Project",
- "launchBrowser": true,
+ "launchBrowser": false,
"environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Development"
}
diff --git a/Timeline/Startup.cs b/Timeline/Startup.cs
index 88348892..f7b2e64d 100644
--- a/Timeline/Startup.cs
+++ b/Timeline/Startup.cs
@@ -1,7 +1,6 @@
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Mvc;
-using Microsoft.AspNetCore.SpaServices.AngularCli;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.AspNetCore.Authentication.JwtBearer;
@@ -16,11 +15,15 @@ namespace Timeline
{
public class Startup
{
- public Startup(IConfiguration configuration)
+ private const string corsPolicyName = "MyPolicy";
+
+ public Startup(IConfiguration configuration, IHostingEnvironment environment)
{
+ Environment = environment;
Configuration = configuration;
}
+ public IHostingEnvironment Environment { get; }
public IConfiguration Configuration { get; }
// This method gets called by the runtime. Use this method to add services to the container.
@@ -31,14 +34,26 @@ namespace Timeline
options.InputFormatters.Add(new StringInputFormatter());
}).SetCompatibilityVersion(CompatibilityVersion.Version_2_2);
- // In production, the Angular files will be served from this directory
- services.AddSpaStaticFiles(configuration =>
+ services.AddCors(options =>
{
- configuration.RootPath = "ClientApp/dist";
+ if (Environment.IsProduction())
+ {
+ options.AddPolicy(corsPolicyName, builder =>
+ {
+ builder.WithOrigins("www.crupest.xyz", "crupest.xyz").AllowAnyMethod().AllowAnyHeader();
+ });
+ }
+ else
+ {
+ options.AddPolicy(corsPolicyName, builder =>
+ {
+ builder.AllowAnyOrigin().AllowAnyMethod().AllowAnyHeader();
+ });
+ }
});
- services.Configure<JwtConfig>(Configuration.GetSection("JwtConfig"));
- var jwtConfig = Configuration.GetSection("JwtConfig").Get<JwtConfig>();
+ services.Configure<JwtConfig>(Configuration.GetSection(nameof(JwtConfig)));
+ var jwtConfig = Configuration.GetSection(nameof(JwtConfig)).Get<JwtConfig>();
services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
.AddJwtBearer(o =>
@@ -57,9 +72,9 @@ namespace Timeline
}
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
- public void Configure(IApplicationBuilder app, IHostingEnvironment env)
+ public void Configure(IApplicationBuilder app)
{
- if (env.IsDevelopment())
+ if (Environment.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
@@ -68,14 +83,13 @@ namespace Timeline
app.UseExceptionHandler("/Error");
}
- app.UseStaticFiles();
- app.UseSpaStaticFiles();
-
app.UseForwardedHeaders(new ForwardedHeadersOptions
{
ForwardedHeaders = ForwardedHeaders.XForwardedFor | ForwardedHeaders.XForwardedProto
});
+ app.UseCors(corsPolicyName);
+
app.UseAuthentication();
app.UseMvc(routes =>
@@ -84,16 +98,6 @@ namespace Timeline
name: "default",
template: "{controller}/{action=Index}/{id?}");
});
-
- app.UseSpa(spa =>
- {
- spa.Options.SourcePath = "ClientApp";
-
- if (env.IsDevelopment())
- {
- spa.UseAngularCliServer(npmScript: "start");
- }
- });
}
}
}
diff --git a/Timeline/Timeline-CI.csproj b/Timeline/Timeline-CI.csproj
deleted file mode 100644
index 65bfacdf..00000000
--- a/Timeline/Timeline-CI.csproj
+++ /dev/null
@@ -1,35 +0,0 @@
-<Project Sdk="Microsoft.NET.Sdk.Web">
-
- <PropertyGroup>
- <TargetFramework>netcoreapp2.2</TargetFramework>
- <TypeScriptCompileBlocked>true</TypeScriptCompileBlocked>
- <TypeScriptToolsVersion>Latest</TypeScriptToolsVersion>
- <IsPackable>false</IsPackable>
- <SpaRoot>ClientApp\</SpaRoot>
- <DefaultItemExcludes>$(DefaultItemExcludes);$(SpaRoot)node_modules\**</DefaultItemExcludes>
- <Authors>crupest</Authors>
- <AssemblyName>Timeline</AssemblyName>
- </PropertyGroup>
-
- <ItemGroup>
- <PackageReference Include="Microsoft.AspNetCore.App" />
- </ItemGroup>
-
- <ItemGroup>
- <!-- Don't publish the SPA source files, but do show them in the project files list -->
- <Content Remove="$(SpaRoot)**" />
- <None Remove="$(SpaRoot)**" />
- <None Include="$(SpaRoot)**" Exclude="$(SpaRoot)node_modules\**" />
- </ItemGroup>
-
- <Target Name="PublishRunWebpack" AfterTargets="ComputeFilesToPublish">
- <ItemGroup>
- <DistFiles Include="$(SpaRoot)dist\**" />
- <ResolvedFileToPublish Include="@(DistFiles->'%(FullPath)')" Exclude="@(ResolvedFileToPublish)">
- <RelativePath>%(DistFiles.Identity)</RelativePath>
- <CopyToPublishDirectory>PreserveNewest</CopyToPublishDirectory>
- </ResolvedFileToPublish>
- </ItemGroup>
- </Target>
-
-</Project>
diff --git a/Timeline/Timeline.csproj b/Timeline/Timeline.csproj
index e55eb90d..93513bd3 100644
--- a/Timeline/Timeline.csproj
+++ b/Timeline/Timeline.csproj
@@ -1,57 +1,16 @@
<Project Sdk="Microsoft.NET.Sdk.Web">
+ <PropertyGroup>
+ <TargetFramework>netcoreapp2.2</TargetFramework>
+ <IsPackable>false</IsPackable>
+ <UserSecretsId>1f6fb74d-4277-4bc0-aeea-b1fc5ffb0b43</UserSecretsId>
+ <Authors>crupest</Authors>
+ </PropertyGroup>
- <PropertyGroup>
- <TargetFramework>netcoreapp2.2</TargetFramework>
- <TypeScriptCompileBlocked>true</TypeScriptCompileBlocked>
- <TypeScriptToolsVersion>Latest</TypeScriptToolsVersion>
- <IsPackable>false</IsPackable>
- <SpaRoot>ClientApp\</SpaRoot>
- <DefaultItemExcludes>$(DefaultItemExcludes);$(SpaRoot)node_modules\**</DefaultItemExcludes>
-
- <!-- Set this to true if you enable server-side prerendering -->
- <BuildServerSideRenderer>false</BuildServerSideRenderer>
- <UserSecretsId>1f6fb74d-4277-4bc0-aeea-b1fc5ffb0b43</UserSecretsId>
- <Authors>crupest</Authors>
- </PropertyGroup>
-
- <ItemGroup>
- <PackageReference Include="Microsoft.AspNetCore.App" />
- <PackageReference Include="Microsoft.AspNetCore.Razor.Design" Version="2.2.0" PrivateAssets="All" />
- <PackageReference Include="Microsoft.VisualStudio.Web.CodeGeneration.Design" Version="2.2.1" />
- </ItemGroup>
-
- <ItemGroup>
- <!-- Don't publish the SPA source files, but do show them in the project files list -->
- <Content Remove="$(SpaRoot)**" />
- <None Remove="$(SpaRoot)**" />
- <None Include="$(SpaRoot)**" Exclude="$(SpaRoot)node_modules\**" />
- </ItemGroup>
-
- <Target Name="DebugEnsureNodeEnv" BeforeTargets="Build" Condition=" '$(Configuration)' == 'Debug' And !Exists('$(SpaRoot)node_modules') ">
- <!-- Ensure Node.js is installed -->
- <Exec Command="node --version" ContinueOnError="true">
- <Output TaskParameter="ExitCode" PropertyName="ErrorCode" />
- </Exec>
- <Error Condition="'$(ErrorCode)' != '0'" Text="Node.js is required to build and run this project. To continue, please install Node.js from https://nodejs.org/, and then restart your command prompt or IDE." />
- <Message Importance="high" Text="Restoring dependencies using 'yarn'. This may take several minutes..." />
- <Exec WorkingDirectory="$(SpaRoot)" Command="yarn install" />
- </Target>
-
- <Target Name="PublishRunWebpack" AfterTargets="ComputeFilesToPublish">
- <!-- As part of publishing, ensure the JS resources are freshly built in production mode -->
- <Exec WorkingDirectory="$(SpaRoot)" Command="yarn install" />
- <Exec WorkingDirectory="$(SpaRoot)" Command="yarn run build --prod" />
- <Exec WorkingDirectory="$(SpaRoot)" Command="yarn run build:ssr --prod" Condition=" '$(BuildServerSideRenderer)' == 'true' " />
-
- <!-- Include the newly-built files in the publish output -->
<ItemGroup>
- <DistFiles Include="$(SpaRoot)dist\**; $(SpaRoot)dist-server\**" />
- <DistFiles Include="$(SpaRoot)node_modules\**" Condition="'$(BuildServerSideRenderer)' == 'true'" />
- <ResolvedFileToPublish Include="@(DistFiles->'%(FullPath)')" Exclude="@(ResolvedFileToPublish)">
- <RelativePath>%(DistFiles.Identity)</RelativePath>
- <CopyToPublishDirectory>PreserveNewest</CopyToPublishDirectory>
- </ResolvedFileToPublish>
+ <PackageReference Include="Microsoft.AspNetCore.App" />
+ <PackageReference Include="Microsoft.AspNetCore.Razor.Design" Version="2.2.0" PrivateAssets="All" />
+ <PackageReference Include="Microsoft.VisualStudio.Web.CodeGeneration.Design" Version="2.2.3" />
+ <PackageReference Include="Pomelo.EntityFrameworkCore.MySql" Version="2.2.0" />
+ <PackageReference Include="Pomelo.EntityFrameworkCore.MySql.Design" Version="1.1.2" />
</ItemGroup>
- </Target>
-
</Project>
diff --git a/Timeline/appsettings.json b/Timeline/appsettings.json
index 74d3da4e..81f83d68 100644
--- a/Timeline/appsettings.json
+++ b/Timeline/appsettings.json
@@ -5,8 +5,8 @@
}
},
"JwtConfig": {
- "Issuer": "crupest.xyz",
- "Audience": "crupest.xyz"
+ "Issuer": "api.crupest.xyz",
+ "Audience": "api.crupest.xyz"
},
"TodoPageConfig": {
"GithubInfo": {
diff --git a/Timeline/wwwroot/android-chrome-192x192.png b/Timeline/wwwroot/android-chrome-192x192.png
deleted file mode 100644
index 5dff06b1..00000000
--- a/Timeline/wwwroot/android-chrome-192x192.png
+++ /dev/null
Binary files differ
diff --git a/Timeline/wwwroot/android-chrome-512x512.png b/Timeline/wwwroot/android-chrome-512x512.png
deleted file mode 100644
index ab68aace..00000000
--- a/Timeline/wwwroot/android-chrome-512x512.png
+++ /dev/null
Binary files differ
diff --git a/Timeline/wwwroot/apple-touch-icon.png b/Timeline/wwwroot/apple-touch-icon.png
deleted file mode 100644
index 1397e419..00000000
--- a/Timeline/wwwroot/apple-touch-icon.png
+++ /dev/null
Binary files differ
diff --git a/Timeline/wwwroot/browserconfig.xml b/Timeline/wwwroot/browserconfig.xml
deleted file mode 100644
index b3930d0f..00000000
--- a/Timeline/wwwroot/browserconfig.xml
+++ /dev/null
@@ -1,9 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<browserconfig>
- <msapplication>
- <tile>
- <square150x150logo src="/mstile-150x150.png"/>
- <TileColor>#da532c</TileColor>
- </tile>
- </msapplication>
-</browserconfig>
diff --git a/Timeline/wwwroot/favicon-16x16.png b/Timeline/wwwroot/favicon-16x16.png
deleted file mode 100644
index 0cee9398..00000000
--- a/Timeline/wwwroot/favicon-16x16.png
+++ /dev/null
Binary files differ
diff --git a/Timeline/wwwroot/favicon-32x32.png b/Timeline/wwwroot/favicon-32x32.png
deleted file mode 100644
index 2e358474..00000000
--- a/Timeline/wwwroot/favicon-32x32.png
+++ /dev/null
Binary files differ
diff --git a/Timeline/wwwroot/favicon.ico b/Timeline/wwwroot/favicon.ico
deleted file mode 100644
index fba217fd..00000000
--- a/Timeline/wwwroot/favicon.ico
+++ /dev/null
Binary files differ
diff --git a/Timeline/wwwroot/mstile-144x144.png b/Timeline/wwwroot/mstile-144x144.png
deleted file mode 100644
index b111e9f7..00000000
--- a/Timeline/wwwroot/mstile-144x144.png
+++ /dev/null
Binary files differ
diff --git a/Timeline/wwwroot/mstile-150x150.png b/Timeline/wwwroot/mstile-150x150.png
deleted file mode 100644
index 50eb11aa..00000000
--- a/Timeline/wwwroot/mstile-150x150.png
+++ /dev/null
Binary files differ
diff --git a/Timeline/wwwroot/mstile-310x150.png b/Timeline/wwwroot/mstile-310x150.png
deleted file mode 100644
index b2de3715..00000000
--- a/Timeline/wwwroot/mstile-310x150.png
+++ /dev/null
Binary files differ
diff --git a/Timeline/wwwroot/mstile-310x310.png b/Timeline/wwwroot/mstile-310x310.png
deleted file mode 100644
index 011b7b7e..00000000
--- a/Timeline/wwwroot/mstile-310x310.png
+++ /dev/null
Binary files differ
diff --git a/Timeline/wwwroot/mstile-70x70.png b/Timeline/wwwroot/mstile-70x70.png
deleted file mode 100644
index bcc60d24..00000000
--- a/Timeline/wwwroot/mstile-70x70.png
+++ /dev/null
Binary files differ
diff --git a/Timeline/wwwroot/safari-pinned-tab.svg b/Timeline/wwwroot/safari-pinned-tab.svg
deleted file mode 100644
index 0716ee63..00000000
--- a/Timeline/wwwroot/safari-pinned-tab.svg
+++ /dev/null
@@ -1,30 +0,0 @@
-<?xml version="1.0" standalone="no"?>
-<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 20010904//EN"
- "http://www.w3.org/TR/2001/REC-SVG-20010904/DTD/svg10.dtd">
-<svg version="1.0" xmlns="http://www.w3.org/2000/svg"
- width="700.000000pt" height="700.000000pt" viewBox="0 0 700.000000 700.000000"
- preserveAspectRatio="xMidYMid meet">
-<metadata>
-Created by potrace 1.11, written by Peter Selinger 2001-2013
-</metadata>
-<g transform="translate(0.000000,700.000000) scale(0.100000,-0.100000)"
-fill="#000000" stroke="none">
-<path d="M2800 6277 l0 -722 -62 -24 c-185 -70 -379 -177 -538 -297 -93 -70
--107 -81 -200 -169 -50 -47 -177 -185 -199 -219 -8 -11 -18 -23 -22 -26 -4 -3
--42 -57 -84 -120 -63 -95 -174 -302 -206 -385 -59 -152 -111 -336 -125 -440
--3 -27 -8 -52 -9 -55 -21 -35 -26 -479 -6 -600 6 -41 14 -91 17 -110 22 -147
-114 -421 194 -579 107 -214 284 -454 438 -595 25 -22 55 -50 66 -61 48 -47
-208 -165 301 -222 111 -68 277 -150 373 -184 l62 -22 0 -723 0 -724 700 0 700
-0 0 724 0 723 43 17 c23 9 46 16 50 16 4 0 46 18 93 40 47 22 88 40 89 40 2 0
-55 30 117 67 508 301 862 770 1007 1333 16 63 32 133 35 155 9 65 18 119 23
-140 3 11 6 106 8 210 2 169 -1 232 -20 353 -3 17 -8 49 -11 70 -11 71 -65 268
--104 373 -117 320 -323 621 -585 855 -75 67 -267 214 -279 214 -3 0 -31 16
--62 36 -67 43 -254 133 -341 164 l-63 23 0 723 0 724 -700 0 -700 0 0 -723z
-m920 -1467 c197 -38 348 -96 501 -195 242 -156 431 -394 528 -665 38 -108 49
--151 70 -285 13 -85 6 -315 -14 -415 -62 -313 -238 -602 -487 -796 -54 -42
--220 -149 -226 -145 -1 1 -15 -5 -32 -14 -73 -38 -239 -88 -365 -111 -72 -13
--298 -12 -385 2 -432 67 -801 331 -989 709 -77 154 -104 232 -132 385 -15 80
--20 296 -9 370 30 198 56 287 133 442 186 376 549 646 963 717 125 21 339 22
-444 1z"/>
-</g>
-</svg>
diff --git a/Timeline/wwwroot/site.webmanifest b/Timeline/wwwroot/site.webmanifest
deleted file mode 100644
index b20abb7c..00000000
--- a/Timeline/wwwroot/site.webmanifest
+++ /dev/null
@@ -1,19 +0,0 @@
-{
- "name": "",
- "short_name": "",
- "icons": [
- {
- "src": "/android-chrome-192x192.png",
- "sizes": "192x192",
- "type": "image/png"
- },
- {
- "src": "/android-chrome-512x512.png",
- "sizes": "512x512",
- "type": "image/png"
- }
- ],
- "theme_color": "#ffffff",
- "background_color": "#ffffff",
- "display": "standalone"
-}