-
-
Notifications
You must be signed in to change notification settings - Fork 3k
Extract BibEntry endpoints into BibEntryResource and add tests #13731
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
Changes from all commits
0b17f24
009b223
9788ad3
d0e2f38
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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<BibEntry> 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<BibEntryDTO> 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()); | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -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} <br><br> | ||
* | ||
* 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<BibEntry> 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} <br><br> | ||
* | ||
* 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<BibEntry> 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 = | ||
"<strong>Author:</strong> " + author + "<br>" + | ||
"<strong>Title:</strong> " + title + "<br>" + | ||
"<strong>Journal:</strong> " + journal + "<br>" + | ||
"<strong>Volume:</strong> " + volume + "<br>" + | ||
"<strong>Number:</strong> " + number + "<br>" + | ||
"<strong>Pages:</strong> " + pages + "<br>" + | ||
"<strong>Released on:</strong> " + releaseDate; | ||
|
||
return preview; | ||
} | ||
Comment on lines
-254
to
-299
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. those too |
||
|
||
/** | ||
* At http://localhost:23119/libraries/{id}/entries/pdffiles <br><br> | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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"); | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -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 | ||
Comment on lines
+97
to
+99
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Please add a heading like on the others and move it up to the other cases which handle the entries itself, the end is for error cases. Also add that it only accepts the according mediatype |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Those are the ones that should be moved to the new resource.