Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[#23] Add example user interface implementation #25

Merged
merged 9 commits into from
Nov 4, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -14,3 +14,4 @@

# OS files
.DS_Store
/node_modules
9 changes: 9 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,3 +19,12 @@ registry type to another.
## Device communication API

See the [device-communication](device-communication) directory for api specifications and implementation.

## Device Management User Interface

See the [device-management-ui](device-management-ui) directory for an example User Interface containing a list
of tenants with the option to create, update and delete a tenant. Each tenant contains a list of its devices with the
option to create and delete a device. Furthermore, the credentials of a device can be listed, created and deleted.
Currently only [Password Credentials](https://www.eclipse.org/hono/docs/concepts/device-identity/#usernamepassword-based-authentication)
and [RPK Credentials](https://www.eclipse.org/hono/docs/concepts/device-identity/#json-web-token-based-authentication)
are supported. This UI also contains the functionality to send a configuration or command through the [Device Communication API](device-communication).
16 changes: 16 additions & 0 deletions device-management-ui/.editorconfig
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
# Editor configuration, see https://editorconfig.org
root = true

[*]
charset = utf-8
indent_style = space
indent_size = 2
insert_final_newline = true
trim_trailing_whitespace = true

[*.ts]
quote_type = single

[*.md]
max_line_length = off
trim_trailing_whitespace = false
42 changes: 42 additions & 0 deletions device-management-ui/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
# Compiled output
/dist
/tmp
/out-tsc
/bazel-out

# Node
/node_modules
npm-debug.log
yarn-error.log

# IDEs and editors
.idea/
.project
.classpath
.c9/
*.launch
.settings/
*.sublime-workspace

# Visual Studio Code
.vscode/*
!.vscode/settings.json
!.vscode/tasks.json
!.vscode/launch.json
!.vscode/extensions.json
.history/*

# Miscellaneous
/.angular/cache
.sass-cache/
/connect.lock
/coverage
/libpeerconnection.log
testem.log
/typings

# System files
.DS_Store
Thumbs.db

package-lock.json
24 changes: 24 additions & 0 deletions device-management-ui/Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
# STAGE 1: Build app
FROM node:16-alpine as build

# Make /app as working directory
WORKDIR /app

COPY ./src /app/src/
COPY *.* /app/

# Install all the dependencies
RUN npm install && npm run build

# STAGE 2: Serve app with NgInx
FROM nginx:alpine

# Copy dist folder from build stage to nginx public folder
COPY --from=build /app/dist/device-management-ui /usr/share/nginx/html
COPY ./nginx.conf /etc/nginx/conf.d/default.conf

# Start NgInx service
CMD ["/bin/sh", "-c", "envsubst < /usr/share/nginx/html/assets/env.template.js > /usr/share/nginx/html/assets/env.js && exec nginx -g 'daemon off;'"]

# Expose port 80
EXPOSE 80
65 changes: 65 additions & 0 deletions device-management-ui/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
# Device Management UI

This project was generated with [Angular CLI](https://github.com/angular/angular-cli) version 15.2.0. This is an example
implementation for a Google based deployment in order to give users the possibility to operate with the [Device Registry
API](https://www.eclipse.org/hono/docs/api/management/) as well as with the [Device Communication API](../device-communication).
We are recommending using Identity-Aware Proxy ([IAP](https://cloud.google.com/iap/docs)) for your API security. Make
sure to configure the [OAuth consent](https://developers.google.com/workspace/guides/configure-oauth-consent) screen.
This UI will provide the possibility to:

1. Access the Device Registry API containing actions like:
- listing tenants
- creating a new tenant with an ID and messaging-type
- deleting a tenant
- updating a tenant with another messaging-type
- listing devices of a tenant
- creating a new device with an ID
- deleting a device
- listing credentials of a device
- creating [Username/Password](https://www.eclipse.org/hono/docs/concepts/device-identity/#usernamepassword-based-authentication) or [JSON Web Token](https://www.eclipse.org/hono/docs/concepts/device-identity/#json-web-token-based-authentication) based credentials
- updating JSON Web Token based credentials
- deleting credentials


2. Access the Device Communication API containing actions like:
- listing configs
- updating a config
- listing states
- sending a command

**_NOTE:_** This UI cannot be run without further adjustments! If one wants to use this UI in other environments than on
Google Cloud, adjustments have to be made
to **not** include the GoogleService and to update the url suffixes of the services.

### Development server

The development server uses the Proxy Configuration file [proxy.config.json](proxy.config.json). So the target address
must be updated
with the address the Hono API is hosted. To run the dev server `ng serve` must be executed. The UI can be then accessed
via `http://localhost:4200/`.
The application will automatically reload if any of the source files is changed.

#### Google APIs

If the Device Registry API and/or the Device Communication API is hosted in GCP with Identity Aware Proxy,
the Google Client ID of the enabled IAP must be provided in the environment
file [environment.development.ts](../device-management-ui/src/environments/environment.development.ts).

### Build

#### Google APIs

If the UI, the Device Registry API and/or the Device Communication API is hosted in GCP with Identity Aware Proxy,
the Google Client ID of the enabled IAP must be provided inside an environment variable `ENV_GOOGLE_CLIENT_ID`.
This environment variable is then set as `googleClientId` inside the environment
file [environment.ts](../device-management-ui/src/environments/environment.ts).

#### Docker Image

A docker image can be built by executing: `docker build -t {REGISTRY}/{IMAGE_NAME}:{TAG} .`.
The docker image is installing all the dependencies and building the project with ng build automatically.
Also, a nginx based image is used for building the server.

### Running unit tests

Run `ng test` to execute the unit tests via [Karma](https://karma-runner.github.io).
122 changes: 122 additions & 0 deletions device-management-ui/angular.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,122 @@
{
"$schema": "./node_modules/@angular/cli/lib/config/schema.json",
"version": 1,
"newProjectRoot": "projects",
"projects": {
"device-management-ui": {
"projectType": "application",
"schematics": {
"@schematics/angular:component": {
"style": "scss"
}
},
"root": "",
"sourceRoot": "src",
"prefix": "app",
"architect": {
"build": {
"builder": "@angular-devkit/build-angular:browser",
"options": {
"outputPath": "dist/device-management-ui",
"index": "src/index.html",
"main": "src/main.ts",
"polyfills": [
"zone.js"
],
"tsConfig": "tsconfig.app.json",
"inlineStyleLanguage": "scss",
"assets": [
"src/favicon.ico",
"src/assets",
"src/assets/env.js"
],
"styles": [
"src/styles.scss"
],
"scripts": []
},
"configurations": {
"production": {
"budgets": [
{
"type": "initial",
"maximumWarning": "500kb",
"maximumError": "2mb"
},
{
"type": "anyComponentStyle",
"maximumWarning": "2kb",
"maximumError": "4kb"
}
],
"outputHashing": "all"
},
"development": {
"buildOptimizer": false,
"optimization": false,
"vendorChunk": true,
"extractLicenses": false,
"sourceMap": true,
"namedChunks": true,
"fileReplacements": [
{
"replace": "src/environments/environment.ts",
"with": "src/environments/environment.development.ts"
}
]
}
},
"defaultConfiguration": "production"
},
"serve": {
"builder": "@angular-devkit/build-angular:dev-server",
"configurations": {
"production": {
"browserTarget": "device-management-ui:build:production"
},
"development": {
"browserTarget": "device-management-ui:build:development",
"proxyConfig": "proxy.config.json"
}
},
"defaultConfiguration": "development"
},
"extract-i18n": {
"builder": "@angular-devkit/build-angular:extract-i18n",
"options": {
"browserTarget": "device-management-ui:build"
}
},
"test": {
"builder": "@angular-devkit/build-angular:karma",
"options": {
"codeCoverage": true,
"polyfills": [
"zone.js",
"zone.js/testing"
],
"tsConfig": "tsconfig.spec.json",
"inlineStyleLanguage": "scss",
"assets": [
"src/favicon.ico",
"src/assets"
],
"styles": [
"src/styles.scss"
],
"fileReplacements": [
{
"replace": "src/environments/environment.ts",
"with": "src/environments/environment.development.ts"
}
],
"karmaConfig": "karma.conf.js"
}
}
}
}
},
"cli": {
"analytics": false
}
}
34 changes: 34 additions & 0 deletions device-management-ui/karma.conf.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
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'),
require('@angular-devkit/build-angular/plugins/karma')
],
client: {
jasmine: {
random: false,
verboseDeprecations: true
},
clearContext: false
},
jasmineHtmlReporter: {
suppressAll: true
},
coverageReporter: {
dir: require('path').join(__dirname, './coverage/device-management-ui'),
subdir: '.',
reporters: [
{ type: 'html' },
{ type: 'text-summary' }
]
},
reporters: ['progress', 'kjhtml'],
browsers: ['Chrome'],
restartOnFileChange: true
});
};
16 changes: 16 additions & 0 deletions device-management-ui/nginx.conf
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
server {
listen 80;
server_name localhost;

location / {
root /usr/share/nginx/html;
try_files $uri $uri/ /index.html;
index index.html index.htm;
}

error_page 500 502 503 504 /50x.html;
location = /50x.html {
root /usr/share/nginx/html;
}

}
47 changes: 47 additions & 0 deletions device-management-ui/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
{
"name": "device-management-ui",
"version": "0.0.0",
"scripts": {
"ng": "ng",
"start": "ng serve",
"build": "ng build",
"watch": "ng build --watch --configuration development",
"test": "ng test"
},
"private": true,
"dependencies": {
"@angular/animations": "^15.2.0",
"@angular/common": "^15.2.0",
"@angular/compiler": "^15.2.0",
"@angular/core": "^15.2.0",
"@angular/forms": "^15.2.0",
"@angular/platform-browser": "^15.2.0",
"@angular/platform-browser-dynamic": "^15.2.0",
"@angular/router": "^15.2.0",
"@fortawesome/angular-fontawesome": "^0.12.1",
"@fortawesome/free-solid-svg-icons": "^6.3.0",
"@ng-bootstrap/ng-bootstrap": "^14.0.1",
"@ng-select/ng-select": "^10.0.3",
"angular-oauth2-oidc": "^15.0.1",
"angular-oauth2-oidc-jwks": "^15.0.1",
"bootstrap": "^5.3.0-alpha1",
"rxjs": "~7.8.0",
"tslib": "^2.3.0",
"zone.js": "~0.12.0"
},
"devDependencies": {
"@angular-devkit/build-angular": "^15.2.0",
"@angular/cli": "~15.2.0",
"@angular/compiler-cli": "^15.2.0",
"@angular/localize": "^15.2.0",
"@types/jasmine": "~4.3.0",
"@types/lodash": "^4.14.197",
"jasmine-core": "~4.5.0",
"karma": "~6.4.0",
"karma-chrome-launcher": "~3.1.0",
"karma-coverage": "~2.2.0",
"karma-jasmine": "~5.1.0",
"karma-jasmine-html-reporter": "~2.0.0",
"typescript": "~4.9.4"
}
}
Loading
Loading