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

Commit 4c4b0dbe authored by Michael Breu's avatar Michael Breu 💬
Browse files

Intermediate commit (first complete version)

parent 3c84784b
This diff is collapsed.
......@@ -113,7 +113,30 @@
<artifactId>SharingPluginPlatformAPI</artifactId>
<version>0.0.1</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.glassfish.jersey.core/jersey-client -->
<dependency>
<groupId>org.glassfish.jersey.core</groupId>
<artifactId>jersey-client</artifactId>
<version>2.29.1</version>
</dependency>
<!-- https://mvnrepository.com/artifact/commons-io/commons-io -->
<dependency>
<groupId>commons-io</groupId>
<artifactId>commons-io</artifactId>
<version>2.6</version>
</dependency>
<dependency>
<groupId>org.glassfish.jersey.inject</groupId>
<artifactId>jersey-hk2</artifactId>
<version>2.33</version>
</dependency>
<dependency>
<groupId>org.glassfish.jersey.media</groupId>
<artifactId>jersey-media-json-jackson</artifactId>
<version>2.29.1</version>
</dependency>
<dependency>
<groupId>io.github.jhipster</groupId>
<artifactId>jhipster-framework</artifactId>
......
{
"*": {
"target": "http://localhost:8080",
"target": "http://localhost:8082",
"secure": false,
"loglevel": "debug"
}
......
......@@ -46,6 +46,7 @@ public class SecurityConfiguration extends WebSecurityConfigurerAdapter {
public SecurityConfiguration(CorsFilter corsFilter, JHipsterProperties jHipsterProperties, SecurityProblemSupport problemSupport) {
this.corsFilter = corsFilter;
this.problemSupport = problemSupport;
this.jHipsterProperties = jHipsterProperties;
}
......@@ -67,6 +68,7 @@ public class SecurityConfiguration extends WebSecurityConfigurerAdapter {
// @formatter:off
http
.csrf()
.ignoringAntMatchers("/api/sharingImport/**")
.csrfTokenRepository(CookieCsrfTokenRepository.withHttpOnlyFalse())
.and()
.addFilterBefore(corsFilter, CsrfFilter.class)
......@@ -87,6 +89,9 @@ public class SecurityConfiguration extends WebSecurityConfigurerAdapter {
.authorizeRequests()
.antMatchers("/api/auth-info").permitAll()
.antMatchers("/api/sharingPluginConfig").permitAll()
.antMatchers("/api/sharingImport/**").permitAll()
.antMatchers("/api/sharingImport/basket").permitAll()
.antMatchers("/api/**").authenticated()
.antMatchers("/management/health").permitAll()
.antMatchers("/management/info").permitAll()
......
package org.codeability.sharing.demo.service;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.util.Optional;
import java.util.concurrent.TimeUnit;
import javax.ws.rs.client.Client;
import javax.ws.rs.client.ClientBuilder;
import javax.ws.rs.client.WebTarget;
import javax.ws.rs.core.MediaType;
import org.apache.commons.io.FileUtils;
import org.codeability.sharing.demo.web.rest.dto.SharingInfoDTO;
import org.codeability.sharing.plugins.api.ShoppingBasket;
import org.glassfish.jersey.client.ClientConfig;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.data.util.Pair;
import org.springframework.stereotype.Service;
import com.google.common.cache.CacheBuilder;
import com.google.common.cache.CacheLoader;
import com.google.common.cache.LoadingCache;
@Service
public class SharingDemoService {
public SharingDemoService() {
}
private final Logger log = LoggerFactory.getLogger(SharingDemoService.class);
public Optional<ShoppingBasket> getBasketInfo(SharingInfoDTO sharingInfo) {
ClientConfig restClientConfig = new ClientConfig();
restClientConfig.register(ShoppingBasket.class);
Client client = ClientBuilder.newClient(restClientConfig);
WebTarget target = client.target(sharingInfo.getApiBaseURL()+"/basket/"+sharingInfo.getBasketToken());
// TODO make error proof
ShoppingBasket shoppingBasket = target.
request().
accept(MediaType.APPLICATION_JSON).
get(ShoppingBasket.class);
if(shoppingBasket!=null) {
for(ShoppingBasket.ExerciseInfo ex: shoppingBasket.exerciseInfo) {
convertKeyWordsToArtemisJSON(ex);
}
}
return Optional.of(shoppingBasket);
}
// TODO this is the wrong place, should be done in GUI.
private void convertKeyWordsToArtemisJSON(ShoppingBasket.ExerciseInfo ex) {
// TODO escape keyword for JSON
for(int i=0; i<ex.keywords.size(); i++) {
ex.keywords.set(i, "{ \"category\": \"" + ex.keywords.get(i) + "\", \"color\":\"#691b0b\"}");
}
}
LoadingCache<Pair<String, Integer>, File> repositoryCache = CacheBuilder.newBuilder()
.maximumSize(10000)
.expireAfterWrite(4, TimeUnit.HOURS)
.build(
new CacheLoader<Pair<String, Integer>, File>() {
public File load(Pair<String, Integer> key) {
return null;
}
});
public Optional<InputStream> getBasketItemStream(SharingInfoDTO sharingInfo, int itemPosition) {
ClientConfig restClientConfig = new ClientConfig();
restClientConfig.register(ShoppingBasket.class);
Client client = ClientBuilder.newClient(restClientConfig);
WebTarget target = client.target(sharingInfo.getApiBaseURL()+"/basket/"+sharingInfo.getBasketToken() + "/repository/" + itemPosition);
// TODO make error proof
InputStream zipInput = target.
request().
accept(MediaType.APPLICATION_OCTET_STREAM).
get(InputStream.class);
return Optional.of(zipInput);
}
public InputStream getCachedBasketItemStream(SharingInfoDTO sharingInfo, int itemPosition) throws IOException {
File f = null;
f = repositoryCache.getIfPresent(Pair.of(sharingInfo.getBasketToken(), itemPosition));
if(f!=null) {
try {
return new FileInputStream(f);
} catch (FileNotFoundException e) {
log.warn("Cannot find cached file for {}:{} at {}", sharingInfo.getBasketToken(), itemPosition, f.getAbsoluteFile(), e);
}
}
f = File.createTempFile("baskedCache" + sharingInfo.getBasketToken() + "-" + itemPosition, "zip");
Optional<InputStream> isO = getBasketItemStream(sharingInfo, itemPosition);
if(isO.isEmpty()) {
return null;
}
FileUtils.copyInputStreamToFile(isO.get(), f);
return new FileInputStream(f);
}
}
......@@ -20,11 +20,11 @@ public class SharingPluginService {
public SharingPluginConfig getPluginConfig(String baseAPIURI) {
SharingPluginConfig.Action actionCollection = new SharingPluginConfig.Action(
"DemoAction", "/sharingPluginLandingPage",
"DemoAction1", "/sharingPluginLandingPage",
"Transfer Collection to Demo Plugin",
"metadata.type.externalName=='collections'"); // select for collections
"metadata.type.externalName=='collection'"); // select for collections
SharingPluginConfig.Action actionProgExercise = new SharingPluginConfig.Action(
"DemoAction", "/sharingPluginLandingPage",
"DemoAction2", "/sharingPluginLandingPage",
"Transfer Programming Exercise to Demo Plugin",
"metadata.type.externalName=='programming exercise'"); // select for programming exercise
// "Import to Artemis", "metadata.type.externalName=='programming exercise' || metadata.type.externalName=='collection'");
......
package org.codeability.sharing.demo.web.rest;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.nio.charset.StandardCharsets;
import java.util.Optional;
import java.util.zip.ZipEntry;
import java.util.zip.ZipInputStream;
import org.codeability.sharing.demo.service.SharingDemoService;
import org.codeability.sharing.demo.web.rest.dto.SharingInfoDTO;
import org.codeability.sharing.plugins.api.ShoppingBasket;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.http.ResponseEntity;
import org.springframework.util.StreamUtils;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import io.github.jhipster.web.util.ResponseUtil;
/**
* REST controller for managing Exercise.
*/
@RestController
@RequestMapping("/api")
public class SharingDemoResource {
private final Logger log = LoggerFactory.getLogger(SharingDemoResource.class);
private final SharingDemoService exerciseSharingService;
public SharingDemoResource(SharingDemoService exerciseSharingService) {
this.exerciseSharingService = exerciseSharingService;
}
/**
* GET /sharingImport/
*
* @param sharingInfo information about sharing input
* @return the ResponseEntity with status 200 (OK) and with body the exercise, or with status 404 (Not Found)
*/
@PostMapping("/sharingImport/basket")
public ResponseEntity<ShoppingBasket> loadShoppingBasket(@RequestBody SharingInfoDTO sharingInfo) {
Optional<ShoppingBasket> sharingInfoDTO = exerciseSharingService.getBasketInfo(sharingInfo);
return ResponseUtil.wrapOrNotFound(sharingInfoDTO);
}
public static class SharingSetupInfo {
SharingInfoDTO sharingInfo;
int exercisePosition;
public SharingInfoDTO getSharingInfo() {
return sharingInfo;
}
public void setSharingInfo(SharingInfoDTO sharingInfo) {
this.sharingInfo = sharingInfo;
}
public int getExercisePosition() {
return exercisePosition;
}
public void setExercisePosition(int exercisePosition) {
this.exercisePosition = exercisePosition;
}
}
/**
* GET /sharingImport/basket/exercise/{exercisePosition}/readMe : get problem statement.
*
* @param basketToken the basket
* @return the ResponseEntity with status 200 (OK) and with body the exercise, or with status 404 (Not Found)
*/
@PostMapping("/sharingImport/basket/exercise/{exercisePosition}/readMe.md")
// @PreAuthorize("hasAnyRole('INSTRUCTOR', 'ADMIN')")
public ResponseEntity<String> getProblemStatement(@PathVariable("exercisePosition") int exercisePosition, @RequestBody SharingInfoDTO basketToken) throws IOException {
InputStream repositoryStream = null;
try {
repositoryStream = exerciseSharingService.getCachedBasketItemStream(basketToken, exercisePosition);
} catch (IOException e) {
log.error("Cannot read input Template for " + basketToken.getBasketToken());
}
ZipInputStream zippedRepositoryStream = new ZipInputStream(repositoryStream);
ZipEntry statement = getEntry("exercise.md", zippedRepositoryStream);
if (statement == null) {
return ResponseEntity.ok().body("Statement not found!") ;
}
ByteArrayOutputStream baos = new ByteArrayOutputStream();
StreamUtils.copy(zippedRepositoryStream, baos);
String statementString = baos.toString(StandardCharsets.UTF_8);
return ResponseEntity.ok().body(statementString);
}
public ZipEntry getEntry(String path, ZipInputStream zippedRepositoryStream) throws IOException {
ZipEntry currentEntry = zippedRepositoryStream.getNextEntry();
String prefix = "";
if(currentEntry.isDirectory()) { // main directory is prefix to all entries
prefix = currentEntry.getName();
}
while(currentEntry != null) {
if (!currentEntry.isDirectory() && currentEntry.getName().equals(prefix + path))
return currentEntry;
currentEntry = zippedRepositoryStream.getNextEntry();
}
return null;
}
}
package org.codeability.sharing.demo.web.rest.dto;
public class SharingInfoDTO {
private String basketToken;
private String returnURL;
private String apiBaseURL;
public String getReturnURL() {
return returnURL;
}
public void setReturnURL(String returnURL) {
this.returnURL = returnURL;
}
public String getApiBaseURL() {
return apiBaseURL;
}
public void setApiBaseURL(String apiBaseURL) {
this.apiBaseURL = apiBaseURL;
}
public String getBasketToken() {
return basketToken;
}
public void setBasketToken(String basketToken) {
this.basketToken = basketToken;
}
}
......@@ -63,7 +63,7 @@ spring:
cache: false
server:
port: 8080
port: 8082
# ===================================================================
# JHipster specific properties
......@@ -85,7 +85,7 @@ jhipster:
allow-credentials: true
max-age: 1800
mail: # specific JHipster mail property, for standard properties see MailProperties
base-url: http://127.0.0.1:8080
base-url: http://127.0.0.1:8082
metrics:
logs: # Reports metrics in the logs
enabled: false
......
......@@ -22,7 +22,7 @@ management:
include: ['configprops', 'env', 'health', 'info', 'jhimetrics', 'logfile', 'loggers', 'prometheus', 'threaddump']
endpoint:
health:
show-details: when_authorized
show-details: WHEN_AUTHORIZED
roles: 'ROLE_ADMIN'
jhimetrics:
enabled: true
......
import { Injectable } from '@angular/core';
import { HttpClient } from '@angular/common/http';
import { Observable } from 'rxjs';
import { SharingInfo } from 'app/sharing/sharing.model';
export type ExerciseInfo = {
title: string;
gitLabProjectId: string;
gitLabURI: string;
keywords: string[];
};
export type ShoppingBasket = {
exerciseInfo: Array<ExerciseInfo>;
};
const SERVER_API_URL = 'http://localhost:8082/'
@Injectable({ providedIn: 'root' })
export class SharingDemoService {
public resourceUrl = SERVER_API_URL + 'api/sharingImport';
public resourceUrlPS = SERVER_API_URL + 'api/sharingImport/basket/exercise/';
constructor(private http: HttpClient, private sharingInfo: SharingInfo) {}
loadShoppingBasket(): Observable<ShoppingBasket> {
return this.http.post<ShoppingBasket>(this.resourceUrl + '/basket', this.sharingInfo);
}
}
......@@ -4,11 +4,23 @@
<h1>Happy Sharing</h1>
</div>
<div class="content">
<div><b>This page is intentionally ugly :-)</b></div>
<h1>Data from URL</h1>
<div>basketToken: {{this.sharingInfo.basketToken}}</div>
<div>returnURL: {{this.sharingInfo.returnURL}}</div>
<div>apiBaseURL: {{this.sharingInfo.apiBaseURL}}</div>
<div *ngIf="!isInstuctor()">Please ensure to log in as instructor!</div>
</div>
<h1>Shopping Basket Data from SharingPlattform (loaded via plugin backend)</h1>
<div><span *ngIf="!shoppingBasket" >No Shopping Basket loaded</span></div>
<div *ngIf="shoppingBasket">
<div >
The Shopping Basket contains {{shoppingBasket.exerciseInfo.length}} element(s):
</div>
<div *ngFor="let execInfo of shoppingBasket.exerciseInfo">
<div>title: {{execInfo.title}}</div>
<div>gitLabURI: {{execInfo.gitLabURI}}</div>
<div>gitLabProjectId: {{execInfo.gitLabProjectId}}</div>
<div>keywords: <span *ngFor="let kw of execInfo.keywords"></span>"{{kw}}" </div>
</div>
</div>
</div>
</body>
import { Component, OnInit } from '@angular/core';
import { ActivatedRoute, Router } from '@angular/router';
import { ActivatedRoute } from '@angular/router';
import { SharingInfo } from './sharing.model';
import { SharingDemoService, ShoppingBasket } from './service/sharing-demo.service';
@Component({
selector: 'jhi-sharing',
......@@ -9,10 +10,13 @@ import { SharingInfo } from './sharing.model';
})
export class SharingComponent implements OnInit {
public shoppingBasket: ShoppingBasket;
constructor(
private route: ActivatedRoute,
private router: Router,
public sharingInfo: SharingInfo,
private sharingDemoService: SharingDemoService,
) {
this.route.params.subscribe((params) => {
......@@ -21,10 +25,19 @@ export class SharingComponent implements OnInit {
this.route.queryParams.subscribe((qparam) => {
sharingInfo.returnURL = qparam['returnURL'];
sharingInfo.apiBaseURL = qparam['apiBaseURL'];
// }
this.loadShoppingBasket();
});
this.shoppingBasket = {} as ShoppingBasket;
}
loadShoppingBasket(): void {
this.sharingDemoService.loadShoppingBasket().subscribe((res:ShoppingBasket) => {
this.shoppingBasket = res;
});
}
/**
* Initialises the sharing page for import
*/
......
import { NgModule } from '@angular/core';
import { RouterModule } from '@angular/router';
import { CommonModule } from '@angular/common';
import { SharingComponent } from 'app/sharing/sharing.component';
import { featureOverviewState } from 'app/sharing/sharing.route';
const SHARING_ROUTES = [...featureOverviewState];
@NgModule({
imports: [RouterModule.forChild(SHARING_ROUTES)],
imports: [CommonModule, RouterModule.forChild(SHARING_ROUTES)],
declarations: [SharingComponent],
})
export class SharingModule {}
......@@ -99,7 +99,7 @@ jhipster:
queue-size: 512
mail:
from: test@localhost
base-url: http://127.0.0.1:8080
base-url: http://127.0.0.1:8082
metrics:
logs: # Reports metrics in the logs
enabled: true
......
Markdown is supported
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