blob: 8b44d4355be0759e76a4dd8d8f1ffba621b94b8f [file] [log] [blame]
// ***************************************************************************************************************************
// * Licensed to the Apache Software Foundation (ASF) under one or more contributor license agreements. See the NOTICE file *
// * distributed with this work for additional information regarding copyright ownership. The ASF licenses this file *
// * to you under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance *
// * with the License. You may obtain a copy of the License at *
// * *
// * http://www.apache.org/licenses/LICENSE-2.0 *
// * *
// * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an *
// * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the *
// * specific language governing permissions and limitations under the License. *
// ***************************************************************************************************************************
package org.apache.juneau.rest.annotation;
import static org.apache.juneau.http.HttpMethod.*;
import static org.junit.Assert.*;
import static org.junit.runners.MethodSorters.*;
import java.io.*;
import java.util.*;
import org.apache.juneau.annotation.*;
import org.apache.juneau.collections.*;
import org.apache.juneau.http.annotation.Body;
import org.apache.juneau.http.annotation.HasQuery;
import org.apache.juneau.http.annotation.Query;
import org.apache.juneau.internal.*;
import org.apache.juneau.json.*;
import org.apache.juneau.marshall.*;
import org.apache.juneau.rest.client2.*;
import org.apache.juneau.rest.mock2.*;
import org.apache.juneau.testutils.pojos.*;
import org.apache.juneau.uon.*;
import org.apache.juneau.urlencoding.*;
import org.apache.juneau.urlencoding.annotation.*;
import org.apache.juneau.urlencoding.annotation.UrlEncoding;
import org.junit.*;
@FixMethodOrder(NAME_ASCENDING)
public class Body_Test {
//------------------------------------------------------------------------------------------------------------------
// @Body on parameter
//------------------------------------------------------------------------------------------------------------------
@Rest(serializers=SimpleJsonSerializer.class, parsers=JsonParser.class, defaultAccept="text/json")
public static class A {
@RestMethod(name=PUT, path="/String")
public String a(@Body String b) {
return b;
}
@RestMethod(name=PUT, path="/Integer")
public Integer b(@Body Integer b) {
return b;
}
@RestMethod(name=PUT, path="/int")
public Integer c(@Body int b) {
return b;
}
@RestMethod(name=PUT, path="/Boolean")
public Boolean d(@Body Boolean b) {
return b;
}
@RestMethod(name=PUT, path="/boolean")
public Boolean e(@Body boolean b) {
return b;
}
@RestMethod(name=PUT, path="/float")
public float f(@Body float f) {
return f;
}
@RestMethod(name=PUT, path="/Float")
public Float g(@Body Float f) {
return f;
}
@RestMethod(name=PUT, path="/Map")
public TreeMap<String,Integer> h(@Body TreeMap<String,Integer> m) {
return m;
}
@RestMethod(name=PUT, path="/enum")
public TestEnum i(@Body TestEnum e) {
return e;
}
public static class A11 {
public String f1;
}
@RestMethod(name=PUT, path="/Bean")
public A11 j(@Body A11 b) {
return b;
}
@RestMethod(name=PUT, path="/InputStream")
public String k(@Body InputStream b) throws Exception {
return IOUtils.read(b);
}
@RestMethod(name=PUT, path="/Reader")
public String l(@Body Reader b) throws Exception {
return IOUtils.read(b);
}
@RestMethod(name=PUT, path="/InputStreamTransform")
public A14 m(@Body A14 b) throws Exception {
return b;
}
public static class A14 {
String s;
public A14(InputStream in) throws Exception { this.s = IOUtils.read(in); }
@Override public String toString() { return s; }
}
@RestMethod(name=PUT, path="/ReaderTransform")
public A15 n(@Body A15 b) throws Exception {
return b;
}
public static class A15 {
private String s;
public A15(Reader in) throws Exception { this.s = IOUtils.read(in); }
@Override public String toString() { return s; }
}
@RestMethod(name=PUT, path="/StringTransform")
public A16 o(@Body A16 b) throws Exception { return b; }
public static class A16 {
private String s;
public A16(String s) throws Exception { this.s = s; }
@Override public String toString() { return s; }
}
}
@Test
public void a01_onParameters() throws Exception {
RestClient a = MockRestClient.buildLax(A.class);
a.put("/String", "foo")
.json()
.run()
.assertBody().is("'foo'");
// If no Content-Type specified, should be treated as plain-text.
a.put("/String", "'foo'")
.run()
.assertBody().is("'\\'foo\\''");
// If Content-Type not matched, should be treated as plain-text.
a.put("/String", "'foo'").contentType("")
.run()
.assertBody().is("'\\'foo\\''");
a.put("/String", "'foo'").contentType("text/plain")
.run()
.assertBody().is("'\\'foo\\''");
a.put("/String?body=foo", null)
.run()
.assertBody().is("'foo'");
a.put("/String?body=null", null)
.run()
.assertBody().is("null");
a.put("/String?body=", null)
.run()
.assertBody().is("''");
a.put("/Integer", "123").json()
.run()
.assertBody().is("123");
// Integer takes in a String arg, so it can be parsed without Content-Type.
a.put("/Integer", "123")
.run()
.assertBody().is("123");
a.put("/Integer?body=123", null)
.run()
.assertBody().is("123");
a.put("/Integer?body=-123", null)
.run()
.assertBody().is("-123");
a.put("/Integer?body=null", null)
.run()
.assertBody().is("null");
a.put("/Integer?body=", null)
.run()
.assertBody().is("null");
a.put("/Integer?body=bad&noTrace=true", null)
.run()
.assertCode().is(400);
a.put("/int", "123").json()
.run()
.assertBody().is("123");
a.put("/int", "123")
.run()
.assertBody().is("123"); // Uses part parser.
a.put("/int?body=123", null)
.run()
.assertBody().is("123");
a.put("/int?body=-123", null)
.run()
.assertBody().is("-123");
a.put("/int?body=null", null)
.run()
.assertBody().is("0");
a.put("/int?body=", null)
.run()
.assertBody().is("0");
a.put("/int?body=bad&noTrace=true", null)
.run()
.assertCode().is(400);
a.put("/Boolean", "true").json()
.run()
.assertBody().is("true");
// Boolean takes in a String arg, so it can be parsed without Content-Type.
a.put("/Boolean", "true")
.run()
.assertBody().is("true");
a.put("/Boolean?body=true", null)
.run()
.assertBody().is("true");
a.put("/Boolean?body=false", null)
.run()
.assertBody().is("false");
a.put("/Boolean?body=null", null)
.run()
.assertBody().is("null");
a.put("/Boolean?body=", null)
.run()
.assertBody().is("null");
a.put("/Boolean?body=bad&noTrace=true", null)
.run()
.assertCode().is(400);
a.put("/boolean", "true").json()
.run()
.assertBody().is("true");
a.put("/boolean", "true")
.run()
.assertBody().is("true"); // Uses part parser.
a.put("/boolean?body=true", null)
.run()
.assertBody().is("true");
a.put("/boolean?body=false", null)
.run()
.assertBody().is("false");
a.put("/boolean?body=null", null)
.run()
.assertBody().is("false");
a.put("/boolean?body=", null)
.run()
.assertBody().is("false");
a.put("/boolean?body=bad&noTrace=true", null)
.run()
.assertCode().is(400);
a.put("/float", "1.23").json()
.run()
.assertBody().is("1.23");
a.put("/float", "1.23")
.run()
.assertBody().is("1.23"); // Uses part parser.
a.put("/float?body=1.23", null)
.run()
.assertBody().is("1.23");
a.put("/float?body=-1.23", null)
.run()
.assertBody().is("-1.23");
a.put("/float?body=null", null)
.run()
.assertBody().is("0.0");
a.put("/float?body=", null)
.run()
.assertBody().is("0.0");
a.put("/float?body=bad&noTrace=true", null)
.run()
.assertCode().is(400);
a.put("/Float", "1.23").json()
.run()
.assertBody().is("1.23");
// Float takes in a String arg, so it can be parsed without Content-Type.
a.put("/Float", "1.23")
.run()
.assertBody().is("1.23");
a.put("/Float?body=1.23", null)
.run()
.assertBody().is("1.23");
a.put("/Float?body=-1.23", null)
.run()
.assertBody().is("-1.23");
a.put("/Float?body=null", null)
.run()
.assertBody().is("null");
a.put("/Float?body=", null)
.run()
.assertBody().is("null");
a.put("/Float?body=bad&noTrace=true", null)
.run()
.assertCode().is(400);
a.put("/Map", "{foo:123}", "application/json")
.run()
.assertBody().is("{foo:123}");
a.put("/Map", "(foo=123)", "text/openapi")
.run()
.assertCode().is(415);
a.put("/Map?body=(foo=123)", null)
.run()
.assertBody().is("{foo:123}");
a.put("/Map?body=()", null)
.run()
.assertBody().is("{}");
a.put("/Map?body=null", null)
.run()
.assertBody().is("null");
a.put("/Map?body=", null)
.run()
.assertBody().is("null");
a.put("/Map?body=bad&noTrace=true", null)
.run()
.assertCode().is(400);
a.put("/enum", "'ONE'", "application/json")
.run()
.assertBody().is("'ONE'");
a.put("/enum", "ONE")
.run()
.assertBody().is("'ONE'");
a.put("/enum?body=ONE", null)
.run()
.assertBody().is("'ONE'");
a.put("/enum?body=TWO", null)
.run()
.assertBody().is("'TWO'");
a.put("/enum?body=null", null)
.run()
.assertBody().is("null");
a.put("/enum?body=", null)
.run()
.assertBody().is("null");
a.put("/enum?body=bad&noTrace=true", null)
.run()
.assertCode().is(400);
a.put("/Bean", "{f1:'a'}", "application/json")
.run()
.assertBody().is("{f1:'a'}");
a.put("/Bean", "(f1=a)", "text/openapi")
.run()
.assertCode().is(415);
a.put("/Bean?body=(f1=a)", null)
.run()
.assertBody().is("{f1:'a'}");
a.put("/Bean?body=()", null)
.run()
.assertBody().is("{}");
a.put("/Bean?body=null", null)
.run()
.assertBody().is("null");
a.put("/Bean?body=", null)
.run()
.assertBody().is("null");
a.put("/Bean?body=bad&noTrace=true", null)
.run()
.assertCode().is(400);
// Content-Type should always be ignored.
a.put("/InputStream", "'a'", "application/json")
.run()
.assertBody().is("'\\'a\\''");
a.put("/InputStream", "'a'")
.run()
.assertBody().is("'\\'a\\''");
a.put("/InputStream?body=a", null)
.run()
.assertBody().is("'a'");
a.put("/InputStream?body=null", null)
.run()
.assertBody().is("'null'");
a.put("/InputStream?body=", null)
.run()
.assertBody().is("''");
// Content-Type should always be ignored.
a.put("/Reader", "'a'", "application/json")
.run()
.assertBody().is("'\\'a\\''");
a.put("/Reader", "'a'")
.run()
.assertBody().is("'\\'a\\''");
a.put("/Reader?body=a", null)
.run()
.assertBody().is("'a'");
a.put("/Reader?body=null", null)
.run()
.assertBody().is("'null'");
a.put("/Reader?body=", null)
.run()
.assertBody().is("''");
// It's not currently possible to pass in a &body parameter for InputStream/Reader transforms.
// Input stream transform requests must not specify Content-Type or else gets resolved as POJO.
a.put("/InputStreamTransform?noTrace=true", "'a'", "application/json")
.run()
.assertBody().contains("Bad Request");
a.put("/InputStreamTransform", "'a'")
.run()
.assertBody().is("'\\'a\\''");
// Reader transform requests must not specify Content-Type or else gets resolved as POJO.
a.put("/ReaderTransform?noTrace=true", "'a'", "application/json")
.run()
.assertBody().contains("Bad Request");
a.put("/ReaderTransform", "'a'")
.run()
.assertBody().is("'\\'a\\''");
// When Content-Type specified and matched, treated as a parsed POJO.
a.put("/StringTransform", "'a'", "application/json")
.run()
.assertBody().is("'a'");
// When Content-Type not matched, treated as plain text.
a.put("/StringTransform", "'a'")
.run()
.assertBody().is("'\\'a\\''");
}
//------------------------------------------------------------------------------------------------------------------
// @Body on POJO
//------------------------------------------------------------------------------------------------------------------
@Rest(serializers=SimpleJsonSerializer.class, parsers=JsonParser.class, defaultAccept="application/json")
public static class B {
@RestMethod(name=PUT, path="/StringTransform")
public B1 a(B1 b) {
return b;
}
@Body
public static class B1 {
private String val;
public B1(String val) { this.val = val; }
@Override public String toString() { return val; }
}
@RestMethod(name=PUT, path="/Bean")
public B2 b(B2 b) {
return b;
}
@Body
public static class B2 {
public String f1;
}
@RestMethod(name=PUT, path="/BeanList")
public B3 c(B3 b) {
return b;
}
@SuppressWarnings("serial")
@Body
public static class B3 extends LinkedList<B2> {}
@RestMethod(name=PUT, path="/InputStreamTransform")
public B4 d(B4 b) throws Exception {
return b;
}
@Body
public static class B4 {
String s;
public B4(InputStream in) throws Exception { this.s = IOUtils.read(in); }
@Override public String toString() { return s; }
}
@RestMethod(name=PUT, path="/ReaderTransform")
public B5 e(B5 b) throws Exception {
return b;
}
@Body
public static class B5 {
private String s;
public B5(Reader in) throws Exception { this.s = IOUtils.read(in); }
@Override public String toString() { return s; }
}
}
@Test
public void b01_onPojos() throws Exception {
RestClient b = MockRestClient.buildLax(B.class);
b.put("/StringTransform", "'foo'", "application/json")
.run()
.assertBody().is("'foo'");
// When Content-Type not matched, treated as plain text.
b.put("/StringTransform", "'foo'")
.run()
.assertBody().is("'\\'foo\\''");
b.put("/Bean", "{f1:'a'}", "application/json")
.run()
.assertBody().is("{f1:'a'}");
b.put("/Bean", "(f1=a)", "text/openapi")
.run()
.assertCode().is(415);
b.put("/BeanList", "[{f1:'a'}]", "application/json")
.run()
.assertBody().is("[{f1:'a'}]");
b.put("/BeanList", "(f1=a)", "text/openapi")
.run()
.assertCode().is(415);
b.put("/InputStreamTransform", "a")
.run()
.assertBody().is("'a'");
// When Content-Type matched, treated as parsed POJO.
b.put("/InputStreamTransform?noTrace=true", "a", "application/json")
.run()
.assertBody().contains("Bad Request");
b.put("/ReaderTransform", "a")
.run()
.assertBody().is("'a'");
// When Content-Type matched, treated as parsed POJO.
b.put("/ReaderTransform?noTrace=true", "a", "application/json")
.run()
.assertBody().contains("Bad Request");
}
//------------------------------------------------------------------------------------------------------------------
// No serializers or parsers needed when using only streams and readers.
//------------------------------------------------------------------------------------------------------------------
@Rest
public static class D {
@RestMethod(name=PUT, path="/String")
public Reader a(@Body Reader b) throws Exception {
return b;
}
@RestMethod(name=PUT, path="/InputStream")
public InputStream b(@Body InputStream b) throws Exception {
return b;
}
@RestMethod(name=PUT, path="/Reader")
public Reader c(@Body Reader b) throws Exception {
return b;
}
@RestMethod(name=PUT, path="/StringTransform")
public Reader d(@Body D1 b) throws Exception {
return new StringReader(b.toString());
}
public static class D1 {
private String s;
public D1(String in) throws Exception { this.s = in; }
@Override public String toString() { return s; }
}
@RestMethod(name=PUT, path="/InputStreamTransform")
public Reader e(@Body D2 b) throws Exception {
return new StringReader(b.toString());
}
public static class D2 {
String s;
public D2(InputStream in) throws Exception { this.s = IOUtils.read(in); }
@Override public String toString() { return s; }
}
@RestMethod(name=PUT, path="/ReaderTransform")
public Reader f(@Body D3 b) throws Exception {
return new StringReader(b.toString());
}
public static class D3 {
private String s;
public D3(Reader in) throws Exception{ this.s = IOUtils.read(in); }
@Override public String toString() { return s; }
}
@RestMethod(name=PUT, path="/StringTransformBodyOnPojo")
public Reader g(D4 b) throws Exception {
return new StringReader(b.toString());
}
@Body
public static class D4 {
private String s;
public D4(String in) throws Exception { this.s = in; }
@Override public String toString() { return s; }
}
@RestMethod(name=PUT, path="/InputStreamTransformBodyOnPojo")
public Reader h(D5 b) throws Exception {
return new StringReader(b.toString());
}
@Body
public static class D5 {
String s;
public D5(InputStream in) throws Exception { this.s = IOUtils.read(in); }
@Override public String toString() { return s; }
}
@RestMethod(name=PUT, path="/ReaderTransformBodyOnPojo")
public Reader i(D6 b) throws Exception {
return new StringReader(b.toString());
}
@Body
public static class D6 {
private String s;
public D6(Reader in) throws Exception{ this.s = IOUtils.read(in); }
@Override public String toString() { return s; }
}
}
@Test
public void d01_noMediaTypesOnStreams() throws Exception {
RestClient d = MockRestClient.buildLax(D.class);
d.put("/String", "a")
.run()
.assertBody().is("a");
d.put("/String", "a", "application/json")
.run()
.assertBody().is("a");
d.put("/InputStream", "a")
.run()
.assertBody().is("a");
d.put("/InputStream", "a", "application/json")
.run()
.assertBody().is("a");
d.put("/Reader", "a")
.run()
.assertBody().is("a");
d.put("/Reader", "a", "application/json")
.run()
.assertBody().is("a");
d.put("/StringTransform", "a")
.run()
.assertBody().is("a");
d.put("/StringTransform?noTrace=true", "a", "application/json")
.run()
.assertCode().is(415);
d.put("/InputStreamTransform", "a")
.run()
.assertBody().is("a");
d.put("/InputStreamTransform", "a", "application/json")
.run()
.assertBody().is("a");
d.put("/ReaderTransform", "a")
.run()
.assertBody().is("a");
d.put("/ReaderTransform", "a", "application/json")
.run()
.assertBody().is("a");
d.put("/StringTransformBodyOnPojo", "a")
.run()
.assertBody().is("a");
d.put("/StringTransformBodyOnPojo?noTrace=true", "a", "application/json")
.run()
.assertCode().is(415);
d.put("/InputStreamTransformBodyOnPojo", "a")
.run()
.assertBody().is("a");
d.put("/InputStreamTransformBodyOnPojo", "a", "application/json")
.run()
.assertBody().is("a");
d.put("/ReaderTransformBodyOnPojo", "a")
.run()
.assertBody().is("a");
d.put("/ReaderTransformBodyOnPojo", "a", "application/json")
.run()
.assertBody().is("a");
}
//------------------------------------------------------------------------------------------------------------------
// Complex POJOs
//------------------------------------------------------------------------------------------------------------------
@Rest(serializers=SimpleJsonSerializer.class, parsers=JsonParser.class, defaultAccept="application/json")
public static class E {
@RestMethod(name=PUT, path="/B")
public XBeans.XB a(@Body XBeans.XB b) {
return b;
}
@RestMethod(name=PUT, path="/C")
public XBeans.XC b(@Body XBeans.XC c) {
return c;
}
}
@Test
public void e01_complexPojos() throws Exception {
RestClient e = MockRestClient.build(E.class);
String expected;
expected = "{f01:['a','b'],f02:['c','d'],f03:[1,2],f04:[3,4],f05:[['e','f'],['g','h']],f06:[['i','j'],['k','l']],f07:[{a:'a',b:1,c:true},{a:'a',b:1,c:true}],f08:[{a:'a',b:1,c:true},{a:'a',b:1,c:true}],f09:[[{a:'a',b:1,c:true}],[{a:'a',b:1,c:true}]],f10:[[{a:'a',b:1,c:true}],[{a:'a',b:1,c:true}]],f11:['a','b'],f12:['c','d'],f13:[1,2],f14:[3,4],f15:[['e','f'],['g','h']],f16:[['i','j'],['k','l']],f17:[{a:'a',b:1,c:true},{a:'a',b:1,c:true}],f18:[{a:'a',b:1,c:true},{a:'a',b:1,c:true}],f19:[[{a:'a',b:1,c:true}],[{a:'a',b:1,c:true}]],f20:[[{a:'a',b:1,c:true}],[{a:'a',b:1,c:true}]]}";
e.put("/B", SimpleJsonSerializer.DEFAULT.toString(XBeans.XB.INSTANCE), "application/json")
.run()
.assertBody().is(expected);
expected = "{f01:['a','b'],f02:['c','d'],f03:[1,2],f04:[3,4],f05:[['e','f'],['g','h']],f06:[['i','j'],['k','l']],f07:[{a:'a',b:1,c:true},{a:'a',b:1,c:true}],f08:[{a:'a',b:1,c:true},{a:'a',b:1,c:true}],f09:[[{a:'a',b:1,c:true}],[{a:'a',b:1,c:true}]],f10:[[{a:'a',b:1,c:true}],[{a:'a',b:1,c:true}]],f11:['a','b'],f12:['c','d'],f13:[1,2],f14:[3,4],f15:[['e','f'],['g','h']],f16:[['i','j'],['k','l']],f17:[{a:'a',b:1,c:true},{a:'a',b:1,c:true}],f18:[{a:'a',b:1,c:true},{a:'a',b:1,c:true}],f19:[[{a:'a',b:1,c:true}],[{a:'a',b:1,c:true}]],f20:[[{a:'a',b:1,c:true}],[{a:'a',b:1,c:true}]]}";
e.put("/B?body=" + UonSerializer.DEFAULT.serialize(XBeans.XB.INSTANCE), "a")
.run()
.assertBody().is(expected);
expected = "{f01:['a','b'],f02:['c','d'],f03:[1,2],f04:[3,4],f05:[['e','f'],['g','h']],f06:[['i','j'],['k','l']],f07:[{a:'a',b:1,c:true},{a:'a',b:1,c:true}],f08:[{a:'a',b:1,c:true},{a:'a',b:1,c:true}],f09:[[{a:'a',b:1,c:true}],[{a:'a',b:1,c:true}]],f10:[[{a:'a',b:1,c:true}],[{a:'a',b:1,c:true}]],f11:['a','b'],f12:['c','d'],f13:[1,2],f14:[3,4],f15:[['e','f'],['g','h']],f16:[['i','j'],['k','l']],f17:[{a:'a',b:1,c:true},{a:'a',b:1,c:true}],f18:[{a:'a',b:1,c:true},{a:'a',b:1,c:true}],f19:[[{a:'a',b:1,c:true}],[{a:'a',b:1,c:true}]],f20:[[{a:'a',b:1,c:true}],[{a:'a',b:1,c:true}]]}";
e.put("/C", SimpleJsonSerializer.DEFAULT.toString(XBeans.XB.INSTANCE), "application/json")
.run()
.assertBody().is(expected);
expected = "{f01:['a','b'],f02:['c','d'],f03:[1,2],f04:[3,4],f05:[['e','f'],['g','h']],f06:[['i','j'],['k','l']],f07:[{a:'a',b:1,c:true},{a:'a',b:1,c:true}],f08:[{a:'a',b:1,c:true},{a:'a',b:1,c:true}],f09:[[{a:'a',b:1,c:true}],[{a:'a',b:1,c:true}]],f10:[[{a:'a',b:1,c:true}],[{a:'a',b:1,c:true}]],f11:['a','b'],f12:['c','d'],f13:[1,2],f14:[3,4],f15:[['e','f'],['g','h']],f16:[['i','j'],['k','l']],f17:[{a:'a',b:1,c:true},{a:'a',b:1,c:true}],f18:[{a:'a',b:1,c:true},{a:'a',b:1,c:true}],f19:[[{a:'a',b:1,c:true}],[{a:'a',b:1,c:true}]],f20:[[{a:'a',b:1,c:true}],[{a:'a',b:1,c:true}]]}";
e.put("/C?body=" + UonSerializer.DEFAULT.serialize(XBeans.XB.INSTANCE), "a")
.run()
.assertBody().is(expected);
}
@Rest(serializers=SimpleJsonSerializer.class, parsers=JsonParser.class, defaultAccept="application/json")
@BeanConfig(applyBean={@Bean(on="A,B,C",sort=true)})
@UrlEncodingConfig(applyUrlEncoding={@UrlEncoding(on="C",expandedParams=true)})
public static class E2 {
@RestMethod(name=PUT, path="/B")
public XBeans.XE a(@Body XBeans.XE b) {
return b;
}
@RestMethod(name=PUT, path="/C")
public XBeans.XF b(@Body XBeans.XF c) {
return c;
}
}
@Test
public void e02_complexPojos() throws Exception {
RestClient e2 = MockRestClient.build(E2.class);
String expected;
expected = "{f01:['a','b'],f02:['c','d'],f03:[1,2],f04:[3,4],f05:[['e','f'],['g','h']],f06:[['i','j'],['k','l']],f07:[{a:'a',b:1,c:true},{a:'a',b:1,c:true}],f08:[{a:'a',b:1,c:true},{a:'a',b:1,c:true}],f09:[[{a:'a',b:1,c:true}],[{a:'a',b:1,c:true}]],f10:[[{a:'a',b:1,c:true}],[{a:'a',b:1,c:true}]],f11:['a','b'],f12:['c','d'],f13:[1,2],f14:[3,4],f15:[['e','f'],['g','h']],f16:[['i','j'],['k','l']],f17:[{a:'a',b:1,c:true},{a:'a',b:1,c:true}],f18:[{a:'a',b:1,c:true},{a:'a',b:1,c:true}],f19:[[{a:'a',b:1,c:true}],[{a:'a',b:1,c:true}]],f20:[[{a:'a',b:1,c:true}],[{a:'a',b:1,c:true}]]}";
e2.put("/B", SimpleJsonSerializer.DEFAULT.builder().applyAnnotations(XBeans.Annotations.class).build().toString(XBeans.XE.INSTANCE), "application/json")
.run()
.assertBody().is(expected);
expected = "{f01:['a','b'],f02:['c','d'],f03:[1,2],f04:[3,4],f05:[['e','f'],['g','h']],f06:[['i','j'],['k','l']],f07:[{a:'a',b:1,c:true},{a:'a',b:1,c:true}],f08:[{a:'a',b:1,c:true},{a:'a',b:1,c:true}],f09:[[{a:'a',b:1,c:true}],[{a:'a',b:1,c:true}]],f10:[[{a:'a',b:1,c:true}],[{a:'a',b:1,c:true}]],f11:['a','b'],f12:['c','d'],f13:[1,2],f14:[3,4],f15:[['e','f'],['g','h']],f16:[['i','j'],['k','l']],f17:[{a:'a',b:1,c:true},{a:'a',b:1,c:true}],f18:[{a:'a',b:1,c:true},{a:'a',b:1,c:true}],f19:[[{a:'a',b:1,c:true}],[{a:'a',b:1,c:true}]],f20:[[{a:'a',b:1,c:true}],[{a:'a',b:1,c:true}]]}";
e2.put("/B?body=" + UonSerializer.DEFAULT.builder().applyAnnotations(XBeans.Annotations.class).build().serialize(XBeans.XE.INSTANCE), "a")
.run()
.assertBody().is(expected);
expected = "{f01:['a','b'],f02:['c','d'],f03:[1,2],f04:[3,4],f05:[['e','f'],['g','h']],f06:[['i','j'],['k','l']],f07:[{a:'a',b:1,c:true},{a:'a',b:1,c:true}],f08:[{a:'a',b:1,c:true},{a:'a',b:1,c:true}],f09:[[{a:'a',b:1,c:true}],[{a:'a',b:1,c:true}]],f10:[[{a:'a',b:1,c:true}],[{a:'a',b:1,c:true}]],f11:['a','b'],f12:['c','d'],f13:[1,2],f14:[3,4],f15:[['e','f'],['g','h']],f16:[['i','j'],['k','l']],f17:[{a:'a',b:1,c:true},{a:'a',b:1,c:true}],f18:[{a:'a',b:1,c:true},{a:'a',b:1,c:true}],f19:[[{a:'a',b:1,c:true}],[{a:'a',b:1,c:true}]],f20:[[{a:'a',b:1,c:true}],[{a:'a',b:1,c:true}]]}";
e2.put("/C", SimpleJsonSerializer.DEFAULT.builder().applyAnnotations(XBeans.Annotations.class).build().toString(XBeans.XE.INSTANCE), "application/json")
.run()
.assertBody().is(expected);
expected = "{f01:['a','b'],f02:['c','d'],f03:[1,2],f04:[3,4],f05:[['e','f'],['g','h']],f06:[['i','j'],['k','l']],f07:[{a:'a',b:1,c:true},{a:'a',b:1,c:true}],f08:[{a:'a',b:1,c:true},{a:'a',b:1,c:true}],f09:[[{a:'a',b:1,c:true}],[{a:'a',b:1,c:true}]],f10:[[{a:'a',b:1,c:true}],[{a:'a',b:1,c:true}]],f11:['a','b'],f12:['c','d'],f13:[1,2],f14:[3,4],f15:[['e','f'],['g','h']],f16:[['i','j'],['k','l']],f17:[{a:'a',b:1,c:true},{a:'a',b:1,c:true}],f18:[{a:'a',b:1,c:true},{a:'a',b:1,c:true}],f19:[[{a:'a',b:1,c:true}],[{a:'a',b:1,c:true}]],f20:[[{a:'a',b:1,c:true}],[{a:'a',b:1,c:true}]]}";
e2.put("/C?body=" + UonSerializer.DEFAULT.builder().applyAnnotations(XBeans.Annotations.class).build().serialize(XBeans.XE.INSTANCE), "a")
.run()
.assertBody().is(expected);
}
//------------------------------------------------------------------------------------------------------------------
// Form POSTS with @Body parameter
//------------------------------------------------------------------------------------------------------------------
@Rest(serializers=JsonSerializer.class,parsers=JsonParser.class)
public static class F {
@RestMethod(name=POST, path="/*")
public Reader a(
@Body F1 bean,
@HasQuery("p1") boolean hqp1, @HasQuery("p2") boolean hqp2,
@Query("p1") String qp1, @Query("p2") int qp2) throws Exception {
return new StringReader("bean=["+SimpleJsonSerializer.DEFAULT.toString(bean)+"],qp1=["+qp1+"],qp2=["+qp2+"],hqp1=["+hqp1+"],hqp2=["+hqp2+"]");
}
public static class F1 {
public String p1;
public int p2;
}
}
@Test
public void f01_formPostAsContent() throws Exception {
RestClient f = MockRestClient.build(F.class);
f.post("/", "{p1:'p1',p2:2}", "application/json")
.run()
.assertBody().is("bean=[{p1:'p1',p2:2}],qp1=[null],qp2=[0],hqp1=[false],hqp2=[false]");
f.post("/", "{}", "application/json")
.run()
.assertBody().is("bean=[{p2:0}],qp1=[null],qp2=[0],hqp1=[false],hqp2=[false]");
f.post("?p1=p3&p2=4", "{p1:'p1',p2:2}", "application/json")
.run()
.assertBody().is("bean=[{p1:'p1',p2:2}],qp1=[p3],qp2=[4],hqp1=[true],hqp2=[true]");
f.post("?p1=p3&p2=4", "{}", "application/json")
.run()
.assertBody().is("bean=[{p2:0}],qp1=[p3],qp2=[4],hqp1=[true],hqp2=[true]");
}
//------------------------------------------------------------------------------------------------------------------
// Test multi-part parameter keys on bean properties of type array/Collection (i.e. &key=val1,&key=val2)
// using @UrlEncoding(expandedParams=true) annotation on bean.
// A simple round-trip test to verify that both serializing and parsing works.
//------------------------------------------------------------------------------------------------------------------
@Rest(serializers=UrlEncodingSerializer.class,parsers=UrlEncodingParser.class)
public static class G {
@RestMethod(name=POST,path="/")
public XBeans.XC a(@Body XBeans.XC content) throws Exception {
return content;
}
}
@Test
public void g01_multiPartParameterKeysOnCollections() throws Exception {
RestClient g = MockRestClient.build(G.class);
String in = ""
+ "f01=a&f01=b"
+ "&f02=c&f02=d"
+ "&f03=1&f03=2"
+ "&f04=3&f04=4"
+ "&f05=@(e,f)&f05=@(g,h)"
+ "&f06=@(i,j)&f06=@(k,l)"
+ "&f07=(a=a,b=1,c=true)&f07=(a=b,b=2,c=false)"
+ "&f08=(a=a,b=1,c=true)&f08=(a=b,b=2,c=false)"
+ "&f09=@((a=a,b=1,c=true))&f09=@((a=b,b=2,c=false))"
+ "&f10=@((a=a,b=1,c=true))&f10=@((a=b,b=2,c=false))"
+ "&f11=a&f11=b"
+ "&f12=c&f12=d"
+ "&f13=1&f13=2"
+ "&f14=3&f14=4"
+ "&f15=@(e,f)&f15=@(g,h)"
+ "&f16=@(i,j)&f16=@(k,l)"
+ "&f17=(a=a,b=1,c=true)&f17=(a=b,b=2,c=false)"
+ "&f18=(a=a,b=1,c=true)&f18=(a=b,b=2,c=false)"
+ "&f19=@((a=a,b=1,c=true))&f19=@((a=b,b=2,c=false))"
+ "&f20=@((a=a,b=1,c=true))&f20=@((a=b,b=2,c=false))";
g.post("/", in, "application/x-www-form-urlencoded")
.run()
.assertBody().is(in);
}
//------------------------------------------------------------------------------------------------------------------
// Test multi-part parameter keys on bean properties of type array/Collection (i.e. &key=val1,&key=val2)
// using URLENC_expandedParams property.
// A simple round-trip test to verify that both serializing and parsing works.
//------------------------------------------------------------------------------------------------------------------
@Rest(serializers=UrlEncodingSerializer.class,parsers=UrlEncodingParser.class)
public static class H {
@RestMethod(name=POST,path="/",
properties={
@Property(name=UrlEncodingSerializer.URLENC_expandedParams, value="true"),
@Property(name=UrlEncodingParser.URLENC_expandedParams, value="true")
}
)
public XBeans.XB a(@Body XBeans.XB content) throws Exception {
return content;
}
}
@Test
public void h01_multiPartParameterKeysOnCollections_usingExpandedParams() throws Exception {
RestClient h = MockRestClient.build(H.class);
String in = ""
+ "f01=a&f01=b"
+ "&f02=c&f02=d"
+ "&f03=1&f03=2"
+ "&f04=3&f04=4"
+ "&f05=@(e,f)&f05=@(g,h)"
+ "&f06=@(i,j)&f06=@(k,l)"
+ "&f07=(a=a,b=1,c=true)&f07=(a=b,b=2,c=false)"
+ "&f08=(a=a,b=1,c=true)&f08=(a=b,b=2,c=false)"
+ "&f09=@((a=a,b=1,c=true))&f09=@((a=b,b=2,c=false))"
+ "&f10=@((a=a,b=1,c=true))&f10=@((a=b,b=2,c=false))"
+ "&f11=a&f11=b"
+ "&f12=c&f12=d"
+ "&f13=1&f13=2"
+ "&f14=3&f14=4"
+ "&f15=@(e,f)&f15=@(g,h)"
+ "&f16=@(i,j)&f16=@(k,l)"
+ "&f17=(a=a,b=1,c=true)&f17=(a=b,b=2,c=false)"
+ "&f18=(a=a,b=1,c=true)&f18=(a=b,b=2,c=false)"
+ "&f19=@((a=a,b=1,c=true))&f19=@((a=b,b=2,c=false))"
+ "&f20=@((a=a,b=1,c=true))&f20=@((a=b,b=2,c=false))";
h.post("/", in, "application/x-www-form-urlencoded")
.run()
.assertBody().is(in);
}
@Rest(serializers=UrlEncodingSerializer.class,parsers=UrlEncodingParser.class)
@BeanConfig(applyBean={@Bean(on="A,B,C",sort=true)})
@UrlEncodingConfig(applyUrlEncoding={@UrlEncoding(on="C",expandedParams=true)})
public static class H2 {
@RestMethod(name=POST,path="/",
properties={
@Property(name=UrlEncodingSerializer.URLENC_expandedParams, value="true"),
@Property(name=UrlEncodingParser.URLENC_expandedParams, value="true")
}
)
public XBeans.XE a(@Body XBeans.XE content) throws Exception {
return content;
}
}
@Test
public void h02_multiPartParameterKeysOnCollections_usingExpandedParams() throws Exception {
RestClient h2 = MockRestClient.build(H2.class);
String in = ""
+ "f01=a&f01=b"
+ "&f02=c&f02=d"
+ "&f03=1&f03=2"
+ "&f04=3&f04=4"
+ "&f05=@(e,f)&f05=@(g,h)"
+ "&f06=@(i,j)&f06=@(k,l)"
+ "&f07=(a=a,b=1,c=true)&f07=(a=b,b=2,c=false)"
+ "&f08=(a=a,b=1,c=true)&f08=(a=b,b=2,c=false)"
+ "&f09=@((a=a,b=1,c=true))&f09=@((a=b,b=2,c=false))"
+ "&f10=@((a=a,b=1,c=true))&f10=@((a=b,b=2,c=false))"
+ "&f11=a&f11=b"
+ "&f12=c&f12=d"
+ "&f13=1&f13=2"
+ "&f14=3&f14=4"
+ "&f15=@(e,f)&f15=@(g,h)"
+ "&f16=@(i,j)&f16=@(k,l)"
+ "&f17=(a=a,b=1,c=true)&f17=(a=b,b=2,c=false)"
+ "&f18=(a=a,b=1,c=true)&f18=(a=b,b=2,c=false)"
+ "&f19=@((a=a,b=1,c=true))&f19=@((a=b,b=2,c=false))"
+ "&f20=@((a=a,b=1,c=true))&f20=@((a=b,b=2,c=false))";
h2.post("/", in, "application/x-www-form-urlencoded")
.run()
.assertBody().is(in);
}
//------------------------------------------------------------------------------------------------------------------
// Test behavior of @Body(required=true).
//------------------------------------------------------------------------------------------------------------------
@Rest(serializers=JsonSerializer.class,parsers=JsonParser.class)
public static class I {
@RestMethod(name=POST)
public XBeans.XB a(@Body(r=true) XBeans.XB content) throws Exception {
return content;
}
@RestMethod(name=POST)
@BeanConfig(applyBean={@Bean(on="A,B,C",sort=true)})
@UrlEncodingConfig(applyUrlEncoding={@UrlEncoding(on="C",expandedParams=true)})
public XBeans.XE b(@Body(r=true) XBeans.XE content) throws Exception {
return content;
}
}
@Test
public void i01_required() throws Exception {
RestClient i = MockRestClient.buildLax(I.class);
i.post("/a", "", "application/json")
.run()
.assertCode().is(400)
.assertBody().contains("Required value not provided.");
i.post("/a", "{}", "application/json")
.run()
.assertCode().is(200);
i.post("/b", "", "application/json")
.run()
.assertCode().is(400)
.assertBody().contains("Required value not provided.");
i.post("/b", "{}", "application/json")
.run()
.assertCode().is(200);
}
//------------------------------------------------------------------------------------------------------------------
// Optional body parameter.
//------------------------------------------------------------------------------------------------------------------
@Rest(serializers=SimpleJsonSerializer.class,parsers=JsonParser.class)
public static class J {
@RestMethod(name=POST)
public Object a(@Body Optional<Integer> body) throws Exception {
assertNotNull(body);
return body;
}
@RestMethod(name=POST)
public Object b(@Body Optional<ABean> body) throws Exception {
assertNotNull(body);
return body;
}
@RestMethod(name=POST)
public Object c(@Body Optional<List<ABean>> body) throws Exception {
assertNotNull(body);
return body;
}
@RestMethod(name=POST)
public Object d(@Body List<Optional<ABean>> body) throws Exception {
return body;
}
}
@Test
public void j01_optionalParams() throws Exception {
RestClient j = MockRestClient.buildJson(J.class);
j.post("/a", 123)
.run()
.assertCode().is(200)
.assertBody().is("123");
j.post("/a", null)
.run()
.assertCode().is(200)
.assertBody().is("null");
j.post("/b", ABean.get())
.run()
.assertCode().is(200)
.assertBody().is("{a:1,b:'foo'}");
j.post("/b", null)
.run()
.assertCode().is(200)
.assertBody().is("null");
String body1 = SimpleJson.DEFAULT.toString(AList.of(ABean.get()));
j.post("/c", body1, "application/json")
.run()
.assertCode().is(200)
.assertBody().is("[{a:1,b:'foo'}]");
j.post("/c", null)
.run()
.assertCode().is(200)
.assertBody().is("null");
String body2 = SimpleJson.DEFAULT.toString(AList.of(Optional.of(ABean.get())));
j.post("/d", body2, "application/json")
.run()
.assertCode().is(200)
.assertBody().is("[{a:1,b:'foo'}]");
j.post("/d", null)
.run()
.assertCode().is(200)
.assertBody().is("null");
}
}