Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
21 commits
Select commit Hold shift + click to select a range
b2797f3
started on submit endpoint, refactoring now
danielfang97 Jul 4, 2026
1684dbd
added collectionservice as a service class to help with submit
danielfang97 Jul 4, 2026
10d5b60
removed collectionChoices, added JsonUtil, and moved some other methods
danielfang97 Jul 4, 2026
249a2a2
moved readSbol function into sbolservice
danielfang97 Jul 5, 2026
16e260d
updated to 3.4.13 for spring boot to use RestClient
danielfang97 Jul 7, 2026
f7e3c3f
moved attachment part of submit to attachmentservice
danielfang97 Jul 8, 2026
f7aa50a
Merge branch 'main' into submitEndpoint4
danielfang97 Jul 16, 2026
10a67e9
missing a comma throwing an error
danielfang97 Jul 16, 2026
0eb63b1
missing imports in submit controller
danielfang97 Jul 16, 2026
2a7f76b
Merge branch 'main' into submitEndpoint4
danielfang97 Jul 20, 2026
8bc18f6
got sboltestrunner running
danielfang97 Jul 21, 2026
6fa1bfb
fixed an issue with the jar file when running sboltestrunner on github
danielfang97 Jul 23, 2026
bea16bc
removed a tag that caused the test to fail
danielfang97 Jul 23, 2026
e2dabe1
fixed an issue where the change log said there was a file diff in tes…
danielfang97 Jul 23, 2026
167f6a5
added a rewriter to download so that prefixes match (may revert)
danielfang97 Jul 23, 2026
2148bca
applied same fix as search refactor branch for admin graphs
danielfang97 Jul 23, 2026
777f6c2
added python installs to sbolsuite test script
danielfang97 Jul 23, 2026
dc96ac5
fixed an issue with some unique prefixes messing up the rewriter
danielfang97 Jul 23, 2026
9794730
Merge branch 'main' into submitEndpoint4
danielfang97 Jul 24, 2026
914bb8e
Merge branch 'main' into submitEndpoint4
danielfang97 Jul 24, 2026
a4a2ac5
fixed some issues that arose due to merge conflicts
danielfang97 Jul 24, 2026
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
36 changes: 36 additions & 0 deletions .github/workflows/test-backend.yml
Original file line number Diff line number Diff line change
Expand Up @@ -55,3 +55,39 @@ jobs:
- name: Run tests
run: |
tests/test.sh

sboltestrunner:
name: SBOLTestRunner
needs: build
runs-on: ubuntu-latest
timeout-minutes: 180
steps:
- uses: actions/checkout@v3
name: Checkout source tree
- uses: actions/download-artifact@v4
name: Download Docker image
with:
name: sbh3-image
- name: Import saved Docker image
run: |
cat sbh3.tar.gz | docker load
- name: Install Docker Compose
run: |
sudo apt-get update
sudo apt-get install -y docker-compose
- uses: actions/setup-java@v4
name: Install Java
with:
distribution: temurin
java-version: '17'
- uses: actions/setup-python@v4
name: Install Python
with:
python-version: '3.9'
- name: Install test dependencies
run: |
pip install -r tests/test_requirements.txt
- name: Run SBOLTestRunner
run: |
cd tests
bash ./sbolsuite.sh
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -171,4 +171,6 @@ tests/Compared/
tests/Emulated/
tests/Retrieved/
tests/SynBioHubRunner/
tests/Timing/
tests/sbol_testrunner_result
tests/logs_from_test_suite/

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What is this file for?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is for helping with submit plugins. The plugin needs access to the file that it is trying to convert. So, this gives the plugin a link to download the file. After it's downloaded, SynBioHub deletes the link.

Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
package com.synbiohub.sbh3.controllers;

import com.synbiohub.sbh3.submit.SubmitExposeRegistry;
import lombok.RequiredArgsConstructor;
import org.springframework.core.io.FileSystemResource;
import org.springframework.core.io.Resource;
import org.springframework.http.HttpHeaders;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RestController;

import java.net.URLConnection;
import java.nio.file.Files;
import java.nio.file.Path;

@RestController
@RequiredArgsConstructor
public class ExposeController {

private final SubmitExposeRegistry exposeRegistry;

@GetMapping("/expose/{token}")
public ResponseEntity<Resource> exposeFile(@PathVariable String token) {
Path file = exposeRegistry.resolve(token);
if (file == null || !Files.isRegularFile(file)) {
return ResponseEntity.notFound().build();
}
String filename = file.getFileName().toString();
String contentType = URLConnection.guessContentTypeFromName(filename);
MediaType mediaType = contentType != null
? MediaType.parseMediaType(contentType)
: MediaType.APPLICATION_OCTET_STREAM;
return ResponseEntity.ok()
.header(HttpHeaders.CONTENT_DISPOSITION, "inline; filename=\"" + filename + "\"")
.contentType(mediaType)
.body(new FileSystemResource(file));
}
}
Original file line number Diff line number Diff line change
@@ -1,15 +1,19 @@
package com.synbiohub.sbh3.controllers;

import com.synbiohub.sbh3.services.SubmitService;
import com.synbiohub.sbh3.submit.SubmitPayload;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
import lombok.RequiredArgsConstructor;
import org.sbolstandard.core2.SBOLDocument;
import org.sbolstandard.core2.SBOLValidationException;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;

import java.io.IOException;
import java.util.Map;

@Tag(name = "Submissions", description = "Endpoints for creating and submitting registry objects")
Expand All @@ -21,17 +25,17 @@ public class SubmitController {
/**
* Create a new collection or submit file contents into an existing collection.
* <p>
* String form fields are bound via {@code allParams} (e.g. id, version, name,
* description,
* citations, overwrite_merge, rootCollections, plugin). The uploaded design
* file is bound
* separately because multipart files are not included in a
* {@code Map<String, String>}.
* String form fields are bound via {@code allParams} (e.g. id, version, name, description,
* citations, overwrite_merge, rootCollections, plugin). The uploaded design file is bound
* separately because multipart files are not included in a {@code Map<String, String>}.
*/
@PostMapping(value = "/submit", consumes = MediaType.MULTIPART_FORM_DATA_VALUE, produces = "text/plain; charset=UTF-8")
@Operation(summary = "Submit object (stub)", description = "Temporary stub so the branch compiles.")
public ResponseEntity<String> submit(@RequestParam Map<String, String> allParams) {
return ResponseEntity.ok("Form submitted");
@PostMapping(value = "/submit",
consumes = MediaType.MULTIPART_FORM_DATA_VALUE,
produces = "text/plain; charset=UTF-8")
public ResponseEntity<String> submit(
@ModelAttribute SubmitPayload allParams,
@RequestPart(value = "file", required = false) MultipartFile file) throws IOException, SBOLValidationException {
return submitService.submit(allParams, file);
}

@Operation(summary = "Create new collection (Unimplemented)", description = "Currently an empty stub.", deprecated = true)
Expand Down
80 changes: 80 additions & 0 deletions backend/src/main/java/com/synbiohub/sbh3/dao/SparqlService.java
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
package com.synbiohub.sbh3.dao;

import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.synbiohub.sbh3.repo.SparqlRepository;
import com.synbiohub.sbh3.sparql.SPARQLQuery;
import com.synbiohub.sbh3.utils.ConfigUtil;
Expand All @@ -12,6 +14,7 @@
import org.springframework.stereotype.Service;

import java.io.IOException;
import java.nio.file.Path;
import java.util.HashMap;
import java.util.Map;
import java.util.Objects;
Expand All @@ -22,8 +25,85 @@
public class SparqlService {

private final SparqlRepository sparqlRepository;
private final ObjectMapper objectMapper;
private final JsonUtil jsonUtil;

/**
* Virtuoso DELETE templates return one row per batch; loop until nothing remains.
* Legacy {@code sparql.deleteStaggered}.
*/
public void deleteCollection(Map<String, String> params, String graphUri) throws IOException {
deleteStaggered(sparqlRepository.REMOVE_COLLECTION_SPARQL, params, graphUri);
}

public void delete(Map<String, String> params, String graphUri) throws IOException {
deleteStaggered(sparqlRepository.REMOVE_SPARQL, params, graphUri);
}

public void uploadGraphStore(String graphUri, Path file) throws IOException {
sparqlRepository.save(graphUri, file);
}

public void uploadAttachment(Map<String, String> params, String graphUri) throws IOException {
String query = new SPARQLQuery(sparqlRepository.ATTACHMENT_UPDATE_SPARQL).loadTemplate(params);
sparqlRepository.update(query, graphUri, false);
}

public void update(String query, String graphUri, boolean jsonResults) throws IOException {
sparqlRepository.update(query, graphUri, jsonResults);
}

private void deleteStaggered(String queryTemplate, Map<String, String> params, String graphUri) throws IOException {
String query = new SPARQLQuery(queryTemplate).loadTemplate(params);
while (true) {
String raw = sparqlRepository.update(query, graphUri, true);
JsonNode bindings = objectMapper.readTree(raw).path("results").path("bindings");
if (!bindings.isArray() || bindings.isEmpty()) {
break;
}
String msg = bindings.get(0).path("callret-0").path("value").asText("");
if (msg.contains("nothing to do")) {
break;
}
}
}

/** Maps existing {@code sbol:source} values (e.g. {@code file:foo.png}) to attachment URIs. */
public Map<String, String> loadAttachmentSources(String collectionUri, String graphUri) throws IOException {
String query = new SPARQLQuery(SparqlRepository.GET_ATTACHMENT_SOURCE_SPARQL)
.loadTemplate(Map.of("uri", collectionUri));
String raw = sparqlRepository.getQuery(query, graphUri);
Map<String, String> sources = new HashMap<>();
JsonNode bindings = objectMapper.readTree(raw).path("results").path("bindings");
if (!bindings.isArray()) {
return sources;
}
for (JsonNode row : bindings) {
String source = row.path("source").path("value").asText(null);
String attachment = row.path("attachment").path("value").asText(null);
if (source != null && attachment != null) {
sources.put(source, attachment);
}
}
return sources;
}

public void attachUpload(Map<String, String> params, String graphUri, boolean jsonResults) throws IOException {
String query = new SPARQLQuery(sparqlRepository.ATTACH_UPLOAD_SPARQL).loadTemplate(params);
update(query, graphUri, false);
}

/** Replaces hash/size on an existing attachment when re-uploading the same {@code file:} source. */
public void updateAttachment(String graphUri, String attachmentUri, String uploadHash, long size)
throws IOException {
String query = new SPARQLQuery(sparqlRepository.UPDATE_ATTACHMENT_SPARQL).loadTemplate(Map.of(
"attachmentURI", attachmentUri,
"attachmentSource", attachmentUri + "/download",
"hash", StringUtil.sparqlStringLiteral(uploadHash),
"size", StringUtil.sparqlStringLiteral(Long.toString(size))));
update(query, graphUri, false);
}

public Map<String, String> buildSearchQuery(Map<String, String> allParams) {
HashMap<String, String> sparqlArgs = new HashMap<>
(Map.of("from", "", "criteria", "", "limit", "", "offset", ""));
Expand Down
27 changes: 0 additions & 27 deletions backend/src/main/java/com/synbiohub/sbh3/dto/SubmissionDTO.java

This file was deleted.

Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
package com.synbiohub.sbh3.dto;

public enum SubmitFileFormat {
SBOL, GENBANK, FASTA, GFF3, ATTACHMENT
}
Loading
Loading