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()));

  }

}

Embarrassing Report About Egypt’s Involvement in the Ceasefire Deal

A truly difficult article to read.

https://edition.cnn.com/2024/05/21/politics/sources-say-they-were-duped-by-egypt-changing-ceasefire-terms-for-hamas/index.html?utm_source=ground.news&utm_medium=referral

Worth looking at the coverage from a bird-eye’s view here on Ground News.

https://ground.news/article/egypt-changed-terms-of-gaza-ceasefire-deal-presented-to-hamas-surprising-negotiators-sources-say_fa5b9c

Hamba Kahle, Siya

My friend Siya slept in Christ 2 years ago today. He left suddenly and in much pain.

I haven’t been able to visit his grave or see his family since then.

I miss him.

Yet, I’m all blocked up now, as I try to write about him. He seems so far away. There are still digital footprints all over my life – a DM on Twitter, Google Meet, an e-mail, WhatsApp. I sometimes struggle with the question on whether I should delete or not. Does it mean that I no longer care? If they’re just 1s and 0s, why is it so hard to delete? Does deleting mean he’s gone fully, as if death is only final when it’s coupled with digital death? Charlie Kaufmann was onto something truly disturbing about modern life in the film The Eternal Sunshine of the Spotless Mind…

I miss him. He was a good egg.

The news of his passing came during a time of many deaths of friends. It was a wave of death, notwithstanding Covid. Some names and stories passed by, I unable to really deal with some because it was too much too soon. But Siya’s news broke me. I just spoken to him a few days earlier. I felt powerless and I was heartbroken. He was so young. I realized the power of a final phone call or goodbye, to help carry over the point of realization – this person is leaving now. And when you don’t have neither, death is so brutal. The idea of rituals – the services, seeing the casket, attending the burial – ease the pain of loss. Siya died during Covid, where funerals were digital and there were no tangible rituals. It was brutal, as much as death itself.

As I wrestle with the idea of removing digital traces, I look at the other side of the argument – that the actual person is gone. Maybe all digital traces aren’t memories, they’re just that. Breadcrumbs on a table or plate after a meal. The real memory – the laughs, the conversation, the teasing. That’s what you’ll remember. Not the crumbs. Sometimes, the crumbs get on my nerves and I delete them on the spot. Sometimes, I sit and stare at a message of pain or hurt. A funny thread.

There’s a hole in our mutual group of friends, sometimes I avoid those gatherings so that I don’t remember he’s gone.

I miss him. He was a good egg. He left too early.

Siya was a real person – greater than social media posts and not reducible to his pain or struggles. He loved his family and friends. He struggled with darkness. He was ambitious, driven, and a consummate dreamer. He was sporty and active. He was a Star Wars nerd and all-around tech geek. He was awake. He had a loud, roaring infectious laugh. I can hear it now in my mind…

He thrived on to-do and checklists. He had a thousand plans and ideas. He was human.

He left too early.

We were just getting started.

كلام للرجالة

الكلام ده لكل شاب و راجل مش مرتبط، اعزب،  بيفكر يخش علاقة، عايز بس مش قادر، قادر بس مقيد، مش مقتنع بس من جواه عايز اوي. واحد عايز يتعرف بس خايف.

النهارده بحتفل بست شهور في علاقة. بذلت مجهود و تفكير الاقي صيغة بالعربي لعبارة relationship بالانجلبزي من غير علامات استفهم. اظن ان مفيش الصراحة كلمة تعني a committed relationship برا نطاق الخطوبة او الجواز و ده مفهوم بحكم ان كلمة تعارف لا تساوي بالتحديد a committed relationship. Dating و relationship عبارات و افكار دخيلة على الثقافة الشرقية و في غياب مصطلحات تواكب الشرق تسخدم تلك العبارات. بس هناك فجوة رهيبة بين المفهوم الشرقي و الغربي و بالتالي نجد صعوبة في التعبير.

التفكير الوصفي (thinking on a meta level) حتة من لحمي و بالاخص التفكير المقارن بين اللغات. حسيت اوي بوجود التفكير في حياتي في اخر ستة اشهر. بتكلم عربي و انجليزي و بعدي كل فكرة و كلمة من خلال التواصل نفسه و مقارنة الكلام بين اللغات. البعد يظن “ايه يا عم كل ده، جايب وقت لده من فين!” الحقيقة ان صار كل ده بشكل لا ارادي و بديهي. بحبه. جزء من شخصيتي و تركيبتي.

قررت من زمان اني هتواجد في ارض جديدة، بين الكلام السلبي المخفي عن الجواز و الارتباط (“يا عم بلا هم… كلهم صنف واحد… كبر دماغك… عاملها كانها ملاك… سيطر… تتجوز ليه و تتوكس ليه… خليك سينجل و عيش حياتك… الجواز غم و مسؤلية”)، و الكلام الخيالي المطلق المستخرج من الميديا و الافلام (“الجواز الحل لكل مشكلاتي… عمري ما هحس بالوحدة تاني… الرومانسية اهم حاجه في العلاقة… البنت هتسد كل احتياجاتي.. الفرح اهم يوم في حياتنا… الحب يحل كالمشاكل”)

انا متبقي من فكر الخيالي و ارفض المنظور السلبي. و هفضل ارفض و احارب الفكر السلبي بدون مهاجمة او انتقد بشكل مباشر. هحارب الفكر من خلال فكر و ممارسة – أمل انه فكر سليم – حسب الكتاب المقدس و فكر الله في الارتباط و الجواز.

انا مخلوط و مزيج من الشرق و الغرب، النور و الظلمة، الماضي و الحاضر، الشخصي المكتسب و المروث، النظري و المعاش. و العلاقة بتكشف لي مدة الخلط و المزيج، و نسبة العمل المطلوبة في تفكيك الافكار و التصرفات اللي ممكن تعطلني و تهددنا او تكون سبب اذى. العلاقة نادي تمرين (gymnasium) بالمعنة و الغرض عن اليونانيون القدماء، تمرين و تهذيب الذهن و العقل و الجسد و القلب. عمل دائم.

ادركت و بادرك افكاري و معتقداتي عن الرجولة و الانوثة. من حين الى اخر افهم ايه اللي دايقني، ليه اتعصبت او حسيت بالاهانة، الاهمال. كنت فاكر نفسي سلس و متفهم، بس في حاجات بقفش، حدث بسيط يفجر جويا مشاعر الرفض و الخزي. كنت عارف اني عندي محفظات كتيرة و لكن اكتشفت اني عندي اكتر من كده، اجزاء من العالم الداخلي غير مستكشفة و غير مختبرة.
مدة نجاح العلاقة و التواصل متناسب مع مدة و استعداد تعبيري عن مشاعري و افكاري، مش متناسب مع مدة ادائي في دور الراجل الكريم او اللطيف او المؤدب او الملفت. كل ده مهم و مرغوب فيه بس تواجدي في العلاقة اهم. اتأخد مسؤلية مشاعري و تصرفاتي. مشاكلي. تسديد احتياجاتي. رجولتي في ده. مش علو صوتي و لا عمق غضبي. اتعلمت اكتر لما كنت هادي و متحكم في نفسي عن لما فقدت اعصابي و اتهورت.

٦ اشهر من النمو و التدريب و التمرين و العمل، اعطاء و الخدمة و التواجد. الاعمال البسيطة. التعبير و الاستماع. حل المشاكل مع بعد، مش جوا زنزانة دماغي، في الارض الجديدة، بثقة انها ترغب في الحل و نمو و نجاح العلاقة.
دخلت المشروع ده و انا مٌحمّل من الماضي، بمخاوف و صدمات و افكار مسبقة و هلاوس و معتقدات من الشلاحات. اي حد يقللك انك لازم تبقى جاهز و مثالي وغد و احمق. جرب و شوف. اعظم و اصعب و اكبر تحدي في حياتك. كان كده بالنسبة لي. المخاوف قلٍت و الصدمات بفهمها و بتعامل معها، و الافكار المسبقة بفكهم و بعيد تركيب المفيد و ترك الخطاء. و الشلاحات الحمدلله بتاعمل معها بفكاهة و سلاسة.

ماحدش علمني الكلام ده. ماكنش ينفع اتعلم ده في كورس و الكتب على قد ما اليوتيوب و كتب معينة كانوا محورين في تفكري. بس اتعلمت العوم لما نزلت البحر. و العلاقة دي بحر. كنت بحب المياه بس مرعوب من الحميمية و التواصل. النهارده بحب البحر و مش خايف منه.

اُترك المدرستين، السلبية و الخيالية، و جرّب.

انا ممتن اوي لربنا اني جربت بعد ماسمحت له انه يهدم اللي كان مترسخ جوا و بيعيد بنائه. بثق فس الرب و مش ملتزم بالنتيجة. طبعا عايز المشروح يكمل، بس سلمت النتيجة لربنا و بركز على الرحلة و العمل. الله خلقني لكي اعمل.

و انا فخور بنفسي اني بعمل في الارض الجديدة. بثق فنفسي اني بتعلم و بنمو و بتغير.

2 وَلا تَتَشَبَّهُوا بِهَذَا الْعَالَمِ، بَلْ تَغَيَّرُوا بِتَجْدِيدِ الذِّهْنِ، لِتُمَيِّزُوا مَا هِيَ إِرَادَةُ اللهِ الصَّالِحَةُ الْمَقْبُولَةُ الْكَامِلَةُ.

Captain My Captain 💔

I haven’t written anything about Fadi since his passing 3 years ago today. It was both an intentional decision and in hindsight, a wise one. I know myself and I have been through a loss before on social media. I had no control before and I just spilled everything online, almost in real-time. I didn’t want to do that. I wanted to grieve privately and allow others to grieve in their own way. I would have resented people, most probably, had my acts of public grief not been met with immediate praise and validation.

Watching others in my family grieve publicly and openly on social media was difficult at times because a part of me wanted to join in. But he was my cousin and he was their brother, husband, and uncle. They were closer. His passing hit them deeper and harder. His loss broke me, but I decided that it was their time to grieve. My relationship with Fadi was different than theirs.

Fadi was my father, mother, and sister. He was my family. He was my safe space, my north star, captain my captain. He laid his hand lovingly on my shoulder. Him dying brought that all to the conscious. That’s why it was so devastating. I had taken him for granted while he was on earth. I couldn’t admit this to myself then. But it’s become obvious over time that I just assumed, perhaps childishly, that he would always be here. He would be best man at my wedding. We would sit in his garden in Mâne and have coffee and talk music. He would give me his blessing about my wife and he would see my children grow up. I would grow older and he would be there. I would dedicate albums to him and honor him at gigs. I would invite him on my first tour to guest feature and let him steal the show. I would consult him on songwriting and get back his stone-cold, sharp critique. Him dying wasn’t part of the equation.

His death revealed to me how deeply I loved him and how much he meant to me. How I didn’t show up for him. How I didn’t visit him enough, despite him living only 2 hours away from Stockholm. How he would show up in my dreams when I would be so sad and disheartened. How I didn’t ask enough about him, check in, see what he needed, just connect with him. I did no work of the intimacy pursuant to the depth of love I felt for him.

I’ve only dreamt of him a few times since his passing, signaling to me perhaps that him not being in this realm means he’s also left my consciousness. I’m growing up and growing older without him. I still think of him and I still love him. But he’s not part of my every breath, as he used to be for a while after his death. He’s no longer part of my subconscious stirring quietly, like when he was alive.

I had his wedding anniversary saved in my calendar. It was the day before his death. I deleted it. He’s no longer here.
I used to go back and re-read his messages and our conversations. I haven’t done that in a while. He’s no longer here.
I would stop and notice when his photo would show up in other profile photos. The other day, I saw him and I was surprised he was there. Then, I remembered that he was gone. He’s no longer here.

I became sad that his death had become a given. Just like I had taken his life for granted.

As I approached this year’s memorial, I have started to stop and look wistfully at his old apartment door here in Cairo. I never thought that I would live in Cairo, let alone live here without him being alive. It didn’t matter if he had lived here or in France. He just needed to be alive. But I pass by his door and there is no trace of him. The door has been re-modelled and re-designed. On my first day here back in March, I happened to see the inside of the hallway, as I was passing. The new owners had left the door open and I peered in. The walls are different and the flooring is different. The image from my childhood and teenage years is gone. He is gone and so is his apartment. It’s now an unmarked grave. There is no physical evidence of it being his apartment other than the memories in me and all whom knew him.

In poorer parts of Egypt, it is common for people to live close to, or on top, or next to communal tombs. It could be their immediate or even distant relatives. I never quite understood this, thinking as a Westerner. It seemed macabre and odd. Death should be separate and removed out of sight. We shouldn’t have it staring it in our faces every day. It should be in a graveyard, far away from everyday life, perhaps on church grounds or even outside the city.

But this is Egypt. Our pharaonic heritage reminds us that life and death co-exist silently. We live together with death. And now, despite being fortunate to live in a middle-income area of Cairo, I live on top of my cousin’s grave. His grave is in my apartment building and I pass it multiple times a day. Some days, I notice it and I remember him. On other days, I pass by the 3rd floor and I don’t even think about it. I barely remember it’s there. Living on top of a grave has become a normal thing to me. It’s comforting. Despite his grave bearing no marks of him, I knew he was there and I knew he lived there.

Earlier this evening, I took the stairs instead of taking the elevator and I stopped outside the apartment, the grave. I imagined the old grey door, recalling where the broken piece of stained glass was. I used to peer through this when I was younger, to see what was going on inside, whether he was inside jamming or hanging out with friends or if it was dark. It was a portal into the world of love and unconditional acceptance. I came back to the present and saw a polished, brown door. Not his. The portal is gone and he is gone. And his passing no longer pains me. It’s become a part of my bones.

I miss Fadi, the musician, the effortless listener, the philosopher, the comedian, the egyptologist, the polyglot, the older brother, the everything. My everything. His hand no longer rests lovingly on my shoulder. Captain my captain.

My last message to Captain a few days before he passed

مصر بالنسبة لي

فادي و انا و اختي

مصر كانت بالنسبة لي حاجتين، شقة رمسيس و فادي. جدي و جدتي كانوا بيجسموا مصر لي، دوشة الشارع، الاكل اللي مابيتغيرش ، الحكاوي، اللمة يوم الجمعة، الطقوس اللي كانت بتديني الاحساس بالآمان.

و فادي كان الباقي. كان الضحك و المشاركة و الافصاح عن اللي جويا، سنين قبل ما افهم يعني ايه مشاركة. فادي كان الفن و الموسيقى و الفلسفة، المكان اللي كنت بعبر عن الافكار المجنونة الطفولية. كان يسمعني و يبتسم و بس. و لا مرة حكم او اتريق او قلل من حاجه شاركت بيها. فادي كان فاهمني اوي و عارف اني كنت بدور على مكان احلام و افكر بصوت عالي. و هو كان المكان ده. قلبه كان بيسعني.

ثلاثة سنين عدت على وفاته و السنة دي الذكرة ليها طعم تاني. انا هنا في مصر و هو مش هنا. قاعد في شقتنا و هو مش هنا. بعدي على الشقة و مش شايف اي ملامح من الماضي. الباب الرمادي. اسمه على الباب. لو عديت و الباب مفتوح، مفيش ريحت سجاير و لا jazz و لا صوت الجيتار و هو بيعزف. بقى اصوات تانيه و شكل تاني و ارضية تانيه.

و انا بكتب الكلام ده بلغة جديدة علي، من لغاته. و هو مش هنا يشوف التطور ده. مش هيشوف حاجات كتيرة. مش هنقعد نعزف و نهزر و نحلم و نفكر و ننكت تاني. مش هنزل بعربيته و نلف في شوارع مصر الجديدة. مصر اتغيرت في عنياي. الثوابت اتغيرت و انا عديت الاربعين و برغم اني ماعنديش نفس الفكر النوستلجي عن مصر، و لكن فادي كان في الهنا و الدلوقتي. فادي ماكنش فكرة و لا احساس. فادي كان شخص و انسان.

جزء كبير من التعامل مع الحزن كان تقبل التبصر اني ماستغلتيش الوقت معه كفيا. اعتمدت انه دايما هيبق موجود و هعرف اشوفه في تي وقت. كان ساعتين من السويد و شفته مرة في ال ١٠ سنين اللي كان فيهم في فرنسا. كنت بحبه بس كان حب سطحي، مابذلتش مجهود. كنت ملهي في حاجات تانيه. و هو سافر و انا لسه هنا. كنت بجلد نفسي في الاول. بس اقول ايه؟ كان جويا طفل متأكد انه فادي هيبقى موجود دايما. اروح اجي بس هو موجود.

ربنا يرحمه و ربنا يعلمني اني امسك في الناس المهمة و اتهنى بيهم قبل مايمشوا.

Verified by ExactMetrics