ng-etg-base
v1.3.0
Published
ng-etg-base is a package for ETG Angular applications that contains a variety of base implementations for commonly used modules
Downloads
12
Readme
Angular ETG Base
Overview
ng-etg-base is a package for ETG Angular applications that contains a variety of base implementations for commonly used modules
Modules currently included:
Generic API Service
Authentication Services
- OpenIddict
- AWS Cognito
- AuthInterceptor
- ErrorInterceptor
Alert Service
Versions & Change Logs
Version 0.2.0
- Added auth config to AuthVars that is used by auth subjects to configure endpoints.
- Updated required auth config values to OpenIddictService.
- Added auth interceptor blacklist to AuthVars to allow specified values to be ignored when adding the access token to outgoing requests.
- Updated AuthInterceptor to use the blacklist to determine if the token should be added.
Version 0.1.0
Added AlertService.
Fixed AuthInterceptor to properly append the access token to outgoing requests.
Installation
Using npm:
$ npm i --save ng-etg-base
Peer Dependencies
In order for this package to work, you must have the following peer dependencies installed in your application:
"@angular/common": "^7.2.0"
"@angular/core": "^7.2.0"
"sweetalert2": "^8.8.3"
"jwt-decode": "^2.2.0"
"amazon-cognito-identity-js": "^3.0.7"
"aws-sdk": "^2.418.0"
API Module
The API module contains a generic response service to be used as the base for all API services that are meant to interact with the ETG .NET API. This generic response service contains CRUD functions pre-built to call our backend API and process the response in an easy-to-use way. By being able to specify the return object type for each service, the GenericResponseService will automatically convert all response data from the payload to the correct class model when it is returned.
Generic Response API
Implementation
Start by creating an empty Angular service.
Use the Angular generate command:
$ ng generate service <NAME_OF_SERVICE>
Or use the following code:
import { Injectable } from '@angular/core';
@Injectable({
providedIn: 'root',
})
export class UserService {
constructor() {
}
}
Import the GenericResponseService at the top of your empty service. An HttpClient will be required for the GenericResponseService, so import that as well.
...
import { HttpClient } from "@angular/common/http";
import { GenericResponseService } from 'ng-etg-base';
...
Create a class model to represent the objects being returned from the API that this service will be interacting with. For example, if using REST endpoints that manipulate user data ("API_URL/api/v1/Users"), create a User model that reflects that state of the models used on the backend.
Example:
export class User {
id: number;
email: string;
name: string;
tokens: UserTokens;
}
Extend your service with the GenericResponseService. Since the GenericResponseService is setup using generics, the response class type must be set when extending your service. Fill in 'CLASS_TYPE_HERE' with the name of the class model you created previously (example: User).
In the constructor, pass an injected HttpClient, API URL, and an endpoint string. In the User example, the API URL is "http://localhost:5000/api/v1" and the endpoint string would be "Users".
NOTE: The API URL can and should be set via an environment config file. In such a case, use the value from the config instead of "http://localhost:5000/api/v1".
...
@Injectable({
providedIn: 'root',
})
export class UserService extends GenericResponseService<CLASS_TYPE_HERE> {
constructor(
httpClient: HttpClient
) {
super(httpClient, "http://localhost:5000/api/v1", "Users");
}
...
}
The full service is as follows:
import { Injectable } from '@angular/core';
import { HttpClient } from "@angular/common/http";
import { User } from "../models/user.model";
import { GenericResponseService } from 'ng-etg-base';
@Injectable({
providedIn: 'root',
})
export class UserService extends GenericResponseService<User> {
constructor(
httpClient: HttpClient
) {
super(httpClient, "http://localhost:5000/api/v1", "Users");
}
}
Usage
With a service setup to use the GenericResponseService, it can be used simply by calling the CRUD functions included in the service.
In another component, import your new service and inject it into the component constructor (NOTE: Replace 'UserService' and '../service/user.service' with your service name and path respectively).
...
import { UserService } from '../service/user.service';
@Component({
selector: 'component-selector',
templateUrl: 'component-template.html',
styleUrls: ['./component-styles.scss']
})
export class TestComponent {
constructor(
private userService: UserService
) { }
...
}
Call a function on the service based on the CRUD operation you wish to perform and interact with the response in the observables subscribe callback. For example, to retrieve a list of all users from our UserService:
this.userService.list().subscribe(
(users) => {
// Do something with the list of users here...
},
(err) => {
// Handle error here...
}
);
Auth Module
The Auth module included in ng-etg-base is built abstractly using a proxy pattern to allow different authentication methods to be used and swapped in/out easily. The ETGAuthService provides a top-level interface to be interacted with in all applications. The service implements a common AuthSubject interface with various auth functions such as login, logout, getAccessToken, etc. All individual auth subjects, such as OpenIddict or AWSCognito, also implement the same interface to provide common functionality with different implementations. The auth proxy combined with the AuthVars service allows authentication to be easily inserted into an application without muddling with the implementation details per-project.
Setup
To start using the AuthService, you need to import the ETGAuthService and configure a few values in the AuthVar service.
Project Auth Service
It is suggested to create an auth service of your own within your project so your application-specific pages don't interact directly with the ng-etg-base auth services. Start by creating an empty auth service of your own.
import { Injectable } from '@angular/core';
@Injectable({
providedIn: 'root',
})
export class AuthService {
constructor(
) { }
}
ETGAuthService Integration
Once the empty service is created, you can start integrating it with the ETGAuthService. To do so, begin by importing the ETGAuthService and injecting into your service constructor.
With the ETGAuthService injected, you also need to tell the service which authentication subject to use. Authentication subjects represent the various different authentication methods with their own implementations of the AuthSubject interface. For example, OpenIddictService and CognitoService are both examples of auth subjects.
Import the auth subject you wish to use, and set it as the auth subject in the ETGAuthService as shown below.
import { Injectable } from '@angular/core';
import { ETGAuthService, OpenIddictService } from "ng-etg-base";
@Injectable({
providedIn: 'root',
})
export class AuthService {
constructor(
private etgAuth: ETGAuthService,
private openIddictService: OpenIddictService
) {
this.etgAuth.setAuthSubject(this.openIddictService);
}
}
AuthVars Setup
In order for the auth service to work properly, you must specify certain variables for it to use. The AuthVars service can be used directly, but the ETGAuthService also has functons to interact with auth vars, so let's use that. It is suggested to setup auth vars in your app.component so they are loaded before any auth services get initialized. To start, import and inject the ETGAuthService into your app.component.
import { Component } from '@angular/core';
import { ETGAuthService } from 'ng-etg-base';
@Component({
selector: 'app',
templateUrl: './app.component.html',
styleUrls: ['./app.component.scss']
})
export class AppComponent {
constructor(
private etgAuth: ETGAuthService
) { }
}
Now the ETGAuthService can be used to set the needed auth vars.
To start, set the auth url, which represents the URL to use when making authentication endpoint calls. You may use a value from an environment config if it suites your application.
Next, you may set/add to the auth config. The auth config represents a collection of properties that are needed for the auth subject you are using. For example, the OpenIddict service requires the grant_type, scope, and client_id properties.
Last, if using the AuthInterceptor in your project (see AuthInterceptor section below), you may specify certain keywords/urls to be blacklisted. When a blacklisted keyword is present in the URL of an outgoing request, the access token will not be appended to the request's headers.
The setting of the AuthUrl, AuthConfig, and AuthInterceptorBlacklist is shown below.
...
export class AppComponent {
constructor(
private etgAuth: ETGAuthService
) {
// Set the auth url. Replace the string with environment.authUrl (or equivalent) if applicable
this.etgAuth.setAuthUrl("http://localhost:5000/api/v1");
// Set the auth config. This example is using the OpenIddict service. Other services may require different config properties.
this.etgAuth.setAuthConfig({
grant_type: "password",
scope: "openid offline_access",
client_id: "mystarter"
});
// Set the auth interceptor blacklist. This example will prevent all requests with the word "Token" in the URL from having the access token added to them.
this.etgAuth.setAuthInterceptorBlacklist([
"Token"
]);
}
...
}
Using CognitoService
To use the AWS CognitoService to make use of Cognito's authentication, first follow the same steps above for ETGAuthService Integration and AuthVars Setup, but this time use some different values as defined below.
AuthService: For your local AuthService that configures the ETGAuthService, simply set the auth subject to the CognitoService.
import { Injectable } from '@angular/core';
import { ETGAuthService, CognitoService } from "ng-etg-base";
@Injectable({
providedIn: 'root',
})
export class AuthService {
constructor(
private etgAuth: ETGAuthService,
private cognitoService: CognitoService
) {
this.etgAuth.setAuthSubject(this.cognitoService);
}
}
AppComponent AppVars: For app vars, we need to set a few parameters required by Cognito.
- user_pool_id = the user pool id of the Cognito user pool you are using
- app_client_id = the app client id of the app client you created for your application on Cognito
- region = the region that your Cognito pool is created in
- access_key_id = the access key of an IAM user with full access to your Cognito pool
- secret_access_key = the secret key of an IAM user with full access to your Cognito pool
The interceptor blacklist can be updated to include whatever routes you wish, it is not required.
The auth URL is also not needed for CognitoService.
...
export class AppComponent {
constructor(
private etgAuth: ETGAuthService
) {
// The fields below are required for Cognito
this.etgAuth.setAuthConfig({
user_pool_id: environment.userPoolId,
app_client_id: environment.clientId,
region: environment.region,
access_key_id: environment.accessKeyId,
secret_access_key: environment.secretAccessKey
});
// Set the auth interceptor blacklist. Add whatever routes you don't want to have the access token added to
this.etgAuth.setAuthInterceptorBlacklist([
]);
}
...
}
Auth Interceptors
ng-etg-base includes two interceptors that can be used to modify outgoing requests and responses. The two included are the AuthInterceptor and the Error Interceptor.
To use either interceptor, open your application's app.module file. Start by importing the HTTP_INTERCEPTORS module from angular, an importing any ng-etg-base interceptors you wish to use.
import { HTTP_INTERCEPTORS } from '@angular/common/http';
import { AuthInterceptor, ErrorInterceptor } from 'ng-etg-base';
With the needed components imported, add the interceptors as providers in your module.
@NgModule({
declarations: [ ... ],
imports: [ ... ],
providers: [
{ provide: HTTP_INTERCEPTORS, useClass: AuthInterceptor, multi: true },
{ provide: HTTP_INTERCEPTORS, useClass: ErrorInterceptor, multi: true }
],
bootstrap: [ ... ]
})
export class AppModule { }
The interceptors will now work automatically. Read the information below on each interceptor to find out what they do.
Auth Interceptor
The auth interceptor will automatically append the Authorization header with the current user's access token to all outgoing requests. Requests with certain keywords in their URLs can be blacklisted by adding the authInterceptorBlacklist array in the AuthVarsService (or ETGAuthService). In short, you can add blacklisted keywords like so (preferably in your app.component.ts):
this.etgAuth.setAuthInterceptorBlacklist([
"Token",
...
]);
Error Interceptor
The error interceptor will monitor all outgoing request responses. If a response has a status code of 401 (Unauthorized), the interceptor will automatically logout the current user and reload the page.
Alerts Module
The alerts module contains an AlertService to present simple toast messages and confirmation messages to users.
AlertService
Usage
To start using the AlertService, import it into the component you need it in and inject the service into your component constructor.
...
import { AlertService } from 'ng-etg-base';
@Component({
selector: 'component-selector',
templateUrl: 'component-template.html',
styleUrls: ['./component-styles.scss']
})
export class TestComponent {
constructor(
private alertService: AlertService
) { }
...
}
To use the AlertService to display an alert, call the various functions on the service as follows:
this.alertService.toast("This is a toast message!");
...
this.alertService.confirm("This is a confirmation", (result) => { ... });
API
The AlertService provides several functions for presenting toasts and confirmations. There are 4 types of alerts in each category: Info, Success, Warning, and Error.
Toast Alert
A toast alert will present a message at the top-right corner of the screen as a simple message that can be closed with no further action. By default a toast will remain for 5 seconds.
toast(message: string, title: string = "Info", timeout = undefined, customClass = "", position = undefined)
toast() simply presents a toast alert with a type of "info".
toastSuccess(message: string, title: string = "Success", timeout = undefined, customClass = "", position = undefined)
toastSuccess() presents a toast alert with a type of "success" and uses the color green to style the alert.
toastWarning(message: string, title: string = "Warning", timeout = undefined, customClass = "", position = undefined)
toastWarning() presents a toast alert with a type of "warning" and uses the color yellow to style the alert.
toastError(message: string, title: string = "Error", timeout = undefined, customClass = "", position = undefined)
toastError() presents a toast alert with a type of "error" and uses the color red to style the alert.
Confirmation Alert
A confirmation alert will present a message at the top-right corner of the screen with two buttons in it, one to cancel and one to confirm. A callback function is required to be passed to the confirm functions, that function will be called upon dismissing the confirmation alert. The context in which the confirmation was dismissed will be passed to the callback. A response of {value: true}
means the confirm button was clicked, {dismiss: "cancel"}
means the cancel button was clicked. By default, a confirmation alert has no timeout and will remain present until interacted with.
confirm(message: string, onDismiss: any, title: string = "Confirmation", confirmButtonText: string = "Confirm", cancelButtonText: string = "Cancel", timeout = undefined, customClass = "", position = undefined)
confirm() will display a confirmation alert with a type of "info". The two buttons default to "Cancel" and "Confirm".
confirmSuccess(message: string, onDismiss: any, title: string = "Confirmation", confirmButtonText: string = "Confirm", cancelButtonText: string = "Cancel", timeout = undefined, customClass = "", position = undefined)
confirmSuccess() will display a confirmation alert with a type of "success". The two buttons default to "Cancel" and "Confirm".
confirmWarning(message: string, onDismiss: any, title: string = "Confirmation", confirmButtonText: string = "Confirm", cancelButtonText: string = "Cancel", timeout = undefined, customClass = "", position = undefined)
confirmWarning() will display a confirmation alert with a type of "warning". The two buttons default to "Cancel" and "Confirm".
confirmError(message: string, onDismiss: any, title: string = "Confirmation", confirmButtonText: string = "Confirm", cancelButtonText: string = "Cancel", timeout = undefined, customClass = "", position = undefined)
confirmError() will display a confirmation alert with a type of "error". The two buttons default to "Cancel" and "Confirm".
Other Alerts
customAlert(config: any)
The customAlert() function will present an alert with no base settings, all alert settings must be provided in the passes-in config object. Options for the config can be viewed here (SweetAlert2).
customConfirmation(config: any, onDismiss: any)
The customConfirmation() function will present an alert with no base settings and will allow a callback function to be specified that will get called when the confirmation is dismissed, all alert settings must be provided in the passes-in config object. Options for the config can be viewed here (SweetAlert2).
Alert Positions
An alert's position can be set to any value in the AlertPosition enum. Changing an alert's position will cause it to display in a different section of the screen.
To set an alert position, import the Alertposition enum and send in a position option when triggering an alert as shown in the function definitions above. Possible position options are:
- AlertPosition.TOP
- AlertPosition.TOP_LEFT
- AlertPosition.TOP_RIGHT
- AlertPosition.CENTER
- AlertPosition.CENTER_LEFT
- AlertPosition.CENTER_RIGHT
- AlertPosition.BOTTOM
- AlertPosition.BOTTOM_LEFT
- AlertPosition.BOTTOM_RIGHT
Alert Global Values
Some alert config values can be set globally to apply to all alerts. Global values can be retreieved and set by calling the corresponding global functions on the alert service. Possible global values and their setter functions are:
- timeout | setGlobalTimeout(timeout: number)
- customClass | setGlobalCustomClass(customClass: string)
- position | setGlobalPosition(position: AlertPosition)
- confirmationPosition | setGlobalConfirmationPosition(position: AlertPosition)