1+ package com.foo.rest.examples.bb.xml
2+
3+ import io.swagger.v3.oas.annotations.responses.ApiResponse
4+ import io.swagger.v3.oas.annotations.responses.ApiResponses
5+ import org.evomaster.e2etests.utils.CoveredTargets
6+ import org.springframework.http.ResponseEntity
7+ import org.springframework.web.bind.annotation.*
8+ import org.springframework.boot.SpringApplication
9+ import org.springframework.boot.autoconfigure.SpringBootApplication
10+ import org.springframework.boot.autoconfigure.security.servlet.SecurityAutoConfiguration
11+ import org.springframework.http.MediaType
12+ import javax.xml.bind.annotation.*
13+
14+ @SpringBootApplication(exclude = [SecurityAutoConfiguration ::class ])
15+ @RequestMapping(path = [" /api/bbxml" ])
16+ @RestController
17+ open class BBXMLApplication {
18+
19+ companion object {
20+ @JvmStatic
21+ fun main (args : Array <String >) {
22+ SpringApplication .run (BBXMLApplication ::class .java, * args)
23+ }
24+ }
25+
26+ /* ===================== ENDPOINTS ===================== */
27+
28+ // 1. String -> XML
29+ @ApiResponses(value = [
30+ ApiResponse (responseCode = " 200" , description = " XML representation of the input string" ),
31+ ApiResponse (responseCode = " 400" , description = " Body is blank" )
32+ ])
33+ @PostMapping(
34+ " /receive-string-respond-xml" ,
35+ consumes = [" text/plain" ],
36+ produces = [" application/xml" ]
37+ )
38+ fun stringToXml (@RequestBody body : String ): ResponseEntity <Person > {
39+ if (body.isBlank())
40+ return ResponseEntity .status(400 ).build()
41+
42+ CoveredTargets .cover(" STRING_TO_XML" )
43+ return ResponseEntity .status(200 )
44+ .body(Person (name = body, age = body.length))
45+ }
46+
47+ // 2. XML -> String
48+ @ApiResponses(value = [
49+ ApiResponse (responseCode = " 200" , description = " String representation of the XML person" ),
50+ ApiResponse (responseCode = " 400" , description = " Person name is blank or age is negative" )
51+ ])
52+ @PostMapping(
53+ " /receive-xml-respond-string" ,
54+ consumes = [" application/xml" ],
55+ produces = [" text/plain" ]
56+ )
57+ fun xmlToString (@RequestBody person : Person ): ResponseEntity <String > {
58+ if (! isValid(person))
59+ return ResponseEntity
60+ .status(400 ).contentType(MediaType .TEXT_PLAIN ).body(" invalid person" )
61+
62+ CoveredTargets .cover(" XML_TO_STRING" )
63+ return ResponseEntity .status(200 ).body(" not ok" )
64+ }
65+
66+ // 3. Employee (2-level nesting)
67+ @ApiResponses(value = [
68+ ApiResponse (responseCode = " 200" , description = " Employee processed successfully" ),
69+ ApiResponse (responseCode = " 400" , description = " Employee person name is blank or age is negative" )
70+ ])
71+ @PostMapping(
72+ " /employee" ,
73+ consumes = [" application/xml" ],
74+ produces = [" text/plain" ]
75+ )
76+ fun employee (@RequestBody employee : Employee ): ResponseEntity <String > {
77+ if (! isValid(employee))
78+ return ResponseEntity .status(400 )
79+ .contentType(MediaType .TEXT_PLAIN )
80+ .body(" invalid input" )
81+
82+ CoveredTargets .cover(" EMPLOYEE" )
83+ return ResponseEntity .status(200 )
84+ .body(
85+ if (employee.role == Role .ADMIN && employee.person.age > 30 )
86+ " admin"
87+ else
88+ " not admin or too young"
89+ )
90+ }
91+
92+ // 4. Company (3-level nesting)
93+ @ApiResponses(value = [
94+ ApiResponse (responseCode = " 200" , description = " Company processed successfully" ),
95+ ApiResponse (responseCode = " 400" , description = " Company name is blank" )
96+ ])
97+ @PostMapping(
98+ " /company" ,
99+ consumes = [" application/xml" ],
100+ produces = [" text/plain" ]
101+ )
102+ fun company (@RequestBody company : Company ): ResponseEntity <String > {
103+ if (! isValid(company))
104+ return ResponseEntity .status(400 ).contentType(MediaType .TEXT_PLAIN ).body(" invalid company" )
105+
106+ CoveredTargets .cover(" COMPANY" )
107+ return ResponseEntity .status(200 )
108+ .body(if (company.employees.isEmpty()) " small company" else " big company" )
109+ }
110+
111+ // 5. Department (recursive)
112+ @ApiResponses(value = [
113+ ApiResponse (responseCode = " 200" , description = " Department processed successfully" ),
114+ ApiResponse (responseCode = " 400" , description = " Department name is blank" )
115+ ])
116+ @PostMapping(
117+ " /department" ,
118+ consumes = [" application/xml" ],
119+ produces = [" text/plain" ]
120+ )
121+ fun department (@RequestBody department : Department ): ResponseEntity <String > {
122+ if (! isValid(department))
123+ return ResponseEntity .status(400 )
124+ .contentType(MediaType .TEXT_PLAIN )
125+ .body(" invalid input" )
126+
127+ CoveredTargets .cover(" DEPARTMENT" )
128+ return ResponseEntity .status(200 )
129+ .body(" department with ${department.employees.size + 1 } employees" )
130+ }
131+
132+ // 6. Organization (3 lists)
133+ @ApiResponses(value = [
134+ ApiResponse (responseCode = " 200" , description = " Organization processed successfully" ),
135+ ApiResponse (responseCode = " 400" , description = " Organization name is blank" )
136+ ])
137+ @PostMapping(
138+ " /organization" ,
139+ consumes = [" application/xml" ],
140+ produces = [" text/plain" ]
141+ )
142+ fun organization (@RequestBody organization : Organization ): ResponseEntity <String > {
143+ if (! isValid(organization))
144+ return ResponseEntity .status(400 )
145+ .contentType(MediaType .TEXT_PLAIN )
146+ .body(" invalid input" )
147+
148+ CoveredTargets .cover(" ORGANIZATION" )
149+ return ResponseEntity .status(200 )
150+ .body(" organization with ${organization.people.size} people" )
151+ }
152+
153+ // 7. Project (attributes)
154+ @ApiResponses(value = [
155+ ApiResponse (responseCode = " 200" , description = " Project processed successfully" ),
156+ ApiResponse (responseCode = " 400" , description = " Project code is blank" )
157+ ])
158+ @PostMapping(
159+ " /project" ,
160+ consumes = [" application/xml" ],
161+ produces = [" text/plain" ]
162+ )
163+ fun project (@RequestBody project : Project ): ResponseEntity <String > {
164+ if (! isValid(project))
165+ return ResponseEntity .status(400 ).contentType(MediaType .TEXT_PLAIN ).body(" invalid project" )
166+
167+ var adults = 0
168+ for (m in project.members) {
169+ if (m.id.isNotBlank() && m.age >= 18 )
170+ adults++
171+ }
172+
173+ CoveredTargets .cover(" PROJECT" )
174+ return ResponseEntity .status(200 ).body(
175+ if (adults > 0 )
176+ " project ${project.code} has $adults adult members"
177+ else
178+ " project ${project.code} has only minors"
179+ )
180+ }
181+
182+ // 8. Project list
183+ @PostMapping(
184+ " /projects" ,
185+ consumes = [" application/xml" ],
186+ produces = [" text/plain" ]
187+ )
188+ fun postProjects (@RequestBody list : ProjectList ): ResponseEntity <String > {
189+ if (! isValid(list))
190+ return ResponseEntity .status(400 ).body(" " )
191+
192+ var members = 0
193+ var hasCode = false
194+
195+ for (p in list.projects) {
196+ if (p.code.isNotBlank())
197+ hasCode = true
198+ for (m in p.members) {
199+ if (m.id.isNotBlank())
200+ members++
201+ }
202+ }
203+
204+ CoveredTargets .cover(" PROJECTS" )
205+ return ResponseEntity .status(200 )
206+ .body(
207+ if (hasCode && members > 0 )
208+ " valid projects with $members members"
209+ else
210+ " invalid projects"
211+ )
212+ }
213+
214+ // 9. Person with attributes
215+ @ApiResponses(value = [
216+ ApiResponse (responseCode = " 200" , description = " Person with attributes is valid" ),
217+ ApiResponse (responseCode = " 400" , description = " Person id or name is blank, or age is negative" )
218+ ])
219+ @PostMapping(
220+ " /person-with-attr" ,
221+ consumes = [" application/xml" ],
222+ produces = [" text/plain" ]
223+ )
224+ fun personWithAttr (@RequestBody person : PersonWithAttr ): ResponseEntity <String > {
225+
226+ if (! isValid(person))
227+ return ResponseEntity .status(400 ).body(" invalid person" )
228+
229+ CoveredTargets .cover(" PERSON_ATTR" )
230+ return ResponseEntity .status(200 )
231+ .body(" person ${person.id} is valid" )
232+ }
233+
234+ /* ===================== SCHEMA-LIKE VALIDATION ===================== */
235+
236+ private fun isValid (p : Person ): Boolean =
237+ p.name.isNotBlank() && p.age >= 0
238+
239+ private fun isValid (e : Employee ): Boolean =
240+ isValid(e.person)
241+
242+ private fun isValid (c : Company ): Boolean =
243+ c.name.isNotBlank()
244+
245+ private fun isValid (d : Department ): Boolean =
246+ d.name.isNotBlank()
247+
248+ private fun isValid (o : Organization ): Boolean =
249+ o.name.isNotBlank()
250+
251+ private fun isValid (p : PersonWithAttr ): Boolean =
252+ p.id.isNotBlank() &&
253+ p.name.isNotBlank() &&
254+ p.age >= 0
255+
256+ private fun isValid (p : Project ): Boolean =
257+ p.code.isNotBlank()
258+
259+ private fun isValid (pl : ProjectList ): Boolean =
260+ true
261+ }
262+
263+ /* ===================== MODELS (JAXB) ===================== */
264+
265+ @XmlRootElement(name = " person" )
266+ @XmlAccessorType(XmlAccessType .FIELD )
267+ open class Person (
268+ var name : String = " " ,
269+ var age : Int = 0
270+ )
271+
272+ @XmlRootElement(name = " employee" )
273+ @XmlAccessorType(XmlAccessType .FIELD )
274+ open class Employee (
275+ var person : Person = Person (),
276+ var role : Role = Role .USER
277+ )
278+
279+ @XmlRootElement(name = " company" )
280+ @XmlAccessorType(XmlAccessType .FIELD )
281+ open class Company (
282+ var name : String = " " ,
283+ @field:XmlElement(name = "Person ", namespace = "")
284+ var employees : MutableList <Person > = mutableListOf()
285+ )
286+
287+ enum class Role { ADMIN , USER , GUEST }
288+
289+ @XmlRootElement(name = " department" )
290+ @XmlAccessorType(XmlAccessType .FIELD )
291+ open class Department (
292+ var name : String = " " ,
293+ @field:XmlElement(name = "Employee ", namespace = "")
294+ var employees : List <Employee > = emptyList(),
295+ @field:XmlElement(name = "Department ", namespace = "")
296+ var subDepartments : MutableList <Department > = mutableListOf()
297+ )
298+
299+ @XmlRootElement(name = " organization" )
300+ @XmlAccessorType(XmlAccessType .FIELD )
301+ open class Organization (
302+ var name : String = " " ,
303+ @field:XmlElement(name = "Person ", namespace = "")
304+ var people : MutableList <Person > = mutableListOf(),
305+ @field:XmlElement(name = "Employee ", namespace = "")
306+ var employees : MutableList <Employee > = mutableListOf(),
307+ @field:XmlElement(name = "Company ", namespace = "")
308+ var companies : MutableList <Company > = mutableListOf()
309+ )
310+
311+ @XmlRootElement(name = " personWithAttr" )
312+ @XmlAccessorType(XmlAccessType .FIELD )
313+ open class PersonWithAttr (
314+ @XmlAttribute(name = " id" )
315+ var id : String = " " ,
316+ var name : String = " " ,
317+ var age : Int = 0
318+ )
319+
320+ @XmlRootElement(name = " project" )
321+ @XmlAccessorType(XmlAccessType .FIELD )
322+ open class Project (
323+ @XmlAttribute(name = " code" )
324+ var code : String = " " ,
325+ @field:XmlElement(name = "PersonWithAttr ", namespace = "")
326+ var members : MutableList <PersonWithAttr > = mutableListOf()
327+ )
328+
329+ @XmlRootElement(name = " projectList" )
330+ @XmlAccessorType(XmlAccessType .FIELD )
331+ open class ProjectList (
332+ @field:XmlElement(name = "Project ", namespace = "")
333+ var projects : MutableList <Project > = mutableListOf()
334+ )
0 commit comments