mirror of
https://github.com/kubernetes-sigs/kustomize.git
synced 2026-06-12 01:14:22 +00:00
Adds frontend + configs to interal/tools/ui
This commit is contained in:
22
internal/tools/config/webapp/frontend/deployment.yaml
Normal file
22
internal/tools/config/webapp/frontend/deployment.yaml
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
apiVersion: apps/v1
|
||||||
|
kind: Deployment
|
||||||
|
metadata:
|
||||||
|
name: kustomize-search-ui
|
||||||
|
spec:
|
||||||
|
selector:
|
||||||
|
matchLabels:
|
||||||
|
app: kustomize-search
|
||||||
|
tier: frontend
|
||||||
|
replicas: 1
|
||||||
|
template:
|
||||||
|
metadata:
|
||||||
|
labels:
|
||||||
|
app: kustomize-search
|
||||||
|
tier: frontend
|
||||||
|
spec:
|
||||||
|
containers:
|
||||||
|
- name: frontend
|
||||||
|
image: gcr.io/kustomize-search/frontend:latest
|
||||||
|
ports:
|
||||||
|
- name: frontend-port
|
||||||
|
containerPort: 80
|
||||||
4
internal/tools/config/webapp/frontend/kustomization.yaml
Normal file
4
internal/tools/config/webapp/frontend/kustomization.yaml
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
resources:
|
||||||
|
- ../../base
|
||||||
|
- deployment.yaml
|
||||||
|
- service.yaml
|
||||||
14
internal/tools/config/webapp/frontend/service.yaml
Normal file
14
internal/tools/config/webapp/frontend/service.yaml
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
apiVersion: v1
|
||||||
|
kind: Service
|
||||||
|
metadata:
|
||||||
|
name: kustomize-search-ui
|
||||||
|
spec:
|
||||||
|
selector:
|
||||||
|
app: kustomize-search
|
||||||
|
tier: frontend
|
||||||
|
ports:
|
||||||
|
- protocol: "TCP"
|
||||||
|
port: 80
|
||||||
|
targetPort: frontend-port
|
||||||
|
type: LoadBalancer
|
||||||
|
loadBalancerIP: ""
|
||||||
2
internal/tools/ui/.dockerignore
Normal file
2
internal/tools/ui/.dockerignore
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
node_modules
|
||||||
|
dist
|
||||||
2
internal/tools/ui/.gcloudignore
Normal file
2
internal/tools/ui/.gcloudignore
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
node_modules
|
||||||
|
dist
|
||||||
2
internal/tools/ui/.gitignore
vendored
Normal file
2
internal/tools/ui/.gitignore
vendored
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
node_modules
|
||||||
|
dist
|
||||||
16
internal/tools/ui/Dockerfile
Normal file
16
internal/tools/ui/Dockerfile
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
FROM node:latest as builder
|
||||||
|
|
||||||
|
WORKDIR /app
|
||||||
|
COPY package.json package-lock.json /app/
|
||||||
|
RUN cd /app && npm set progress=false && npm install
|
||||||
|
COPY . /app
|
||||||
|
|
||||||
|
RUN cd /app && npm run build
|
||||||
|
|
||||||
|
FROM nginx:alpine
|
||||||
|
RUN rm -rf /usr/share/nginx/html/*
|
||||||
|
# todo(damienr74), put this in configmap.
|
||||||
|
COPY nginx.conf /etc/nginx/nginx.conf
|
||||||
|
COPY --from=builder /app/dist/kustomize-search/ /usr/share/nginx/html
|
||||||
|
EXPOSE 80
|
||||||
|
CMD ["nginx", "-g", "daemon off;"]
|
||||||
25
internal/tools/ui/README.md
Normal file
25
internal/tools/ui/README.md
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
There is a Dockerfile for building container images.
|
||||||
|
|
||||||
|
## 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).
|
||||||
123
internal/tools/ui/angular.json
Normal file
123
internal/tools/ui/angular.json
Normal file
@@ -0,0 +1,123 @@
|
|||||||
|
{
|
||||||
|
"$schema": "./node_modules/@angular/cli/lib/config/schema.json",
|
||||||
|
"version": 1,
|
||||||
|
"newProjectRoot": "projects",
|
||||||
|
"projects": {
|
||||||
|
"kustomize-search": {
|
||||||
|
"projectType": "application",
|
||||||
|
"schematics": {},
|
||||||
|
"root": "",
|
||||||
|
"sourceRoot": "src",
|
||||||
|
"prefix": "app",
|
||||||
|
"architect": {
|
||||||
|
"build": {
|
||||||
|
"builder": "@angular-devkit/build-angular:browser",
|
||||||
|
"options": {
|
||||||
|
"outputPath": "dist/kustomize-search",
|
||||||
|
"index": "src/index.html",
|
||||||
|
"main": "src/main.ts",
|
||||||
|
"polyfills": "src/polyfills.ts",
|
||||||
|
"tsConfig": "tsconfig.app.json",
|
||||||
|
"aot": false,
|
||||||
|
"assets": [
|
||||||
|
"src/favicon.ico",
|
||||||
|
"src/assets"
|
||||||
|
],
|
||||||
|
"styles": [
|
||||||
|
"./node_modules/@angular/material/prebuilt-themes/deeppurple-amber.css",
|
||||||
|
"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,
|
||||||
|
"budgets": [
|
||||||
|
{
|
||||||
|
"type": "initial",
|
||||||
|
"maximumWarning": "2mb",
|
||||||
|
"maximumError": "5mb"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"serve": {
|
||||||
|
"builder": "@angular-devkit/build-angular:dev-server",
|
||||||
|
"options": {
|
||||||
|
"browserTarget": "kustomize-search:build"
|
||||||
|
},
|
||||||
|
"configurations": {
|
||||||
|
"production": {
|
||||||
|
"browserTarget": "kustomize-search:build:production"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"extract-i18n": {
|
||||||
|
"builder": "@angular-devkit/build-angular:extract-i18n",
|
||||||
|
"options": {
|
||||||
|
"browserTarget": "kustomize-search:build"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"test": {
|
||||||
|
"builder": "@angular-devkit/build-angular:karma",
|
||||||
|
"options": {
|
||||||
|
"main": "src/test.ts",
|
||||||
|
"polyfills": "src/polyfills.ts",
|
||||||
|
"tsConfig": "tsconfig.spec.json",
|
||||||
|
"karmaConfig": "karma.conf.js",
|
||||||
|
"assets": [
|
||||||
|
"src/favicon.ico",
|
||||||
|
"src/assets"
|
||||||
|
],
|
||||||
|
"styles": [
|
||||||
|
"./node_modules/@angular/material/prebuilt-themes/deeppurple-amber.css",
|
||||||
|
"src/styles.css"
|
||||||
|
],
|
||||||
|
"scripts": []
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"lint": {
|
||||||
|
"builder": "@angular-devkit/build-angular:tslint",
|
||||||
|
"options": {
|
||||||
|
"tsConfig": [
|
||||||
|
"tsconfig.app.json",
|
||||||
|
"tsconfig.spec.json",
|
||||||
|
"e2e/tsconfig.json"
|
||||||
|
],
|
||||||
|
"exclude": [
|
||||||
|
"**/node_modules/**"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"e2e": {
|
||||||
|
"builder": "@angular-devkit/build-angular:protractor",
|
||||||
|
"options": {
|
||||||
|
"protractorConfig": "e2e/protractor.conf.js",
|
||||||
|
"devServerTarget": "kustomize-search:serve"
|
||||||
|
},
|
||||||
|
"configurations": {
|
||||||
|
"production": {
|
||||||
|
"devServerTarget": "kustomize-search:serve:production"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"defaultProject": "kustomize-search"
|
||||||
|
}
|
||||||
12
internal/tools/ui/browserslist
Normal file
12
internal/tools/ui/browserslist
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
# This file is used by the build system to adjust CSS and JS output to support the specified browsers below.
|
||||||
|
# For additional information regarding the format and rule options, please see:
|
||||||
|
# https://github.com/browserslist/browserslist#queries
|
||||||
|
|
||||||
|
# You can see what browsers were selected by your queries by running:
|
||||||
|
# npx browserslist
|
||||||
|
|
||||||
|
> 0.5%
|
||||||
|
last 2 versions
|
||||||
|
Firefox ESR
|
||||||
|
not dead
|
||||||
|
not IE 9-11 # For IE 9-11 support, remove 'not'.
|
||||||
5
internal/tools/ui/cloudbuild.yaml
Normal file
5
internal/tools/ui/cloudbuild.yaml
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
steps:
|
||||||
|
- name: 'gcr.io/cloud-builders/docker'
|
||||||
|
args: ['build', '-t', 'gcr.io/kustomize-search/frontend', '.']
|
||||||
|
images:
|
||||||
|
- 'gcr.io/kustomize-search/frontend'
|
||||||
32
internal/tools/ui/e2e/protractor.conf.js
Normal file
32
internal/tools/ui/e2e/protractor.conf.js
Normal file
@@ -0,0 +1,32 @@
|
|||||||
|
// @ts-check
|
||||||
|
// Protractor configuration file, see link for more information
|
||||||
|
// https://github.com/angular/protractor/blob/master/lib/config.ts
|
||||||
|
|
||||||
|
const { SpecReporter } = require('jasmine-spec-reporter');
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @type { import("protractor").Config }
|
||||||
|
*/
|
||||||
|
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.json')
|
||||||
|
});
|
||||||
|
jasmine.getEnv().addReporter(new SpecReporter({ spec: { displayStacktrace: true } }));
|
||||||
|
}
|
||||||
|
};
|
||||||
23
internal/tools/ui/e2e/src/app.e2e-spec.ts
Normal file
23
internal/tools/ui/e2e/src/app.e2e-spec.ts
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
import { AppPage } from './app.po';
|
||||||
|
import { browser, logging } from 'protractor';
|
||||||
|
|
||||||
|
describe('workspace-project App', () => {
|
||||||
|
let page: AppPage;
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
page = new AppPage();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should display welcome message', () => {
|
||||||
|
page.navigateTo();
|
||||||
|
expect(page.getTitleText()).toEqual('Welcome to kustomize-search!');
|
||||||
|
});
|
||||||
|
|
||||||
|
afterEach(async () => {
|
||||||
|
// Assert that there are no errors emitted from the browser
|
||||||
|
const logs = await browser.manage().logs().get(logging.Type.BROWSER);
|
||||||
|
expect(logs).not.toContain(jasmine.objectContaining({
|
||||||
|
level: logging.Level.SEVERE,
|
||||||
|
} as logging.Entry));
|
||||||
|
});
|
||||||
|
});
|
||||||
11
internal/tools/ui/e2e/src/app.po.ts
Normal file
11
internal/tools/ui/e2e/src/app.po.ts
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
import { browser, by, element } from 'protractor';
|
||||||
|
|
||||||
|
export class AppPage {
|
||||||
|
navigateTo() {
|
||||||
|
return browser.get(browser.baseUrl) as Promise<any>;
|
||||||
|
}
|
||||||
|
|
||||||
|
getTitleText() {
|
||||||
|
return element(by.css('app-root h1')).getText() as Promise<string>;
|
||||||
|
}
|
||||||
|
}
|
||||||
13
internal/tools/ui/e2e/tsconfig.json
Normal file
13
internal/tools/ui/e2e/tsconfig.json
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
{
|
||||||
|
"extends": "../tsconfig.json",
|
||||||
|
"compilerOptions": {
|
||||||
|
"outDir": "../out-tsc/e2e",
|
||||||
|
"module": "commonjs",
|
||||||
|
"target": "es5",
|
||||||
|
"types": [
|
||||||
|
"jasmine",
|
||||||
|
"jasminewd2",
|
||||||
|
"node"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
32
internal/tools/ui/karma.conf.js
Normal file
32
internal/tools/ui/karma.conf.js
Normal file
@@ -0,0 +1,32 @@
|
|||||||
|
// 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('@angular-devkit/build-angular/plugins/karma')
|
||||||
|
],
|
||||||
|
client: {
|
||||||
|
clearContext: false // leave Jasmine Spec Runner output visible in browser
|
||||||
|
},
|
||||||
|
coverageIstanbulReporter: {
|
||||||
|
dir: require('path').join(__dirname, './coverage/kustomize-search'),
|
||||||
|
reports: ['html', 'lcovonly', 'text-summary'],
|
||||||
|
fixWebpackSourcePaths: true
|
||||||
|
},
|
||||||
|
reporters: ['progress', 'kjhtml'],
|
||||||
|
port: 9876,
|
||||||
|
colors: true,
|
||||||
|
logLevel: config.LOG_INFO,
|
||||||
|
autoWatch: true,
|
||||||
|
browsers: ['Chrome'],
|
||||||
|
singleRun: false,
|
||||||
|
restartOnFileChange: true
|
||||||
|
});
|
||||||
|
};
|
||||||
25
internal/tools/ui/nginx.conf
Normal file
25
internal/tools/ui/nginx.conf
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
worker_processes 1;
|
||||||
|
|
||||||
|
events {
|
||||||
|
worker_connections 1024;
|
||||||
|
}
|
||||||
|
|
||||||
|
http {
|
||||||
|
server {
|
||||||
|
listen 80;
|
||||||
|
server_name 0.0.0.0;
|
||||||
|
|
||||||
|
root /usr/share/nginx/html;
|
||||||
|
index index.html index.htm;
|
||||||
|
include /etc/nginx/mime.types;
|
||||||
|
|
||||||
|
gzip on;
|
||||||
|
gzip_min_length 1000;
|
||||||
|
gzip_proxied expired no-cache no-store private auth;
|
||||||
|
gzip_types text/plain text/css application/json applications/javascript application/x-javascript text/javascript;
|
||||||
|
|
||||||
|
location / {
|
||||||
|
try_files $uri $uri/ /index.html;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
12104
internal/tools/ui/package-lock.json
generated
Normal file
12104
internal/tools/ui/package-lock.json
generated
Normal file
File diff suppressed because it is too large
Load Diff
53
internal/tools/ui/package.json
Normal file
53
internal/tools/ui/package.json
Normal file
@@ -0,0 +1,53 @@
|
|||||||
|
{
|
||||||
|
"name": "kustomize-search",
|
||||||
|
"version": "0.0.0",
|
||||||
|
"scripts": {
|
||||||
|
"ng": "ng",
|
||||||
|
"start": "ng serve",
|
||||||
|
"build": "ng build --prod --aot",
|
||||||
|
"test": "ng test",
|
||||||
|
"lint": "ng lint",
|
||||||
|
"e2e": "ng e2e"
|
||||||
|
},
|
||||||
|
"private": true,
|
||||||
|
"dependencies": {
|
||||||
|
"@angular/animations": "~8.1.0",
|
||||||
|
"@angular/cdk": "~8.0.2",
|
||||||
|
"@angular/common": "~8.1.0",
|
||||||
|
"@angular/compiler": "~8.1.0",
|
||||||
|
"@angular/core": "~8.1.0",
|
||||||
|
"@angular/forms": "~8.1.0",
|
||||||
|
"@angular/http": "^7.2.15",
|
||||||
|
"@angular/material": "^8.0.2",
|
||||||
|
"@angular/platform-browser": "~8.1.0",
|
||||||
|
"@angular/platform-browser-dynamic": "~8.1.0",
|
||||||
|
"@angular/router": "~8.1.0",
|
||||||
|
"angular-google-charts": "^0.1.6",
|
||||||
|
"chart.js": "^2.8.0",
|
||||||
|
"hammerjs": "^2.0.8",
|
||||||
|
"rxjs": "~6.4.0",
|
||||||
|
"tslib": "^1.9.0",
|
||||||
|
"zone.js": "~0.9.1"
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"@angular-devkit/build-angular": "~0.801.0",
|
||||||
|
"@angular/cli": "^8.1.2",
|
||||||
|
"@angular/compiler-cli": "~8.1.0",
|
||||||
|
"@angular/language-service": "~8.1.0",
|
||||||
|
"@types/jasmine": "~3.3.8",
|
||||||
|
"@types/jasminewd2": "~2.0.3",
|
||||||
|
"@types/node": "~8.9.4",
|
||||||
|
"codelyzer": "^5.0.0",
|
||||||
|
"jasmine-core": "~3.4.0",
|
||||||
|
"jasmine-spec-reporter": "~4.2.1",
|
||||||
|
"karma": "~4.1.0",
|
||||||
|
"karma-chrome-launcher": "~2.2.0",
|
||||||
|
"karma-coverage-istanbul-reporter": "~2.0.1",
|
||||||
|
"karma-jasmine": "~2.0.1",
|
||||||
|
"karma-jasmine-html-reporter": "^1.4.0",
|
||||||
|
"protractor": "~5.4.0",
|
||||||
|
"ts-node": "~7.0.0",
|
||||||
|
"tslint": "~5.15.0",
|
||||||
|
"typescript": "~3.4.3"
|
||||||
|
}
|
||||||
|
}
|
||||||
0
internal/tools/ui/src/app/app.component.css
Normal file
0
internal/tools/ui/src/app/app.component.css
Normal file
2
internal/tools/ui/src/app/app.component.html
Normal file
2
internal/tools/ui/src/app/app.component.html
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
<h1>{{ title }}</h1>
|
||||||
|
<router-outlet></router-outlet>
|
||||||
31
internal/tools/ui/src/app/app.component.spec.ts
Normal file
31
internal/tools/ui/src/app/app.component.spec.ts
Normal file
@@ -0,0 +1,31 @@
|
|||||||
|
import { TestBed, async } from '@angular/core/testing';
|
||||||
|
import { AppComponent } from './app.component';
|
||||||
|
|
||||||
|
describe('AppComponent', () => {
|
||||||
|
beforeEach(async(() => {
|
||||||
|
TestBed.configureTestingModule({
|
||||||
|
declarations: [
|
||||||
|
AppComponent
|
||||||
|
],
|
||||||
|
}).compileComponents();
|
||||||
|
}));
|
||||||
|
|
||||||
|
it('should create the app', () => {
|
||||||
|
const fixture = TestBed.createComponent(AppComponent);
|
||||||
|
const app = fixture.debugElement.componentInstance;
|
||||||
|
expect(app).toBeTruthy();
|
||||||
|
});
|
||||||
|
|
||||||
|
it(`should have as title 'kustomize-search'`, () => {
|
||||||
|
const fixture = TestBed.createComponent(AppComponent);
|
||||||
|
const app = fixture.debugElement.componentInstance;
|
||||||
|
expect(app.title).toEqual('kustomize-search');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should render title in a h1 tag', () => {
|
||||||
|
const fixture = TestBed.createComponent(AppComponent);
|
||||||
|
fixture.detectChanges();
|
||||||
|
const compiled = fixture.debugElement.nativeElement;
|
||||||
|
expect(compiled.querySelector('h1').textContent).toContain('Welcome to kustomize-search!');
|
||||||
|
});
|
||||||
|
});
|
||||||
10
internal/tools/ui/src/app/app.component.ts
Normal file
10
internal/tools/ui/src/app/app.component.ts
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
import { Component } from '@angular/core';
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'app-root',
|
||||||
|
templateUrl: './app.component.html',
|
||||||
|
styleUrls: ['./app.component.css']
|
||||||
|
})
|
||||||
|
export class AppComponent {
|
||||||
|
title = 'k8s Search';
|
||||||
|
}
|
||||||
58
internal/tools/ui/src/app/app.module.ts
Normal file
58
internal/tools/ui/src/app/app.module.ts
Normal file
@@ -0,0 +1,58 @@
|
|||||||
|
import { BrowserModule } from '@angular/platform-browser';
|
||||||
|
import { Routes, RouterModule } from '@angular/router';
|
||||||
|
import { NgModule } from '@angular/core';
|
||||||
|
import { FormsModule } from '@angular/forms';
|
||||||
|
import { HttpClientModule } from '@angular/common/http';
|
||||||
|
|
||||||
|
import { MatExpansionModule } from '@angular/material/expansion';
|
||||||
|
import { MatInputModule } from '@angular/material/input';
|
||||||
|
import { MatListModule } from '@angular/material/list';
|
||||||
|
import { MatButtonModule } from '@angular/material/button';
|
||||||
|
|
||||||
|
import { AppComponent } from './app.component';
|
||||||
|
import { SearchComponent } from './search/search.component';
|
||||||
|
import { BrowserAnimationsModule } from '@angular/platform-browser/animations';
|
||||||
|
import { HistogramComponent } from './histogram/histogram.component';
|
||||||
|
import { TimeseriesComponent } from './timeseries/timeseries.component';
|
||||||
|
|
||||||
|
const appRoutes: Routes = [
|
||||||
|
{
|
||||||
|
path: 'search',
|
||||||
|
component: SearchComponent,
|
||||||
|
runGuardsAndResolvers: 'always'
|
||||||
|
},
|
||||||
|
// Always ridirect to the search endpoint for now.
|
||||||
|
{
|
||||||
|
path: '',
|
||||||
|
redirectTo: 'search',
|
||||||
|
pathMatch: 'full',
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
@NgModule({
|
||||||
|
declarations: [
|
||||||
|
AppComponent,
|
||||||
|
SearchComponent,
|
||||||
|
HistogramComponent,
|
||||||
|
TimeseriesComponent,
|
||||||
|
],
|
||||||
|
imports: [
|
||||||
|
BrowserModule,
|
||||||
|
BrowserAnimationsModule,
|
||||||
|
HttpClientModule,
|
||||||
|
MatExpansionModule,
|
||||||
|
MatInputModule,
|
||||||
|
MatListModule,
|
||||||
|
MatButtonModule,
|
||||||
|
FormsModule,
|
||||||
|
RouterModule.forRoot(
|
||||||
|
appRoutes,
|
||||||
|
{ onSameUrlNavigation: 'reload', }
|
||||||
|
)
|
||||||
|
],
|
||||||
|
providers: [
|
||||||
|
{provide: HttpClientModule}
|
||||||
|
],
|
||||||
|
bootstrap: [AppComponent]
|
||||||
|
})
|
||||||
|
export class AppModule {}
|
||||||
41
internal/tools/ui/src/app/documents.ts
Normal file
41
internal/tools/ui/src/app/documents.ts
Normal file
@@ -0,0 +1,41 @@
|
|||||||
|
export interface SearchResults {
|
||||||
|
hits: SearchResults.Hits;
|
||||||
|
aggregations?: SearchResults.Aggregations;
|
||||||
|
};
|
||||||
|
|
||||||
|
export namespace SearchResults {
|
||||||
|
export class Hits {
|
||||||
|
total: number;
|
||||||
|
hits: SearchResults.InnerHits[];
|
||||||
|
};
|
||||||
|
|
||||||
|
export class InnerHits {
|
||||||
|
id: string;
|
||||||
|
result: SearchResults.Result;
|
||||||
|
};
|
||||||
|
|
||||||
|
export class Result {
|
||||||
|
repositoryUrl: string;
|
||||||
|
filePath: string;
|
||||||
|
defaultBranch: string;
|
||||||
|
document: string;
|
||||||
|
creationTime: Date;
|
||||||
|
values: string;
|
||||||
|
kinds: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
export interface Aggregations {
|
||||||
|
timeseries?: SearchResults.BucketAggregation;
|
||||||
|
kinds?: SearchResults.BucketAggregation;
|
||||||
|
};
|
||||||
|
|
||||||
|
export interface BucketAggregation {
|
||||||
|
otherResults?: number;
|
||||||
|
buckets: SearchResults.Bucket[];
|
||||||
|
};
|
||||||
|
|
||||||
|
export class Bucket {
|
||||||
|
key: string;
|
||||||
|
count: number;
|
||||||
|
};
|
||||||
|
};
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
<div><canvas id="histogram">{{hist}}</canvas></div>
|
||||||
@@ -0,0 +1,25 @@
|
|||||||
|
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
|
||||||
|
|
||||||
|
import { HistogramComponent } from './histogram.component';
|
||||||
|
|
||||||
|
describe('HistogramComponent', () => {
|
||||||
|
let component: HistogramComponent;
|
||||||
|
let fixture: ComponentFixture<HistogramComponent>;
|
||||||
|
|
||||||
|
beforeEach(async(() => {
|
||||||
|
TestBed.configureTestingModule({
|
||||||
|
declarations: [ HistogramComponent ]
|
||||||
|
})
|
||||||
|
.compileComponents();
|
||||||
|
}));
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
fixture = TestBed.createComponent(HistogramComponent);
|
||||||
|
component = fixture.componentInstance;
|
||||||
|
fixture.detectChanges();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should create', () => {
|
||||||
|
expect(component).toBeTruthy();
|
||||||
|
});
|
||||||
|
});
|
||||||
61
internal/tools/ui/src/app/histogram/histogram.component.ts
Normal file
61
internal/tools/ui/src/app/histogram/histogram.component.ts
Normal file
@@ -0,0 +1,61 @@
|
|||||||
|
import { Chart } from 'chart.js';
|
||||||
|
import { SearchResults } from '../documents';
|
||||||
|
|
||||||
|
import { Component, OnInit } from '@angular/core';
|
||||||
|
import { Subject, Observable } from 'rxjs';
|
||||||
|
|
||||||
|
const otherLabel = 'Other Kinds';
|
||||||
|
|
||||||
|
// Draws a histogram from SearchResults.BucketAggregation data.
|
||||||
|
@Component({
|
||||||
|
selector: 'app-histogram',
|
||||||
|
templateUrl: './histogram.component.html',
|
||||||
|
styleUrls: ['./histogram.component.css']
|
||||||
|
})
|
||||||
|
export class HistogramComponent implements OnInit {
|
||||||
|
hist;
|
||||||
|
|
||||||
|
constructor() {}
|
||||||
|
ngOnInit() {}
|
||||||
|
|
||||||
|
public update(agg: SearchResults.BucketAggregation): Observable<string> {
|
||||||
|
if (this.hist) {
|
||||||
|
this.hist.destroy();
|
||||||
|
}
|
||||||
|
|
||||||
|
let labels = agg.buckets.map(bucket => bucket.key);
|
||||||
|
let counts = agg.buckets.map(bucket => bucket.count);
|
||||||
|
if (agg.otherResults && agg.otherResults > 0) {
|
||||||
|
labels.push(otherLabel)
|
||||||
|
counts.push(agg.otherResults)
|
||||||
|
}
|
||||||
|
|
||||||
|
let selectedLabel = new Subject<string>();
|
||||||
|
|
||||||
|
this.hist = new Chart('histogram', {
|
||||||
|
type: 'bar',
|
||||||
|
data: {
|
||||||
|
datasets: [ { data: counts } ],
|
||||||
|
labels: labels,
|
||||||
|
},
|
||||||
|
options: {
|
||||||
|
legend: { display: false },
|
||||||
|
'onClick' : function(e, it) {
|
||||||
|
if (!(it && it[0] && it[0]._model && it[0]._model.label)) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
let label = it[0]._model.label;
|
||||||
|
if (label != otherLabel) {
|
||||||
|
selectedLabel.next(label);
|
||||||
|
}
|
||||||
|
}.bind(selectedLabel),
|
||||||
|
scales: {
|
||||||
|
// no floating point
|
||||||
|
yAxes: [ { ticks: { precision: 0, beginAtZero: true } } ],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
return selectedLabel;
|
||||||
|
}
|
||||||
|
}
|
||||||
8
internal/tools/ui/src/app/search/search.component.css
Normal file
8
internal/tools/ui/src/app/search/search.component.css
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
.json_query > * {
|
||||||
|
width: 100%;
|
||||||
|
font-family: monospace;
|
||||||
|
}
|
||||||
|
|
||||||
|
mat-expansion-panel-header {
|
||||||
|
padding: 20px;
|
||||||
|
}
|
||||||
36
internal/tools/ui/src/app/search/search.component.html
Normal file
36
internal/tools/ui/src/app/search/search.component.html
Normal file
@@ -0,0 +1,36 @@
|
|||||||
|
<div class="json_query">
|
||||||
|
<mat-form-field>
|
||||||
|
<input matInput (keydown.enter)="search()" placeholder="Search" [(ngModel)]='inputQueryValue'>
|
||||||
|
</mat-form-field>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<br>
|
||||||
|
<mat-expansion-panel
|
||||||
|
[disabled]="docs.hits.total == 0"
|
||||||
|
[expanded]="docs.hits.hits.length == 0 && docs.hits.total != 0">
|
||||||
|
|
||||||
|
<mat-expansion-panel-header>
|
||||||
|
{{docs.hits.total}} matching config files
|
||||||
|
<div *ngIf="docs.hits.total > 0">, expand to see a breakdown by kind.</div>
|
||||||
|
</mat-expansion-panel-header>
|
||||||
|
<app-histogram></app-histogram>
|
||||||
|
<app-timeseries></app-timeseries>
|
||||||
|
</mat-expansion-panel>
|
||||||
|
|
||||||
|
<br>
|
||||||
|
<mat-expansion-panel class="result" *ngFor="let doc of docs.hits.hits">
|
||||||
|
<mat-expansion-panel-header class="result" [collapsedHeight]="'auto'" [expandedHeight]="'auto'">
|
||||||
|
{{ doc.result.repositoryUrl }}/{{ doc.result.filePath }}
|
||||||
|
</mat-expansion-panel-header>
|
||||||
|
<div mat-line>
|
||||||
|
<h3>File Contents</h3>
|
||||||
|
<pre><code>{{doc.result.document}}</code></pre>
|
||||||
|
</div>
|
||||||
|
</mat-expansion-panel>
|
||||||
|
|
||||||
|
<button mat-button [disabled]="first()" (click)="prev()">
|
||||||
|
Previous
|
||||||
|
</button>
|
||||||
|
<button mat-button [disabled]="last()" (click)="next()">
|
||||||
|
Next
|
||||||
|
</button>
|
||||||
25
internal/tools/ui/src/app/search/search.component.spec.ts
Normal file
25
internal/tools/ui/src/app/search/search.component.spec.ts
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
|
||||||
|
|
||||||
|
import { SearchComponent } from './search.component';
|
||||||
|
|
||||||
|
describe('SearchComponent', () => {
|
||||||
|
let component: SearchComponent;
|
||||||
|
let fixture: ComponentFixture<SearchComponent>;
|
||||||
|
|
||||||
|
beforeEach(async(() => {
|
||||||
|
TestBed.configureTestingModule({
|
||||||
|
declarations: [ SearchComponent ]
|
||||||
|
})
|
||||||
|
.compileComponents();
|
||||||
|
}));
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
fixture = TestBed.createComponent(SearchComponent);
|
||||||
|
component = fixture.componentInstance;
|
||||||
|
fixture.detectChanges();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should create', () => {
|
||||||
|
expect(component).toBeTruthy();
|
||||||
|
});
|
||||||
|
});
|
||||||
111
internal/tools/ui/src/app/search/search.component.ts
Normal file
111
internal/tools/ui/src/app/search/search.component.ts
Normal file
@@ -0,0 +1,111 @@
|
|||||||
|
import { Component, OnInit, NgModule } from '@angular/core';
|
||||||
|
import { Router, ActivatedRoute } from '@angular/router';
|
||||||
|
import { SearchResults } from '../documents';
|
||||||
|
import { HistogramComponent } from '../histogram/histogram.component';
|
||||||
|
import { TimeseriesComponent } from '../timeseries/timeseries.component';
|
||||||
|
import { SearchService } from './search.service';
|
||||||
|
|
||||||
|
const perPage = 10;
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'app-search',
|
||||||
|
templateUrl: './search.component.html',
|
||||||
|
styleUrls: ['./search.component.css'],
|
||||||
|
providers: [SearchService]
|
||||||
|
})
|
||||||
|
export class SearchComponent implements OnInit {
|
||||||
|
inputQuery: string[] = [];
|
||||||
|
from: number = 0;
|
||||||
|
disableNav: boolean = false;
|
||||||
|
|
||||||
|
docs: SearchResults = {
|
||||||
|
hits: {
|
||||||
|
total: 0,
|
||||||
|
hits: [],
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
kindBreakdown = new HistogramComponent();
|
||||||
|
timeseries = new TimeseriesComponent();
|
||||||
|
|
||||||
|
constructor(
|
||||||
|
private searcher : SearchService,
|
||||||
|
private router: Router,
|
||||||
|
private route: ActivatedRoute
|
||||||
|
) {}
|
||||||
|
|
||||||
|
ngOnInit() {
|
||||||
|
this.route.queryParams.subscribe(params => {
|
||||||
|
if (params.q instanceof Array) {
|
||||||
|
this.inputQuery = params.q || [""]
|
||||||
|
} else {
|
||||||
|
this.inputQuery = [params.q || "" ];
|
||||||
|
}
|
||||||
|
|
||||||
|
this.from = parseInt(params.from) || 0;
|
||||||
|
if (this.from < 0) {
|
||||||
|
this.from = Math.max(this.from, 0);
|
||||||
|
this.searchWithParams();
|
||||||
|
}
|
||||||
|
|
||||||
|
this.searcher.search(params).subscribe(sr => {
|
||||||
|
this.docs = sr;
|
||||||
|
this.kindBreakdown.update(sr.aggregations.kinds).subscribe(selectedKind => {
|
||||||
|
this.addToQuery('kind='+selectedKind)
|
||||||
|
this.search();
|
||||||
|
})
|
||||||
|
this.timeseries.update(sr.aggregations.timeseries);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
public addToQuery(q: string) {
|
||||||
|
for (let v of this.inputQuery) {
|
||||||
|
if (v == q) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
this.inputQuery.push(q)
|
||||||
|
}
|
||||||
|
|
||||||
|
search(): void {
|
||||||
|
this.from = 0;
|
||||||
|
this.searchWithParams();
|
||||||
|
}
|
||||||
|
|
||||||
|
searchWithParams(): void {
|
||||||
|
let params = {
|
||||||
|
q: this.inputQuery,
|
||||||
|
from: this.from,
|
||||||
|
}
|
||||||
|
this.router.navigate([], {
|
||||||
|
relativeTo: this.route,
|
||||||
|
queryParams: params,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
first(): boolean {
|
||||||
|
return this.from <= 0 || this.disableNav;
|
||||||
|
}
|
||||||
|
|
||||||
|
last(): boolean {
|
||||||
|
return this.from + perPage >= this.docs.hits.total || this.disableNav;
|
||||||
|
}
|
||||||
|
|
||||||
|
next (): void {
|
||||||
|
this.from += perPage;
|
||||||
|
this.searchWithParams();
|
||||||
|
}
|
||||||
|
prev (): void {
|
||||||
|
this.from -= perPage;
|
||||||
|
this.searchWithParams();
|
||||||
|
}
|
||||||
|
|
||||||
|
get inputQueryValue() : string {
|
||||||
|
return this.inputQuery.join(' ')
|
||||||
|
}
|
||||||
|
|
||||||
|
set inputQueryValue(input : string) {
|
||||||
|
this.inputQuery = [input]
|
||||||
|
}
|
||||||
|
}
|
||||||
41
internal/tools/ui/src/app/search/search.service.ts
Normal file
41
internal/tools/ui/src/app/search/search.service.ts
Normal file
@@ -0,0 +1,41 @@
|
|||||||
|
import { SearchResults } from '../documents';
|
||||||
|
|
||||||
|
import { Injectable } from '@angular/core';
|
||||||
|
import {
|
||||||
|
HttpClient,
|
||||||
|
HttpResponse,
|
||||||
|
HttpParams } from '@angular/common/http';
|
||||||
|
import { Params, convertToParamMap } from '@angular/router';
|
||||||
|
|
||||||
|
import { Observable } from 'rxjs';
|
||||||
|
import { filter, map, catchError } from 'rxjs/operators';
|
||||||
|
|
||||||
|
@Injectable()
|
||||||
|
export class SearchService {
|
||||||
|
private serviceUrl = "https://www.example.com/";
|
||||||
|
|
||||||
|
constructor(private http: HttpClient) {}
|
||||||
|
|
||||||
|
public search(params: Params): Observable<SearchResults> {
|
||||||
|
let requestParams = new HttpParams();
|
||||||
|
let pmap = convertToParamMap(params);
|
||||||
|
let hasQuery = false;
|
||||||
|
|
||||||
|
for (var k of pmap.keys) {
|
||||||
|
for (var v of pmap.getAll(k)) {
|
||||||
|
if (k == "q" && v != "") {
|
||||||
|
hasQuery = true
|
||||||
|
}
|
||||||
|
requestParams.append(k, v)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let queryUrl = this.serviceUrl
|
||||||
|
if (hasQuery) {
|
||||||
|
queryUrl += "search"
|
||||||
|
} else {
|
||||||
|
queryUrl += "metrics"
|
||||||
|
}
|
||||||
|
return this.http.get<SearchResults>(queryUrl, {params: params});
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
<!--
|
||||||
|
TODO(someone who knows angular) Canvas is still populated when the chart
|
||||||
|
is empty. I'm not sure how to do this.
|
||||||
|
-->
|
||||||
|
<div>
|
||||||
|
<canvas max-height="100%" max-width="100%" id="timeseries">
|
||||||
|
{{timeseries}}
|
||||||
|
</canvas>
|
||||||
|
</div>
|
||||||
@@ -0,0 +1,25 @@
|
|||||||
|
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
|
||||||
|
|
||||||
|
import { TimeseriesComponent } from './timeseries.component';
|
||||||
|
|
||||||
|
describe('TimeseriesComponent', () => {
|
||||||
|
let component: TimeseriesComponent;
|
||||||
|
let fixture: ComponentFixture<TimeseriesComponent>;
|
||||||
|
|
||||||
|
beforeEach(async(() => {
|
||||||
|
TestBed.configureTestingModule({
|
||||||
|
declarations: [ TimeseriesComponent ]
|
||||||
|
})
|
||||||
|
.compileComponents();
|
||||||
|
}));
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
fixture = TestBed.createComponent(TimeseriesComponent);
|
||||||
|
component = fixture.componentInstance;
|
||||||
|
fixture.detectChanges();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should create', () => {
|
||||||
|
expect(component).toBeTruthy();
|
||||||
|
});
|
||||||
|
});
|
||||||
65
internal/tools/ui/src/app/timeseries/timeseries.component.ts
Normal file
65
internal/tools/ui/src/app/timeseries/timeseries.component.ts
Normal file
@@ -0,0 +1,65 @@
|
|||||||
|
import { Chart } from 'chart.js';
|
||||||
|
import { SearchResults } from '../documents';
|
||||||
|
|
||||||
|
import { Component, OnInit } from '@angular/core';
|
||||||
|
import { Subject, Observable } from 'rxjs';
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'app-timeseries',
|
||||||
|
templateUrl: './timeseries.component.html',
|
||||||
|
styleUrls: ['./timeseries.component.css']
|
||||||
|
})
|
||||||
|
export class TimeseriesComponent implements OnInit {
|
||||||
|
timeseries;
|
||||||
|
|
||||||
|
constructor() {}
|
||||||
|
|
||||||
|
ngOnInit() {}
|
||||||
|
|
||||||
|
update(agg: SearchResults.BucketAggregation) {
|
||||||
|
if (this.timeseries) {
|
||||||
|
this.timeseries.destroy();
|
||||||
|
}
|
||||||
|
if (!agg || agg.buckets.length == 0) {
|
||||||
|
this.timeseries = null;
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
let buckets = agg.buckets
|
||||||
|
.filter(bucket => new Date(bucket.key) > new Date(2017, 1));
|
||||||
|
|
||||||
|
let labels = buckets.map(bucket => new Date(bucket.key))
|
||||||
|
let counts = buckets.map(bucket => bucket.count);
|
||||||
|
|
||||||
|
let sum = 0;
|
||||||
|
for (let i = 0; i < counts.length; i++) {
|
||||||
|
sum += counts[i];
|
||||||
|
counts[i] = sum;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.timeseries = new Chart('timeseries', {
|
||||||
|
type: 'line',
|
||||||
|
data: {
|
||||||
|
datasets: [{
|
||||||
|
label: 'Kustomizations Over time',
|
||||||
|
data: counts,
|
||||||
|
type: 'line',
|
||||||
|
pointRadius: 0,
|
||||||
|
lineTension: 0,
|
||||||
|
}],
|
||||||
|
labels: labels,
|
||||||
|
},
|
||||||
|
options: {
|
||||||
|
scales: {
|
||||||
|
xAxes: [{
|
||||||
|
type: 'time',
|
||||||
|
distribution: 'linear',
|
||||||
|
ticks: {
|
||||||
|
autoSkip: true,
|
||||||
|
},
|
||||||
|
}],
|
||||||
|
}
|
||||||
|
},
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
0
internal/tools/ui/src/assets/.gitkeep
Normal file
0
internal/tools/ui/src/assets/.gitkeep
Normal file
3
internal/tools/ui/src/environments/environment.prod.ts
Normal file
3
internal/tools/ui/src/environments/environment.prod.ts
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
export const environment = {
|
||||||
|
production: true
|
||||||
|
};
|
||||||
16
internal/tools/ui/src/environments/environment.ts
Normal file
16
internal/tools/ui/src/environments/environment.ts
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
// 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
|
||||||
|
};
|
||||||
|
|
||||||
|
/*
|
||||||
|
* For easier debugging in development mode, you can import the following file
|
||||||
|
* to ignore zone related error stack frames such as `zone.run`, `zoneDelegate.invokeTask`.
|
||||||
|
*
|
||||||
|
* This import should be commented out in production mode because it will have a negative impact
|
||||||
|
* on performance if an error is thrown.
|
||||||
|
*/
|
||||||
|
// import 'zone.js/dist/zone-error'; // Included with Angular CLI.
|
||||||
BIN
internal/tools/ui/src/favicon.ico
Normal file
BIN
internal/tools/ui/src/favicon.ico
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 5.3 KiB |
16
internal/tools/ui/src/index.html
Normal file
16
internal/tools/ui/src/index.html
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
<!doctype html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="utf-8">
|
||||||
|
<title>Kustomize Search</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/css?family=Roboto:300,400,500" rel="stylesheet">
|
||||||
|
<link href="https://fonts.googleapis.com/icon?family=Material+Icons" rel="stylesheet">
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<app-root></app-root>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
13
internal/tools/ui/src/main.ts
Normal file
13
internal/tools/ui/src/main.ts
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
import 'hammerjs';
|
||||||
|
import { enableProdMode } from '@angular/core';
|
||||||
|
import { platformBrowserDynamic } from '@angular/platform-browser-dynamic';
|
||||||
|
|
||||||
|
import { AppModule } from './app/app.module';
|
||||||
|
import { environment } from './environments/environment';
|
||||||
|
|
||||||
|
if (environment.production) {
|
||||||
|
enableProdMode();
|
||||||
|
}
|
||||||
|
|
||||||
|
platformBrowserDynamic().bootstrapModule(AppModule)
|
||||||
|
.catch(err => console.error(err));
|
||||||
63
internal/tools/ui/src/polyfills.ts
Normal file
63
internal/tools/ui/src/polyfills.ts
Normal file
@@ -0,0 +1,63 @@
|
|||||||
|
/**
|
||||||
|
* 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/guide/browser-support
|
||||||
|
*/
|
||||||
|
|
||||||
|
/***************************************************************************************************
|
||||||
|
* BROWSER POLYFILLS
|
||||||
|
*/
|
||||||
|
|
||||||
|
/** IE10 and IE11 requires the following for NgClass support on SVG elements */
|
||||||
|
// import 'classlist.js'; // Run `npm install --save classlist.js`.
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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
|
||||||
|
* because those flags need to be set before `zone.js` being loaded, and webpack
|
||||||
|
* will put import in the top of bundle, so user need to create a separate file
|
||||||
|
* in this directory (for example: zone-flags.ts), and put the following flags
|
||||||
|
* into that file, and then add the following code before importing zone.js.
|
||||||
|
* import './zone-flags.ts';
|
||||||
|
*
|
||||||
|
* The flags allowed in zone-flags.ts are listed here.
|
||||||
|
*
|
||||||
|
* The following flags will work for all browsers.
|
||||||
|
*
|
||||||
|
* (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__UNPATCHED_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
|
||||||
|
*/
|
||||||
36
internal/tools/ui/src/styles.css
Normal file
36
internal/tools/ui/src/styles.css
Normal file
@@ -0,0 +1,36 @@
|
|||||||
|
/* You can add global styles to this file, and also import other style files */
|
||||||
|
@import '~@angular/material/prebuilt-themes/deeppurple-amber.css';
|
||||||
|
html, body { height: 100%; }
|
||||||
|
|
||||||
|
body {
|
||||||
|
font-family: Roboto, "Helvetica Neue", sans-serif;
|
||||||
|
font-style: normal;
|
||||||
|
max-width: 800px;
|
||||||
|
margin-right: auto;
|
||||||
|
margin-left: auto;
|
||||||
|
padding-left: 16px;
|
||||||
|
padding-right: 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
h1, .h1, h2, .h2, h3, .h3 {
|
||||||
|
font-style: normal;
|
||||||
|
font-weight: 400;
|
||||||
|
margin-top: 20px;
|
||||||
|
margin-bottom: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
pre {
|
||||||
|
overflow: auto;
|
||||||
|
padding: 10px;
|
||||||
|
background-color: #f0f0f0;
|
||||||
|
border-radius: 4px;
|
||||||
|
border: 1px solid #ddd;
|
||||||
|
}
|
||||||
|
|
||||||
|
mat-expansion-panel.result {
|
||||||
|
margin-top: 8px;
|
||||||
|
margin-bottom: 8px;
|
||||||
|
}
|
||||||
|
.mat-expansion-panel-header {
|
||||||
|
padding: 20px;
|
||||||
|
}
|
||||||
20
internal/tools/ui/src/test.ts
Normal file
20
internal/tools/ui/src/test.ts
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
// This file is required by karma.conf.js and loads recursively all the .spec and framework files
|
||||||
|
|
||||||
|
import 'zone.js/dist/zone-testing';
|
||||||
|
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);
|
||||||
14
internal/tools/ui/tsconfig.app.json
Normal file
14
internal/tools/ui/tsconfig.app.json
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
{
|
||||||
|
"extends": "./tsconfig.json",
|
||||||
|
"compilerOptions": {
|
||||||
|
"outDir": "./out-tsc/app",
|
||||||
|
"types": []
|
||||||
|
},
|
||||||
|
"include": [
|
||||||
|
"src/**/*.ts"
|
||||||
|
],
|
||||||
|
"exclude": [
|
||||||
|
"src/test.ts",
|
||||||
|
"src/**/*.spec.ts"
|
||||||
|
]
|
||||||
|
}
|
||||||
26
internal/tools/ui/tsconfig.json
Normal file
26
internal/tools/ui/tsconfig.json
Normal file
@@ -0,0 +1,26 @@
|
|||||||
|
{
|
||||||
|
"compileOnSave": false,
|
||||||
|
"compilerOptions": {
|
||||||
|
"baseUrl": "./",
|
||||||
|
"outDir": "./dist/out-tsc",
|
||||||
|
"sourceMap": true,
|
||||||
|
"declaration": false,
|
||||||
|
"downlevelIteration": true,
|
||||||
|
"experimentalDecorators": true,
|
||||||
|
"module": "esnext",
|
||||||
|
"moduleResolution": "node",
|
||||||
|
"importHelpers": true,
|
||||||
|
"target": "es2015",
|
||||||
|
"typeRoots": [
|
||||||
|
"node_modules/@types"
|
||||||
|
],
|
||||||
|
"lib": [
|
||||||
|
"es2018",
|
||||||
|
"dom"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"angularCompilerOptions": {
|
||||||
|
"fullTemplateTypeCheck": true,
|
||||||
|
"strictInjectionParameters": true
|
||||||
|
}
|
||||||
|
}
|
||||||
18
internal/tools/ui/tsconfig.spec.json
Normal file
18
internal/tools/ui/tsconfig.spec.json
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
{
|
||||||
|
"extends": "./tsconfig.json",
|
||||||
|
"compilerOptions": {
|
||||||
|
"outDir": "./out-tsc/spec",
|
||||||
|
"types": [
|
||||||
|
"jasmine",
|
||||||
|
"node"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"files": [
|
||||||
|
"src/test.ts",
|
||||||
|
"src/polyfills.ts"
|
||||||
|
],
|
||||||
|
"include": [
|
||||||
|
"src/**/*.spec.ts",
|
||||||
|
"src/**/*.d.ts"
|
||||||
|
]
|
||||||
|
}
|
||||||
92
internal/tools/ui/tslint.json
Normal file
92
internal/tools/ui/tslint.json
Normal file
@@ -0,0 +1,92 @@
|
|||||||
|
{
|
||||||
|
"extends": "tslint:recommended",
|
||||||
|
"rules": {
|
||||||
|
"array-type": false,
|
||||||
|
"arrow-parens": false,
|
||||||
|
"deprecation": {
|
||||||
|
"severity": "warning"
|
||||||
|
},
|
||||||
|
"component-class-suffix": true,
|
||||||
|
"contextual-lifecycle": true,
|
||||||
|
"directive-class-suffix": true,
|
||||||
|
"directive-selector": [
|
||||||
|
true,
|
||||||
|
"attribute",
|
||||||
|
"app",
|
||||||
|
"camelCase"
|
||||||
|
],
|
||||||
|
"component-selector": [
|
||||||
|
true,
|
||||||
|
"element",
|
||||||
|
"app",
|
||||||
|
"kebab-case"
|
||||||
|
],
|
||||||
|
"import-blacklist": [
|
||||||
|
true,
|
||||||
|
"rxjs/Rx"
|
||||||
|
],
|
||||||
|
"interface-name": false,
|
||||||
|
"max-classes-per-file": false,
|
||||||
|
"max-line-length": [
|
||||||
|
true,
|
||||||
|
140
|
||||||
|
],
|
||||||
|
"member-access": false,
|
||||||
|
"member-ordering": [
|
||||||
|
true,
|
||||||
|
{
|
||||||
|
"order": [
|
||||||
|
"static-field",
|
||||||
|
"instance-field",
|
||||||
|
"static-method",
|
||||||
|
"instance-method"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"no-consecutive-blank-lines": false,
|
||||||
|
"no-console": [
|
||||||
|
true,
|
||||||
|
"debug",
|
||||||
|
"info",
|
||||||
|
"time",
|
||||||
|
"timeEnd",
|
||||||
|
"trace"
|
||||||
|
],
|
||||||
|
"no-empty": false,
|
||||||
|
"no-inferrable-types": [
|
||||||
|
true,
|
||||||
|
"ignore-params"
|
||||||
|
],
|
||||||
|
"no-non-null-assertion": true,
|
||||||
|
"no-redundant-jsdoc": true,
|
||||||
|
"no-switch-case-fall-through": true,
|
||||||
|
"no-use-before-declare": true,
|
||||||
|
"no-var-requires": false,
|
||||||
|
"object-literal-key-quotes": [
|
||||||
|
true,
|
||||||
|
"as-needed"
|
||||||
|
],
|
||||||
|
"object-literal-sort-keys": false,
|
||||||
|
"ordered-imports": false,
|
||||||
|
"quotemark": [
|
||||||
|
true,
|
||||||
|
"single"
|
||||||
|
],
|
||||||
|
"trailing-comma": false,
|
||||||
|
"no-conflicting-lifecycle": true,
|
||||||
|
"no-host-metadata-property": true,
|
||||||
|
"no-input-rename": true,
|
||||||
|
"no-inputs-metadata-property": true,
|
||||||
|
"no-output-native": true,
|
||||||
|
"no-output-on-prefix": true,
|
||||||
|
"no-output-rename": true,
|
||||||
|
"no-outputs-metadata-property": true,
|
||||||
|
"template-banana-in-box": true,
|
||||||
|
"template-no-negated-async": true,
|
||||||
|
"use-lifecycle-interface": true,
|
||||||
|
"use-pipe-transform-interface": true
|
||||||
|
},
|
||||||
|
"rulesDirectory": [
|
||||||
|
"codelyzer"
|
||||||
|
]
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user