Skip to content

Commit 0b17f24

Browse files
author
igor
committed
Extract BibEntry endpoints into BibEntryResource and add tests
1 parent 85dd7c1 commit 0b17f24

File tree

6 files changed

+208
-99
lines changed

6 files changed

+208
-99
lines changed

CHANGELOG.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,9 @@ Note that this project **does not** adhere to [Semantic Versioning](https://semv
2626
- We added an initial [cite as you write](https://retorque.re/zotero-better-bibtex/citing/cayw/) endpoint. [#13187](https://github.com/JabRef/jabref/issues/13187)
2727
- We added "copy preview as markdown" feature. [#12552](https://github.com/JabRef/jabref/issues/12552)
2828
- In case no citation relation information can be fetched, we show the data providers reason. [#13549](https://github.com/JabRef/jabref/pull/13549)
29+
- When relativizing file names, symlinks are now taken into account. [#12995](https://github.com/JabRef/jabref/issues/12995)
30+
- We added a new button for shortening the DOI near the DOI field in the general tab when viewing an entry. [#13639](https://github.com/JabRef/jabref/issues/13639)
31+
2932

3033
### Changed
3134

Lines changed: 98 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,98 @@
1+
package org.jabref.http.server;
2+
3+
import java.io.IOException;
4+
import java.util.List;
5+
6+
import org.jabref.http.JabrefMediaType;
7+
import org.jabref.http.SrvStateManager;
8+
import org.jabref.http.dto.BibEntryDTO;
9+
import org.jabref.http.server.services.FilesToServe;
10+
import org.jabref.http.server.services.ServerUtils;
11+
import org.jabref.logic.citationstyle.JabRefItemDataProvider;
12+
import org.jabref.logic.preferences.CliPreferences;
13+
import org.jabref.model.database.BibDatabaseContext;
14+
import org.jabref.model.entry.BibEntry;
15+
import org.jabref.model.entry.BibEntryTypesManager;
16+
17+
import com.airhacks.afterburner.injection.Injector;
18+
import com.google.gson.Gson;
19+
import de.undercouch.citeproc.csl.CSLItemData;
20+
import jakarta.inject.Inject;
21+
import jakarta.ws.rs.GET;
22+
import jakarta.ws.rs.NotFoundException;
23+
import jakarta.ws.rs.Path;
24+
import jakarta.ws.rs.PathParam;
25+
import jakarta.ws.rs.Produces;
26+
import jakarta.ws.rs.core.MediaType;
27+
import org.slf4j.Logger;
28+
import org.slf4j.LoggerFactory;
29+
30+
@Path("libraries/{id}/entries/{entryId}")
31+
public class BibEntryResource {
32+
33+
private static final Logger LOGGER = LoggerFactory.getLogger(BibEntryResource.class);
34+
35+
@Inject
36+
private CliPreferences preferences;
37+
38+
@Inject
39+
private SrvStateManager srvStateManager;
40+
41+
@Inject
42+
private FilesToServe filesToServe;
43+
44+
@Inject
45+
private Gson gson;
46+
47+
@GET
48+
@Produces(JabrefMediaType.JSON_CSL_ITEM)
49+
public String getCSLJsonRepresentation(@PathParam("id") String id,
50+
@PathParam("entryId") String entryId) throws IOException {
51+
List<BibEntry> entriesByCitationKey = getDatabaseContext(id)
52+
.getDatabase()
53+
.getEntriesByCitationKey(entryId);
54+
55+
if (entriesByCitationKey.isEmpty()) {
56+
throw new NotFoundException("Entry with citation key '" + entryId + "' not found in library " + id);
57+
}
58+
59+
if (entriesByCitationKey.size() > 1) {
60+
LOGGER.warn("Multiple CSL JSON entries found with citation key '{}'. Using the first one.", entryId);
61+
}
62+
63+
BibEntryTypesManager entryTypesManager = Injector.instantiateModelOrService(BibEntryTypesManager.class);
64+
JabRefItemDataProvider jabRefItemDataProvider = new JabRefItemDataProvider();
65+
jabRefItemDataProvider.setData(entriesByCitationKey, getDatabaseContext(id), entryTypesManager);
66+
67+
CSLItemData cslItem = jabRefItemDataProvider.retrieveItem(entryId);
68+
69+
if (cslItem == null) {
70+
throw new NotFoundException("Unable to convert entry '" + entryId + "' to CSL JSON");
71+
}
72+
73+
return gson.toJson(List.of(cslItem));
74+
}
75+
76+
@GET
77+
@Produces(MediaType.APPLICATION_JSON)
78+
public String getJsonRepresentation(@PathParam("id") String id,
79+
@PathParam("entryId") String entryId) throws IOException {
80+
BibDatabaseContext databaseContextBib = getDatabaseContext(id);
81+
BibEntryTypesManager entryTypesManager = Injector.instantiateModelOrService(BibEntryTypesManager.class);
82+
83+
List<BibEntryDTO> list = databaseContextBib.getDatabase()
84+
.getEntriesByCitationKey(entryId)
85+
.stream()
86+
.map(entry -> new BibEntryDTO(entry,
87+
databaseContextBib.getMode(),
88+
preferences.getFieldPreferences(),
89+
entryTypesManager))
90+
.toList();
91+
92+
return gson.toJson(list);
93+
}
94+
95+
private BibDatabaseContext getDatabaseContext(String id) throws IOException {
96+
return ServerUtils.getBibDatabaseContext(id, filesToServe, srvStateManager, preferences.getImportFormatPreferences());
97+
}
98+
}

jabsrv/src/main/java/org/jabref/http/server/LibraryResource.java

Lines changed: 4 additions & 99 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,6 @@
2020
import org.jabref.model.entry.BibEntry;
2121
import org.jabref.model.entry.BibEntryTypesManager;
2222
import org.jabref.model.entry.LinkedFile;
23-
import org.jabref.model.entry.field.StandardField;
2423

2524
import com.airhacks.afterburner.injection.Injector;
2625
import com.google.gson.Gson;
@@ -45,16 +44,16 @@ public class LibraryResource {
4544
private static final Logger LOGGER = LoggerFactory.getLogger(LibraryResource.class);
4645

4746
@Inject
48-
CliPreferences preferences;
47+
private CliPreferences preferences;
4948

5049
@Inject
51-
SrvStateManager srvStateManager;
50+
private SrvStateManager srvStateManager;
5251

5352
@Inject
54-
FilesToServe filesToServe;
53+
private FilesToServe filesToServe;
5554

5655
@Inject
57-
Gson gson;
56+
private Gson gson;
5857

5958
/**
6059
* At http://localhost:23119/libraries/{id}
@@ -202,101 +201,7 @@ private BibDatabaseContext getDatabaseContext(String id) throws IOException {
202201
return ServerUtils.getBibDatabaseContext(id, filesToServe, srvStateManager, preferences.getImportFormatPreferences());
203202
}
204203

205-
/**
206-
* At http://localhost:23119/libraries/{id}/entries/{entryId} <br><br>
207-
*
208-
* Combines attributes of a given BibEntry into a basic entry preview for as plain text.
209-
*
210-
* @param id The name of the library
211-
* @param entryId The CitationKey of the BibEntry
212-
* @return a basic entry preview as plain text
213-
* @throws IOException
214-
* @throws NotFoundException
215-
*/
216-
@GET
217-
@Path("entries/{entryId}")
218-
@Produces(MediaType.TEXT_PLAIN + ";charset=UTF-8")
219-
public String getPlainRepresentation(@PathParam("id") String id, @PathParam("entryId") String entryId) throws IOException {
220-
BibDatabaseContext databaseContext = getDatabaseContext(id);
221-
List<BibEntry> entriesByCitationKey = databaseContext.getDatabase().getEntriesByCitationKey(entryId);
222-
if (entriesByCitationKey.isEmpty()) {
223-
throw new NotFoundException("Entry with citation key '" + entryId + "' not found in library " + id);
224-
}
225-
if (entriesByCitationKey.size() > 1) {
226-
LOGGER.warn("Multiple entries found with citation key '{}'. Using the first one.", entryId);
227-
}
228-
229-
// TODO: Currently, the preview preferences are in GUI package, which is not accessible here.
230-
// build the preview
231-
BibEntry entry = entriesByCitationKey.getFirst();
232-
233-
String author = entry.getField(StandardField.AUTHOR).orElse("(N/A)");
234-
String title = entry.getField(StandardField.TITLE).orElse("(N/A)");
235-
String journal = entry.getField(StandardField.JOURNAL).orElse("(N/A)");
236-
String volume = entry.getField(StandardField.VOLUME).orElse("(N/A)");
237-
String number = entry.getField(StandardField.NUMBER).orElse("(N/A)");
238-
String pages = entry.getField(StandardField.PAGES).orElse("(N/A)");
239-
String releaseDate = entry.getField(StandardField.DATE).orElse("(N/A)");
240-
241-
// the only difference to the HTML version of this method is the format of the output:
242-
String preview =
243-
"Author: " + author
244-
+ "\nTitle: " + title
245-
+ "\nJournal: " + journal
246-
+ "\nVolume: " + volume
247-
+ "\nNumber: " + number
248-
+ "\nPages: " + pages
249-
+ "\nReleased on: " + releaseDate;
250-
251-
return preview;
252-
}
253204

254-
/**
255-
* At http://localhost:23119/libraries/{id}/entries/{entryId} <br><br>
256-
*
257-
* Combines attributes of a given BibEntry into a basic entry preview for as HTML text.
258-
*
259-
* @param id The name of the library
260-
* @param entryId The CitationKey of the BibEntry
261-
* @return a basic entry preview as HTML text
262-
* @throws IOException
263-
*/
264-
@GET
265-
@Path("entries/{entryId}")
266-
@Produces(MediaType.TEXT_HTML + ";charset=UTF-8")
267-
public String getHTMLRepresentation(@PathParam("id") String id, @PathParam("entryId") String entryId) throws IOException {
268-
List<BibEntry> entriesByCitationKey = getDatabaseContext(id).getDatabase().getEntriesByCitationKey(entryId);
269-
if (entriesByCitationKey.isEmpty()) {
270-
throw new NotFoundException("Entry with citation key '" + entryId + "' not found in library " + id);
271-
}
272-
if (entriesByCitationKey.size() > 1) {
273-
LOGGER.warn("Multiple entries found with citation key '{}'. Using the first one.", entryId);
274-
}
275-
276-
// TODO: Currently, the preview preferences are in GUI package, which is not accessible here.
277-
// build the preview
278-
BibEntry entry = entriesByCitationKey.getFirst();
279-
280-
String author = entry.getField(StandardField.AUTHOR).orElse("(N/A)");
281-
String title = entry.getField(StandardField.TITLE).orElse("(N/A)");
282-
String journal = entry.getField(StandardField.JOURNAL).orElse("(N/A)");
283-
String volume = entry.getField(StandardField.VOLUME).orElse("(N/A)");
284-
String number = entry.getField(StandardField.NUMBER).orElse("(N/A)");
285-
String pages = entry.getField(StandardField.PAGES).orElse("(N/A)");
286-
String releaseDate = entry.getField(StandardField.DATE).orElse("(N/A)");
287-
288-
// the only difference to the plain text version of this method is the format of the output:
289-
String preview =
290-
"<strong>Author:</strong> " + author + "<br>" +
291-
"<strong>Title:</strong> " + title + "<br>" +
292-
"<strong>Journal:</strong> " + journal + "<br>" +
293-
"<strong>Volume:</strong> " + volume + "<br>" +
294-
"<strong>Number:</strong> " + number + "<br>" +
295-
"<strong>Pages:</strong> " + pages + "<br>" +
296-
"<strong>Released on:</strong> " + releaseDate;
297-
298-
return preview;
299-
}
300205

301206
/**
302207
* At http://localhost:23119/libraries/{id}/entries/pdffiles <br><br>

jabsrv/src/main/java/org/jabref/http/server/Server.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -99,6 +99,7 @@ private HttpServer startServer(ServiceLocator serviceLocator, URI uri) {
9999
resourceConfig.register(CommandResource.class);
100100
resourceConfig.register(CORSFilter.class);
101101
resourceConfig.register(GlobalExceptionMapper.class);
102+
resourceConfig.register(BibEntryResource.class);
102103

103104
LOGGER.debug("Starting HTTP server...");
104105
final HttpServer httpServer =
Lines changed: 99 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,99 @@
1+
package org.jabref.http.server;
2+
3+
import org.jabref.http.JabrefMediaType;
4+
5+
import com.fasterxml.jackson.databind.JsonNode;
6+
import com.fasterxml.jackson.databind.ObjectMapper;
7+
import jakarta.ws.rs.core.Application;
8+
import jakarta.ws.rs.core.MediaType;
9+
import org.glassfish.jersey.server.ResourceConfig;
10+
import org.junit.jupiter.api.Test;
11+
12+
import static org.junit.jupiter.api.Assertions.assertEquals;
13+
14+
class BibEntryResourceTest extends ServerTest {
15+
16+
17+
private final ObjectMapper mapper = new ObjectMapper();
18+
19+
@Override
20+
protected Application configure() {
21+
ResourceConfig resourceConfig = new ResourceConfig(
22+
LibraryResource.class,
23+
LibrariesResource.class,
24+
BibEntryResource.class
25+
);
26+
addFilesToServeToResourceConfig(resourceConfig);
27+
addGuiBridgeToResourceConfig(resourceConfig);
28+
addPreferencesToResourceConfig(resourceConfig);
29+
addGsonToResourceConfig(resourceConfig);
30+
return resourceConfig.getApplication();
31+
}
32+
33+
@Test
34+
void getCSLJsonRepresentation() throws Exception {
35+
String response = target("/libraries/" + TestBibFile.GENERAL_SERVER_TEST.id + "/entries/Author2023test")
36+
.request(JabrefMediaType.JSON_CSL_ITEM)
37+
.get(String.class);
38+
String expected = """
39+
[
40+
{
41+
"id": "Author2023test",
42+
"type": "ARTICLE",
43+
"author": [
44+
{
45+
"family": "Author",
46+
"given": "Demo"
47+
}
48+
],
49+
"eventDate": {
50+
"dateParts": [
51+
[
52+
2023
53+
]
54+
]
55+
},
56+
"issued": {
57+
"dateParts": [
58+
[
59+
2023
60+
]
61+
]
62+
},
63+
"title": "Demo Title"
64+
}
65+
]
66+
""";
67+
68+
JsonNode root = mapper.readTree(response);
69+
JsonNode expectedJson = mapper.readTree(expected);
70+
71+
assertEquals(root, expectedJson, "CSL JSON must match the expected structure");
72+
}
73+
74+
@Test
75+
void getJsonRepresentation() throws Exception {
76+
String response = target("/libraries/" + TestBibFile.GENERAL_SERVER_TEST.id + "/entries/Author2023test")
77+
.request(MediaType.APPLICATION_JSON)
78+
.get(String.class);
79+
String expected = """
80+
[
81+
{
82+
"sharingMetadata": {
83+
"sharedID": -1,
84+
"version": 1
85+
},
86+
"userComments": "",
87+
"citationKey": "Author2023test",
88+
"bibtex": "@Misc{Author2023test,\\n author \\u003d {Demo Author},\\n title \\u003d {Demo Title},\\n year \\u003d {2023},\\n}\\n"
89+
}
90+
]
91+
""";
92+
93+
JsonNode root = mapper.readTree(response);
94+
95+
JsonNode expectedJson = mapper.readTree(expected);
96+
97+
assertEquals(root, expectedJson, "JSON must match the expected structure");
98+
}
99+
}

jabsrv/src/test/rest-api.http

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -94,3 +94,6 @@ Accept: application/x-bibtex-library-csl+json
9494
### GET not available library
9595

9696
GET http://localhost:23119/libraries/notfound
97+
98+
###
99+
GET http://localhost:23119/libraries/demo/entries/Corti_2009

0 commit comments

Comments
 (0)