diff --git a/Sources/CodeGenerator/Models/GenerationModels/OperationGenerationModel.swift b/Sources/CodeGenerator/Models/GenerationModels/OperationGenerationModel.swift index fc6ea8c8..56cd2209 100644 --- a/Sources/CodeGenerator/Models/GenerationModels/OperationGenerationModel.swift +++ b/Sources/CodeGenerator/Models/GenerationModels/OperationGenerationModel.swift @@ -1,6 +1,6 @@ // // OperationGenerationModel.swift -// +// // // Created by Александр Кравченков on 19.10.2021. // @@ -63,11 +63,13 @@ public struct OperationGenerationModel: Encodable { public let requestModel: Reference? public let pathParameters: [ParameterModel] + public let headerParameters: [ParameterModel] public let queryParameters: [ParameterModel] public let requestGenerationModel: DataGenerationModel? public let responseGenerationModel: Keyed? public let allGenerationResponses: [ResponseGenerationModel]? + public let id: String? init(operationModel: OperationModel) { self.httpMethod = operationModel.httpMethod @@ -82,6 +84,7 @@ public struct OperationGenerationModel: Encodable { .map { $0.value } .sorted { $0.name < $1.name } self.pathParameters = allParameters.filter { $0.location == .path } + self.headerParameters = allParameters.filter { $0.location == .header } self.queryParameters = allParameters.filter { $0.location == .query } let request = operationModel.requestModel?.value @@ -108,6 +111,7 @@ public struct OperationGenerationModel: Encodable { responses: response.values.map { DataGenerationModel(dataModel: $0) } ) } + self.id = operationModel.id } } diff --git a/Sources/CodeGenerator/Models/PropertyModel.swift b/Sources/CodeGenerator/Models/PropertyModel.swift index ddf0207a..45dff6ed 100644 --- a/Sources/CodeGenerator/Models/PropertyModel.swift +++ b/Sources/CodeGenerator/Models/PropertyModel.swift @@ -80,6 +80,12 @@ public struct PropertyModel { public let type: PossibleType public let isNullable: Bool public let pattern: String? + public let example: Any? + public let format: String? + public let minimum: Int? + public let maximum: Int? + public let maxLength: Int? + public let minLength: Int? /// This value will be used as type for generation public let typeModel: ItemTypeModel @@ -88,7 +94,13 @@ public struct PropertyModel { description: String?, type: PropertyModel.PossibleType, isNullable: Bool, - pattern: String?) { + pattern: String?, + example: Any?, + format: String?, + minimum: Int?, + maximum: Int?, + minLength: Int?, + maxLength: Int?) { self.name = name self.description = description self.type = type @@ -99,6 +111,12 @@ public struct PropertyModel { enumTypeName: type.enumTypeName, aliasTypeName: type.aliasTypeName) self.pattern = pattern + self.example = example + self.format = format + self.minimum = minimum + self.maximum = maximum + self.minLength = minLength + self.maxLength = maxLength } } diff --git a/Sources/CodeGenerator/Models/ServiceMethod/OperationModel.swift b/Sources/CodeGenerator/Models/ServiceMethod/OperationModel.swift index 94fd48ac..381041b4 100644 --- a/Sources/CodeGenerator/Models/ServiceMethod/OperationModel.swift +++ b/Sources/CodeGenerator/Models/ServiceMethod/OperationModel.swift @@ -1,6 +1,6 @@ // // OperationModel.swift -// +// // // Created by Александр Кравченков on 17.12.2020. // @@ -56,19 +56,21 @@ public struct OperationModel: Encodable { public let parameters: [Reference]? public let responses: [Reference]? public let requestModel: Reference? + public let id: String? init(httpMethod: String, summary: String?, description: String?, parameters: [Reference]?, responses: [Reference]?, - requestModel: Reference?) { + requestModel: Reference?, + id: String?) { self.httpMethod = httpMethod self.summary = summary - self.description = description self.parameters = parameters self.responses = responses self.requestModel = requestModel + self.id = id } } diff --git a/Sources/CodeGenerator/Stages/GenerationStage/TemplateFiller/DefaultTemplateFiller.swift b/Sources/CodeGenerator/Stages/GenerationStage/TemplateFiller/DefaultTemplateFiller.swift index 530c6bcc..cd3a0bcd 100644 --- a/Sources/CodeGenerator/Stages/GenerationStage/TemplateFiller/DefaultTemplateFiller.swift +++ b/Sources/CodeGenerator/Stages/GenerationStage/TemplateFiller/DefaultTemplateFiller.swift @@ -76,6 +76,14 @@ public class DefaultTemplateFiller: TemplateFiller { $0.withEscapedCharacters() } + templateExtension.registerStringFilter("splitByHyphenAndGetLastNonEmpty") { + $0.splitByHyphenAndGetLastNonEmpty() + } + + templateExtension.registerStringFilter("splitByUppercaseAndGetLast") { + $0.splitByUppercaseAndGetLast() + } + return templateExtension } diff --git a/Sources/CodeGenerator/Stages/TreeParserStage/TreeParser/Resolver.swift b/Sources/CodeGenerator/Stages/TreeParserStage/TreeParser/Resolver.swift index 4e1ca357..376df47b 100644 --- a/Sources/CodeGenerator/Stages/TreeParserStage/TreeParser/Resolver.swift +++ b/Sources/CodeGenerator/Stages/TreeParserStage/TreeParser/Resolver.swift @@ -191,13 +191,25 @@ public class Resolver { description: property.description, type: .array(.init(name: "", itemsType: try arrayItemTypeUnwrapper(arr.itemsType))), isNullable: property.nullable, - pattern: property.pattern) + pattern: property.pattern, + example: property.example, + format: property.format, + minimum: property.minimum, + maximum: property.maximum, + minLength: property.minLength, + maxLength: property.maxLength) case .simple(let val): return .init(name: property.name, description: property.description, type: try propertyTypeUnwrapper(val), isNullable: property.nullable, - pattern: property.pattern) + pattern: property.pattern, + example: property.example, + format: property.format, + minimum: property.minimum, + maximum: property.maximum, + minLength: property.minLength, + maxLength: property.maxLength) } } diff --git a/Sources/CodeGenerator/Stages/TreeParserStage/TreeParser/TreeParser.swift b/Sources/CodeGenerator/Stages/TreeParserStage/TreeParser/TreeParser.swift index 331fbf1b..01f774aa 100644 --- a/Sources/CodeGenerator/Stages/TreeParserStage/TreeParser/TreeParser.swift +++ b/Sources/CodeGenerator/Stages/TreeParserStage/TreeParser/TreeParser.swift @@ -1,6 +1,6 @@ // // File.swift -// +// // // Created by Александр Кравченков on 17.12.2020. // @@ -67,7 +67,7 @@ public struct TreeParser { func parse(operation: OperationNode, current: DependencyWithTree, other: [DependencyWithTree]) throws -> OperationModel { let params = try operation.parameters.map { parameter -> Reference in - + return try wrap( self.parametersParser.parse(parameter: parameter, current: current, other: other), message: "While parsing parameter \(parameter.view)") @@ -94,7 +94,8 @@ public struct TreeParser { description: operation.description, parameters: params, responses: responses, - requestModel: requestBody + requestModel: requestBody, + id: operation.id ) } } diff --git a/Sources/Common/Extensions/String.swift b/Sources/Common/Extensions/String.swift index b0543e3c..86133a5f 100644 --- a/Sources/Common/Extensions/String.swift +++ b/Sources/Common/Extensions/String.swift @@ -144,6 +144,49 @@ extension String { .joined() } + /// Splits string by '-' and returns last element. + public func splitByHyphenAndGetLastNonEmpty() -> String { + let parts = self.components(separatedBy: "-").filter { !$0.isEmpty } + return parts.last ?? "" + } + + /// Splits string by Uppercase letter and returns last element. + /// Example: UpdateProfileRequest returns Request + public func splitByUppercaseAndGetLast() -> String { + do { + let pattern = "(? String { return self .split(whereSeparator: { $0 == "/" || $0 == "_" }) diff --git a/Sources/GASTBuilder/Builders/AnySchemaBuilder.swift b/Sources/GASTBuilder/Builders/AnySchemaBuilder.swift index 5d25b976..dc2b1a65 100644 --- a/Sources/GASTBuilder/Builders/AnySchemaBuilder.swift +++ b/Sources/GASTBuilder/Builders/AnySchemaBuilder.swift @@ -161,34 +161,66 @@ public struct AnySchemaBuilder: SchemaBuilder { func build(object: ObjectSchema, meta: Metadata, name: String, apiDefinitionFileRef: String) throws -> SchemaModelNode { let properties = try object.properties.map { property -> PropertyNode in - let type = try wrap(property.schema.extractType(), - message: "In object \(name), in property \(property.name)") - + let schema = property.schema + let type = try wrap( + schema.extractType(), + message: "In object \(name), in property \(property.name)" + ) var isNullable = property.isNullable - if self.useNewNullableDeterminationStrategy { - isNullable = property.schema.metadata.nullable + isNullable = schema.metadata.nullable } var pattern: String? + var format: String? + var minimum: Int? + var maximum: Int? + var minLength: Int? + var maxLength: Int? - switch property.schema.type { + switch schema.type { case .string(let stringSchema): pattern = stringSchema.pattern + format = stringSchema.format?.rawValue + minLength = stringSchema.minLength + maxLength = stringSchema.maxLength + + case .number(let numberSchema): + format = numberSchema.format?.rawValue + minimum = numberSchema.minimum.flatMap { Int($0) } + maximum = numberSchema.maximum.flatMap { Int($0) } + + case .integer(let integerSchema): + format = integerSchema.format?.rawValue + minimum = integerSchema.minimum + maximum = integerSchema.maximum + default: break } - return PropertyNode(name: property.name, - type: type, - description: property.schema.metadata.description, - example: property.schema.metadata.example, - nullable: isNullable, - pattern: pattern) + return PropertyNode( + name: property.name, + type: type, + description: schema.metadata.description, + example: schema.metadata.example, + nullable: isNullable, + pattern: pattern, + format: format, + minimum: minimum, + maximum: maximum, + maxLength: maxLength, + minLength: minLength + ) } - return SchemaModelNode(name: name, properties: properties, description: meta.description, apiDefinitionFileRef: apiDefinitionFileRef) + return SchemaModelNode( + name: name, + properties: properties, + description: meta.description, + apiDefinitionFileRef: apiDefinitionFileRef + ) } func build(array: ArraySchema, name: String, apiDefinitionFileRef: String) throws -> SchemaArrayNode { diff --git a/Sources/GASTBuilder/Builders/AnyServiceBuilder.swift b/Sources/GASTBuilder/Builders/AnyServiceBuilder.swift index 375a8ae8..b4105d2a 100644 --- a/Sources/GASTBuilder/Builders/AnyServiceBuilder.swift +++ b/Sources/GASTBuilder/Builders/AnyServiceBuilder.swift @@ -1,6 +1,6 @@ // // AnyServiceBuilder.swift -// +// // // Created by Александр Кравченков on 14.12.2020. // @@ -17,7 +17,7 @@ public protocol ServiceBuilder { } /// Default implementation for `ServiceBuilder` -/// +/// /// Builds `path` elements of Open-API spec /// /// **WARNING** @@ -34,7 +34,6 @@ public struct AnyServiceBuilder: ServiceBuilder { let schemaBuilder: SchemaBuilder let requestBodyBuilder: RequestBodyBuilder let responseBuilder: ResponseBuilder - public init(parameterBuilder: ParametersBuilder, schemaBuilder: SchemaBuilder, requestBodyBuilder: RequestBodyBuilder, @@ -93,7 +92,8 @@ extension AnyServiceBuilder { summary: operation.summary, parameters: params, requestBody: requestBody, - responses: responses) + responses: responses, + id: operation.generatedIdentifier) } } diff --git a/Sources/GASTTree/PropertyNode.swift b/Sources/GASTTree/PropertyNode.swift index e9d30922..61dba710 100644 --- a/Sources/GASTTree/PropertyNode.swift +++ b/Sources/GASTTree/PropertyNode.swift @@ -20,19 +20,34 @@ public struct PropertyNode { public let example: Any? public let nullable: Bool public let pattern: String? + public let format: String? + public let minimum: Int? + public let maximum: Int? + public let maxLength: Int? + public let minLength: Int? public init(name: String, type: PossibleType, description: String?, example: Any?, nullable: Bool, - pattern: String?) { + pattern: String?, + format: String?, + minimum: Int?, + maximum: Int?, + maxLength: Int?, + minLength: Int?) { self.name = name self.type = type self.description = description self.example = example self.nullable = nullable self.pattern = pattern + self.format = format + self.minimum = minimum + self.maximum = maximum + self.minLength = minLength + self.maxLength = maxLength } } diff --git a/Sources/GASTTree/Services/OperationNode.swift b/Sources/GASTTree/Services/OperationNode.swift index c5713fdd..ca0b5299 100644 --- a/Sources/GASTTree/Services/OperationNode.swift +++ b/Sources/GASTTree/Services/OperationNode.swift @@ -1,6 +1,6 @@ // // OperationNode.swift -// +// // // Created by Александр Кравченков on 14.12.2020. // @@ -27,18 +27,21 @@ public struct OperationNode { public let parameters: [Referenced] public let requestBody: Referenced? public let responses: [ResponseBody] + public let id: String? public init(method: String, description: String?, summary: String?, parameters: [Referenced], requestBody: Referenced?, - responses: [ResponseBody]) { + responses: [ResponseBody], + id: String?) { self.method = method self.description = description self.summary = summary self.parameters = parameters self.requestBody = requestBody self.responses = responses + self.id = id } } diff --git a/Sources/SurfGenKit/Models/GenerationModels/OperationGenerationModel.swift b/Sources/SurfGenKit/Models/GenerationModels/OperationGenerationModel.swift index 92841f42..c3335391 100644 --- a/Sources/SurfGenKit/Models/GenerationModels/OperationGenerationModel.swift +++ b/Sources/SurfGenKit/Models/GenerationModels/OperationGenerationModel.swift @@ -1,6 +1,6 @@ // // OperationGenerationModel.swift -// +// // // Created by Dmitry Demyanov on 04.11.2020. // @@ -11,7 +11,7 @@ enum HttpMethod: String { case patch case put case delete - + var name: String { switch self { case .get, .post, .delete: @@ -29,7 +29,7 @@ enum ResponseBody: Equatable { } public struct OperationGenerationModel { - + private enum Constants { static let multipartModel = "MultipartModel" } @@ -46,11 +46,12 @@ public struct OperationGenerationModel { let hasBody: Bool var requestBody: RequestBodyGenerationModel? + let id: String? private(set) var hasUndefinedResponseBody = false private(set) var hasResponseModel = false private(set) var responseModel: String? - + init(name: String, description: String?, path: PathGenerationModel, @@ -58,7 +59,8 @@ public struct OperationGenerationModel { pathParameters: [ParameterGenerationModel], queryParameters: [ParameterGenerationModel], requestBody: RequestBodyGenerationModel.BodyType?, - responseBody: ResponseBody) { + responseBody: ResponseBody, + id: String?) { self.name = name self.hasDescription = description != nil self.description = description @@ -70,7 +72,7 @@ public struct OperationGenerationModel { self.queryParameters = queryParameters self.hasBody = requestBody != nil self.requestBody = RequestBodyGenerationModel(type: requestBody) - + self.id = id switch responseBody { case .model(let modelName): self.hasResponseModel = true diff --git a/Sources/SurfGenKit/Parsers/OperationNodeParser.swift b/Sources/SurfGenKit/Parsers/OperationNodeParser.swift index 68746a0d..b4ad4d10 100644 --- a/Sources/SurfGenKit/Parsers/OperationNodeParser.swift +++ b/Sources/SurfGenKit/Parsers/OperationNodeParser.swift @@ -1,6 +1,6 @@ // // OperationNodeParser.swift -// +// // // Created by Dmitry Demyanov on 07.11.2020. // @@ -17,7 +17,7 @@ class OperationNodeParser { private let mediaContentParser: MediaContentNodeParser private let parametersParser: ParametersNodeParser - + public var logger: Loger = DefaultLogger.default private let platform: Platform init(mediaContentParser: MediaContentNodeParser, parametersParser: ParametersNodeParser, platform: Platform) { @@ -80,7 +80,7 @@ class OperationNodeParser { let responseBody = try wrap(mediaContentParser.parseResponseBody(node: operation.subNodes.responseBodyNode, forOperationName: name), with: ErrorMessages.errorMessage(for: name)) - + return OperationGenerationModel(name: name, description: description, @@ -88,8 +88,10 @@ class OperationNodeParser { httpMethod: method, pathParameters: parameters.filter { $0.location == .path }, queryParameters: parameters.filter { $0.location == .query }, + headerParameters: parameters.filter { $0.location == .header }, requestBody: requestBody, - responseBody: responseBody) + responseBody: responseBody, + id: name) } - + } diff --git a/Templates/v2/Java/api.stencil b/Templates/v2/Java/api.stencil new file mode 100644 index 00000000..7682ad33 --- /dev/null +++ b/Templates/v2/Java/api.stencil @@ -0,0 +1,39 @@ +package {{package}}.api.contract; + +import {{package}}.api.dto.*; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.tags.Tag; +import jakarta.validation.Valid; +import jakarta.validation.constraints.NotNull; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.*; + +@Tag(name = "{{ service.name }} API") +public interface {{ service.name }}Api { + +{%- paths: for path in service.paths %}{% operations: for operation in path.operations %} + @Operation(summary = "{{ operation.summary }}") +{%- if operation.httpMethod|lowercase == "delete" %} + @ResponseStatus(HttpStatus.NO_CONTENT) +{%- endif %} + @{{ operation.httpMethod|capitalizeFirstLetter }}Mapping("{{ path.path }}") +{%- if operation.responseGenerationModel.key != "204" %} + @NotNull +{%- endif %} +{%- if operation.httpMethod|lowercase == "delete" %} + void{% else %} + ResponseEntity<{{ operation.responseGenerationModel.value.typeNames|join }}> + {%- endif %} {{operation.id}}( + {%- for parameter in operation.pathParameters %}@PathVariable("{{ parameter.name }}") final {{ parameter.typeModel.name|capitalizeFirstLetter }} {{ parameter.name|snakeCaseToCamelCase }} + {%- if not forloop.last or operation.headerParameters or operation.queryParameters or operation.requestGenerationModel.typeNames%}, {% endif -%}{% endfor -%} + {% for parameter in operation.headerParameters %}@RequestHeader(name = "{{ parameter.name }}", required={{ parameter.isRequired }}) final {{ parameter.typeModel.name|capitalizeFirstLetter }} {{ parameter.name|splitByHyphenAndGetLastNonEmpty }} + {%- if not forloop.last or operation.queryParameters or operation.requestGenerationModel.typeNames%}, {% endif -%}{% endfor -%} + {% for parameter in operation.queryParameters%}@RequestParam(name = "{{ parameter.name }}", required={{ parameter.isRequired }}) final {{ parameter.typeModel.name|capitalizeFirstLetter }} {{ parameter.name|snakeCaseToCamelCase }} + {%- if not forloop.last or operation.requestGenerationModel.typeNames%}, {% endif -%}{% endfor -%} + {% for requestModelName in operation.requestGenerationModel.typeNames %}@Valid @NotNull @RequestBody final {{ requestModelName|capitalizeFirstLetter }} {{ requestModelName|snakeCaseToCamelCase }} + {%- if not forloop.last%}, {% endif -%}{% endfor -%}); + {%- if not forloop.paths.last or not forloop.operations.last %} + {% endif -%} +{% endfor %}{% endfor %} +} \ No newline at end of file diff --git a/Templates/v2/Java/config/java.config.yaml b/Templates/v2/Java/config/java.config.yaml new file mode 100644 index 00000000..7a4d0d85 --- /dev/null +++ b/Templates/v2/Java/config/java.config.yaml @@ -0,0 +1,33 @@ +useNewNullableDeterminationStrategy: false + +prefixesToCutDownInServiceNames: + - /api/v1.1 + - /api/1.1 +environment: + package: + +analytcsConfig: + logstashEnpointURI: http://logs.ps.surfstudio.ru + payload: + project: TEST + +templates: + - type: service + nameSuffix: Api + fileExtension: java + templatePath: + destinationPath: + - type: service + nameSuffix: Controller + fileExtension: java + templatePath: + destinationPath: + - type: model + nameSuffix: + fileExtension: java + templatePath: + destinationPath: + - type: enum + fileExtension: java + templatePath: + destinationPath: \ No newline at end of file diff --git a/Templates/v2/Java/controller.stencil b/Templates/v2/Java/controller.stencil new file mode 100644 index 00000000..6b98ddf8 --- /dev/null +++ b/Templates/v2/Java/controller.stencil @@ -0,0 +1,63 @@ +package {{package}}.controller; + +import {{package}}.api.contract.{{ service.name }}Api; +import {{package}}.service.{{ service.name }}Service; +import {{package}}.api.dto.*; +import lombok.RequiredArgsConstructor; +import org.jetbrains.annotations.NotNull; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.RestController; + +@RestController +@RequiredArgsConstructor +public class {{ service.name }}Controller implements {{ service.name }}Api { + private final {{ service.name }}Service {{ service.name|lowercaseFirstLetter }}Service; + + {%- for path in service.paths -%} + {%- for operation in path.operations%} + + @Override + public {% if operation.responseGenerationModel.key == "204" %}void{% else %}ResponseEntity<{{ operation.responseGenerationModel.value.typeNames|join }}>{% endif %} {{ operation.id }}( + {%- for parameter in operation.pathParameters %}final {{ parameter.typeModel.name|capitalizeFirstLetter }} {{ parameter.name|snakeCaseToCamelCase }} + {%- if not forloop.last or operation.headerParameters or operation.queryParameters or operation.requestGenerationModel.typeNames%}, {% endif -%}{% endfor -%} + {% for parameter in operation.headerParameters %}final {{ parameter.typeModel.name|capitalizeFirstLetter }} {{ parameter.name|splitByHyphenAndGetLastNonEmpty }} + {%- if not forloop.last or operation.queryParameters or operation.requestGenerationModel.typeNames%}, {% endif -%}{% endfor -%} + {% for parameter in operation.queryParameters %}final {{ parameter.typeModel.name|capitalizeFirstLetter }} {{ parameter.name|snakeCaseToCamelCase }} + {%- if not forloop.last or operation.requestGenerationModel.typeNames%}, {% endif -%}{% endfor -%} + {% for requestModelName in operation.requestGenerationModel.typeNames -%}final {{ requestModelName|capitalizeFirstLetter }} {{ requestModelName|snakeCaseToCamelCase }} + {%- if not forloop.last%}, {% endif -%}{% endfor -%}) { + {%- if operation.responseGenerationModel.key == "204" %} + {{ service.name|lowercaseFirstLetter }}Service.{{ operation.id }}( + {%- for parameter in operation.pathParameters %}{{ parameter.name|snakeCaseToCamelCase }} + {%- if not forloop.last or operation.headerParameters or operation.queryParameters or operation.requestGenerationModel.typeNames%}, {% endif -%}{% endfor -%} + {%- for parameter in operation.headerParameters %}{{ parameter.name|splitByHyphenAndGetLastNonEmpty }} + {%- if not forloop.last or operation.queryParameters or operation.requestGenerationModel.typeNames%}, {% endif -%}{% endfor -%} + {%- for parameter in operation.queryParameters %}{{ parameter.name|snakeCaseToCamelCase }} + {%- if not forloop.last or operation.requestGenerationModel.typeNames%}, {% endif -%}{% endfor -%} + {%- for requestModelName in operation.requestGenerationModel.typeNames %}{{ requestModelName|snakeCaseToCamelCase }} + {%- if not forloop.last%}, {% endif -%}{% endfor -%}); + {%- else %} + return {% if operation.responseGenerationModel.key == "201" %}new ResponseEntity<>({{ service.name|lowercaseFirstLetter }}Service.{{ operation.id }}( + {%- for parameter in operation.pathParameters %}{{ parameter.name|snakeCaseToCamelCase }} + {%- if not forloop.last or operation.headerParameters or operation.queryParameters or operation.requestGenerationModel.typeNames%}, {% endif -%}{% endfor -%} + {% for parameter in operation.queryParameters %}{{ parameter.name|snakeCaseToCamelCase }} + {%- if not forloop.last or operation.queryParameters or operation.requestGenerationModel.typeNames%}, {% endif -%}{% endfor -%} + {% for parameter in operation.headerParameters %}{{ parameter.name|splitByHyphenAndGetLastNonEmpty }} + {%- if not forloop.last or operation.requestGenerationModel.typeNames%}, {% endif -%}{% endfor -%} + {% for requestModelName in operation.requestGenerationModel.typeNames %}{{ requestModelName|snakeCaseToCamelCase }} + {%- if not forloop.last%}, {% endif -%}{% endfor -%}), HttpStatus.CREATED); + {%- else -%} + ResponseEntity.ok({{ service.name|lowercaseFirstLetter }}Service.{{ operation.id }}( + {%- for parameter in operation.pathParameters %}{{ parameter.name|snakeCaseToCamelCase }} + {%- if not forloop.last or operation.headerParameters or operation.queryParameters or operation.requestGenerationModel.typeNames%}, {% endif -%}{% endfor -%} + {% for parameter in operation.queryParameters %}{{ parameter.name|snakeCaseToCamelCase }} + {%- if not forloop.last or operation.queryParameters or operation.requestGenerationModel.typeNames%}, {% endif -%}{% endfor -%} + {% for parameter in operation.headerParameters %}{{ parameter.name|splitByHyphenAndGetLastNonEmpty }} + {%- if not forloop.last or operation.requestGenerationModel.typeNames%}, {% endif -%}{% endfor -%} + {% for requestModelName in operation.requestGenerationModel.typeNames %}{{ requestModelName|snakeCaseToCamelCase }} + {%- if not forloop.last%}, {% endif -%}{% endfor -%})); + {%- endif -%} + {%- endif %} + }{% endfor %}{% endfor %} +} \ No newline at end of file diff --git a/Templates/v2/Java/dto.stencil b/Templates/v2/Java/dto.stencil new file mode 100644 index 00000000..c1c87655 --- /dev/null +++ b/Templates/v2/Java/dto.stencil @@ -0,0 +1,46 @@ +package {{package}}.api.dto; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; +import lombok.NoArgsConstructor; +import lombok.AllArgsConstructor; +{%- if model.name|splitByUppercaseAndGetLast == "Request" %} +import jakarta.validation.constraints.*; +import jakarta.annotation.Nullable; +{%- else %} +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; +{%- endif -%} +{%- for property in model.properties %} +{%- if property.typeModel.isArray %} +import java.util.List; +{%- elif property.format == "date-time" %} +import java.time.LocalDateTime; +{%- elif property.format == "date" %} +import java.time.LocalDate; +{%- elif property.format == "uuid" %} +import org.hibernate.validator.constraints.UUID; +{%- endif -%} +{% endfor %} + +@Data +@NoArgsConstructor +@AllArgsConstructor +{%- if model.description %} +@Schema(description = "{{ model.description }}"){% endif %} +public class {{ model.name }} { + {%- for property in model.properties %} + @Schema(description = "{{ property.description }}"{% if property.example %}, example = "{{ property.example }}"{% else %}){% endif %}{% if property.format == "uuid" %} + @UUID{% endif %}{% if property.pattern %} + @Pattern(regexp = "{{ property.pattern|withEscapedCharacters }}"){% endif %}{% if not property.isNullable and property.typeModel.name != "string" %} + @NotNull{% elif not property.isNullable and property.typeModel.name == "string" %} + @NotBlank{% elif property.isNullable %} + @Nullable{% endif %}{% if property.typeModel.name == "string" %}{% if property.minLength or property.maxLength %} + @Size(min = {{ property.minLength }}, max = {{ property.maxLength }}){% endif %}{% elif property.typeModel.name == "integer" %}{% if property.minimum or property.maximum %} + @Min({{ property.minimum }}) + @Max({{ property.maximum }}){% endif %}{% endif %} + private {% if property.typeModel.isArray %}List<{{ property.typeModel.name|capitalizeFirstLetter }}>{% else %}{% if property.typeModel.name == "integer" %}{% if property.format == "int64" %}Long{% else %}Integer{% endif %}{% elif property.typeModel.name == "number" %}Double{% elif property.typeModel.name == "string" %}{% if property.format == "date-time" %}LocalDateTime{% elif property.format == "date" %}LocalDate{% else %}String{% endif %}{% else %}{{ property.typeModel.name|capitalizeFirstLetter }}{% endif %}{% endif %} {{ property.name|snakeCaseToCamelCase }}; + {%- if not forloop.last %} + {% endif -%} + {% endfor %} +} \ No newline at end of file diff --git a/Templates/v2/Java/enum.stencil b/Templates/v2/Java/enum.stencil new file mode 100644 index 00000000..629d8f6f --- /dev/null +++ b/Templates/v2/Java/enum.stencil @@ -0,0 +1,24 @@ +package {{package}}.api.dto; + +import lombok.AllArgsConstructor; + +@AllArgsConstructor +public enum {{ enum.name }} { + {% for caseValue in enum.cases %}{# + #}{{ caseValue|uppercase }}("{{ caseValue }}"){% if not forloop.last %},{% else %};{% endif %} + {%- endfor %} + + private final String value; + + public static {{ enum.name }} getByValue(String value) { + if (value == null) { + return null; + } + for ({{ enum.name }} item : values()) { + if (item.value.equals(value)) { + return item; + } + } + return null; + } +} \ No newline at end of file