Permalink
Browse files

-n

Overview documentation for api-tools
  • Loading branch information...
1 parent e91fa8c commit 1bfbbed57fe7f74b9254565e1c443e1ecd6e700b @adamgundry adamgundry committed Feb 12, 2014
Showing with 259 additions and 0 deletions.
  1. +11 −0 README.md
  2. +1 −0 api-tools.cabal
  3. +247 −0 src/Data/API/Tutorial.hs
View
@@ -0,0 +1,11 @@
+api-tools
+=========
+
+The `api-tools` library provides a compact DSL for describing an API.
+It uses Template Haskell to generate the corresponding data types and
+assorted tools for working with it, including code for converting
+between JSON and the generated types and writing unit tests. It
+supports maintaining a log of changes to the API and migrating data
+between different versions.
+
+See `Data.API.Tutorial` for a brief overview of the library.
View
@@ -43,6 +43,7 @@ Library
Data.API.Tools.Lens
Data.API.Tools.QuickCheck
Data.API.Tools.SafeCopy
+ Data.API.Tutorial
Data.API.Types
Data.API.Utils
@@ -0,0 +1,247 @@
+{-# OPTIONS_GHC -fno-warn-unused-imports #-}
+module Data.API.Tutorial (
+ -- * Introduction
+ -- $introduction
+
+ -- * The api DSL
+ -- $api
+
+ -- ** Generating types for an API
+ -- $generate
+
+ -- * Generating tools for an API
+ -- $tools
+
+ -- * Data migration
+ -- $migration
+
+ -- ** Custom migrations
+ -- $custom
+
+ -- * Documenting a REST-like API
+ -- $docs
+ ) where
+
+import Data.API.Changes
+import Data.API.Doc.Call
+import Data.API.Doc.Dir
+import Data.API.Doc.Types
+import Data.API.JSON
+import Data.API.Parse
+import Data.API.Tools
+import Data.API.Types
+
+import Data.Text
+import Data.Time
+
+{- $introduction
+
+The @api-tools@ library provides a compact DSL for describing an API.
+It uses Template Haskell to generate the corresponding data types and
+assorted tools for working with it, including code for converting
+between JSON and the generated types and writing unit tests. It
+supports maintaining a log of changes to the API and migrating data
+between different versions.
+
+-}
+
+{- $api
+
+An 'API' is a list of datatypes, which must be in one of five
+simple forms:
+
+ * Records, which have one constructor but many fields
+
+ * Unions, which have many constructors each with a single argument
+
+ * Enumerations, which have many nullary constructors
+
+ * Newtypes, which wrap a built-in basic type
+
+ * Type synonyms
+
+To define an 'API', you can use the 'parseAPI' function or the 'api'
+quasi-quoter, like this:
+
+> example :: API
+> example = [api|
+>
+> rec :: MyRecord
+> // A record type containing two fields
+> = record
+> x :: [integer] // one field
+> y :: ? [utc] // another field
+>
+> chc :: MyChoice
+> // A disjoint union
+> = union
+> | a :: MyRecord
+> | b :: string
+>
+> enm :: MyEnum
+> // An enumeration
+> = enum
+> | e1
+> | e2
+>
+> str :: MyString
+> // A newtype
+> = basic string
+>
+> flg :: MyFlag
+> // A type synonym
+> = boolean
+>
+> |]
+
+The basic types available (and their Haskell representations) are
+@string@ ('Text'), @binary@ ('Binary'), @integer@ ('Int'), @boolean@
+('Bool') and @utc@ ('UTCTime').
+
+The prefix (given before the @::@ on each type declaration) is used to
+name record fields and enumeration/union constructors in the generated
+Haskell code. It must be unique throughout the API. It is not a type
+signature, despite the appearance!
+
+-}
+
+{- $generate
+
+Once an API is defined, the 'generate' function can be used in a
+Template Haskell splice to produce the corresponding Haskell datatype
+declarations. Thus @$(generate example)@ will produce something like:
+
+> data MyRecord = MyRecord { rec_x :: [Int]
+> , rec_y :: Maybe [UTCTime]
+> }
+>
+> data MyChoice = CHC_a MyRecord | CHC_b String
+>
+> data MyEnum = ENM_e1 | ENM_e2
+>
+> newtype MyString = MyString { _MyString :: String }
+>
+> type MyFlag = Bool
+
+The Template Haskell staging restriction means that @example@ must be
+defined in one module and imported into another to call @generate@.
+
+-}
+
+{- $tools
+
+Once the Haskell datatypes have been created by 'generate', additional
+tools can be created with 'generateAPITools'. See "Data.API.Tools"
+for a list of tools supplied with the library. For example, the call
+
+> $(generateAPITools [enumTool, jsonTool, quickCheckTool] example)
+
+will define:
+
+* @_text_MyEnum :: MyEnum -> 'Text'@ and @_map_MyEnum :: Map 'Text' MyEnum@,
+ for converting between enumerations and text representations
+
+* 'ToJSON', 'FromJSONWithErrs' and 'Arbitrary' instances for all the
+ generated types
+
+-}
+
+{- $migration
+
+A key feature of @api-tools@ is support for migrating data between
+different versions of an 'API'. The 'apiWithChangelog' quasi-quoter
+allows an API to be followed by a changelog in a formal syntax,
+providing a record of changes between versions. For example:
+
+> example :: API
+> exampleChangelog :: APIChangelog
+> (example, exampleChangelog) = [apiWithChangelog|
+>
+> // ...api specification as before...
+>
+> changes
+>
+> version "0.3"
+> added MyFlag boolean
+>
+> version "0.2"
+> changed record MyRecord
+> field added y :: ? [utc]
+>
+> // Initial version
+> version "0.1"
+> |]
+
+The 'migrateDataDump' function can be used to migrate data, encoded
+with JSON, from a previous API version to a more recent version. The
+old and new 'API's must be supplied, and the changes in the changelog
+must describe how to get from the old to the new 'API'. The
+'validateChanges' function can be used to check that a changelog is
+sufficient.
+
+A changelog consists of the keyword @changes@ and a list of version
+blocks. A version block consists of the keyword @version@ starting in
+the leftmost column, a version number in double quotes, then a list of
+changes. The following changes are available:
+
+> added <Type name> <Specification>
+> removed <Type name>
+> renamed <Source type> to <Target type>
+> changed record <Type name>
+> field added <field name> :: <Type> [default <value>]
+> field removed <field name>
+> field renamed <source field> to <target field>
+> field changed <field name> :: <New type> migration <Migration name>
+> changed union <Type name>
+> alternative added <alternative name> :: <Type>
+> alternative removed <alternative name>
+> alternative renamed <source alternative> to <target alternative>
+> changed enum <Type name>
+> alternative added <value name>
+> alternative removed <value name>
+> alternative renamed <source value> to <target value>
+> migration <Migration name>
+> migration record <Type name> <Migration name>
+
+-}
+
+{- $custom
+
+For more extensive changes to the 'API' that cannot be expressed using
+the primitive changes, /custom/ migrations can be used to migrate data
+between versions.
+
+Custom migrations can be applied to the whole dataset, a single record
+or an individual record field, thus:
+
+> version "0.42"
+> migration MigrateWholeDataset
+> migration record Widget MigrateWidgetRecord
+> changed record Widget where
+> field changed foo :: String migration MigrateFooField
+
+The 'generateMigrationKinds' function creates enumeration types
+corresponding to the custom migration names used in a changelog.
+These types should then be used to create a 'CustomMigrations' record,
+which describes how to transform the data (and 'API', if appropriate)
+for each custom migration. For example,
+
+> $(generateMigrationKinds myChangelog "DatabaseMigration" "RecordMigration" "FieldMigration")
+
+with the changelog fragment above would give
+
+> data DatabaseMigration = MigrateWholeDatabase | ...
+> data RecordMigration = MigrateWidgetRecord | ...
+> data FieldMigration = MigrateFooField | ...
+
+-}
+
+{- $docs
+
+A 'Call' is a description of a web resource, intended to be generated
+in an application-specific way from the code of a web server. The
+'callHtml' function can be used to generate HTML documentation of
+individual resources, and 'dirHtml' can be used to generate an index
+page for the documentation of a collection of resources.
+
+-}

0 comments on commit 1bfbbed

Please sign in to comment.