Automating OpenApi Spec Validation with Gradle and Spring Boot

Recently, the front-end team I’m on really saw how the back-end team was swamped. During the sprint retro, we expressed that we wanted to help out any way we could. So, with their help, we got set up in IntelliJ and back-end’s Spring Boot application. My team ended up getting wrapped up in our main projects. I put aside some time on my own to look at back-end’s backlog. After touching base with them and seeing where I could best help them, I picked up a technical debt ticket around automating the validation of the OpenAPI spec.

It was really a great adventure and learning opportunity! I had never worked before with Gradle and Spring Boot in any professional capacity, but I know my way around Gradle now. 😀

The requirements were:

  • the validation would be automated
  • it could be integrated into the CI/CD flow with GitHub actions

At first, I fell down a deep rabbit hole with trying to implement a custom Gradle Task with plain Java files. The Gradle 8.4 documentation actually pointed people to write them in Groovy or Kotlin, but I think I must have hit a random blog through a Google search, and gotten stuck there. I’m sure it could have worked, but I had already surpassed my initial timebox.

Then, I tried to solve it the simplest way, by using the springdoc-OpenAI Gradle plugin. But that required to run the application, and we wanted to avoid that in the CI/CD flow.

Finally, I went back to the original concept of custom Gradle Tasks, but kept it simple with plain Groovy. Then, I found the winning combination. A blog suggested validating the OpenAPI spec in test cases. The final solution was as follows:

  1. Writing custom Gradle Tasks in plain Groovy that executed test cases
  2. This case would manually generate the OpenAPI spec from the code
  3. Then executing a local shell script that loads a npm library to manually validate the generated spec.

It’s lightweight, it can run easily in a CI/CD flow, and implementing it with straight Groovy keeps the `build.gradle` file light.

“Final product!”

I added the custom task to the gradlew command in the appropriate GitHub Action workflow file.

I added the variables extraction.api-spec.json and springdoc.api-docs.path to the application.properties file.

build.gradle

tasks.register("validateOpenApiDocs", Exec) {

    group = "documentation"

    description = "Validates locally generated OpenApi spec"

    def stdout = new ByteArrayOutputStream()

    ignoreExitValue true

    doFirst() {

        println "Validating generated Open API docs..."

    }

    commandLine './validate-docs.sh'

    doLast() {

        ObjectMapper mapper = new ObjectMapper();

        JsonNode taskResult = mapper.readTree(stdout.toString())

        if (taskResult.valid.equals(false)) {

            println "FAILED"

            println taskResult.errors

        } else {

            println "OpenAPI spec validation passed!"

        }

    }

}

validate-docs.sh

#!/bin/bash

npx -p @seriousme/openapi-schema-validator validate-api docs.json

ApiSpecJsonFileExtractor.java

@SpringBootTest

@ActiveProfiles("test")

public class ApiSpecJsonFileExtractor {

  @Value("${extraction.api-spec.json}")

  String filename;

  @Value("${springdoc.api-docs.path}")

  String apiDocJsonPath;

<snip>

  MockMvc mvc;

  @BeforeEach

  public void setup() {

    mvc = MockMvcBuilders.webAppContextSetup(context).apply(springSecurity()).build();

  }

  @Test

  void extractApiSpecJsonFile() throws Exception {

    File file = new File(filename);

    Path filePath = file.toPath();

    if (file.exists()) {

      Assertions.assertThat(file.isFile()).isTrue();

    } else {

      Path path = file.getParentFile().toPath();

      if (Files.notExists(path)) {

        Files.createDirectory(path);

      }

      if (Files.notExists(filePath)) {

        Files.createFile(file.toPath());

      }

    }

    mvc.perform(MockMvcRequestBuilders.get(apiDocJsonPath))

        .andDo(

            result ->

                Files.write(file.toPath(), result.getResponse().getContentAsString().getBytes()));

  }

}

My 6 Books of 2015

This year, I committed to reading 24 books before the end of December. Mission was accomplished about two months early. I found great satisfaction in the achievement and the process.

This is a short piece on the six most memorable books from this year’s challenge.

The One where You Can’t Think the Same Again. Karen E. Field’s Racecraft has left its mark on my thinking about politics, my own privilege, and how pervasive race is. The tripartite formula of race, racism, and racecraft seems inescapable to me now. Thank you, Dr. Field. And thank you to all friends who were patient as I spoke to them about it at great lengths. A friend, NYTimes journalist Ron Nixon, wrapped it up in a single sentence: “Race doesn’t exist, but it’s real.”

The One that Went Straight for the Heartular. The Art of Asking by Amanda Palmer. This one made me cry, more than my usual quota of tears (three merciless bouts every 5 or 6 years, followed by drought and emotional detachment), because I related to her as a musician and as the non-conformist. Wonderful writing, a wealth of powerful aphorisms, and courage imparted from Palmer’s fearless life. I’ve never cared for another stranger, through his life and death, as for her friend Anthony. I wished I could have been by his deathbed and at his funeral.

The One that Will Be Forgotten. Andy Weir’s The Martian is a blogpost gone nuclear. No literary value, no journey, no punch. Just worldly dialogue and a three-act structure.

The One that Had Me Creasing*. Best White and Other Anxious Delusions by South African journalist Rebecca Davis. She’s the patron saint of projectile spit from your mouth while laughing out loud at her cutting, thought-provoking, and sharp prose. An excellent book about South African politics, history, and current affairs. *London Cockney dysphemism about your stomach creasing when you laugh hard.

The One that Made Me GO WTF Mate. Every Spy a Prince is a detailed monograph about the history of the Israeli intelligence. It’s well-researched, well-written, and fascinating. I don’t put anything past Mossad anymore and I understand how Shin Bet tracked down the alleged killers of the two Jewish teenagers last year, the incident that started the war in Gaza.

The One where I Must Write a Novel. Ahmed Mourad’s Arabic-language Diamond Dust (Ar: ‘Torab al-Mas’) starts off with excruciating detail (think Joyce in Ulysses), and then accelerates into a superb novel about revenge and family, only marred by a romcom ending. Why you do zis, Ahmed? (I would recommend learning Arabic to read this novel.)

One thing changed the way I read this year and that’s keeping a journal, to note down great sentences, turns of phrase, ideas or other books to look into, or just thoughts generated while reading. Reading is no longer a race against time or a milestone or simply ticking off names on a list. It’s a way to become the owner of the process and phenomenon of thinking and developing your thinking. Shout out to Maria Popova, of Brainpickings.org, for the inspiration.

You can check out all the other books I read here: https://www.goodreads.com/user_challenges/2425711

 

Verified by ExactMetrics