Header menu logo Migrondi

The IMiFileSystem object is one of the most likely object to replace if you're implementing your own storage mechanism. It is used to read and write files.

#r "nuget: Migrondi.Core, 1.0.0-beta-010"

open System
open Migrondi.Core.FileSystem
open Migrondi.Core.Serialization
open Microsoft.Extensions.Logging

let logger =
  // create a sample logger, you can provide your own
  LoggerFactory.Create(fun builder -> builder.AddConsole() |> ignore)
  |> fun x -> x.CreateLogger("FileSystem")

let serializer = MigrondiSerializer()

let rootDir = Uri("https://my-storage.com/project-a/", UriKind.Absolute)
let migrationsDir = Uri("./migrations/", UriKind.Relative)

NOTE: Please keep in mind that URI objects treat trailing shashes differently, since we're using them to represent directories. The Uri object must contain the trailing slash from the rootDir and migrationsDir objects. If you fail to ensure the trailing slash is there, the library will not work as expected.

let fs: IMiFileSystem =
  MiFileSystem(logger, serializer, serializer, rootDir, migrationsDir)

In order to list the migrations in the file system, you can call the ListMigrations(targetDirectory) method.

fs.ListMigrations("./")

Internally this will build the URI from the rootDir and migrationsDir objects, and then list the files in the directory.

So you have to keep into consideration how will you take external paths and convert them into URIs to be used by the library. This is mainly an implementation detail, but it's important to keep in mind when you're implementing your own IMiFileSystem object. A simplified way to list the migrations internally that we currently do is similar to the following:

// usually "readFrom" is the content of config.migrationsDir
let listMigrations (readFrom: string) =
  let path = Uri(rootDir, readFrom)
  let dir = IO.DirectoryInfo(path.LocalPath)
  // list all files in the directory
  // filter out the ones that are migrations (we have a specific schema for the file name)
  // for each found file, decode the text and return  migration object otherwise return an error
  []

When you read a migration file specifically you can call the ReadMigration(readFrom) method.

This readFrom is the relative path to the migrationsDir objects including the name of the file. e.g.

fs.ReadMigration("initial-tables_1708216610033.sql")

This will build the URI from the rootDir and migrationsDir objects, and then read the file. Internally it looks like this:

let readMigration (readFrom: string) =
  let path = Uri(rootDir, Uri(migrationsDir, readFrom))
  let file = IO.File.ReadAllText(path.LocalPath)
  let migration = (serializer :> IMiMigrationSerializer).DecodeText(file)
  migration

Writing a migration is similar, and usually we don't write migrations directly, but we do it through the serializers.

fs.WriteMigration(
  {
    name = "initial-tables_1708216610033"
    timestamp = DateTimeOffset.Now.ToUnixTimeMilliseconds()
    upContent = "CREATE TABLE users (id INT, name VARCHAR(255));"
    downContent = "DROP TABLE users;"
    manualTransaction = false
  },
  "initial-tables_1708216610033.sql"
)

In this case it just means that you need to know how you will want to store the migrations in your own backing store.

namespace System
namespace Migrondi
namespace Migrondi.Core
namespace Migrondi.Core.FileSystem
namespace Migrondi.Core.Serialization
namespace Microsoft
namespace Microsoft.Extensions
namespace Microsoft.Extensions.Logging
val logger: ILogger
Multiple items
type LoggerFactory = interface ILoggerFactory interface IDisposable new: unit -> unit + 5 overloads member AddProvider: provider: ILoggerProvider -> unit member CreateLogger: categoryName: string -> ILogger member Dispose: unit -> unit static member Create: configure: Action<ILoggingBuilder> -> ILoggerFactory
<summary> Produces instances of <see cref="T:Microsoft.Extensions.Logging.ILogger" /> classes based on the given providers. </summary>

--------------------
LoggerFactory() : LoggerFactory
LoggerFactory(providers: Collections.Generic.IEnumerable<ILoggerProvider>) : LoggerFactory
LoggerFactory(providers: Collections.Generic.IEnumerable<ILoggerProvider>, filterOptions: LoggerFilterOptions) : LoggerFactory
LoggerFactory(providers: Collections.Generic.IEnumerable<ILoggerProvider>, filterOption: Extensions.Options.IOptionsMonitor<LoggerFilterOptions>) : LoggerFactory
LoggerFactory(providers: Collections.Generic.IEnumerable<ILoggerProvider>, filterOption: Extensions.Options.IOptionsMonitor<LoggerFilterOptions>, options: Extensions.Options.IOptions<LoggerFactoryOptions>) : LoggerFactory
LoggerFactory(providers: Collections.Generic.IEnumerable<ILoggerProvider>, filterOption: Extensions.Options.IOptionsMonitor<LoggerFilterOptions>, ?options: Extensions.Options.IOptions<LoggerFactoryOptions>, ?scopeProvider: IExternalScopeProvider) : LoggerFactory
LoggerFactory.Create(configure: Action<ILoggingBuilder>) : ILoggerFactory
val builder: ILoggingBuilder
(extension) ILoggingBuilder.AddConsole() : ILoggingBuilder
(extension) ILoggingBuilder.AddConsole(configure: Action<Console.ConsoleLoggerOptions>) : ILoggingBuilder
val ignore: value: 'T -> unit
val x: ILoggerFactory
(extension) ILoggerFactory.CreateLogger<'T>() : ILogger<'T>
(extension) ILoggerFactory.CreateLogger(``type`` : Type) : ILogger
ILoggerFactory.CreateLogger(categoryName: string) : ILogger
val serializer: MigrondiSerializer
Multiple items
type MigrondiSerializer = interface IMiConfigurationSerializer interface IMiMigrationSerializer new: unit -> MigrondiSerializer

--------------------
new: unit -> MigrondiSerializer
val rootDir: Uri
Multiple items
type Uri = interface ISpanFormattable interface IFormattable interface ISerializable new: uriString: string -> unit + 6 overloads member Equals: comparand: obj -> bool member GetComponents: components: UriComponents * format: UriFormat -> string member GetHashCode: unit -> int member GetLeftPart: part: UriPartial -> string member IsBaseOf: uri: Uri -> bool member IsWellFormedOriginalString: unit -> bool ...
<summary>Provides an object representation of a uniform resource identifier (URI) and easy access to the parts of the URI.</summary>

--------------------
Uri(uriString: string) : Uri
Uri(uriString: string, creationOptions: inref<UriCreationOptions>) : Uri
Uri(uriString: string, uriKind: UriKind) : Uri
Uri(baseUri: Uri, relativeUri: string) : Uri
Uri(baseUri: Uri, relativeUri: Uri) : Uri
[<Struct>] type UriKind = | RelativeOrAbsolute = 0 | Absolute = 1 | Relative = 2
<summary>Defines the different kinds of URIs.</summary>
field UriKind.Absolute: UriKind = 1
val migrationsDir: Uri
field UriKind.Relative: UriKind = 2
val fs: IMiFileSystem
type IMiFileSystem = abstract ListMigrations: migrationsLocation: string -> IReadOnlyList<Migration> abstract ListMigrationsAsync: migrationsLocation: string * [<Optional>] ?cancellationToken: CancellationToken -> Task<IReadOnlyList<Migration>> abstract ReadConfiguration: readFrom: string -> MigrondiConfig abstract ReadConfigurationAsync: readFrom: string * [<Optional>] ?cancellationToken: CancellationToken -> Task<MigrondiConfig> abstract ReadMigration: migrationName: string -> Migration abstract ReadMigrationAsync: migrationName: string * [<Optional>] ?cancellationToken: CancellationToken -> Task<Migration> abstract WriteConfiguration: config: MigrondiConfig * writeTo: string -> unit abstract WriteConfigurationAsync: config: MigrondiConfig * writeTo: string * [<Optional>] ?cancellationToken: CancellationToken -> Task abstract WriteMigration: migration: Migration * migrationName: string -> unit abstract WriteMigrationAsync: migration: Migration * migrationName: string * [<Optional>] ?cancellationToken: CancellationToken -> Task
<summary> This is an abstraction to the file system, it allows for custom implementations to either use a physical file system or even a virtual one. It provides both sync and async methods to read and write migrondi specific files. </summary>
Multiple items
type MiFileSystem = interface IMiFileSystem new: logger: ILogger * configurationSerializer: IMiConfigurationSerializer * migrationSerializer: IMiMigrationSerializer * projectRootUri: Uri * migrationsRootUri: Uri -> MiFileSystem

--------------------
new: logger: ILogger * configurationSerializer: IMiConfigurationSerializer * migrationSerializer: IMiMigrationSerializer * projectRootUri: Uri * migrationsRootUri: Uri -> MiFileSystem
abstract IMiFileSystem.ListMigrations: migrationsLocation: string -> Collections.Generic.IReadOnlyList<Migrondi.Core.Migration>
val listMigrations: readFrom: string -> 'a list
val readFrom: string
Multiple items
val string: value: 'T -> string

--------------------
type string = String
val path: Uri
val dir: IO.DirectoryInfo
namespace System.IO
Multiple items
type DirectoryInfo = inherit FileSystemInfo new: path: string -> unit member Create: unit -> unit member CreateSubdirectory: path: string -> DirectoryInfo member Delete: unit -> unit + 1 overload member EnumerateDirectories: unit -> IEnumerable<DirectoryInfo> + 3 overloads member EnumerateFileSystemInfos: unit -> IEnumerable<FileSystemInfo> + 3 overloads member EnumerateFiles: unit -> IEnumerable<FileInfo> + 3 overloads member GetDirectories: unit -> DirectoryInfo array + 3 overloads member GetFileSystemInfos: unit -> FileSystemInfo array + 3 overloads ...
<summary>Exposes instance methods for creating, moving, and enumerating through directories and subdirectories. This class cannot be inherited.</summary>

--------------------
IO.DirectoryInfo(path: string) : IO.DirectoryInfo
property Uri.LocalPath: string with get
<summary>Gets a local operating-system representation of a file name.</summary>
<exception cref="T:System.InvalidOperationException">This instance represents a relative URI, and this property is valid only for absolute URIs.</exception>
<returns>The local operating-system representation of a file name.</returns>
abstract IMiFileSystem.ReadMigration: migrationName: string -> Migrondi.Core.Migration
val readMigration: readFrom: string -> Migrondi.Core.Migration
val file: string
type File = static member AppendAllLines: path: string * contents: IEnumerable<string> -> unit + 1 overload static member AppendAllLinesAsync: path: string * contents: IEnumerable<string> * encoding: Encoding * ?cancellationToken: CancellationToken -> Task + 1 overload static member AppendAllText: path: string * contents: string -> unit + 1 overload static member AppendAllTextAsync: path: string * contents: string * encoding: Encoding * ?cancellationToken: CancellationToken -> Task + 1 overload static member AppendText: path: string -> StreamWriter static member Copy: sourceFileName: string * destFileName: string -> unit + 1 overload static member Create: path: string -> FileStream + 2 overloads static member CreateSymbolicLink: path: string * pathToTarget: string -> FileSystemInfo static member CreateText: path: string -> StreamWriter static member Decrypt: path: string -> unit ...
<summary>Provides static methods for the creation, copying, deletion, moving, and opening of a single file, and aids in the creation of <see cref="T:System.IO.FileStream" /> objects.</summary>
IO.File.ReadAllText(path: string) : string
IO.File.ReadAllText(path: string, encoding: Text.Encoding) : string
val migration: Migrondi.Core.Migration
type IMiMigrationSerializer = abstract DecodeJson: content: string -> Migration abstract DecodeMigrationRecord: content: string -> MigrationRecord abstract DecodeText: content: string * [<Optional>] ?migrationName: string -> Migration abstract EncodeJson: content: Migration -> string abstract EncodeMigrationRecord: content: MigrationRecord -> string abstract EncodeText: content: Migration -> string
<summary> This service is responsible for serializing and deserializing the migration files. The default implementation coordinates between the v0.x and the v1.x formats to provide backwards compatibility. </summary>
abstract IMiFileSystem.WriteMigration: migration: Migrondi.Core.Migration * migrationName: string -> unit
Multiple items
[<Struct>] type DateTimeOffset = new: dateTime: DateTime -> unit + 8 overloads member Add: timeSpan: TimeSpan -> DateTimeOffset member AddDays: days: float -> DateTimeOffset member AddHours: hours: float -> DateTimeOffset member AddMicroseconds: microseconds: float -> DateTimeOffset member AddMilliseconds: milliseconds: float -> DateTimeOffset member AddMinutes: minutes: float -> DateTimeOffset member AddMonths: months: int -> DateTimeOffset member AddSeconds: seconds: float -> DateTimeOffset member AddTicks: ticks: int64 -> DateTimeOffset ...
<summary>Represents a point in time, typically expressed as a date and time of day, relative to Coordinated Universal Time (UTC).</summary>

--------------------
DateTimeOffset ()
DateTimeOffset(dateTime: DateTime) : DateTimeOffset
DateTimeOffset(dateTime: DateTime, offset: TimeSpan) : DateTimeOffset
DateTimeOffset(ticks: int64, offset: TimeSpan) : DateTimeOffset
DateTimeOffset(date: DateOnly, time: TimeOnly, offset: TimeSpan) : DateTimeOffset
DateTimeOffset(year: int, month: int, day: int, hour: int, minute: int, second: int, offset: TimeSpan) : DateTimeOffset
DateTimeOffset(year: int, month: int, day: int, hour: int, minute: int, second: int, millisecond: int, offset: TimeSpan) : DateTimeOffset
DateTimeOffset(year: int, month: int, day: int, hour: int, minute: int, second: int, millisecond: int, calendar: Globalization.Calendar, offset: TimeSpan) : DateTimeOffset
DateTimeOffset(year: int, month: int, day: int, hour: int, minute: int, second: int, millisecond: int, microsecond: int, offset: TimeSpan) : DateTimeOffset
DateTimeOffset(year: int, month: int, day: int, hour: int, minute: int, second: int, millisecond: int, microsecond: int, calendar: Globalization.Calendar, offset: TimeSpan) : DateTimeOffset
property DateTimeOffset.Now: DateTimeOffset with get
<summary>Gets a <see cref="T:System.DateTimeOffset" /> object that is set to the current date and time on the current computer, with the offset set to the local time's offset from Coordinated Universal Time (UTC).</summary>
<returns>A <see cref="T:System.DateTimeOffset" /> object whose date and time is the current local time and whose offset is the local time zone's offset from Coordinated Universal Time (UTC).</returns>
DateTimeOffset.ToUnixTimeMilliseconds() : int64

Type something to start searching.