Compare commits

...

13 Commits

27 changed files with 1258 additions and 609 deletions

View File

@@ -22,10 +22,12 @@ Stage: build
&& apt clean -y \
&& rm -rf /var/lib/apt/lists/*
export PATH="$PATH:/root/.dotnet/tools"
# Install dotnet tools
dotnet tool install fable -g
cd /build
cd /build/src/Archivist
dotnet run bundle
Bootstrap: docker
@@ -33,7 +35,7 @@ From: mcr.microsoft.com/dotnet/runtime:9.0
Stage: runtime
%files from build
/build/dist /app
/build/src/Archivist/dist /app
%post
apt update \

View File

@@ -1,14 +1,8 @@
with import <nixpkgs> {};
let
port = 9000;
in
mkShell rec {
mkShell {
nativeBuildInputs = [
tilt
dapr-cli
kustomize
nodejs
mkcert
];
buildInputs = [
@@ -25,13 +19,11 @@ mkShell rec {
PGSSLMODE = "disable";
DAPR_API_TOKEN = "anVzdCBmb3IgbG9jYWwgdXNlCg==";
ARCHMAESTER_AUTH = "YWRtaW46ZW4tdG8tdHJlLWZpcmU=";
CLIENT_PORT = port + 80;
SERVER_PORT = port + 85;
TILT_PORT = port + 50;
# ARCHMAESTER_AUTH = "YWRtaW46ZW4tdG8tdHJlLWZpcmU=";
ARCHMAESTER_AUTH = "admin:en-to-tre-fire";
shellHook = ''
export ARCHMAESTER_URL="https://$USER-atlantis.dev.oceanbox.io"
#export ARCHMAESTER_URL="https://$USER-atlantis.dev.oceanbox.io"
export ARCHMAESTER_URL="https://atlantis.beta.oceanbox.io"
'';
}

View File

@@ -1,251 +0,0 @@
module AclCli
open Argu
open Args
open FSharpPlus
open Serilog
open ArchiveIndex
open Oceanbox.DataAgent
let inline private execAsync job =
job
|> Async.RunSynchronously
|> function
| Ok _ -> ()
| Error e -> Log.Error e
type private Handler = string[] -> Async<Result<unit, string>>
let addOwners (args: ParseResults<Prinicipal>) =
let owners = args.GetResult Prinicipal.Ids |> Array.ofList
if args.Contains Prinicipal.Archive then
let aid = args.GetResult Prinicipal.Archive
withCliAuth (fun auth ->
let api = Remoting.v1.InternalApi(Settings.archmaesterUrl, auth)
let aclApi = api.aclApi ()
async {
try
match! aclApi.addOwners (aid, owners) with
| Ok _ ->
Log.Information $"Added owners %A{owners} to archive {aid}"
return Ok()
| Error e ->
Log.Error $"Error: {e}"
return Error e
with exn ->
return Error exn.Message
})
else
async { return Ok() }
|> Async.RunSynchronously
|> function
| Ok _ -> ()
| Error e -> Log.Error e
let addUsers (args: ParseResults<Prinicipal>) =
let users = args.GetResult Prinicipal.Ids |> Array.ofList
if args.Contains Prinicipal.Archive then
let aid = args.GetResult Prinicipal.Archive
withCliAuth (fun auth ->
let api = Remoting.v1.InternalApi(Settings.archmaesterUrl, auth)
let aclApi = api.aclApi ()
async {
try
match! aclApi.addUsers (aid, users) with
| Ok _ ->
Log.Information $"Added users %A{users} to archive {aid}"
return Ok()
| Error e ->
Log.Error $"Error: {e}"
return Error e
with exn ->
return Error exn.Message
})
else
withCliAuth (fun auth ->
let api = Remoting.v1.InternalApi(Settings.archmaesterUrl, auth)
let aclApi = api.adminApi ()
async {
try
match! aclApi.addUsers (users) with
| Ok _ ->
Log.Information $"Added users %A{users}"
return Ok()
| Error e ->
Log.Error $"Error: {e}"
return Error e
with exn ->
return Error exn.Message
})
|> Async.RunSynchronously
|> function
| Ok _ -> ()
| Error e -> Log.Error e
let addGroups (args: ParseResults<Prinicipal>) =
let groups = args.GetResult Prinicipal.Ids |> Array.ofList
if args.Contains Prinicipal.Archive then
let aid = args.GetResult Prinicipal.Archive
withCliAuth (fun auth ->
let api = Remoting.v1.InternalApi(Settings.archmaesterUrl, auth)
let aclApi = api.aclApi ()
async {
try
match! aclApi.addGroups (aid, groups) with
| Ok _ ->
Log.Information $"Added groups %A{groups} to archive {aid}"
return Ok()
| Error e ->
Log.Error $"Error: {e}"
return Error e
with exn ->
return Error exn.Message
})
else
withCliAuth (fun auth ->
let api = Remoting.v1.InternalApi(Settings.archmaesterUrl, auth)
let aclApi = api.adminApi ()
async {
try
match! aclApi.addGroups (groups) with
| Ok _ ->
Log.Information $"Added groups %A{groups}"
return Ok()
| Error e ->
Log.Error $"Error: {e}"
return Error e
with exn ->
return Error exn.Message
})
|> Async.RunSynchronously
|> function
| Ok _ -> ()
| Error e -> Log.Error e
let deleteOwners (args: ParseResults<Prinicipal>) =
let owners = args.GetResult Prinicipal.Ids |> Array.ofList
if args.Contains Prinicipal.Archive then
let aid = args.GetResult Prinicipal.Archive
withCliAuth (fun auth ->
let api = Remoting.v1.InternalApi(Settings.archmaesterUrl, auth)
let aclApi = api.aclApi ()
async {
try
match! aclApi.removeOwners (aid, owners) with
| Ok _ ->
Log.Information $"Removed owners %A{owners} from archive {aid}"
return Ok()
| Error e ->
Log.Error $"Error: {e}"
return Error e
with exn ->
return Error exn.Message
})
else
async { return Ok() }
|> Async.RunSynchronously
|> function
| Ok _ -> ()
| Error e -> Log.Error e
let deleteUsers (args: ParseResults<Prinicipal>) =
let users = args.GetResult Prinicipal.Ids |> Array.ofList
if args.Contains Prinicipal.Archive then
let aid = args.GetResult Prinicipal.Archive
withCliAuth (fun auth ->
let api = Remoting.v1.InternalApi(Settings.archmaesterUrl, auth)
let aclApi = api.aclApi ()
async {
try
match! aclApi.removeUsers (aid, users) with
| Ok _ ->
Log.Information $"Removed users %A{users} from archive {aid}"
return Ok()
| Error e ->
Log.Error $"Error: {e}"
return Error e
with exn ->
return Error exn.Message
})
else
withCliAuth (fun auth ->
let api = Remoting.v1.InternalApi(Settings.archmaesterUrl, auth)
let aclApi = api.adminApi ()
async {
try
match! aclApi.removeUsers (users) with
| Ok _ ->
Log.Information $"Removed users %A{users}"
return Ok()
| Error e ->
Log.Error $"Error: {e}"
return Error e
with exn ->
return Error exn.Message
})
|> Async.RunSynchronously
|> function
| Ok _ -> ()
| Error e -> Log.Error e
let deleteGroups (args: ParseResults<Prinicipal>) =
let groups = args.GetResult Prinicipal.Ids |> Array.ofList
if args.Contains Prinicipal.Archive then
let aid = args.GetResult Prinicipal.Archive
withCliAuth (fun auth ->
let api = Remoting.v1.InternalApi(Settings.archmaesterUrl, auth)
let aclApi = api.aclApi ()
async {
try
match! aclApi.removeGroups (aid, groups) with
| Ok _ ->
Log.Information $"Removed groups %A{groups} from archive {aid}"
return Ok()
| Error e ->
Log.Error $"Error: {e}"
return Error e
with exn ->
return Error exn.Message
})
else
withCliAuth (fun auth ->
let api = Remoting.v1.InternalApi(Settings.archmaesterUrl, auth)
let aclApi = api.adminApi ()
async {
try
match! aclApi.removeGroups (groups) with
| Ok _ ->
Log.Information $"Removed groups %A{groups}"
return Ok()
| Error e ->
Log.Error $"Error: {e}"
return Error e
with exn ->
return Error exn.Message
})
|> Async.RunSynchronously
|> function
| Ok _ -> ()
| Error e -> Log.Error e

View File

@@ -17,28 +17,18 @@ let inline private execAsync job =
| Ok _ -> ()
| Error e -> Log.Error e
let getArchiveId (args: ParseResults<AddArchive>) (idx: ArchiveIndex.ArchiveIndex) =
match args.TryGetResult AddArchive.Id with
| Some id -> id
| None -> idx.archiveId
let getArchiveBasePath (args: ParseResults<AddArchive>) =
let e = "neither index.json or base path specified"
let fs = args.GetResult AddArchive.Files
match args.TryGetResult AddArchive.Index with
| Some f -> getBasePath f
| None ->
if fs.Length = 1 then
// TODO: Handle exn
if isDir fs[0] then
Path.GetFullPath fs[0]
else
Log.Error e
failwith e
let path = args.GetResult AddArchive.Index
if File.Exists path then
getBasePath path
elif Directory.Exists path then
let p = Path.Join [| path; "index.json" |]
if File.Exists p then
getBasePath p
else
Log.Error e
failwith e
failwith $"Could not determine base path: {path}"
else
failwith $"Could not determine base path: {path}"
let getModelAreaArchives modelId =
withCliAuth (fun auth ->
@@ -220,8 +210,8 @@ let modifyArchiveAttribs (args: ParseResults<ModifyArchiveAttribs>) =
})
|> execAsync
let deleteArchives (args: ParseResults<Delete>) =
args.GetResult Delete.Archive |> retireArchive |> ignore
let deleteArchives (args: ParseResults<DeleteArchive>) =
args.GetResult DeleteArchive.Archive |> retireArchive |> ignore
let readDriftersInputJson file =
if File.Exists file then
@@ -239,8 +229,7 @@ let rec addArchive (args: ParseResults<AddArchive>) =
| None -> $"{basePath}/index.json"
|> readArchiveIdx
let files =
getArchiveFiles idx.archiveType basePath (args.GetResult AddArchive.Files |> Array.ofList)
let files = getArchiveFiles idx.archiveType basePath
let modelArea = initiateModelArea idx basePath
@@ -275,7 +264,7 @@ let rec addArchive (args: ParseResults<AddArchive>) =
| Error e -> failwith e
| _ -> "", false
let published = args.GetResult(AddArchive.Published, defaultValue = true)
let published = true
let saveRes =
try
@@ -289,15 +278,15 @@ let rec addArchive (args: ParseResults<AddArchive>) =
| Ok() -> Log.Information $"Successfully added archive %s{idx.name}"
| Error err -> Log.Error $"Error: {err}"
let createSubArchive (args: ParseResults<SubArchive>) =
let createSubArchive (args: ParseResults<SubArchiveArgs>) =
let fetchRefArchive = getArchive >> Async.RunSynchronously
let createSubArchive refArchive = {
uuid = Guid.NewGuid()
reference = refArchive.props.archiveId
name = args.GetResult SubArchive.Name
startFile = args.GetResult SubArchive.From
endFile = args.GetResult(SubArchive.To, defaultValue = Int32.MaxValue)
name = args.GetResult SubArchiveArgs.Name
startFile = args.GetResult SubArchiveArgs.From
endFile = args.GetResult(SubArchiveArgs.To, defaultValue = Int32.MaxValue)
acl = {
owners = [||]
groups = [||]
@@ -307,12 +296,12 @@ let createSubArchive (args: ParseResults<SubArchive>) =
polygon = [||]
json = ""
isPublic = false
isPublished = args.GetResult(SubArchive.Published, defaultValue = true)
isPublished = args.GetResult(SubArchiveArgs.Published, defaultValue = true)
}
let postSubArchive = postSubArchive false >> Async.RunSynchronously
fetchRefArchive (args.GetResult SubArchive.Ref)
fetchRefArchive (args.GetResult SubArchiveArgs.Parent)
|> Result.map createSubArchive
|> Result.bind postSubArchive
|> function
@@ -384,9 +373,9 @@ let showRelatedArchives (args: ParseResults<ShowArchive>) =
}
|> Async.RunSynchronously
let augmentArchive (args: ParseResults<Augment>) =
let aid = args.GetResult Augment.Archive
let fnames = args.GetResult Augment.Files |> List.map Path.GetFullPath
let augmentArchive (args: ParseResults<AugmentArchive>) =
let aid = args.GetResult AugmentArchive.Archive
let fnames = args.GetResult AugmentArchive.Files |> List.map Path.GetFullPath
withCliAuth (fun auth ->
let api = Remoting.v1.InternalApi(Settings.archmaesterUrl, auth)
@@ -429,10 +418,10 @@ let augmentArchive (args: ParseResults<Augment>) =
})
|> execAsync
let resizeArchive (args: ParseResults<Resize>) =
let aid = args.GetResult Resize.Archive
let first = args.GetResult(Resize.From, Int32.MinValue)
let last = args.GetResult(Resize.To, Int32.MaxValue)
let resizeArchive (args: ParseResults<ResizeArchive>) =
let aid = args.GetResult ResizeArchive.Archive
let first = args.GetResult(ResizeArchive.From, Int32.MinValue)
let last = args.GetResult(ResizeArchive.To, Int32.MaxValue)
withCliAuth (fun auth ->
let api = Remoting.v1.InternalApi(Settings.archmaesterUrl, auth)

View File

@@ -48,8 +48,8 @@ type ModelAreaOrId =
type ArchiveIndex = {
archiveId: ArchiveId
modelArea: ModelAreaId
reference: ArchiveId option
modelArea: ModelAreaId option
name: string
description: string
archiveType: ArchiveType
@@ -57,17 +57,15 @@ type ArchiveIndex = {
center: (single * single) option
initialZoom: float option
startTime: DateTime
owners: string array option
users: string[]
groups: string[]
owner: string option
isPublished: bool
isPublic: bool
associated: ArchiveId[]
} with
static member empty = {
archiveId = Guid.Empty
modelArea = Guid.Empty
reference = None
modelArea = None
name = ""
description = ""
archiveType = Fvcom(FvcomVariant.Any, FvcomFormat.Any)
@@ -75,9 +73,7 @@ type ArchiveIndex = {
center = None
initialZoom = None
startTime = DateTime.UnixEpoch
owners = None
users = [||]
groups = [||]
owner = None
isPublished = true
isPublic = false
associated = [||]
@@ -94,7 +90,7 @@ module ArchiveIndex =
archiveId = get.Required.Field "archiveId" Decode.guid
reference = get.Optional.Field "reference" Decode.guid
name = get.Required.Field "name" Decode.string
modelArea = get.Optional.Field "modelArea" Decode.guid
modelArea = get.Required.Field "modelArea" Decode.guid
description = get.Optional.Field "description" Decode.string |> Option.defaultValue ""
archiveType = get.Required.Field "format" formatDecoder
projection = get.Optional.Field "projection" Decode.string |> Option.defaultValue ""
@@ -103,19 +99,15 @@ module ArchiveIndex =
startTime =
get.Optional.Field "startTime" Decode.datetimeUtc
|> Option.defaultValue DateTime.UnixEpoch
owners = get.Optional.Field "owners" (Decode.array Decode.string)
users = get.Required.Field "users" (Decode.array Decode.string)
groups = get.Required.Field "groups" (Decode.array Decode.string)
owner = get.Optional.Field "owner" Decode.string
isPublished = get.Optional.Field "isPublished" Decode.bool |> Option.defaultValue true
isPublic = get.Optional.Field "isPublic" Decode.bool |> Option.defaultValue false
associated = get.Required.Field "associated" (Decode.array Decode.guid)
})
let inline withCliAuth f =
let inline withCliAuth (f: string -> Async<'a>): Async<'a> =
match Settings.cliAuth with
| None ->
Log.Fatal "You must provide ARCHMAESTER_AUTH"
Error "You are not logged in" |> async.Return
| None -> failwith "ARCHMAESTER_AUTH is not set"
| Some auth -> f auth
let private (|NumSeq|_|) x =
@@ -298,13 +290,8 @@ let FvStatsArchive =
member x.getNumFrames ds = 12 // year
}
let getArchiveFiles format basePath (files: string[]) =
let fs =
if files.Length = 1 then
Directory.GetFiles(basePath, "*.nc", SearchOption.AllDirectories)
else
files
let getArchiveFiles format basePath =
let fs = Directory.GetFiles(basePath, "*.nc", SearchOption.AllDirectories)
let f =
match format with
| Atmo _
@@ -312,7 +299,6 @@ let getArchiveFiles format basePath (files: string[]) =
| FvStats _ -> fs
| Fvcom _ -> numSort fs
| Any -> failwith "not possible"
f
let updateCoordinateProjection (p: string option) (file: string) =
@@ -434,7 +420,7 @@ let tryAddModelArea (model: ModelAreaIndex) basePath =
async {
match! modelAreaApi.getModelArea model.modelAreaId with
| Some model -> return Ok model
| Some model -> return model
| None ->
let newModelArea: ModelArea = {
modelAreaId = model.modelAreaId
@@ -450,7 +436,7 @@ let tryAddModelArea (model: ModelAreaIndex) basePath =
}
let! m = adminApi.addModelArea newModelArea
return m |> Result.map (fun _ -> newModelArea)
return newModelArea
})
let initiateModelArea (idx: ArchiveIndex) (basePath: string) =
@@ -465,7 +451,7 @@ let initiateModelArea (idx: ArchiveIndex) (basePath: string) =
let! parent = adminApi.getArchiveDto rid
match parent with
| Ok p -> return Ok p.props.modelArea
| Ok p -> return p.props.modelArea
| Error e -> return failwith $"Could not get model area id: {e}"
}
@@ -475,8 +461,7 @@ let initiateModelArea (idx: ArchiveIndex) (basePath: string) =
Environment.Exit 1
failwith "Missing modelArea or reference."
match idx.modelArea with
| None ->
if idx.modelArea = Guid.Empty then
if idx.reference.IsSome then
async {
match! inventoryApi.getArchive idx.reference.Value with
@@ -487,56 +472,43 @@ let initiateModelArea (idx: ArchiveIndex) (basePath: string) =
}
else
failBadly ()
| Some mid -> modelAreaApi.getModelArea mid
// match model with
// | ModelAreaId mid -> modelAreaApi.getModelArea mid
// | ModelArea m -> tryAddModelArea m basePath
// | ModelAreaFile f ->
// async {
// match readModelAreaFile f with
// | Ok m -> return! tryAddModelArea m basePath
// | Error e -> return Error e
// }
else modelAreaApi.getModelArea idx.modelArea
match idx.archiveType with
| FvStats _
| Atmo _ ->
// NOTE(simkir): For now, arome takes one model area which it is an archive in. In this example only world,
// which basically covers the nordics, which is where most (if not all) model areas are in.
idx.modelArea
|> function
| Some mid ->
modelAreaApi.getModelArea mid
|> Async.map (
function
| Some m -> Ok m.modelAreaId
| None -> Error "Arome must specify a model area")
| None -> Error "Arome must specify a model area" |> async.Return
// NOTE(simkir): For now, arome takes one model area which it is an archive in.
// In this example only world, which basically covers the nordics, which is where most (if not all) model areas are in.
modelAreaApi.getModelArea idx.modelArea
|> Async.map (
function
| Some m -> m.modelAreaId
| None -> failwith "Arome must specify a model area")
| Fvcom _ ->
async {
let! model = modelAreaApi.getModelAreaId idx.name
match model with
| Ok m ->
Log.Debug $"Existing model area: %A{model}"
return Ok m
| Error _ ->
Log.Debug "Make new model area"
if idx.modelArea <> Guid.Empty then
match! modelAreaApi.getModelArea idx.modelArea with
| Some m ->
Log.Debug $"Existing model area: %A{m}"
return m.modelAreaId
| None -> return failwith "Unknown model area"
else
Log.Debug "Model area not specified, checking parent archive"
match! checkModelArea () with
| Some ma ->
Log.Debug $"New model area: %A{ma}"
return Ok ma.modelAreaId
return ma.modelAreaId
| None ->
Log.Error "Error"
return Error "Error"
let msg = "Model area not specified."
Log.Error msg
return failwith msg
}
| _ ->
match idx.reference with
| Some rid -> getParentModelAreaId rid
| None -> failwith $"Missing archive reference for non-base archive {idx.archiveType}")
|> Async.RunSynchronously
|> Result.defaultValue HelloWorld
let archiveTypeToIArchive t =
match t with
@@ -610,17 +582,17 @@ let instantiateArchiveDto (idx, modelArea, basePath, files, reverse, json, publi
endTime = endTimeUtc
expires = None
created = DateTime.MinValue
owner = idx.owners |> Option.bind Array.tryHead |> Option.defaultValue ""
owner = idx.owner |> Option.defaultValue ""
isPublic = false
isPublished = published
polygon = None
json = ""
}
acl = {
owners = idx.owners |> Option.defaultValue Array.empty
users = idx.users
groups = idx.groups
shares = [||] // TODO: fix
owners = idx.owner |> Option.map Array.singleton |> Option.defaultValue [||]
users = [||]
groups = [||]
shares = [||]
}
files = files'
polygon = boundingPoly

View File

@@ -11,7 +11,6 @@
<Compile Include="ArchiveIndex.fs"/>
<Compile Include="ArchiveCli.fs"/>
<Compile Include="ModelAreaCli.fs"/>
<Compile Include="AclCli.fs"/>
<Compile Include="Listing.fs"/>
<Compile Include="Main.fs"/>
</ItemGroup>
@@ -28,7 +27,7 @@
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
<PrivateAssets>all</PrivateAssets>
</PackageReference>
<PackageReference Include="Oceanbox.FvcomKit" Version="5.6.0"/>
<PackageReference Include="Oceanbox.FvcomKit" Version="5.12.0"/>
<PackageReference Include="Serilog" Version="4.2.0"/>
<PackageReference Include="Serilog.Sinks.Console" Version="6.0.0"/>
<PackageReference Include="Serilog.Sinks.Seq" Version="9.0.0"/>

View File

@@ -1,7 +1,6 @@
module Args
open System
open System.IO
open Argu
open Archmaester.Dto
@@ -55,21 +54,21 @@ type ListModel =
| Verbose -> "Detailed listing"
| Json -> "Json output"
type List =
| [<CliPrefix(CliPrefix.None); AltCommandLine("a")>] Archives of ParseResults<ListArchive>
| [<CliPrefix(CliPrefix.None); AltCommandLine("m")>] Models of ParseResults<ListModel>
| [<CliPrefix(CliPrefix.None); AltCommandLine("t")>] Types
type ListArchiveAttribs =
| Base_path of regex: string
| Verbose
| Json
interface IArgParserTemplate with
member this.Usage =
match this with
| Archives _ -> "List archives"
| Models _ -> "List models"
| Types -> "List types"
| Base_path _ -> "List attribs matching base path"
| Verbose -> "Detailed listing"
| Json -> "Json output"
type ShowArchive =
| All
| Refs
| Related
| Assoc
| Files
| Json
| [<MainCommand; Last>] Archive of id: ArchiveId
@@ -78,7 +77,7 @@ type ShowArchive =
match this with
| Json -> "Json output"
| Refs -> "List archives referencing archive"
| Related -> "List related archives"
| Assoc -> "List associated archives"
| Files -> "List file names"
| All -> "List all available files (for resize)"
| Archive _ -> "Archive id"
@@ -94,81 +93,54 @@ type ShowModel =
| Verbose -> "Detailed listing"
| Json -> "Json output"
type Show =
| [<CliPrefix(CliPrefix.None); AltCommandLine("a")>] Archive of ParseResults<ShowArchive>
| [<CliPrefix(CliPrefix.None); AltCommandLine("m")>] Model of ParseResults<ShowModel>
type ShowArchiveAttribs =
| Verbose
| Json
| [<MainCommand; Last>] Attribs of id: AttribsId
interface IArgParserTemplate with
member this.Usage =
match this with
| Archive _ -> "List archive"
| Model _ -> "List model"
| Attribs _ -> "Attribs id"
| Verbose -> "Detailed listing"
| Json -> "Json output"
type AddArchive =
| Index of index: string
| No_sort
| Id of guid: ArchiveId
| Force
| Published of bool
| [<MainCommand; Last>] Files of files: string list
interface IArgParserTemplate with
member this.Usage =
match this with
| No_sort -> "No sorting of files"
| Index _ -> "index.json"
| Id _ -> "Archive id to update"
| Files _ -> "Files to add"
| Force -> "Forcefully add or update"
| Published _ -> "Publish archive (default: true)"
type SubArchive =
| [<Mandatory>] Ref of guid: ArchiveId
type SubArchiveArgs =
| [<Mandatory>] Parent of guid: ArchiveId
| [<Mandatory>] From of first: int
| To of last: int
| Published of bool
| [<MainCommand; Last>] Name of name: string
| [<MainCommand; ExactlyOnce>] Name of name: string
interface IArgParserTemplate with
member this.Usage =
match this with
| Ref _ -> "Reference archive"
| Parent _ -> "Reference archive"
| From _ -> "Index of first file to include"
| To _ -> "Index of last file to include"
| Published _ -> "Publish archive (default: true)"
| Name _ -> "Name of the new archive"
type AddArchive =
| [<MainCommand; Last>] Index of json: string
interface IArgParserTemplate with
member this.Usage =
match this with
| Index _ -> "index.json"
type AddModel =
| [<MainCommand; Last>] File of json: string
| [<MainCommand; Last>] Index of json: string
interface IArgParserTemplate with
member this.Usage =
match this with
| File _ -> "model.json"
| Index _ -> "model.json"
type Prinicipal =
| Archive of id: ArchiveId
| [<MainCommand; Last>] Ids of name: string list
type DeleteArchive =
| Force
| [<MainCommand; Last>] Archive of id: string
interface IArgParserTemplate with
member this.Usage =
match this with
| Archive _ -> "Apply to archive"
| Ids _ -> "Identies"
type Add =
| [<CliPrefix(CliPrefix.None); AltCommandLine("a")>] Archive of ParseResults<AddArchive>
| [<CliPrefix(CliPrefix.None); AltCommandLine("s")>] Sub of ParseResults<SubArchive>
| [<CliPrefix(CliPrefix.None); AltCommandLine("m")>] Model of ParseResults<AddModel>
| [<CliPrefix(CliPrefix.None); AltCommandLine("o")>] Owner of ParseResults<Prinicipal>
| [<CliPrefix(CliPrefix.None); AltCommandLine("u")>] User of ParseResults<Prinicipal>
| [<CliPrefix(CliPrefix.None); AltCommandLine("g")>] Group of ParseResults<Prinicipal>
| [<CliPrefix(CliPrefix.None); AltCommandLine("t")>] Type of string
interface IArgParserTemplate with
member this.Usage =
match this with
| Archive _ -> "Add archive"
| Sub _ -> "Add sub-archive"
| Model _ -> "Add model"
| Owner _ -> "Add owner"
| User _ -> "Add user"
| Group _ -> "Add group"
| Type _ -> "Add type"
| Force -> "Force delete"
| Archive _ -> "Archive id or index.json"
type DeleteModel =
| Force
@@ -179,22 +151,14 @@ type DeleteModel =
| Force -> "Force delete"
| Model _ -> "Model id"
type Delete =
| [<CliPrefix(CliPrefix.None); AltCommandLine("a")>] Archive of id: string
| [<CliPrefix(CliPrefix.None); AltCommandLine("m")>] Model of ParseResults<DeleteModel>
| [<CliPrefix(CliPrefix.None); AltCommandLine("o")>] Owner of ParseResults<Prinicipal>
| [<CliPrefix(CliPrefix.None); AltCommandLine("u")>] User of ParseResults<Prinicipal>
| [<CliPrefix(CliPrefix.None); AltCommandLine("g")>] Group of ParseResults<Prinicipal>
| [<CliPrefix(CliPrefix.None); AltCommandLine("t")>] Type of string
type DeleteArchiveAttrib =
| Force
| [<MainCommand; Last>] Attrib of id: AttribsId
interface IArgParserTemplate with
member this.Usage =
match this with
| Archive _ -> "Delete archive"
| Model _ -> "Delete model"
| Owner _ -> "Delete owner"
| User _ -> "Delete user"
| Group _ -> "Delete group"
| Type _ -> "Delete type"
| Force -> "Force delete"
| Attrib _ -> "Model id"
type ModifyArchiveAttribs =
| Base_Path of string
@@ -208,7 +172,7 @@ type ModifyArchiveAttribs =
| Json of json: string
| Add_Related of ArchiveId
| Remove_Related of ArchiveId
| [<MainCommand; Last>] ArchiveId of id: ModelAreaId
| [<MainCommand; ExactlyOnce>] ArchiveId of id: ModelAreaId
interface IArgParserTemplate with
member this.Usage =
match this with
@@ -233,7 +197,7 @@ type ModifyArchive =
| Json of file: string
| Fence of string list
| End
| [<MainCommand; Last>] ArchiveId of id: ArchiveId
| [<MainCommand; ExactlyOnce>] ArchiveId of id: ArchiveId
interface IArgParserTemplate with
member this.Usage =
match this with
@@ -253,7 +217,7 @@ type ModifyModel =
| Zoom of single
| Polygon of file: string
| Projection of string
| [<MainCommand; Last>] ModelId of id: ModelAreaId
| [<MainCommand; ExactlyOnce>] ModelId of id: ModelAreaId
interface IArgParserTemplate with
member this.Usage =
match this with
@@ -265,30 +229,19 @@ type ModifyModel =
| Projection _ -> "Projection of bounding polygon"
| ModelId _ -> ""
type Modify =
| [<CliPrefix(CliPrefix.None); AltCommandLine("a")>] Archive of ParseResults<ModifyArchive>
| [<CliPrefix(CliPrefix.None); AltCommandLine("t")>] ArchiveAttribs of ParseResults<ModifyArchiveAttribs>
| [<CliPrefix(CliPrefix.None); AltCommandLine("m")>] Model of ParseResults<ModifyModel>
interface IArgParserTemplate with
member this.Usage =
match this with
| Archive _ -> "Modify archive"
| ArchiveAttribs _ -> "Modify archive attribs"
| Model _ -> "Modify model"
type Augment =
type AugmentArchive =
| [<Mandatory>] Archive of guid: ArchiveId
| [<MainCommand; Last>] Files of name: string list
| [<MainCommand; ExactlyOnce>] Files of name: string list
interface IArgParserTemplate with
member this.Usage =
match this with
| Archive _ -> "Archive id to augment"
| Files _ -> "Files to add"
type Resize =
type ResizeArchive =
| [<Mandatory>] From of first: int
| To of last: int
| [<MainCommand; Last>] Archive of id: ArchiveId
| [<MainCommand; ExactlyOnce>] Archive of id: ArchiveId
interface IArgParserTemplate with
member this.Usage =
match this with
@@ -296,25 +249,66 @@ type Resize =
| To _ -> "Index of last file to include"
| Archive _ -> "Archive to resize"
type Verbs =
| [<CliPrefix(CliPrefix.None)>] Add of ParseResults<Add>
| [<CliPrefix(CliPrefix.None); AltCommandLine([| "ls" |])>] List of ParseResults<List>
| [<CliPrefix(CliPrefix.None)>] Show of ParseResults<Show>
| [<CliPrefix(CliPrefix.None); AltCommandLine([| "rm" |])>] Delete of ParseResults<Delete>
| [<CliPrefix(CliPrefix.None)>] Modify of ParseResults<Modify>
| [<CliPrefix(CliPrefix.None)>] Augment of ParseResults<Augment>
| [<CliPrefix(CliPrefix.None)>] Resize of ParseResults<Resize>
| Log_Level of level: int
type ArchiveArgs =
| [<CliPrefix(CliPrefix.None); AltCommandLine("a")>] Add of ParseResults<AddArchive>
| [<CliPrefix(CliPrefix.None); AltCommandLine([| "ls" |])>] List of ParseResults<ListArchive>
| [<CliPrefix(CliPrefix.None); AltCommandLine("s")>] Show of ParseResults<ShowArchive>
| [<CliPrefix(CliPrefix.None); AltCommandLine([| "m" |])>] Modify of ParseResults<ModifyArchive>
| [<CliPrefix(CliPrefix.None); AltCommandLine([| "rm" |])>] Delete of ParseResults<DeleteArchive>
| [<CliPrefix(CliPrefix.None); AltCommandLine([| "aug" |])>] Augment of ParseResults<AugmentArchive>
| [<CliPrefix(CliPrefix.None); AltCommandLine([| "r" |])>] Resize of ParseResults<ResizeArchive>
interface IArgParserTemplate with
member this.Usage =
match this with
| Add _ -> "Add archive"
| List _ -> "List archives"
| Show _ -> "Show archive"
| Modify _ -> "Modify archive"
| Delete _ -> "Delete earchive"
| Augment _ -> "Augment archive"
| Resize _ -> "Resize archive"
type ModelAreaArgs =
| [<CliPrefix(CliPrefix.None); AltCommandLine("a")>] Add of ParseResults<AddModel>
| [<CliPrefix(CliPrefix.None); AltCommandLine([| "ls" |])>] List of ParseResults<ListModel>
| [<CliPrefix(CliPrefix.None); AltCommandLine("s")>] Show of ParseResults<ShowModel>
| [<CliPrefix(CliPrefix.None); AltCommandLine([| "m" |])>] Modify of ParseResults<ModifyModel>
| [<CliPrefix(CliPrefix.None); AltCommandLine([| "rm" |])>] Delete of ParseResults<DeleteModel>
interface IArgParserTemplate with
member this.Usage =
match this with
| Add _ -> "Add model area"
| List _ -> "List model areas"
| Show _ -> "Show model area"
| Modify _ -> "Modify model area"
| Delete _ -> "Delete model area"
type AttrArgs =
| [<CliPrefix(CliPrefix.None); AltCommandLine([| "ls" |])>] List of ParseResults<ListArchiveAttribs>
| [<CliPrefix(CliPrefix.None); AltCommandLine("s")>] Show of ParseResults<ShowArchiveAttribs>
| [<CliPrefix(CliPrefix.None); AltCommandLine([| "m" |])>] Modify of ParseResults<ModifyArchiveAttribs>
| [<CliPrefix(CliPrefix.None); AltCommandLine([| "rm" |])>] Delete of ParseResults<DeleteArchiveAttrib>
interface IArgParserTemplate with
member this.Usage =
match this with
| List _ -> "List archive attribs"
| Show _ -> "Show archive attribs"
| Modify _ -> "Modify archive attribs"
| Delete _ -> "Delete archive attribs"
type Args =
| [<CliPrefix(CliPrefix.None)>] Archive of ParseResults<ArchiveArgs>
| [<CliPrefix(CliPrefix.None)>] Sub of ParseResults<SubArchiveArgs>
| [<CliPrefix(CliPrefix.None)>] Model of ParseResults<ModelAreaArgs>
| [<CliPrefix(CliPrefix.None)>] Attr of ParseResults<AttrArgs>
| [<AltCommandLine("-l")>] Log_Level of level: int
| [<AltCommandLine("-v")>] Version
interface IArgParserTemplate with
member this.Usage =
match this with
| List _ -> "List entities"
| Show _ -> "Show entities"
| Add _ -> "Add entities"
| Modify _ -> "Modify entities"
| Delete _ -> "Delete entities"
| Augment _ -> "Augment archive"
| Resize _ -> "Resize archive"
| Archive _ -> "Archive commands"
| Sub _ -> "Sub-archive commands"
| Model _ -> "Model area commands"
| Attr _ -> "Archive attribute commands"
| Log_Level _ -> "0=Error, 1=Warning, 2=Info, 3=Debug, 4=Verbose"
| Version -> "Show the current version"
| Version -> "Show the current version"

View File

@@ -1,5 +1,6 @@
module Archivist
open Oceanbox.FvcomKit.Thredds
open Serilog
open Serilog.Events
open Argu
@@ -8,112 +9,92 @@ open Args
let configureSerilog level =
let n =
match level with
| 0 -> LogEventLevel.Error
| 1 -> LogEventLevel.Warning
| 0 -> LogEventLevel.Verbose
| 1 -> LogEventLevel.Debug
| 2 -> LogEventLevel.Information
| 3 -> LogEventLevel.Debug
| _ -> LogEventLevel.Verbose
| 3 -> LogEventLevel.Warning
| _ -> LogEventLevel.Error
LoggerConfiguration()
.MinimumLevel.Is(n)
.WriteTo.Console(theme = Serilog.Sinks.SystemConsole.Themes.ConsoleTheme.None)
.CreateLogger()
let addHandler (args: ParseResults<Add>) =
if args.Contains Add.Archive then
ArchiveCli.addArchive (args.GetResult Add.Archive)
elif args.Contains Add.Sub then
ArchiveCli.createSubArchive (args.GetResult Add.Sub)
elif args.Contains Add.Model then
ModelAreaCli.addModel (args.GetResult Add.Model)
elif args.Contains Add.Owner then
AclCli.addOwners (args.GetResult Add.Owner)
elif args.Contains Add.User then
AclCli.addUsers (args.GetResult Add.User)
elif args.Contains Add.Group then
AclCli.addGroups (args.GetResult Add.Group)
elif args.Contains Add.Type then
ArchiveCli.addType (args.GetResult Add.Type)
else
printfn $"{args.Parser.PrintUsage()}"
let listHandler (args: ParseResults<List>) =
if args.Contains List.Archives then
Listing.listArchives (args.GetResult List.Archives)
elif args.Contains List.Models then
ModelAreaCli.listModels (args.GetResult List.Models)
else
eprintfn "No list subcommand listed"
eprintfn "%s" (args.Parser.PrintUsage())
()
let showHandler (args: ParseResults<Show>) =
if args.Contains Show.Archive then
let show = args.GetResult Show.Archive
if show.Contains Related then
let archiveHandler (args: ParseResults<ArchiveArgs>) =
if args.Contains ArchiveArgs.Add then
ArchiveCli.addArchive (args.GetResult ArchiveArgs.Add)
elif args.Contains ArchiveArgs.List then
Listing.listArchives (args.GetResult ArchiveArgs.List)
elif args.Contains ArchiveArgs.Show then
let show = args.GetResult ArchiveArgs.Show
if show.Contains Assoc then
ArchiveCli.showRelatedArchives show
else
ArchiveCli.showArchive show
elif args.Contains Show.Model then
ModelAreaCli.showModel (args.GetResult Show.Model)
elif args.Contains ArchiveArgs.Delete then
ArchiveCli.deleteArchives (args.GetResult ArchiveArgs.Delete)
elif args.Contains ArchiveArgs.Modify then
ArchiveCli.modifyArchive (args.GetResult ArchiveArgs.Modify)
elif args.Contains ArchiveArgs.Augment then
ArchiveCli.augmentArchive (args.GetResult ArchiveArgs.Augment)
elif args.Contains ArchiveArgs.Resize then
ArchiveCli.resizeArchive (args.GetResult ArchiveArgs.Resize)
else
args.Parser.PrintUsage() |> printfn "%s"
printfn $"{args.Parser.PrintUsage()}"
let deleteHandler (args: ParseResults<Delete>) =
if args.Contains Delete.Archive then
ArchiveCli.deleteArchives args
elif args.Contains Delete.Model then
ModelAreaCli.deleteModel (args.GetResult Delete.Model)
elif args.Contains Delete.Owner then
AclCli.deleteOwners (args.GetResult Delete.Owner)
elif args.Contains Delete.User then
AclCli.deleteUsers (args.GetResult Delete.User)
elif args.Contains Delete.Group then
AclCli.deleteGroups (args.GetResult Delete.Group)
elif args.Contains Delete.Type then
ArchiveCli.deleteType (args.GetResult Delete.Type)
let archiveAttribsHandler (args: ParseResults<AttrArgs>) =
if args.Contains AttrArgs.List then
failwith "not implemented"
elif args.Contains AttrArgs.Show then
failwith "not implemented"
elif args.Contains AttrArgs.Modify then
ArchiveCli.modifyArchiveAttribs (args.GetResult AttrArgs.Modify)
elif args.Contains AttrArgs.Delete then
failwith "not implemented"
else
()
printfn $"{args.Parser.PrintUsage()}"
let modifyHandler (args: ParseResults<Modify>) =
if args.Contains Modify.Archive then
ArchiveCli.modifyArchive (args.GetResult Modify.Archive)
elif args.Contains Modify.ArchiveAttribs then
ArchiveCli.modifyArchiveAttribs (args.GetResult Modify.ArchiveAttribs)
elif args.Contains Modify.Model then
ModelAreaCli.modifyModel (args.GetResult Modify.Model)
let modelAreaHandler (args: ParseResults<ModelAreaArgs>) =
if args.Contains ModelAreaArgs.Add then
ModelAreaCli.addModel (args.GetResult ModelAreaArgs.Add)
elif args.Contains ModelAreaArgs.List then
ModelAreaCli.listModels (args.GetResult ModelAreaArgs.List)
elif args.Contains ModelAreaArgs.Show then
ModelAreaCli.showModel (args.GetResult ModelAreaArgs.Show)
elif args.Contains ModelAreaArgs.Modify then
ModelAreaCli.modifyModel (args.GetResult ModelAreaArgs.Modify)
elif args.Contains ModelAreaArgs.Delete then
ModelAreaCli.deleteModel (args.GetResult ModelAreaArgs.Delete)
else
()
printfn $"{args.Parser.PrintUsage()}"
let subArchiveHandler (args: ParseResults<SubArchiveArgs>) =
ArchiveCli.createSubArchive args
[<EntryPoint>]
let main argv =
let parser =
ArgumentParser.Create<Verbs>(programName = "Archivist", errorHandler = errorHandler)
ArgumentParser.Create<Args>(programName = "Archivist", errorHandler = errorHandler)
let args = parser.Parse argv
Log.Logger <- configureSerilog (args.GetResult(Log_Level, defaultValue = 3))
printfn $"Archivist connection string: {Settings.archmaesterUrl}"
if args.Contains Add then
addHandler (args.GetResult Add)
elif args.Contains List then
listHandler (args.GetResult List)
elif args.Contains Show then
showHandler (args.GetResult Show)
elif args.Contains Delete then
deleteHandler (args.GetResult Delete)
elif args.Contains Modify then
modifyHandler (args.GetResult Modify)
elif args.Contains Augment then
ArchiveCli.augmentArchive (args.GetResult Augment)
elif args.Contains Resize then
ArchiveCli.resizeArchive (args.GetResult Resize)
elif args.Contains Version then
showVersion ()
else
parser.PrintUsage() |> printfn "%s"
0
try
if args.Contains Archive then
archiveHandler (args.GetResult Archive)
elif args.Contains Model then
modelAreaHandler (args.GetResult Model)
elif args.Contains Attr then
archiveAttribsHandler (args.GetResult Attr)
elif args.Contains Sub then
subArchiveHandler (args.GetResult Sub)
elif args.Contains Version then
showVersion ()
else
parser.PrintUsage() |> printfn "%s"
0
with exn ->
Log.Fatal exn.Message
1

View File

@@ -97,7 +97,7 @@ let getBaseModelAreas (helloWorld: Guid) : Result<ModelArea[], string> =
|> Async.RunSynchronously
let addModel (args: ParseResults<AddModel>) =
let file = args.GetResult AddModel.File |> Path.GetFullPath
let file = args.GetResult AddModel.Index |> Path.GetFullPath
let basePath = Path.GetDirectoryName file
match readModelAreaFile file with

View File

@@ -17,5 +17,4 @@ let archmaesterUrl =
let cliAuth = tryGetEnv "ARCHMAESTER_AUTH" |> Option.map base64enc
let julianStart = DateTime.Parse("1858-11-17 00:00:00Z").ToUniversalTime()

View File

@@ -561,13 +561,13 @@ module Handlers =
return None
}
let getModelAreaId (ctx: HttpContext) (name: string) =
let getModelAreaIdByName (ctx: HttpContext) (name: string) =
Log.Information $"Getting model area: {name}"
async {
let db = Archives.Archivist (Db.getDataSource ())
match db.getModelAreaId name with
match db.getModelAreaIdByName name with
| Ok modelArea ->
Log.Debug $"getModelAreaId: %A{modelArea}"
ctx.SetStatusCode 201
@@ -943,7 +943,7 @@ module Handlers =
let modelAreaHandlers (ctx: HttpContext) : Api.ModelArea = {
getModelArea = getModelArea ctx
getSubModelAreas = getSubModelAreas ctx
getModelAreaId = getModelAreaId ctx
getModelAreaIdByName = getModelAreaIdByName ctx
getModelAreaPolygon = getModelAreaPolygon ctx
}

View File

@@ -1015,7 +1015,7 @@ type Archivist(dataSource: NpgsqlDataSource) =
|> Result.flatten
|> Result.mapError (fun err -> $"Could not retrieve model area with {mid}: {err}")
member x.getModelAreaId(name: string) =
member x.getModelAreaIdByName(name: string) =
let f (ctx: Entity.ArchiveContext) =
let modelArea =
ctx.ModelAreas.AsNoTracking().Where(fun m -> m.Name = name).ToArray ()
@@ -1322,12 +1322,12 @@ type Archivist(dataSource: NpgsqlDataSource) =
.Include(_.Associations)
.SelectMany (fun t ->
t.Associations.Where (fun t ->
(t.Ref.Type.Kind = knd || t.Ref.Type.Kind = "*" || knd = "*")
&& (t.Ref.Type.Variant = vrt || t.Ref.Type.Variant = "*" || vrt = "*")
&& (t.Ref.Type.Format = fmt || t.Ref.Type.Format = "*" || fmt = "*")))
(t.ToAttr.Type.Kind = knd || t.ToAttr.Type.Kind = "*" || knd = "*")
&& (t.ToAttr.Type.Variant = vrt || t.ToAttr.Type.Variant = "*" || vrt = "*")
&& (t.ToAttr.Type.Format = fmt || t.ToAttr.Type.Format = "*" || fmt = "*")))
|> Seq.map (fun t ->
Log.Debug $"assoc attrib id: %A{t.AttributesId}"
t.AttributesId)
Log.Debug $"assoc attrib id: %A{t.FromId}"
t.FromId)
|> Seq.toArray
|> Ok
with e ->
@@ -1358,7 +1358,7 @@ type Archivist(dataSource: NpgsqlDataSource) =
try
withDb (fun ctx ->
let t = x.getAttribs (ctx, aid)
let refAttribs = t.Associations.Select(_.RefId).ToArray ()
let refAttribs = t.Associations.Select(_.ToId).ToArray ()
Log.Debug $"refs {t.AttribsId} has length: {refAttribs.Length} (%A{refAttribs})"
ctx.Archives
@@ -1390,8 +1390,8 @@ type Archivist(dataSource: NpgsqlDataSource) =
let rt = x.getAttribs (ctx, related)
Log.Debug $"addAssociatedAttribs: {rt.AttribsId} -> {t.AttribsId}"
let q = Entity.Association ()
q.Attributes <- t
q.Ref <- rt
q.FromAttr <- t
q.ToAttr <- rt
ctx.Add q |> ignore
ctx.SaveChanges () |> ignore)
@@ -1401,7 +1401,7 @@ type Archivist(dataSource: NpgsqlDataSource) =
let rt = x.getAttribs (ctx, related)
let ref =
ctx.Association.Where(fun y -> y.AttributesId = t.AttribsId && y.RefId = rt.AttribsId).First ()
ctx.Association.Where(fun y -> y.FromId = t.AttribsId && y.ToId = rt.AttribsId).First ()
ctx.Remove ref |> ignore
Log.Debug $"removeAssociatedAttribs: {t.AttribsId} <- {rt.AttribsId}"

View File

@@ -146,6 +146,8 @@ SqlMapper.AddTypeHandler(GeometryHandler<LineString>())
let taskToList (t: Task<seq<'a>>) = t |> Async.AwaitTask |> Async.RunSynchronously |> Seq.toList
let taskToArray (t: Task<seq<'a>>) = t |> Async.AwaitTask |> Async.RunSynchronously |> Seq.toArray
let taskToListAsync (t: Task<seq<'a>>) = t |> Async.AwaitTask |> Async.map Seq.toList
let taskToArrayAsync (t: Task<seq<'a>>) = t |> Async.AwaitTask |> Async.map Seq.toArray
let inline (=>) a b = a, box b
let private geometryToPolygon (geometry: Geometry) =
@@ -498,3 +500,88 @@ type Archivist(conn: IDbConnection) =
Log.Error $"Archivist.getRefArchivesProps error: {e.Message}"
Log.Debug $"{e}"
Error $"getRefArchivesProps: Could not retrieve sub-archives to '{aid}' of type '{atype}': {e.Message}"
member this.getArchiveOwnersAsync(aid: Dto.ArchiveId) : Async<string[]> =
select {
for ao in Table.archive_owner do
innerJoin u in Table.user on (ao.owner_id = u.id)
where (ao.archive_id = aid)
}
|> conn.SelectAsync<Table.User>
|> taskToArrayAsync
|> Async.map (Array.map _.name)
member this.getArchiveOwners(aid: Dto.ArchiveId) : Result<string[], string> =
try
this.getArchiveOwnersAsync aid
|> Async.RunSynchronously
|> Ok
with e ->
let msg = $"Achmaester.getArchiveAcl: Could not get owners for {aid}: {e.Message}"
Log.Error msg
Log.Debug $"{e}"
Error msg
member this.getArchiveUsersAsync(aid: Dto.ArchiveId) : Async<string[]> =
select {
for au in Table.archive_user do
innerJoin u in Table.user on (au.user_id = u.id)
where (au.archive_id = aid)
}
|> conn.SelectAsync<Table.User>
|> taskToArrayAsync
|> Async.map (Array.map _.name)
member this.getArchiveUsers(aid: Dto.ArchiveId) : Result<string[], string> =
try
this.getArchiveUsersAsync aid
|> Async.RunSynchronously
|> Ok
with e ->
let msg = $"Achmaester.getArchiveAcl: Could not get users for {aid}: {e.Message}"
Log.Error msg
Log.Debug $"{e}"
Error msg
member this.getArchiveGroupsAsync(aid: Dto.ArchiveId) : Async<string[]> =
select {
for ag in Table.archive_group do
innerJoin g in Table.group on (ag.group_id = g.id)
where (ag.archive_id = aid)
}
|> conn.SelectAsync<Table.Group>
|> taskToArrayAsync
|> Async.map (Array.map _.name)
member this.getArchiveGroups(aid: Dto.ArchiveId) : Result<string[], string> =
try
this.getArchiveGroupsAsync aid
|> Async.RunSynchronously
|> Ok
with e ->
let msg = $"Achmaester.getArchiveAcl: Could not get groups for {aid}: {e.Message}"
Log.Error msg
Log.Debug $"{e}"
Error msg
member this.getArchiveAcl(aid: Dto.ArchiveId) : Result<Dto.ArchiveAcl, string> =
try
[|
this.getArchiveOwnersAsync aid
this.getArchiveUsersAsync aid
this.getArchiveGroupsAsync aid
|]
|> Async.Parallel
|> Async.RunSynchronously
|> fun x ->
Ok {
users = x[0]
owners = x[1]
groups = x[2]
shares = [||]
}
with e ->
let msg = $"Achmaester.getArchiveAcl: Could not get acl:s for {aid}: {e.Message}"
Log.Error msg
Log.Debug $"{e}"
Error msg

View File

@@ -52,6 +52,9 @@ namespace Entity
[Column("owners")]
public ICollection<ArchiveOwner> Owners { get; set; } = new List<ArchiveOwner>();
[Column("principals")]
public ICollection<ArchivePrincipal> Principals { get; set; } = new List<ArchivePrincipal>();
[Column("users")]
public ICollection<ArchiveUser> Users { get; set; } = new List<ArchiveUser>();

View File

@@ -35,9 +35,11 @@ namespace Entity
public DbSet<File> Files { get; set; }
public DbSet<Group> Groups { get; set; }
public DbSet<User> Users { get; set; }
public DbSet<Principal> Principal { get; set; }
public DbSet<ArchiveFile> ArchiveFiles { get; set; }
public DbSet<ArchiveOwner> ArchiveOwners { get; set; }
public DbSet<ArchiveUser> ArchiveUsers { get; set; }
public DbSet<ArchivePrincipal> ArchivePrincipal { get; set; }
public DbSet<ArchiveGroup> ArchiveGroups { get; set; }
public DbSet<Share> Shares { get; set; }
public DbSet<Association> Association { get; set; }
@@ -90,6 +92,10 @@ namespace Entity
.WithOne(f => f.Archive)
.OnDelete(DeleteBehavior.Cascade);
entity.HasMany(e => e.Principals)
.WithOne(f => f.Archive)
.OnDelete(DeleteBehavior.Cascade);
entity.HasMany(e => e.Users)
.WithOne(f => f.Archive)
.OnDelete(DeleteBehavior.Cascade);
@@ -124,7 +130,7 @@ namespace Entity
.OnDelete(DeleteBehavior.Cascade);
entity.HasMany(e => e.Associations)
.WithOne(f => f.Attributes)
.WithOne(f => f.FromAttr)
.OnDelete(DeleteBehavior.Cascade);
entity
@@ -158,6 +164,25 @@ namespace Entity
.WithMany(f => f.Owners);
});
modelBuilder.Entity<Principal>(entity =>
{
entity.ToTable("principals");
entity.HasIndex(i => i.Name).IsUnique();
entity
.HasMany(x => x.Archives)
.WithOne(x => x.Principal)
.OnDelete(DeleteBehavior.Cascade);
});
modelBuilder.Entity<ArchivePrincipal>(entity =>
{
entity.ToTable("archive_principals");
entity.HasKey(p => new {p.ArchiveId , p.PrincipalId});
entity
.HasOne(f => f.Archive)
.WithMany(f => f.Principals);
});
modelBuilder.Entity<User>(entity =>
{
entity.ToTable("users");
@@ -204,7 +229,7 @@ namespace Entity
modelBuilder.Entity<Association>(entity =>
{
entity.HasKey(rt => new { rt.AttributesId, rt.RefId });
entity.HasKey(rt => new { AttributesId = rt.FromId, RefId = rt.ToId });
entity.ToTable("associations");
});

View File

@@ -0,0 +1,21 @@
using System;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
namespace Entity
{
public partial class ArchivePrincipal
{
[Column("principal_id")]
public int PrincipalId { get; set; }
[Column("archive_id")]
public Guid ArchiveId { get; set; }
[ForeignKey("PrincipalId")]
public Principal Principal{ get; set; } = null!;
[ForeignKey("ArchiveId")]
public Archive Archive{ get; set; } = null!;
}
}

View File

@@ -5,16 +5,16 @@ namespace Entity
{
public partial class Association
{
[Column("attributes_id")]
public int AttributesId { get; set; }
[Column("from_id")]
public int FromId { get; set; }
[Column("ref_id")]
public int RefId { get; set; }
[Column("to_id")]
public int ToId { get; set; }
[ForeignKey("AttributesId")]
public Attribs Attributes { get; set; } = null!;
[ForeignKey("from_id")]
public Attribs FromAttr { get; set; } = null!;
[ForeignKey("RefId")]
public Attribs Ref { get; set; } = null!;
[ForeignKey("to_id")]
public Attribs ToAttr { get; set; } = null!;
}
}

View File

@@ -18,7 +18,11 @@ namespace Entity
[Required]
[ForeignKey("TypeId")]
public Type Type { get; set; } = null!;
public Type PType { get; set; } = null!;
[Required]
[Column("type")]
public string Type { get; set; } = "";
// [Column("ref_id")]
// public int? RefId { get; set; }

View File

@@ -33,6 +33,10 @@ namespace Entity
[Column("attribs_id")]
public int AttribsId { get; set; }
[StringLength(80)]
[Column("status")]
public string Status { get; set; }
// This is needed to cascade delete files upon attribs deletion
[ForeignKey("AttribsId")]
public Attribs Attribs { get; set; } = null!;

View File

@@ -0,0 +1,646 @@
// <auto-generated />
using System;
using Entity;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Infrastructure;
using Microsoft.EntityFrameworkCore.Migrations;
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
using NetTopologySuite.Geometries;
using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata;
#nullable disable
namespace Entity.Migrations
{
[DbContext(typeof(ArchiveContext))]
[Migration("20250308073137_RenameAssociations")]
partial class RenameAssociations
{
/// <inheritdoc />
protected override void BuildTargetModel(ModelBuilder modelBuilder)
{
#pragma warning disable 612, 618
modelBuilder
.HasAnnotation("ProductVersion", "9.0.2")
.HasAnnotation("Relational:MaxIdentifierLength", 63);
NpgsqlModelBuilderExtensions.HasPostgresExtension(modelBuilder, "postgis");
NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder);
modelBuilder.Entity("Entity.Archive", b =>
{
b.Property<Guid>("ArchiveId")
.ValueGeneratedOnAdd()
.HasColumnType("uuid")
.HasColumnName("id");
b.Property<int>("AttribsId")
.HasColumnType("integer")
.HasColumnName("attribs_id");
b.Property<DateTime>("Created")
.HasColumnType("timestamp with time zone")
.HasColumnName("created");
b.Property<DateTime?>("Expires")
.HasColumnType("timestamp with time zone")
.HasColumnName("expires");
b.Property<int>("Frames")
.HasColumnType("integer")
.HasColumnName("frames");
b.Property<Geometry>("Geometry")
.HasColumnType("geometry")
.HasColumnName("geometry");
b.Property<string>("Json")
.HasColumnType("jsonb")
.HasColumnName("json");
b.Property<string>("Name")
.IsRequired()
.HasMaxLength(80)
.HasColumnType("character varying(80)")
.HasColumnName("name");
b.Property<bool>("Public")
.HasColumnType("boolean")
.HasColumnName("public");
b.Property<bool>("Published")
.HasColumnType("boolean")
.HasColumnName("published");
b.Property<Guid?>("RefId")
.HasColumnType("uuid")
.HasColumnName("archive_ref_id");
b.Property<DateTime>("StartTime")
.HasColumnType("timestamp with time zone")
.HasColumnName("start_time");
b.HasKey("ArchiveId");
b.HasIndex("AttribsId");
b.HasIndex("RefId");
b.ToTable("archives", (string)null);
});
modelBuilder.Entity("Entity.ArchiveFile", b =>
{
b.Property<Guid>("ArchiveId")
.HasColumnType("uuid")
.HasColumnName("archive_id");
b.Property<int>("FileId")
.HasColumnType("integer")
.HasColumnName("file_id");
b.HasKey("ArchiveId", "FileId");
b.HasIndex("FileId");
b.ToTable("archive_files", (string)null);
});
modelBuilder.Entity("Entity.ArchiveGroup", b =>
{
b.Property<Guid>("ArchiveId")
.HasColumnType("uuid")
.HasColumnName("archive_id");
b.Property<int>("GroupId")
.HasColumnType("integer")
.HasColumnName("group_id");
b.HasKey("ArchiveId", "GroupId");
b.HasIndex("GroupId");
b.ToTable("archive_groups", (string)null);
});
modelBuilder.Entity("Entity.ArchiveOwner", b =>
{
b.Property<Guid>("ArchiveId")
.HasColumnType("uuid")
.HasColumnName("archive_id");
b.Property<int>("OwnerId")
.HasColumnType("integer")
.HasColumnName("owner_id");
b.HasKey("ArchiveId", "OwnerId");
b.HasIndex("OwnerId");
b.ToTable("archive_owners", (string)null);
});
modelBuilder.Entity("Entity.ArchiveUser", b =>
{
b.Property<Guid>("ArchiveId")
.HasColumnType("uuid")
.HasColumnName("archive_id");
b.Property<int>("UserId")
.HasColumnType("integer")
.HasColumnName("user_id");
b.HasKey("ArchiveId", "UserId");
b.HasIndex("UserId");
b.ToTable("archive_users", (string)null);
});
modelBuilder.Entity("Entity.Association", b =>
{
b.Property<int>("FromId")
.HasColumnType("integer")
.HasColumnName("from_id");
b.Property<int>("ToId")
.HasColumnType("integer")
.HasColumnName("to_id");
b.Property<int>("from_id")
.HasColumnType("integer");
b.Property<int>("to_id")
.HasColumnType("integer");
b.HasKey("FromId", "ToId");
b.HasIndex("from_id");
b.HasIndex("to_id");
b.ToTable("associations", null, t =>
{
t.Property("from_id")
.HasColumnName("from_id1");
t.Property("to_id")
.HasColumnName("to_id1");
});
});
modelBuilder.Entity("Entity.Attribs", b =>
{
b.Property<int>("AttribsId")
.ValueGeneratedOnAdd()
.HasColumnType("integer")
.HasColumnName("id");
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<int>("AttribsId"));
b.Property<string>("BasePath")
.IsRequired()
.HasColumnType("text")
.HasColumnName("base_path");
b.Property<float>("DefaultZoom")
.HasColumnType("real")
.HasColumnName("default_zoom");
b.Property<string>("Description")
.IsRequired()
.HasMaxLength(250)
.HasColumnType("character varying(250)")
.HasColumnName("description");
b.Property<Point>("FocalPoint")
.HasColumnType("geometry (point)")
.HasColumnName("focal_point");
b.Property<int>("Freq")
.HasColumnType("integer")
.HasColumnName("freq");
b.Property<Geometry>("Geometry")
.HasColumnType("geometry")
.HasColumnName("geometry");
b.Property<string>("Json")
.HasColumnType("jsonb")
.HasColumnName("json");
b.Property<Guid>("ModelAreaId")
.HasColumnType("uuid")
.HasColumnName("model_area_id");
b.Property<string>("Projection")
.HasColumnType("text")
.HasColumnName("projection");
b.Property<bool>("Retired")
.HasColumnType("boolean")
.HasColumnName("retired");
b.Property<int>("TypeId")
.HasColumnType("integer")
.HasColumnName("type_id");
b.HasKey("AttribsId");
b.HasIndex("ModelAreaId");
b.HasIndex("TypeId");
b.ToTable("attribs", (string)null);
});
modelBuilder.Entity("Entity.File", b =>
{
b.Property<int>("FileId")
.ValueGeneratedOnAdd()
.HasColumnType("integer")
.HasColumnName("id");
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<int>("FileId"));
b.Property<int>("AttribsId")
.HasColumnType("integer")
.HasColumnName("attribs_id");
b.Property<int>("Frames")
.HasColumnType("integer")
.HasColumnName("frames");
b.Property<string>("Name")
.IsRequired()
.HasMaxLength(500)
.HasColumnType("character varying(500)")
.HasColumnName("name");
b.Property<int>("Ordering")
.HasColumnType("integer")
.HasColumnName("ordering");
b.Property<bool>("Reverse")
.HasColumnType("boolean")
.HasColumnName("reverse");
b.Property<DateTime>("StartTime")
.HasColumnType("timestamp with time zone")
.HasColumnName("start_time");
b.HasKey("FileId");
b.HasIndex("AttribsId");
b.ToTable("files", (string)null);
});
modelBuilder.Entity("Entity.Group", b =>
{
b.Property<int>("GroupId")
.ValueGeneratedOnAdd()
.HasColumnType("integer")
.HasColumnName("id");
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<int>("GroupId"));
b.Property<string>("Name")
.IsRequired()
.HasMaxLength(80)
.HasColumnType("character varying(80)")
.HasColumnName("name");
b.HasKey("GroupId");
b.HasIndex("Name")
.IsUnique();
b.ToTable("groups", (string)null);
});
modelBuilder.Entity("Entity.ModelArea", b =>
{
b.Property<Guid>("ModelAreaId")
.HasColumnType("uuid")
.HasColumnName("id");
b.Property<float>("DefaultZoom")
.HasColumnType("real")
.HasColumnName("default_zoom");
b.Property<string>("Description")
.IsRequired()
.HasMaxLength(250)
.HasColumnType("character varying(250)")
.HasColumnName("description");
b.Property<Point>("FocalPoint")
.HasColumnType("geometry (point)")
.HasColumnName("focal_point");
b.Property<Geometry>("Geometry")
.HasColumnType("geometry")
.HasColumnName("geometry");
b.Property<string>("Json")
.HasColumnType("jsonb")
.HasColumnName("json");
b.Property<string>("Name")
.IsRequired()
.HasColumnType("text")
.HasColumnName("name");
b.Property<Guid?>("parent_area_id")
.HasColumnType("uuid");
b.HasKey("ModelAreaId");
b.HasIndex("Name")
.IsUnique();
b.HasIndex("parent_area_id");
b.ToTable("model_areas", (string)null);
});
modelBuilder.Entity("Entity.Share", b =>
{
b.Property<Guid>("ShareId")
.HasColumnType("uuid")
.HasColumnName("share_id");
b.Property<Guid>("ArchiveId")
.HasColumnType("uuid")
.HasColumnName("archive_id");
b.HasKey("ShareId", "ArchiveId");
b.HasIndex("ArchiveId");
b.ToTable("shares", (string)null);
});
modelBuilder.Entity("Entity.Type", b =>
{
b.Property<int>("TypeId")
.ValueGeneratedOnAdd()
.HasColumnType("integer")
.HasColumnName("id");
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<int>("TypeId"));
b.Property<string>("Format")
.HasMaxLength(32)
.HasColumnType("character varying(32)")
.HasColumnName("format");
b.Property<string>("Kind")
.IsRequired()
.HasMaxLength(32)
.HasColumnType("character varying(32)")
.HasColumnName("kind");
b.Property<string>("Variant")
.IsRequired()
.HasMaxLength(32)
.HasColumnType("character varying(32)")
.HasColumnName("variant");
b.HasKey("TypeId");
b.HasIndex("Kind", "Variant", "Format")
.IsUnique();
b.ToTable("types", (string)null);
});
modelBuilder.Entity("Entity.User", b =>
{
b.Property<int>("UserId")
.ValueGeneratedOnAdd()
.HasColumnType("integer")
.HasColumnName("id");
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<int>("UserId"));
b.Property<string>("Name")
.IsRequired()
.HasMaxLength(80)
.HasColumnType("character varying(80)")
.HasColumnName("name");
b.HasKey("UserId");
b.HasIndex("Name")
.IsUnique();
b.ToTable("users", (string)null);
});
modelBuilder.Entity("Entity.Archive", b =>
{
b.HasOne("Entity.Attribs", "Attribs")
.WithMany("Archives")
.HasForeignKey("AttribsId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.HasOne("Entity.Archive", "Ref")
.WithMany("RefArchives")
.HasForeignKey("RefId")
.OnDelete(DeleteBehavior.Cascade);
b.Navigation("Attribs");
b.Navigation("Ref");
});
modelBuilder.Entity("Entity.ArchiveFile", b =>
{
b.HasOne("Entity.Archive", "Archive")
.WithMany("Files")
.HasForeignKey("ArchiveId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.HasOne("Entity.File", "File")
.WithMany()
.HasForeignKey("FileId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.Navigation("Archive");
b.Navigation("File");
});
modelBuilder.Entity("Entity.ArchiveGroup", b =>
{
b.HasOne("Entity.Archive", "Archive")
.WithMany("Groups")
.HasForeignKey("ArchiveId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.HasOne("Entity.Group", "Group")
.WithMany("Archives")
.HasForeignKey("GroupId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.Navigation("Archive");
b.Navigation("Group");
});
modelBuilder.Entity("Entity.ArchiveOwner", b =>
{
b.HasOne("Entity.Archive", "Archive")
.WithMany("Owners")
.HasForeignKey("ArchiveId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.HasOne("Entity.User", "Owner")
.WithMany()
.HasForeignKey("OwnerId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.Navigation("Archive");
b.Navigation("Owner");
});
modelBuilder.Entity("Entity.ArchiveUser", b =>
{
b.HasOne("Entity.Archive", "Archive")
.WithMany("Users")
.HasForeignKey("ArchiveId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.HasOne("Entity.User", "User")
.WithMany("Archives")
.HasForeignKey("UserId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.Navigation("Archive");
b.Navigation("User");
});
modelBuilder.Entity("Entity.Association", b =>
{
b.HasOne("Entity.Attribs", "FromAttr")
.WithMany("Associations")
.HasForeignKey("from_id")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.HasOne("Entity.Attribs", "ToAttr")
.WithMany()
.HasForeignKey("to_id")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.Navigation("FromAttr");
b.Navigation("ToAttr");
});
modelBuilder.Entity("Entity.Attribs", b =>
{
b.HasOne("Entity.ModelArea", "ModelArea")
.WithMany("Attribs")
.HasForeignKey("ModelAreaId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.HasOne("Entity.Type", "Type")
.WithMany()
.HasForeignKey("TypeId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.Navigation("ModelArea");
b.Navigation("Type");
});
modelBuilder.Entity("Entity.File", b =>
{
b.HasOne("Entity.Attribs", "Attribs")
.WithMany("Files")
.HasForeignKey("AttribsId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.Navigation("Attribs");
});
modelBuilder.Entity("Entity.ModelArea", b =>
{
b.HasOne("Entity.ModelArea", "Parent")
.WithMany()
.HasForeignKey("parent_area_id");
b.Navigation("Parent");
});
modelBuilder.Entity("Entity.Share", b =>
{
b.HasOne("Entity.Archive", "Archive")
.WithMany("Shares")
.HasForeignKey("ArchiveId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.Navigation("Archive");
});
modelBuilder.Entity("Entity.Archive", b =>
{
b.Navigation("Files");
b.Navigation("Groups");
b.Navigation("Owners");
b.Navigation("RefArchives");
b.Navigation("Shares");
b.Navigation("Users");
});
modelBuilder.Entity("Entity.Attribs", b =>
{
b.Navigation("Archives");
b.Navigation("Associations");
b.Navigation("Files");
});
modelBuilder.Entity("Entity.Group", b =>
{
b.Navigation("Archives");
});
modelBuilder.Entity("Entity.ModelArea", b =>
{
b.Navigation("Attribs");
});
modelBuilder.Entity("Entity.User", b =>
{
b.Navigation("Archives");
});
#pragma warning restore 612, 618
}
}
}

View File

@@ -0,0 +1,145 @@
using Microsoft.EntityFrameworkCore.Migrations;
#nullable disable
namespace Entity.Migrations
{
/// <inheritdoc />
public partial class RenameAssociations : Migration
{
/// <inheritdoc />
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropForeignKey(
name: "FK_associations_attribs_attributes_id",
table: "associations");
migrationBuilder.DropForeignKey(
name: "FK_associations_attribs_ref_id",
table: "associations");
migrationBuilder.DropPrimaryKey(
name: "PK_associations",
table: "associations");
migrationBuilder.RenameColumn(
name: "ref_id",
table: "associations",
newName: "to_id1");
migrationBuilder.RenameColumn(
name: "attributes_id",
table: "associations",
newName: "from_id1");
migrationBuilder.RenameIndex(
name: "IX_associations_ref_id",
table: "associations",
newName: "IX_associations_to_id1");
migrationBuilder.AddColumn<int>(
name: "from_id",
table: "associations",
type: "integer",
nullable: false,
defaultValue: 0);
migrationBuilder.AddColumn<int>(
name: "to_id",
table: "associations",
type: "integer",
nullable: false,
defaultValue: 0);
migrationBuilder.AddPrimaryKey(
name: "PK_associations",
table: "associations",
columns: new[] { "from_id", "to_id" });
migrationBuilder.CreateIndex(
name: "IX_associations_from_id1",
table: "associations",
column: "from_id1");
migrationBuilder.AddForeignKey(
name: "FK_associations_attribs_from_id1",
table: "associations",
column: "from_id1",
principalTable: "attribs",
principalColumn: "id",
onDelete: ReferentialAction.Cascade);
migrationBuilder.AddForeignKey(
name: "FK_associations_attribs_to_id1",
table: "associations",
column: "to_id1",
principalTable: "attribs",
principalColumn: "id",
onDelete: ReferentialAction.Cascade);
}
/// <inheritdoc />
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropForeignKey(
name: "FK_associations_attribs_from_id1",
table: "associations");
migrationBuilder.DropForeignKey(
name: "FK_associations_attribs_to_id1",
table: "associations");
migrationBuilder.DropPrimaryKey(
name: "PK_associations",
table: "associations");
migrationBuilder.DropIndex(
name: "IX_associations_from_id1",
table: "associations");
migrationBuilder.DropColumn(
name: "from_id",
table: "associations");
migrationBuilder.DropColumn(
name: "to_id",
table: "associations");
migrationBuilder.RenameColumn(
name: "to_id1",
table: "associations",
newName: "ref_id");
migrationBuilder.RenameColumn(
name: "from_id1",
table: "associations",
newName: "attributes_id");
migrationBuilder.RenameIndex(
name: "IX_associations_to_id1",
table: "associations",
newName: "IX_associations_ref_id");
migrationBuilder.AddPrimaryKey(
name: "PK_associations",
table: "associations",
columns: new[] { "attributes_id", "ref_id" });
migrationBuilder.AddForeignKey(
name: "FK_associations_attribs_attributes_id",
table: "associations",
column: "attributes_id",
principalTable: "attribs",
principalColumn: "id",
onDelete: ReferentialAction.Cascade);
migrationBuilder.AddForeignKey(
name: "FK_associations_attribs_ref_id",
table: "associations",
column: "ref_id",
principalTable: "attribs",
principalColumn: "id",
onDelete: ReferentialAction.Cascade);
}
}
}

View File

@@ -156,19 +156,34 @@ namespace Entity.Migrations
modelBuilder.Entity("Entity.Association", b =>
{
b.Property<int>("AttributesId")
b.Property<int>("FromId")
.HasColumnType("integer")
.HasColumnName("attributes_id");
.HasColumnName("from_id");
b.Property<int>("RefId")
b.Property<int>("ToId")
.HasColumnType("integer")
.HasColumnName("ref_id");
.HasColumnName("to_id");
b.HasKey("AttributesId", "RefId");
b.Property<int>("from_id")
.HasColumnType("integer");
b.HasIndex("RefId");
b.Property<int>("to_id")
.HasColumnType("integer");
b.ToTable("associations", (string)null);
b.HasKey("FromId", "ToId");
b.HasIndex("from_id");
b.HasIndex("to_id");
b.ToTable("associations", null, t =>
{
t.Property("from_id")
.HasColumnName("from_id1");
t.Property("to_id")
.HasColumnName("to_id1");
});
});
modelBuilder.Entity("Entity.Attribs", b =>
@@ -517,21 +532,21 @@ namespace Entity.Migrations
modelBuilder.Entity("Entity.Association", b =>
{
b.HasOne("Entity.Attribs", "Attributes")
b.HasOne("Entity.Attribs", "FromAttr")
.WithMany("Associations")
.HasForeignKey("AttributesId")
.HasForeignKey("from_id")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.HasOne("Entity.Attribs", "Ref")
b.HasOne("Entity.Attribs", "ToAttr")
.WithMany()
.HasForeignKey("RefId")
.HasForeignKey("to_id")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.Navigation("Attributes");
b.Navigation("FromAttr");
b.Navigation("Ref");
b.Navigation("ToAttr");
});
modelBuilder.Entity("Entity.Attribs", b =>

View File

@@ -0,0 +1,22 @@
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
namespace Entity
{
public partial class Principal
{
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
[Column("id")]
public int Id { get; set; }
[Required]
[StringLength(80)]
[Column("name")]
public string Name { get; set; } = "";
[Column("archives")]
public ICollection<ArchivePrincipal> Archives { get; set; } = new List<ArchivePrincipal>();
}
}

View File

@@ -7,7 +7,7 @@ open Helpers
initializeContext()
let libPath = Path.getFullName "Api"
let libPath = Path.getFullName "Trident"
let testPath = Path.getFullName "tests"
let distPath = Path.getFullName "dist"

View File

@@ -42,7 +42,7 @@ module Api =
type ModelArea = {
getModelArea: ModelAreaId -> Async<Dto.ModelArea option>
getSubModelAreas: ModelAreaId -> Async<Dto.ModelArea[]>
getModelAreaId: string -> Async<Result<ModelAreaId, string>>
getModelAreaIdByName: string -> Async<Result<ModelAreaId, string>>
getModelAreaPolygon: ModelAreaId -> Async<Result<(single * single)[], string>>
}

View File

@@ -4,7 +4,7 @@
<OutputType>Library</OutputType>
<TargetFramework>net9.0</TargetFramework>
<IsPackable>true</IsPackable>
<PackageId>Poseidon.Api</PackageId>
<PackageId>Trident.Apis</PackageId>
<Authors/>
<Company/>
<Version>2.101.0</Version>