Path: ...!Xl.tags.giganews.com!local-1.nntp.ord.giganews.com!news.giganews.com.POSTED!not-for-mail NNTP-Posting-Date: Fri, 19 Apr 2024 20:48:06 +0000 Subject: Re: Java HTTP API Signatures, schemas, XML and JSON, Pojo's and Poji's, Java and Spring and schema Newsgroups: comp.lang.java.programmer References: <2imdnULqbvFsBr_7nZ2dnZfqn_qdnZ2d@giganews.com> From: Ross Finlayson Date: Fri, 19 Apr 2024 13:48:59 -0700 User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:38.0) Gecko/20100101 Thunderbird/38.6.0 MIME-Version: 1.0 In-Reply-To: Content-Type: text/plain; charset=utf-8; format=flowed Content-Transfer-Encoding: 7bit Message-ID: Lines: 1040 X-Usenet-Provider: http://www.giganews.com X-Trace: sv3-wlY3y4b5ff9DZj4bNgC3TR7Hx6j4mDdD5xcRWGCjiC5zTla7KW6nYjVxSoQ3dPTPVtuXrxBKmfMi/Sg!2r0zxyPu/46X4b7Avgs0aR3uS7fS17wzHkJc/E4F0Rybufgk2UmuG2OSxqrNR7nfmbh/g9hdA0Lp!8g== X-Complaints-To: abuse@giganews.com X-DMCA-Notifications: http://www.giganews.com/info/dmca.html X-Abuse-and-DMCA-Info: Please be sure to forward a copy of ALL headers X-Abuse-and-DMCA-Info: Otherwise we will be unable to process your complaint properly X-Postfilter: 1.3.40 Bytes: 41091 On 04/19/2024 11:49 AM, Ross Finlayson wrote: > On 04/19/2024 10:22 AM, Ross Finlayson wrote: >> On 04/19/2024 09:30 AM, Ross Finlayson wrote: >>> Signature API >>> >>> ccc.api >>> ccc.api.exception >>> ccc.api.fault >>> >>> ccc.api.client >>> >>> ccc.springsup.web >>> >>> ccc.serdes >>> >>> >>> Api ApiMapping >>> >>> ApiFlt ApiHardFlt >>> OtherCauseFault CovariantFault >>> >>> Pth Hdr Qpa Req Rsp Flt Err >>> >>> >>> The other day I was writing a Spring Boot server, >>> and the implementation of the Api looks like this >>> >>> For a service Srv, group of APIs Grp, and API Api >>> >>> public interface Srv { >>> interface Grp { >>> interface Api extends ccc.api.Api { >>> >>> static class Pth{} >>> static class Qpa{} >>> static class Hdr{} >>> static class Req{} >>> static class Rsp{} >>> static class Flt{} >>> static class Err{} >>> >>> >>> Rsp api( >>> @Valid @NotNull Pth pth, >>> @Valid @NotNull Qpa qpa, >>> @Valid @NotNull Hdr hdr, >>> @Valid @NotNull Req req >>> ) throws ApiFlt; >>> >>> } >>> } >>> >>> } >>> >>> The validation annotations have to be on the root type, >>> so, they go on the Api. >>> >>> public interface SrvMapping { >>> interface Grp { >>> @RequestMapping >>> interface Api extends Srv.Grp.Api, ApiMapping { >>> >>> @Override >>> @GetMapping("/api/{pth}") >>> @ResponseBody >>> Rsp api( >>> @PathVariable Pth pth, >>> @QueryParam Qpa qpa, >>> @RequestHeader Hdr hdr, >>> @RequestBody Req req >>> ) throws ApiFlt; >>> >>> } >>> } >>> } >>> >>> The request mapping and also the openapi or documentation >>> interfaces have to be on the interface the controller implements, >>> and they don't mean anything to the Api, so they go on the ApiMapping. >>> >>> @Controller >>> public class SrvController implements SrvMapping.Grp.Api >>> { >>> @Override >>> Srv.Grp.Rsp api( >>> Srv.Grp.Pth pth, >>> Srv.Grp.Qpa qpa, >>> Srv.Grp.Hdr hdr, >>> Srv.Grp.Req req >>> ) throws ApiFlt; >>> } >>> >>> The controller just implements the Api, Spring Web wires >>> it up and Openapi documents it up. >>> >>> And it results sort of thusly a very simple organization of APIs. >>> >>> public interface Srv { >>> >>> interface Grp1 { >>> interface Api1 { /* */ } >>> interface Api2 { /* */ } >>> interface Api3 { /* */ } >>> interface Api4 { /* */ } >>> } >>> interface Grp@ { >>> interface Api1 { /* */ } >>> interface Api2 { /* */ } >>> interface Api3 { /* */ } >>> interface Api4 { /* */ } >>> } >>> /* */ >>> } >>> >>> The key is that given those, the Api and ApiMapping an entire >>> Signature, of the mapping, sort of results, with adding this >>> sort of ApiStatusMapping, to relate the HTTP status codes, >>> with, the covariant return bodies, then it's sort of all one thing. >>> >>> ("a functional interface may still have multiple default methods ...") >>> >>> The idea is that the interfaces carry very well down from these. >>> >>> For most often usual "APIs" these days it'd often look: >>> >>> @Data >>> static class MyGrp { >>> >>> } >>> >>> public interface Srv { >>> interface Grp { >>> interface Get1 extends ccc.api.Api { >>> >>> static class Pth{ String myGrpId;} >>> static class Req extends MyGrp{}; >>> static class Rsp{} >>> static class Flt{} >>> static class Err{} >>> >>> Rsp get1( >>> @Valid @NotNull Pth pth, >>> @Valid @NotNull Req req >>> ) throws ApiFlt; >>> >>> } >>> interface Put1 extends ccc.api.Api { >>> >>> static class Pth{ String myGrpId;} >>> static class Req extends MyGrp{} >>> static class Rsp{} >>> static class Flt{} >>> static class Err{} >>> >>> Rsp put1( >>> @Valid @NotNull Pth pth, >>> @Valid @NotNull Req req >>> ) throws ApiFlt; >>> >>> } >>> } >>> >>> } >>> >>> >>> public interface SrvMapping { >>> interface Grp { >>> @RequestMapping >>> interface Api extends Srv.Grp.Get1, ApiMapping { >>> >>> @Override >>> @GetMapping("/grp/{myGrpId}") >>> @ResponseBody ========== REMAINDER OF ARTICLE TRUNCATED ==========