Generowanie kodu z OpenAPI

0

Cześć,
czy ktoś z was używa generatora serwera oraz modeli z OpenAPI?
Próbowałem 2 i nie do końca jestem z nich zadowolony:
https://github.com/OpenAPITools/openapi-generator
https://github.com/deepmap/oapi-codegen

Wygenerowane modele są ok, ale mam problem z wygenerowaniem całego boiler plate pod kontrolery.
Zależy mi na tym, by móc pracować z OpenAPI jak z Graphql, czyli móc wygenerować kod, który wyciąga z requestów body, headery, żebym nie musiał tego ręcznie robić.

Jakie są wasze doświadczenia z generowaniem kodu na podstawie specyfikacji OpenAPI?

3

ja to próbowałem robić i skończyło się tym, że piszę to ręcznie. W Go nie ma dobrego generatora (a przynajmniej takiego, którego znam), a żeby dawał rozsądny kod wynikowy

1

Możesz powiedzieć na czym polegają problemy z "wygenerowaniem całego boiler plate pod kontrolery "? Używałem https://github.com/deepmap/oapi-codegen z echo i wszystko śmigało

0

Ja pracowałem długo w projekcie komercyjnym z użyciem: https://github.com/go-swagger/go-swagger
Było ok. Oczywiście nie wszystko idealnie, ale i tak wydaje mi się, że sporo czasu to zaoszczędzało i do tego jeszcze mieliśmy dokumentacje od razu.
Dużym minusem wspomnianej paczki jest brak obsługi openapi v3, więc w wielu przypadkach dyskwalifikuje jej użycie.

0

Pytanie do społeczności - nie znam tej technologii, więc może czegoś nie łapię, ale może ktoś mi wytłumaczyć czemu się nie da w tym przypadku zbudować po prostu odpowiedniej abstrakcji na to? Bo jeśli coś jest tak nieistotne, że aż musimy to generować, to to wydawać by się mogło jest idealnym kandydatem na to żeby to gdzieś schować, i potem po prostu użyć w kodzie?

Chyba że palnąłem teraz totalną bzdurę, bo się nie znam.

0

@Riddle: Nie do końca rozumiem pytanie, ale jeśli dobrze zgaduję, to pytasz o co tu chodzi,

idea jest taka, że tworzę serwis, który ma jakieś endpointy. Każdy endpoint ma zdefiniowany payload, który trzeba mu wysłać i też to co ten endpoint zwraca.
No to siadam do kodu i muszę stworzyć struktury, które reprezentują ten payload i zwrotkę. Serwis zazwyczaj mogą mieć sporo takich endpointów, więc miałbym sporo pisania kodu, który w sumie mógłbym wygenerować. Do tego dochodzi jeszcze trochę więcej rzeczy, które można wygenerować np. niektóre pola muszę spełniać jakieś warunki tzn. może coś np. musi być emailem, może coś musi być liczbą a nie stringiem etc. Bez generowania niczego bym musiał siedzieć i pisać ten kod, ale po co jeśli jest jakieś narzędzie, które może go wygnerować. No i do tego wszystkiego, dobrze, żeby serwis miał dokumentację, więc ja lubię podchodzić do tego w ten sposób, że pisze najpierw dokumentację w openapi, bo i tak jej potrzebuję. Jak przejdę całą tą przygodę w ten sposób, to kończę z działającym kodem i odpowiednią dokumentacją co wg mnie jest super. Oczywiście im bardziej niestandardowe endpointy, to tym trudniej do tego w ten sposób podejść.

Do tego wszystkiego można dodać, że takie podejście można z powodzeniem stosować w innych językach technologiach, nie jest to tylko specyficzne dla Go.

0
Ąowski napisał(a):

@Riddle: Nie do końca rozumiem pytanie, ale jeśli dobrze zgaduję, to pytasz o co tu chodzi,

idea jest taka, że tworzę serwis, który ma jakieś endpointy. Każdy endpoint ma zdefiniowany payload, który trzeba mu wysłać i też to co ten endpoint zwraca.
No to siadam do kodu i muszę stworzyć struktury, które reprezentują ten payload i zwrotkę. Serwis zazwyczaj mogą mieć sporo takich endpointów, więc miałbym sporo pisania kodu, który w sumie mógłbym wygenerować. Do tego dochodzi jeszcze trochę więcej rzeczy, które można wygenerować np. niektóre pola muszę spełniać jakieś warunki tzn. może coś np. musi być emailem, może coś musi być liczbą a nie stringiem etc. Bez generowania niczego bym musiał siedzieć i pisać ten kod, ale po co jeśli jest jakieś narzędzie, które może go wygnerować. No i do tego wszystkiego, dobrze, żeby serwis miał dokumentację, więc ja lubię podchodzić do tego w ten sposób, że pisze najpierw dokumentację w openapi, bo i tak jej potrzebuję. Jak przejdę całą tą przygodę w ten sposób, to kończę z działającym kodem i odpowiednią dokumentacją co wg mnie jest super. Oczywiście im bardziej niestandardowe endpointy, to tym trudniej do tego w ten sposób podejść.

:|

No to to jeszcze bardziej brzmi jak na to się powinno nałożyć abstrakcję, zamiast "generować" cokolwiek.

No to siadam do kodu i muszę stworzyć struktury, które reprezentują ten payload i zwrotkę - no widocznie nie musisz, skoro one są na tyle powtarzalne, przewidywalne i samopodobne że jakiś genrator jest w stanie je wytworzyć.

Serwis zazwyczaj mogą mieć sporo takich endpointów, więc miałbym sporo pisania kodu, który w sumie mógłbym wygenerować. a to z kolei żywcem brzmi jak nieumiejętność reużywania kodu. Zamiast tego ja bym powiedział "Serwis wymaga obsługiwać wiele różnych pathów, ale skoro da się je przewidzieć, to można je schować w abtrakcji, która oszczędzi mi sporo pisania kodu".

Powinna powstać warstwa abstrakcji w której musisz podać tylko te rzeczy które wsadzasz do tego generatora (np to czy pole jest emailem czy nie), i to po prostu działa - bez żadnych struktur. Taka warstwa abstrakcji powinna się zapiąć pod kontrolery, które powinny własnie ją obsługiwać zamiast te wasze struktury. Wtedy nie musiałbyś nic generować, a kod byłby krótszy i prostszy.

0

skoro one są na tyle powtarzalne, przewidywalne i samopodobne że jakiś genrator jest w stanie je wytworzyć

Ale one mogą być zupełnie różne od siebie. To, że generator może je wytworzyć/wygenerować, to nie znaczy, że muszą być podobne do siebie. Nie wiem czy do końca rozumiem w którą stronę zmierzasz.

0
Ąowski napisał(a):

skoro one są na tyle powtarzalne, przewidywalne i samopodobne że jakiś genrator jest w stanie je wytworzyć

Ale one mogą być zupełnie różne od siebie. To, że generator może je wytworzyć/wygenerować, to nie znaczy, że muszą być podobne do siebie. Nie wiem czy do końca rozumiem w którą stronę zmierzasz.

Masz jakiś input, wsadzasz go w generator, generator wypluwa Ci jakiś kod i ten kod wsadzasz do projektu, i używasz w swoim kodzie.

Pytanie po co? Czemu Twój kod nie może używać tego inputa który wsadzasz do generatora? I pominąć ten generator całkowicie. To wygląda jak miejsce w którym można dodać ładną abstrakcję, i ułatwić sobie życie.

0

Inputem w tym przypadku jest dokumnetacja/specyfikacja openAPI, czyli w zasadzie plik YAML/JSON.
Nie zrobię tak, bo Go nie rozumie/nie parsuje/nie kompiluje inputu samego w sobie.

0
Ąowski napisał(a):

Inputem w tym przypadku jest dokumnetacja/specyfikacja openAPI, czyli w zasadzie plik YAML/JSON.
Nie zrobię tak, bo Go nie rozumie/nie parsuje/nie kompiluje inputu samego w sobie.

To jeszcze bardziej potwierdza, że tak na prawdę tutaj powinna być abstrakcja - a zamiast tego się dzieje jakieś nie wiadomo co, i wrzucanie niepotrzebnie wygenerowanego kodu do projektu.

Czemu nie zrobić dictionary key->value, albo drzewo faktycznych inputów (nazwy pół, to czy jest mailem czy nie, dodatkowe informacje), i niech z tej kolekcji się generuje Twoje openAPI i niech z tego korzystają kontrolery i endpointy.

Wtedy dodanie nowego feature'a sprowadzałoby się do zadeklarowania takiego słownika, i reszta by się zrobiła "sama".

0

Czyli sugerujesz, że można byłoby napisać kawałek kodu, który po prostu ten input by rozumiał? Albo trzymać go w postaci słownika/dictionary?
A no właśnie taki YAML/JSON zgodny ze specyfikacją openAPI już jest tym słownikiem, więc słownik już mamy.

A ten generator właśnie korzysta z tego słownika.

Rozumiem, że ten wygenerowany kod Ci się nie podoba. No dałoby się napisać taki kawałek kodu, który na podstawie tego słownika byłby wstanie wszystko ogarnąć bez generowania, ale wydaje mi się, że w Go, żeby to zadziałało, to skończyłoby się z masą generyków/interface{} itd. (przez to, że w Go jest silne typowanie). Dodam jeszcze, że interface{} jest czymś w rodzaju mixed z PHP, a generyki zostały nie tak dawno do Go wprowadzone, są względnie nowe i nie radzą sobie jeszcze w każdej sytuacji.

Bardzo wątpię, żeby ten kod naprawdę był prostszy i krótszy, ale oczywiście mogę się mylić.

0

Używałem takich generatorów w aplikacjach opartych o Angular i ASP.NET Core i zawsze wygenerowany kod nie był idealny więc nie wiem czy znajdziesz taki który cię zadowoli. One tak jak napisałeś zwykle generują jakiś domyślny boiler plate, który i tak potem trzeba dostosować pod wymagania w projekcie - bezpośrednie referencje, zahardkodowane url-e, itd.

0
Ąowski napisał(a):

Czyli sugerujesz, że można byłoby napisać kawałek kodu, który po prostu ten input by rozumiał? Albo trzymać go w postaci słownika/dictionary?
A no właśnie taki YAML/JSON zgodny ze specyfikacją openAPI już jest tym słownikiem, więc słownik już mamy.

Ale to musi być słownik wyrażony w języku programowania (więc np HashMap w javie, tablica asocjacyjna w PHP). Nie wiem jaki jest w GO.

Rozumiem, że ten wygenerowany kod Ci się nie podoba. No dałoby się napisać taki kawałek kodu, który na podstawie tego słownika byłby wstanie wszystko ogarnąć bez generowania, ale wydaje mi się, że w Go, żeby to zadziałało, to skończyłoby się z masą generyków/interface{} itd. (przez to, że w Go jest silne typowanie).

Jeśli napiszesz to w głupi sposób, to pewnie tak. Ale w zupełności da się to napisać prosto.

Bardzo wątpię, żeby ten kod naprawdę był prostszy i krótszy, ale oczywiście mogę się mylić.

To pokaż przykład Twojego OpenAPI oraz wszystkie pliki które ten generator Ci generuje, to Ci pokażę.

0

To pokaż przykład Twojego OpenAPI oraz wszystkie pliki które ten generator Ci generuje, to Ci pokażę.

Niestety nie mogę się tym podzielić, ponieważ należy to do firmy w której pracuje, ale nie powinieneś mieć problemu ze znalezieniem przykładu gdzieś tutaj: https://github.com/go-swagger/go-swagger

Ale to musi być słownik wyrażony w języku programowania (więc np HashMap w javie, tablica asocjacyjna w PHP). Nie wiem jaki jest w GO.

W go jest mapa i zajęło by to bardzo mało linijek kodu, żeby wczytać takiego JSONa/YAMLa do tej mapy w Go, więc można powiedzieć, że nie musimy się tym zbytnio przejmować, że to nie jest wprost w kodzie zdefiniowane.

Jeśli napiszesz to w głupi sposób, to pewnie tak. Ale w zupełności da się to napisać prosto.

Myślę, że to dość odważne stwierdzenie, ale chętnie bym to zobaczył. Myślę, że jak napisałbyś to w zupełności prosto i fajnie, to bardzo szybko zostałbyś milionerem także nie wahaj się ani chwili tylko bierz się do roboty.

Przychodzi mi tutaj na myśl generowanie kodu dla gRPC. Historia w pewien sposób podobna. Endpointy/metody definiuje się w plikach .proto . Do tego w parze idą odpowiednie narzędzia, które generują kod z tych definicji. Już jest to dość popularne i popularność jeszcze się zwiększa. Opracowane i wspierane przez Google. W każdej pracy w której pracowałem w GoLang przez ostatnie 4 lata się z tym spotykałem. Więcej tutaj: https://grpc.io/

0

Swagger/OpenApi opiera się na szablonach. Jeżeli nie ma szablonu który Ci odpowiada to nie jest żadnym problemem stworzyć w tydzień zestaw szablonów skrojony pod waszą aplikację. Będzie to dodatkowy koszt przy utrzymaniu programu ale przy wielu endpointach może okazać się dobrą inwestycją.

Generalnie w OpenApi wybiera się język i framework docelowy, więc wszystko zależy od tego jakiego frameworka web'owego w Go użyjesz. Na pewno jest coś co pozwoli zdeserializować JSON do konkretnego obiektu w Go.

1 użytkowników online, w tym zalogowanych: 0, gości: 1