/swagger1st

A Clojure Ring handler that parses, validates and routes requests based on your swagger definitions.

Primary LanguageJavaScriptISC LicenseISC

swagger1st (swagger first)

Maven Central Build Status Coverage Status

swagger1st is a Clojure Ring handler that parses, validates and routes requests based on your Swagger definition. Instead of defining routes and validation rules in your code, you specify your API in the Swagger 2.0 Specification format using their great tool set. This encourages an "API first" approach by letting you specify your API in a technology independent format. The resulting definition is the ultimate format for publishing, sharing and reviewing your API. swagger1st will use it as a configuration file for processing incoming requests. This approach makes sure, that your implementation and specification never gets out of sync. During runtime, you can inspect and easily test your API with the built-in Swagger UI. You are also free to extend the interpretation of your definition according to your own needs.

Imagine a simple API definition like that:

swagger: '2.0'

info:
  title: Example API
  version: '0.1'

paths:
  /helloworld:
    get:
      summary: Returns a greeting.
      operationId: example.api/generate-greeting
      parameters:
        - name: firstname
          in: query
          type: string
          pattern: "^[A-Z][a-z]+"
      responses:
          200:
              description: say hello

By default, this definition is connected to your business logic via the operationId, which might be defined like that:

(ns example.api
  (:require [ring.util.response :as r]))

(defn generate-greeting [request]
  (let [firstname (-> request :parameters :query :name)]
    (-> (r/response (str "Hello " firstname "!"))
        (r/content-type "plain/text"))))

That is everything you need to do to define and implement your API. Only fully validated requests get to your function, so you can rely on swagger1st to properly check all input parameters according to your definition. The function itself is a normal Clojure function without any dependencies to swagger1st - simple as that.

Kickstart

Leiningen Template

For the following steps, you need Leiningen installed.

Simple Setup

If you are bootstrapping a complete new project or just want to try out swagger1st, you can use the Leiningen template:

$ lein new swagger1st myproject

Go into the new project folder myproject and start a new webserver with lein ring server-headless. Go with your browser to http://localhost:3000/ui/.

Complex Setup

You can also generate a project setup which includes Stuart Sierra's component framework in order to see how you can handle dependency injection with swagger1st:

$ lein new swagger1st myproject +component

Go into the new project folder myproject and run its main function via lein run -m myproject.core. Go with your browser to http://localhost:3000/ui/.

Manual Setup

Use the following dependency in your Leiningen project:

[io.sarnowski/swagger1st "<latest>"]

You find the latest version in Maven central:

Maven Central

The following setup creates a ring compliant handler.

(ns example
  (:require [io.sarnowski.swagger1st.core :as s1st]
            [io.sarnowski.swagger1st.util.security :as s1stsec]))

(def app
  (-> (s1st/context :yaml-cp "my-swagger-api.yaml")
      (s1st/discoverer)
      (s1st/mapper)
      (s1st/parser)
      (s1st/protector {"oauth2" (s1stsec/allow-all)})
      (s1st/executor)))

Complete Example Projects

Checkout the Leiningen templates as mentioned in the Kickstart section for working examples.

Friboo

Friboo is Zalando's opinionated Clojure microservice library which uses swagger1st at its base for RESTful HTTP endpoints and also integrates with the component framework. See the following projects who are real world applications of Zalando's cloud infrastructure based on Friboo with swagger1st:

swagger-mock

swagger-mock is an example how to also use swagger1st. Since you have the complete data of your Swagger definition at hand, you can modify it before processing and also use its definition information during execution. This allows the swagger-mock to hook into your definition and parse the examples from it in order to return them on each request.

Compatibility Overview

swagger1st aims to implement all features of the Swagger 2.0 specification. Everything that you can define with Swagger should be handled by swagger1st, so that you only have to write your business logic. Version 1.0 will implement all elements (that makes sense to handle). Until then, the following document shows the current supported aspects of the specification:

The Ring handler in detail

  • s1st/context (required)
    • Creates a new context from a given definition. This context will be used by the next steps to prepare the execution of requests.
  • s1st/discoverer (optional)
    • The discoverer enables certain HTTP endpoints, that makes it easy to work with your API. In particular, this enables the Swagger UI under the path /ui/ and exposes the Swagger definition under /swagger.json.
  • s1st/mapper (required)
    • The mapper denormalizes the given definition (e.g. resolves all $refs) and figures out, which request definition maps to the actual incoming request. After this function, your request map contains the :swagger key, which contains a :request key containing the denormalized definition of the request and a :key key which can be used to uniquely identify a request.
  • s1st/parser (required)
    • The parser parses the incoming request according to the definition and validates all inputs.
  • s1st/protector (optional)
    • The protector can enforce all security definitions for you. As the security check implementations vary depending on your environment, this is only a framework to hook into the system and define callbacks for the actual checks.
  • s1st/executor (required)
    • The executor executes your defined function in the end. At this point, the whole definition was validated and only valid requests make it up until here. You can also specify an own function resolver function in order to hook into your own framework.

Development on swagger1st

Source code can be found on GitHub. Read this documentation if you are just starting with GitHub. In addition, you need Leiningen as the build tool, make sure it works first.

The following commands are a kickstarter for development:

# get the source
$ git clone https://github.com/sarnowski/swagger1st.git
$ cd swagger1st

# run the tests
$ lein test

# run all tests, including performance benchmarks
$ lein test :all

# build an own artifact for local development
$ lein install

# release a new version
$ lein release :minor

For interactive development, you can start a REPL by typing lein repl.

License

Copyright (c) 2015, Tobias Sarnowski

Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted, provided that the above copyright notice and this permission notice appear in all copies.

THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.