Please help the beginner.
When sending a request to the requestBody json I get:
key: "{
"grant_type" : "client_credentials",
"client_id" : "OC_CLIENT_ID",
"client_secret" : "OC_CLIENT_SECRET"
}"
value: ""
and it is required that the requestBody looks like this
{
key:"grant_type"
value: "client_credentials"
}
{
key:"client_id"
value: "OC_CLIENT_ID"
}
{
key:"client_secret"
value: "OC_CLIENT_SECRET"
}
The server sent for some reason not a set of parameters, but simply stuck json into the name of the first parameter.
The code is:
#Path("/")
public interface OAuth2RequestService {
#POST
AccessTokenRecord create(#HeaderParam(value = "Content-type") String type,
#FormParam(value = "grant_type") String grantType,
#FormParam(value = "client_id") String clientId,
#FormParam(value = "client_secret") String clientSecret);
}
#Override
#TransactionAttribute(TransactionAttributeType.NOT_SUPPORTED)
public OAuth2Interceptor getAccessTokenInterceptor(Account account,
Boolean isGeneralEpaService) {
if (openAmIsEnabled(account)) {
final FeignOptions<OAuth2RequestService> options =
new FeignOptions<>(getAccessTokenUrl(account, isGeneralEpaService));
final AccessTokenRecord accessTokenRecord = workerRestService(options)
.create(HEADER_TYPE, CLIENT_CREDENTIALS, getClientId(isGeneralEpaService),
getClientSecret(isGeneralEpaService));
logger.infof("OAuth2 access token retrieval succeeded.");
return new OAuth2Interceptor(accessTokenRecord);
}
final AccessTokenRecord accessTokenRecord = new AccessTokenRecord();
accessTokenRecord.setAccessToken(getOsDefaultAccessToken(account));
accessTokenRecord.setTokenType(TOKEN_TYPE);
return new OAuth2Interceptor(accessTokenRecord);
}
private OAuth2RequestService workerRestService(
final FeignOptions<OAuth2RequestService> options) {
final Request.Options requestOptions =
new Request.Options(options.getConnectionTimeOut(), options.getReadTimeOut());
return Feign.builder().options(requestOptions).client(new OkHttpClient())
.contract(new JAXRSContract()).encoder(new JacksonEncoder())
.decoder(new JacksonDecoder()).decode404()
.target(OAuth2RequestService.class, options.getHostUrl());
}
I have tried several options with #QueueParam #FormParam
Related
We have a put api that will update an assignment based on its id. As we should be cleaning up the data after a test, our assignment id would change after the original one is deleted, so we're trying to dynamically inject that into the body for the request from the provider side. However, we seem to be perhaps missing something here as it's not updating correctly and the request is still being triggered with the id set as the example.
This is the provider class:
#Slf4j
#Provider("Assignments API")
#Consumer("LTI-AGS-Tool")
//#PactBroker(url = BROKER_PACT_URL, authentication = #PactBrokerAuth(token = "${pactbroker.auth.token}"))
#VerificationReports(value = {"console", "markdown"}, reportDir = "target/pacts")
class PactProviderLTIAGSIT {
private HashMap<String, String> headers = new HashMap<>();
private String updateAssignmentId;
private final String SERVICE_TOKEN = "myToken";
#BeforeEach
void createTeacherAssignment() {
String assignmentBody = createBodyStringForStudentAssignmentSetup();
assignmentBody = assignmentBody.replace("CPWAG", "OTHER_TEXT_RESOURCE");
headers.put("Content-Type", "application/json");
headers.put("Authorization", "myToken");
RequestSpecification rq = Util.getRequestSpecification().baseUri(baseAssignmentUrl).headers(headers);
Response response = rq.body(assignmentBody).post();
assertEquals(201, response.getStatusCode());
updateAssignmentId = response.jsonPath().get("assignments[0].refId");
log.info("assignment id is " + updateAssignmentId);
}
#TestTemplate
#ExtendWith(PactVerificationInvocationContextProvider.class)
void pactTestTemplate(PactVerificationContext context, HttpRequest request) {
request.addHeader("Authorization", SERVICE_TOKEN);
logCurlFromPact(context, request);
context.verifyInteraction();
}
#BeforeEach
void before(PactVerificationContext context) {
context.setTarget(new HttpsTestTarget(BASE_PACT_TEACHER_ASSIGNMENTS_URL, 443, ""));
}
#State("Scoring info is passed between ags-tool and assignmentapi")
Map<String, Object> getScoringInfo() {
Map<String, Object> map = new HashMap<>();
map.put("assignmentId", updateAssignmentId);
return map;
}
}
And here the consumer contract:
#ExtendWith(PactConsumerTestExt.class)
class PactConsumerSendScoreIT {
private final Map<String, String> headers = new HashMap<>();
private final String path = "/v5/assignmentStatus/update";
#Pact(provider = PACT_PROVIDER, consumer = PACT_CONSUMER)
public RequestResponsePact scoreConsumerPact(PactDslWithProvider builder) {
headers.put("Content-Type", "application/json");
//Body given and returned
DslPart body = new PactDslJsonBody()
.valueFromProviderState("assignmentId", "assignmentId", "c1ef3bbf-55a2-4638-8f93-22b2916fe085")
.stringType("timestamp", DateTime.now().plusHours(3).toString())
.decimalType("scoreGiven", 75.00)
.decimalType("scoreMaximum", 100.00)
.stringType("comment", "Good work!")
.stringType("status", "IN_PROGRESS")
.stringType("userId", "c2ef3bbf-55a2-4638-8f93-22b2916fe085")
.close();
return builder
.given("Scoring info is passed between ags-tool and assignmentapi")
.uponReceiving("Scoring info is passed between ags-tool and assignmentapi")
.path(path)
.method("POST")
.body(body)
.headers(headers)
.willRespondWith()
.status(201)
.body(body)
.toPact();
}
#Test
#PactTestFor(pactMethod = "scoreConsumerPact", providerName = PACT_PROVIDER, port = "8080", pactVersion = PactSpecVersion.V3)
void runTest(MockServer mockServer) {
String updateAssignmentId = "c2ef3bbf-55a2-4638-8f93-22b2916fe085";
HashMap<String, Object> map = new HashMap<>();
map.put("timestamp", DateTime.now().plusHours(3).toString());
map.put("scoreGiven", 75.00);
map.put("scoreMaximum", 100.00);
map.put("comment", "Good work!");
map.put("status", "IN_PROGRESS");
map.put("userId", "c2ef3bbf-55a2-4638-8f93-22b2916fe085");
map.put("assignmentId", updateAssignmentId);
//Mock url
RequestSpecification rq = Util.getRequestSpecification().baseUri(mockServer.getUrl()).headers(headers);
Response response = rq.body(map)
.post(path);
assertEquals(201, response.getStatusCode());
}
}
Thank you.
We figured out that the expression needed is to include the ${} (at least if it's a string).
Once we updated it to valueFromProviderState("assignmentId", "${assignmentId}", "c1ef3bbf-55a2-4638-8f93-22b2916fe085") it seemed to work.
I have an entity class like below:
public class InputData {
byte[] nameBytes;
InputType inputType;
InputType outputType;
String inputName;
Description desc;
}
Here is my rest controller:
#PostMapping(path = "/submitData", consumes = "application/json")
public HttpStatus callDataService(#RequestBody Map<String, String> json) {
Gson gson = new GsonBuilder().create();
InputData inputData = gson.fromJson(json.get("inputData"), InputData.class);
Report report = dataService.getReport(inputData);
//return HttpStatus.OK;
}
I have two questions:
How can I send the report as well as Http Status back as a response?
How to send the data to controller?
I have created the following test case:
#Test
public void testController() throws JSONException {
Gson gson = new Gson();
Description desc = new Description();
desc.setMinimumValidSize(512);
File file = new File("src/test/resources/sampleDocuments/test_1.pdf");
byte[] byteArray = { 'P', 'A', 'N', 'K', 'A', 'J' };
JSONObject inputSample = new JSONObject();
inputSample.put("nameBytes", byteArray);
inputSample.put("inputType", ImageType.PDF);
inputSample.put("outputType", ImageType.TIFF);
inputSample.put("inputName", "ABCDEF");
inputSample.put("desc", desc);
String result = invokeRest(fileInputSample.toString(),"/submitData", HttpMethod.POST);
assertEquals("200", result);
}
private String invokeRest(String basicParams, String inputImageType, String
outputImageType, String options, String url, HttpMethod httpMethod) {
String testUrl = "http://localhost:" + port + url;
Map<String, Object> body = new HashMap<>();
body.put("fileInput", basicParams);
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.APPLICATION_JSON);
HttpEntity<String> entity = new HttpEntity(body, headers);
String result = "";
ResponseEntity<String> response = restTemplate.exchange(testUrl, httpMethod, entity, String.class);
if (response.getStatusCode() == HttpStatus.OK) {
result = response.getBody();
} else {
result = response.getStatusCode().toString();
}
return result;
}
When I run this the test case failed and I was able to pin point the issue :
Expected BEGIN_OBJECT but was STRING at line 1 column 13 path $.desc
So I am guessing I am not sending this values in right way
For Description POJO is below:
public class Description {
private static final int DPI = 300;
private Ctype c = CType.NONE;
private ColorType color = DEFAULT_COLOR;
private int dpi = DPI;
}
public enum CType {
NONE, GROUPA,GROUPB,GROUPB_B,GROUPD
}
public enum ColorType {
RGB, GREY;
}
Here is the values that is being send:
{"desc":"org.restservice.Description#1213ffbc”,
"outputType":"TIFF","inputType":"PDF","nameBytes":"src/test/resources/sampleDocuments/test_16.pdf","inputName":"98111"}
How can I send that as Object if I am sending a Map of <String, String> in body? Is there any other way to send that object to controller?
To return the status and also the object you can try to do it like this:
#PostMapping(path = "/submitData", consumes = "application/json")
public ResponseEntity<Report> callDataService(#RequestBody Map<String, String> json) {
Gson gson = new GsonBuilder().create();
InputData inputData = gson.fromJson(json.get("inputData"), InputData.class);
Report report = dataService.getReport(inputData);
return ResponseEntity.ok(report);
}
I'm trying to test getting parameters for processing a request using the Post method
#RestController
#RequestMapping("api")
public class InnerRestController {
…
#PostMapping("createList")
public ItemListId createList(#RequestParam String strListId,
#RequestParam String strDate) {
…
return null;
}
}
test method
variant 1
#RunWith(SpringRunner.class)
#SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
class InnerRestControllerTest {
#LocalServerPort
private int port;
#Autowired
private TestRestTemplate restTemplate;
#Test
void innerCreatePublishList() {
String url = "http://localhost:" + this.port;
String uri = "/api/createList";
String listStr = "kl";
String strDate = "10:21";
URI uriToEndpoint = UriComponentsBuilder
.fromHttpUrl(url)
.path(uri)
.queryParam("strListId", listStr)
.queryParam("strDate ", strDate)
.build()
.encode()
.toUri();
ResponseEntity< ItemListId > listIdResponseEntity =
restTemplate.postForEntity(uri, uriToEndpoint, ItemListId.class);
}
}
variant 2
#Test
void createList() {
String uri = "/api/createList";
String listStr = "kl";
String strDate = "10:21";
UriComponentsBuilder builder = UriComponentsBuilder.fromHttpUrl(uri)
.queryParam("strListId", listStr)
.queryParam("strDate ", strDate);
Map<String, String> map = new HashMap<>();
map.put("strListId", listStr);//request parameters
map.put("strDate", strDate);
ResponseEntity< ItemListId > listIdResponseEntity =
restTemplate.postForEntity(uri, map, ItemListId.class);
}
Update_1
In my project exceptions is handled thus:
dto
public final class ErrorResponseDto {
private String errorMsg;
private int status;
#JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd hh:mm:ss")
LocalDateTime timestamp;
...
handler
#RestControllerAdvice
public class ExceptionAdviceHandler {
#ExceptionHandler(value = PublishListException.class)
public ResponseEntity<ErrorResponseDto> handleGenericPublishListDublicateException(PublishListException e) {
ErrorResponseDto error = new ErrorResponseDto(e.getMessage());
error.setTimestamp(LocalDateTime.now());
error.setStatus((HttpStatus.CONFLICT.value()));
return new ResponseEntity<>(error, HttpStatus.CONFLICT);
}
}
In methods, where necessary, I throw a specific exception...
.w.s.m.s.DefaultHandlerExceptionResolver : Resolved
[org.springframework.web.bind.MissingServletRequestParameterException:
Required String parameter 'strListId' is not present]
Who knows what the error is. Please explain what you need to add here and why ?
Let's take a look on declarations of postEntity:
postForEntity(URI url, Object request, Class<T> responseType)
...
postForEntity(String url, Object request, Class<T> responseType, Object... uriVariables)
As you can see, first argument is either URI or String with uriVariables, but second argument is always request entity.
In you first variant you put uri String as URI and then pass uriToEndpoint as request entity, pretending that it is request object. Correct solution will be:
ResponseEntity<ItemListId> listIdResponseEntity =
restTemplate.postForEntity(uriToEndpoint, null, ItemListId.class);
Addressing your comments.
If server responded with HTTP 409, RestTemplate will throw exception with content of your ErrorResponseDto. You can catch RestClientResponseException and deserialize server response stored in exception. Something like this:
try {
ResponseEntity<ItemListId> listIdResponseEntity =
restTemplate.postForEntity(uriToEndpoint, null,
ItemListId.class);
...
} catch(RestClientResponseException e) {
byte[] errorResponseDtoByteArray = e.getResponseBodyAsByteArray();
// Deserialize byte[] array using Jackson
}
I am trying to make get call like this:
#GET(AppConstants.BASE_URL + "{category_type}/")
Call<JsonObject> callCustomFilterApI(#Path("category_type") String type,
#QueryMap(encoded = true) Map<String,String> fields ,
#Query("page") String pageNo);
But #QueryMap can have "&" in the data, so retrofit encoding it to %26.
Is there anyway "&" do not change to "%26".
Solution I tried:
Solution mentioned here
setting encoded=true/false
And also this one.
#DebDeep Asked:
I am passing data in QueryMap as:
private void callCustomFilterSearchPagesApi(String type, ArrayList<FilterListWithHeaderTitle> customFiltersList, int pageNumber, final ApiInteractor listener) {
Map<String, String> queryMap = new HashMap<>();
for (FilterListWithHeaderTitle item: customFiltersList) {
String pairValue;
if (queryMap.containsKey(item.getHeaderTitle())){
// Add the duplicate key and new value onto the previous value
// so (key, value) will now look like (key, value&key=value2)
// which is a hack to work with Retrofit's QueryMap
String oldValue=queryMap.get(item.getHeaderTitle());
String newValue="filters[" + item.getHeaderTitle() + "][]"
+oldValue+ "&"+"filters[" + item.getHeaderTitle() + "][]"+item.getFilterItem();
pairValue=newValue;
}else {
// adding first time
pairValue= item.getFilterItem();
}
try {
//pairValue= URLEncoder.encode(pairValue, "utf-8");
// LoggerUtils.logE(TAG,pairValue);
//queryMap.put(item.getHeaderTitle(), Html.fromHtml(pairValue).toString());
queryMap.put(item.getHeaderTitle(), pairValue);
}catch (Exception u){
LoggerUtils.crashlyticsLog(TAG,u.getMessage());
}
}
Call<JsonObject> call = TagTasteApplicationInitializer.mRetroClient.callCustomFilterApI(type, queryMap, "1");
requestCall(call, listener);
}
Use Interceptor and convert %26 to &:
class RequestInterceptor implements Interceptor {
#Override
Response intercept(Interceptor.Chain chain) throws IOException {
Request request = chain.request();
String stringurl = request.url().toString();
stringurl = stringurl.replace("%26", "&");
Request newRequest = new Request.Builder()
.url(stringurl)
.build();
return chain.proceed(newRequest);
}
}
Set this to your OkHttp builder:
OkHttpClient client = new OkHttpClient.Builder();
client.addInterceptor(new RequestInterceptor());
I want deserialize a Object but json data is a string and the element of my object is not a string but a enum (of string).
My Object (want deserialize):
public enum ResponseStatus {
SUCCESS, ERROR, WARNING, NO_ACCESS
}
public class SessionResponse extends OperationResponse {
#ApiModelProperty(required = true, value = "")
private SessionItem item;
public SessionResponse() {
super();
}
public SessionItem getItem() {
return item;
}
public void setItem(SessionItem item) {
this.item = item;
}
}
public class OperationResponse {
#ApiModelProperty(required = true)
private ResponseStatus operationStatus;
private String operationMessage;
public ResponseStatus getOperationStatus() {
return operationStatus;
}
public void setOperationStatus(ResponseStatus operationStatus) {
this.operationStatus = operationStatus;
}
public String getOperationMessage() {
return operationMessage;
}
public void setOperationMessage(String operationMessage) {
this.operationMessage = operationMessage;
}
}
I use this web client:
MultiValueMap<String, String> headers = new LinkedMultiValueMap<>();
headers.add("Content-Type", "application/json");
HttpEntity<Login> request = new HttpEntity<>(new Login("demo", "demo"), headers);
ResponseEntity<String> response = new TestRestTemplate().postForEntity("http://localhost:" + port + "/session", request, String.class);
System.out.println(response.getBody());
console:
{
"operationStatus" : "SUCCESS",
"operationMessage" : "Login Success",
"item" : {
"token" : "eyJhbGciOiJIUzI",
"userId" : "demo",
"firstName" : "Zinédine",
"lastName" : "Zidane",
"email" : "zzidane#mail.com",
"roles" : [ ]
}
}
But if I use
MultiValueMap<String, String> headers = new LinkedMultiValueMap<>();
headers.add("Content-Type", "application/json");
HttpEntity<Login> request = new HttpEntity<>(new Login("demo", "demo"), headers);
ResponseEntity<SessionResponse> response = new TestRestTemplate().postForEntity("http://localhost:" + port + "/session", request, SessionResponse.class);
System.out.println(response.getBody());
trace:
org.springframework.web.client.RestClientException: Could not extract response: no suitable HttpMessageConverter found for response type [class com.xxx.xxx.model.session.SessionResponse]
EDIT 1
if I add gson deserializer just after, is it OK
Gson gson = new Gson();
SessionResponse s = gson.fromJson(response.getBody(), SessionResponse.class);
System.out.println(s.getItem().getLastName());
is it possoble to use gson directly in TestRestTemplate().postForEntity ?