diff --git a/templates/api.mustache b/templates/api.mustache new file mode 100644 index 00000000..9d0dc1da --- /dev/null +++ b/templates/api.mustache @@ -0,0 +1,96 @@ +package {{package}} + +{{#imports}}import {{import}} +{{/imports}} +{{#swagger2AnnotationLibrary}} + import io.swagger.v3.oas.annotations.* + import io.swagger.v3.oas.annotations.enums.* + import io.swagger.v3.oas.annotations.media.* + import io.swagger.v3.oas.annotations.responses.* + import io.swagger.v3.oas.annotations.security.* +{{/swagger2AnnotationLibrary}} +{{#swagger1AnnotationLibrary}} + import io.swagger.annotations.Api + import io.swagger.annotations.ApiOperation + import io.swagger.annotations.ApiParam + import io.swagger.annotations.ApiResponse + import io.swagger.annotations.ApiResponses + import io.swagger.annotations.Authorization + import io.swagger.annotations.AuthorizationScope +{{/swagger1AnnotationLibrary}} +import org.springframework.http.HttpStatus +import org.springframework.http.MediaType +import org.springframework.http.ResponseEntity + +import org.springframework.web.bind.annotation.* +{{#useBeanValidation}} + import org.springframework.validation.annotation.Validated +{{/useBeanValidation}} +import org.springframework.web.context.request.NativeWebRequest +import org.springframework.beans.factory.annotation.Autowired +import dev.usbharu.hideout.config.JsonOrFormBind + +{{#useBeanValidation}} + import {{javaxPackage}}.validation.Valid + import {{javaxPackage}}.validation.constraints.DecimalMax + import {{javaxPackage}}.validation.constraints.DecimalMin + import {{javaxPackage}}.validation.constraints.Email + import {{javaxPackage}}.validation.constraints.Max + import {{javaxPackage}}.validation.constraints.Min + import {{javaxPackage}}.validation.constraints.NotNull + import {{javaxPackage}}.validation.constraints.Pattern + import {{javaxPackage}}.validation.constraints.Size +{{/useBeanValidation}} + +{{#reactive}} + import kotlinx.coroutines.flow.Flow +{{/reactive}} +import kotlin.collections.List +import kotlin.collections.Map + +@RestController{{#beanQualifiers}}("{{package}}.{{classname}}Controller"){{/beanQualifiers}} +{{#useBeanValidation}} + @Validated +{{/useBeanValidation}} +{{#swagger1AnnotationLibrary}} + @Api(value = "{{{baseName}}}", description = "The {{{baseName}}} API") +{{/swagger1AnnotationLibrary}} +{{=<% %>=}} +@RequestMapping("\${api.base-path:<%contextPath%>}") +<%={{ }}=%> +{{#operations}} + class {{classname}}Controller({{#serviceInterface}}@Autowired(required = true) val service: {{classname}}Service{{/serviceInterface}}) { + {{#operation}} + + {{#swagger2AnnotationLibrary}} + @Operation( + summary = "{{{summary}}}", + operationId = "{{{operationId}}}", + description = """{{{unescapedNotes}}}""", + responses = [{{#responses}} + ApiResponse(responseCode = "{{{code}}}", description = "{{{message}}}"{{#baseType}}, content = [Content({{#isArray}}array = ArraySchema({{/isArray}}schema = Schema(implementation = {{{baseType}}}::class)){{#isArray}}){{/isArray}}]{{/baseType}}){{^-last}},{{/-last}}{{/responses}} ]{{#hasAuthMethods}}, + security = [ {{#authMethods}}SecurityRequirement(name = "{{name}}"{{#isOAuth}}, scopes = [ {{#scopes}}"{{scope}}"{{^-last}}, {{/-last}}{{/scopes}} ]{{/isOAuth}}){{^-last}},{{/-last}}{{/authMethods}} ]{{/hasAuthMethods}} + ){{/swagger2AnnotationLibrary}}{{#swagger1AnnotationLibrary}} + @ApiOperation( + value = "{{{summary}}}", + nickname = "{{{operationId}}}", + notes = "{{{notes}}}"{{#returnBaseType}}, + response = {{{.}}}::class{{/returnBaseType}}{{#returnContainer}}, + responseContainer = "{{{.}}}"{{/returnContainer}}{{#hasAuthMethods}}, + authorizations = [{{#authMethods}}Authorization(value = "{{name}}"{{#isOAuth}}, scopes = [{{#scopes}}AuthorizationScope(scope = "{{scope}}", description = "{{description}}"){{^-last}}, {{/-last}}{{/scopes}}]{{/isOAuth}}){{^-last}}, {{/-last}}{{/authMethods}}]{{/hasAuthMethods}}) + @ApiResponses( + value = [{{#responses}}ApiResponse(code = {{{code}}}, message = "{{{message}}}"{{#baseType}}, response = {{{.}}}::class{{/baseType}}{{#containerType}}, responseContainer = "{{{.}}}"{{/containerType}}){{^-last}},{{/-last}}{{/responses}}]){{/swagger1AnnotationLibrary}} + @RequestMapping( + method = [RequestMethod.{{httpMethod}}], + value = ["{{#lambda.escapeDoubleQuote}}{{path}}{{/lambda.escapeDoubleQuote}}"]{{#singleContentTypes}}{{#hasProduces}}, + produces = "{{{vendorExtensions.x-accepts}}}"{{/hasProduces}}{{#hasConsumes}}, + consumes = "{{{vendorExtensions.x-content-type}}}"{{/hasConsumes}}{{/singleContentTypes}}{{^singleContentTypes}}{{#hasProduces}}, + produces = [{{#produces}}"{{{mediaType}}}"{{^-last}}, {{/-last}}{{/produces}}]{{/hasProduces}}{{#hasConsumes}}, + consumes = [{{#consumes}}"{{{mediaType}}}"{{^-last}}, {{/-last}}{{/consumes}}]{{/hasConsumes}}{{/singleContentTypes}} + ) + {{#reactive}}{{^isArray}}suspend {{/isArray}}{{/reactive}}fun {{operationId}}({{#allParams}}{{>queryParams}}{{>pathParams}}{{>headerParams}}{{>bodyParams}}{{>formParams}}{{^-last}},{{/-last}}{{/allParams}}): ResponseEntity<{{>returnTypes}}> { + return {{>returnValue}} + } + {{/operation}} + } +{{/operations}} diff --git a/templates/apiController.mustache b/templates/apiController.mustache new file mode 100644 index 00000000..3c70a76f --- /dev/null +++ b/templates/apiController.mustache @@ -0,0 +1,25 @@ +package {{package}} + +import org.springframework.stereotype.Controller +import org.springframework.web.bind.annotation.RequestMapping +import java.util.Optional +import dev.usbharu.hideout.config.JsonOrFormBind + +{{>generatedAnnotation}} +@Controller{{#beanQualifiers}}("{{package}}.{{classname}}Controller"){{/beanQualifiers}} +{{=<% %>=}} +@RequestMapping("\${openapi.<%title%>.base-path:<%>defaultBasePath%>}") +<%={{ }}=%> +{{#operations}} + class {{classname}}Controller( + @org.springframework.beans.factory.annotation.Autowired(required = false) delegate: {{classname}}Delegate? + ) : {{classname}} { + private val delegate: {{classname}}Delegate + + init { + this.delegate = Optional.ofNullable(delegate).orElse(object : {{classname}}Delegate {}) + } + + override fun getDelegate(): {{classname}}Delegate = delegate + } +{{/operations}} diff --git a/templates/apiDelegate.mustache b/templates/apiDelegate.mustache new file mode 100644 index 00000000..e7d62ddb --- /dev/null +++ b/templates/apiDelegate.mustache @@ -0,0 +1,46 @@ +package {{package}} + +{{#imports}}import {{import}} +{{/imports}} +import org.springframework.http.HttpStatus +import org.springframework.http.MediaType +import org.springframework.http.ResponseEntity +import org.springframework.web.context.request.NativeWebRequest +import org.springframework.core.io.Resource +{{#reactive}} + import kotlinx.coroutines.flow.Flow +{{/reactive}} + +import java.util.Optional +{{#async}} + import java.util.concurrent.CompletableFuture +{{/async}} + +{{#operations}} + /** + * A delegate to be called by the {@link {{classname}}Controller}}. + * Implement this interface with a {@link org.springframework.stereotype.Service} annotated class. + */ + {{>generatedAnnotation}} + interface {{classname}}Delegate { + + fun getRequest(): Optional + = Optional.empty() + {{#operation}} + + /** + * @see {{classname}}#{{operationId}} + */ + {{#reactive}}{{^isArray}}suspend {{/isArray}}{{/reactive}}fun {{operationId}}({{#allParams}}{{paramName}} + : {{^isFile}}{{^reactive}}{{>optionalDataType}}{{/reactive}}{{#reactive}}{{^isArray}}{{>optionalDataType}}{{/isArray}}{{#isArray}}{{#isBodyParam}} + Flow<{{{baseType}}} + >{{/isBodyParam}}{{^isBodyParam}}{{>optionalDataType}}{{/isBodyParam}}{{/isArray}}{{/reactive}}{{/isFile}}{{#isFile}} + Resource?{{/isFile}}{{^-last}}, + {{/-last}}{{/allParams}}): {{#responseWrapper}}{{.}} + <{{/responseWrapper}}ResponseEntity<{{>returnTypes}}>{{#responseWrapper}}>{{/responseWrapper}} { + {{>methodBody}} + } + + {{/operation}} + } +{{/operations}} diff --git a/templates/apiInterface.mustache b/templates/apiInterface.mustache new file mode 100644 index 00000000..ca99383c --- /dev/null +++ b/templates/apiInterface.mustache @@ -0,0 +1,112 @@ +/** +* NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech) ({{{generatorVersion}}}). +* https://openapi-generator.tech +* Do not edit the class manually. +*/ +package {{package}} + +{{#imports}}import {{import}} +{{/imports}} +{{#swagger2AnnotationLibrary}} + import io.swagger.v3.oas.annotations.* + import io.swagger.v3.oas.annotations.enums.* + import io.swagger.v3.oas.annotations.media.* + import io.swagger.v3.oas.annotations.responses.* + import io.swagger.v3.oas.annotations.security.* +{{/swagger2AnnotationLibrary}} +{{#swagger1AnnotationLibrary}} + import io.swagger.annotations.Api + import io.swagger.annotations.ApiOperation + import io.swagger.annotations.ApiParam + import io.swagger.annotations.ApiResponse + import io.swagger.annotations.ApiResponses + import io.swagger.annotations.Authorization + import io.swagger.annotations.AuthorizationScope +{{/swagger1AnnotationLibrary}} +import org.springframework.http.HttpStatus +import org.springframework.http.MediaType +import org.springframework.http.ResponseEntity + +import org.springframework.web.bind.annotation.* +{{#useBeanValidation}} + import org.springframework.validation.annotation.Validated +{{/useBeanValidation}} +import org.springframework.web.context.request.NativeWebRequest +import org.springframework.beans.factory.annotation.Autowired + +{{#useBeanValidation}} + import {{javaxPackage}}.validation.constraints.DecimalMax + import {{javaxPackage}}.validation.constraints.DecimalMin + import {{javaxPackage}}.validation.constraints.Email + import {{javaxPackage}}.validation.constraints.Max + import {{javaxPackage}}.validation.constraints.Min + import {{javaxPackage}}.validation.constraints.NotNull + import {{javaxPackage}}.validation.constraints.Pattern + import {{javaxPackage}}.validation.constraints.Size + import {{javaxPackage}}.validation.Valid +{{/useBeanValidation}} + +{{#reactive}} + import kotlinx.coroutines.flow.Flow +{{/reactive}} +import kotlin.collections.List +import kotlin.collections.Map +import dev.usbharu.hideout.config.JsonOrFormBind + +{{#useBeanValidation}} + @Validated +{{/useBeanValidation}} +{{#swagger1AnnotationLibrary}} + @Api(value = "{{{baseName}}}", description = "The {{{baseName}}} API") +{{/swagger1AnnotationLibrary}} +{{^useFeignClient}} + {{=<% %>=}} + @RequestMapping("\${api.base-path:<%contextPath%>}") + <%={{ }}=%> +{{/useFeignClient}} +{{#operations}} + interface {{classname}} { + {{#isDelegate}} + + fun getDelegate(): {{classname}}Delegate = object: {{classname}}Delegate {} + {{/isDelegate}} + {{#operation}} + + {{#swagger2AnnotationLibrary}} + @Operation( + summary = "{{{summary}}}", + operationId = "{{{operationId}}}", + description = """{{{unescapedNotes}}}""", + responses = [{{#responses}} + ApiResponse(responseCode = "{{{code}}}", description = "{{{message}}}"{{#baseType}}, content = [Content({{#isArray}}array = ArraySchema({{/isArray}}schema = Schema(implementation = {{{baseType}}}::class)){{#isArray}}){{/isArray}}]{{/baseType}}){{^-last}},{{/-last}}{{/responses}} + ]{{#hasAuthMethods}}, + security = [ {{#authMethods}}SecurityRequirement(name = "{{name}}"{{#isOAuth}}, scopes = [ {{#scopes}}"{{scope}}"{{^-last}}, {{/-last}}{{/scopes}} ]{{/isOAuth}}){{^-last}},{{/-last}}{{/authMethods}} ]{{/hasAuthMethods}} + ){{/swagger2AnnotationLibrary}}{{#swagger1AnnotationLibrary}} + @ApiOperation( + value = "{{{summary}}}", + nickname = "{{{operationId}}}", + notes = "{{{notes}}}"{{#returnBaseType}}, + response = {{{.}}}::class{{/returnBaseType}}{{#returnContainer}}, + responseContainer = "{{{.}}}"{{/returnContainer}}{{#hasAuthMethods}}, + authorizations = [{{#authMethods}}Authorization(value = "{{name}}"{{#isOAuth}}, scopes = [{{#scopes}}AuthorizationScope(scope = "{{scope}}", description = "{{description}}"){{^-last}}, {{/-last}}{{/scopes}}]{{/isOAuth}}){{^-last}}, {{/-last}}{{/authMethods}}]{{/hasAuthMethods}}) + @ApiResponses( + value = [{{#responses}}ApiResponse(code = {{{code}}}, message = "{{{message}}}"{{#baseType}}, response = {{{.}}}::class{{/baseType}}{{#containerType}}, responseContainer = "{{{.}}}"{{/containerType}}){{^-last}}, {{/-last}}{{/responses}}]){{/swagger1AnnotationLibrary}} + @RequestMapping( + method = [RequestMethod.{{httpMethod}}], + value = ["{{#lambda.escapeDoubleQuote}}{{path}}{{/lambda.escapeDoubleQuote}}"]{{#singleContentTypes}}{{#hasProduces}}, + produces = "{{{vendorExtensions.x-accepts}}}"{{/hasProduces}}{{#hasConsumes}}, + consumes = "{{{vendorExtensions.x-content-type}}}"{{/hasConsumes}}{{/singleContentTypes}}{{^singleContentTypes}}{{#hasProduces}}, + produces = [{{#produces}}"{{{mediaType}}}"{{^-last}}, {{/-last}}{{/produces}}]{{/hasProduces}}{{#hasConsumes}}, + consumes = [{{#consumes}}"{{{mediaType}}}"{{^-last}}, {{/-last}}{{/consumes}}]{{/hasConsumes}}{{/singleContentTypes}} + ) + {{#reactive}}{{^isArray}}suspend {{/isArray}}{{/reactive}}fun {{operationId}}({{#allParams}}{{>queryParams}}{{>pathParams}}{{>headerParams}}{{>bodyParams}}{{>formParams}}{{^-last}},{{/-last}}{{/allParams}}): ResponseEntity<{{>returnTypes}}>{{^skipDefaultInterface}} { + {{^isDelegate}} + return {{>returnValue}} + {{/isDelegate}} + {{#isDelegate}} + return getDelegate().{{operationId}}({{#allParams}}{{paramName}}{{^-last}}, {{/-last}}{{/allParams}}) + {{/isDelegate}} + }{{/skipDefaultInterface}} + {{/operation}} + } +{{/operations}} diff --git a/templates/apiUtil.mustache b/templates/apiUtil.mustache new file mode 100644 index 00000000..101ed40e --- /dev/null +++ b/templates/apiUtil.mustache @@ -0,0 +1,23 @@ +package {{apiPackage}} + +{{^reactive}} + import org.springframework.web.context.request.NativeWebRequest + + import {{javaxPackage}}.servlet.http.HttpServletResponse + import java.io.IOException +{{/reactive}} + +object ApiUtil { +{{^reactive}} + fun setExampleResponse(req: NativeWebRequest, contentType: String, example: String) { + try { + val res = req.getNativeResponse(HttpServletResponse::class.java) + res?.characterEncoding = "UTF-8" + res?.addHeader("Content-Type", contentType) + res?.writer?.print(example) + } catch (e: IOException) { + throw RuntimeException(e) + } + } +{{/reactive}} +} diff --git a/templates/api_test.mustache b/templates/api_test.mustache new file mode 100644 index 00000000..d84af783 --- /dev/null +++ b/templates/api_test.mustache @@ -0,0 +1,38 @@ +package {{package}} + +{{#imports}}import {{import}} +{{/imports}} +import org.junit.jupiter.api.Test +{{#reactive}} + import kotlinx.coroutines.flow.Flow + import kotlinx.coroutines.test.runBlockingTest +{{/reactive}} +import org.springframework.http.ResponseEntity + +class {{classname}}Test { + +{{#serviceInterface}} + private val service: {{classname}}Service = {{classname}}ServiceImpl() +{{/serviceInterface}} +private val api: {{classname}}Controller = {{classname}}Controller({{#serviceInterface}}service{{/serviceInterface}}) +{{#operations}} + {{#operation}} + + /** + * To test {{classname}}Controller.{{operationId}} + * + * @throws ApiException + * if the Api call fails + */ + @Test + fun {{operationId}}Test() {{#reactive}}= runBlockingTest {{/reactive}}{ + {{#allParams}} + val {{{paramName}}}: {{>optionalDataType}} = TODO() + {{/allParams}} + val response: ResponseEntity<{{>returnTypes}}> = api.{{operationId}}({{#allParams}}{{{paramName}}}{{^-last}}, {{/-last}}{{/allParams}}) + + // TODO: test validations + } + {{/operation}} +{{/operations}} +} diff --git a/templates/beanValidation.mustache b/templates/beanValidation.mustache new file mode 100644 index 00000000..ee25df4a --- /dev/null +++ b/templates/beanValidation.mustache @@ -0,0 +1,4 @@ +{{#isContainer}}{{^isPrimitiveType}}{{^isEnum}} + @field:Valid{{/isEnum}}{{/isPrimitiveType}}{{/isContainer}}{{! +}}{{^isContainer}}{{^isPrimitiveType}}{{^isNumber}}{{^isUuid}}{{^isDateTime}} + @field:Valid{{/isDateTime}}{{/isUuid}}{{/isNumber}}{{/isPrimitiveType}}{{/isContainer}} \ No newline at end of file diff --git a/templates/beanValidationModel.mustache b/templates/beanValidationModel.mustache new file mode 100644 index 00000000..99635314 --- /dev/null +++ b/templates/beanValidationModel.mustache @@ -0,0 +1,38 @@ +{{! +format: email +}}{{#isEmail}} + @get:Email{{/isEmail}}{{! +pattern set +}}{{#pattern}} + @get:Pattern(regexp="{{{.}}}"){{/pattern}}{{! +minLength && maxLength set +}}{{#minLength}}{{#maxLength}} + @get:Size(min={{minLength}},max={{maxLength}}){{/maxLength}}{{/minLength}}{{! +minLength set, maxLength not +}}{{#minLength}}{{^maxLength}} + @get:Size(min={{minLength}}){{/maxLength}}{{/minLength}}{{! +minLength not set, maxLength set +}}{{^minLength}}{{#maxLength}} + @get:Size(max={{.}}){{/maxLength}}{{/minLength}}{{! +@Size: minItems && maxItems set +}}{{#minItems}}{{#maxItems}} + @get:Size(min={{minItems}},max={{maxItems}}) {{/maxItems}}{{/minItems}}{{! +@Size: minItems set, maxItems not +}}{{#minItems}}{{^maxItems}} + @get:Size(min={{minItems}}){{/maxItems}}{{/minItems}}{{! +@Size: minItems not set && maxItems set +}}{{^minItems}}{{#maxItems}} + @get:Size(max={{.}}){{/maxItems}}{{/minItems}}{{! +check for integer or long / all others=decimal type with @Decimal* +isInteger set +}}{{#isInteger}}{{#minimum}} + @get:Min({{.}}){{/minimum}}{{#maximum}} + @get:Max({{.}}){{/maximum}}{{/isInteger}}{{! +isLong set +}}{{#isLong}}{{#minimum}} + @get:Min({{.}}L){{/minimum}}{{#maximum}} + @get:Max({{.}}L){{/maximum}}{{/isLong}}{{! +Not Integer, not Long => we have a decimal value! +}}{{^isInteger}}{{^isLong}}{{#minimum}} + @get:DecimalMin("{{.}}"){{/minimum}}{{#maximum}} + @get:DecimalMax("{{.}}"){{/maximum}}{{/isLong}}{{/isInteger}} \ No newline at end of file diff --git a/templates/beanValidationPath.mustache b/templates/beanValidationPath.mustache new file mode 100644 index 00000000..8eb9029b --- /dev/null +++ b/templates/beanValidationPath.mustache @@ -0,0 +1,22 @@ +{{#isEmail}}@Email {{/isEmail}}{{! +pattern set +}}{{#pattern}}@Pattern(regexp="{{{.}}}") {{/pattern}}{{! +minLength && maxLength set +}}{{#minLength}}{{#maxLength}}@Size(min={{minLength}},max={{maxLength}}) {{/maxLength}}{{/minLength}}{{! +minLength set, maxLength not +}}{{#minLength}}{{^maxLength}}@Size(min={{minLength}}) {{/maxLength}}{{/minLength}}{{! +minLength not set, maxLength set +}}{{^minLength}}{{#maxLength}}@Size(max={{.}}) {{/maxLength}}{{/minLength}}{{! +@Size: minItems && maxItems set +}}{{#minItems}}{{#maxItems}}@Size(min={{minItems}},max={{maxItems}}) {{/maxItems}}{{/minItems}}{{! +@Size: minItems set, maxItems not +}}{{#minItems}}{{^maxItems}}@Size(min={{minItems}}) {{/maxItems}}{{/minItems}}{{! +@Size: minItems not set && maxItems set +}}{{^minItems}}{{#maxItems}}@Size(max={{.}}) {{/maxItems}}{{/minItems}}{{! +check for integer or long / all others=decimal type with @Decimal* +isInteger set +}}{{#isInteger}}{{#minimum}}@Min({{.}}){{/minimum}}{{#maximum}} @Max({{.}}) {{/maximum}}{{/isInteger}}{{! +isLong set +}}{{#isLong}}{{#minimum}}@Min({{.}}L){{/minimum}}{{#maximum}} @Max({{.}}L) {{/maximum}}{{/isLong}}{{! +Not Integer, not Long => we have a decimal value! +}}{{^isInteger}}{{^isLong}}{{#minimum}}@DecimalMin("{{.}}"){{/minimum}}{{#maximum}} @DecimalMax("{{.}}") {{/maximum}}{{/isLong}}{{/isInteger}} \ No newline at end of file diff --git a/templates/beanValidationPathParams.mustache b/templates/beanValidationPathParams.mustache new file mode 100644 index 00000000..3c57e76b --- /dev/null +++ b/templates/beanValidationPathParams.mustache @@ -0,0 +1 @@ +{{! PathParam is always required, no @NotNull necessary }}{{>beanValidationPath}} \ No newline at end of file diff --git a/templates/beanValidationQueryParams.mustache b/templates/beanValidationQueryParams.mustache new file mode 100644 index 00000000..cc53bc96 --- /dev/null +++ b/templates/beanValidationQueryParams.mustache @@ -0,0 +1 @@ +{{#required}}@NotNull {{/required}}{{>beanValidationPath}} \ No newline at end of file diff --git a/templates/bodyParams.mustache b/templates/bodyParams.mustache new file mode 100644 index 00000000..723e7d7f --- /dev/null +++ b/templates/bodyParams.mustache @@ -0,0 +1 @@ +{{#isBodyParam}}{{#swagger2AnnotationLibrary}}@Parameter(description = "{{{description}}}"{{#required}}, required = true{{/required}}{{^isContainer}}{{#allowableValues}}{{#defaultValue}}, schema = Schema(allowableValues = ["{{{allowableValues}}}"], defaultValue = {{^isString}}"{{/isString}}{{#isString}}{{#isEnum}}"{{/isEnum}}{{/isString}}{{{defaultValue}}}{{^isString}}"{{/isString}}{{#isString}}{{#isEnum}}"{{/isEnum}}{{/isString}}){{/defaultValue}}{{/allowableValues}}{{^allowableValues}}{{#defaultValue}}, schema = Schema(defaultValue = {{^isString}}"{{/isString}}{{#isString}}{{#isEnum}}"{{/isEnum}}{{/isString}}{{{defaultValue}}}{{^isString}}"{{/isString}}{{#isString}}{{#isEnum}}"{{/isEnum}}{{/isString}}){{/defaultValue}}{{/allowableValues}}{{#allowableValues}}{{^defaultValue}}, schema = Schema(allowableValues = ["{{{allowableValues}}}"]){{/defaultValue}}{{/allowableValues}}{{/isContainer}}){{/swagger2AnnotationLibrary}}{{#swagger1AnnotationLibrary}}@ApiParam(value = "{{{description}}}"{{#required}}, required = true{{/required}}{{^isContainer}}{{#allowableValues}}, allowableValues = "{{{.}}}"{{/allowableValues}}{{/isContainer}}{{#defaultValue}}, defaultValue = "{{{.}}}"{{/defaultValue}}){{/swagger1AnnotationLibrary}} {{#useBeanValidation}}@Valid{{/useBeanValidation}} @JsonOrForm {{{paramName}}}: {{^reactive}}{{>optionalDataType}}{{/reactive}}{{#reactive}}{{^isArray}}{{>optionalDataType}}{{/isArray}}{{#isArray}}Flow<{{{baseType}}}>{{/isArray}}{{/reactive}}{{/isBodyParam}} diff --git a/templates/dataClass.mustache b/templates/dataClass.mustache new file mode 100644 index 00000000..0fb78397 --- /dev/null +++ b/templates/dataClass.mustache @@ -0,0 +1,33 @@ +/** +* {{{description}}} +{{#vars}} + * @param {{name}} {{{description}}} +{{/vars}} +*/{{#discriminator}} + {{>typeInfoAnnotation}}{{/discriminator}} +{{#discriminator}}interface {{classname}}{{/discriminator}}{{^discriminator}}{{#hasVars}}data {{/hasVars}}class {{classname}}( +{{#requiredVars}} + {{>dataClassReqVar}}{{^-last}}, + {{/-last}}{{/requiredVars}}{{#hasRequired}}{{#hasOptional}}, +{{/hasOptional}}{{/hasRequired}}{{#optionalVars}}{{>dataClassOptVar}}{{^-last}}, +{{/-last}}{{/optionalVars}} +) {{/discriminator}}{{#parent}}: {{{.}}}{{/parent}}{ +{{#discriminator}} + {{#requiredVars}} + {{>interfaceReqVar}} + {{/requiredVars}} + {{#optionalVars}} + {{>interfaceOptVar}} + {{/optionalVars}} +{{/discriminator}} +{{#hasEnums}}{{#vars}}{{#isEnum}} + /** + * {{{description}}} + * Values: {{#allowableValues}}{{#enumVars}}{{&name}}{{^-last}},{{/-last}}{{/enumVars}}{{/allowableValues}} + */ + enum class {{{nameInCamelCase}}}(val value: {{#isContainer}}{{#items}}{{{dataType}}}{{/items}}{{/isContainer}}{{^isContainer}}{{{dataType}}}{{/isContainer}}) { + {{#allowableValues}}{{#enumVars}} + @JsonProperty({{{value}}}) {{{name}}}({{{value}}}){{^-last}},{{/-last}}{{/enumVars}}{{/allowableValues}} + } +{{/isEnum}}{{/vars}}{{/hasEnums}} +} diff --git a/templates/dataClassOptVar.mustache b/templates/dataClassOptVar.mustache new file mode 100644 index 00000000..3ba523bb --- /dev/null +++ b/templates/dataClassOptVar.mustache @@ -0,0 +1,5 @@ +{{#useBeanValidation}}{{>beanValidation}}{{>beanValidationModel}}{{/useBeanValidation}}{{#swagger2AnnotationLibrary}} + @Schema({{#example}}example = "{{#lambdaRemoveLineBreak}}{{#lambdaEscapeDoubleQuote}}{{{.}}}{{/lambdaEscapeDoubleQuote}}{{/lambdaRemoveLineBreak}}", {{/example}}{{#isReadOnly}}readOnly = {{{isReadOnly}}}, {{/isReadOnly}}description = "{{{description}}}"){{/swagger2AnnotationLibrary}}{{#swagger1AnnotationLibrary}} + @ApiModelProperty({{#example}}example = "{{#lambdaRemoveLineBreak}}{{#lambdaEscapeDoubleQuote}}{{{.}}}{{/lambdaEscapeDoubleQuote}}{{/lambdaRemoveLineBreak}}", {{/example}}{{#isReadOnly}}readOnly = {{{isReadOnly}}}, {{/isReadOnly}}value = "{{{description}}}"){{/swagger1AnnotationLibrary}}{{#deprecated}} + @Deprecated(message = ""){{/deprecated}} +@get:JsonProperty("{{{baseName}}}"){{#isInherited}} override{{/isInherited}} {{>modelMutable}} {{{name}}}: {{#isEnum}}{{#isArray}}{{baseType}}<{{/isArray}}{{classname}}.{{{nameInCamelCase}}}{{#isArray}}>{{/isArray}}{{/isEnum}}{{^isEnum}}{{{dataType}}}{{/isEnum}}? = {{{defaultValue}}}{{^defaultValue}}null{{/defaultValue}} diff --git a/templates/dataClassReqVar.mustache b/templates/dataClassReqVar.mustache new file mode 100644 index 00000000..1812c1cf --- /dev/null +++ b/templates/dataClassReqVar.mustache @@ -0,0 +1,4 @@ +{{#useBeanValidation}}{{>beanValidation}}{{>beanValidationModel}}{{/useBeanValidation}}{{#swagger2AnnotationLibrary}} + @Schema({{#example}}example = "{{#lambdaRemoveLineBreak}}{{#lambdaEscapeDoubleQuote}}{{{.}}}{{/lambdaEscapeDoubleQuote}}{{/lambdaRemoveLineBreak}}", {{/example}}required = true, {{#isReadOnly}}readOnly = {{{isReadOnly}}}, {{/isReadOnly}}description = "{{{description}}}"){{/swagger2AnnotationLibrary}}{{#swagger1AnnotationLibrary}} + @ApiModelProperty({{#example}}example = "{{#lambdaRemoveLineBreak}}{{#lambdaEscapeDoubleQuote}}{{{.}}}{{/lambdaEscapeDoubleQuote}}{{/lambdaRemoveLineBreak}}", {{/example}}required = true, {{#isReadOnly}}readOnly = {{{isReadOnly}}}, {{/isReadOnly}}value = "{{{description}}}"){{/swagger1AnnotationLibrary}} +@get:JsonProperty("{{{baseName}}}", required = true){{#isInherited}} override{{/isInherited}} {{>modelMutable}} {{{name}}}: {{#isEnum}}{{#isArray}}{{baseType}}<{{/isArray}}{{classname}}.{{{nameInCamelCase}}}{{#isArray}}>{{/isArray}}{{/isEnum}}{{^isEnum}}{{{dataType}}}{{/isEnum}}{{#isNullable}}?{{/isNullable}}{{#defaultValue}} = {{{.}}}{{/defaultValue}} diff --git a/templates/enumClass.mustache b/templates/enumClass.mustache new file mode 100644 index 00000000..57287535 --- /dev/null +++ b/templates/enumClass.mustache @@ -0,0 +1,8 @@ +/** +* {{{description}}} +* Values: {{#allowableValues}}{{#enumVars}}{{&name}}{{^-last}},{{/-last}}{{/enumVars}}{{/allowableValues}} +*/ +enum class {{classname}}(val value: {{dataType}}) { +{{#allowableValues}}{{#enumVars}} + @JsonProperty({{{value}}}) {{&name}}({{{value}}}){{^-last}},{{/-last}}{{/enumVars}}{{/allowableValues}} +} diff --git a/templates/exceptions.mustache b/templates/exceptions.mustache new file mode 100644 index 00000000..5a2a7aec --- /dev/null +++ b/templates/exceptions.mustache @@ -0,0 +1,29 @@ +package {{apiPackage}} + +import org.springframework.http.HttpStatus +import org.springframework.web.bind.annotation.ControllerAdvice +import org.springframework.web.bind.annotation.ExceptionHandler +import {{javaxPackage}}.servlet.http.HttpServletResponse +import {{javaxPackage}}.validation.ConstraintViolationException + +// TODO Extend ApiException for custom exception handling, e.g. the below NotFound exception +sealed class ApiException(msg: String, val code: Int) : Exception(msg) + +class NotFoundException(msg: String, code: Int = HttpStatus.NOT_FOUND.value()) : ApiException(msg, code) + + +@ControllerAdvice +class DefaultExceptionHandler { + +@ExceptionHandler(value = [ApiException::class]) +fun onApiException(ex: ApiException, response: HttpServletResponse): Unit = +response.sendError(ex.code, ex.message) + +@ExceptionHandler(value = [NotImplementedError::class]) +fun onNotImplemented(ex: NotImplementedError, response: HttpServletResponse): Unit = +response.sendError(HttpStatus.NOT_IMPLEMENTED.value()) + +@ExceptionHandler(value = [ConstraintViolationException::class]) +fun onConstraintViolation(ex: ConstraintViolationException, response: HttpServletResponse): Unit = +response.sendError(HttpStatus.BAD_REQUEST.value(), ex.constraintViolations.joinToString(", ") { it.message }) +} diff --git a/templates/formParams.mustache b/templates/formParams.mustache new file mode 100644 index 00000000..ec72b53b --- /dev/null +++ b/templates/formParams.mustache @@ -0,0 +1 @@ +{{#isFormParam}}{{^isFile}}{{#swagger2AnnotationLibrary}}@Parameter(description = "{{{description}}}"{{#required}}, required = true{{/required}}{{#allowableValues}}{{#defaultValue}}, schema = Schema(allowableValues = [{{#values}}"{{{.}}}"{{^-last}}, {{/-last}}{{/values}}]{{^isContainer}}, defaultValue = {{^isString}}"{{/isString}}{{#isString}}{{#isEnum}}"{{/isEnum}}{{/isString}}{{{defaultValue}}}{{^isString}}"{{/isString}}{{#isString}}{{#isEnum}}"{{/isEnum}}{{/isString}}{{/isContainer}}){{/defaultValue}}{{/allowableValues}}{{#allowableValues}}{{^defaultValue}}, schema = Schema(allowableValues = [{{#values}}"{{{.}}}"{{^-last}}, {{/-last}}{{/values}}]){{/defaultValue}}{{/allowableValues}}{{^allowableValues}}{{#defaultValue}}{{^isContainer}}, schema = Schema(defaultValue = {{^isString}}"{{/isString}}{{#isString}}{{#isEnum}}"{{/isEnum}}{{/isString}}{{{defaultValue}}}{{^isString}}"{{/isString}}{{#isString}}{{#isEnum}}"{{/isEnum}}{{/isString}}){{/isContainer}}{{/defaultValue}}{{/allowableValues}}){{/swagger2AnnotationLibrary}}{{#swagger1AnnotationLibrary}}@ApiParam(value = "{{{description}}}"{{#required}}, required = true{{/required}}{{#allowableValues}}, allowableValues = "{{#values}}{{{.}}}{{^-last}}, {{/-last}}{{/values}}"{{/allowableValues}}{{#defaultValue}}, defaultValue = "{{{.}}}"{{/defaultValue}}){{/swagger1AnnotationLibrary}} @RequestParam(value = "{{baseName}}"{{#required}}, required = true{{/required}}{{^required}}, required = false{{/required}}) {{paramName}}: {{>optionalDataType}} {{/isFile}}{{#isFile}}{{#swagger2AnnotationLibrary}}@Parameter(description = "file detail"){{/swagger2AnnotationLibrary}}{{#swagger1AnnotationLibrary}}@ApiParam(value = "file detail"){{/swagger1AnnotationLibrary}} {{#useBeanValidation}}@Valid{{/useBeanValidation}} @RequestPart("file") {{baseName}}: {{>optionalDataType}}{{/isFile}}{{/isFormParam}} \ No newline at end of file diff --git a/templates/generatedAnnotation.mustache b/templates/generatedAnnotation.mustache new file mode 100644 index 00000000..1be8e755 --- /dev/null +++ b/templates/generatedAnnotation.mustache @@ -0,0 +1 @@ +@{{javaxPackage}}.annotation.Generated(value = ["{{generatorClass}}"]{{^hideGenerationTimestamp}}, date = "{{generatedDate}}"{{/hideGenerationTimestamp}}) \ No newline at end of file diff --git a/templates/headerParams.mustache b/templates/headerParams.mustache new file mode 100644 index 00000000..9bc3f600 --- /dev/null +++ b/templates/headerParams.mustache @@ -0,0 +1 @@ +{{#isHeaderParam}}{{#useBeanValidation}}{{>beanValidationPath}}{{/useBeanValidation}}{{#swagger2AnnotationLibrary}}@Parameter(description = "{{{description}}}", `in` = ParameterIn.HEADER{{#required}}, required = true{{/required}}{{#allowableValues}}{{#defaultValue}}, schema = Schema(allowableValues = [{{#values}}"{{{.}}}"{{^-last}}, {{/-last}}{{/values}}]{{^isContainer}}, defaultValue = {{^isString}}"{{/isString}}{{#isString}}{{#isEnum}}"{{/isEnum}}{{/isString}}{{{defaultValue}}}{{^isString}}"{{/isString}}{{#isString}}{{#isEnum}}"{{/isEnum}}{{/isString}}{{/isContainer}}){{/defaultValue}}{{/allowableValues}}{{#allowableValues}}{{^defaultValue}}, schema = Schema(allowableValues = [{{#values}}"{{{.}}}"{{^-last}}, {{/-last}}{{/values}}]){{/defaultValue}}{{/allowableValues}}{{^allowableValues}}{{#defaultValue}}{{^isContainer}}, schema = Schema(defaultValue = {{^isString}}"{{/isString}}{{#isString}}{{#isEnum}}"{{/isEnum}}{{/isString}}{{{defaultValue}}}{{^isString}}"{{/isString}}{{#isString}}{{#isEnum}}"{{/isEnum}}{{/isString}}){{/isContainer}}{{/defaultValue}}{{/allowableValues}}){{/swagger2AnnotationLibrary}}{{#swagger1AnnotationLibrary}}@ApiParam(value = "{{{description}}}"{{#required}}, required = true{{/required}}{{#allowableValues}}, allowableValues = "{{#values}}{{{.}}}{{^-last}}, {{/-last}}{{/values}}"{{/allowableValues}}{{#defaultValue}}, defaultValue = "{{{.}}}"{{/defaultValue}}){{/swagger1AnnotationLibrary}} @RequestHeader(value = "{{baseName}}", required = {{#required}}true{{/required}}{{^required}}false{{/required}}) {{paramName}}: {{>optionalDataType}}{{/isHeaderParam}} \ No newline at end of file diff --git a/templates/homeController.mustache b/templates/homeController.mustache new file mode 100644 index 00000000..b9ab27dd --- /dev/null +++ b/templates/homeController.mustache @@ -0,0 +1,91 @@ +package {{basePackage}} + +import org.springframework.context.annotation.Bean +import org.springframework.stereotype.Controller +import org.springframework.web.bind.annotation.RequestMapping +{{#sourceDocumentationProvider}} + import com.fasterxml.jackson.dataformat.yaml.YAMLMapper + import org.springframework.beans.factory.annotation.Value + import org.springframework.core.io.Resource + import org.springframework.util.StreamUtils + import org.springframework.web.bind.annotation.ResponseBody + import org.springframework.web.bind.annotation.GetMapping +{{/sourceDocumentationProvider}} +{{^sourceDocumentationProvider}} + {{#useSwaggerUI}} + import org.springframework.web.bind.annotation.ResponseBody + import org.springframework.web.bind.annotation.GetMapping + {{/useSwaggerUI}} +{{/sourceDocumentationProvider}} +{{#reactive}} + import org.springframework.web.reactive.function.server.HandlerFunction + import org.springframework.web.reactive.function.server.RequestPredicates.GET + import org.springframework.web.reactive.function.server.RouterFunction + import org.springframework.web.reactive.function.server.RouterFunctions.route + import org.springframework.web.reactive.function.server.ServerResponse + import java.net.URI +{{/reactive}} +{{#sourceDocumentationProvider}} + import java.nio.charset.Charset +{{/sourceDocumentationProvider}} + +/** +* Home redirection to OpenAPI api documentation +*/ +@Controller +class HomeController { +{{#useSwaggerUI}} + {{^springDocDocumentationProvider}} + {{#sourceDocumentationProvider}} + private val apiDocsPath = "/openapi.json" + {{/sourceDocumentationProvider}} + {{#springFoxDocumentationProvider}} + private val apiDocsPath = "/v2/api-docs" + {{/springFoxDocumentationProvider}} + {{/springDocDocumentationProvider}} +{{/useSwaggerUI}} +{{#sourceDocumentationProvider}} + private val yamlMapper = YAMLMapper() + + @Value("classpath:/openapi.yaml") + private lateinit var openapi: Resource + + @Bean + fun openapiContent(): String { + return openapi.inputStream.use { + StreamUtils.copyToString(it, Charset.defaultCharset()) + } + } + + @GetMapping(value = ["/openapi.yaml"], produces = ["application/vnd.oai.openapi"]) + @ResponseBody + fun openapiYaml(): String = openapiContent() + + @GetMapping(value = ["/openapi.json"], produces = ["application/json"]) + @ResponseBody + fun openapiJson(): Any = yamlMapper.readValue(openapiContent(), Any::class.java) +{{/sourceDocumentationProvider}} +{{#useSwaggerUI}} + {{^springDocDocumentationProvider}} + + @GetMapping(value = ["/swagger-config.yaml"], produces = ["text/plain"]) + @ResponseBody + fun swaggerConfig(): String = "url: $apiDocsPath\n" + {{/springDocDocumentationProvider}} + {{#reactive}} + + @Bean + fun index(): RouterFunction + = route( + GET("/"), HandlerFunction + { + ServerResponse.temporaryRedirect(URI.create("swagger-ui.html")).build() + }) + {{/reactive}} + {{^reactive}} + + @RequestMapping("/") + fun index(): String = "redirect:swagger-ui.html" + {{/reactive}} +{{/useSwaggerUI}} + } diff --git a/templates/interfaceOptVar.mustache b/templates/interfaceOptVar.mustache new file mode 100644 index 00000000..158ad759 --- /dev/null +++ b/templates/interfaceOptVar.mustache @@ -0,0 +1,4 @@ +{{#swagger2AnnotationLibrary}} + @get:Schema({{#example}}example = "{{{.}}}", {{/example}}{{#required}}requiredMode = Schema.RequiredMode.REQUIRED, {{/required}}{{#isReadOnly}}readOnly = {{{isReadOnly}}}, {{/isReadOnly}}description = "{{{description}}}"){{/swagger2AnnotationLibrary}}{{#swagger1AnnotationLibrary}} + @get:ApiModelProperty({{#example}}example = "{{{.}}}", {{/example}}{{#required}}required = {{required}}, {{/required}}{{#isReadOnly}}readOnly = {{{isReadOnly}}}, {{/isReadOnly}}value = "{{{description}}}"){{/swagger1AnnotationLibrary}} +{{>modelMutable}} {{{name}}}: {{#isEnum}}{{classname}}.{{nameInCamelCase}}{{/isEnum}}{{^isEnum}}{{{dataType}}}{{/isEnum}}? {{^discriminator}}= {{{defaultValue}}}{{^defaultValue}}null{{/defaultValue}}{{/discriminator}} diff --git a/templates/interfaceReqVar.mustache b/templates/interfaceReqVar.mustache new file mode 100644 index 00000000..eeeda60d --- /dev/null +++ b/templates/interfaceReqVar.mustache @@ -0,0 +1,4 @@ +{{#swagger2AnnotationLibrary}} + @get:Schema({{#example}}example = "{{{.}}}", {{/example}}{{#required}}requiredMode = Schema.RequiredMode.REQUIRED, {{/required}}{{#isReadOnly}}readOnly = {{{isReadOnly}}}, {{/isReadOnly}}description = "{{{description}}}"){{/swagger2AnnotationLibrary}}{{#swagger1AnnotationLibrary}} + @get:ApiModelProperty({{#example}}example = "{{{.}}}", {{/example}}{{#required}}required = {{required}}, {{/required}}{{#isReadOnly}}readOnly = {{{isReadOnly}}}, {{/isReadOnly}}value = "{{{description}}}"){{/swagger1AnnotationLibrary}} +{{>modelMutable}} {{{name}}}: {{#isEnum}}{{classname}}.{{nameInCamelCase}}{{/isEnum}}{{^isEnum}}{{{dataType}}}{{/isEnum}} diff --git a/templates/libraries/spring-boot/README.mustache b/templates/libraries/spring-boot/README.mustache new file mode 100644 index 00000000..c75176be --- /dev/null +++ b/templates/libraries/spring-boot/README.mustache @@ -0,0 +1,21 @@ +# {{title}}{{^title}}Generated Kotlin Spring Boot App{{/title}} + +This Kotlin based [Spring Boot](https://spring.io/projects/spring-boot) application has been generated using the [OpenAPI Generator](https://github.com/OpenAPITools/openapi-generator). + +## Getting Started + +This document assumes you have either maven or gradle available, either via the wrapper or otherwise. This does not come with a gradle / maven wrapper checked in. + +By default a [`pom.xml`](pom.xml) file will be generated. If you specified `gradleBuildFile=true` when generating this project, a `build.gradle.kts` will also be generated. Note this uses [Gradle Kotlin DSL](https://github.com/gradle/kotlin-dsl). + +To build the project using maven, run: +```bash +mvn package && java -jar target/{{artifactId}}-{{artifactVersion}}.jar +``` + +To build the project using gradle, run: +```bash +gradle build && java -jar build/libs/{{artifactId}}-{{artifactVersion}}.jar +``` + +If all builds successfully, the server should run on [http://localhost:8080/](http://localhost:{{serverPort}}/) diff --git a/templates/libraries/spring-boot/application.mustache b/templates/libraries/spring-boot/application.mustache new file mode 100644 index 00000000..9f752949 --- /dev/null +++ b/templates/libraries/spring-boot/application.mustache @@ -0,0 +1,10 @@ +spring: +application: +name: {{title}} + +jackson: +serialization: +WRITE_DATES_AS_TIMESTAMPS: false + +server: +port: {{serverPort}} diff --git a/templates/libraries/spring-boot/buildGradle-sb3-Kts.mustache b/templates/libraries/spring-boot/buildGradle-sb3-Kts.mustache new file mode 100644 index 00000000..adc4d735 --- /dev/null +++ b/templates/libraries/spring-boot/buildGradle-sb3-Kts.mustache @@ -0,0 +1,61 @@ +import org.jetbrains.kotlin.gradle.tasks.KotlinCompile + +group = "{{groupId}}" +version = "{{artifactVersion}}" +java.sourceCompatibility = JavaVersion.VERSION_17 + +repositories { +mavenCentral() +maven { url = uri("https://repo.spring.io/milestone") } +} + +tasks.withType + { + kotlinOptions.jvmTarget = "17" + } + + plugins { + val kotlinVersion = "1.7.10" + id("org.jetbrains.kotlin.jvm") version kotlinVersion + id("org.jetbrains.kotlin.plugin.jpa") version kotlinVersion + id("org.jetbrains.kotlin.plugin.spring") version kotlinVersion + id("org.springframework.boot") version "3.0.2" + id("io.spring.dependency-management") version "1.0.14.RELEASE" + } + + dependencies { + {{#reactive}} val kotlinxCoroutinesVersion = "1.6.1" + {{/reactive}} implementation("org.jetbrains.kotlin:kotlin-stdlib-jdk8") + implementation("org.jetbrains.kotlin:kotlin-reflect"){{^reactive}} + implementation("org.springframework.boot:spring-boot-starter-web"){{/reactive}}{{#reactive}} + implementation("org.springframework.boot:spring-boot-starter-webflux") + implementation("org.jetbrains.kotlinx:kotlinx-coroutines-core:$kotlinxCoroutinesVersion") + implementation("org.jetbrains.kotlinx:kotlinx-coroutines-reactor:$kotlinxCoroutinesVersion"){{/reactive}}{{#springDocDocumentationProvider}}{{#useSwaggerUI}} + implementation("org.springdoc:springdoc-openapi-starter-{{#reactive}} + webflux{{/reactive}}{{^reactive}}webmvc{{/reactive}}-ui:2.0.0-M5"){{/useSwaggerUI}}{{^useSwaggerUI}} + implementation("org.springdoc:springdoc-openapi-{{#reactive}} + webflux{{/reactive}}{{^reactive}}webmvc{{/reactive}} + -core:2.0.0-M5"){{/useSwaggerUI}}{{/springDocDocumentationProvider}}{{#springFoxDocumentationProvider}} + implementation("io.springfox:springfox-swagger2:2.9.2"){{/springFoxDocumentationProvider}}{{#useSwaggerUI}}{{^springDocDocumentationProvider}} + implementation("org.webjars:swagger-ui:4.10.3") + implementation("org.webjars:webjars-locator-core"){{/springDocDocumentationProvider}}{{/useSwaggerUI}}{{^springFoxDocumentationProvider}}{{^springDocDocumentationProvider}}{{#swagger1AnnotationLibrary}} + implementation("io.swagger:swagger-annotations:1.6.6"){{/swagger1AnnotationLibrary}}{{#swagger2AnnotationLibrary}} + implementation("io.swagger.core.v3:swagger-annotations:2.2.0"){{/swagger2AnnotationLibrary}}{{/springDocDocumentationProvider}}{{/springFoxDocumentationProvider}} + + implementation("com.google.code.findbugs:jsr305:3.0.2") + implementation("com.fasterxml.jackson.dataformat:jackson-dataformat-yaml") + implementation("com.fasterxml.jackson.dataformat:jackson-dataformat-xml") + implementation("com.fasterxml.jackson.datatype:jackson-datatype-jsr310") + implementation("com.fasterxml.jackson.module:jackson-module-kotlin") + {{#useBeanValidation}} + implementation("jakarta.validation:jakarta.validation-api"){{/useBeanValidation}} + implementation("jakarta.annotation:jakarta.annotation-api:2.1.0") + + testImplementation("org.jetbrains.kotlin:kotlin-test-junit5") + testImplementation("org.springframework.boot:spring-boot-starter-test") { + exclude(module = "junit") + } + {{#reactive}} + testImplementation`("org.jetbrains.kotlinx:kotlinx-coroutines-test:$kotlinxCoroutinesVersion") + {{/reactive}} + } diff --git a/templates/libraries/spring-boot/buildGradleKts.mustache b/templates/libraries/spring-boot/buildGradleKts.mustache new file mode 100644 index 00000000..3459ef3d --- /dev/null +++ b/templates/libraries/spring-boot/buildGradleKts.mustache @@ -0,0 +1,68 @@ +import org.jetbrains.kotlin.gradle.tasks.KotlinCompile + +buildscript { +repositories { +mavenCentral() +} +dependencies { +classpath("org.springframework.boot:spring-boot-gradle-plugin:2.6.7") +} +} + +group = "{{groupId}}" +version = "{{artifactVersion}}" + +repositories { +mavenCentral() +} + +tasks.withType + { + kotlinOptions.jvmTarget = "1.8" + } + + plugins { + val kotlinVersion = "1.6.21" + id("org.jetbrains.kotlin.jvm") version kotlinVersion + id("org.jetbrains.kotlin.plugin.jpa") version kotlinVersion + id("org.jetbrains.kotlin.plugin.spring") version kotlinVersion + id("org.springframework.boot") version "2.6.7" + id("io.spring.dependency-management") version "1.0.11.RELEASE" + } + + dependencies { + {{#reactive}} val kotlinxCoroutinesVersion = "1.6.1" + {{/reactive}} compile("org.jetbrains.kotlin:kotlin-stdlib-jdk8") + compile("org.jetbrains.kotlin:kotlin-reflect"){{^reactive}} + compile("org.springframework.boot:spring-boot-starter-web"){{/reactive}}{{#reactive}} + compile("org.springframework.boot:spring-boot-starter-webflux") + compile("org.jetbrains.kotlinx:kotlinx-coroutines-core:$kotlinxCoroutinesVersion") + compile("org.jetbrains.kotlinx:kotlinx-coroutines-reactor:$kotlinxCoroutinesVersion"){{/reactive}}{{#springDocDocumentationProvider}}{{#useSwaggerUI}} + compile("org.springdoc:springdoc-openapi-{{#reactive}} + webflux-{{/reactive}}ui:1.6.8"){{/useSwaggerUI}}{{^useSwaggerUI}} + compile("org.springdoc:springdoc-openapi-{{#reactive}} + webflux{{/reactive}}{{^reactive}}webmvc{{/reactive}} + -core:1.6.8"){{/useSwaggerUI}}{{/springDocDocumentationProvider}}{{#springFoxDocumentationProvider}} + compile("io.springfox:springfox-swagger2:2.9.2"){{/springFoxDocumentationProvider}}{{#useSwaggerUI}}{{^springDocDocumentationProvider}} + compile("org.webjars:swagger-ui:4.10.3") + compile("org.webjars:webjars-locator-core"){{/springDocDocumentationProvider}}{{/useSwaggerUI}}{{^springFoxDocumentationProvider}}{{^springDocDocumentationProvider}}{{#swagger1AnnotationLibrary}} + compile("io.swagger:swagger-annotations:1.6.6"){{/swagger1AnnotationLibrary}}{{#swagger2AnnotationLibrary}} + compile("io.swagger.core.v3:swagger-annotations:2.2.0"){{/swagger2AnnotationLibrary}}{{/springDocDocumentationProvider}}{{/springFoxDocumentationProvider}} + + compile("com.google.code.findbugs:jsr305:3.0.2") + compile("com.fasterxml.jackson.dataformat:jackson-dataformat-yaml") + compile("com.fasterxml.jackson.dataformat:jackson-dataformat-xml") + compile("com.fasterxml.jackson.datatype:jackson-datatype-jsr310") + compile("com.fasterxml.jackson.module:jackson-module-kotlin") + {{#useBeanValidation}} + compile("jakarta.validation:jakarta.validation-api"){{/useBeanValidation}} + compile("jakarta.annotation:jakarta.annotation-api:2.1.0") + + testCompile("org.jetbrains.kotlin:kotlin-test-junit5") + testCompile("org.springframework.boot:spring-boot-starter-test") { + exclude(module = "junit") + } + {{#reactive}} + testCompile("org.jetbrains.kotlinx:kotlinx-coroutines-test:$kotlinxCoroutinesVersion") + {{/reactive}} + } diff --git a/templates/libraries/spring-boot/defaultBasePath.mustache b/templates/libraries/spring-boot/defaultBasePath.mustache new file mode 100644 index 00000000..3c7185bd --- /dev/null +++ b/templates/libraries/spring-boot/defaultBasePath.mustache @@ -0,0 +1 @@ +{{contextPath}} \ No newline at end of file diff --git a/templates/libraries/spring-boot/pom-sb3.mustache b/templates/libraries/spring-boot/pom-sb3.mustache new file mode 100644 index 00000000..5fa48b58 --- /dev/null +++ b/templates/libraries/spring-boot/pom-sb3.mustache @@ -0,0 +1,210 @@ + + 4.0.0 + {{groupId}} + {{artifactId}} + jar + {{artifactId}} + {{artifactVersion}} + {{#reactive}} + 1.6.1 + {{/reactive}}{{#springDocDocumentationProvider}}{{#useSwaggerUI}} + 2.0.2 + {{/useSwaggerUI}}{{/springDocDocumentationProvider}}{{#springFoxDocumentationProvider}} + 2.9.2 + {{/springFoxDocumentationProvider}}{{#useSwaggerUI}}{{^springDocDocumentationProvider}} + 4.15.5 + {{/springDocDocumentationProvider}}{{/useSwaggerUI}}{{^springFoxDocumentationProvider}}{{^springDocDocumentationProvider}}{{#swagger1AnnotationLibrary}} + 1.6.6 + {{/swagger1AnnotationLibrary}}{{#swagger2AnnotationLibrary}} + 2.2.7 + {{/swagger2AnnotationLibrary}}{{/springDocDocumentationProvider}}{{/springFoxDocumentationProvider}} + 3.0.2 + 2.1.0 + 1.7.10 + + 1.7.10 + UTF-8 + + + org.springframework.boot + spring-boot-starter-parent + 3.0.2 + + + + repository.spring.milestone + Spring Milestone Repository + https://repo.spring.io/milestone + + + + + spring-milestones + https://repo.spring.io/milestone + + + + ${project.basedir}/src/main/kotlin + ${project.basedir}/src/test/kotlin + {{^interfaceOnly}} + + org.springframework.boot + spring-boot-maven-plugin + + + + repackage + + + + {{/interfaceOnly}} + + kotlin-maven-plugin + org.jetbrains.kotlin + ${kotlin.version} + + + spring + + 17 + + + + compile + compile + + compile + + + + test-compile + test-compile + + test-compile + + + + + + org.jetbrains.kotlin + kotlin-maven-allopen + ${kotlin.version} + + + + + + + + org.jetbrains.kotlin + kotlin-stdlib-jdk8 + ${kotlin.version} + + + org.jetbrains.kotlin + kotlin-reflect + ${kotlin.version} + {{^reactive}} + + org.springframework.boot + spring-boot-starter-web + {{/reactive}}{{#reactive}} + + org.springframework.boot + spring-boot-starter-webflux + + + org.jetbrains.kotlinx + kotlinx-coroutines-core + ${kotlinx-coroutines.version} + + + org.jetbrains.kotlinx + kotlinx-coroutines-reactor + ${kotlinx-coroutines.version} + {{/reactive}} + + {{#springDocDocumentationProvider}} + {{#useSwaggerUI}} + + org.springdoc + springdoc-openapi-starter-{{#reactive}} + webflux{{/reactive}}{{^reactive}}webmvc{{/reactive}}-ui + + ${springdoc-openapi.version} + {{/useSwaggerUI}}{{^useSwaggerUI}} + + org.springdoc + springdoc-openapi-{{#reactive}}webflux{{/reactive}}{{^reactive}}webmvc{{/reactive}}-core + + ${springdoc-openapi.version} + {{/useSwaggerUI}}{{/springDocDocumentationProvider}}{{#springFoxDocumentationProvider}} + + + io.springfox + springfox-swagger2 + ${springfox-swagger2.version} + {{/springFoxDocumentationProvider}}{{#useSwaggerUI}}{{^springDocDocumentationProvider}} + + org.webjars + swagger-ui + ${swagger-ui.version} + + + org.webjars + webjars-locator-core + {{/springDocDocumentationProvider}}{{/useSwaggerUI}}{{^springFoxDocumentationProvider}}{{^springDocDocumentationProvider}}{{#swagger1AnnotationLibrary}} + + io.swagger + swagger-annotations + ${swagger-annotations.version} + {{/swagger1AnnotationLibrary}}{{#swagger2AnnotationLibrary}} + + io.swagger.core.v3 + swagger-annotations + ${swagger-annotations.version} + {{/swagger2AnnotationLibrary}}{{/springDocDocumentationProvider}}{{/springFoxDocumentationProvider}} + + + + com.google.code.findbugs + jsr305 + ${findbugs-jsr305.version} + + + com.fasterxml.jackson.dataformat + jackson-dataformat-yaml + + + com.fasterxml.jackson.dataformat + jackson-dataformat-xml + + + com.fasterxml.jackson.datatype + jackson-datatype-jsr310 + + + com.fasterxml.jackson.module + jackson-module-kotlin + + {{#useBeanValidation}} + + + jakarta.validation + jakarta.validation-api + {{/useBeanValidation}} + + jakarta.annotation + jakarta.annotation-api + ${jakarta-annotation.version} + provided + + + org.jetbrains.kotlin + kotlin-test-junit5 + ${kotlin-test-junit5.version} + test + + + diff --git a/templates/libraries/spring-boot/pom.mustache b/templates/libraries/spring-boot/pom.mustache new file mode 100644 index 00000000..967ff19b --- /dev/null +++ b/templates/libraries/spring-boot/pom.mustache @@ -0,0 +1,195 @@ + + 4.0.0 + {{groupId}} + {{artifactId}} + jar + {{artifactId}} + {{artifactVersion}} + {{#reactive}} + 1.6.1 + {{/reactive}}{{#springDocDocumentationProvider}}{{#useSwaggerUI}} + 1.6.8 + {{/useSwaggerUI}}{{/springDocDocumentationProvider}}{{#springFoxDocumentationProvider}} + 2.9.2 + {{/springFoxDocumentationProvider}}{{#useSwaggerUI}}{{^springDocDocumentationProvider}} + 4.10.3 + {{/springDocDocumentationProvider}}{{/useSwaggerUI}}{{^springFoxDocumentationProvider}}{{^springDocDocumentationProvider}}{{#swagger1AnnotationLibrary}} + 1.6.6 + {{/swagger1AnnotationLibrary}}{{#swagger2AnnotationLibrary}} + 2.2.0 + {{/swagger2AnnotationLibrary}}{{/springDocDocumentationProvider}}{{/springFoxDocumentationProvider}} + 3.0.2 + 2.1.0 + 1.6.21 + + 1.6.21 + UTF-8 + + + org.springframework.boot + spring-boot-starter-parent + 2.6.7 + + + ${project.basedir}/src/main/kotlin + ${project.basedir}/src/test/kotlin + {{^interfaceOnly}} + + org.springframework.boot + spring-boot-maven-plugin + + + + repackage + + + + {{/interfaceOnly}} + + kotlin-maven-plugin + org.jetbrains.kotlin + ${kotlin.version} + + + spring + + 1.8 + + + + compile + compile + + compile + + + + test-compile + test-compile + + test-compile + + + + + + org.jetbrains.kotlin + kotlin-maven-allopen + ${kotlin.version} + + + + + + + + org.jetbrains.kotlin + kotlin-stdlib-jdk8 + ${kotlin.version} + + + org.jetbrains.kotlin + kotlin-reflect + ${kotlin.version} + {{^reactive}} + + org.springframework.boot + spring-boot-starter-web + {{/reactive}}{{#reactive}} + + org.springframework.boot + spring-boot-starter-webflux + + + org.jetbrains.kotlinx + kotlinx-coroutines-core + ${kotlinx-coroutines.version} + + + org.jetbrains.kotlinx + kotlinx-coroutines-reactor + ${kotlinx-coroutines.version} + {{/reactive}} + + {{#springDocDocumentationProvider}} + {{#useSwaggerUI}} + + org.springdoc + springdoc-openapi-{{#reactive}}webflux-{{/reactive}}ui + ${springdoc-openapi.version} + {{/useSwaggerUI}}{{^useSwaggerUI}} + + org.springdoc + springdoc-openapi-{{#reactive}}webflux{{/reactive}}{{^reactive}}webmvc{{/reactive}}-core + + ${springdoc-openapi.version} + {{/useSwaggerUI}}{{/springDocDocumentationProvider}}{{#springFoxDocumentationProvider}} + + + io.springfox + springfox-swagger2 + ${springfox-swagger2.version} + {{/springFoxDocumentationProvider}}{{#useSwaggerUI}}{{^springDocDocumentationProvider}} + + org.webjars + swagger-ui + ${swagger-ui.version} + + + org.webjars + webjars-locator-core + {{/springDocDocumentationProvider}}{{/useSwaggerUI}}{{^springFoxDocumentationProvider}}{{^springDocDocumentationProvider}}{{#swagger1AnnotationLibrary}} + + io.swagger + swagger-annotations + ${swagger-annotations.version} + {{/swagger1AnnotationLibrary}}{{#swagger2AnnotationLibrary}} + + io.swagger.core.v3 + swagger-annotations + ${swagger-annotations.version} + {{/swagger2AnnotationLibrary}}{{/springDocDocumentationProvider}}{{/springFoxDocumentationProvider}} + + + + com.google.code.findbugs + jsr305 + ${findbugs-jsr305.version} + + + com.fasterxml.jackson.dataformat + jackson-dataformat-yaml + + + com.fasterxml.jackson.dataformat + jackson-dataformat-xml + + + com.fasterxml.jackson.datatype + jackson-datatype-jsr310 + + + com.fasterxml.jackson.module + jackson-module-kotlin + + {{#useBeanValidation}} + + + jakarta.validation + jakarta.validation-api + {{/useBeanValidation}} + + jakarta.annotation + jakarta.annotation-api + ${jakarta-annotation.version} + provided + + + org.jetbrains.kotlin + kotlin-test-junit5 + ${kotlin-test-junit5.version} + test + + + diff --git a/templates/libraries/spring-boot/settingsGradle.mustache b/templates/libraries/spring-boot/settingsGradle.mustache new file mode 100644 index 00000000..3176ec97 --- /dev/null +++ b/templates/libraries/spring-boot/settingsGradle.mustache @@ -0,0 +1,15 @@ +pluginManagement { +repositories { +maven { url = uri("https://repo.spring.io/snapshot") } +maven { url = uri("https://repo.spring.io/milestone") } +gradlePluginPortal() +} +resolutionStrategy { +eachPlugin { +if (requested.id.id == "org.springframework.boot") { +useModule("org.springframework.boot:spring-boot-gradle-plugin:${requested.version}") +} +} +} +} +rootProject.name = "{{artifactId}}" diff --git a/templates/libraries/spring-boot/springBootApplication.mustache b/templates/libraries/spring-boot/springBootApplication.mustache new file mode 100644 index 00000000..e86b92e0 --- /dev/null +++ b/templates/libraries/spring-boot/springBootApplication.mustache @@ -0,0 +1,15 @@ +package {{basePackage}} + +import org.springframework.boot.runApplication +import org.springframework.boot.autoconfigure.SpringBootApplication +import org.springframework.context.annotation.ComponentScan + +@SpringBootApplication +@ComponentScan(basePackages = ["{{basePackage}}", "{{apiPackage}}", "{{modelPackage}}"]) +class Application + +fun main(args: Array +) { + runApplication + (*args) + } diff --git a/templates/libraries/spring-boot/swagger-ui.mustache b/templates/libraries/spring-boot/swagger-ui.mustache new file mode 100644 index 00000000..ce2cfd92 --- /dev/null +++ b/templates/libraries/spring-boot/swagger-ui.mustache @@ -0,0 +1,57 @@ + + + + + + Swagger UI + + + + + + + +
+ + + + + + diff --git a/templates/libraries/spring-cloud/README.mustache b/templates/libraries/spring-cloud/README.mustache new file mode 100644 index 00000000..9949253f --- /dev/null +++ b/templates/libraries/spring-cloud/README.mustache @@ -0,0 +1,83 @@ +{{^interfaceOnly}} + # {{artifactId}} + + ## Requirements + + Building the API client library requires [Maven](https://maven.apache.org/) to be installed. + + ## Installation + + To install the API client library to your local Maven repository, simply execute: + + ```shell + mvn install + ``` + + To deploy it to a remote Maven repository instead, configure the settings of the repository and execute: + + ```shell + mvn deploy + ``` + + Refer to the [official documentation](https://maven.apache.org/plugins/maven-deploy-plugin/usage.html) for more information. + + ### Maven users + + Add this dependency to your project's POM: + + ```xml + + {{{groupId}}} + {{{artifactId}}} + {{{artifactVersion}}} + compile + + ``` + + ### Gradle users + + Add this dependency to your project's build file: + + ```groovy + compile "{{{groupId}}}:{{{artifactId}}}:{{{artifactVersion}}}" + ``` + + ### Others + + At first generate the JAR by executing: + + mvn package + + Then manually install the following JARs: + + * target/{{{artifactId}}}-{{{artifactVersion}}}.jar + * target/lib/*.jar +{{/interfaceOnly}} +{{#interfaceOnly}} + # OpenAPI generated API stub + + Spring Framework stub + + + ## Overview + This code was generated by the [OpenAPI Generator](https://openapi-generator.tech) project. + By using the [OpenAPI-Spec](https://openapis.org), you can easily generate an API stub. + This is an example of building API stub interfaces in Java using the Spring framework. + + The stubs generated can be used in your existing Spring-MVC or Spring-Boot application to create controller endpoints + by adding ```@Controller``` classes that implement the interface. Eg: + ```java + @Controller + public class PetController implements PetApi { + // implement all PetApi methods + } + ``` + + You can also use the interface to create [Spring-Cloud Feign clients](http://projects.spring.io/spring-cloud/spring-cloud.html#spring-cloud-feign-inheritance).Eg: + ```java + @FeignClient(name="pet", url="http://petstore.swagger.io/v2") + public interface PetClient extends PetApi { + + } + ``` +{{/interfaceOnly}} diff --git a/templates/libraries/spring-cloud/apiClient.mustache b/templates/libraries/spring-cloud/apiClient.mustache new file mode 100644 index 00000000..877bfbbe --- /dev/null +++ b/templates/libraries/spring-cloud/apiClient.mustache @@ -0,0 +1,11 @@ +package {{package}} + +import org.springframework.cloud.openfeign.FeignClient +import {{configPackage}}.ClientConfiguration + +@FeignClient( +name="\${{openbrace}}{{classVarName}}.name:{{classVarName}}{{closebrace}}", +{{#useFeignClientUrl}}url="\${{openbrace}}{{classVarName}}.url:{{basePath}}{{closebrace}}", {{/useFeignClientUrl}} +configuration = [ClientConfiguration::class] +) +interface {{classname}}Client : {{classname}} diff --git a/templates/libraries/spring-cloud/apiKeyRequestInterceptor.mustache b/templates/libraries/spring-cloud/apiKeyRequestInterceptor.mustache new file mode 100644 index 00000000..e0fa18d2 --- /dev/null +++ b/templates/libraries/spring-cloud/apiKeyRequestInterceptor.mustache @@ -0,0 +1,19 @@ +package {{configPackage}} + +import feign.RequestInterceptor +import feign.RequestTemplate + +class ApiKeyRequestInterceptor( +private val location: String, +private val name: String, +private val value: String, +) : RequestInterceptor { + +override fun apply(requestTemplate: RequestTemplate) { +if (location == "header") { +requestTemplate.header(name, value) +} else if (location == "query") { +requestTemplate.query(name, value) +} +} +} diff --git a/templates/libraries/spring-cloud/buildGradle-sb3-Kts.mustache b/templates/libraries/spring-cloud/buildGradle-sb3-Kts.mustache new file mode 100644 index 00000000..d1cb45c9 --- /dev/null +++ b/templates/libraries/spring-cloud/buildGradle-sb3-Kts.mustache @@ -0,0 +1,72 @@ +import org.jetbrains.kotlin.gradle.tasks.KotlinCompile + +group = "{{groupId}}" +version = "{{artifactVersion}}" +java.sourceCompatibility = JavaVersion.VERSION_17 + +repositories { +mavenCentral() +maven { url = uri("https://repo.spring.io/milestone") } +} + +tasks.withType + { + kotlinOptions.jvmTarget = "17" + } + + plugins { + val kotlinVersion = "1.7.10" + id("org.jetbrains.kotlin.jvm") version kotlinVersion + id("org.jetbrains.kotlin.plugin.jpa") version kotlinVersion + id("org.jetbrains.kotlin.plugin.spring") version kotlinVersion + id("org.springframework.boot") version "3.0.2" + id("io.spring.dependency-management") version "1.0.14.RELEASE" + } + + tasks.getByName("bootJar") { + enabled = false + } + + tasks.getByName("jar") { + enabled = true + } + + dependencyManagement { + imports { + mavenBom("org.springframework.cloud:spring-cloud-dependencies:2021.0.5") + } + } + + dependencies { + {{#reactive}} val kotlinxCoroutinesVersion = "1.6.1" + {{/reactive}} implementation("org.jetbrains.kotlin:kotlin-stdlib-jdk8") + implementation("org.jetbrains.kotlin:kotlin-reflect"){{^reactive}} + implementation("org.springframework.boot:spring-boot-starter-web"){{/reactive}}{{#reactive}} + implementation("org.springframework.boot:spring-boot-starter-webflux") + implementation("org.jetbrains.kotlinx:kotlinx-coroutines-core:$kotlinxCoroutinesVersion") + implementation("org.jetbrains.kotlinx:kotlinx-coroutines-reactor:$kotlinxCoroutinesVersion"){{/reactive}}{{#springDocDocumentationProvider}}{{#useSwaggerUI}} + implementation("org.springdoc:springdoc-openapi-starter-{{#reactive}} + webflux{{/reactive}}{{^reactive}}webmvc{{/reactive}}-ui:2.0.0-M5"){{/useSwaggerUI}}{{^useSwaggerUI}} + implementation("org.springdoc:springdoc-openapi-{{#reactive}} + webflux{{/reactive}}{{^reactive}}webmvc{{/reactive}} + -core:2.0.0-M5"){{/useSwaggerUI}}{{/springDocDocumentationProvider}}{{#springFoxDocumentationProvider}} + implementation("io.springfox:springfox-swagger2:2.9.2"){{/springFoxDocumentationProvider}}{{#useSwaggerUI}}{{^springDocDocumentationProvider}} + implementation("org.webjars:swagger-ui:4.10.3") + implementation("org.webjars:webjars-locator-core"){{/springDocDocumentationProvider}}{{/useSwaggerUI}}{{^springFoxDocumentationProvider}}{{^springDocDocumentationProvider}}{{#swagger1AnnotationLibrary}} + implementation("io.swagger:swagger-annotations:1.6.6"){{/swagger1AnnotationLibrary}}{{#swagger2AnnotationLibrary}} + implementation("io.swagger.core.v3:swagger-annotations:2.2.0"){{/swagger2AnnotationLibrary}}{{/springDocDocumentationProvider}}{{/springFoxDocumentationProvider}} + + implementation("com.google.code.findbugs:jsr305:3.0.2") + implementation("com.fasterxml.jackson.dataformat:jackson-dataformat-yaml") + implementation("com.fasterxml.jackson.dataformat:jackson-dataformat-xml") + implementation("com.fasterxml.jackson.datatype:jackson-datatype-jsr310") + implementation("com.fasterxml.jackson.module:jackson-module-kotlin") + + implementation("org.springframework.cloud:spring-cloud-starter-openfeign"){{#hasAuthMethods}} + implementation("org.springframework.cloud:spring-cloud-starter-oauth2:2.2.5.RELEASE"){{/hasAuthMethods}} + + {{#useBeanValidation}} + implementation("jakarta.validation:jakarta.validation-api"){{/useBeanValidation}} + implementation("jakarta.annotation:jakarta.annotation-api:2.1.0") + + } diff --git a/templates/libraries/spring-cloud/buildGradleKts.mustache b/templates/libraries/spring-cloud/buildGradleKts.mustache new file mode 100644 index 00000000..b944c04c --- /dev/null +++ b/templates/libraries/spring-cloud/buildGradleKts.mustache @@ -0,0 +1,79 @@ +import org.jetbrains.kotlin.gradle.tasks.KotlinCompile + +buildscript { +repositories { +mavenCentral() +} +dependencies { +classpath("org.springframework.boot:spring-boot-gradle-plugin:2.6.7") +} +} + +group = "{{groupId}}" +version = "{{artifactVersion}}" + +repositories { +mavenCentral() +} + +tasks.withType + { + kotlinOptions.jvmTarget = "1.8" + } + + plugins { + val kotlinVersion = "1.6.21" + id("org.jetbrains.kotlin.jvm") version kotlinVersion + id("org.jetbrains.kotlin.plugin.jpa") version kotlinVersion + id("org.jetbrains.kotlin.plugin.spring") version kotlinVersion + id("org.springframework.boot") version "2.6.7" + id("io.spring.dependency-management") version "1.0.11.RELEASE" + } + + tasks.getByName("bootJar") { + enabled = false + } + + tasks.getByName("jar") { + enabled = true + } + + dependencyManagement { + imports { + mavenBom("org.springframework.cloud:spring-cloud-dependencies:2021.0.5") + } + } + + dependencies { + {{#reactive}} val kotlinxCoroutinesVersion = "1.6.1" + {{/reactive}} implementation("org.jetbrains.kotlin:kotlin-stdlib-jdk8") + implementation("org.jetbrains.kotlin:kotlin-reflect"){{^reactive}} + implementation("org.springframework.boot:spring-boot-starter-web"){{/reactive}}{{#reactive}} + implementation("org.springframework.boot:spring-boot-starter-webflux") + implementation("org.jetbrains.kotlinx:kotlinx-coroutines-core:$kotlinxCoroutinesVersion") + implementation("org.jetbrains.kotlinx:kotlinx-coroutines-reactor:$kotlinxCoroutinesVersion"){{/reactive}}{{#springDocDocumentationProvider}}{{#useSwaggerUI}} + implementation("org.springdoc:springdoc-openapi-{{#reactive}} + webflux-{{/reactive}}ui:1.6.8"){{/useSwaggerUI}}{{^useSwaggerUI}} + implementation("org.springdoc:springdoc-openapi-{{#reactive}} + webflux{{/reactive}}{{^reactive}}webmvc{{/reactive}} + -core:1.6.8"){{/useSwaggerUI}}{{/springDocDocumentationProvider}}{{#springFoxDocumentationProvider}} + implementation("io.springfox:springfox-swagger2:2.9.2"){{/springFoxDocumentationProvider}}{{#useSwaggerUI}}{{^springDocDocumentationProvider}} + implementation("org.webjars:swagger-ui:4.10.3") + implementation("org.webjars:webjars-locator-core"){{/springDocDocumentationProvider}}{{/useSwaggerUI}}{{^springFoxDocumentationProvider}}{{^springDocDocumentationProvider}}{{#swagger1AnnotationLibrary}} + implementation("io.swagger:swagger-annotations:1.6.6"){{/swagger1AnnotationLibrary}}{{#swagger2AnnotationLibrary}} + implementation("io.swagger.core.v3:swagger-annotations:2.2.0"){{/swagger2AnnotationLibrary}}{{/springDocDocumentationProvider}}{{/springFoxDocumentationProvider}} + + implementation("com.google.code.findbugs:jsr305:3.0.2") + implementation("com.fasterxml.jackson.dataformat:jackson-dataformat-yaml") + implementation("com.fasterxml.jackson.dataformat:jackson-dataformat-xml") + implementation("com.fasterxml.jackson.datatype:jackson-datatype-jsr310") + implementation("com.fasterxml.jackson.module:jackson-module-kotlin") + + implementation("org.springframework.cloud:spring-cloud-starter-openfeign"){{#hasAuthMethods}} + implementation("org.springframework.cloud:spring-cloud-starter-oauth2:2.2.5.RELEASE"){{/hasAuthMethods}} + + {{#useBeanValidation}} + implementation("jakarta.validation:jakarta.validation-api"){{/useBeanValidation}} + implementation("jakarta.annotation:jakarta.annotation-api:2.1.0") + + } diff --git a/templates/libraries/spring-cloud/clientConfiguration.mustache b/templates/libraries/spring-cloud/clientConfiguration.mustache new file mode 100644 index 00000000..4b9615c4 --- /dev/null +++ b/templates/libraries/spring-cloud/clientConfiguration.mustache @@ -0,0 +1,132 @@ +package {{configPackage}} + +{{#authMethods}} + {{#isBasicBasic}} + import feign.auth.BasicAuthRequestInterceptor + {{/isBasicBasic}} + {{#-first}} + import org.springframework.beans.factory.annotation.Value + import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty + {{/-first}} + {{#isOAuth}} + import org.springframework.boot.context.properties.ConfigurationProperties + {{/isOAuth}} +{{/authMethods}} +import org.springframework.boot.context.properties.EnableConfigurationProperties +{{#authMethods}} + {{#-first}} + import org.springframework.context.annotation.Bean + {{/-first}} +{{/authMethods}} +import org.springframework.context.annotation.Configuration +{{#authMethods}} + {{#isOAuth}} + import org.springframework.cloud.openfeign.security.OAuth2FeignRequestInterceptor + import org.springframework.security.oauth2.client.DefaultOAuth2ClientContext + import org.springframework.security.oauth2.client.OAuth2ClientContext + {{#isApplication}} + import org.springframework.security.oauth2.client.token.grant.client.ClientCredentialsResourceDetails + {{/isApplication}} + {{#isCode}} + import org.springframework.security.oauth2.client.token.grant.code.AuthorizationCodeResourceDetails + {{/isCode}} + {{#isImplicit}} + import org.springframework.security.oauth2.client.token.grant.implicit.ImplicitResourceDetails + {{/isImplicit}} + {{#isPassword}} + import org.springframework.security.oauth2.client.token.grant.password.ResourceOwnerPasswordResourceDetails + {{/isPassword}} + {{/isOAuth}} +{{/authMethods}} + +@Configuration +@EnableConfigurationProperties +class ClientConfiguration { + +{{#authMethods}} + {{#isBasicBasic}} + @Value("\${{openbrace}}{{#lambda.lowercase}}{{{title}}}{{/lambda.lowercase}}.security.{{{name}}}.username:{{closebrace}}") + private lateinit var {{#lambda.camelcase}}{{{name}}}{{/lambda.camelcase}}Username: String + + @Value("\${{openbrace}}{{#lambda.lowercase}}{{{title}}}{{/lambda.lowercase}}.security.{{{name}}}.password:{{closebrace}}") + private lateinit var {{#lambda.camelcase}}{{{name}}}{{/lambda.camelcase}}Password: String + + @Bean + @ConditionalOnProperty("{{#lambda.lowercase}}{{{title}}}{{/lambda.lowercase}}.security.{{{name}}}.username") + fun {{{name}}}RequestInterceptor(): BasicAuthRequestInterceptor { + return BasicAuthRequestInterceptor(this.{{#lambda.camelcase}}{{{name}}}{{/lambda.camelcase}}Username, this.{{#lambda.camelcase}}{{{name}}}{{/lambda.camelcase}}Password) + } + + {{/isBasicBasic}} + {{#isApiKey}} + @Value("\${{openbrace}}{{#lambda.lowercase}}{{{title}}}{{/lambda.lowercase}}.security.{{{name}}}.key:{{closebrace}}") + private lateinit var {{#lambda.camelcase}}{{{name}}}{{/lambda.camelcase}}Key: String + + @Bean + @ConditionalOnProperty("{{#lambda.lowercase}}{{{title}}}{{/lambda.lowercase}}.security.{{{name}}}.key") + fun {{#lambda.camelcase}}{{{name}}}{{/lambda.camelcase}}RequestInterceptor(): ApiKeyRequestInterceptor { + return ApiKeyRequestInterceptor({{#isKeyInHeader}}"header"{{/isKeyInHeader}}{{^isKeyInHeader}}"query"{{/isKeyInHeader}}, "{{{keyParamName}}}", this.{{#lambda.camelcase}}{{{name}}}{{/lambda.camelcase}}Key) + } + + {{/isApiKey}} + {{#isOAuth}} + @Bean + @ConditionalOnProperty("{{#lambda.lowercase}}{{{title}}}{{/lambda.lowercase}}.security.{{{name}}}.client-id") + fun {{#lambda.camelcase}}{{{name}}}{{/lambda.camelcase}}RequestInterceptor(oAuth2ClientContext: OAuth2ClientContext): OAuth2FeignRequestInterceptor { + return OAuth2FeignRequestInterceptor(oAuth2ClientContext, {{#lambda.camelcase}}{{{name}}}{{/lambda.camelcase}}ResourceDetails()) + } + + @Bean + @ConditionalOnProperty("{{#lambda.lowercase}}{{{title}}}{{/lambda.lowercase}}.security.{{{name}}}.client-id") + fun oAuth2ClientContext(): OAuth2ClientContext { + return DefaultOAuth2ClientContext() + } + + {{#isCode}} + @Bean + @ConditionalOnProperty("{{#lambda.lowercase}}{{{title}}}{{/lambda.lowercase}}.security.{{{name}}}.client-id") + @ConfigurationProperties("{{#lambda.lowercase}}{{{title}}}{{/lambda.lowercase}}.security.{{{name}}}") + fun {{#lambda.camelcase}}{{{name}}}{{/lambda.camelcase}}ResourceDetails(): AuthorizationCodeResourceDetails { + val details = AuthorizationCodeResourceDetails() + details.accessTokenUri = "{{{tokenUrl}}}" + details.userAuthorizationUri = "{{{authorizationUrl}}}" + return details + } + + {{/isCode}} + {{#isPassword}} + @Bean + @ConditionalOnProperty("{{#lambda.lowercase}}{{{title}}}{{/lambda.lowercase}}.security.{{{name}}}.client-id") + @ConfigurationProperties("{{#lambda.lowercase}}{{{title}}}{{/lambda.lowercase}}.security.{{{name}}}") + fun {{#lambda.camelcase}}{{{name}}}{{/lambda.camelcase}}ResourceDetails(): ResourceOwnerPasswordResourceDetails { + val details = ResourceOwnerPasswordResourceDetails() + details.accessTokenUri = "{{{tokenUrl}}}" + return details + } + + {{/isPassword}} + {{#isApplication}} + @Bean + @ConditionalOnProperty("{{#lambda.lowercase}}{{{title}}}{{/lambda.lowercase}}.security.{{{name}}}.client-id") + @ConfigurationProperties("{{#lambda.lowercase}}{{{title}}}{{/lambda.lowercase}}.security.{{{name}}}") + fun {{#lambda.camelcase}}{{{name}}}{{/lambda.camelcase}}ResourceDetails(): ClientCredentialsResourceDetails { + val details = ClientCredentialsResourceDetails() + details.accessTokenUri = "{{{tokenUrl}}}" + return details + } + + {{/isApplication}} + {{#isImplicit}} + @Bean + @ConditionalOnProperty("{{#lambda.lowercase}}{{{title}}}{{/lambda.lowercase}}.security.{{{name}}}.client-id") + @ConfigurationProperties("{{#lambda.lowercase}}{{{title}}}{{/lambda.lowercase}}.security.{{{name}}}") + fun {{#lambda.camelcase}}{{{name}}}{{/lambda.camelcase}}ResourceDetails(): ImplicitResourceDetails { + val details = ImplicitResourceDetails() + details.userAuthorizationUri= "{{{authorizationUrl}}}" + return details + } + + {{/isImplicit}} + {{/isOAuth}} +{{/authMethods}} +} diff --git a/templates/libraries/spring-cloud/pom-sb3.mustache b/templates/libraries/spring-cloud/pom-sb3.mustache new file mode 100644 index 00000000..4dfd1558 --- /dev/null +++ b/templates/libraries/spring-cloud/pom-sb3.mustache @@ -0,0 +1,233 @@ + + 4.0.0 + {{groupId}} + {{artifactId}} + jar + {{artifactId}} + {{artifactVersion}} + {{#reactive}} + 1.6.1 + {{/reactive}}{{#springDocDocumentationProvider}}{{#useSwaggerUI}} + 2.0.2 + {{/useSwaggerUI}}{{/springDocDocumentationProvider}}{{#springFoxDocumentationProvider}} + 2.9.2 + {{/springFoxDocumentationProvider}}{{#useSwaggerUI}}{{^springDocDocumentationProvider}} + 4.15.5 + {{/springDocDocumentationProvider}}{{/useSwaggerUI}}{{^springFoxDocumentationProvider}}{{^springDocDocumentationProvider}}{{#swagger1AnnotationLibrary}} + 1.6.6 + {{/swagger1AnnotationLibrary}}{{#swagger2AnnotationLibrary}} + 2.2.7 + {{/swagger2AnnotationLibrary}}{{/springDocDocumentationProvider}}{{/springFoxDocumentationProvider}} + 3.0.2 + 2.1.0 + 1.7.10 + + 1.7.10 + UTF-8 + + + org.springframework.boot + spring-boot-starter-parent + 3.0.2 + + + + + org.springframework.cloud + spring-cloud-starter-parent + 2021.0.5 + pom + import + + + + + + repository.spring.milestone + Spring Milestone Repository + https://repo.spring.io/milestone + + + + + spring-milestones + https://repo.spring.io/milestone + + + + ${project.basedir}/src/main/kotlin + {{^interfaceOnly}} + + org.springframework.boot + spring-boot-maven-plugin + + + + repackage + + + + {{/interfaceOnly}} + + kotlin-maven-plugin + org.jetbrains.kotlin + ${kotlin.version} + + + spring + + 17 + + + + compile + compile + + compile + + + + test-compile + test-compile + + test-compile + + + + + + org.jetbrains.kotlin + kotlin-maven-allopen + ${kotlin.version} + + + + + + + + org.jetbrains.kotlin + kotlin-stdlib-jdk8 + ${kotlin.version} + + + org.jetbrains.kotlin + kotlin-reflect + ${kotlin.version} + {{^reactive}} + + org.springframework.boot + spring-boot-starter-web + {{/reactive}}{{#reactive}} + + org.springframework.boot + spring-boot-starter-webflux + + + org.jetbrains.kotlinx + kotlinx-coroutines-core + ${kotlinx-coroutines.version} + + + org.jetbrains.kotlinx + kotlinx-coroutines-reactor + ${kotlinx-coroutines.version} + {{/reactive}} + + {{#springDocDocumentationProvider}} + {{#useSwaggerUI}} + + org.springdoc + springdoc-openapi-starter-{{#reactive}} + webflux{{/reactive}}{{^reactive}}webmvc{{/reactive}}-ui + + ${springdoc-openapi.version} + {{/useSwaggerUI}}{{^useSwaggerUI}} + + org.springdoc + springdoc-openapi-{{#reactive}}webflux{{/reactive}}{{^reactive}}webmvc{{/reactive}}-core + + ${springdoc-openapi.version} + {{/useSwaggerUI}}{{/springDocDocumentationProvider}}{{#springFoxDocumentationProvider}} + + + io.springfox + springfox-swagger2 + ${springfox-swagger2.version} + {{/springFoxDocumentationProvider}}{{#useSwaggerUI}}{{^springDocDocumentationProvider}} + + org.webjars + swagger-ui + ${swagger-ui.version} + + + org.webjars + webjars-locator-core + {{/springDocDocumentationProvider}}{{/useSwaggerUI}}{{^springFoxDocumentationProvider}}{{^springDocDocumentationProvider}}{{#swagger1AnnotationLibrary}} + + io.swagger + swagger-annotations + ${swagger-annotations.version} + {{/swagger1AnnotationLibrary}}{{#swagger2AnnotationLibrary}} + + io.swagger.core.v3 + swagger-annotations + ${swagger-annotations.version} + {{/swagger2AnnotationLibrary}}{{/springDocDocumentationProvider}}{{/springFoxDocumentationProvider}} + + + + com.google.code.findbugs + jsr305 + ${findbugs-jsr305.version} + + + org.springframework.cloud + spring-cloud-starter-openfeign + + {{#hasAuthMethods}} + + org.springframework.cloud + spring-cloud-starter-oauth2 + {{^parentOverridden}} + 2.2.5.RELEASE + {{/parentOverridden}} + + {{/hasAuthMethods}} + + com.fasterxml.jackson.dataformat + jackson-dataformat-yaml + + + com.fasterxml.jackson.dataformat + jackson-dataformat-xml + + + com.fasterxml.jackson.datatype + jackson-datatype-jsr310 + + + com.fasterxml.jackson.module + jackson-module-kotlin + + {{#useBeanValidation}} + + + jakarta.validation + jakarta.validation-api + {{/useBeanValidation}} + + jakarta.annotation + jakarta.annotation-api + ${jakarta-annotation.version} + provided + + + org.jetbrains.kotlin + kotlin-test-junit5 + ${kotlin-test-junit5.version} + test + + + diff --git a/templates/libraries/spring-cloud/pom.mustache b/templates/libraries/spring-cloud/pom.mustache new file mode 100644 index 00000000..867c9cf2 --- /dev/null +++ b/templates/libraries/spring-cloud/pom.mustache @@ -0,0 +1,218 @@ + + 4.0.0 + {{groupId}} + {{artifactId}} + jar + {{artifactId}} + {{artifactVersion}} + {{#reactive}} + 1.6.1 + {{/reactive}}{{#springDocDocumentationProvider}}{{#useSwaggerUI}} + 1.6.8 + {{/useSwaggerUI}}{{/springDocDocumentationProvider}}{{#springFoxDocumentationProvider}} + 2.9.2 + {{/springFoxDocumentationProvider}}{{#useSwaggerUI}}{{^springDocDocumentationProvider}} + 4.10.3 + {{/springDocDocumentationProvider}}{{/useSwaggerUI}}{{^springFoxDocumentationProvider}}{{^springDocDocumentationProvider}}{{#swagger1AnnotationLibrary}} + 1.6.6 + {{/swagger1AnnotationLibrary}}{{#swagger2AnnotationLibrary}} + 2.2.0 + {{/swagger2AnnotationLibrary}}{{/springDocDocumentationProvider}}{{/springFoxDocumentationProvider}} + 3.0.2 + 2.1.0 + 1.6.21 + + 1.6.21 + UTF-8 + + + org.springframework.boot + spring-boot-starter-parent + 2.6.7 + + + + + org.springframework.cloud + spring-cloud-starter-parent + 2021.0.5 + pom + import + + + + + ${project.basedir}/src/main/kotlin + {{^interfaceOnly}} + + org.springframework.boot + spring-boot-maven-plugin + + + + repackage + + + + {{/interfaceOnly}} + + kotlin-maven-plugin + org.jetbrains.kotlin + ${kotlin.version} + + + spring + + 1.8 + + + + compile + compile + + compile + + + + test-compile + test-compile + + test-compile + + + + + + org.jetbrains.kotlin + kotlin-maven-allopen + ${kotlin.version} + + + + + + + + org.jetbrains.kotlin + kotlin-stdlib-jdk8 + ${kotlin.version} + + + org.jetbrains.kotlin + kotlin-reflect + ${kotlin.version} + {{^reactive}} + + org.springframework.boot + spring-boot-starter-web + {{/reactive}}{{#reactive}} + + org.springframework.boot + spring-boot-starter-webflux + + + org.jetbrains.kotlinx + kotlinx-coroutines-core + ${kotlinx-coroutines.version} + + + org.jetbrains.kotlinx + kotlinx-coroutines-reactor + ${kotlinx-coroutines.version} + {{/reactive}} + + {{#springDocDocumentationProvider}} + {{#useSwaggerUI}} + + org.springdoc + springdoc-openapi-{{#reactive}}webflux-{{/reactive}}ui + ${springdoc-openapi.version} + {{/useSwaggerUI}}{{^useSwaggerUI}} + + org.springdoc + springdoc-openapi-{{#reactive}}webflux{{/reactive}}{{^reactive}}webmvc{{/reactive}}-core + + ${springdoc-openapi.version} + {{/useSwaggerUI}}{{/springDocDocumentationProvider}}{{#springFoxDocumentationProvider}} + + + io.springfox + springfox-swagger2 + ${springfox-swagger2.version} + {{/springFoxDocumentationProvider}}{{#useSwaggerUI}}{{^springDocDocumentationProvider}} + + org.webjars + swagger-ui + ${swagger-ui.version} + + + org.webjars + webjars-locator-core + {{/springDocDocumentationProvider}}{{/useSwaggerUI}}{{^springFoxDocumentationProvider}}{{^springDocDocumentationProvider}}{{#swagger1AnnotationLibrary}} + + io.swagger + swagger-annotations + ${swagger-annotations.version} + {{/swagger1AnnotationLibrary}}{{#swagger2AnnotationLibrary}} + + io.swagger.core.v3 + swagger-annotations + ${swagger-annotations.version} + {{/swagger2AnnotationLibrary}}{{/springDocDocumentationProvider}}{{/springFoxDocumentationProvider}} + + + + com.google.code.findbugs + jsr305 + ${findbugs-jsr305.version} + + + org.springframework.cloud + spring-cloud-starter-openfeign + + {{#hasAuthMethods}} + + org.springframework.cloud + spring-cloud-starter-oauth2 + {{^parentOverridden}} + 2.2.5.RELEASE + {{/parentOverridden}} + + {{/hasAuthMethods}} + + com.fasterxml.jackson.dataformat + jackson-dataformat-yaml + + + com.fasterxml.jackson.dataformat + jackson-dataformat-xml + + + com.fasterxml.jackson.datatype + jackson-datatype-jsr310 + + + com.fasterxml.jackson.module + jackson-module-kotlin + + {{#useBeanValidation}} + + + jakarta.validation + jakarta.validation-api + {{/useBeanValidation}} + + jakarta.annotation + jakarta.annotation-api + ${jakarta-annotation.version} + provided + + + org.jetbrains.kotlin + kotlin-test-junit5 + ${kotlin-test-junit5.version} + test + + + diff --git a/templates/libraries/spring-cloud/settingsGradle.mustache b/templates/libraries/spring-cloud/settingsGradle.mustache new file mode 100644 index 00000000..3176ec97 --- /dev/null +++ b/templates/libraries/spring-cloud/settingsGradle.mustache @@ -0,0 +1,15 @@ +pluginManagement { +repositories { +maven { url = uri("https://repo.spring.io/snapshot") } +maven { url = uri("https://repo.spring.io/milestone") } +gradlePluginPortal() +} +resolutionStrategy { +eachPlugin { +if (requested.id.id == "org.springframework.boot") { +useModule("org.springframework.boot:spring-boot-gradle-plugin:${requested.version}") +} +} +} +} +rootProject.name = "{{artifactId}}" diff --git a/templates/methodBody.mustache b/templates/methodBody.mustache new file mode 100644 index 00000000..f7e69bcd --- /dev/null +++ b/templates/methodBody.mustache @@ -0,0 +1,28 @@ +{{^reactive}} + {{#examples}} + {{#-first}} + {{#async}} + return CompletableFuture.supplyAsync(()-> { + {{/async}}getRequest().ifPresent { request -> + {{#async}} {{/async}} for (mediaType in MediaType.parseMediaTypes(request.getHeader("Accept"))) { + {{/-first}} + {{#async}} {{/async}}{{^async}} {{/async}} if (mediaType.isCompatibleWith(MediaType.valueOf("{{{contentType}}}"))) { + {{#async}} {{/async}}{{^async}} {{/async}} ApiUtil.setExampleResponse(request, "{{{contentType}}}", "{{#lambdaRemoveLineBreak}}{{#lambdaEscapeDoubleQuote}}{{{example}}}{{/lambdaEscapeDoubleQuote}}{{/lambdaRemoveLineBreak}}") + {{#async}} {{/async}}{{^async}} {{/async}} break + {{#async}} {{/async}}{{^async}} {{/async}} } + {{#-last}} + {{#async}} {{/async}}{{^async}} {{/async}} } + {{#async}} {{/async}} } + {{#async}} {{/async}} return ResponseEntity({{#returnSuccessCode}}HttpStatus.valueOf({{{statusCode}}}){{/returnSuccessCode}}{{^returnSuccessCode}}HttpStatus.NOT_IMPLEMENTED{{/returnSuccessCode}}) + {{#async}} + }, Runnable::run) + {{/async}} + {{/-last}} + {{/examples}} + {{^examples}} + return {{#async}}CompletableFuture.completedFuture({{/async}}ResponseEntity({{#returnSuccessCode}}HttpStatus.OK{{/returnSuccessCode}}{{^returnSuccessCode}}HttpStatus.NOT_IMPLEMENTED{{/returnSuccessCode}}) + {{/examples}} +{{/reactive}} +{{#reactive}} + return ResponseEntity({{#returnSuccessCode}}HttpStatus.OK{{/returnSuccessCode}}{{^returnSuccessCode}}HttpStatus.NOT_IMPLEMENTED{{/returnSuccessCode}}) +{{/reactive}} diff --git a/templates/model.mustache b/templates/model.mustache new file mode 100644 index 00000000..48ca8ae6 --- /dev/null +++ b/templates/model.mustache @@ -0,0 +1,28 @@ +package {{package}} + +import java.util.Objects +{{#imports}}import {{import}} +{{/imports}} +{{#useBeanValidation}} + import {{javaxPackage}}.validation.constraints.DecimalMax + import {{javaxPackage}}.validation.constraints.DecimalMin + import {{javaxPackage}}.validation.constraints.Email + import {{javaxPackage}}.validation.constraints.Max + import {{javaxPackage}}.validation.constraints.Min + import {{javaxPackage}}.validation.constraints.NotNull + import {{javaxPackage}}.validation.constraints.Pattern + import {{javaxPackage}}.validation.constraints.Size + import {{javaxPackage}}.validation.Valid +{{/useBeanValidation}} +{{#swagger2AnnotationLibrary}} + import io.swagger.v3.oas.annotations.media.Schema +{{/swagger2AnnotationLibrary}} +{{#swagger1AnnotationLibrary}} + import io.swagger.annotations.ApiModelProperty +{{/swagger1AnnotationLibrary}} + +{{#models}} + {{#model}} + {{#isEnum}}{{>enumClass}}{{/isEnum}}{{^isEnum}}{{>dataClass}}{{/isEnum}} + {{/model}} +{{/models}} diff --git a/templates/modelMutable.mustache b/templates/modelMutable.mustache new file mode 100644 index 00000000..4c7f3900 --- /dev/null +++ b/templates/modelMutable.mustache @@ -0,0 +1 @@ +{{#modelMutable}}var{{/modelMutable}}{{^modelMutable}}val{{/modelMutable}} \ No newline at end of file diff --git a/templates/openapi.mustache b/templates/openapi.mustache new file mode 100644 index 00000000..51ebafb0 --- /dev/null +++ b/templates/openapi.mustache @@ -0,0 +1 @@ +{{{openapi-yaml}}} \ No newline at end of file diff --git a/templates/optionalDataType.mustache b/templates/optionalDataType.mustache new file mode 100644 index 00000000..7c026fa8 --- /dev/null +++ b/templates/optionalDataType.mustache @@ -0,0 +1 @@ +{{#required}}{{{dataType}}}{{/required}}{{^required}}{{#defaultValue}}{{{dataType}}}{{/defaultValue}}{{^defaultValue}}{{{dataType}}}?{{/defaultValue}}{{/required}} \ No newline at end of file diff --git a/templates/pathParams.mustache b/templates/pathParams.mustache new file mode 100644 index 00000000..957ca220 --- /dev/null +++ b/templates/pathParams.mustache @@ -0,0 +1 @@ +{{#isPathParam}}{{#useBeanValidation}}{{>beanValidationPathParams}}{{/useBeanValidation}}{{#swagger2AnnotationLibrary}}@Parameter(description = "{{{description}}}"{{#required}}, required = true{{/required}}{{#allowableValues}}{{#defaultValue}}, schema = Schema(allowableValues = [{{#enumVars}}"{{#lambdaEscapeDoubleQuote}}{{{value}}}{{/lambdaEscapeDoubleQuote}}"{{^-last}}, {{/-last}}{{/enumVars}}]{{^isContainer}}, defaultValue = {{^isString}}"{{/isString}}{{#isString}}{{#isEnum}}"{{/isEnum}}{{/isString}}{{{defaultValue}}}{{^isString}}"{{/isString}}{{#isString}}{{#isEnum}}"{{/isEnum}}{{/isString}}{{/isContainer}}{{/defaultValue}}{{/allowableValues}}{{#allowableValues}}{{^defaultValue}}, schema = Schema(allowableValues = [{{#enumVars}}"{{#lambdaEscapeDoubleQuote}}{{{value}}}{{/lambdaEscapeDoubleQuote}}"{{^-last}}, {{/-last}}{{/enumVars}}]){{/defaultValue}}{{/allowableValues}}{{^allowableValues}}{{#defaultValue}}{{^isContainer}}, schema = Schema(defaultValue = {{^isString}}"{{/isString}}{{#isString}}{{#isEnum}}"{{/isEnum}}{{/isString}}{{{defaultValue}}}{{^isString}}"{{/isString}}{{#isString}}{{#isEnum}}"{{/isEnum}}{{/isString}}{{/isContainer}}{{/defaultValue}}{{/allowableValues}}){{/swagger2AnnotationLibrary}}{{#swagger1AnnotationLibrary}}@ApiParam(value = "{{{description}}}"{{#required}}, required = true{{/required}}{{#allowableValues}}, allowableValues = "{{#enumVars}}{{#lambda.escapeDoubleQuote}}{{{value}}}{{/lambda.escapeDoubleQuote}}{{^-last}}, {{/-last}}{{/enumVars}}"{{/allowableValues}}{{#defaultValue}}, defaultValue = "{{{.}}}"{{/defaultValue}}){{/swagger1AnnotationLibrary}} @PathVariable("{{baseName}}") {{paramName}}: {{>optionalDataType}}{{/isPathParam}} \ No newline at end of file diff --git a/templates/queryParams.mustache b/templates/queryParams.mustache new file mode 100644 index 00000000..3c254c53 --- /dev/null +++ b/templates/queryParams.mustache @@ -0,0 +1 @@ +{{#isQueryParam}}{{#useBeanValidation}}{{>beanValidationQueryParams}}{{/useBeanValidation}}{{#swagger2AnnotationLibrary}}@Parameter(description = "{{{description}}}"{{#required}}, required = true{{/required}}{{#allowableValues}}{{#defaultValue}}, schema = Schema(allowableValues = [{{#values}}"{{{.}}}"{{^-last}}, {{/-last}}{{/values}}]{{^isContainer}}, defaultValue = {{^isString}}"{{/isString}}{{#isString}}{{#isEnum}}"{{/isEnum}}{{/isString}}{{{defaultValue}}}{{^isString}}"{{/isString}}{{#isString}}{{#isEnum}}"{{/isEnum}}{{/isString}}{{/isContainer}}){{/defaultValue}}{{/allowableValues}}{{#allowableValues}}{{^defaultValue}}, schema = Schema(allowableValues = [{{#values}}"{{{.}}}"{{^-last}}, {{/-last}}{{/values}}]){{/defaultValue}}{{/allowableValues}}{{^allowableValues}}{{#defaultValue}}{{^isContainer}}, schema = Schema(defaultValue = {{^isString}}"{{/isString}}{{#isString}}{{#isEnum}}"{{/isEnum}}{{/isString}}{{{defaultValue}}}{{^isString}}"{{/isString}}{{#isString}}{{#isEnum}}"{{/isEnum}}{{/isString}}){{/isContainer}}{{/defaultValue}}{{/allowableValues}}){{/swagger2AnnotationLibrary}}{{#swagger1AnnotationLibrary}}@ApiParam(value = "{{{description}}}"{{#required}}, required = true{{/required}}{{#allowableValues}}, allowableValues = "{{#values}}{{{.}}}{{^-last}}, {{/-last}}{{/values}}"{{/allowableValues}}{{^isContainer}}{{#defaultValue}}, defaultValue = {{^isString}}"{{/isString}}{{#isString}}{{#isEnum}}"{{/isEnum}}{{/isString}}{{{defaultValue}}}{{^isString}}"{{/isString}}{{#isString}}{{#isEnum}}"{{/isEnum}}{{/isString}}{{/defaultValue}}{{/isContainer}}){{/swagger1AnnotationLibrary}}{{#useBeanValidation}} @Valid{{/useBeanValidation}}{{^isModel}} @RequestParam(value = "{{baseName}}"{{#required}}, required = true{{/required}}{{^required}}, required = false{{/required}}{{^isContainer}}{{#defaultValue}}, defaultValue = {{^isString}}"{{/isString}}{{#isString}}{{#isEnum}}"{{/isEnum}}{{/isString}}{{{defaultValue}}}{{^isString}}"{{/isString}}{{#isString}}{{#isEnum}}"{{/isEnum}}{{/isString}}{{/defaultValue}}{{/isContainer}}){{/isModel}}{{#isDate}} @org.springframework.format.annotation.DateTimeFormat(iso = org.springframework.format.annotation.DateTimeFormat.ISO.DATE){{/isDate}}{{#isDateTime}} @org.springframework.format.annotation.DateTimeFormat(iso = org.springframework.format.annotation.DateTimeFormat.ISO.DATE_TIME){{/isDateTime}} {{paramName}}: {{>optionalDataType}}{{/isQueryParam}} \ No newline at end of file diff --git a/templates/returnTypes.mustache b/templates/returnTypes.mustache new file mode 100644 index 00000000..eddd93e3 --- /dev/null +++ b/templates/returnTypes.mustache @@ -0,0 +1,2 @@ +{{#isMap}}Map +{{/isMap}}{{#isArray}}{{#reactive}}Flow{{/reactive}}{{^reactive}}{{{returnContainer}}}{{/reactive}}<{{{returnType}}}>{{/isArray}}{{^returnContainer}}{{{returnType}}}{{/returnContainer}} diff --git a/templates/returnValue.mustache b/templates/returnValue.mustache new file mode 100644 index 00000000..8c7c1aa4 --- /dev/null +++ b/templates/returnValue.mustache @@ -0,0 +1 @@ +{{#serviceInterface}}ResponseEntity(service.{{operationId}}({{#allParams}}{{{paramName}}}{{^-last}}, {{/-last}}{{/allParams}}), {{#responses}}{{#-first}}HttpStatus.valueOf({{code}}){{/-first}}{{/responses}}){{/serviceInterface}}{{^serviceInterface}}ResponseEntity(HttpStatus.NOT_IMPLEMENTED){{/serviceInterface}} \ No newline at end of file diff --git a/templates/service.mustache b/templates/service.mustache new file mode 100644 index 00000000..f212e407 --- /dev/null +++ b/templates/service.mustache @@ -0,0 +1,36 @@ +package {{package}} + +{{#imports}}import {{import}} +{{/imports}} +{{#reactive}} + import kotlinx.coroutines.flow.Flow +{{/reactive}} + +{{#operations}} + interface {{classname}}Service { + {{#operation}} + + /** + * {{httpMethod}} {{{path}}}{{#summary}} : {{.}}{{/summary}} + {{#notes}} + * {{.}} + {{/notes}} + * + {{#allParams}} + * @param {{{paramName}}} {{description}}{{#required}} (required){{/required}}{{^required}} (optional{{#defaultValue}}, default to {{.}}{{/defaultValue}}){{/required}} + {{/allParams}} + * @return {{#responses}}{{message}} (status code {{code}}){{^-last}} + * or {{/-last}}{{/responses}} + {{#isDeprecated}} + * @deprecated + {{/isDeprecated}} + {{#externalDocs}} + * {{description}} + * @see {{summary}} Documentation + {{/externalDocs}} + * @see {{classname}}#{{operationId}} + */ + {{#reactive}}{{^isArray}}suspend {{/isArray}}{{/reactive}}fun {{operationId}}({{#allParams}}{{{paramName}}}: {{^isBodyParam}}{{>optionalDataType}}{{/isBodyParam}}{{#isBodyParam}}{{^reactive}}{{>optionalDataType}}{{/reactive}}{{#reactive}}{{^isArray}}{{>optionalDataType}}{{/isArray}}{{#isArray}}Flow<{{{baseType}}}>{{/isArray}}{{/reactive}}{{/isBodyParam}}{{^-last}}, {{/-last}}{{/allParams}}): {{>returnTypes}} + {{/operation}} + } +{{/operations}} diff --git a/templates/serviceImpl.mustache b/templates/serviceImpl.mustache new file mode 100644 index 00000000..7a707cb3 --- /dev/null +++ b/templates/serviceImpl.mustache @@ -0,0 +1,19 @@ +package {{package}} + +{{#imports}}import {{import}} +{{/imports}} +{{#reactive}} + import kotlinx.coroutines.flow.Flow +{{/reactive}} +import org.springframework.stereotype.Service +@Service +{{#operations}} + class {{classname}}ServiceImpl : {{classname}}Service { + {{#operation}} + + override {{#reactive}}{{^isArray}}suspend {{/isArray}}{{/reactive}}fun {{operationId}}({{#allParams}}{{paramName}}: {{^isBodyParam}}{{>optionalDataType}}{{/isBodyParam}}{{#isBodyParam}}{{^reactive}}{{>optionalDataType}}{{/reactive}}{{#reactive}}{{^isArray}}{{>optionalDataType}}{{/isArray}}{{#isArray}}Flow<{{{baseType}}}>{{/isArray}}{{/reactive}}{{/isBodyParam}}{{^-last}}, {{/-last}}{{/allParams}}): {{>returnTypes}} { + TODO("Implement me") + } + {{/operation}} + } +{{/operations}} diff --git a/templates/springdocDocumentationConfig.mustache b/templates/springdocDocumentationConfig.mustache new file mode 100644 index 00000000..95c4799c --- /dev/null +++ b/templates/springdocDocumentationConfig.mustache @@ -0,0 +1,53 @@ +package {{basePackage}} + +import org.springframework.context.annotation.Bean +import org.springframework.context.annotation.Configuration + +import io.swagger.v3.oas.models.OpenAPI +import io.swagger.v3.oas.models.info.Info +import io.swagger.v3.oas.models.info.Contact +import io.swagger.v3.oas.models.info.License +import io.swagger.v3.oas.models.Components +import io.swagger.v3.oas.models.security.SecurityScheme + +@Configuration +class SpringDocConfiguration { + +@Bean +fun apiInfo(): OpenAPI { +return OpenAPI() +.info( +Info(){{#appName}} + .title("{{appName}}"){{/appName}} +.description("{{{appDescription}}}"){{#termsOfService}} + .termsOfService("{{termsOfService}}"){{/termsOfService}}{{#openAPI}}{{#info}}{{#contact}} + .contact( + Contact(){{#infoName}} + .name("{{infoName}}"){{/infoName}}{{#infoUrl}} + .url("{{infoUrl}}"){{/infoUrl}}{{#infoEmail}} + .email("{{infoEmail}}"){{/infoEmail}} + ){{/contact}}{{#license}} + .license( + License() + {{#licenseInfo}}.name("{{licenseInfo}}") + {{/licenseInfo}}{{#licenseUrl}}.url("{{licenseUrl}}") + {{/licenseUrl}} + ){{/license}}{{/info}}{{/openAPI}} +.version("{{appVersion}}") +){{#hasAuthMethods}} + .components( + Components(){{#authMethods}} + .addSecuritySchemes("{{name}}", SecurityScheme(){{#isBasic}} + .type(SecurityScheme.Type.HTTP) + .scheme("{{scheme}}"){{#bearerFormat}} + .bearerFormat("{{bearerFormat}}"){{/bearerFormat}}{{/isBasic}}{{#isApiKey}} + .type(SecurityScheme.Type.APIKEY){{#isKeyInHeader}} + .`in`(SecurityScheme.In.HEADER){{/isKeyInHeader}}{{#isKeyInQuery}} + .`in`(SecurityScheme.In.QUERY){{/isKeyInQuery}}{{#isKeyInCookie}} + .`in`(SecurityScheme.In.COOKIE){{/isKeyInCookie}} + .name("{{keyParamName}}"){{/isApiKey}}{{#isOAuth}} + .type(SecurityScheme.Type.OAUTH2){{/isOAuth}} + ){{/authMethods}} + ){{/hasAuthMethods}} +} +} diff --git a/templates/springfoxDocumentationConfig.mustache b/templates/springfoxDocumentationConfig.mustache new file mode 100644 index 00000000..ec08792e --- /dev/null +++ b/templates/springfoxDocumentationConfig.mustache @@ -0,0 +1,66 @@ +package {{basePackage}} + +import org.springframework.beans.factory.annotation.Value +import org.springframework.context.annotation.Bean +import org.springframework.context.annotation.Configuration +import org.springframework.web.util.UriComponentsBuilder +import springfox.documentation.builders.ApiInfoBuilder +import springfox.documentation.builders.RequestHandlerSelectors +import springfox.documentation.service.ApiInfo +import springfox.documentation.service.Contact +import springfox.documentation.spi.DocumentationType +import springfox.documentation.spring.web.paths.Paths +import springfox.documentation.spring.web.paths.RelativePathProvider +import springfox.documentation.spring.web.plugins.Docket +import springfox.documentation.swagger2.annotations.EnableSwagger2 +import {{javaxPackage}}.servlet.ServletContext + + +{{>generatedAnnotation}} +@Configuration +@EnableSwagger2 +class SpringFoxConfiguration { + +fun apiInfo(): ApiInfo { +return ApiInfoBuilder() +.title("{{appName}}") +.description("{{{appDescription}}}") +.license("{{licenseInfo}}") +.licenseUrl("{{licenseUrl}}") +.termsOfServiceUrl("{{infoUrl}}") +.version("{{appVersion}}") +.contact(Contact("", "", "{{infoEmail}}")) +.build() +} + +@Bean +{{=<% %>=}} +fun customImplementation(servletContext: ServletContext, @Value("\${openapi.<%title%>.base-path:<%>defaultBasePath%>}") basePath: String): Docket { +<%={{ }}=%> +return Docket(DocumentationType.SWAGGER_2) +.select() +.apis(RequestHandlerSelectors.basePackage("{{apiPackage}}")) +.build() +.pathProvider(BasePathAwareRelativePathProvider(servletContext, basePath)) +.directModelSubstitute(java.time.LocalDate::class.java, java.sql.Date::class.java) +.directModelSubstitute(java.time.OffsetDateTime::class.java, java.util.Date::class.java) +.apiInfo(apiInfo()) +} + +class BasePathAwareRelativePathProvider(servletContext: ServletContext, private val basePath: String) : +RelativePathProvider(servletContext) { + +override fun applicationPath(): String { +return Paths.removeAdjacentForwardSlashes( +UriComponentsBuilder.fromPath(super.applicationPath()).path(basePath).build().toString() +) +} + +override fun getOperationPath(operationPath: String): String { +val uriComponentsBuilder = UriComponentsBuilder.fromPath("/") +return Paths.removeAdjacentForwardSlashes( +uriComponentsBuilder.path(operationPath.replaceFirst("^$basePath", "")).build().toString() +) +} +} +} diff --git a/templates/typeInfoAnnotation.mustache b/templates/typeInfoAnnotation.mustache new file mode 100644 index 00000000..acc68dc7 --- /dev/null +++ b/templates/typeInfoAnnotation.mustache @@ -0,0 +1,8 @@ +{{#jackson}} + + @JsonTypeInfo(use = JsonTypeInfo.Id.NAME, include = JsonTypeInfo.As.PROPERTY, property = "{{{discriminator.propertyBaseName}}}", visible = true) + @JsonSubTypes( + {{#discriminator.mappedModels}} + JsonSubTypes.Type(value = {{modelName}}::class, name = "{{^vendorExtensions.x-discriminator-value}}{{mappingName}}{{/vendorExtensions.x-discriminator-value}}{{#vendorExtensions.x-discriminator-value}}{{{vendorExtensions.x-discriminator-value}}}{{/vendorExtensions.x-discriminator-value}}"){{^-last}},{{/-last}} + {{/discriminator.mappedModels}} + ){{/jackson}}