Skip to content

Commit

Permalink
Check IANA media types in the Rule 172
Browse files Browse the repository at this point in the history
* Add a build task to generate media types configuration from IANA list
* Extend default rules configuration to support the generated configuration.
* Add a step to the release process about generating the configuration.
  • Loading branch information
vadeg committed Nov 5, 2021
1 parent e2be83e commit 524ccfd
Show file tree
Hide file tree
Showing 8 changed files with 2,090 additions and 37 deletions.
19 changes: 12 additions & 7 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -94,22 +94,27 @@ configured

1. Create a separate branch with a name `release-<release-version>`.
2. Update current version in `server/gradle.properties` from `-SNAPSHOT` to a final version.
3. Release Zally server and API using the command
3. Update mime types configuration:
```shell
./gradlew -q generate-media-types-config --info
```
4. Commit the updated file to the repository.
5. Release Zally server and API using the command
```
cd server
./gradlew clean build publishAllPublicationsToMavenRepository
```
4. Commit `server/gradle.properties` with the release version
5. Create a tag
6. Commit `server/gradle.properties` with the release version
7. Create a tag
```shell script
git tag v<release-version> -m "Version <release-version>"
```
6. Bump version in `server/gradle.properties` to the next `-SNAPSHOT`
8. Bump version in `server/gradle.properties` to the next `-SNAPSHOT`

7. Push `release` branch and tag
9. Push `release` branch and tag
```shell script
git push origin
git push origin <tag-name>
```
8. Create a Pull Request with the version update
9. Create and publish a release with a new version in GitHub
10. Create a Pull Request with the version update
11. Create and publish a release with a new version in GitHub
16 changes: 16 additions & 0 deletions server/buildSrc/build.gradle.kts
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import org.gradle.kotlin.dsl.`kotlin-dsl`

plugins {
`kotlin-dsl`
}

dependencies {
implementation(platform("com.fasterxml.jackson:jackson-bom:2.12.2"))
implementation("com.fasterxml.jackson.module:jackson-module-kotlin")
implementation("com.fasterxml.jackson.core:jackson-annotations")
implementation("com.fasterxml.jackson.core:jackson-core")
}

repositories {
mavenCentral()
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
package org.zalando.zally.task

import com.fasterxml.jackson.annotation.JsonProperty
import com.fasterxml.jackson.databind.ObjectMapper
import com.fasterxml.jackson.databind.PropertyNamingStrategies
import com.fasterxml.jackson.databind.SerializationFeature
import org.gradle.api.DefaultTask
import org.gradle.api.file.RegularFileProperty
import org.gradle.api.provider.ListProperty
import org.gradle.api.tasks.Input
import org.gradle.api.tasks.OutputFile
import org.gradle.api.tasks.TaskAction
import java.io.StringReader
import java.net.URI
import java.net.http.HttpClient
import java.net.http.HttpRequest
import java.net.http.HttpResponse

abstract class MediaTypesConfigurationTask : DefaultTask() {

private val host = "https://www.iana.org/assignments/media-types"
private val jsonMapper = ObjectMapper()
.enable(SerializationFeature.INDENT_OUTPUT)
.setPropertyNamingStrategy(PropertyNamingStrategies.UPPER_CAMEL_CASE)


@get:Input
val mediaTypes: ListProperty<String> = project.objects.listProperty(String::class.java)

@get:OutputFile
val outputFile: RegularFileProperty = project.objects.fileProperty()


@TaskAction
fun generate() {
val httpClient = HttpClient.newHttpClient()

logger.info("Generate media types configuration from IANA media types list ($host)")
val resultMediaTypes = ArrayList<String>()

for (mediaType in mediaTypes.get()) {
logger.debug("Download: ${host}/$mediaType.csv")
val request = HttpRequest.newBuilder(URI.create("${host}/$mediaType.csv")).GET().build()
val response = httpClient.send(request, HttpResponse.BodyHandlers.ofString()).body()

var counter = 0;
StringReader(response).forEachLine {
if (!isHeader(it)) {
try {
val registeredMediaType = it.split(",")[1].trim()
if (registeredMediaType.isNotBlank()) {
resultMediaTypes.add(registeredMediaType)
counter++
}
} catch (e: Exception) {
logger.error("Failed to get media type from $it", e)
}

}
}
logger.info("Downloaded $counter media types from ${host}/$mediaType.csv")
}


jsonMapper.writeValue(outputFile.get().asFile, Configuration(MediaTypesRuleConfiguration(resultMediaTypes)))
}

private fun isHeader(str: String) = str == "Name,Template,Reference"
}

class MediaTypesRuleConfiguration(@get:JsonProperty("standard_media_types") val standardMediaTypes: List<String>)

class Configuration(@get:JsonProperty("MediaTypesRule") val mediaTypesRuleConfiguration: MediaTypesRuleConfiguration)

19 changes: 19 additions & 0 deletions server/zally-ruleset-zalando/build.gradle.kts
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import org.zalando.zally.task.MediaTypesConfigurationTask

dependencies {
kapt(project(":zally-core"))

Expand All @@ -6,3 +8,20 @@ dependencies {

testImplementation(project(":zally-test"))
}

tasks.register<MediaTypesConfigurationTask>("generate-media-types-config") {
mediaTypes.set(
listOf(
"application",
"audio",
"font",
"image",
"message",
"model",
"multipart",
"text",
"video"
)
)
outputFile.set(file("${project.projectDir}/src/main/resources/media-types.json"))
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package org.zalando.zally.ruleset.zalando

import com.typesafe.config.Config
import org.zalando.zally.rule.api.Check
import org.zalando.zally.rule.api.Context
import org.zalando.zally.rule.api.Rule
Expand All @@ -20,18 +21,13 @@ import io.swagger.v3.oas.models.responses.ApiResponse
severity = Severity.SHOULD,
title = "Prefer standard media type names"
)
class MediaTypesRule {
class MediaTypesRule(config: Config) {

private val versionedMediaType = "^\\w+/[-+.\\w]+;(v|version)=\\d+$".toRegex()

private val description = "Custom media types should only be used for versioning"

private val standardMediaTypes = listOf(
"application/json",
"application/problem+json",
"application/json-patch+json",
"application/merge-patch+json"
)
private val standardMediaTypes = config.getStringList("${javaClass.simpleName}.standard_media_types")

@Check(severity = Severity.SHOULD)
fun validate(context: Context): List<Violation> =
Expand Down
Loading

0 comments on commit 524ccfd

Please sign in to comment.