diff --git a/.classpath b/.classpath index 1badeb561c4cbb328db13cd6bc617b79b310386a..6e0021feb305ffa1cda191a1eb3c4d28089b9cf0 100644 --- a/.classpath +++ b/.classpath @@ -6,12 +6,6 @@ <attribute name="maven.pomderived" value="true"/> </attributes> </classpathentry> - <classpathentry kind="src" output="target/test-classes" path="target/generated-test-sources/test-annotations"> - <attributes> - <attribute name="test" value="true"/> - </attributes> - </classpathentry> - <classpathentry kind="src" path="target/generated-sources/annotations"/> <classpathentry excluding="**" kind="src" output="target/classes" path="src/main/resources"> <attributes> <attribute name="maven.pomderived" value="true"/> @@ -19,20 +13,19 @@ </classpathentry> <classpathentry kind="src" output="target/test-classes" path="src/test/java"> <attributes> - <attribute name="test" value="true"/> <attribute name="optional" value="true"/> <attribute name="maven.pomderived" value="true"/> + <attribute name="test" value="true"/> </attributes> </classpathentry> <classpathentry excluding="**" kind="src" output="target/test-classes" path="src/test/resources"> <attributes> - <attribute name="test" value="true"/> <attribute name="maven.pomderived" value="true"/> + <attribute name="test" value="true"/> </attributes> </classpathentry> <classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-11"> <attributes> - <attribute name="module" value="true"/> <attribute name="maven.pomderived" value="true"/> </attributes> </classpathentry> @@ -41,5 +34,22 @@ <attribute name="maven.pomderived" value="true"/> </attributes> </classpathentry> + <classpathentry kind="src" path="target/generated-sources/annotations"> + <attributes> + <attribute name="optional" value="true"/> + <attribute name="maven.pomderived" value="true"/> + <attribute name="ignore_optional_problems" value="true"/> + <attribute name="m2e-apt" value="true"/> + </attributes> + </classpathentry> + <classpathentry kind="src" output="target/test-classes" path="target/generated-test-sources/test-annotations"> + <attributes> + <attribute name="optional" value="true"/> + <attribute name="maven.pomderived" value="true"/> + <attribute name="ignore_optional_problems" value="true"/> + <attribute name="m2e-apt" value="true"/> + <attribute name="test" value="true"/> + </attributes> + </classpathentry> <classpathentry kind="output" path="target/classes"/> </classpath> diff --git a/.gitignore b/.gitignore index eb314f1436cf4862f5d1403630118baaf2a2470c..91274bca2c6044c28c5190fac42c907c9f906ba1 100644 --- a/.gitignore +++ b/.gitignore @@ -32,6 +32,7 @@ tmp/**/* local.properties .loadpath .factorypath +.settings/org.eclipse.jdt.apt.core.prefs # CDT-specific diff --git a/.project b/.project index 6c9cf5fa2bbdf2f2bd2c600d1a4712e2ff3d8b93..53f18a33bd1b691236a6099e91dc8fda2930bf99 100644 --- a/.project +++ b/.project @@ -39,4 +39,15 @@ <nature>org.eclipse.jdt.core.javanature</nature> <nature>org.eclipse.m2e.core.maven2Nature</nature> </natures> + <filteredResources> + <filter> + <id>1617023900149</id> + <name></name> + <type>30</type> + <matcher> + <id>org.eclipse.core.resources.regexFilterMatcher</id> + <arguments>node_modules|.git|__CREATED_BY_JAVA_LANGUAGE_SERVER__</arguments> + </matcher> + </filter> + </filteredResources> </projectDescription> diff --git a/.settings/org.eclipse.jdt.apt.core.prefs b/.settings/org.eclipse.jdt.apt.core.prefs new file mode 100644 index 0000000000000000000000000000000000000000..dfa4f3adb289a4f7d73e0694f9045f99325604d7 --- /dev/null +++ b/.settings/org.eclipse.jdt.apt.core.prefs @@ -0,0 +1,4 @@ +eclipse.preferences.version=1 +org.eclipse.jdt.apt.aptEnabled=true +org.eclipse.jdt.apt.genSrcDir=target/generated-sources/annotations +org.eclipse.jdt.apt.genTestSrcDir=target/generated-test-sources/test-annotations diff --git a/.settings/org.eclipse.jdt.core.prefs b/.settings/org.eclipse.jdt.core.prefs index 2bd4e77333c71a363a69f94617a46fdac942825d..93d8913371f1c8c83ccd5db57c650ef42e4dac72 100644 --- a/.settings/org.eclipse.jdt.core.prefs +++ b/.settings/org.eclipse.jdt.core.prefs @@ -12,5 +12,6 @@ org.eclipse.jdt.core.compiler.problem.enablePreviewFeatures=disabled org.eclipse.jdt.core.compiler.problem.enumIdentifier=error org.eclipse.jdt.core.compiler.problem.forbiddenReference=warning org.eclipse.jdt.core.compiler.problem.reportPreviewFeatures=warning +org.eclipse.jdt.core.compiler.processAnnotations=enabled org.eclipse.jdt.core.compiler.release=disabled org.eclipse.jdt.core.compiler.source=11 diff --git a/src/main/java/at/ac/uibk/gitsearch/service/GitlabService.java b/src/main/java/at/ac/uibk/gitsearch/service/GitlabService.java index efe0af09a277b9fcc099f22a9d31ebc9bccd990a..f6f27f359779f58c2ee81698b9b105e3944c792b 100644 --- a/src/main/java/at/ac/uibk/gitsearch/service/GitlabService.java +++ b/src/main/java/at/ac/uibk/gitsearch/service/GitlabService.java @@ -20,6 +20,7 @@ import org.springframework.util.StreamUtils; import org.springframework.util.StringUtils; import at.ac.uibk.gitsearch.repository.gitlab.GitLabRepository; +import at.ac.uibk.gitsearch.security.jwt.TokenProvider; /** * Service for exercise/course search results @@ -30,14 +31,16 @@ public class GitlabService { @Autowired private GitLabRepository gitLabRepository; + @Autowired + protected TokenProvider tokenProvider; private final Logger log = LoggerFactory.getLogger(ShoppingBasketService.class); public Boolean repositoryExists(String projectID) { - final GitLabApi gitLabApi = gitLabRepository.getGitLabApi(); - final ProjectApi gitLabProjectApi = gitLabApi.getProjectApi(); try{ + final GitLabApi gitLabApi = gitLabRepository.getGitLabApi(tokenProvider.getGitLabAccessInfo()); + final ProjectApi gitLabProjectApi = gitLabApi.getProjectApi(); return gitLabProjectApi.getProject(projectID) != null;} catch(GitLabApiException e){ log.error(e.getMessage(), e); @@ -48,7 +51,7 @@ public class GitlabService { public InputStream getRepositoryZip(String exerciseID) throws GitLabApiException, IOException { - final GitLabApi gitLabApi = gitLabRepository.getGitLabApi(Optional.empty()); + final GitLabApi gitLabApi = gitLabRepository.getGitLabApi(tokenProvider.getGitLabAccessInfo()); return rePackageGitLabProjectZip(new ZipInputStream(gitLabApi.getRepositoryApi().getRepositoryArchive(exerciseID, "HEAD", "zip")), "from project " + exerciseID); } diff --git a/src/main/java/at/ac/uibk/gitsearch/service/SearchService.java b/src/main/java/at/ac/uibk/gitsearch/service/SearchService.java index 4e99536ca3d5c8d6f1b164255d505535b679796e..f00f1fdbb73f0c310156f1596f53a11d43444f14 100644 --- a/src/main/java/at/ac/uibk/gitsearch/service/SearchService.java +++ b/src/main/java/at/ac/uibk/gitsearch/service/SearchService.java @@ -244,8 +244,8 @@ public class SearchService { return copyInputStreamToFile(inputFile, file);} catch(GitLabApiException exception){ log.error(exception.getMessage()); + return null; } - return null; } private File copyInputStreamToFile(InputStream inputStream, File file) throws IOException { diff --git a/src/main/java/at/ac/uibk/gitsearch/web/rest/StatisticsResource.java b/src/main/java/at/ac/uibk/gitsearch/web/rest/StatisticsResource.java index aff0f6d515fd975a8b9cfbe8d218a3f34849686d..759cbad8d83b2c2a3cfc22290864e9cb16666b2f 100644 --- a/src/main/java/at/ac/uibk/gitsearch/web/rest/StatisticsResource.java +++ b/src/main/java/at/ac/uibk/gitsearch/web/rest/StatisticsResource.java @@ -163,7 +163,7 @@ public class StatisticsResource { return ResponseUtil.wrapOrNotFound(statisticsDTO); } else{ - return null; + return ResponseUtil.wrapOrNotFound(statisticsDTO); } } diff --git a/src/main/resources/config/application-prod.yml b/src/main/resources/config/application-prod.yml index b5cf61124a4a7d950c7437b9bbea0141a2f1df8b..171d4e70b0a935593787d5d334a2b1194e04c597 100644 --- a/src/main/resources/config/application-prod.yml +++ b/src/main/resources/config/application-prod.yml @@ -74,7 +74,6 @@ spring: client-id: ${SECURITY_OAUTH2_CLIENT_REGISTRATION_GITLABOIDC_CLIENTID} client-secret: ${SECURITY_OAUTH2_CLIENT_REGISTRATION_GITLABOIDC_CLIENTSECRET} - # =================================================================== # To enable TLS in production, generate a certificate using: # keytool -genkey -alias gitsearch -storetype PKCS12 -keyalg RSA -keysize 2048 -keystore keystore.p12 -validity 3650 @@ -154,7 +153,7 @@ jhipster: application: registeredPlugins: - - "https://artemis.codeability.uibk.ac.at/api/sharing/config" + - 'https://artemis.codeability.uibk.ac.at/api/sharing/config' gitlab: - url: https://sharing-codeability.uibk.ac.at/ - generalAccessToken: ${APPLICATION_GITLAB_GENERALACCESSTOKEN} + url: https://sharing-codeability.uibk.ac.at/ + generalAccessToken: ${APPLICATION_GITLAB_GENERALACCESSTOKEN} diff --git a/src/main/webapp/app/admin/health/health.component.ts b/src/main/webapp/app/admin/health/health.component.ts index 509cc0b531f6e29d5de94f9ce0109211fa3fe400..3dd1a9c4f985cf0432450b4206b7da6627aedd6d 100644 --- a/src/main/webapp/app/admin/health/health.component.ts +++ b/src/main/webapp/app/admin/health/health.component.ts @@ -42,9 +42,9 @@ export class HealthComponent implements OnInit { const modalRef = this.modalService.open(HealthModalComponent); modalRef.componentInstance.health = health; } - + translateKeys(key: string) { - if ( key.startsWith('health.indicator.http')) return key.substr(17); + if (key.startsWith('health.indicator.http')) return key.substr(17); return this.translateService.instant(key); -} + } } diff --git a/src/main/webapp/app/app.module.ts b/src/main/webapp/app/app.module.ts index 90a55ad673d9537e9249b7af5ee8577c28ee39cb..f82548f29847e655fbc97ca611a1cd79bf714c7a 100644 --- a/src/main/webapp/app/app.module.ts +++ b/src/main/webapp/app/app.module.ts @@ -16,7 +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' +import { CacheService } from 'app/shared/service/cache.service'; @NgModule({ imports: [ @@ -30,17 +30,8 @@ import { CacheService } from 'app/shared/service/cache.service' GitSearchV2AppRoutingModule, QueryParamModule, ], - declarations: [ - MainComponent, - NavbarComponent, - ErrorComponent, - PageRibbonComponent, - ActiveMenuDirective, - FooterComponent], + declarations: [MainComponent, NavbarComponent, ErrorComponent, PageRibbonComponent, ActiveMenuDirective, FooterComponent], bootstrap: [MainComponent], - providers: [ - CacheService, - ] - + providers: [CacheService], }) export class GitSearchV2AppModule {} diff --git a/src/main/webapp/app/core/application/applicationInfo.service.ts b/src/main/webapp/app/core/application/applicationInfo.service.ts index b235c0c19a8b610d160806bbe8d1c3d6bf12ab53..980a98ba4e173d495047513af89d6c412422442e 100644 --- a/src/main/webapp/app/core/application/applicationInfo.service.ts +++ b/src/main/webapp/app/core/application/applicationInfo.service.ts @@ -1,38 +1,33 @@ import { Injectable } from '@angular/core'; import { HttpClient } from '@angular/common/http'; -import { Observable, } from 'rxjs'; +import { Observable } from 'rxjs'; import { SERVER_API_URL } from 'app/app.constants'; export class DeploymentInfo { - branch = ""; - commitId = ""; + branch = ''; + commitId = ''; } @Injectable({ providedIn: 'root' }) export class ApplicationInfoService { - cachedDeploymentInfo: DeploymentInfo; inLoading = false; - constructor( - private http: HttpClient, - ) { - this.cachedDeploymentInfo = {} as DeploymentInfo; - } + constructor(private http: HttpClient) { + this.cachedDeploymentInfo = {} as DeploymentInfo; + } private loadDeploymentInfo(): Observable<DeploymentInfo> { return this.http.get<DeploymentInfo>(SERVER_API_URL + 'api/applicationInfo/deploymentInfo'); } - + public getDeploymentInfo(): DeploymentInfo { - if(!this.cachedDeploymentInfo.branch && !this.inLoading) { - this.inLoading = true; - this.loadDeploymentInfo().subscribe((res) => { - this.cachedDeploymentInfo = res; - this.inLoading = false; - }); + if (!this.cachedDeploymentInfo.branch && !this.inLoading) { + this.inLoading = true; + this.loadDeploymentInfo().subscribe(res => { + this.cachedDeploymentInfo = res; + this.inLoading = false; + }); } return this.cachedDeploymentInfo; - } - - + } } diff --git a/src/main/webapp/app/exercise/exercise-card/exercise-card.component.ts b/src/main/webapp/app/exercise/exercise-card/exercise-card.component.ts index 73c759ab444b6d3d1cdfe824387840cdb8ba5540..151f8ba9580d9534adef45391caad3f42a322679 100644 --- a/src/main/webapp/app/exercise/exercise-card/exercise-card.component.ts +++ b/src/main/webapp/app/exercise/exercise-card/exercise-card.component.ts @@ -26,7 +26,7 @@ export class ExerciseCardComponent implements OnInit { this.exercise!.views = data.views!; this.exercise!.downloads = data.downloads!; }, - () => alert('Request failed') + () => alert('Could not load exercise statistics') ); } this.exerciseSelectionEvent.emit(this.exercise); diff --git a/src/main/webapp/app/exercise/exercise-details/exercise-details.component.ts b/src/main/webapp/app/exercise/exercise-details/exercise-details.component.ts index 109966ff106134e19841da8c02f99fca88e4b533..072b7434e434f6f6f355168e4d957dbc04cc8995 100644 --- a/src/main/webapp/app/exercise/exercise-details/exercise-details.component.ts +++ b/src/main/webapp/app/exercise/exercise-details/exercise-details.component.ts @@ -5,8 +5,6 @@ import { Subscription } from 'rxjs'; import { PluginActionInfo } from 'app/shared/model/search/search-result-dto.model'; import { PluginService } from 'app/shared/service/plugin-service'; import { ShoppingBasketInfo, ShoppingBasketRedirectInfoDTO } from 'app/shared/model/basket/shopping-basket-info.model'; -import { HttpErrorResponse } from '@angular/common/http'; - import { AccountService } from 'app/core/auth/account.service'; import { Account } from 'app/core/user/account.model'; import { SearchService } from 'app/search/service/search-service.ts'; @@ -51,7 +49,7 @@ export class ExerciseDetailsComponent implements OnInit, OnDestroy { } public getPersonDetailsWithEmail(person: Person): string { - return "<a class='text-dark' href= 'mailto:'" + person.email + '>' + person.name + ', ' + person.affiliation + '</a>'; + return "<a class='text-dark' href='mailto:" + person.email + "'>" + person.name + ', ' + person.affiliation + '</a>'; } public arrayToString(array: string[]): string { @@ -71,22 +69,21 @@ export class ExerciseDetailsComponent implements OnInit, OnDestroy { return result; } - public startAction(action: PluginActionInfo, exercise: Exercise): void { - const basketInfo: ShoppingBasketInfo = { - plugin: action.plugin, - action: action.action, - itemInfos: [ - exercise.originalResult - ] - }; - this.pluginService.getRedirectLink(basketInfo).subscribe( - (redirectInfo: ShoppingBasketRedirectInfoDTO) => { - // alert('redirecting to ' + redirectInfo.redirectURL); - // location.href = redirectInfo.redirectURL; - window.open(redirectInfo.redirectURL, action.action); - }, - () => alert('Search failed')) - } + public startAction(action: PluginActionInfo, exercise: Exercise): void { + const basketInfo: ShoppingBasketInfo = { + plugin: action.plugin, + action: action.action, + itemInfos: [exercise.originalResult], + }; + this.pluginService.getRedirectLink(basketInfo).subscribe( + (redirectInfo: ShoppingBasketRedirectInfoDTO) => { + // alert('redirecting to ' + redirectInfo.redirectURL); + // location.href = redirectInfo.redirectURL; + window.open(redirectInfo.redirectURL, action.action); + }, + () => alert('Search failed') + ); + } public download(): void { this.exportExercise(Number(this.exercise!.originalResult.project.project_id)); @@ -107,7 +104,7 @@ export class ExerciseDetailsComponent implements OnInit, OnDestroy { window.URL.revokeObjectURL(url); } }, - (error: HttpErrorResponse) => this.jhiAlertService.error('Unable to export exercise. Error: ' + error.message) + () => alert('Unable to export exercise. Please log in to export.') ); } diff --git a/src/main/webapp/app/layouts/main/main.component.ts b/src/main/webapp/app/layouts/main/main.component.ts index 1c8a8fa17fec8834ef50ed8ca0852af3fece0c9a..81943c2ed5148cffb0b77858335510debb880f38 100644 --- a/src/main/webapp/app/layouts/main/main.component.ts +++ b/src/main/webapp/app/layouts/main/main.component.ts @@ -6,7 +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 { MessageService, BroadCastMessage } from 'app/shared/service/message-service'; // import { AlertErrorComponent } from 'app/shared/alert/alert-error.component'; @Component({ @@ -24,9 +24,8 @@ export class MainComponent implements OnInit { rootRenderer: RendererFactory2, private authServerProvider: AuthServerProvider, private cookieService: CookieService, - private messageService: MessageService, - ) // private alertErrorComponent: AlertErrorComponent - { + private messageService: MessageService // private alertErrorComponent: AlertErrorComponent + ) { this.renderer = rootRenderer.createRenderer(document.querySelector('html'), null); } @@ -50,11 +49,10 @@ export class MainComponent implements OnInit { this.renderer.setAttribute(document.querySelector('html'), 'lang', langChangeEvent.lang); }); } - - public getActiveMessages(): Array<BroadCastMessage> { - return this.messageService.getActiveMessages(); // - } + public getActiveMessages(): Array<BroadCastMessage> { + return this.messageService.getActiveMessages(); // + } private checkRequestToken(): void { const tokenCookie = this.cookieService.get('tempRequestToken'); diff --git a/src/main/webapp/app/search/search-input/search-input.component.ts b/src/main/webapp/app/search/search-input/search-input.component.ts index a3a5f469dabe812e717ca394135d0973dc741685..98a88b1e90aafd53ff9ce26008071d7af933c053 100644 --- a/src/main/webapp/app/search/search-input/search-input.component.ts +++ b/src/main/webapp/app/search/search-input/search-input.component.ts @@ -4,16 +4,16 @@ import { ActivatedRoute, Router } from '@angular/router'; import { QueryParam, QueryParamBuilder, QueryParamGroup } from '@ngqp/core'; import { takeUntil } from 'rxjs/operators'; import { SearchInput } from 'app/shared/model/search/search-input.model'; -import { SearchService } from '../service/search-service' +import { SearchService } from '../service/search-service'; import { faQuestion } from '@fortawesome/free-solid-svg-icons'; -import {Observable} from 'rxjs'; -import {debounceTime, distinctUntilChanged, map, switchMap} from 'rxjs/operators'; +import { Observable } from 'rxjs'; +import { debounceTime, distinctUntilChanged, map, switchMap } from 'rxjs/operators'; -import {IExerciseType } from 'app/shared/model/exercise.model'; +import { IExerciseType } from 'app/shared/model/exercise.model'; interface LooseObject { - [key: string]: any + [key: string]: any; } @Component({ @@ -26,9 +26,9 @@ export class SearchInputComponent implements OnInit, OnDestroy { @Output() searchInputEvent = new EventEmitter<SearchInput>(); public pageSize = 4; - + typeValues = Object.keys(IExerciseType); - + questionIcon = faQuestion; public paramGroup: QueryParamGroup; private componentDestroyed$ = new Subject<void>(); @@ -37,15 +37,67 @@ export class SearchInputComponent implements OnInit, OnDestroy { public showSearchUsage = false; - private states = ['Alabama', 'Alaska', 'American Samoa', 'Arizona', 'Arkansas', 'California', 'Colorado', - 'Connecticut', 'Delaware', 'District Of Columbia', 'Federated States Of Micronesia', 'Florida', 'Georgia', - 'Guam', 'Hawaii', 'Idaho', 'Illinois', 'Indiana', 'Iowa', 'Kansas', 'Kentucky', 'Louisiana', 'Maine', - 'Marshall Islands', 'Maryland', 'Massachusetts', 'Michigan', 'Minnesota', 'Mississippi', 'Missouri', 'Montana', - 'Nebraska', 'Nevada', 'New Hampshire', 'New Jersey', 'New Mexico', 'New York', 'North Carolina', 'North Dakota', - 'Northern Mariana Islands', 'Ohio', 'Oklahoma', 'Oregon', 'Palau', 'Pennsylvania', 'Puerto Rico', 'Rhode Island', - 'South Carolina', 'South Dakota', 'Tennessee', 'Texas', 'Utah', 'Vermont', 'Virgin Islands', 'Virginia', - 'Washington', 'West Virginia', 'Wisconsin', 'Wyoming']; - + private states = [ + 'Alabama', + 'Alaska', + 'American Samoa', + 'Arizona', + 'Arkansas', + 'California', + 'Colorado', + 'Connecticut', + 'Delaware', + 'District Of Columbia', + 'Federated States Of Micronesia', + 'Florida', + 'Georgia', + 'Guam', + 'Hawaii', + 'Idaho', + 'Illinois', + 'Indiana', + 'Iowa', + 'Kansas', + 'Kentucky', + 'Louisiana', + 'Maine', + 'Marshall Islands', + 'Maryland', + 'Massachusetts', + 'Michigan', + 'Minnesota', + 'Mississippi', + 'Missouri', + 'Montana', + 'Nebraska', + 'Nevada', + 'New Hampshire', + 'New Jersey', + 'New Mexico', + 'New York', + 'North Carolina', + 'North Dakota', + 'Northern Mariana Islands', + 'Ohio', + 'Oklahoma', + 'Oregon', + 'Palau', + 'Pennsylvania', + 'Puerto Rico', + 'Rhode Island', + 'South Carolina', + 'South Dakota', + 'Tennessee', + 'Texas', + 'Utah', + 'Vermont', + 'Virgin Islands', + 'Virginia', + 'Washington', + 'West Virginia', + 'Wisconsin', + 'Wyoming', + ]; constructor( protected activatedRoute: ActivatedRoute, @@ -80,17 +132,15 @@ export class SearchInputComponent implements OnInit, OnDestroy { emptyOn: '', }), }; - + // add type checkbox support - this.typeValues.forEach( - function(type: string): void { - const typeName = type.toString(); - groupDef[typeName] = qpb.stringParam(typeName, { - debounceTime: SearchInputComponent.DEBOUNCE_TIME, - emptyOn: '', - }); - } - ); + this.typeValues.forEach(function (type: string): void { + const typeName = type.toString(); + groupDef[typeName] = qpb.stringParam(typeName, { + debounceTime: SearchInputComponent.DEBOUNCE_TIME, + emptyOn: '', + }); + }); this.paramGroup = qpb.group(groupDef); @@ -98,19 +148,18 @@ export class SearchInputComponent implements OnInit, OnDestroy { this.searchInput.setValues(value); this.searchInputEvent.emit(this.searchInput); }); - } - + getSelectedTypes(): string[] { const result: string[] = []; - this.typeValues.forEach( - type => {if(this.paramGroup.queryParams[type].value) result.push(type)}); + this.typeValues.forEach(type => { + if (this.paramGroup.queryParams[type].value) result.push(type); + }); return result; -} + } ngOnInit(): void {} - ngOnDestroy(): void { this.componentDestroyed$.next(); this.componentDestroyed$.complete(); @@ -123,39 +172,33 @@ export class SearchInputComponent implements OnInit, OnDestroy { public onPageChange(page: number): void { this.pageParam.setValue(page); } - -/* + + /* public itemSelected(): void { this.searchInputEvent.emit(this.searchInput); } */ - autoCompleteContributorCreator = (text$: Observable<string>) => + autoCompleteContributorCreator = (text$: Observable<string>) => text$.pipe( debounceTime(200), distinctUntilChanged(), - switchMap((searchText) => { - if(searchText.length <= 2) return []; - return this.searchService.getContributorCreatorAutoComplete( searchText).pipe(map( - ace => (ace.map(ac => ac.target)) ) ); } - ) - ); - - autoCompleteKeyWords = (text$: Observable<string>) => + switchMap(searchText => { + if (searchText.length <= 2) return []; + return this.searchService.getContributorCreatorAutoComplete(searchText).pipe(map(ace => ace.map(ac => ac.target))); + }) + ); + + autoCompleteKeyWords = (text$: Observable<string>) => text$.pipe( debounceTime(200), distinctUntilChanged(), - switchMap((searchText) => - this.searchService.getKeywordsAutoComplete( searchText).pipe(map( - ace => (ace.map(ac => ac.target)) ) )) - ); + switchMap(searchText => this.searchService.getKeywordsAutoComplete(searchText).pipe(map(ace => ace.map(ac => ac.target)))) + ); - autoCompleteProgrammingLanguage = (text$: Observable<string>) => + autoCompleteProgrammingLanguage = (text$: Observable<string>) => text$.pipe( debounceTime(200), distinctUntilChanged(), - switchMap((searchText) => - this.searchService.getProgrammingLanguageAutoComplete( searchText).pipe(map( - ace => (ace.map(ac => ac.target)) ) )) - ); - + switchMap(searchText => this.searchService.getProgrammingLanguageAutoComplete(searchText).pipe(map(ace => ace.map(ac => ac.target)))) + ); } diff --git a/src/main/webapp/app/search/search.component.ts b/src/main/webapp/app/search/search.component.ts index 2a70db9b233724329c5406d4563117eb7693d7b5..f69a68032f874cb464bb5b976f5aeaafecefc57c 100644 --- a/src/main/webapp/app/search/search.component.ts +++ b/src/main/webapp/app/search/search.component.ts @@ -57,11 +57,11 @@ export class SearchComponent implements OnInit { this.hitCount = searchResultsDTO.hitCount; // fix string to date conversion: unfortunatelly dates are not converted correctly :-() searchResultsDTO.searchResult.forEach(hit => { - // eslint-disable-next-line @typescript-eslint/camelcase - hit.project.last_activity_at = new Date(hit.project.last_activity_at); - // eslint-disable-next-line @typescript-eslint/camelcase - hit.file.indexing_date = new Date(hit.file.indexing_date); - }); + // eslint-disable-next-line @typescript-eslint/camelcase + hit.project.last_activity_at = new Date(hit.project.last_activity_at); + // eslint-disable-next-line @typescript-eslint/camelcase + hit.file.indexing_date = new Date(hit.file.indexing_date); + }); return searchResultsDTO.searchResult.map(this.searchResultToExercise); } diff --git a/src/main/webapp/app/search/service/search-service.ts b/src/main/webapp/app/search/service/search-service.ts index 64719cff3cbd53c344702dba2f22dec85c87260b..094c73a4872597d198037d7b6ceed1006ee2cc26 100644 --- a/src/main/webapp/app/search/service/search-service.ts +++ b/src/main/webapp/app/search/service/search-service.ts @@ -1,3 +1,4 @@ +/* eslint-disable */ import { Injectable } from '@angular/core'; import { HttpClient, HttpParams, HttpResponse } from '@angular/common/http'; import { Observable } from 'rxjs'; @@ -69,8 +70,8 @@ export class SearchService { params: new HttpParams().set('contributorPrefix', prefix), }); } - - getContributorCreatorAutoComplete(prefix: string): Observable<Array<AutoCompletionEntry>> { + + getContributorCreatorAutoComplete(prefix: string): Observable<Array<AutoCompletionEntry>> { const options: HttpParams = new HttpParams(); options.append('keyWordPrefix', prefix); return this.http.get<Array<AutoCompletionEntry>>(this.resourceContributorCreatorAutoCompleteDetails, { @@ -85,7 +86,6 @@ export class SearchService { params: new HttpParams().set('contributorPrefix', prefix), }); } - } export class AutoCompletionEntry { diff --git a/src/main/webapp/app/shared/service/cache.service.ts b/src/main/webapp/app/shared/service/cache.service.ts index d7cc47f008389a999e53eac8c50aa03e285c519e..5873a9d8673084e2e58f6b22d8c7996a11282e9b 100644 --- a/src/main/webapp/app/shared/service/cache.service.ts +++ b/src/main/webapp/app/shared/service/cache.service.ts @@ -1,53 +1,53 @@ -import { Injectable } from '@angular/core' +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 + 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) - } + remove(key: string) { + localStorage.removeItem(key); + } - cleanLocalStorage() { - localStorage.clear() - } + cleanLocalStorage() { + localStorage.clear(); + } } export class LocalStorageSaveOptions { - key = ''; - data: any - expirationMins?: number -} \ No newline at end of file + key = ''; + data: any; + expirationMins?: number; +} diff --git a/src/main/webapp/app/shared/service/message-service.ts b/src/main/webapp/app/shared/service/message-service.ts index 478dcf03633feb1d0a2ab31aab23a72d77cd9f82..8efcf3d69d1c789f47630ab1c7308cf1f6b92b7f 100644 --- a/src/main/webapp/app/shared/service/message-service.ts +++ b/src/main/webapp/app/shared/service/message-service.ts @@ -3,19 +3,18 @@ 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 + 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 } /** @@ -23,53 +22,43 @@ export interface BroadCastMessage { */ @Injectable({ providedIn: 'root' }) export class MessageService { + public messageResourceURL = SERVER_API_URL + 'api/applicationInfo/broadcastMessages'; - public messageResourceURL = SERVER_API_URL + 'api/applicationInfo/broadcastMessages'; - - private messages = new Array<BroadCastMessage>();; - private nextUpdate = 0; - - constructor(protected http: HttpClient,) { - - } + private messages = new Array<BroadCastMessage>(); + private nextUpdate = 0; - public getActiveMessages(): Array<BroadCastMessage> { - const now = new Date(); - return this.getMessages().filter(m => - ((m.starts_at <= now) && (m.ends_at >= now) ) - ); // - } + constructor(protected http: HttpClient) {} - public getMessages(): Array<BroadCastMessage> { + public getActiveMessages(): Array<BroadCastMessage> { + const now = new Date(); + return this.getMessages().filter(m => m.starts_at <= now && m.ends_at >= now); // + } - const now = new Date().getTime(); - if (this.nextUpdate > now) { - return this.messages; - } + 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); + 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 + resp.subscribe(response => { + // first convert dates correctly :-( - return (response); - } - ); - return this.messages; - } + 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; + } } - diff --git a/src/main/webapp/i18n/de/health.json b/src/main/webapp/i18n/de/health.json index b8ddba445436dcbfdef3ef3cdbc7ff99c557cc17..d0acbac326286efb10a91cc6d0c2e43ecb4c8207 100644 --- a/src/main/webapp/i18n/de/health.json +++ b/src/main/webapp/i18n/de/health.json @@ -17,7 +17,7 @@ "db": "Datenbank", "elasticsearch": "Elasticsearch", "elasticsearchRest": "Elasticsearch REST API", - "pluginHealthCheck": "Plugin Status" + "pluginHealthCheck": "Plugin Status" }, "table": { "service": "Dienst Name", diff --git a/src/main/webapp/i18n/en/health.json b/src/main/webapp/i18n/en/health.json index 2f0a7fc1754a706fcd238d99ae0a16b05ebce497..8c030728fadbbf8d8d1e4e53417ec3b5115bd48d 100644 --- a/src/main/webapp/i18n/en/health.json +++ b/src/main/webapp/i18n/en/health.json @@ -17,8 +17,7 @@ "db": "Database", "elasticsearch": "Elasticsearch", "elasticsearchRest": "Elasticsearch REST API", - "pluginHealthCheck": "State of plugins" - + "pluginHealthCheck": "State of plugins" }, "table": { "service": "Service name", diff --git a/src/test/java/at/ac/uibk/gitsearch/web/rest/StatisticsResourceIT.java b/src/test/java/at/ac/uibk/gitsearch/web/rest/StatisticsResourceIT.java index 552819c97c9b869a8a1968e5767fdb9f75591d83..b7b7b39be3052315034e5e54d07366b2d9ffd9e8 100644 --- a/src/test/java/at/ac/uibk/gitsearch/web/rest/StatisticsResourceIT.java +++ b/src/test/java/at/ac/uibk/gitsearch/web/rest/StatisticsResourceIT.java @@ -3,11 +3,15 @@ package at.ac.uibk.gitsearch.web.rest; import at.ac.uibk.gitsearch.GitsearchApp; import at.ac.uibk.gitsearch.domain.Statistics; import at.ac.uibk.gitsearch.repository.StatisticsRepository; +import at.ac.uibk.gitsearch.repository.gitlab.GitLabRepository; import at.ac.uibk.gitsearch.repository.search.StatisticsSearchRepository; +import at.ac.uibk.gitsearch.service.GitlabService; import at.ac.uibk.gitsearch.service.StatisticsService; import at.ac.uibk.gitsearch.service.dto.StatisticsDTO; import at.ac.uibk.gitsearch.service.mapper.StatisticsMapper; +import org.gitlab4j.api.GitLabApi; +import org.gitlab4j.api.ProjectApi; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.mockito.Mock; @@ -25,12 +29,15 @@ import org.springframework.transaction.annotation.Transactional; import javax.persistence.EntityManager; import java.util.Collections; import java.util.List; +import java.util.Optional; import at.ac.uibk.gitsearch.security.AuthoritiesConstants; +import at.ac.uibk.gitsearch.security.jwt.TokenProvider.GitLabAccessInfo; import static org.assertj.core.api.Assertions.assertThat; import static org.elasticsearch.index.query.QueryBuilders.queryStringQuery; import static org.hamcrest.Matchers.hasItem; +import static org.junit.jupiter.api.Assertions.assertEquals; import static org.mockito.Mockito.*; import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.*; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*; @@ -63,6 +70,10 @@ public class StatisticsResourceIT { @Autowired private StatisticsService statisticsService; + @Autowired + private GitLabRepository gitLabRepository; + + /** * This repository is mocked in the at.ac.uibk.gitsearch.repository.search test * package. @@ -78,6 +89,9 @@ public class StatisticsResourceIT { @Autowired private MockMvc restStatisticsMockMvc; + @Autowired + private GitlabService gitlabService; + private Statistics statistics; /** @@ -289,4 +303,46 @@ public class StatisticsResourceIT { .andExpect(jsonPath("$.[*].downloads").value(hasItem(DEFAULT_DOWNLOADS))) .andExpect(jsonPath("$.[*].exerciseID").value(hasItem(DEFAULT_EXERCISE_ID.intValue()))); } + + @Test + @Transactional + @WithMockUser(authorities = AuthoritiesConstants.ADMIN) + public void getStatisticsByExerciseId() throws Exception { + // Initialize the database + statisticsRepository.saveAndFlush(statistics); + + // Get the statistics and see if it coincides with the mock stats + assertEquals(statisticsService.findOneByExerciseID(statistics.getExerciseID()).get().getExerciseID(), statistics.getExerciseID()); + } + + + @Test + @Transactional + @WithMockUser(authorities = AuthoritiesConstants.ADMIN) + public void createStatisticsByExerciseId() throws Exception { + // Initialize the database + statisticsRepository.saveAndFlush(statistics); + // Optional<GitLabAccessInfo> accessInfo = gitLabRepository.getGitLabAccessInfo(); + // GitLabApi gitlabApi = gitLabRepository.getGitLabApi(accessInfo); + // ProjectApi projectApi = gitlabApi.getProjectApi(); + // Integer id = projectApi.getProjects().get(0).getId(); + + // Get the statistics + // System.out.println("My request: ##################################################################"); + // System.out.println(restStatisticsMockMvc.perform(get("/api/statistics/exercise/{id}", 3).with(csrf().asHeader()))); + // restStatisticsMockMvc.perform(get("/api/statistics/exercise/{id}", 3).with(csrf().asHeader())).andExpect(status().isOk()) + // .andExpect(content().contentType(MediaType.APPLICATION_JSON_VALUE)) + // .andExpect(jsonPath("$.exerciseID").value(3)); + + + //assertEquals(true,gitlabService.repositoryExists("3")); + + + // Get the statistics and see if it coincides with the mock stats + assertEquals(statisticsService.findOneByExerciseID(statistics.getExerciseID()).get().getExerciseID(), statistics.getExerciseID()); + } + + + + } diff --git a/src/test/resources/config/application.yml b/src/test/resources/config/application.yml index e59a98231e66dd4b40213481919a16daa7b02877..153614d2fae0fdd139788d506e66b1f07b9afb51 100644 --- a/src/test/resources/config/application.yml +++ b/src/test/resources/config/application.yml @@ -113,7 +113,7 @@ jhipster: base-url: http://127.0.0.1:8080 security: oauth2: -# TODO: audience seems not really relevant, could be omitted? It is identical with client-id above + # TODO: audience seems not really relevant, could be omitted? It is identical with client-id above audience: - 149276ac11138d9ba72fb3cd12815e3fa2f372866df0eac0f7d1aae5fdffea24 authentication: @@ -142,8 +142,8 @@ application: highlight-pre: <mark><strong> highlight-post: </strong></mark> gitlab: - url: https://sharing.codeability-austria.uibk.ac.at/ - generalAccessToken: zPxPmJE3UXAZJpBzxqej + url: https://sharing.codeability-austria.uibk.ac.at/ + generalAccessToken: zPxPmJE3UXAZJpBzxqej registeredPlugins: - - "http://localhost:8081/api/sharing/config" # may not be reachable for testing! - - "http://localhost:8082/api/sharingPluginConfig" # may not be reachable for testing! + - 'http://localhost:8081/api/sharing/config' # may not be reachable for testing! + - 'http://localhost:8082/api/sharingPluginConfig' # may not be reachable for testing!