Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,8 @@ class OasResourceRenderer constructor(val api: Api, val vrapTypeProvider: VrapTy
| description: |-
| <<${type.description.value.trim()}>>""" else ""}${if (type.fullUriParameters.size > 0) """
| parameters:
| <<${type.fullUriParameters.joinToString("\n") { it.renderUriParameter() }}>>""" else ""}
| <<${type.fullUriParameters.joinToString("\n") { it.renderUriParameter() }}>>""" else ""}${if (type.annotations.isNotEmpty()) """
| <<${type.annotations.joinToString("\n") { it.renderAnnotation() }} >>""" else ""}
| <<${type.methods.joinToString("\n") { renderMethod(it) }}>>
""".trimMargin().keepAngleIndent()
val relativePath = "resources/" + type.toResourceName()+ ".raml"
Expand All @@ -42,6 +43,7 @@ class OasResourceRenderer constructor(val api: Api, val vrapTypeProvider: VrapTy

private fun renderMethod(method: Method): String {
val bodies = method.bodies.filter { it.type != null }.filterNot { it.type is FileType }.plus(method.bodies.firstOrNull { it.type is FileType }).filterNotNull()
val annotations = method.annotations.plus(method.`is`.flatMap { it.trait.annotations }).distinctBy { it.type.name }
return """
|${method.methodName}:${if (method.securedBy.isNotEmpty()) """
| security:
Expand All @@ -50,9 +52,10 @@ class OasResourceRenderer constructor(val api: Api, val vrapTypeProvider: VrapTy
| description: |-
| <<${method.description.value.trim()}>>""" else ""}${if (method.queryParameters.isNotEmpty()) """
| parameters:
| <<${method.queryParameters.joinToString("\n") { renderQueryParameter(it) }}>>""" else ""}${if (method.bodies.any { it.type != null }) """
| <<${method.queryParameters.joinToString("\n") { renderQueryParameter(method,it) }}>>""" else ""}${if (method.bodies.any { it.type != null }) """
| requestBody:
| <<${bodies.joinToString("\n") { renderBody(it, method) } }>>""" else ""}
| <<${bodies.joinToString("\n") { renderBody(it, method) } }>>""" else ""}${if (annotations.isNotEmpty()) """
| <<${annotations.joinToString("\n") { it.renderAnnotation() }} >>""" else ""}
| responses:
| <<${method.responses.joinToString("\n") { renderResponse(it, method) }}>>
""".trimMargin().keepAngleIndent()
Expand Down Expand Up @@ -157,19 +160,27 @@ class OasResourceRenderer constructor(val api: Api, val vrapTypeProvider: VrapTy
}

public fun renderUriParameter(uriParameter: UriParameter): String {
val annotations = uriParameter.annotations.plus(uriParameter.type.annotations).distinctBy { it.type.name }
return """
|${uriParameter.name}:${if (uriParameter.type.enum.size > 0) """
| enum:
| <<${uriParameter.type.enum.joinToString("\n") { "- ${it.value}"}}>>""" else ""}
| <<${uriParameter.type.enum.joinToString("\n") { "- ${it.value}"}}>>""" else ""}${if (annotations.isNotEmpty()) """
| <<${annotations.joinToString("\n") { it.renderAnnotation() }} >>""" else ""}
| <<${uriParameter.type.renderType()}>>
| required: ${uriParameter.required}
""".trimMargin().keepAngleIndent()
}

private fun renderQueryParameter(queryParameter: QueryParameter): String {
private fun renderQueryParameter(method: Method, queryParameter: QueryParameter): String {
val traitAnnotations = method.`is`
.flatMap { it.trait.queryParameters }
.filter { it.name == queryParameter.name }
.flatMap { it.annotations.plus(it.type.annotations) }
val annotations = queryParameter.annotations.plus(queryParameter.type.annotations).plus(traitAnnotations).distinctBy { it.type.name }
return """
|- name: ${queryParameter.name}${if (queryParameter.type.default != null) """
| x-annotation-default: ${queryParameter.type.default.toYaml()}""" else ""}
| x-annotation-default: ${queryParameter.type.default.toYaml()}""" else ""}${if (annotations.isNotEmpty()) """
| <<${ annotations.joinToString("\n") { it.renderAnnotation() } } >>""" else ""}
| in: query
| required: ${queryParameter.required}
| style: form
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -75,4 +75,30 @@ class TestCodeGenerator {
private fun cleanFolder(path: String) {
Paths.get(path).toFile().deleteRecursively()
}

@Test
fun betaAnnotationRender() {
val generatorConfig = CodeGeneratorConfig(
basePackageName = "com/commercetools/importer",
outputFolder = Paths.get("build/gensrc")
)

val apiProvider = RamlApiProvider(Paths.get("src/test/resources/beta-annotation.raml"))

val dataSink = MemoryDataSink()
val generatorModule = RamlGeneratorModule(apiProvider, generatorConfig, OasBaseTypes, dataSink = dataSink)
val generatorComponent = RamlGeneratorComponent(generatorModule, OasModelModule)
generatorComponent.generateFiles()

Assertions.assertThat(dataSink.files).hasSize(1)

Assertions.assertThat(
DiffUtils.diff(
"src/test/resources/fixtures/beta-annotation.yaml".readFileLines(),
dataSink.files.get("openapi.yaml")?.trim()?.lines(),
).deltas).`as`("openapi.yaml").isEmpty()

Assertions.assertThat(dataSink.files.get("openapi.yaml")?.trim())
.isEqualTo("src/test/resources/fixtures/beta-annotation.yaml".readFile())
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
#%RAML 1.0
title: Beta Annotation Test API (direct, no trait)
baseUri: https://example.com/{version}
version: v1
mediaType: application/json

annotationTypes:
beta:
type: boolean
allowedTargets: [ TypeDeclaration ]

/test:
get:
queryParameters:
priceRecurrencePolicy?:
type: string
(beta): true
description: |
`id` of an existing RecurrencePolicy used for Product price selection.
responses:
200:
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
#%RAML 1.0
title: Beta Method Annotation Test API
baseUri: https://example.com/{version}
version: v1
mediaType: application/json

annotationTypes:
beta:
type: boolean
allowedTargets: [ Method, Trait ]

traits:
betaTrait:
(beta): true
description: a trait that marks any method using it as beta

/test:
get:
is: [ betaTrait ]
responses:
200:
26 changes: 26 additions & 0 deletions languages/oas/src/test/resources/beta-annotation-test.raml
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@

#%RAML 1.0
title: Beta Annotation Test API
baseUri: https://example.com/{version}
version: v1
mediaType: application/json

annotationTypes:
beta:
type: boolean
allowedTargets: [ TypeDeclaration ]

traits:
priceSelecting:
queryParameters:
priceRecurrencePolicy?:
type: string
(beta): true
description: |
`id` of an existing RecurrencePolicy used for Product price selection.

/test:
get:
is: [ priceSelecting ]
responses:
200:
28 changes: 28 additions & 0 deletions languages/oas/src/test/resources/beta-annotation.raml
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
#%RAML 1.0
title: Beta Annotation Regression Test API
baseUri: https://example.com/{version}
version: v1
mediaType: application/json

annotationTypes:
beta:
type: boolean
allowedTargets: [ Method, Trait, TypeDeclaration ]

traits:
priceSelecting:
queryParameters:
priceRecurrencePolicy?:
type: string
(beta): true
description: |
`id` of an existing RecurrencePolicy used for Product price selection.
betaTrait:
(beta): true
description: a trait that marks any method using it as beta

/test:
get:
is: [ priceSelecting, betaTrait ]
responses:
200:
33 changes: 33 additions & 0 deletions languages/oas/src/test/resources/fixtures/beta-annotation.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
openapi: "3.0.0"

Check failure on line 1 in languages/oas/src/test/resources/fixtures/beta-annotation.yaml

View check run for this annotation

Orca Security (EU) / Orca Security - Infrastructure as Code

[HIGH] Global Security Field Is Undefined

Details: OpenAPI specifications without a global security field may expose API endpoints without authentication or authorization controls, allowing unrestricted access. Define a global security requirement referencing schemes from securitySchemes to ensure baseline protection across all paths. Recommendation: A default security property should be defined
info:
title: Beta Annotation Regression Test API
version: "v1"

servers:
- url: https://example.com/{version}

Check warning on line 7 in languages/oas/src/test/resources/fixtures/beta-annotation.yaml

View check run for this annotation

Orca Security (EU) / Orca Security - Infrastructure as Code

[INFO] Server URL Uses Undefined Variables

Details: Server URLs containing variables must declare those variables in the Server Object's 'variables' field. Undefined variables break API specification compliance and prevent proper URL template substitution. Define all URL template variables within the corresponding Server Object. Recommendation: servers.{{0}}.url uses server object variables defined in the server object variables

paths:
/test:
get:

Check warning on line 11 in languages/oas/src/test/resources/fixtures/beta-annotation.yaml

View check run for this annotation

Orca Security (EU) / Orca Security - Infrastructure as Code

[LOW] No Global And Operation Security Defined

Details: OpenAPI specifications without security requirements at either the global or operation level lack authentication and authorization controls, allowing unrestricted access to API endpoints. Define security schemes globally or per operation to protect resources from unauthorized use. Recommendation: A security schema should be used
operationId: TestGet
description: |-
a trait that marks any method using it as beta
parameters:
- name: priceRecurrencePolicy
x-beta: true

Check warning on line 17 in languages/oas/src/test/resources/fixtures/beta-annotation.yaml

View check run for this annotation

Orca Security (EU) / Orca Security - Infrastructure as Code

[INFO] Unknown Property

Details: OpenAPI v3.0 specifications contain properties not recognized by the standard schema, indicating potential typos, deprecated fields, or custom extensions. Unknown properties may cause validation failures or unexpected API behavior. Review and remove unrecognized fields or properly namespace custom extensions using 'x-' prefix. Recommendation: The field 'x-beta' is known in the parameters object
in: query
required: false
style: form
schema:
type: "string"
explode: true
x-beta: true
responses:

Check warning on line 25 in languages/oas/src/test/resources/fixtures/beta-annotation.yaml

View check run for this annotation

Orca Security (EU) / Orca Security - Infrastructure as Code

[LOW] Response Code Missing (v3)

Details: OpenAPI operations lack standard HTTP response codes for error handling and security scenarios. Missing response definitions prevent proper API documentation, client error handling, and security response patterns. Define appropriate response codes for each operation type based on HTTP standards and security requirements. Recommendation: 500 response should be set
"200":
description: |-
200
content: {}

Check warning on line 29 in languages/oas/src/test/resources/fixtures/beta-annotation.yaml

View check run for this annotation

Orca Security (EU) / Orca Security - Infrastructure as Code

[LOW] Response on operations that should have a body has undefined schema

Details: OpenAPI responses for operations that return data must define a schema or content type. Without response schemas, API consumers cannot parse responses correctly, leading to integration failures and potential security issues from mishandled data. Define response schemas for all non-empty responses. Recommendation: paths.{{/test}}.{{get}}.responses.{{200}}.content should have at least one content-type defined

components:

Check warning on line 31 in languages/oas/src/test/resources/fixtures/beta-annotation.yaml

View check run for this annotation

Orca Security (EU) / Orca Security - Infrastructure as Code

[MEDIUM] Field 'securityScheme' On Components Is Undefined

Details: OpenAPI 3.0 specifications lack defined security schemes in the components section, preventing standardized authentication and authorization enforcement across API operations. Without security schemes, APIs cannot enforce consistent access controls. Define security schemes in the components section and apply them to operations. Recommendation: A security scheme on components should be defined

schemas:
Loading