From 6eff60969665dd249b6609f31472dec177ea255d Mon Sep 17 00:00:00 2001 From: GODrums Date: Sat, 12 Oct 2024 19:10:24 +0200 Subject: [PATCH] Meta Endpoint with repositories --- server/application-server/openapi.yaml | 19 +++ .../www1/hephaestus/meta/MetaController.java | 22 +++ .../in/www1/hephaestus/meta/MetaDataDTO.java | 8 + .../in/www1/hephaestus/meta/MetaService.java | 19 +++ .../modules/openapi/.openapi-generator/FILES | 3 + .../src/app/core/modules/openapi/api/api.ts | 5 +- .../core/modules/openapi/api/meta.service.ts | 157 ++++++++++++++++++ .../openapi/api/meta.serviceInterface.ts | 33 ++++ .../modules/openapi/model/meta-data-dto.ts | 17 ++ .../app/core/modules/openapi/model/models.ts | 1 + webapp/src/app/home/home.component.html | 2 +- webapp/src/app/home/home.component.ts | 7 + .../leaderboard/filter/filter.component.html | 2 +- .../leaderboard/filter/filter.component.ts | 5 +- .../filter/repository/repository.component.ts | 20 ++- 15 files changed, 308 insertions(+), 12 deletions(-) create mode 100644 server/application-server/src/main/java/de/tum/in/www1/hephaestus/meta/MetaController.java create mode 100644 server/application-server/src/main/java/de/tum/in/www1/hephaestus/meta/MetaDataDTO.java create mode 100644 server/application-server/src/main/java/de/tum/in/www1/hephaestus/meta/MetaService.java create mode 100644 webapp/src/app/core/modules/openapi/api/meta.service.ts create mode 100644 webapp/src/app/core/modules/openapi/api/meta.serviceInterface.ts create mode 100644 webapp/src/app/core/modules/openapi/model/meta-data-dto.ts diff --git a/server/application-server/openapi.yaml b/server/application-server/openapi.yaml index 65ed51de..bc2f0c88 100644 --- a/server/application-server/openapi.yaml +++ b/server/application-server/openapi.yaml @@ -89,6 +89,18 @@ paths: type: array items: $ref: "#/components/schemas/PullRequest" + /meta: + get: + tags: + - meta + operationId: getMetaData + responses: + "200": + description: OK + content: + application/json: + schema: + $ref: "#/components/schemas/MetaDataDTO" /leaderboard: get: tags: @@ -474,6 +486,13 @@ components: type: array items: $ref: "#/components/schemas/PullRequestReview" + MetaDataDTO: + type: object + properties: + repositoriesToMonitor: + type: array + items: + type: string LeaderboardEntry: type: object properties: diff --git a/server/application-server/src/main/java/de/tum/in/www1/hephaestus/meta/MetaController.java b/server/application-server/src/main/java/de/tum/in/www1/hephaestus/meta/MetaController.java new file mode 100644 index 00000000..ff83ce16 --- /dev/null +++ b/server/application-server/src/main/java/de/tum/in/www1/hephaestus/meta/MetaController.java @@ -0,0 +1,22 @@ +package de.tum.in.www1.hephaestus.meta; + +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; + +@RestController +@RequestMapping("/meta") +public class MetaController { + + private final MetaService metaService; + + public MetaController(MetaService metaService) { + this.metaService = metaService; + } + + @GetMapping + public ResponseEntity getMetaData() { + return ResponseEntity.ok(metaService.getMetaData()); + } +} diff --git a/server/application-server/src/main/java/de/tum/in/www1/hephaestus/meta/MetaDataDTO.java b/server/application-server/src/main/java/de/tum/in/www1/hephaestus/meta/MetaDataDTO.java new file mode 100644 index 00000000..cf116807 --- /dev/null +++ b/server/application-server/src/main/java/de/tum/in/www1/hephaestus/meta/MetaDataDTO.java @@ -0,0 +1,8 @@ +package de.tum.in.www1.hephaestus.meta; + +import com.fasterxml.jackson.annotation.JsonInclude; + +@JsonInclude(JsonInclude.Include.NON_EMPTY) +public record MetaDataDTO(String[] repositoriesToMonitor) { + +} diff --git a/server/application-server/src/main/java/de/tum/in/www1/hephaestus/meta/MetaService.java b/server/application-server/src/main/java/de/tum/in/www1/hephaestus/meta/MetaService.java new file mode 100644 index 00000000..24c0b310 --- /dev/null +++ b/server/application-server/src/main/java/de/tum/in/www1/hephaestus/meta/MetaService.java @@ -0,0 +1,19 @@ +package de.tum.in.www1.hephaestus.meta; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.stereotype.Service; + +@Service +public class MetaService { + private static final Logger logger = LoggerFactory.getLogger(MetaService.class); + + @Value("${monitoring.repositories}") + private String[] repositoriesToMonitor; + + public MetaDataDTO getMetaData() { + logger.info("Getting meta data..."); + return new MetaDataDTO(repositoriesToMonitor); + } +} diff --git a/webapp/src/app/core/modules/openapi/.openapi-generator/FILES b/webapp/src/app/core/modules/openapi/.openapi-generator/FILES index de16ac04..e5544a76 100644 --- a/webapp/src/app/core/modules/openapi/.openapi-generator/FILES +++ b/webapp/src/app/core/modules/openapi/.openapi-generator/FILES @@ -7,6 +7,8 @@ api/admin.serviceInterface.ts api/api.ts api/leaderboard.service.ts api/leaderboard.serviceInterface.ts +api/meta.service.ts +api/meta.serviceInterface.ts api/pull-request.service.ts api/pull-request.serviceInterface.ts api/user.service.ts @@ -18,6 +20,7 @@ index.ts model/issue-comment-dto.ts model/issue-comment.ts model/leaderboard-entry.ts +model/meta-data-dto.ts model/models.ts model/pull-request-dto.ts model/pull-request-label.ts diff --git a/webapp/src/app/core/modules/openapi/api/api.ts b/webapp/src/app/core/modules/openapi/api/api.ts index 30528fda..d8152646 100644 --- a/webapp/src/app/core/modules/openapi/api/api.ts +++ b/webapp/src/app/core/modules/openapi/api/api.ts @@ -4,10 +4,13 @@ export * from './admin.serviceInterface'; export * from './leaderboard.service'; import { LeaderboardService } from './leaderboard.service'; export * from './leaderboard.serviceInterface'; +export * from './meta.service'; +import { MetaService } from './meta.service'; +export * from './meta.serviceInterface'; export * from './pull-request.service'; import { PullRequestService } from './pull-request.service'; export * from './pull-request.serviceInterface'; export * from './user.service'; import { UserService } from './user.service'; export * from './user.serviceInterface'; -export const APIS = [AdminService, LeaderboardService, PullRequestService, UserService]; +export const APIS = [AdminService, LeaderboardService, MetaService, PullRequestService, UserService]; diff --git a/webapp/src/app/core/modules/openapi/api/meta.service.ts b/webapp/src/app/core/modules/openapi/api/meta.service.ts new file mode 100644 index 00000000..a3da5964 --- /dev/null +++ b/webapp/src/app/core/modules/openapi/api/meta.service.ts @@ -0,0 +1,157 @@ +/** + * Hephaestus API + * API documentation for the Hephaestus application server. + * + * The version of the OpenAPI document: 0.0.1 + * Contact: felixtj.dietrich@tum.de + * + * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). + * https://openapi-generator.tech + * Do not edit the class manually. + */ +/* tslint:disable:no-unused-variable member-ordering */ + +import { Inject, Injectable, Optional } from '@angular/core'; +import { HttpClient, HttpHeaders, HttpParams, + HttpResponse, HttpEvent, HttpParameterCodec, HttpContext + } from '@angular/common/http'; +import { CustomHttpParameterCodec } from '../encoder'; +import { Observable } from 'rxjs'; + +// @ts-ignore +import { MetaDataDTO } from '../model/meta-data-dto'; + +// @ts-ignore +import { BASE_PATH, COLLECTION_FORMATS } from '../variables'; +import { Configuration } from '../configuration'; +import { + MetaServiceInterface +} from './meta.serviceInterface'; + + + +@Injectable({ + providedIn: 'root' +}) +export class MetaService implements MetaServiceInterface { + + protected basePath = 'http://localhost'; + public defaultHeaders = new HttpHeaders(); + public configuration = new Configuration(); + public encoder: HttpParameterCodec; + + constructor(protected httpClient: HttpClient, @Optional()@Inject(BASE_PATH) basePath: string|string[], @Optional() configuration: Configuration) { + if (configuration) { + this.configuration = configuration; + } + if (typeof this.configuration.basePath !== 'string') { + const firstBasePath = Array.isArray(basePath) ? basePath[0] : undefined; + if (firstBasePath != undefined) { + basePath = firstBasePath; + } + + if (typeof basePath !== 'string') { + basePath = this.basePath; + } + this.configuration.basePath = basePath; + } + this.encoder = this.configuration.encoder || new CustomHttpParameterCodec(); + } + + + // @ts-ignore + private addToHttpParams(httpParams: HttpParams, value: any, key?: string): HttpParams { + if (typeof value === "object" && value instanceof Date === false) { + httpParams = this.addToHttpParamsRecursive(httpParams, value); + } else { + httpParams = this.addToHttpParamsRecursive(httpParams, value, key); + } + return httpParams; + } + + private addToHttpParamsRecursive(httpParams: HttpParams, value?: any, key?: string): HttpParams { + if (value == null) { + return httpParams; + } + + if (typeof value === "object") { + if (Array.isArray(value)) { + (value as any[]).forEach( elem => httpParams = this.addToHttpParamsRecursive(httpParams, elem, key)); + } else if (value instanceof Date) { + if (key != null) { + httpParams = httpParams.append(key, (value as Date).toISOString().substring(0, 10)); + } else { + throw Error("key may not be null if value is Date"); + } + } else { + Object.keys(value).forEach( k => httpParams = this.addToHttpParamsRecursive( + httpParams, value[k], key != null ? `${key}.${k}` : k)); + } + } else if (key != null) { + httpParams = httpParams.append(key, value); + } else { + throw Error("key may not be null if value is not object or array"); + } + return httpParams; + } + + /** + * @param observe set whether or not to return the data Observable as the body, response or events. defaults to returning the body. + * @param reportProgress flag to report request and response progress. + */ + public getMetaData(observe?: 'body', reportProgress?: boolean, options?: {httpHeaderAccept?: 'application/json', context?: HttpContext, transferCache?: boolean}): Observable; + public getMetaData(observe?: 'response', reportProgress?: boolean, options?: {httpHeaderAccept?: 'application/json', context?: HttpContext, transferCache?: boolean}): Observable>; + public getMetaData(observe?: 'events', reportProgress?: boolean, options?: {httpHeaderAccept?: 'application/json', context?: HttpContext, transferCache?: boolean}): Observable>; + public getMetaData(observe: any = 'body', reportProgress: boolean = false, options?: {httpHeaderAccept?: 'application/json', context?: HttpContext, transferCache?: boolean}): Observable { + + let localVarHeaders = this.defaultHeaders; + + let localVarHttpHeaderAcceptSelected: string | undefined = options && options.httpHeaderAccept; + if (localVarHttpHeaderAcceptSelected === undefined) { + // to determine the Accept header + const httpHeaderAccepts: string[] = [ + 'application/json' + ]; + localVarHttpHeaderAcceptSelected = this.configuration.selectHeaderAccept(httpHeaderAccepts); + } + if (localVarHttpHeaderAcceptSelected !== undefined) { + localVarHeaders = localVarHeaders.set('Accept', localVarHttpHeaderAcceptSelected); + } + + let localVarHttpContext: HttpContext | undefined = options && options.context; + if (localVarHttpContext === undefined) { + localVarHttpContext = new HttpContext(); + } + + let localVarTransferCache: boolean | undefined = options && options.transferCache; + if (localVarTransferCache === undefined) { + localVarTransferCache = true; + } + + + let responseType_: 'text' | 'json' | 'blob' = 'json'; + if (localVarHttpHeaderAcceptSelected) { + if (localVarHttpHeaderAcceptSelected.startsWith('text')) { + responseType_ = 'text'; + } else if (this.configuration.isJsonMime(localVarHttpHeaderAcceptSelected)) { + responseType_ = 'json'; + } else { + responseType_ = 'blob'; + } + } + + let localVarPath = `/meta`; + return this.httpClient.request('get', `${this.configuration.basePath}${localVarPath}`, + { + context: localVarHttpContext, + responseType: responseType_, + withCredentials: this.configuration.withCredentials, + headers: localVarHeaders, + observe: observe, + transferCache: localVarTransferCache, + reportProgress: reportProgress + } + ); + } + +} diff --git a/webapp/src/app/core/modules/openapi/api/meta.serviceInterface.ts b/webapp/src/app/core/modules/openapi/api/meta.serviceInterface.ts new file mode 100644 index 00000000..f6fb5e53 --- /dev/null +++ b/webapp/src/app/core/modules/openapi/api/meta.serviceInterface.ts @@ -0,0 +1,33 @@ +/** + * Hephaestus API + * API documentation for the Hephaestus application server. + * + * The version of the OpenAPI document: 0.0.1 + * Contact: felixtj.dietrich@tum.de + * + * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). + * https://openapi-generator.tech + * Do not edit the class manually. + */ +import { HttpHeaders } from '@angular/common/http'; + +import { Observable } from 'rxjs'; + +import { MetaDataDTO } from '../model/models'; + + +import { Configuration } from '../configuration'; + + + +export interface MetaServiceInterface { + defaultHeaders: HttpHeaders; + configuration: Configuration; + + /** + * + * + */ + getMetaData(extraHttpRequestParams?: any): Observable; + +} diff --git a/webapp/src/app/core/modules/openapi/model/meta-data-dto.ts b/webapp/src/app/core/modules/openapi/model/meta-data-dto.ts new file mode 100644 index 00000000..0049533e --- /dev/null +++ b/webapp/src/app/core/modules/openapi/model/meta-data-dto.ts @@ -0,0 +1,17 @@ +/** + * Hephaestus API + * API documentation for the Hephaestus application server. + * + * The version of the OpenAPI document: 0.0.1 + * Contact: felixtj.dietrich@tum.de + * + * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). + * https://openapi-generator.tech + * Do not edit the class manually. + */ + + +export interface MetaDataDTO { + repositoriesToMonitor?: Array; +} + diff --git a/webapp/src/app/core/modules/openapi/model/models.ts b/webapp/src/app/core/modules/openapi/model/models.ts index 3f1ef3af..349ec7a5 100644 --- a/webapp/src/app/core/modules/openapi/model/models.ts +++ b/webapp/src/app/core/modules/openapi/model/models.ts @@ -1,6 +1,7 @@ export * from './issue-comment'; export * from './issue-comment-dto'; export * from './leaderboard-entry'; +export * from './meta-data-dto'; export * from './pull-request'; export * from './pull-request-dto'; export * from './pull-request-label'; diff --git a/webapp/src/app/home/home.component.html b/webapp/src/app/home/home.component.html index 9b02ad9a..534696f9 100644 --- a/webapp/src/app/home/home.component.html +++ b/webapp/src/app/home/home.component.html @@ -8,7 +8,7 @@

Artemis Leaderboard

Hi {{ userValue.name }} 👋

} - +
@if (query.error()) { diff --git a/webapp/src/app/home/home.component.ts b/webapp/src/app/home/home.component.ts index 4e9e71e7..b30b692c 100644 --- a/webapp/src/app/home/home.component.ts +++ b/webapp/src/app/home/home.component.ts @@ -11,6 +11,7 @@ import { LeaderboardComponent } from '@app/home/leaderboard/leaderboard.componen import { LeaderboardFilterComponent } from './leaderboard/filter/filter.component'; import { SecurityStore } from '@app/core/security/security-store.service'; import { HlmAlertModule } from '@spartan-ng/ui-alert-helm'; +import { MetaService } from '@app/core/modules/openapi'; dayjs.extend(isoWeek); @@ -24,6 +25,7 @@ export class HomeComponent { protected CircleX = CircleX; securityStore = inject(SecurityStore); + metaService = inject(MetaService); leaderboardService = inject(LeaderboardService); signedIn = this.securityStore.signedIn; @@ -44,4 +46,9 @@ export class HomeComponent { ) ) })); + + metaQuery = injectQuery(() => ({ + queryKey: ['meta'], + queryFn: async () => lastValueFrom(this.metaService.getMetaData()) + })); } diff --git a/webapp/src/app/home/leaderboard/filter/filter.component.html b/webapp/src/app/home/leaderboard/filter/filter.component.html index fee368f4..3fa36028 100644 --- a/webapp/src/app/home/leaderboard/filter/filter.component.html +++ b/webapp/src/app/home/leaderboard/filter/filter.component.html @@ -5,6 +5,6 @@

Filter

- +
diff --git a/webapp/src/app/home/leaderboard/filter/filter.component.ts b/webapp/src/app/home/leaderboard/filter/filter.component.ts index 16632643..8d8e8d2f 100644 --- a/webapp/src/app/home/leaderboard/filter/filter.component.ts +++ b/webapp/src/app/home/leaderboard/filter/filter.component.ts @@ -1,8 +1,9 @@ -import { Component } from '@angular/core'; +import { Component, input } from '@angular/core'; import { FormsModule } from '@angular/forms'; import { ListFilter, LucideAngularModule } from 'lucide-angular'; import { LeaderboardFilterTimeframeComponent } from './timeframe/timeframe.component'; import { LeaderboardFilterRepositoryComponent } from './repository/repository.component'; +import { MetaDataDTO } from '@app/core/modules/openapi'; @Component({ selector: 'app-leaderboard-filter', @@ -12,4 +13,6 @@ import { LeaderboardFilterRepositoryComponent } from './repository/repository.co }) export class LeaderboardFilterComponent { protected ListFilter = ListFilter; + + metaData = input(); } diff --git a/webapp/src/app/home/leaderboard/filter/repository/repository.component.ts b/webapp/src/app/home/leaderboard/filter/repository/repository.component.ts index b9142865..c247816e 100644 --- a/webapp/src/app/home/leaderboard/filter/repository/repository.component.ts +++ b/webapp/src/app/home/leaderboard/filter/repository/repository.component.ts @@ -1,9 +1,10 @@ -import { Component, computed, effect, signal } from '@angular/core'; +import { Component, computed, effect, input, signal } from '@angular/core'; import { FormsModule } from '@angular/forms'; import { Router, RouterLink } from '@angular/router'; import { BrnSelectModule } from '@spartan-ng/ui-select-brain'; import { HlmSelectModule } from '@spartan-ng/ui-select-helm'; import { HlmLabelModule } from '@spartan-ng/ui-label-helm'; +import { injectQuery } from '@tanstack/angular-query-experimental'; export const repositoryNames = [ 'ls1intum/Artemis', @@ -30,6 +31,7 @@ interface SelectOption { templateUrl: './repository.component.html' }) export class LeaderboardFilterRepositoryComponent { + repositories = input(); value = signal(''); placeholder = computed(() => { @@ -37,13 +39,15 @@ export class LeaderboardFilterRepositoryComponent { }); options = computed(() => { - const options: SelectOption[] = repositoryNames.map((name, index) => { - return { - id: index + 1, - value: name, - label: name - }; - }); + const options: SelectOption[] = !this.repositories() + ? [] + : this.repositories()!.map((name, index) => { + return { + id: index + 1, + value: name, + label: name + }; + }); options.unshift({ id: 0, value: 'all',