Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Playground] Add Database sharing #1525

Merged
merged 5 commits into from
Nov 23, 2023
Merged
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
feat(playground): add database sharing
  • Loading branch information
Zitrone44 committed Nov 23, 2023
commit 8723792556f9145d510193a70334416c79e0272e
1 change: 1 addition & 0 deletions modules/fbs-core/api/build.gradle
Original file line number Diff line number Diff line change
@@ -78,6 +78,7 @@ dependencies {
testImplementation group: 'junit', name: 'junit', version: '4.13.2'
implementation group: 'org.apache.commons', name: 'commons-compress', version: '1.23.0'
implementation group: 'org.jgrapht', name: 'jgrapht-core', version: '1.5.2'
implementation group: 'org.hashids', name: 'hashids', version: '1.0.3'
}

jacoco {
Original file line number Diff line number Diff line change
@@ -8,6 +8,7 @@ import de.thm.ii.fbs.model.v2.playground.*
import de.thm.ii.fbs.model.v2.playground.api.SqlPlaygroundDatabaseCreation
import de.thm.ii.fbs.model.v2.playground.api.SqlPlaygroundQueryCreation
import de.thm.ii.fbs.model.v2.playground.api.SqlPlaygroundResult
import de.thm.ii.fbs.model.v2.playground.api.SqlPlaygroundShareResponse
import de.thm.ii.fbs.model.v2.security.LegacyToken
import de.thm.ii.fbs.services.v2.checker.SqlPlaygroundCheckerService
import de.thm.ii.fbs.services.v2.persistence.*
@@ -72,11 +73,12 @@ class PlaygroundController(
return databaseRepository.save(db)
}

@PostMapping("/{dbId}/dump")
@PostMapping("/{dbId}/share")
@ResponseBody
fun createSharePlayground(@CurrentToken currentToken: LegacyToken, @PathVariable("dbId") dbId: Int): String {
fun createPlaygroundShare(@CurrentToken currentToken: LegacyToken, @PathVariable("dbId") dbId: Int): SqlPlaygroundShareResponse {
val currentActiveDb = databaseRepository.findByOwner_IdAndIdAndDeleted(currentToken.id, dbId, false) ?: throw NotFoundException()
return sqlPlaygroundCheckerService.createSharePlayground(currentActiveDb)
val url = sqlPlaygroundCheckerService.shareDatabase(currentActiveDb)
return SqlPlaygroundShareResponse(url)
}

@PostMapping("/{dbId}/reset")
Original file line number Diff line number Diff line change
@@ -4,5 +4,5 @@ import com.fasterxml.jackson.annotation.JsonValue

enum class RunnerType(@JsonValue val label: String) {
SQL_PLAYGROUND("sql-playground"),
SHARE_PLAYGROUND("share")
SQL_PLAYGROUND_SHARE("sql-playground-share")
}
Original file line number Diff line number Diff line change
@@ -2,11 +2,15 @@ package de.thm.ii.fbs.model.v2.checker

import com.fasterxml.jackson.annotation.JsonProperty

data class SharePlaygroundArguments(
data class SqlPlaygroundShareArguments(
@JsonProperty("user")
val user: RunnerUser,
@JsonProperty("database")
val database: RunnerDatabase,
@JsonProperty("id")
val id: String,
@JsonProperty("password")
val password: String,
@JsonProperty("runner")
val runner: Runner = Runner(RunnerType.SHARE_PLAYGROUND)
) : RunnerArguments()
val runner: Runner = Runner(RunnerType.SQL_PLAYGROUND_SHARE)
) : RunnerArguments()
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
package de.thm.ii.fbs.model.v2.checker

import com.fasterxml.jackson.annotation.JsonProperty

data class SqlPlaygroundShareDeleteArguments(
@JsonProperty("database")
val database: RunnerDatabase,
@JsonProperty("id")
val id: String,
@JsonProperty("delete")
val delete: Boolean = true,
@JsonProperty("runner")
val runner: Runner = Runner(RunnerType.SQL_PLAYGROUND_SHARE)
) : RunnerArguments()
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
package de.thm.ii.fbs.model.v2.playground
import java.time.LocalDateTime
import javax.persistence.*

@Entity
@Table(name = "sql_playground_share")
class SqlPlaygroundShare(
@OneToOne(cascade = [CascadeType.ALL])
@PrimaryKeyJoinColumn
val database: SqlPlaygroundDatabase,
@Column(nullable = false)
val creationTime: LocalDateTime,
@Id
@Column(name = "database_id")
val id: Int? = null,
)
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
package de.thm.ii.fbs.model.v2.playground.api

import com.fasterxml.jackson.annotation.JsonProperty

data class SqlPlaygroundShareResponse(
@JsonProperty("url")
var url: String
)

This file was deleted.

Original file line number Diff line number Diff line change
@@ -3,8 +3,9 @@ package de.thm.ii.fbs.services.v2.checker
import de.thm.ii.fbs.model.v2.checker.*
import de.thm.ii.fbs.model.v2.playground.SqlPlaygroundDatabase
import de.thm.ii.fbs.model.v2.playground.SqlPlaygroundQuery
import de.thm.ii.fbs.model.v2.security.SharePlaygroundToken
import de.thm.ii.fbs.services.v2.persistence.SharePlaygroundTokenRepository
import de.thm.ii.fbs.model.v2.playground.SqlPlaygroundShare
import de.thm.ii.fbs.services.v2.misc.IdService
import de.thm.ii.fbs.services.v2.persistence.SqlSharePlaygroundShareRepository
import org.springframework.beans.factory.annotation.Value
import org.springframework.stereotype.Service
import java.time.LocalDateTime
@@ -16,7 +17,12 @@ class SqlPlaygroundCheckerService(
insecure: Boolean,
@Value("\${services.masterRunner.url}")
private val masterRunnerURL: String,
private val sharePlaygroundTokenRepository: SharePlaygroundTokenRepository,
@Value("\${services.sqlPlayground.share.publicHost}")
private val publicShareHost: String,
@Value("\${services.sqlPlayground.share.publicPort}")
private val publicSharePort: Int,
private val sqlSharePlaygroundShareRepository: SqlSharePlaygroundShareRepository,
private val idService: IdService,
) : RemoteCheckerV2Service(insecure, masterRunnerURL) {

fun submit(query: SqlPlaygroundQuery) {
@@ -33,16 +39,30 @@ class SqlPlaygroundCheckerService(
)
}

fun createSharePlayground(db: SqlPlaygroundDatabase): String {
fun shareDatabase(db: SqlPlaygroundDatabase): String {
val id = idService.encode(db.id!!)
val token = UUID.randomUUID().toString()
val expiryTime = LocalDateTime.now()
val uri = this.sendToRunner(
SharePlaygroundArguments(
val creationTime = LocalDateTime.now()
this.sendToRunner(
SqlPlaygroundShareArguments(

RunnerUser(db.owner.id!!, db.owner.username),
RunnerDatabase(db.id!!, db.name)
RunnerDatabase(db.id!!, db.name),
id,
token
))
sharePlaygroundTokenRepository.save(SharePlaygroundToken(token, db.owner.id!!, db.id!!, expiryTime, uri.toString()))
return uri.toString()
sqlSharePlaygroundShareRepository.save(SqlPlaygroundShare(db, creationTime, db.id!!))
return "postgresql://${id}:${token}@${publicShareHost}:${publicSharePort}/${id}"
}

fun deleteDatabaseShare(share: SqlPlaygroundShare) {
/*val db = share.database
this.sendToRunner(
SqlPlaygroundShareDeleteArguments(
RunnerDatabase(db.id!!, db.name),
idService.encode(share.database.id!!),
))
sqlSharePlaygroundShareRepository.delete(share)*/
}

fun deleteDatabase(database: SqlPlaygroundDatabase, userId: Int, username: String) {
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
package de.thm.ii.fbs.services.v2.misc

import org.hashids.Hashids
import org.springframework.beans.factory.annotation.Value
import org.springframework.stereotype.Service

@Service
class IdService(
@Value("\${services.ids.salt}")
salt: String,
@Value("\${services.ids.length}")
length: Int,
) {
private val hashids = Hashids(salt, length)

fun encode(id: Long): String =
hashids.encode(id)

fun encode(id: Int): String =
encode(id.toLong())
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
package de.thm.ii.fbs.services.v2.misc

import de.thm.ii.fbs.model.v2.playground.SqlPlaygroundShare
import de.thm.ii.fbs.services.v2.checker.SqlPlaygroundCheckerService
import de.thm.ii.fbs.services.v2.persistence.SqlSharePlaygroundShareRepository
import org.springframework.scheduling.annotation.Scheduled
import org.springframework.stereotype.Service
import java.time.LocalDateTime

@Service
class SharePlaygroundCleanupService(
private val sqlSharePlaygroundShareRepository: SqlSharePlaygroundShareRepository,
private val sqlPlaygroundCheckerService: SqlPlaygroundCheckerService,
) {
@Scheduled(fixedDelayString = "PT1H") // Runs every hour
fun cleanupExpiredShares() {
val now = LocalDateTime.now()
val expiredShares: List<SqlPlaygroundShare> = sqlSharePlaygroundShareRepository.findAllByCreationTimeLessThan(now.minusDays(1))
expiredShares.forEach { share ->
sqlPlaygroundCheckerService.deleteDatabaseShare(share)
}
}
}

This file was deleted.

Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
package de.thm.ii.fbs.services.v2.persistence

import de.thm.ii.fbs.model.v2.playground.SqlPlaygroundShare
import org.springframework.data.jpa.repository.JpaRepository
import java.time.LocalDateTime

interface SqlSharePlaygroundShareRepository : JpaRepository<SqlPlaygroundShare, String> {
fun findAllByCreationTimeLessThan(creationTime: LocalDateTime): List<SqlPlaygroundShare>
}

This file was deleted.

7 changes: 7 additions & 0 deletions modules/fbs-core/api/src/main/resources/application.yml
Original file line number Diff line number Diff line change
@@ -60,6 +60,13 @@ services:
url: ${MASTER_RUNNER_URL:https://localhost:8081}
insecure: ${MASTER_RUNNER_TLS_INSECURE:false}
selfUrl: ${SELF_URL:https://localhost}
sqlPlayground:
share:
publicHost: ${SQL_PLAYGROUND_SHARE_PUBLIC_HOST:127.0.0.1}
publicPort: ${SQL_PLAYGROUND_SHARE_PUBLIC_PORT:8432}
ids:
salt: ${ID_SALT:feedbacksystem_id_salt}
length: ${ID_LENGTH:8}
spring:
main:
allow-bean-definition-overriding: true
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
BEGIN;

CREATE TABLE sql_playground_share
(
database_id INT NOT NULL,
creation_time TIMESTAMP NOT NULL,
CONSTRAINT sql_playground_share_pk
PRIMARY KEY (database_id),
constraint sql_playground_share_sql_playground_database_id_fk
FOREIGN KEY (database_id) references sql_playground_database (id)
ON UPDATE CASCADE
);


INSERT INTO migration (number) VALUES (19);
COMMIT;
Original file line number Diff line number Diff line change
@@ -8,3 +8,7 @@ interface ResponseTable {
head: string[];
rows: string[];
}

export interface SQLPlaygroundShare {
url: string;
}
Original file line number Diff line number Diff line change
@@ -32,7 +32,6 @@ export class DbControlDbOverviewComponent implements OnInit {
selectedDb: number = 0;
token: JWTToken = this.authService.getToken();
pending: boolean = false;
dbURI: string = "";

ngOnInit(): void {
this.sqlPlaygroundService.getDatabases(this.token.id).subscribe(
@@ -186,16 +185,17 @@ export class DbControlDbOverviewComponent implements OnInit {
const selectedDb = this.dbs.find((db) => db.id == this.selectedDb);
this.sqlPlaygroundService
.getSharePlaygroundURI(this.token.id, selectedDb.id)
.subscribe((uri) => (this.dbURI = uri));
this.dialog.open(SharePlaygroundLinkDialogComponent, {
height: "auto",
width: "50%",
autoFocus: false,
data: {
message: `Der URI-Link zu deiner Datenbank \" ${selectedDb.name} \" ist nur für 24 Stunden verfügbar!\n`,
uri: this.dbURI,
},
});
.subscribe((share) => {
this.dialog.open(SharePlaygroundLinkDialogComponent, {
height: "auto",
width: "50%",
autoFocus: false,
data: {
message: `Der URI-Link zu deiner Datenbank \" ${selectedDb.name} \" ist nur für 24 Stunden verfügbar!\n`,
uri: share.url,
},
});
})
}

addDb() {
Original file line number Diff line number Diff line change
@@ -1,15 +1,14 @@
import { HttpClient } from "@angular/common/http";
import { Injectable } from "@angular/core";
import { Observable, of } from "rxjs";
import { Observable } from "rxjs";
import { Constraint } from "../model/sql_playground/Constraint";
import { Database } from "../model/sql_playground/Database";
import { Routine } from "../model/sql_playground/Routine";
import { SQLExecuteResponse } from "../model/sql_playground/SQLExecuteResponse";
import { SQLResponse } from "../model/sql_playground/SQLResponse";
import {SQLPlaygroundShare, SQLResponse} from "../model/sql_playground/SQLResponse";
import { Table } from "../model/sql_playground/Table";
import { Trigger } from "../model/sql_playground/Trigger";
import { View } from "../model/sql_playground/View";
import { map } from "rxjs/operators";

@Injectable({
providedIn: "root",
@@ -84,9 +83,9 @@ export class SqlPlaygroundService {
* Get Database Temp URI
* @return the temporary database URI
*/
getSharePlaygroundURI(uid: number, dbId: number): Observable<string> {
getSharePlaygroundURI(uid: number, dbId: number): Observable<SQLPlaygroundShare> {
return this.http.post<any>(
`/api/v2/playground/${uid}/databases/${dbId}/dump`,
`/api/v2/playground/${uid}/databases/${dbId}/share`,
{}
);
}
2 changes: 1 addition & 1 deletion modules/fbs-runner/checker/Dockerfile
Original file line number Diff line number Diff line change
@@ -8,7 +8,7 @@ RUN gradle fbs-runner.checker:installDist
FROM eclipse-temurin:18.0.2_9-jre-alpine

# Install Docker
RUN apk add --no-cache docker curl
RUN apk add --no-cache docker curl postgresql-client

COPY --from=build /build/modules/fbs-runner/checker/build/install/fbs-runner.checker /usr/local/fbs-runner.checker
EXPOSE 8081
Loading