This is the codeAbility Sharing Platform! Learn more about the codeAbility Sharing Platform.

Skip to content
Snippets Groups Projects
Commit 7f02a806 authored by Michael Breu's avatar Michael Breu :speech_balloon:
Browse files

Fully implementing broadcast messages

parent 8bacf47e
2 merge requests!188Merging Peer Reviewing et. al to Master,!164211 peer reviewing functionality
Showing
with 190 additions and 31 deletions
......@@ -161,7 +161,7 @@ public class SecurityConfiguration extends WebSecurityConfigurerAdapter {
.antMatchers("/oauth2/**").permitAll()
.antMatchers("/api/search/**").permitAll() // search is always allowed, may return more data, if authenticated
.antMatchers("/api/pluginIF/**").permitAll() // plugins calls are always allowed, security by tokens
.antMatchers("/api/deploymentInfo").permitAll() // everybody may retrieve deployment infos
.antMatchers("/api/applicationInfo/**").permitAll() // everybody may retrieve deployment infos
.antMatchers("/api/authenticate").permitAll()
.antMatchers("/api/refreshToken").permitAll()
.antMatchers("/api/register").denyAll()
......
package at.ac.uibk.gitsearch.service;
import java.util.Date;
import java.util.Timer;
import java.util.TimerTask;
......@@ -19,7 +18,7 @@ import org.springframework.stereotype.Service;
import com.fasterxml.jackson.jaxrs.json.JacksonJsonProvider;
import at.ac.uibk.gitsearch.config.ApplicationProperties;
import at.ac.uibk.gitsearch.repository.gitlab.GitLabRepository;
import at.ac.uibk.gitsearch.service.dto.BroadCastMessageDTO;
/**
* this service provides messages. To make things simple, we use gitlab to
......@@ -31,29 +30,15 @@ import at.ac.uibk.gitsearch.repository.gitlab.GitLabRepository;
@Service
public class MessageService {
public static class BroadCastMessage {
public String message;
public Date starts_at;
public Date ends_at;
public String color; // :"#E75E40",
public String font; // #FFFFFF",
public int id; // 1,
public boolean active; // false,
public String target_path; // "*/welcome",
public String broadcast_type; // "banner",
public boolean dismissable; // false
}
private static final Logger log = LoggerFactory.getLogger(MessageService.class);
@Autowired
protected ApplicationProperties applicationProperties;
BroadCastMessage[] messages = null;
BroadCastMessageDTO[] messages = null;
public BroadCastMessage[] getMessages() {
public BroadCastMessageDTO[] getMessages() {
return messages;
}
......@@ -67,13 +52,15 @@ public class MessageService {
public class MessageLoaderTask extends TimerTask {
public void run() {
// Unfortunatelly the gitlab API does not yet support fetching of broadcast messages :-(
ClientConfig restClientConfig = new ClientConfig();
restClientConfig.register(JacksonJsonProvider.class);
Client client = ClientBuilder.newClient(restClientConfig);
WebTarget target = client.target(applicationProperties.getGitLab().getUrl() + "/api/v4/broadcast_messages");
try {
messages = target.request().accept(MediaType.APPLICATION_JSON).get(BroadCastMessage[].class);
messages = target.request().accept(MediaType.APPLICATION_JSON).get(BroadCastMessageDTO[].class);
} catch (Exception e) {
log.warn("Cannot load broadcast messages from {}", e.getMessage());
}
......
package at.ac.uibk.gitsearch.service.dto;
import java.util.Date;
public class BroadCastMessageDTO {
public String message;
public Date starts_at;
public Date ends_at;
public String color; // :"#E75E40",
public String font; // #FFFFFF",
public int id; // 1,
public boolean active; // false,
public String target_path; // "*/welcome",
public String broadcast_type; // "banner",
public boolean dismissable; // false
}
\ No newline at end of file
......@@ -8,6 +8,8 @@ import org.springframework.web.bind.annotation.RestController;
import at.ac.uibk.gitsearch.config.ApplicationProperties;
import at.ac.uibk.gitsearch.config.ApplicationProperties.DeploymentInfo;
import at.ac.uibk.gitsearch.service.MessageService;
import at.ac.uibk.gitsearch.service.dto.BroadCastMessageDTO;
/**
* REST controller to retrieve deployment infos.
......@@ -21,16 +23,18 @@ public class ApplicationInfoResource {
private final Logger log = LoggerFactory.getLogger(ApplicationInfoResource.class);
private final ApplicationProperties applicationProperties;
private final MessageService messageService;
public ApplicationInfoResource(ApplicationProperties applicationProperties) {
public ApplicationInfoResource(ApplicationProperties applicationProperties, MessageService messageService) {
this.applicationProperties = applicationProperties;
this.messageService = messageService;
}
/**
* {@code GET /deploymentInfo} : get generic deployment info.
* {@code GET /applicationInfo/deploymentInfo} : get generic deployment info.
*
*/
@GetMapping("/deploymentInfo")
@GetMapping("/applicationInfo/deploymentInfo")
public ApplicationProperties.DeploymentInfo getApplicationInfo() {
final DeploymentInfo deploymentInfo = applicationProperties.getDeploymentInfo();
if(deploymentInfo==null || "${git.branch}".equals(deploymentInfo.getBranch())) {
......@@ -38,5 +42,14 @@ public class ApplicationInfoResource {
}
return deploymentInfo;
}
/**
* {@code GET /applicationInfo/deploymentInfo} : get generic deployment info.
*
*/
@GetMapping("/applicationInfo/broadcastMessages")
public BroadCastMessageDTO[] getBroadCastMessages() {
return messageService.getMessages();
}
}
......@@ -16,6 +16,7 @@ import { PageRibbonComponent } from './layouts/profiles/page-ribbon.component';
import { ActiveMenuDirective } from './layouts/navbar/active-menu.directive';
import { ErrorComponent } from './layouts/error/error.component';
import { QueryParamModule } from '@ngqp/core';
import { CacheService } from 'app/shared/service/cache.service'
@NgModule({
imports: [
......@@ -37,5 +38,9 @@ import { QueryParamModule } from '@ngqp/core';
ActiveMenuDirective,
FooterComponent],
bootstrap: [MainComponent],
providers: [
CacheService,
]
})
export class GitSearchV2AppModule {}
......@@ -16,11 +16,11 @@ export class ApplicationInfoService {
constructor(
private http: HttpClient,
) {
this.cachedDeploymentInfo = {} as DeploymentInfo;
this.cachedDeploymentInfo = {} as DeploymentInfo;
}
private loadDeploymentInfo(): Observable<DeploymentInfo> {
return this.http.get<DeploymentInfo>(SERVER_API_URL + 'api/deploymentInfo');
return this.http.get<DeploymentInfo>(SERVER_API_URL + 'api/applicationInfo/deploymentInfo');
}
public getDeploymentInfo(): DeploymentInfo {
......
......@@ -8,7 +8,9 @@
<div>
<router-outlet name="navbar"></router-outlet>
</div>
<div *ngFor="let m of getActiveMessages()">
<div class="alert alert-warning">{{m.message}}</div>
</div>
<div class="container-fluid">
<div class="pagewrapper"></div>
......
......@@ -6,6 +6,7 @@ import { TranslateService, LangChangeEvent } from '@ngx-translate/core';
import { AccountService } from 'app/core/auth/account.service';
import { AuthServerProvider } from 'app/core/auth/auth-jwt.service';
import { CookieService } from 'ngx-cookie-service';
import { MessageService, BroadCastMessage} from 'app/shared/service/message-service'
// import { AlertErrorComponent } from 'app/shared/alert/alert-error.component';
@Component({
......@@ -22,7 +23,8 @@ export class MainComponent implements OnInit {
private translateService: TranslateService,
rootRenderer: RendererFactory2,
private authServerProvider: AuthServerProvider,
private cookieService: CookieService
private cookieService: CookieService,
private messageService: MessageService,
) // private alertErrorComponent: AlertErrorComponent
{
this.renderer = rootRenderer.createRenderer(document.querySelector('html'), null);
......@@ -48,6 +50,11 @@ export class MainComponent implements OnInit {
this.renderer.setAttribute(document.querySelector('html'), 'lang', langChangeEvent.lang);
});
}
public getActiveMessages(): Array<BroadCastMessage> {
return this.messageService.getActiveMessages(); //
}
private checkRequestToken(): void {
const tokenCookie = this.cookieService.get('tempRequestToken');
......
......@@ -63,7 +63,7 @@
</a><span *ngFor="let type of getSelectedTypes(), let isLast=last">{{'exercise.metadata.' + type | translate}}{{isLast ? '' : ', '}}</span>
<div class="dropdown-menu" style="padding-left: 10px;">
<table><tr *ngFor="let type of typeValues"><td>
<input class="form-check-input" [value]="type" type="checkbox" queryParamName="{{type}}" id="type">
<input class="form-check-input" [value]="type" type="checkbox" queryParamName="{{type}}">
</td><td style="text-align: left;">
<label class="form-check-label" style="display: inline-block;align-items: left" for="type">{{'exercise.metadata.' + type | translate}}</label>
</td></table>
......
import { Injectable } from '@angular/core'
/** provides a caching service, with a key, the data and a timeout ()
Warning: Objects are stored as json-Strings. I.e. Dates objects (and others) may not be converted correctly */
@Injectable()
export class CacheService {
constructor() { }
save(options: LocalStorageSaveOptions) {
// Set default values for optionals
options.expirationMins = options.expirationMins || 0
// Set expiration date in miliseconds
const expirationMS = options.expirationMins !== 0 ? options.expirationMins * 60 * 1000 : 0
const record = {
value: typeof options.data === 'string' ? options.data : JSON.stringify(options.data),
expiration: expirationMS !== 0 ? new Date().getTime() + expirationMS : null,
hasExpiration: expirationMS !== 0 ? true : false
}
localStorage.setItem(options.key, JSON.stringify(record))
}
load(key: string) {
// Get cached data from localstorage
const item = localStorage.getItem(key)
if (item !== null) {
const record = JSON.parse(item)
const now = new Date().getTime()
// Expired data will return null
if (!record || (record.hasExpiration && record.expiration <= now)) {
return null
} else {
return JSON.parse(record.value)
}
}
return null
}
remove(key: string) {
localStorage.removeItem(key)
}
cleanLocalStorage() {
localStorage.clear()
}
}
export class LocalStorageSaveOptions {
key = '';
data: any
expirationMins?: number
}
\ No newline at end of file
import { Injectable } from '@angular/core';
import { HttpClient } from '@angular/common/http';
import { SERVER_API_URL } from 'app/app.constants';
export interface BroadCastMessage {
message: string;
starts_at: Date;
ends_at: Date;
color: string;
font: string;
id: number;
active: boolean; // false,
target_path: string; // "*/welcome",
broadcast_type: string; // "banner",
dismissable: boolean; // false
}
/**
provides infrastructure services for Plugins
*/
@Injectable({ providedIn: 'root' })
export class MessageService {
public messageResourceURL = SERVER_API_URL + 'api/applicationInfo/broadcastMessages';
private messages = new Array<BroadCastMessage>();;
private nextUpdate = 0;
constructor(protected http: HttpClient,) {
}
public getActiveMessages(): Array<BroadCastMessage> {
const now = new Date();
return this.getMessages().filter(m =>
((m.starts_at <= now) && (m.ends_at >= now) )
); //
}
public getMessages(): Array<BroadCastMessage> {
const now = new Date().getTime();
if (this.nextUpdate > now) {
return this.messages;
}
this.nextUpdate = now + 60*1000; // wait at least 1 Minute for next try
const resp = this.http.get<Array<BroadCastMessage>>(this.messageResourceURL);
resp.subscribe(
response => {
// first convert dates correctly :-(
response.forEach( m => {
// eslint-disable-next-line @typescript-eslint/camelcase
m.starts_at = new Date(m.starts_at);
// eslint-disable-next-line @typescript-eslint/camelcase
m.ends_at = new Date(m.ends_at);})
// Data will be cached
this.messages = response;
this.nextUpdate = new Date().getTime() + 10*60*1000; // wait 10 Minute for next try
return (response);
}
);
return this.messages;
}
}
......@@ -12,7 +12,7 @@ import com.fasterxml.jackson.core.JsonParseException;
import com.fasterxml.jackson.databind.JsonMappingException;
import at.ac.uibk.gitsearch.GitsearchApp;
import at.ac.uibk.gitsearch.service.MessageService.BroadCastMessage;
import at.ac.uibk.gitsearch.service.dto.BroadCastMessageDTO;
@SpringBootTest(classes = GitsearchApp.class)
public class MessageServiceIT {
......@@ -24,7 +24,7 @@ public class MessageServiceIT {
@Test
public void loadMessages() throws JsonParseException, JsonMappingException, IOException {
BroadCastMessage[] messages = messageService.getMessages();
BroadCastMessageDTO[] messages = messageService.getMessages();
if(messages==null) {
// give it more time (or trigger reload)
messageService.triggerReload();
......@@ -32,7 +32,7 @@ public class MessageServiceIT {
}
Assert.assertNotNull("This may fail, due to timing problems", messages);
Assert.assertThat("This may fail, if there are not messages", messages, Matchers.arrayWithSize(2));
Assert.assertThat("This may fail, if there are no messages in gitlab", messages, Matchers.arrayWithSize(2));
}
}
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment