Skip to content

Commit

Permalink
feat: x2x3endpoint can now return formats that can be interpreted by …
Browse files Browse the repository at this point in the history
…humans

Co-authored-by: Jannik Volkland <[email protected]>
  • Loading branch information
towi and volkland committed Nov 27, 2024
1 parent d4ba484 commit d149672
Show file tree
Hide file tree
Showing 2 changed files with 144 additions and 16 deletions.
Original file line number Diff line number Diff line change
@@ -1,25 +1,38 @@
package com.sipgate.li.simulator.controller;

import com.fasterxml.jackson.databind.ObjectMapper;
import com.sipgate.li.lib.x2x3.protocol.PayloadDirection;
import com.sipgate.li.lib.x2x3.protocol.PayloadFormat;
import com.sipgate.li.lib.x2x3.protocol.PduObject;
import com.sipgate.li.lib.x2x3.protocol.PduType;
import com.sipgate.li.simulator.rtp.RtpMediaExtractor;
import com.sipgate.li.simulator.x2x3.X2X3Memory;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.Parameters;
import io.swagger.v3.oas.annotations.media.Schema;
import io.swagger.v3.oas.annotations.responses.ApiResponse;
import io.swagger.v3.oas.annotations.responses.ApiResponses;
import java.io.ByteArrayOutputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.util.*;
import java.util.Base64;
import java.util.Comparator;
import java.util.List;
import java.util.Optional;
import java.util.UUID;
import java.util.function.Predicate;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;

@RestController
@RequestMapping("/x2x3")
Expand Down Expand Up @@ -66,6 +79,15 @@ public ResponseEntity<String> getLast() throws IOException {
}

@Operation(summary = "Get all items from the X2X3 Storage")
@Parameters(
value = {
@Parameter(
name = "format",
schema = @Schema(allowableValues = { "java", "json", "hex", "base64", "" }, defaultValue = "base64"),
description = "The format of each element in the response"
),
}
)
@ApiResponses(
value = {
@ApiResponse(
Expand All @@ -75,12 +97,21 @@ public ResponseEntity<String> getLast() throws IOException {
}
)
@GetMapping("/all")
public ResponseEntity<List<String>> getAll() {
final var respList = getStorageAsList(pdu -> true);
public ResponseEntity<List<String>> getAll(@RequestParam(required = false) final String format) {
final var respList = getStorageAsList(pdu -> true, format);
return ResponseEntity.ok(respList);
}

@Operation(summary = "Get all X3 items from the X2X3 Storage")
@Parameters(
value = {
@Parameter(
name = "format",
schema = @Schema(allowableValues = { "java", "json", "hex", "base64", "" }, defaultValue = "base64"),
description = "The format of each element in the response"
),
}
)
@ApiResponses(
value = {
@ApiResponse(
Expand All @@ -90,8 +121,8 @@ public ResponseEntity<List<String>> getAll() {
}
)
@GetMapping("/all/x3")
public ResponseEntity<List<String>> getAllX3() {
final var respList = getStorageAsList(pdu -> PduType.X3_PDU.equals(pdu.pduType()));
public ResponseEntity<List<String>> getAllX3(@RequestParam(required = false) final String format) {
final var respList = getStorageAsList(pdu -> PduType.X3_PDU.equals(pdu.pduType()), format);
return ResponseEntity.ok(respList);
}

Expand Down Expand Up @@ -148,23 +179,54 @@ public Optional<Integer> findSequenceNumber(final PduObject pdu) {

// ================================

private List<String> getStorageAsList(final Predicate<PduObject> filter) {
return x2X3Memory
.getStorage()
.stream()
.filter(filter)
.map(pdu -> {
private List<String> getStorageAsList(final Predicate<PduObject> filter, final String format) {
return x2X3Memory.getStorage().stream().filter(filter).map(pdu -> formatPdu(pdu, format)).toList();
}

static String formatPdu(final PduObject pdu, final String format) {
switch (format) {
case "java" -> {
return pdu.toString();
}
case "json" -> {
final var objectMapper = new ObjectMapper();
try {
return objectMapper.writeValueAsString(pdu);
} catch (final IOException e) {
throw new RuntimeException(e);
}
}
case "hex" -> {
final var pduBytes = new ByteArrayOutputStream();
final var pduStream = new DataOutputStream(pduBytes);
try {
pdu.writeTo(pduStream);
} catch (final IOException e) {
throw new RuntimeException(e);
}
return byteArrayToHex(pduBytes.toByteArray());
}
case null, default -> {
final var pduBytes = new ByteArrayOutputStream();
final var pduStream = new DataOutputStream(pduBytes);

try {
pdu.writeTo(pduStream);
} catch (final IOException e) {
throw new RuntimeException(e);
}

return Base64.getEncoder().encodeToString(pduBytes.toByteArray());
})
.toList();
}
}
}

private static String byteArrayToHex(final byte[] a) {
final var sb = new StringBuilder(a.length * 3);
for (var i = 0; i < a.length; i++) {
sb.append(String.format("%02x", a[i]));
if ((i + 1) % 4 == 0 && i + 1 < a.length) {
sb.append(" ");
}
}
return sb.toString();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
package com.sipgate.li.simulator.controller;

import static org.assertj.core.api.Assertions.assertThat;

import com.sipgate.li.lib.x2x3.protocol.PayloadDirection;
import com.sipgate.li.lib.x2x3.protocol.PayloadFormat;
import com.sipgate.li.lib.x2x3.protocol.PduObject;
import com.sipgate.li.lib.x2x3.protocol.PduType;
import com.sipgate.li.lib.x2x3.protocol.tlv.TLV;
import java.util.UUID;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.NullAndEmptySource;
import org.junit.jupiter.params.provider.ValueSource;

class X2X3ControllerTest {

private final PduObject pdu = new PduObject(
(short) 0,
(short) 5,
PduType.X3_PDU,
PayloadFormat.RTP,
PayloadDirection.SENT_FROM_TARGET,
UUID.fromString("62cd13da-7797-468c-befd-77f05008c996"),
new byte[] { 0, 1, 2, 3, 4, 5, 6, 7 },
new TLV[0],
new byte[] { 2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47, 53 }
);

@ParameterizedTest
@NullAndEmptySource
@ValueSource(strings = { "base64" })
void it_formats_pdu_to_base64(final String format) {
final var actual = X2X3Controller.formatPdu(pdu, format);
assertThat(actual).isEqualTo("AAUAAgAAACgAAAAQAAgAA2LNE9p3l0aMvv138FAIyZYAAQIDBAUGBwIDBQcLDRETFx0fJSkrLzU=");
}

@Test
void it_formats_pdu_to_java() {
final var actual = X2X3Controller.formatPdu(pdu, "java");
final var fixedActual = actual.replaceAll("@\\w+", "@(addr)");
assertThat(fixedActual).isEqualTo(
"PduObject[majorVersion=0, minorVersion=5, pduType=X3_PDU, payloadFormat=RTP, payloadDirection=SENT_FROM_TARGET, xid=62cd13da-7797-468c-befd-77f05008c996, correlationID=[B@(addr), conditionalAttributeFields=[Lcom.sipgate.li.lib.x2x3.protocol.tlv.TLV;@(addr), payload=[B@(addr)]"
);
}

@Test
void it_formats_pdu_to_json() {
final var actual = X2X3Controller.formatPdu(pdu, "json");
assertThat(actual).isEqualTo(
"""
{"majorVersion":0,"minorVersion":5,"pduType":"X3_PDU","payloadFormat":"RTP","payloadDirection":"SENT_FROM_TARGET","xid":"62cd13da-7797-468c-befd-77f05008c996","correlationID":"AAECAwQFBgc=","conditionalAttributeFields":[],"payload":"AgMFBwsNERMXHR8lKSsvNQ=="}
""".trim()
);
}

@Test
void it_formats_pdu_to_hex() {
final var actual = X2X3Controller.formatPdu(pdu, "hex");
assertThat(actual).isEqualTo(
"""
00050002 00000028 00000010 00080003 62cd13da 7797468c befd77f0 5008c996 00010203 04050607 02030507 0b0d1113 171d1f25 292b2f35
""".trim()
);
}
}

0 comments on commit d149672

Please sign in to comment.