First cut of migration program
This commit is contained in:
		
							parent
							
								
									42e3a58131
								
							
						
					
					
						commit
						194cd2b5cc
					
				| @ -5,6 +5,31 @@ open Npgsql | |||||||
| open Npgsql.FSharp | open Npgsql.FSharp | ||||||
| open PrayerTracker.Entities | open PrayerTracker.Entities | ||||||
| 
 | 
 | ||||||
|  | /// Table names | ||||||
|  | [<RequireQualifiedAccess>] | ||||||
|  | module Table = | ||||||
|  | 
 | ||||||
|  |     /// The church table | ||||||
|  |     [<Literal>] | ||||||
|  |     let Church = "church" | ||||||
|  | 
 | ||||||
|  |     /// The small group table | ||||||
|  |     [<Literal>] | ||||||
|  |     let Group = "small_group" | ||||||
|  | 
 | ||||||
|  |     /// The small group member table | ||||||
|  |     [<Literal>] | ||||||
|  |     let Member = "member" | ||||||
|  | 
 | ||||||
|  |     /// The prayer request table | ||||||
|  |     [<Literal>] | ||||||
|  |     let Request = "prayer_request" | ||||||
|  | 
 | ||||||
|  |     /// The user table | ||||||
|  |     [<Literal>] | ||||||
|  |     let User = "pt_user" | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
| /// Helper functions for the PostgreSQL data implementation | /// Helper functions for the PostgreSQL data implementation | ||||||
| [<AutoOpen>] | [<AutoOpen>] | ||||||
| module private Helpers = | module private Helpers = | ||||||
| @ -100,6 +125,7 @@ module private Helpers = | |||||||
|             IsAdmin      = row.bool   "is_admin" |             IsAdmin      = row.bool   "is_admin" | ||||||
|             PasswordHash = row.string "password_hash" |             PasswordHash = row.string "password_hash" | ||||||
|             LastSeen     = row.fieldValueOrNone<Instant> "last_seen" |             LastSeen     = row.fieldValueOrNone<Instant> "last_seen" | ||||||
|  |             SmallGroups  = [] | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
|  | |||||||
| @ -127,6 +127,15 @@ type RequestSort = | |||||||
|         | _ -> invalidArg "code" $"Unknown code {code}" |         | _ -> invalidArg "code" $"Unknown code {code}" | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
|  | /// Type for a time zone ID | ||||||
|  | type TimeZoneId = | ||||||
|  |     | TimeZoneId of string | ||||||
|  | 
 | ||||||
|  |     override this.ToString() = | ||||||
|  |         match this with | ||||||
|  |         | TimeZoneId it -> it | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
| open System | open System | ||||||
| 
 | 
 | ||||||
| /// PK type for the Church entity | /// PK type for the Church entity | ||||||
| @ -173,15 +182,6 @@ type SmallGroupId = | |||||||
|             | SmallGroupId guid -> guid |             | SmallGroupId guid -> guid | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| /// PK type for the TimeZone entity |  | ||||||
| type TimeZoneId = |  | ||||||
|     | TimeZoneId of string |  | ||||||
| 
 |  | ||||||
|     override this.ToString() = |  | ||||||
|         match this with |  | ||||||
|         | TimeZoneId it -> it |  | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
| /// PK type for the User entity | /// PK type for the User entity | ||||||
| type UserId = | type UserId = | ||||||
|     | UserId of Guid |     | UserId of Guid | ||||||
| @ -523,6 +523,9 @@ type User = | |||||||
| 
 | 
 | ||||||
|         /// The last time the user was seen (set whenever the user is loaded into a session) |         /// The last time the user was seen (set whenever the user is loaded into a session) | ||||||
|         LastSeen: Instant option |         LastSeen: Instant option | ||||||
|  | 
 | ||||||
|  |         /// The small groups to which this user is authorized | ||||||
|  |         SmallGroups: SmallGroupId list | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     /// The full name of the user |     /// The full name of the user | ||||||
| @ -536,7 +539,8 @@ type User = | |||||||
|           Email = "" |           Email = "" | ||||||
|           IsAdmin = false |           IsAdmin = false | ||||||
|           PasswordHash = "" |           PasswordHash = "" | ||||||
|           LastSeen = None } |           LastSeen = None | ||||||
|  |           SmallGroups = [] } | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| /// Cross-reference between user and small group | /// Cross-reference between user and small group | ||||||
|  | |||||||
| @ -8,6 +8,7 @@ | |||||||
| 
 | 
 | ||||||
|   <ItemGroup> |   <ItemGroup> | ||||||
|     <PackageReference Include="BitBadger.Documents.Postgres" Version="3.1.0" /> |     <PackageReference Include="BitBadger.Documents.Postgres" Version="3.1.0" /> | ||||||
|  |     <PackageReference Include="BitBadger.Documents.Sqlite" Version="4.0.1" /> | ||||||
|     <PackageReference Include="Giraffe" Version="7.0.2" /> |     <PackageReference Include="Giraffe" Version="7.0.2" /> | ||||||
|     <PackageReference Include="NodaTime" Version="3.2.0" /> |     <PackageReference Include="NodaTime" Version="3.2.0" /> | ||||||
|     <PackageReference Include="Npgsql.FSharp" Version="5.7.0" /> |     <PackageReference Include="Npgsql.FSharp" Version="5.7.0" /> | ||||||
|  | |||||||
							
								
								
									
										20
									
								
								src/PrayerTracker.MigrateV9/PrayerTracker.MigrateV9.fsproj
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										20
									
								
								src/PrayerTracker.MigrateV9/PrayerTracker.MigrateV9.fsproj
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,20 @@ | |||||||
|  | <Project Sdk="Microsoft.NET.Sdk"> | ||||||
|  | 
 | ||||||
|  |   <PropertyGroup> | ||||||
|  |     <OutputType>Exe</OutputType> | ||||||
|  |     <TargetFramework>net9.0</TargetFramework> | ||||||
|  |   </PropertyGroup> | ||||||
|  | 
 | ||||||
|  |   <ItemGroup> | ||||||
|  |     <Compile Include="Program.fs" /> | ||||||
|  |   </ItemGroup> | ||||||
|  | 
 | ||||||
|  |   <ItemGroup> | ||||||
|  |     <ProjectReference Include="..\PrayerTracker.Data\PrayerTracker.Data.fsproj" /> | ||||||
|  |   </ItemGroup> | ||||||
|  | 
 | ||||||
|  |   <ItemGroup> | ||||||
|  |     <PackageReference Include="BitBadger.Documents.Postgres" Version="4.0.1" /> | ||||||
|  |   </ItemGroup> | ||||||
|  | 
 | ||||||
|  | </Project> | ||||||
							
								
								
									
										141
									
								
								src/PrayerTracker.MigrateV9/Program.fs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										141
									
								
								src/PrayerTracker.MigrateV9/Program.fs
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,141 @@ | |||||||
|  |  | ||||||
|  | open NodaTime | ||||||
|  | open Npgsql.FSharp | ||||||
|  | open PrayerTracker.Data | ||||||
|  | open PrayerTracker.Entities | ||||||
|  | 
 | ||||||
|  | module PgMappings = | ||||||
|  |     /// Map a row to a Church instance | ||||||
|  |     let mapToChurch (row : RowReader) = | ||||||
|  |         {   Id               = ChurchId         (row.uuid "id") | ||||||
|  |             Name             = row.string       "church_name" | ||||||
|  |             City             = row.string       "city" | ||||||
|  |             State            = row.string       "state" | ||||||
|  |             HasVpsInterface  = row.bool         "has_vps_interface" | ||||||
|  |             InterfaceAddress = row.stringOrNone "interface_address" | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |     /// Map a row to a ListPreferences instance | ||||||
|  |     let mapToListPreferences (row : RowReader) = | ||||||
|  |         {   SmallGroupId        = SmallGroupId (row.uuid "small_group_id") | ||||||
|  |             DaysToKeepNew       = row.int      "days_to_keep_new" | ||||||
|  |             DaysToExpire        = row.int      "days_to_expire" | ||||||
|  |             LongTermUpdateWeeks = row.int      "long_term_update_weeks" | ||||||
|  |             EmailFromName       = row.string   "email_from_name" | ||||||
|  |             EmailFromAddress    = row.string   "email_from_address" | ||||||
|  |             Fonts               = row.string   "fonts" | ||||||
|  |             HeadingColor        = row.string   "heading_color" | ||||||
|  |             LineColor           = row.string   "line_color" | ||||||
|  |             HeadingFontSize     = row.int      "heading_font_size" | ||||||
|  |             TextFontSize        = row.int      "text_font_size" | ||||||
|  |             GroupPassword       = row.string   "group_password" | ||||||
|  |             IsPublic            = row.bool     "is_public" | ||||||
|  |             PageSize            = row.int      "page_size" | ||||||
|  |             TimeZoneId          = TimeZoneId    (row.string "time_zone_id") | ||||||
|  |             RequestSort         = RequestSort.Parse     (row.string "request_sort") | ||||||
|  |             DefaultEmailType    = EmailFormat.Parse     (row.string "default_email_type") | ||||||
|  |             AsOfDateDisplay     = AsOfDateDisplay.Parse (row.string "as_of_date_display") | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |     /// Map a row to a Member instance | ||||||
|  |     let mapToMember (row : RowReader) = | ||||||
|  |         {   Id           = MemberId         (row.uuid "id") | ||||||
|  |             SmallGroupId = SmallGroupId     (row.uuid "small_group_id") | ||||||
|  |             Name         = row.string       "member_name" | ||||||
|  |             Email        = row.string       "email" | ||||||
|  |             Format       = row.stringOrNone "email_format" |> Option.map EmailFormat.Parse | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |     /// Map a row to a Prayer Request instance | ||||||
|  |     let mapToPrayerRequest (row : RowReader) = | ||||||
|  |         {   Id             = PrayerRequestId         (row.uuid "id") | ||||||
|  |             UserId         = UserId                  (row.uuid "user_id") | ||||||
|  |             SmallGroupId   = SmallGroupId            (row.uuid "small_group_id") | ||||||
|  |             EnteredDate    = row.fieldValue<Instant> "entered_date" | ||||||
|  |             UpdatedDate    = row.fieldValue<Instant> "updated_date" | ||||||
|  |             Requestor      = row.stringOrNone        "requestor" | ||||||
|  |             Text           = row.string              "request_text" | ||||||
|  |             NotifyChaplain = row.bool                "notify_chaplain" | ||||||
|  |             RequestType    = PrayerRequestType.Parse (row.string "request_type") | ||||||
|  |             Expiration     = Expiration.Parse        (row.string "expiration") | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |     /// Map a row to a Small Group instance | ||||||
|  |     let mapToSmallGroup (row : RowReader) = | ||||||
|  |         {   Id          = SmallGroupId (row.uuid "id") | ||||||
|  |             ChurchId    = ChurchId     (row.uuid "church_id") | ||||||
|  |             Name        = row.string   "group_name" | ||||||
|  |             Preferences = ListPreferences.Empty | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |     /// Map a row to a Small Group instance with populated list preferences | ||||||
|  |     let mapToSmallGroupWithPreferences (row : RowReader) = | ||||||
|  |         { mapToSmallGroup row with | ||||||
|  |             Preferences = mapToListPreferences row | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |     /// Map a row to a User instance | ||||||
|  |     let mapToUser (row : RowReader) = | ||||||
|  |         {   Id           = UserId     (row.uuid "id") | ||||||
|  |             FirstName    = row.string "first_name" | ||||||
|  |             LastName     = row.string "last_name" | ||||||
|  |             Email        = row.string "email" | ||||||
|  |             IsAdmin      = row.bool   "is_admin" | ||||||
|  |             PasswordHash = row.string "password_hash" | ||||||
|  |             LastSeen     = row.fieldValueOrNone<Instant> "last_seen" | ||||||
|  |             SmallGroups  = [] | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  | // TODO: Configure PostgreSQL and SQLite connections | ||||||
|  | 
 | ||||||
|  | task { | ||||||
|  | 
 | ||||||
|  |     let source = BitBadger.Documents.Postgres.Configuration.dataSource () | ||||||
|  | 
 | ||||||
|  |     let! churches = | ||||||
|  |         Sql.fromDataSource source | ||||||
|  |         |> Sql.query "SELECT * FROM pt.church" | ||||||
|  |         |> Sql.executeAsync PgMappings.mapToChurch | ||||||
|  |     for church in churches do | ||||||
|  |         do! BitBadger.Documents.Sqlite.Document.insert Table.Church church | ||||||
|  |     printfn "Migrated %d churches" churches.Length | ||||||
|  | 
 | ||||||
|  |     let! groups = | ||||||
|  |         Sql.fromDataSource source | ||||||
|  |         |> Sql.query "SELECT sg.*, lp.* FROM pt.small_group sg | ||||||
|  |                         INNER JOIN pt.list_preference lp ON lp.small_group_id = sg.id" | ||||||
|  |         |> Sql.executeAsync PgMappings.mapToSmallGroupWithPreferences | ||||||
|  |     for group in groups do | ||||||
|  |         do! BitBadger.Documents.Sqlite.Document.insert Table.Group group | ||||||
|  |     printfn "Migrated %d groups" groups.Length | ||||||
|  | 
 | ||||||
|  |     let! members = | ||||||
|  |         Sql.fromDataSource source | ||||||
|  |         |> Sql.query "SELECT * from pt.member" | ||||||
|  |         |> Sql.executeAsync PgMappings.mapToMember | ||||||
|  |     for mbr in members do | ||||||
|  |         do! BitBadger.Documents.Sqlite.Document.insert Table.Member mbr | ||||||
|  |     printfn "Migrated %d members" members.Length | ||||||
|  | 
 | ||||||
|  |     let! requests = | ||||||
|  |         Sql.fromDataSource source | ||||||
|  |         |> Sql.query "SELECT * from pt.prayer_request" | ||||||
|  |         |> Sql.executeAsync PgMappings.mapToPrayerRequest | ||||||
|  |     for request in requests do | ||||||
|  |         do! BitBadger.Documents.Sqlite.Document.insert Table.Request request | ||||||
|  |     printfn "Migrated %d requests" requests.Length | ||||||
|  | 
 | ||||||
|  |     let! users = | ||||||
|  |         Sql.fromDataSource source | ||||||
|  |         |> Sql.query "SELECT * FROM pt.pt_user" | ||||||
|  |         |> Sql.executeAsync PgMappings.mapToUser | ||||||
|  |     for user in users do | ||||||
|  |         let! groups = | ||||||
|  |             Sql.fromDataSource source | ||||||
|  |             |> Sql.query "SELECT small_group_id FROM pt.user_small_group WHERE user_id = :user_id" | ||||||
|  |             |> Sql.parameters [ ":user_id", Sql.uuid user.Id.Value ] | ||||||
|  |             |> Sql.executeAsync (fun row -> (row.uuid >> SmallGroupId) "small_group_id") | ||||||
|  |         do! BitBadger.Documents.Sqlite.Document.insert Table.User { user with SmallGroups = groups } | ||||||
|  |     printfn "Migrated %d users" users.Length | ||||||
|  | 
 | ||||||
|  | } |> Async.AwaitTask |> Async.RunSynchronously | ||||||
| @ -16,6 +16,8 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution | |||||||
| 		Directory.Build.props = Directory.Build.props | 		Directory.Build.props = Directory.Build.props | ||||||
| 	EndProjectSection | 	EndProjectSection | ||||||
| EndProject | EndProject | ||||||
|  | Project("{F2A71F9B-5D33-465A-A702-920D77279786}") = "PrayerTracker.MigrateV9", "PrayerTracker.MigrateV9\PrayerTracker.MigrateV9.fsproj", "{CE7C5972-AC9A-44A8-8265-771483FD87DB}" | ||||||
|  | EndProject | ||||||
| Global | Global | ||||||
| 	GlobalSection(SolutionConfigurationPlatforms) = preSolution | 	GlobalSection(SolutionConfigurationPlatforms) = preSolution | ||||||
| 		Debug|Any CPU = Debug|Any CPU | 		Debug|Any CPU = Debug|Any CPU | ||||||
| @ -38,6 +40,10 @@ Global | |||||||
| 		{2B5BA107-9BDA-4A1D-A9AF-AFEE6BF12270}.Debug|Any CPU.Build.0 = Debug|Any CPU | 		{2B5BA107-9BDA-4A1D-A9AF-AFEE6BF12270}.Debug|Any CPU.Build.0 = Debug|Any CPU | ||||||
| 		{2B5BA107-9BDA-4A1D-A9AF-AFEE6BF12270}.Release|Any CPU.ActiveCfg = Release|Any CPU | 		{2B5BA107-9BDA-4A1D-A9AF-AFEE6BF12270}.Release|Any CPU.ActiveCfg = Release|Any CPU | ||||||
| 		{2B5BA107-9BDA-4A1D-A9AF-AFEE6BF12270}.Release|Any CPU.Build.0 = Release|Any CPU | 		{2B5BA107-9BDA-4A1D-A9AF-AFEE6BF12270}.Release|Any CPU.Build.0 = Release|Any CPU | ||||||
|  | 		{CE7C5972-AC9A-44A8-8265-771483FD87DB}.Debug|Any CPU.ActiveCfg = Debug|Any CPU | ||||||
|  | 		{CE7C5972-AC9A-44A8-8265-771483FD87DB}.Debug|Any CPU.Build.0 = Debug|Any CPU | ||||||
|  | 		{CE7C5972-AC9A-44A8-8265-771483FD87DB}.Release|Any CPU.ActiveCfg = Release|Any CPU | ||||||
|  | 		{CE7C5972-AC9A-44A8-8265-771483FD87DB}.Release|Any CPU.Build.0 = Release|Any CPU | ||||||
| 	EndGlobalSection | 	EndGlobalSection | ||||||
| 	GlobalSection(SolutionProperties) = preSolution | 	GlobalSection(SolutionProperties) = preSolution | ||||||
| 		HideSolutionNode = FALSE | 		HideSolutionNode = FALSE | ||||||
|  | |||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user