alisinabh

alisinabh

Hi everyone.

In last month i was dealing with REST Api services mostly from client perspective and i realized how much people don’t care about documentation of their APIs. That’s Crazy! There was even one company who hadn’t any docs at all! They just sent me their real-time generated docs (seriously?) as we were talking on messenger and almost all of it were WRONG!

But even when there was a cool and fair documentation, it was still a time consuming task developing every single action in those documents.

So there is something called ApiBlueprint.
It is simply:

A powerful high-level API description language for web APIs.

It is so nice. If you are familiar with apiary you hopefully know what an ApiBlueprint is.

Since ApiBlueprint is fairly rich, I came up with the idea of writing a code generator based on ApiBlueprint that takes .apib files and convert them to Elixir code for usage.

I’ve named it Prex

Simply Prex can take .apib file and generate Elixir modules like this:

# Created by Prex
defmodule Example.Posts do
  @moduledoc """
  This section groups App.net post resources.
  """

  @base_url "https://alpha-api.app.net"

  ###
  # API Calls
  ###

  # Post

  @doc """
  Returns a specific Post.

  ## Parameters
    - postid: The id of the Post.
  """
  def retrieve_a_post(postid \\ "") do
    req_url = Path.join @base_url, "/stream/0/posts/{post_id}"
    HTTPoison.request(:get, req_url, body: Poison.encode!(%{"post_id" => postid}), headers: ["Content-Type": "application/json"])
  end

  def retrieve_a_post!(postid \\ "") do
    {:ok, result} = retrieve_a_post(postid)
    result
  end

  @doc """
  Delete a Post. The current user must be the same user who created the Post. It
returns the deleted Post on success.

  ## Parameters
    - postid: The id of the Post.
  """
  def delete_a_post(postid \\ "") do
    req_url = Path.join @base_url, "/stream/0/posts/{post_id}"
    HTTPoison.request(:delete, req_url, body: Poison.encode!(%{"post_id" => postid}), headers: ["Content-Type": "application/json"])
  end

  def delete_a_post!(postid \\ "") do
    {:ok, result} = delete_a_post(postid)
    result
  end

  # Posts Collection

  @doc """
  Create a new Post object. Mentions and hashtags will be parsed out of the post
text, as will bare URLs...
  """
  def create_a_post do
    req_url = Path.join @base_url, "/stream/0/posts"
    HTTPoison.request(:post, req_url)
  end

  def create_a_post! do
    {:ok, result} = create_a_post()
    result
  end

  @doc """
  Retrieves all posts.
  """
  def retrieve_all_posts do
    req_url = Path.join @base_url, "/stream/0/posts"
    HTTPoison.request(:get, req_url)
  end

  def retrieve_all_posts! do
    {:ok, result} = retrieve_all_posts()
    result
  end

  # Stars

  @doc """
  Save a given Post to the current User’s stars. This is just a “save” action,
not a sharing action.

*Note: A repost cannot be starred. Please star the parent Post.*

  ## Parameters
    - postid: The id of the Post.
  """
  def star_a_post(postid \\ "") do
    req_url = Path.join @base_url, "/stream/0/posts/{post_id}/star"
    HTTPoison.request(:post, req_url, body: Poison.encode!(%{"post_id" => postid}), headers: ["Content-Type": "application/json"])
  end

  def star_a_post!(postid \\ "") do
    {:ok, result} = star_a_post(postid)
    result
  end

  @doc """
  Remove a Star from a Post.

  ## Parameters
    - postid: The id of the Post.
  """
  def unstar_a_post(postid \\ "") do
    req_url = Path.join @base_url, "/stream/0/posts/{post_id}/star"
    HTTPoison.request(:delete, req_url, body: Poison.encode!(%{"post_id" => postid}), headers: ["Content-Type": "application/json"])
  end

  def unstar_a_post!(postid \\ "") do
    {:ok, result} = unstar_a_post(postid)
    result
  end

end

I’ve been working on this for only about 48 hours now. It’s super buggy. :cold_sweat:
It is going to support SOAP with WSDL too.

I just want to know how much do you think this is useful?

Please share your opinion with me. Even if you think this is very useless.

Thank’s for reading this. :slight_smile:

https://github.com/alisinabh/prex

Showing Posts 1 to 7

mbuhot

mbuhot

This is really neat!

My current workflow uses phoenix_swagger to generate a swagger spec, then bureaucrat to generate markdown documentation.

With good tools available, there’s no excuse to not have a well documented REST API :smiley:

One idea I haven’t tied out yet is to replace custom mix tasks that generate json/markdown/html files with a more integrated mix compiler, this post makes it look pretty strait forward.

For prex, you might be able to use macros to generate the module body from the api blueprint, using @external_resource to cause a recompile when the blueprint changes.

alisinabh

alisinabh OP

Thank you @mbuhot :slight_smile:

I need to make prex check project’s dependencies for HTTPoison (and Poison when i start processing responses)

Since generated code may and should be altered by the developer, I need to know two things about previously in generated actions.

  1. Version of Prex which generated the code
  2. Version of implemented ApiBlueprint

I should research around this topic and gather useful information about code generators (since this is my first code generator)

I hope i find a good solution for this matter.

OvermindDL1

OvermindDL1

That looks quite cool. :slight_smile:

Hold on to your sanity! ^.^;

alisinabh

alisinabh OP

Thank you @OvermindDL1

I’m working on some updates. SOAP will be supported soon. :innocent:

I will :sweat_smile:

alisinabh

alisinabh OP

Prex 0.0.3

Prex 0.0.3 is out for testing. In this version prex supports URL parameters (AKA Query strings)

If a query string is provided in ApiBlueprint, prex will convert it to a function like below:

def list_all_users(since \\ nil, limit \\ nil) do
  req_url = Path.join @base_url, "/users?limit=#{limit |> URI.encode_www_form}" <>
   (if since != nil, do: "since=#{since |> URI.encode_www_form}", else: "")

  HTTPoison.request(:get, req_url, body: Poison.encode!(%{"since" => since, "limit" => limit}), headers: ["Content-Type": "application/json"])
end

Parameters can be optional or required. if a parameter is optional (indicated with ? before its name in ApiBlueprint) then a single line if will get placed.

Parameters in Parameters section of the url will also be in JSON body of request. (Which should not happen always) I will fix that soon.

I admit it’s a bit messy for optional params. Since currently i don’t want prex to be a runtime dependency for scaffolded code, i didn’t use a function in my lib to achieve optional parameters.

Thanks,
Your comments means a lot to me :slight_smile:

OvermindDL1

OvermindDL1

Heh, cool idea. Any way to override in case a ? really is part of the name (unlikely and rare though it is)? :slight_smile:

alisinabh

alisinabh OP

The ? (Question mark) before a variable name is a standard for ApiBlueprint which indicates that parameter is not mandatory. So this is not possible to have it in ApiBlueprint.

This is also applied to HTTP, you cannot have a variable in HTTP Query strings (Which is same as application/x-www-form-urlencoded in post body) that contains a ?.

However you can achieve ? in variable name by using %3F instead of ? in both ApiBluprint and HTTP requests.

Thank you @OvermindDL1 :blush:

— All posts loaded —

Where Next? Top

Trending in Discussions Top

cblavier
Hey there, It’s been more than a year since we started using LiveView as our main UI library and building a whole library of UI componen...
515 12931 101
New
wmnnd
Hey folks, I wanted to share with you a project I’ve been working on for a while: It’s an Open Source (AGPLv3) newsletter tool. Think Ma...
New
raa-ziq
Hi everyone :waving_hand: I wanted to share something I’ve been building — NowBlind, a random chat app designed to help people discover ...
New
axelson
Hi there! :wave: @frigidcode and I (but mostly him) have been running an Elixir Book club, we’re almost done with Designing Elixir Syste...
New
alexslade
Fly’s CEO posted this recently - Turn And Face The Strange · The Fly Blog It says that Fly is going all-in on sprites, which is a worry ...
New
sntran
In my third year of college, around 2010, I found Erlang. A few years later, I found Elixir. I liked the ideas at once: many small proces...
New
Ganna
Hi everyone, I have 8 years of production experience with Erlang/OTP and I’m just starting to get to know Elixir. What would you learn f...
New

Other Trending Topics Top

dominicletz
Hey there, I haven’t seen a list of useful Elixir specific skills for the popular AI tools yet so I’m starting one here with my first. ...
New
mudasobwa
I fully migrated to my own harness from Anthropic/Gemini and I think it’s time to share it. Welcome DSH, the DeepSeek Harness, fully writ...
New
KristerV
Hey. Is there anyone here who creates agents in their apps? Not talking about using agents, but creating them. I’m finding it pretty diff...
New
OndrejValenta
(I just needed to vent somewhere and LinkedIn is full of hope, or hype, I’m not sure which exactly) AI dream has many faces, but general...
New
webofbits
With AI doing more of the implementation work, I’ve been wondering how much coding I should deliberately keep doing myself. My main conc...
#ai
New
kip
In 2021 I started a new library called Tempo with the objective of modelling time as a set of intervals - not as instants. In 2022 I gave...
New

Latest on Elixir Forum

We're in Beta

About us Mission Statement

Options

Thread Display Mode




Thread Preview

Skip Thread Previews