diff --git a/jabsrv/src/main/java/org/jabref/http/server/BibEntryResource.java b/jabsrv/src/main/java/org/jabref/http/server/BibEntryResource.java new file mode 100644 index 00000000000..89e658fda12 --- /dev/null +++ b/jabsrv/src/main/java/org/jabref/http/server/BibEntryResource.java @@ -0,0 +1,98 @@ +package org.jabref.http.server; + +import java.io.IOException; +import java.util.List; + +import org.jabref.http.JabrefMediaType; +import org.jabref.http.SrvStateManager; +import org.jabref.http.dto.BibEntryDTO; +import org.jabref.http.server.services.FilesToServe; +import org.jabref.http.server.services.ServerUtils; +import org.jabref.logic.citationstyle.JabRefItemDataProvider; +import org.jabref.logic.preferences.CliPreferences; +import org.jabref.model.database.BibDatabaseContext; +import org.jabref.model.entry.BibEntry; +import org.jabref.model.entry.BibEntryTypesManager; + +import com.airhacks.afterburner.injection.Injector; +import com.google.gson.Gson; +import de.undercouch.citeproc.csl.CSLItemData; +import jakarta.inject.Inject; +import jakarta.ws.rs.GET; +import jakarta.ws.rs.NotFoundException; +import jakarta.ws.rs.Path; +import jakarta.ws.rs.PathParam; +import jakarta.ws.rs.Produces; +import jakarta.ws.rs.core.MediaType; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +@Path("libraries/{id}/entries/{entryId}") +public class BibEntryResource { + + private static final Logger LOGGER = LoggerFactory.getLogger(BibEntryResource.class); + + @Inject + private CliPreferences preferences; + + @Inject + private SrvStateManager srvStateManager; + + @Inject + private FilesToServe filesToServe; + + @Inject + private Gson gson; + + @GET + @Produces(JabrefMediaType.JSON_CSL_ITEM) + public String getCSLJsonRepresentation(@PathParam("id") String id, + @PathParam("entryId") String entryId) throws IOException { + List entriesByCitationKey = getDatabaseContext(id) + .getDatabase() + .getEntriesByCitationKey(entryId); + + if (entriesByCitationKey.isEmpty()) { + throw new NotFoundException("Entry with citation key '" + entryId + "' not found in library " + id); + } + + if (entriesByCitationKey.size() > 1) { + LOGGER.warn("Multiple CSL JSON entries found with citation key '{}'. Using the first one.", entryId); + } + + BibEntryTypesManager entryTypesManager = Injector.instantiateModelOrService(BibEntryTypesManager.class); + JabRefItemDataProvider jabRefItemDataProvider = new JabRefItemDataProvider(); + jabRefItemDataProvider.setData(entriesByCitationKey, getDatabaseContext(id), entryTypesManager); + + CSLItemData cslItem = jabRefItemDataProvider.retrieveItem(entryId); + + if (cslItem == null) { + throw new NotFoundException("Unable to convert entry '" + entryId + "' to CSL JSON"); + } + + return gson.toJson(List.of(cslItem)); + } + + @GET + @Produces(MediaType.APPLICATION_JSON) + public String getJsonRepresentation(@PathParam("id") String id, + @PathParam("entryId") String entryId) throws IOException { + BibDatabaseContext databaseContextBib = getDatabaseContext(id); + BibEntryTypesManager entryTypesManager = Injector.instantiateModelOrService(BibEntryTypesManager.class); + + List list = databaseContextBib.getDatabase() + .getEntriesByCitationKey(entryId) + .stream() + .map(entry -> new BibEntryDTO(entry, + databaseContextBib.getMode(), + preferences.getFieldPreferences(), + entryTypesManager)) + .toList(); + + return gson.toJson(list); + } + + private BibDatabaseContext getDatabaseContext(String id) throws IOException { + return ServerUtils.getBibDatabaseContext(id, filesToServe, srvStateManager, preferences.getImportFormatPreferences()); + } +} diff --git a/jabsrv/src/main/java/org/jabref/http/server/LibraryResource.java b/jabsrv/src/main/java/org/jabref/http/server/LibraryResource.java index 1d53dcc9ba0..42263d967ef 100644 --- a/jabsrv/src/main/java/org/jabref/http/server/LibraryResource.java +++ b/jabsrv/src/main/java/org/jabref/http/server/LibraryResource.java @@ -20,7 +20,6 @@ import org.jabref.model.entry.BibEntry; import org.jabref.model.entry.BibEntryTypesManager; import org.jabref.model.entry.LinkedFile; -import org.jabref.model.entry.field.StandardField; import com.airhacks.afterburner.injection.Injector; import com.google.gson.Gson; @@ -45,16 +44,16 @@ public class LibraryResource { private static final Logger LOGGER = LoggerFactory.getLogger(LibraryResource.class); @Inject - CliPreferences preferences; + private CliPreferences preferences; @Inject - SrvStateManager srvStateManager; + private SrvStateManager srvStateManager; @Inject - FilesToServe filesToServe; + private FilesToServe filesToServe; @Inject - Gson gson; + private Gson gson; /** * At http://localhost:23119/libraries/{id} @@ -202,101 +201,7 @@ private BibDatabaseContext getDatabaseContext(String id) throws IOException { return ServerUtils.getBibDatabaseContext(id, filesToServe, srvStateManager, preferences.getImportFormatPreferences()); } - /** - * At http://localhost:23119/libraries/{id}/entries/{entryId}

- * - * Combines attributes of a given BibEntry into a basic entry preview for as plain text. - * - * @param id The name of the library - * @param entryId The CitationKey of the BibEntry - * @return a basic entry preview as plain text - * @throws IOException - * @throws NotFoundException - */ - @GET - @Path("entries/{entryId}") - @Produces(MediaType.TEXT_PLAIN + ";charset=UTF-8") - public String getPlainRepresentation(@PathParam("id") String id, @PathParam("entryId") String entryId) throws IOException { - BibDatabaseContext databaseContext = getDatabaseContext(id); - List entriesByCitationKey = databaseContext.getDatabase().getEntriesByCitationKey(entryId); - if (entriesByCitationKey.isEmpty()) { - throw new NotFoundException("Entry with citation key '" + entryId + "' not found in library " + id); - } - if (entriesByCitationKey.size() > 1) { - LOGGER.warn("Multiple entries found with citation key '{}'. Using the first one.", entryId); - } - - // TODO: Currently, the preview preferences are in GUI package, which is not accessible here. - // build the preview - BibEntry entry = entriesByCitationKey.getFirst(); - - String author = entry.getField(StandardField.AUTHOR).orElse("(N/A)"); - String title = entry.getField(StandardField.TITLE).orElse("(N/A)"); - String journal = entry.getField(StandardField.JOURNAL).orElse("(N/A)"); - String volume = entry.getField(StandardField.VOLUME).orElse("(N/A)"); - String number = entry.getField(StandardField.NUMBER).orElse("(N/A)"); - String pages = entry.getField(StandardField.PAGES).orElse("(N/A)"); - String releaseDate = entry.getField(StandardField.DATE).orElse("(N/A)"); - - // the only difference to the HTML version of this method is the format of the output: - String preview = - "Author: " + author - + "\nTitle: " + title - + "\nJournal: " + journal - + "\nVolume: " + volume - + "\nNumber: " + number - + "\nPages: " + pages - + "\nReleased on: " + releaseDate; - - return preview; - } - /** - * At http://localhost:23119/libraries/{id}/entries/{entryId}

- * - * Combines attributes of a given BibEntry into a basic entry preview for as HTML text. - * - * @param id The name of the library - * @param entryId The CitationKey of the BibEntry - * @return a basic entry preview as HTML text - * @throws IOException - */ - @GET - @Path("entries/{entryId}") - @Produces(MediaType.TEXT_HTML + ";charset=UTF-8") - public String getHTMLRepresentation(@PathParam("id") String id, @PathParam("entryId") String entryId) throws IOException { - List entriesByCitationKey = getDatabaseContext(id).getDatabase().getEntriesByCitationKey(entryId); - if (entriesByCitationKey.isEmpty()) { - throw new NotFoundException("Entry with citation key '" + entryId + "' not found in library " + id); - } - if (entriesByCitationKey.size() > 1) { - LOGGER.warn("Multiple entries found with citation key '{}'. Using the first one.", entryId); - } - - // TODO: Currently, the preview preferences are in GUI package, which is not accessible here. - // build the preview - BibEntry entry = entriesByCitationKey.getFirst(); - - String author = entry.getField(StandardField.AUTHOR).orElse("(N/A)"); - String title = entry.getField(StandardField.TITLE).orElse("(N/A)"); - String journal = entry.getField(StandardField.JOURNAL).orElse("(N/A)"); - String volume = entry.getField(StandardField.VOLUME).orElse("(N/A)"); - String number = entry.getField(StandardField.NUMBER).orElse("(N/A)"); - String pages = entry.getField(StandardField.PAGES).orElse("(N/A)"); - String releaseDate = entry.getField(StandardField.DATE).orElse("(N/A)"); - - // the only difference to the plain text version of this method is the format of the output: - String preview = - "Author: " + author + "
" + - "Title: " + title + "
" + - "Journal: " + journal + "
" + - "Volume: " + volume + "
" + - "Number: " + number + "
" + - "Pages: " + pages + "
" + - "Released on: " + releaseDate; - - return preview; - } /** * At http://localhost:23119/libraries/{id}/entries/pdffiles

diff --git a/jabsrv/src/main/java/org/jabref/http/server/Server.java b/jabsrv/src/main/java/org/jabref/http/server/Server.java index 72a79fd707a..7580e94013a 100644 --- a/jabsrv/src/main/java/org/jabref/http/server/Server.java +++ b/jabsrv/src/main/java/org/jabref/http/server/Server.java @@ -99,6 +99,7 @@ private HttpServer startServer(ServiceLocator serviceLocator, URI uri) { resourceConfig.register(CommandResource.class); resourceConfig.register(CORSFilter.class); resourceConfig.register(GlobalExceptionMapper.class); + resourceConfig.register(BibEntryResource.class); LOGGER.debug("Starting HTTP server..."); final HttpServer httpServer = diff --git a/jabsrv/src/test/java/org/jabref/http/server/BibEntryResourceTest.java b/jabsrv/src/test/java/org/jabref/http/server/BibEntryResourceTest.java new file mode 100644 index 00000000000..cc71ca8fac1 --- /dev/null +++ b/jabsrv/src/test/java/org/jabref/http/server/BibEntryResourceTest.java @@ -0,0 +1,99 @@ +package org.jabref.http.server; + +import org.jabref.http.JabrefMediaType; + +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.ObjectMapper; +import jakarta.ws.rs.core.Application; +import jakarta.ws.rs.core.MediaType; +import org.glassfish.jersey.server.ResourceConfig; +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +class BibEntryResourceTest extends ServerTest { + + + private final ObjectMapper mapper = new ObjectMapper(); + + @Override + protected Application configure() { + ResourceConfig resourceConfig = new ResourceConfig( + LibraryResource.class, + LibrariesResource.class, + BibEntryResource.class + ); + addFilesToServeToResourceConfig(resourceConfig); + addGuiBridgeToResourceConfig(resourceConfig); + addPreferencesToResourceConfig(resourceConfig); + addGsonToResourceConfig(resourceConfig); + return resourceConfig.getApplication(); + } + + @Test + void getCSLJsonRepresentation() throws Exception { + String response = target("/libraries/" + TestBibFile.GENERAL_SERVER_TEST.id + "/entries/Author2023test") + .request(JabrefMediaType.JSON_CSL_ITEM) + .get(String.class); + String expected = """ + [ + { + "id": "Author2023test", + "type": "ARTICLE", + "author": [ + { + "family": "Author", + "given": "Demo" + } + ], + "eventDate": { + "dateParts": [ + [ + 2023 + ] + ] + }, + "issued": { + "dateParts": [ + [ + 2023 + ] + ] + }, + "title": "Demo Title" + } + ] + """; + + JsonNode root = mapper.readTree(response); + JsonNode expectedJson = mapper.readTree(expected); + + assertEquals(root, expectedJson, "CSL JSON must match the expected structure"); + } + + @Test + void getJsonRepresentation() throws Exception { + String response = target("/libraries/" + TestBibFile.GENERAL_SERVER_TEST.id + "/entries/Author2023test") + .request(MediaType.APPLICATION_JSON) + .get(String.class); + String expected = """ + [ + { + "sharingMetadata": { + "sharedID": -1, + "version": 1 + }, + "userComments": "", + "citationKey": "Author2023test", + "bibtex": "@Misc{Author2023test,\\n author \\u003d {Demo Author},\\n title \\u003d {Demo Title},\\n year \\u003d {2023},\\n}\\n" + } + ] + """; + + JsonNode root = mapper.readTree(response); + + JsonNode expectedJson = mapper.readTree(expected); + + assertEquals(root, expectedJson, "JSON must match the expected structure"); + } +} diff --git a/jabsrv/src/test/rest-api.http b/jabsrv/src/test/rest-api.http index 8799ee1c1fe..caf73f3bb4f 100644 --- a/jabsrv/src/test/rest-api.http +++ b/jabsrv/src/test/rest-api.http @@ -94,3 +94,6 @@ Accept: application/x-bibtex-library-csl+json ### GET not available library GET http://localhost:23119/libraries/notfound + +### +GET http://localhost:23119/libraries/demo/entries/Corti_2009