Skip to content

Commit

Permalink
#708 - WIP
Browse files Browse the repository at this point in the history
  • Loading branch information
Martin Käser committed Mar 21, 2024
1 parent 9b6ef0a commit 58bc117
Show file tree
Hide file tree
Showing 18 changed files with 184 additions and 48 deletions.
4 changes: 4 additions & 0 deletions backend/src/main/java/ch/puzzle/okr/OkrApplication.java
Original file line number Diff line number Diff line change
@@ -1,11 +1,15 @@
package ch.puzzle.okr;

import ch.puzzle.okr.service.clientconfig.ClientConfigProperties;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.scheduling.annotation.EnableScheduling;

@SpringBootApplication
@EnableScheduling
@EnableConfigurationProperties(ClientConfigProperties.class)
public class OkrApplication {
public static void main(String[] args) {
SpringApplication.run(OkrApplication.class, args);
Expand Down
Original file line number Diff line number Diff line change
@@ -1,14 +1,13 @@
package ch.puzzle.okr.controller;

import ch.puzzle.okr.service.ClientConfigService;
import ch.puzzle.okr.dto.ClientConfigDto;
import ch.puzzle.okr.service.clientconfig.ClientConfigService;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import java.util.Map;

@RestController
@RequestMapping("/config")
public class ClientConfigController {
Expand All @@ -20,7 +19,7 @@ public ClientConfigController(ClientConfigService configService) {
}

@GetMapping
public ResponseEntity<Map<String, String>> getConfig() {
public ResponseEntity<ClientConfigDto> getConfig() {
return ResponseEntity.status(HttpStatus.OK).body(configService.getConfigBasedOnActiveEnv());
}
}
13 changes: 13 additions & 0 deletions backend/src/main/java/ch/puzzle/okr/dto/ClientConfigDto.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
package ch.puzzle.okr.dto;

import java.util.HashMap;

public record ClientConfigDto(
String activeProfile,
String issuer,
String clientId,
String favicon,
String logo,
HashMap<String, String> customStyles
) {
}

This file was deleted.

Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
package ch.puzzle.okr.service.clientconfig;

import ch.puzzle.okr.dto.ClientConfigDto;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Service;

import java.util.HashMap;

@ConfigurationProperties("okr.clientconfig")
public class ClientConfigProperties {
private String favicon;

private String logo;

private HashMap<String, String> customStyles;

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
package ch.puzzle.okr.service.clientconfig;

import ch.puzzle.okr.dto.ClientConfigDto;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;

import java.util.HashMap;

@Service
public class ClientConfigService {

@Value("${spring.security.oauth2.resourceserver.jwt.issuer-uri}")
private String issuer;

@Value("${spring.profiles.active}")
private String activeProfile;

@Value("${spring.security.oauth2.resourceserver.opaquetoken.client-id}")
private String clientId;

@Value("${okr.clientconfig.favicon}")
private String favicon;

@Value("${okr.clientconfig.logo}")
private String logo;

@Value("${okr.clientconfig.customStyles}")
private HashMap<String, String> customStyles;

public ClientConfigDto getConfigBasedOnActiveEnv() {
return new ClientConfigDto(activeProfile, issuer, clientId, favicon, logo, customStyles);
}

}
1 change: 1 addition & 0 deletions backend/src/main/resources/application-staging.properties
Original file line number Diff line number Diff line change
Expand Up @@ -4,3 +4,4 @@ logging.level.org.springframework=debug
spring.security.oauth2.resourceserver.opaquetoken.client-id=pitc_okr_staging

okr.user.champion.usernames=peggimann
okr.clientconfig.customStyles.okr-topbar-background-color=#ab31ad
4 changes: 4 additions & 0 deletions backend/src/main/resources/application.properties
Original file line number Diff line number Diff line change
Expand Up @@ -50,3 +50,7 @@ okr.jwt.user.username=preferred_username
okr.jwt.user.firstname=given_name
okr.jwt.user.lastname=family_name
okr.jwt.user.email=email

okr.clientconfig.favicon=assets/favicon.png
okr.clientconfig.logo=assets/images/okr-logo.svg
okr.clientconfig.customStyles.okr-topbar-background-color=#ff0000
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
package ch.puzzle.okr.controller;

import ch.puzzle.okr.service.ClientConfigService;
import ch.puzzle.okr.service.clientconfig.ClientConfigService;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.junit.jupiter.MockitoExtension;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
package ch.puzzle.okr.service;

import ch.puzzle.okr.dto.ClientConfigDto;
import ch.puzzle.okr.service.clientconfig.ClientConfigService;
import ch.puzzle.okr.test.SpringIntegrationTest;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;

import java.util.Map;

import static org.junit.jupiter.api.Assertions.assertEquals;

@SpringIntegrationTest
Expand All @@ -16,10 +16,10 @@ class ClientConfigServiceIT {

@Test
void saveKeyResultShouldSaveNewKeyResult() {
Map<String, String> configMap = clientConfigService.getConfigBasedOnActiveEnv();
ClientConfigDto clientConfig = clientConfigService.getConfigBasedOnActiveEnv();

assertEquals("prod", configMap.get("activeProfile"));
assertEquals("http://localhost:8544/realms/pitc", configMap.get("issuer"));
assertEquals("prod", clientConfig.activeProfile());
assertEquals("http://localhost:8544/realms/pitc", clientConfig.issuer());
}

}
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
<div id="topBarHeight">
<div id="okrTopbar">
<span class="d-flex h-100 align-items-center ps-4">
<img alt="okr-logo" height="32" ngSrc="assets/images/okr-logo.svg" width="140" />
<img alt="okr-logo" height="32" ngSrc="{{this.logoSrc$ | async}}" width="140" />
</span>

<div class="d-flex align-items-center me-md-5 me-1">
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
z-index: 102;
height: inherit;
justify-content: space-between;
background-color: $pz-dark-blue;
background-color: var(--okr-topbar-background-color)
}

.topBarEntry {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { ChangeDetectionStrategy, Component, Input, OnInit } from '@angular/core';
import { OAuthService } from 'angular-oauth2-oidc';
import { map, ReplaySubject } from 'rxjs';
import { BehaviorSubject, map, ReplaySubject } from 'rxjs';
import { ConfigService } from '../config.service';
import { MatDialog, MatDialogRef } from '@angular/material/dialog';
import { TeamManagementComponent } from '../shared/dialog/team-management/team-management.component';
Expand All @@ -22,6 +22,7 @@ export class ApplicationTopBarComponent implements OnInit {
@Input()
hasAdminAccess!: ReplaySubject<boolean>;
private dialogRef!: MatDialogRef<TeamManagementComponent> | undefined;
logoSrc$ = new BehaviorSubject<String|undefined>(undefined);

constructor(
private oauthService: OAuthService,
Expand All @@ -35,9 +36,7 @@ export class ApplicationTopBarComponent implements OnInit {
this.configService.config$
.pipe(
map((config) => {
if (config.activeProfile === 'staging') {
document.getElementById('okrTopbar')!.style.backgroundColor = '#ab31ad';
}
this.logoSrc$.next(config.logo);
}),
)
.subscribe();
Expand Down
8 changes: 6 additions & 2 deletions frontend/src/app/config.service.ts
Original file line number Diff line number Diff line change
@@ -1,14 +1,18 @@
import { Injectable } from '@angular/core';
import { HttpClient } from '@angular/common/http';
import { Observable, shareReplay } from 'rxjs';
import { ClientConfig } from "./shared/types/model/ClientConfig";


@Injectable({
providedIn: 'root',
})
export class ConfigService {
public config$: Observable<any>;
public config$: Observable<ClientConfig>;

constructor(private httpClient: HttpClient) {
this.config$ = this.httpClient.get('/config').pipe(shareReplay());
this.config$ = this.httpClient
.get<ClientConfig>('/config')
.pipe(shareReplay());
}
}
73 changes: 73 additions & 0 deletions frontend/src/app/shared/services/customization.service.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
import { Injectable } from '@angular/core';
import { ConfigService } from "../../config.service";
import { CustomizationConfig } from "../types/model/ClientConfig";

@Injectable({
providedIn: 'root',
})
export class CustomizationService {
private currentConfig?: CustomizationConfig;

constructor(private configService: ConfigService) {
configService.config$.subscribe(config => {
this.updateCustomizations(config);
})
}


private updateCustomizations(config: CustomizationConfig) {
this.setFavicon(config.favicon);
this.setStyleCustomizations(config.customStyles);

this.currentConfig = config;
}

private setFavicon(favicon: string) {
if (!favicon || this.currentConfig?.favicon === favicon) {
return;
}

document.getElementById("favicon")?.setAttribute("src", favicon);
}

private setStyleCustomizations(customStylesMap: Map<string, string>) {
if (!customStylesMap || this.areStylesTheSame(customStylesMap)) {
return;
}

this.removeStyles(this.currentConfig?.customStyles)
this.setStyles(customStylesMap);
}

private areStylesTheSame(customStylesMap: Map<string, string>) {
return JSON.stringify(this.currentConfig?.customStyles) === JSON.stringify(customStylesMap);
}

private removeStyles(customStylesMap: Map<string, string> | undefined) {
if (!customStylesMap) {
return;
}
const styles = document?.getElementById("html")!.style;
if (!styles) {
return
}

Array.from(customStylesMap.entries()).forEach(([varName, varValue]) => {
styles.setProperty(`--${varName}`, varValue);
});
}

private setStyles(customStylesMap: Map<string, string>) {
if (!customStylesMap) {
return;
}
const styles = document?.getElementById("html")!.style;
if (!styles) {
return
}

Array.from(customStylesMap.keys()).forEach((varName) => {
styles.removeProperty(`--${varName}`);
});
}
}
14 changes: 14 additions & 0 deletions frontend/src/app/shared/types/model/ClientConfig.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@

export interface AuthConfig {
activeProfile: string;
issuer: string;
clientId: string;
}

export interface CustomizationConfig {
favicon: string;
logo: string;
customStyles: Map<string, string>;
}
export interface ClientConfig extends AuthConfig, CustomizationConfig {
}
2 changes: 1 addition & 1 deletion frontend/src/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
<title>Puzzle OKR</title>
<base href="/" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<link rel="icon" type="image" href="assets/favicon.png" />
<link id="favicon" rel="icon" type="image" href="assets/favicon.png" />
</head>
<body>
<app-root></app-root>
Expand Down
2 changes: 2 additions & 0 deletions frontend/src/style/_variables.scss
Original file line number Diff line number Diff line change
Expand Up @@ -47,4 +47,6 @@ $pz-dark-blue-palette: (
--mdc-text-button-label-text-tracking: normal;
--mdc-filled-button-label-text-tracking: normal;
--mdc-outlined-button-label-text-tracking: normal;

--okr-topbar-background-color: $pz-dark-blue;
}

0 comments on commit 58bc117

Please sign in to comment.