Test the migrations
This commit is contained in:
		
							
								
								
									
										74
									
								
								src/Database/Migrations.hs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										74
									
								
								src/Database/Migrations.hs
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,74 @@
 | 
			
		||||
{-# LANGUAGE ConstraintKinds #-}
 | 
			
		||||
{-# LANGUAGE DataKinds #-}
 | 
			
		||||
{-# LANGUAGE FlexibleContexts #-}
 | 
			
		||||
{-# LANGUAGE GADTs #-}
 | 
			
		||||
{-# LANGUAGE RecordWildCards #-}
 | 
			
		||||
{-# LANGUAGE TypeApplications #-}
 | 
			
		||||
{-# LANGUAGE TypeFamilies #-}
 | 
			
		||||
{-|
 | 
			
		||||
Module      : Database.Migrations
 | 
			
		||||
Description : Semiautomatic migrations for json values
 | 
			
		||||
Copyright   : (c) Mats Rauhala, 2020
 | 
			
		||||
License     : BSD-3-Clause
 | 
			
		||||
Maintainer  : mats.rauhala@iki.fi
 | 
			
		||||
Stability   : experimental
 | 
			
		||||
Portability : POSIX
 | 
			
		||||
 | 
			
		||||
The idea is taken from the safecopy module.
 | 
			
		||||
-}
 | 
			
		||||
module Database.Migrations where
 | 
			
		||||
 | 
			
		||||
import Data.Aeson
 | 
			
		||||
 | 
			
		||||
import Data.Proxy
 | 
			
		||||
import GHC.Generics
 | 
			
		||||
       (Generic)
 | 
			
		||||
import GHC.TypeLits
 | 
			
		||||
 | 
			
		||||
data Versioned
 | 
			
		||||
  = Versioned { version :: !Integer, content :: !Value }
 | 
			
		||||
  deriving stock (Show, Generic, Eq)
 | 
			
		||||
  deriving anyclass (ToJSON, FromJSON)
 | 
			
		||||
 | 
			
		||||
data Kind a where
 | 
			
		||||
  Base :: Kind a
 | 
			
		||||
  Extends :: (SafeJSON (MigrateFrom a), KnownNat (Version (MigrateFrom a)), Migrate a) => Proxy (MigrateFrom a) -> Kind a
 | 
			
		||||
 | 
			
		||||
class FromJSON a => SafeJSON a where
 | 
			
		||||
  type Version a :: Nat
 | 
			
		||||
 | 
			
		||||
  kind :: Kind a
 | 
			
		||||
  kind = Base
 | 
			
		||||
 | 
			
		||||
data Extending (a :: Nat) x
 | 
			
		||||
 | 
			
		||||
class Migrate a where
 | 
			
		||||
  type MigrateFrom a :: *
 | 
			
		||||
  migrate :: MigrateFrom a -> Maybe a
 | 
			
		||||
 | 
			
		||||
type Migratable a = (SafeJSON a, KnownNat (Version a))
 | 
			
		||||
 | 
			
		||||
-- | Convert any json value into a versioned version
 | 
			
		||||
toVersioned :: forall a. (ToJSON a, KnownNat (Version a), SafeJSON a) => a -> Versioned
 | 
			
		||||
toVersioned x =
 | 
			
		||||
  Versioned{ version = natVal @(Version a) Proxy
 | 
			
		||||
           , content = toJSON x
 | 
			
		||||
           }
 | 
			
		||||
 | 
			
		||||
-- | Convert a json value into result type 'a' doing migrations if required
 | 
			
		||||
fromVersioned :: forall a. Migratable a => Versioned -> Result a
 | 
			
		||||
fromVersioned = worker (kind @a)
 | 
			
		||||
  where
 | 
			
		||||
    kindFromProxy :: SafeJSON x => Proxy x -> Kind x
 | 
			
		||||
    kindFromProxy _ = kind
 | 
			
		||||
    worker :: forall b. Migratable b => Kind b -> Versioned -> Result b
 | 
			
		||||
    worker thisKind Versioned{..}
 | 
			
		||||
      -- The versions match
 | 
			
		||||
      | version == natVal @(Version b) Proxy = fromJSON content
 | 
			
		||||
      -- Migrations required
 | 
			
		||||
      | otherwise =
 | 
			
		||||
        case thisKind of
 | 
			
		||||
             Base -> Error "Version not found"
 | 
			
		||||
             Extends x -> do
 | 
			
		||||
               previous <- worker (kindFromProxy x) (Versioned version content)
 | 
			
		||||
               maybe (Error "Migration not found") Success $ migrate previous
 | 
			
		||||
		Reference in New Issue
	
	Block a user