diff --git a/docs/pull_request_template.md b/docs/pull_request_template.md index 11568562b2..e651b7af6d 100644 --- a/docs/pull_request_template.md +++ b/docs/pull_request_template.md @@ -1,9 +1,3 @@ ---- -id: pull_request_template -title: Pull request template for contributions to Stratos -sidebar_label: PR template ---- - ## Description diff --git a/website/docs/developer/developers-guide-env-tech.md b/website/docs/developer/developers-guide-env-tech.md index 76918f9fba..0b374c6c01 100644 --- a/website/docs/developer/developers-guide-env-tech.md +++ b/website/docs/developer/developers-guide-env-tech.md @@ -1,40 +1,42 @@ --- id: developers-guide-env-tech -title: Stratos Tech + Developer Environment -sidebar_label: Dev Environment +title: Developer Links + Environment +sidebar_label: Dev Links & Env --- -## ES6 +## Links + +Below is a collection of links that are can be useful when tackling some of the technology involved with Stratos + +### ES6 * [http://stack.formidable.com/es6-interactive-guide](http://stack.formidable.com/es6-interactive-guide) * [http://es6-features.org](http://es6-features.org) -## TypeScript +### TypeScript -* [https://www.sitepen.com/blog/2013/12/31/definitive-guide-to-typescript/](https://www.sitepen.com/blog/2013/12/31/definitive-guide-to-typescript/) * [https://learnxinyminutes.com/docs/typescript/](https://learnxinyminutes.com/docs/typescript/) (cheat sheet) * [https://www.tutorialspoint.com/typescript/](https://www.tutorialspoint.com/typescript/) * [https://tutorialzine.com/2016/07/learn-typescript-in-30-minutes](https://tutorialzine.com/2016/07/learn-typescript-in-30-minutes) -* [https://www.sitepen.com/blog/2013/12/31/typescript-cheat-sheet/](https://www.sitepen.com/blog/2013/12/31/typescript-cheat-sheet/) (advanced cheat sheet) - -## Angular ### Angular +#### Angular + * [https://www.youtube.com/watch?v=KhzGSHNhnbI](https://www.youtube.com/watch?v=KhzGSHNhnbI) (very basic 1h video of angular covering basic app features in a demo) * [https://angular.io/guide/architecture](https://angular.io/guide/architecture) (official angular intro) * [https://angular.io/tutorial](https://angular.io/tutorial) (official angular tutorial. Basic to start with but good introduction to routing, http requests, promises, observables and observable event stream later on) -### Angular CLI +#### Angular CLI * [https://cli.angular.io/reference.pdf](https://cli.angular.io/reference.pdf) (cheat sheet) * [https://github.com/angular/angular-cli](https://github.com/angular/angular-cli) -### Example Apps +#### Example Apps * https://github.com/aviabird/angularspree (covers everything) -## Redux +### Redux * [http://redux.js.org](http://redux.js.org) * [https://gist.github.com/btroncone/a6e4347326749f938510](https://gist.github.com/btroncone/a6e4347326749f938510) @@ -42,27 +44,25 @@ sidebar_label: Dev Environment * [https://www.youtube.com/watch?v=WIqbzHdEPVM](https://www.youtube.com/watch?v=WIqbzHdEPVM) * [https://egghead.io/courses/getting-started-with-redux](https://egghead.io/courses/getting-started-with-redux) -### Observables +#### Observables * [http://reactivex.io/documentation/observable.html](http://reactivex.io/documentation/observable.html) ## VS Code Plug-ins -There's no mandated IDE, but if you choose VS Code here's some helpful plug-ins +There's no mandated IDE, but if you choose Visual Studio Code here's some helpful plug-ins -### Super Helpful +### Really Helpful * TSLint -* SassLint * TS Hero -* Beautify +* Git Lens * gitignore +* Move TS - Move files and automatically update imports ### Helpful -* Angular 2+ Snippets -* Angular v4 TypeScript Snippets -* Git Lens +* Beautify * Document This * Git History * TODO Parser @@ -76,122 +76,10 @@ There's no mandated IDE, but if you choose VS Code here's some helpful plug-ins ``` * F1 + `Parse TODOs (all files)` to see all TODOs -* Move TS - Move files and automatically update imports - -### Of Interest - -* Eclipse Keymap * Code Spell Checker * List of words to exclude coming soon +* Eclipse Keymap -Example settings - -``` -{ - "gitlens.blame.line.enabled": false, - "gitlens.codeLens.recentChange.enabled": false, - "gitlens.codeLens.authors.enabled": false, - "gitlens.blame.highlight.locations": [ - "gutter", - "overview" - ], - "gitlens.currentLine.enabled": false, - "gitlens.hovers.currentLine.enabled": false, - "editor.fontSize": 12, - "editor.formatOnSave": true, - "editor.rulers": [ - 140 - ], - "editor.renderLineHighlight": "none", - "search.exclude": { - "node_modules": true, - "**/node_modules": true, - "**/bower_components": true, - "coverage": true, - "components/*/backend/vendor": true, - "*.orig": true - }, - "search.useIgnoreFilesByDefault": true, - "tslint.validateWithDefaultConfig": true, - "tslint.configFile": "tslint.json", - "tslint.autoFixOnSave": true, - "tslint.alwaysShowStatus": true, - "tslint.alwaysShowRuleFailuresAsWarnings": true, - "explorer.autoReveal": false, - "extensions.ignoreRecommendations": false, - "TodoParser": { - "folderExclude": [ - "node_modules", - ".vscode" - ] - }, - "cSpell.userWords": [ - "Guids", - "action", - "api", - "cnsi", - "cnsis", - "confirmation", - "dialog", - "falsies", - "guid", - "iapi", - "initialise", - "initialised", - "injectable", - "memberof", - "ngrx", - "normalizr", - "strat", - "unsubscribe", - "vars" - ], - "cSpell.language": ",en-GB", - "cSpell.enabledLanguageIds": [ - "c", - "css", - "cpp", - "csharp", - "fonts", - "go", - "handlebars", - "javascript", - "javascriptreact", - "json", - "latex", - "markdown", - "php", - "plaintext", - "python", - "restructuredtext", - "text", - "typescript", - "typescriptreact", - "yml", - "html" - ], - "files.exclude": { - "**/.git": true, - "**/.svn": true, - "**/.hg": true, - "**/CVS": true, - "**/.DS_Store": true, - "**/tmp": true, - "**/node_modules": true, - "**/bower_components": true, - "**/dist": true, - "**/.orig": true - }, - "files.watcherExclude": { - "**/.git/objects/**": true, - "**/.git/subtree-cache/**": true, - "**/node_modules/**": true, - "**/tmp/**": true, - "**/bower_components/**": true, - "**/dist/**": true - }, -} -``` ## Guides diff --git a/website/docs/developer/frontend.md b/website/docs/developer/frontend.md index 17c1e7b037..572dd2032a 100644 --- a/website/docs/developer/frontend.md +++ b/website/docs/developer/frontend.md @@ -69,5 +69,5 @@ Configuration information can be found in two places * Informs the frontend where the backend is * `./src/frontend/packages/core/src/environments/environment.ts` for developer vs production like config * This contains more general settings for the frontend and does not usually need to be changed -* `config.properies` +* `config.properties` * Backend configuration diff --git a/website/docs/extensions/frontend.md b/website/docs/extensions/frontend.md index a253e906ef..6bed192aae 100644 --- a/website/docs/extensions/frontend.md +++ b/website/docs/extensions/frontend.md @@ -3,20 +3,42 @@ title: Frontend Extensions sidebar_label: Frontend Extensions --- -An example illustrating the various front-end extension points of Stratos is included in the folder `examples/custom-src`. +Stratos exposes the following extension points: -To include the customizations in this example, either copy or symlink the `examples/custom-src` to `custom-src` at the top-level of the Stratos repository. +- Adding new items to the side navigation menu +- Adding new tabs to the Application, Cloud Foundry, Organization and Space views +- Adding new action buttons to the Application Wall, Application, Cloud Foundry, Organization and Space and Endpoint views +- Replace the loading page +- Replace the login page -Next, run the customization script (this is done automatically when you do an `npm install`) with: +We use Decorators to annotate components to indicate that they are Stratos extensions. -``` -npm run customize -``` +An example illustrating the various front-end extension points of Stratos is included in the folder `src/frontend/packages/example-extensions`. -You can now run Stratos locally to see the customizations - see the [Developer's Guide](../developer/frontend) for details. +To run Stratos with these customizations see [here](/docs/extensions/introduction#acme). For a walk-through of extending Stratos, see [Example: Adding a Custom Tab](#example-adding-a-custom-tab). +## Including modules and routes +To include code and angular routing in your component there needs to be two access points +- A core module that declares your extension components. +- A core routing module that calls `RouterModule.forRoot`. This is the root for all routes in your package. + +These modules should be made available externally to Stratos by the following steps + +1. Exported in the package's `public-api.ts`, for example `src/frontend/packages/example-extensions/src/public-api.ts`. The public api should be added to the applications `tsconfig.json` for example `src/tsconfig.json` +1. Reference as, or imported by, the two modules defined in the `stratos` section in the package's `package.json`, for example `src/frontend/packages/example-extensions/package.json` + ``` + "stratos": { + ... + "module": "ExampleModule", + "routingModule": "ExampleRoutingModule" + ... + } + ``` + +At build time these are then added to a dynamically created module `src/frontend/packages/core/src/_custom-import.module.ts` and included in the output. + ## Extension Points ### Side Navigation @@ -25,9 +47,9 @@ New items can be added to the Side Navigation menu with extensions. To do so, annotate the routes for your extension with custom metadata, which Stratos will then pick up and add to the side menu. -A full example is in the folder `examples/custom-src/frontend/app/custom/nav-extension`. +A full example is in `src/frontend/packages/example-extensions/src/example-routing.module.ts` and `src/frontend/packages/example-extensions/src/nav-extension`. -Your route should have the following metadata in the `data` field: +Your route should have the following metadata in the `data` field of the route: ``` stratosNavigation: { @@ -38,17 +60,17 @@ Your route should have the following metadata in the `data` field: Where `` is the text label to show in the side navigation and `<ICON NAME>` is the icon to use. -You should place your route declaration in a module named `CustomRoutingModule` in the file `custom-src/frontend/app/custom/custom-routing.module.ts`. +> The routing module must be, or referenced by, the core routing module as described [above](/docs/extensions/frontend#including-modules-and-routes) An example routing module would be: ``` import { NgModule } from '@angular/core'; -import { Routes, RouterModule } from '@angular/router'; +import { RouterModule, Routes } from '@angular/router'; const customRoutes: Routes = [{ path: 'example', - loadChildren: 'app/custom/nav-extension/nav-extension.module#NavExtensionModule', + loadChildren: () => import('./nav-extension/nav-extension.module').then(m => m.NavExtensionModule), data: { stratosNavigation: { text: 'Example', @@ -63,10 +85,12 @@ const customRoutes: Routes = [{ ], declarations: [] }) -export class CustomRoutingModule { } +export class ExampleRoutingModule { } + ``` -This approach ensures that the Angular compiler creates a separate chunk for the extension at compile time. +This approach ensures that the Angular compiler creates a separate chunk for the extension at compile time which can be lazy loaded at run +time. ### Custom Tabs @@ -77,6 +101,8 @@ Tabs can be added to the following views in Stratos: - The Cloud Foundry Org view that shows detail for a Cloud Foundry organization - The Cloud Foundry Space view that shows detail for a Cloud Foundry space +A step by step guide on how to create a custom tab can be found [below](/docs/extensions/frontend#example-adding-a-custom-tab). + For example: ![Example Application tab extension](../../images/extensions/app-tab-example.png) @@ -84,28 +110,34 @@ For example: The approach for all of these is the same: 1. Create a new component that will provide the tab contents -2. Ensure that your component is included in the `EntryComponent` section of your custom module -2. Decorate the component with the `StratosTab` decorator, for example: - -``` -@StratosTab({ - type: StratosTabType.Application, - label: '<LABEL>', - link: '<LINK>' -}) -``` - -Where: - -- < TYPE > indicates where the tab should appear and can be: - - StratosTabType.Application - Application View - - StratosTabType.CloudFoundry - Cloud Foundry view - - StratosTabType.CloudFoundryOrg - Cloud Foundry Org view - - StratosTabType.CloudFoundrySpace - Cloud Foundry Space view -- < LABEL > is the text label to use for the tab -- < LINK > is the name to use for the route (this must only contain characters permitted in URLs) - -An example is included in the file `examples/custom-src/frontend/app/custom/app-tab-extension`. +1. Ensure that your component is included in the `EntryComponent` section of your custom module +1. Decorate the component with the `StratosTab` decorator, for example: + ``` + @StratosTab({ + icon: 'extension', + type: StratosTabType.Application, + label: 'Example App Tab', + link: 'example' + }) + ``` + Where: + - < ICON > is the material design icon name for the icon + - < TYPE > indicates where the tab should appear and can be: + - StratosTabType.Application - Application View + - StratosTabType.CloudFoundry - Cloud Foundry view + - StratosTabType.CloudFoundryOrg - Cloud Foundry Org view + - StratosTabType.CloudFoundrySpace - Cloud Foundry Space view + - < LABEL > is the text label to use for the tab + - < LINK > is the name to use for the route (this must only contain characters permitted in URLs) + An example is included in the file `src/frontend/packages/example-extensions/src/app-tab-extension/app-tab-extension.component.ts`. +1. Declare the component to avoid Angular tree shaking + - In the same module that the component is 'declared' in add the following to `imports` + ``` + ExtensionService.declare([ + <component name>, + ]) + ``` +> The module referencing the component, or another referencing it, must be imported by the core module as described [above](/docs/extensions/frontend#including-modules-and-routes). ### Custom Actions @@ -125,92 +157,239 @@ An action is a icon button that appears at the top-right of a View. For example: The approach for all of these is the same: 1. Create a new component that will provide the contents to show when the action is clicked -2. Ensure that your component is included in the `EntryComponent` section of your custom module -2. Decorate the component with the `StratosAction` decorator, for example: +1. Ensure that your component is included in the `EntryComponent` section of your custom module +1. Decorate the component with the `StratosAction` decorator, for example: + ``` + @StratosAction({ + type: StratosActionType.Applications, + label: '<LABEL>', + link: '<LINK>', + icon: '<ICON> + }) + ``` + Where: + - < TYPE > indicates where the action should appear and can be: + - StratosActionType.Applications - Application Wall View + - StratosActionType.Application - Application View + - StratosActionType.CloudFoundry - Cloud Foundry view + - StratosActionType.CloudFoundryOrg - Cloud Foundry Org view + - StratosActionType.CloudFoundrySpace - Cloud Foundry Space view + - StratosActionType.Endpoints - Endpoints view + - < ICON > is the icon to show + - < LABEL > is the text label to use for the tooltip of the icon (optional) + - < LINK > is the name to use for the route (this must only contain characters permitted in URLs) + An example is included in the file `src/frontend/packages/example-extensions/src/app-action-extension/app-action-extension.component.ts`. +1. Declare the component to avoid Angular tree shaking + - In the same module that the component is 'declared' in add the following to `imports`. + ``` + ExtensionService.declare([ + <component name>, + ]) + ``` + +> The module referencing the component, or another referencing it, must be imported by the core module as described in [above](/docs/extensions/frontend#including-modules-and-routes) + +### Loading Indicator + +On slower connections, it can take a few seconds to load the main Javascript resources for Stratos. + +In order to give the user some initial feedback that Stratos is loading, a loading indicator is included in the `index.html` file. This gets shown as early as possible, as soon as this main html file has loaded. Once the main code has been fetched, the view refreshes to show the application. + +A default loading indicator is provided that can be changed. To do so, create the following two in your extension or theme package: + +- `loading.css` - CSS styles to be included in a style block in the head of the index page +- `loading.html` - HTML markup to be included the the index page to render the loading indicator + +Then reference them to your package's `package.json` ``` -@StratosAction({ - type: StratosActionType.Applications, - label: '<LABEL>', - link: '<LINK>', - icon: '<ICON> -}) + "stratos": { + ... + "theme": { + "loadingCss": "loader/loading.css", + "loadingHtml": "loader/loading.html" + } + ... + }, ``` -Where: - -- < TYPE > indicates where the action should appear and can be: - - StratosActionType.Applications - Application Wall View - - StratosActionType.Application - Application View - - StratosActionType.CloudFoundry - Cloud Foundry view - - StratosActionType.CloudFoundryOrg - Cloud Foundry Org view - - StratosActionType.CloudFoundrySpace - Cloud Foundry Space view - - StratosActionType.Endpoints - Endpoints view -- < ICON > is the icon to show -- < LABEL > is the text label to use for the tooltip of the icon (optional) -- < LINK > is the name to use for the route (this must only contain characters permitted in URLs) - -An example is included in the file `examples/custom-src/frontend/app/custom/app-action-extension`. - -## Example: Adding a Custom Tab - -In this example, we will walk through extending the Stratos front-end. - -This walk-through assumes that you have installed the Angular CLI globally - this can be done with `npm install -g @angular/cli`. - -### Create a new module - -First, create the custom-src folder structure - from the top-level of the Stratos repository run: +The files for the default indicator can be found in the `src/frontend/packages/theme/loader` folder. + +An example of a different loading indicator is included with the ACME sample in `src/frontend/packages/example-theme/loader`. + +The customization task will insert the appropriate CSS and HTML files into the main index.html file when it runs. + +Take a look at the template for the `index.html` file in `src/frontend/packages/core/misc/custom/index.html`. The CSS file is inserted where the marker `/** @@LOADING_CSS@@ **/` is and the HTML file where `<!-- @@LOADING_HTML@@ -->` is. + +### Login Page + +The log in page can be replaced by another Angular component. This can extend the original log in component and provide the same functionality, +see `src/frontend/packages/example-extensions/src/acme-login`. + +1. Create a new log in component that will contain the same form and fields. The component should have the decorator `@StratosLoginComponent()` + ``` + @StratosLoginComponent() + @Component({ + selector: 'app-acme-login', + templateUrl: './acme-login.component.html', + styleUrls: ['./acme-login.component.scss'], + encapsulation: ViewEncapsulation.None + }) + export class AcmeLoginComponent extends LoginPageComponent { + ... + ``` +1. The new component should be declared and set as an entry point, as well as imported via the extension service, in a module + ``` + imports: [ + ... + ExtensionService.declare([ + AcmeLoginComponent, + ]) + ... + ], + // FIXME: Ensure that anything lazy loaded/in kube endpoint pages is not included here - #3675 + declarations: [ + ... + AcmeLoginComponent, + ... + ], + entryComponents: [ + ... + AcmeLoginComponent, + ... + ] + }) + ``` + +### Other Points + +#### Customization Service +A customization service provides a number of smaller extension points. + +|Property | Description| +|--|--| +|hasEula| True if there's a EULA to show. When set to true the asset `/core/eula.html` must exist. For information about custom package assets see the images section [here](/docs/extensions/theming#new-images). | +|copyright| Text shown at the bottom of the side nav| +|logoText| Text shown with the side nav logo| +|aboutInfoComponent| Replace the component used in the Stratos `About` page| +|supportInfoComponent| Replace the component used to provide support information int he Stratos `About` page| +|noEndpointsComponent| Replace the component used in the Endpoints page when there are no registered endpoints| +|alwaysShowNavForEndpointTypes| True to always show the side nav menu items even if an Endpoint for that type is not connected. For example set to `false` to hide CF based menu items such as `Application` if no CF is connected| + +To utilize these define them in a `CustomizationsMetadata` object and apply them using the Angular service `CustomizationService` ``` -mkdir -p custom-src/frontend/app/custom -mkdir -p custom-src/frontend/assets/custom + constructor(cs: CustomizationService) { + const customizations: CustomizationsMetadata = { + copyright: '© 2020 Me', + hasEula: true, + aboutInfoComponent: MyAboutInfoComponent, + noEndpointsComponent: MyWelcomeComponent, + alwaysShowNavForEndpointTypes: (typ) => false, + } + cs.set(customizations); + } + ``` -Next, run the customize task: +#### stratos.yaml +A few 'higher up' extension points can be found in `./stratos.yaml`. For example the [SUSE stratos.yaml](https://github.com/SUSE/stratos/blob/master/stratos.yaml) is below. ``` -npm run customize +title: SUSE Stratos Console +productVersion: 2.0.0 ``` -This will symlink our custom folder into the Stratos application source folder. +|Property|Description| +|--|--| +|title| Official product title, shown in `About` page and other custom places| +|productVersion| Use when building `helm` charts| -Next, use the Angular CLI to create the root module for our custom code with: -``` -ng generate module custom -``` +## Example: Adding a Custom Tab -This create a new Angular module `CustomModule`. +In this example, we will walk through extending the Stratos front-end. A new tab will be added to the Cloud Foundry Application page. -Run the customize script again, now that that we have created the custom module with: +This walk-through assumes that you have installed the Angular CLI globally - this can be done with `npm install -g @angular/cli`. -``` -npm run customize -``` +### Create a new extensions package + +> Extension packages can contain many components and even a theme. The example here assumes a fresh start so a new package must be created + +1. Create the directory + ``` + mdkir src/frontend/packages/my-custom-module + ``` +1. Create an angular module in `my-custom-module` called `my-example.module.ts` + ``` + import { CommonModule } from '@angular/common'; + import { NgModule } from '@angular/core'; + + @NgModule({ + imports: [ + CommonModule + ], + declarations: [ + ] + }) + export class MyExampleModule { } + ``` +1. Create a `public-api.ts` in `my-custom-module` and reference it in your applications `tsconfig.json`. + + `public-api.ts` + ``` + export * from './my-example.module'; + ``` + `tsconfig.json` + ``` + "paths": { + ... + "@myexamples/extensions": ["frontend/packages/my-custom-module/public-api.ts"] + .... + } + ``` + +1. Create a `package.json` in `my-custom-module` + ``` + { + "name": "@myexamples/extensions", + "version": "0.0.1", + "peerDependencies": { + "@angular/common": "^6.0.0-rc.0 || ^6.0.0", + "@angular/core": "^6.0.0-rc.0 || ^6.0.0" + }, + "stratos": { + "module": "MyExampleModule" + } + } + ``` ### Create a new Component for our Tab Create a new Angular component with the CLI: ``` -ng generate component custom/example-tab-extension +cd src/frontend/packages/my-custom-module +ng generate component example-tab-extension ``` +This will automatically declare the component in `MyExampleModule` + ### Add Decorator to make this Component an Extension In a text editor, open the file: ``` -src/frontend/app/custom/example-tab-extension/example-tab-extension.component.ts +src/frontend/packages/my-custom-module/example-tab-extension/example-tab-extension.component.ts ``` Add the following decorator to the component at the top of the file: ``` -import { StratosTab, StratosTabType } from '../../core/extension/extension-service'; +import { StratosTab, StratosTabType } from '@stratosui/core'; @StratosTab({ + icon: 'done', type: StratosTabType.Application, label: 'Example App Tab', link: 'example' @@ -221,9 +400,10 @@ The file should now look like this: ``` import { Component, OnInit } from '@angular/core'; -import { StratosTab, StratosTabType } from '../../core/extension/extension-service'; +import { StratosTab, StratosTabType } from '@stratosui/core'; @StratosTab({ + icon: 'done', type: StratosTabType.Application, label: 'Example App Tab', link: 'example' @@ -237,7 +417,7 @@ export class ExampleTabExtensionComponent implements OnInit { constructor() { } - ngOnInit() { + ngOnInit(): void { } } @@ -245,27 +425,35 @@ export class ExampleTabExtensionComponent implements OnInit { Save the file. +### Update the module -### Mark the component as an entry component - -The last thing we need to do is to mark our Extension component as an entry component. +The component must now be marked as an entry component and imported in such a way angular tree shaking is avoided. -To do this, in a text editor, open the file `src/frontend/app/custom/custom.module.ts` and add the entry components section so it looks like this: +To do this, in a text editor, open the file `src/frontend/packages/my-custom-module/my-example.module.ts` update +- the file imports section +- the module import array +- the entry component array ``` +import { CommonModule } from '@angular/common'; +import { NgModule } from '@angular/core'; +import { ExtensionService } from '@stratosui/core'; + +import { ExampleTabExtensionComponent } from './example-tab-extension/example-tab-extension.component'; + @NgModule({ imports: [ - CommonModule + CommonModule, + ExtensionService.declare([ + ExampleTabExtensionComponent, + ]) ], declarations: [ExampleTabExtensionComponent], entryComponents: [ExampleTabExtensionComponent] }) -export class CustomModule { } +export class MyExampleModule { } ``` ### Run it -You should now be able to run Stratos locally and see this new tab on the application page for an application - as illustrated below: - -![Example tab extension](../../images/extensions/tab-example.png) - +You should now be able to run Stratos [locally](/docs/developer/introduction) and see this new tab on the application page for an application.