Skip to content

Commit

Permalink
Merge pull request #3011 from HenrikJannsen/refactor-report-api
Browse files Browse the repository at this point in the history
Refactor report api
  • Loading branch information
HenrikJannsen authored Nov 17, 2024
2 parents f9ce5fa + acbdea3 commit c681660
Show file tree
Hide file tree
Showing 12 changed files with 83 additions and 97 deletions.
4 changes: 1 addition & 3 deletions apps/rest-api-app/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -39,9 +39,7 @@ dependencies {
implementation("wallets:wallet")

implementation(libs.typesafe.config)
implementation(libs.bundles.glassfish.jersey)
implementation(libs.bundles.jackson)
implementation(libs.swagger.jaxrs2.jakarta)
implementation(libs.bundles.rest.api.libs)
}

tasks {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@ protected void configure() {
bind(new ReportApi(applicationService.getNetworkService(), applicationService.getBondedRolesService())).to(ReportApi.class);
}
});

}

@Override
Expand Down
70 changes: 25 additions & 45 deletions apps/rest-api-app/src/main/java/bisq/rest_api/report/ReportApi.java
Original file line number Diff line number Diff line change
Expand Up @@ -22,9 +22,11 @@
import bisq.bonded_roles.bonded_role.BondedRole;
import bisq.common.network.Address;
import bisq.common.network.TransportType;
import bisq.common.rest_api.error.RestApiException;
import bisq.common.util.CollectionUtil;
import bisq.common.util.CompletableFutureUtils;
import bisq.network.NetworkService;
import bisq.network.p2p.services.reporting.Report;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.media.Content;
Expand All @@ -42,7 +44,6 @@
import java.util.Map;
import java.util.Optional;
import java.util.Set;
import java.util.concurrent.CompletableFuture;
import java.util.stream.Collectors;

@Slf4j
Expand All @@ -62,12 +63,11 @@ public ReportApi(NetworkService networkService, BondedRolesService bondedRolesSe
@ApiResponse(responseCode = "200", description = "the list of seed and oracle node addresses",
content = {
@Content(
mediaType = MediaType.APPLICATION_JSON,
schema = @Schema(implementation = ReportDto.class)
mediaType = MediaType.APPLICATION_JSON
)}
)
@GET
@Path("get-address-list")
@Path("address-list")
public List<String> getAddressList() {
try {
Set<Address> bannedAddresses = bondedRolesService.getAuthorizedBondedRolesService().getBondedRoles().stream()
Expand All @@ -92,27 +92,31 @@ public List<String> getAddressList() {
addresslist.add("s2yxxqvyofzud32mxliya3dihj5rdlowagkblqqtntxhi7cbdaufqkid.onion:54467");
return addresslist;
} catch (Exception e) {
throw new RuntimeException("Failed to get the node address list");
throw new RestApiException(e);
}
}


@Operation(description = "Get report for given address")
@ApiResponse(responseCode = "200", description = "the report for the given address",
content = {
@Content(
mediaType = MediaType.APPLICATION_JSON,
schema = @Schema(implementation = ReportDto.class)
schema = @Schema(implementation = Report.class)
)}
)
@GET
@Path("get-report/{address}")
public ReportDto getReport(
@Path("{address}")
public Report getReport(
@Parameter(description = "address from which we request the report")
@PathParam("address") String address) {
try {
return fetchReportForAddress(address).join();
return networkService.requestReport(Address.fromFullAddress(address)).get();
} catch (InterruptedException e) {
Thread.currentThread().interrupt(); // Restore interrupt status
throw new RestApiException(e);
} catch (Exception e) {
throw new RuntimeException("Failed to get report for address: " + address);
throw new RestApiException(e);
}
}

Expand All @@ -121,48 +125,24 @@ public ReportDto getReport(
content = {
@Content(
mediaType = MediaType.APPLICATION_JSON,
schema = @Schema(implementation = ReportDto.class)
schema = @Schema(implementation = Report.class)
)}
)
@GET
@Path("get-reports/{addresses}")
public List<ReportDto> getReports(
@Path("reports/{addresses}")
public List<Report> getReports(
@Parameter(description = "comma separated addresses from which we request the report")
@PathParam("addresses") String addresses) {

List<String> addressList;
try {
addressList = CollectionUtil.streamFromCsv(addresses).toList();
} catch (Exception e) {
throw new RuntimeException("Failed to parse addresses from CSV input: " + addresses);
}

List<CompletableFuture<ReportDto>> futures = addressList.stream()
.map(this::fetchReportForAddress)
.toList();

CompletableFuture<List<ReportDto>> allFutures = CompletableFutureUtils.allOf(futures);

return allFutures.join();
}

private CompletableFuture<ReportDto> fetchReportForAddress(String addressString) {
try {
Address address = Address.fromFullAddress(addressString);
return networkService.requestReport(address)
.thenApply(report -> {
log.info("Report successfully created for address: {}", address);
return ReportDto.from(report);
})
.exceptionally(e -> {
log.error("Failed to get report for address: {}. Nested: {}", address, e.getMessage());
return ReportDto.fromError(e.getMessage());
});
List<String> addressList = CollectionUtil.streamFromCsv(addresses).toList();
return CompletableFutureUtils.allOf(addressList.stream()
.map(address -> networkService.requestReport(Address.fromFullAddress(address))))
.get();
} catch (InterruptedException e) {
Thread.currentThread().interrupt(); // Restore interrupt status
throw new RestApiException(e);
} catch (Exception e) {
log.error("Error creating report for address: {}. Nested: {}", addressString, e.getMessage());
return CompletableFuture.completedFuture(
ReportDto.fromError(e.getMessage())
);
throw new RestApiException(e);
}
}
}
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
// js/constants.js
App.Constants = {
API_URL_GET_REPORT: 'http://localhost:8082/api/v1/report/get-report',
API_URL_GET_ADDRESS_LIST: 'http://localhost:8082/api/v1/report/get-address-list',
API_URL_GET_REPORT: 'http://localhost:8082/api/v1/report',
API_URL_GET_ADDRESS_LIST: 'http://localhost:8082/api/v1/report/address-list',
STATUS_ERROR: "Failed to fetch data",
STATUS_ENTER_HOSTS: "Please enter a Host:port list in the settings to start fetching data.",
PLACEHOLDER_HOST_LIST: "Host:port list, separated by commas or new lines.\n# Comments and empty lines are alowed.",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,10 @@ App.Views.ReportView = class {

const statusCircle = document.createElement('span');
statusCircle.classList.add('status-circle');
statusCircle.classList.add(data.successful ? 'status-ok' : 'status-error');

//todo
//statusCircle.classList.add(data.successful ? 'status-ok' : 'status-error');
statusCircle.classList.add('status-ok');
header.appendChild(statusCircle);

const hostText = document.createElement('span');
Expand All @@ -39,15 +42,16 @@ App.Views.ReportView = class {

nodeBlock.appendChild(header);

if (data.successful) {
const mainTable = this.#createTable(data.report, "Report", 0);
//todo
//if (data.successful) {
const mainTable = this.#createTable(data, "Report", 0);
nodeBlock.appendChild(mainTable);
} else {
/* } else {
const errorDiv = document.createElement('div');
errorDiv.classList.add('error');
errorDiv.textContent = data.errorMessage || App.Constants.STATUS_ERROR;
nodeBlock.appendChild(errorDiv);
}
}*/
}

toggleDetailButton(button, expand) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,26 +14,56 @@
* You should have received a copy of the GNU Affero General Public License
* along with Bisq. If not, see <http://www.gnu.org/licenses/>.
*/

package bisq.common.rest_api.error;

import com.fasterxml.jackson.core.JsonProcessingException;
import jakarta.ws.rs.core.Response;
import jakarta.ws.rs.ext.ExceptionMapper;
import jakarta.ws.rs.ext.Provider;
import lombok.Getter;
import lombok.Setter;
import lombok.extern.slf4j.Slf4j;

import java.util.concurrent.ExecutionException;

@Slf4j
@Provider
@Getter
public class RestApiException extends RuntimeException {

@Getter
@Setter
protected Response.Status httpStatus;

private static Response.Status getStatus(Exception exception) {
if (exception instanceof InterruptedException) {
return Response.Status.REQUEST_TIMEOUT;
} else if (exception instanceof ExecutionException) {
return Response.Status.INTERNAL_SERVER_ERROR;
} else if (exception instanceof JsonProcessingException) {
return Response.Status.BAD_REQUEST;
} else {
return Response.Status.INTERNAL_SERVER_ERROR;
}
}


private static String getMessage(Exception exception) {
if (exception instanceof InterruptedException) {
return "The request was interrupted or timed out. ";
} else if (exception instanceof ExecutionException) {
return "An error occurred while processing the request. ";
} else if (exception instanceof JsonProcessingException) {
return "Invalid input: Unable to process JSON. ";
} else {
return "An error occurred. ";
}
}

public RestApiException() {
}

public RestApiException(Exception exception) {
this(getStatus(exception), getMessage(exception));
}

public RestApiException(Response.Status httpStatus, String message) {
super(message);
this.httpStatus = httpStatus;
Expand Down
2 changes: 2 additions & 0 deletions gradle/libs.versions.toml
Original file line number Diff line number Diff line change
Expand Up @@ -169,6 +169,8 @@ i2p = ['i2p-core', 'i2p-router', 'i2p-streaming']
i2p-v2 = ['i2p-core-v2', 'i2p-streaming-v2']
jackson = ['jackson-core', 'jackson-annotations', 'jackson-databind']
springfox-libs = ['springfox-boot-starter', 'springfox-swagger2', 'springfox-swagger-ui']
rest-api-libs = ['swagger-jaxrs2-jakarta', 'glassfish-jersey-jdk-http', 'glassfish-jersey-json-jackson', 'glassfish-jersey-inject-hk2',
'glassfish-jaxb-runtime', 'jackson-core', 'jackson-annotations', 'jackson-databind']

# Referenced in subproject's build.gradle > plugin block as alias: `alias(libs.plugins.protobuf)`
# Note: plugin version constraints are not supported by the java-platform plugin, so cannot be enforced there. However,
Expand Down
2 changes: 1 addition & 1 deletion network/network/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -18,10 +18,10 @@ dependencies {
implementation(libs.bouncycastle)
implementation(libs.failsafe)
implementation(libs.typesafe.config)
implementation(libs.bundles.jackson)

implementation(libs.apache.httpcomponents.httpclient)
implementation(libs.chimp.jsocks)
implementation(libs.bundles.rest.api.libs)

integrationTestImplementation(libs.mockito)
}
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
package bisq.network.p2p.services.reporting;

import bisq.common.proto.NetworkProto;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.EqualsAndHashCode;
import lombok.Getter;
import lombok.ToString;
Expand All @@ -29,6 +30,7 @@
@Getter
@EqualsAndHashCode
@ToString
@Schema(name = "Report")
public final class Report implements NetworkProto {
private final TreeMap<String, Integer> authorizedDataPerClassName;
private final TreeMap<String, Integer> authenticatedDataPerClassName;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,31 +15,7 @@
* along with Bisq. If not, see <http://www.gnu.org/licenses/>.
*/

package bisq.rest_api.report;
package bisq.network.p2p.services.reporting;

import bisq.network.p2p.services.reporting.Report;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Getter;

@Getter
@Schema(name = "Report")
public final class ReportDto {
private Report report;
private String errorMessage;

public static ReportDto from(Report report) {
ReportDto dto = new ReportDto();
dto.report = report;
return dto;
}

public static ReportDto fromError(String errorMessage) {
ReportDto dto = new ReportDto();
dto.errorMessage = errorMessage;
return dto;
}

public boolean isSuccessful() {
return errorMessage == null;
}
public class ReportService {
}
4 changes: 1 addition & 3 deletions user/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,5 @@ dependencies {

implementation(libs.google.gson)
implementation(libs.typesafe.config)
implementation(libs.swagger.jaxrs2.jakarta)
implementation(libs.bundles.glassfish.jersey)
implementation(libs.bundles.jackson)
implementation(libs.bundles.rest.api.libs)
}
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,6 @@

import bisq.common.rest_api.error.RestApiException;
import bisq.security.DigestUtil;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.media.Content;
Expand All @@ -35,7 +34,6 @@
import java.security.KeyPair;
import java.util.Collections;
import java.util.Map;
import java.util.concurrent.ExecutionException;

@Path("/user-identity")
@Produces(MediaType.APPLICATION_JSON)
Expand Down Expand Up @@ -133,14 +131,11 @@ public Map<String, String> createUserIdentityAndPublishUserProfile2(CreateUserId
request.terms,
request.statement).get();
return Collections.singletonMap("userProfileId", userIdentity.getId());
} catch (JsonProcessingException e) {
throw new RestApiException(Response.Status.BAD_REQUEST,
"Invalid input: Unable to process JSON");
} catch (InterruptedException e) {
Thread.currentThread().interrupt(); // Restore interrupt status
throw new RestApiException(Response.Status.REQUEST_TIMEOUT, "The request was interrupted or timed out");
} catch (ExecutionException e) {
throw new RestApiException(Response.Status.INTERNAL_SERVER_ERROR, "An error occurred while processing the request");
throw new RestApiException(e);
} catch (Exception e) {
throw new RestApiException(e);
}
}

Expand Down

0 comments on commit c681660

Please sign in to comment.