I have the following test for an HTTP endpoint:
public static final String DATA_PARAMETER = "data";
public static final String ID_PARAMETER = "id";
public static final String VIDEO_SVC_PATH = "/video";
public static final String VIDEO_DATA_PATH = VIDEO_SVC_PATH + "/{id}/data";
#Multipart
#POST(VIDEO_DATA_PATH)
public VideoStatus setVideoData(#Path(ID_PARAMETER) long id, #Part(DATA_PARAMETER) TypedFile videoData);
#Test
public void testAddVideoData() throws Exception {
Video received = videoSvc.addVideo(video);
VideoStatus status = videoSvc.setVideoData(received.getId(),
new TypedFile(received.getContentType(), testVideoData));
assertEquals(VideoState.READY, status.getState());
Response response = videoSvc.getData(received.getId());
assertEquals(200, response.getStatus());
InputStream videoData = response.getBody().in();
byte[] originalFile = IOUtils.toByteArray(new FileInputStream(testVideoData));
byte[] retrievedFile = IOUtils.toByteArray(videoData);
assertTrue(Arrays.equals(originalFile, retrievedFile));
}
I'm trying to implement the requirements defined by this test with the following endpoint defined in Swing:
#RequestMapping(method = RequestMethod.POST, value = "/video/{id}/data")
public void postVideoData(#PathVariable("id") long videoId,
#RequestParam("data") MultipartFile videoData) throws IOException {
if (videoId <= 0 || videoId > videos.size()) {
throw new ResourceNotFoundException("Invalid id: " + videoId);
}
Video video = videos.get((int)videoId - 1);
InputStream in = videoData.getInputStream();
manager.saveVideoData(video, in);
}
The problem is that I get a "405 Method Not Allowed" error. What am I doing wrong so that my POST method is not being recognized?
The problem is that the client interface expects a VideoStatus object returned from the server. I declared the method on the server side to return void.
I don't know if you already fix your problem, but I got the same error, because I am new with Retrofit too :).
The solution for me, was to put an Annotation to specify the response content type, in my case
#ResponseBody
Another change that I must did, was to change void for a custom status.
Hope this helps or at least gives you a light.
Rgds.
I had the same issue. RetroFit request calls must have either a return type or Callback as last argument.
So in the RetroFitted API:
#POST("/path")
public Void saveWhatever(#Body Whatever whatever);
Than in the controller it must be :
#RequestMapping(value = "/path", method = RequestMethod.POST)
public #ResponseBody Void saveWhatever(#RequestBody Whatever whatever) {
repository.save(whatever);
return null;
}
Related
Note client is RestHightLevelClient,
#Override
public void createAlias(final String aliasName, final String indexName, final boolean writable)
throws IOException {
IndicesAliasesRequest request = new IndicesAliasesRequest();
AliasActions aliasAction = new AliasActions(AliasActions.Type.ADD).index(indexName)
.alias(aliasName);
if (writable) {
aliasAction.writeIndex(true);
}
request.addAliasAction(aliasAction);
AcknowledgedResponse response = client.indices().updateAliases(request, RequestOptions.DEFAULT);
}
I tried writing test case for this :
#Test
void testCreateAlias() throws IOException {
AcknowledgedResponse response = AcknowledgedResponse.of(true);
when(client.indices().updateAliases(Mockito.mock(IndicesAliasesRequest.class), RequestOptions.DEFAULT))
.thenReturn(response);
searchManagerService.createAlias("test", "test_idx", true);
}
ERROR : client.indices() is null,
How to resolve this ?
Mock client.indices() to return a mocked instance of IndicesClient. Then mock that IndicesClient updateAliases method to return response.
var mockedIndicesClient = Mockito.mock(IndicesClient.class);
when(client.indices()).thenReturn(mockedIndicesClient);
when(mockedIndicesClient.updateAliases(Mockito.mock(IndicesAliasesRequest.class), RequestOptions.DEFAULT)).thenReturn(response);
Also I believe you want to use matchers in the last line. any(IndicesAliasesRequest.class) instead of Mockito.mock:
when(mockedIndicesClient.updateAliases(any(IndicesAliasesRequest.class), RequestOptions.DEFAULT)).thenReturn(response);
Wanted to test a post request in POSTMAN.
public enum TimeUnit {
HOURS("hours"),
MINUTE("mins");
private String value;
public static TimeUnit get(String text) {
return Arrays.stream(TimeUnit.values())
.filter(a -> Objects.equals(a.getValue(), text))
.findFirst()
.orElse(null);
}
}
public final class SchoolTimeTable {
private Double value;
private TimeUnit unit;
public SchoolTimeTable (double value, TimeUnit unit) {
this.value = value;
this.unit=unit;
}
}
public class SchoolDto {
private String name;
private String address;
private MultipartFile profileImage;
private MultipartFile[] galleryImages;
private SchoolTimeTable openCloseTime;
}
Spring MVC Controller
#PostMapping(value = "/schoolInfo", produces = { MediaType.APPLICATION_JSON_VALUE })
public ResponseEntity<Object> saveSchoolInfo( #Parameter(required = true, schema = #Schema(implementation = SchoolDto.class)) SchoolDto schoolDto) throws IOException, InterruptedException {
...
}
I want to send SchoolDto (POSTMAN: body->raw->json) in post request to get desired result. But I am not able to create the json which supports SchoolTimeTable (Object) and MultipartFile types. I don't even know whether it is possible with JSON or not.
Note: Same could be achieved using body->form-data with key/value.
Please help.
I think you should not upload files within a application/json request, to do so you should use a multipart/form-data request. Your request may have three parts profileImage, galleryImages and schoolInfo.
Remove profileImage and galleryImages from SchoolDto class
Modify your method signature to support the multipart request
#PostMapping(value = "/schoolInfo", consumes = MediaType.MULTIPART_FORM_DATA_VALUE)
public ResponseEntity<Object> saveSchoolInfo(#RequestPart(value = "profileImage") MultipartFile profileImage, #RequestPart(value = "galleryImages") MultipartFile[] galleryImages, #RequestPart(value = "schoolInfo") SchoolDto schoolInfo) throws IOException, InterruptedException {
...
}
In addtion you can implement a #SpringBootTest unit test using RestDocumentationExtension to check whether your code works and to produce a curl request sample that will help you to understand how to make a request to your endpoint
See sb-mvc-multipart-demo
When I have used multipart/form-data I was using Spring Boot 2.0.x, Java 8 and with the same method = POST
There are points that you need to implement correctly. Maybe this can help you:
#RequestMapping(value = MEUVOUCHER_FILE, method = POST, consumes = "multipart/form-data", produces = APPLICATION_JSON_UTF8_VALUE)
#ResponseStatus(code = CREATED)
public ObjectResponse<MEUVOUCHERFileRetrievalDto> createMEUVOUCHERFile(
#RequestPart("MEUVOUCHERFile") MultipartFile MEUVOUCHERFile,
#ApiParam(name = "metadata", value = "{ \"MEUVOUCHERTypeId\" : 0, \"relatedContentId\" : 0, \"expireDate\" : \"datetime\" }", required = true)
I have a method that sends a rest request to an api with multipart-formdata, this will upload a file to the external api. However, I am not able to finish the unit test method for this.
The first problem I am finding is that the content-type that I am expecting is always different than the one that method create. For some reason when sending the request the mediatype is multipart-formdata but the header is set as that in addition to charset and boundary. The latter, boundary, is always changing its value therefore I can not set the expected value on the unit tests because it will always be different.
Apart from that, how do I also expect that the content of the request is the same content that I initiated the test with? How do I assert that the payload is the same.
Please check the code:
Service class:
#Service
#Slf4j
public class JiraService {
private HttpHeaders createRequestHeaders(JiraClient jiraClient, MediaType contenType) {
HttpHeaders headers = new HttpHeaders();
headers.setContentType(contenType);
headers.setAccept(Collections.singletonList(MediaType.APPLICATION_JSON));
headers.setBasicAuth(jiraClient.getUsername(), jiraClient.getPassword());
return headers;
}
private <EC, RC> ResponseEntity<RC> createRequestAndSend(HttpMethod method, String url, HttpHeaders headers,
EC payload, Class<RC> responseType) {
HttpEntity<EC> requestEntity = new HttpEntity<>(payload, headers);
ResponseEntity<RC> responseEntity = restTemplate.exchange(url, method, requestEntity, responseType);
// TODO deal with response
log.error("Loggin something");
return responseEntity;
}
public void addAttachment(JiraClient jiraClient, JiraIssue jiraIssue, JiraAttachment jiraAttachment)
throws MalformedURLException, IOException {
String url = jiraClient.getHost() + "/rest/api/2/issue/" + jiraIssue.getKey() + "/attachments";
HttpHeaders headers = createRequestHeaders(jiraClient, MediaType.MULTIPART_FORM_DATA); // What to do here?
headers.set("X-Atlassian-Token", "no-check");
FileSystemResource file = jiraAttachment.downloadFileFromWeb();
MultiValueMap<String, Object> payload = new LinkedMultiValueMap<>();
payload.add("file", file);
createRequestAndSend(HttpMethod.POST, url, headers, payload, String.class);
jiraAttachment.deleteFileFromSystem();
}
}
ServiceTest.class
#ActiveProfiles("test")
#RestClientTest(JiraService.class)
public class JiraServiceTest {
#Value("classpath:jira/add_attachment/validJiraAttachmentAddition.json")
private Resource validJiraAttachmentAddition;
#Autowired
private MockRestServiceServer server;
#Autowired
private JiraService jiraService;
#Mock
private JiraAttachment mockJiraAttachment;
private FileSystemResource attachmentFileSystemResource;
#BeforeEach
public void setupTests() throws IOException {
// initialize mocks
}
#Test
public void addAttachment_WithValidData_ShouldAddAttachmentToJiraIssue() throws Exception {
String url = host + "/rest/api/2/issue/" + issueKey + "/attachments";
ResponseActions stub = createServiceStub(HttpMethod.POST, url, MediaType.MULTIPART_FORM_DATA_VALUE);
stub = stub.andExpect(header("X-Atlassian-Token", "no-check"));
stub.andRespond(withSuccess());
// How to assert that the content of the request is the same as the resource?
when(mockJiraAttachment.downloadFileFromWeb()).thenReturn(attachmentFileSystemResource);
jiraService.addAttachment(mockJiraClient, mockJiraIssue, mockJiraAttachment);
}
private ResponseActions createServiceStub(HttpMethod method, String url, String contenType) {
String encodedCredentials = Base64.getEncoder().encodeToString((username + ":" + password).getBytes());
ResponseActions stub = server.expect(ExpectedCount.once(), requestTo(url));
stub = stub.andExpect(method(method));
stub = stub.andExpect(header("Content-Type", contenType)); // How to expect the content type here ?
stub = stub.andExpect(header("Authorization", "Basic " + encodedCredentials));
return stub;
}
}
Use ContentRequestMatchers.contentTypeCompatibleWith(MediaType contentType)
import static org.springframework.test.web.client.match.MockRestRequestMatchers.content;
...
stub.andExpect(content().contentTypeCompatibleWith(MediaType.MULTIPART_FORM_DATA))
I'm trying to test a POST and no matter what I do I get the java.lang.AssertionError: Content type not set
My controller:
#RestController
#RequestMapping("/artistas")
public class ArtistaEndpoint {
private final ArtistaService artistaService;
private final ArtistaMapper artistaMapper;
private final AlbumService albumService;
#CrossOrigin(origins = "http://localhost:8080")
#PostMapping
public ResponseEntity<Void> post(#Valid #RequestBody Artista artista) {
artista = artistaService.save(artista);
ArtistaDto artistaDto = artistaMapper.toDtoCompleto(artista);
URI uri = ServletUriComponentsBuilder.fromCurrentRequest().path("/{id}").buildAndExpand(artistaDto.getId()).toUri();
if(!artista.getAlbuns().isEmpty()) {
Set<Album> albuns = artista.getAlbuns();
for(Album album: albuns) {
album.setArtista(artista);
albumService.save(album);
}
}
return ResponseEntity.created(uri).build();
}
}
and my test:
#Test
public void salvarArtistaSemAlbum() throws Exception {
Artista adicionado = new Artista();
adicionado.setId(1L);
adicionado.setNome("Banda Eva");
adicionado.setDescricao("Viva o carnaval");
when(artistaService.save(Mockito.any(Artista.class))).thenReturn(adicionado);
mockMvc.perform(MockMvcRequestBuilders.post("/artistas")
.contentType(MediaType.APPLICATION_JSON_UTF8_VALUE)
.accept(MediaType.APPLICATION_JSON_UTF8_VALUE)and
.content(TestUtil.asJsonString(adicionado)))
.andExpect(status().isCreated())
.andExpect(content().contentType(MediaType.APPLICATION_JSON_UTF8_VALUE))
.andExpect(jsonPath("$.id", is(1)))
.andExpect(jsonPath("$.nome", is("Banda Eva")))
.andExpect(jsonPath("$.descricao", is("Viva o carnaval")));
verify(artistaService, times(1)).save(Mockito.any(Artista.class));
verifyNoMoreInteractions(artistaService);
assertNull(adicionado.getId());
assertThat(adicionado.getNome(), is("Banda Eva"));
assertThat(adicionado.getDescricao(), is("Viva o carnaval"));
}
And the response of the httpservlet:
MockHttpServletResponse:
Status = 201
Error message = null
Headers = {Location=[http://localhost/artistas/1]}
Content type = null
Body =
Forwarded URL = null
Redirected URL = http://localhost/artistas/1
Cookies = []
I already did a question but the error was another, and I could resolve. I don't understand why it's returning 201 created and no content body. Smells like some annotation that I haven't, but I already review with no sucess.
Srry about my english and thanks for the help.
EDIT-
I thought the problem was solved but I was wrong
You may add the content type directly in your PostMapping annotation :
#PostMapping(produces = MediaType.APPLICATION_JSON_UTF8_VALUE)
add #ResponseBody on top of controller method.
OR
#PostMapping(produces = {MediaType.APPLICATION_JSON_VALUE} )
You can add a #RequestMapping annotation to specify the content type in your RestController to the respective method.
Something like:
#RequestMapping(produces = MediaType.APPLICATION_JSON_UTF8_VALUE, consumes = MediaType.APPLICATION_JSON_UTF8_VALUE, method = RequestMethod.POST).
UPDATED:
This newer annotation is a shortcut to the #RequestMapping with RequestMethod.POST:
#PostMapping(produces = MediaType.APPLICATION_JSON_UTF8_VALUE, consumes=MediaType.APPLICATION_JSON_UTF8_VALUE)
Problem solved, my problem was on the controller 'cause it was returning a void:
My new controller looks like:
public class ArtistaEndpoint {
private final ArtistaService artistaService;
private final ArtistaMapper artistaMapper;
private final AlbumService albumService;
#CrossOrigin(origins = "http://localhost:8080")
#PostMapping
public ResponseEntity<ArtistaDto> post(#Valid #RequestBody Artista artista) {
artista = artistaService.save(artista);
ArtistaDto artistaDto = artistaMapper.toDtoCompleto(artista);
URI uri = ServletUriComponentsBuilder.fromCurrentRequest().path("/{id}").buildAndExpand(artistaDto.getId()).toUri();
if(!artista.getAlbuns().isEmpty()) {
Set<Album> albuns = artista.getAlbuns();
for(Album album: albuns) {
album.setArtista(artista);
albumService.save(album);
}
}
return ResponseEntity.created(artistaDto).body();
}
}
thanks for all
My problem like this:
I have two servlets:
servlet_A: http://localhost:8080/test/servlet_wait_for_response
servlet_B: http://localhost:8080/test/servlet_send_data?data=xxx
1. I use Firefox invoking servlet_A, and the servlet_A does nothing but wait;
2. I use Chrome invoking servlet_B, and send "helloworld" to the server, for example: http://localhost:8080/test/servlet_send_data?data=helloworld
3. servlet_B get the message "helloworld", then send this message to servlet_A;
4. servlet_A get the message "helloworld" from servlet_B, then response this message to Firefox.
I got an answer like below:
static String msg = null;
#RequestMapping(value = "/wait_for_data", method = RequestMethod.GET)
#ResponseBody
public String waitData() throws InterruptedException{
while(msg==null){
TimeUnit.SECONDS.sleep(1);
}
return msg;
}
#RequestMapping(value = "/send_data", method = RequestMethod.GET)
#ResponseBody
public String sendData(String data){
msg = data;
return "OK";
}