1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
|
namespace SomhairlesDream.Shared
open System
#if !FABLE_COMPILER
open System.Text.Json
open System.Text.Json.Nodes
#endif
[<CLIMutable>]
type RunIds =
{ ProjectId: string
RunId: string
BaseId: string
TargetId: string }
module Ids =
open System.Text.RegularExpressions
let private resourcePattern = Regex("^[a-z0-9](?:[a-z0-9-]{0,62}[a-z0-9])?$", RegexOptions.CultureInvariant)
let private objectPattern = Regex("^[a-z0-9]+(?:[._-][a-z0-9]+)*$", RegexOptions.CultureInvariant)
let validateResourceId value =
if isNull value || not (resourcePattern.IsMatch(value)) then
Error "resource id must contain only lower-case letters, digits, and hyphens"
else
Ok ()
let validateObjectId value =
if isNull value || not (objectPattern.IsMatch(value)) then
Error "object id must use the dotted lower-case namespace"
else
Ok ()
[<CLIMutable>]
type RunStart =
{ Ids: RunIds
At: DateTimeOffset
TotalSteps: int }
[<CLIMutable>]
type RunHeartbeat =
{ Ids: RunIds
At: DateTimeOffset
Message: string }
[<CLIMutable>]
type RunCheckpoint =
{ Ids: RunIds
At: DateTimeOffset
StepId: string
StepIndex: int
TotalSteps: int
Message: string
ArtifactPath: string }
[<CLIMutable>]
type RunComplete =
{ Ids: RunIds
At: DateTimeOffset
ManifestPath: string }
[<CLIMutable>]
type RunFail =
{ Ids: RunIds
At: DateTimeOffset
StepId: string option
Message: string }
type RunEvent =
| Start of RunStart
| Heartbeat of RunHeartbeat
| Checkpoint of RunCheckpoint
| Complete of RunComplete
| Fail of RunFail
[<CLIMutable>]
type RunEventWire =
{ Kind: string
At: DateTimeOffset
ProjectId: string
RunId: string
BaseId: string
TargetId: string
TotalSteps: int option
StepId: string option
StepIndex: int option
Message: string option
ArtifactPath: string option
ManifestPath: string option }
module RunEvent =
let private ids (value: RunIds) =
value.ProjectId, value.RunId, value.BaseId, value.TargetId
let toWire event =
let kind, eventIds, at, totalSteps, stepId, stepIndex, message, artifactPath, manifestPath =
match event with
| Start value ->
"start", value.Ids, value.At, Some value.TotalSteps, None, None, None, None, None
| Heartbeat value ->
"heartbeat", value.Ids, value.At, None, None, None, Some value.Message, None, None
| Checkpoint value ->
"checkpoint",
value.Ids,
value.At,
Some value.TotalSteps,
Some value.StepId,
Some value.StepIndex,
Some value.Message,
Some value.ArtifactPath,
None
| Complete value ->
"complete", value.Ids, value.At, None, None, None, None, None, Some value.ManifestPath
| Fail value ->
"fail", value.Ids, value.At, None, value.StepId, None, Some value.Message, None, None
let projectId, runId, baseId, targetId = ids eventIds
{ Kind = kind
At = at
ProjectId = projectId
RunId = runId
BaseId = baseId
TargetId = targetId
TotalSteps = totalSteps
StepId = stepId
StepIndex = stepIndex
Message = message
ArtifactPath = artifactPath
ManifestPath = manifestPath }
#if !FABLE_COMPILER
let private addOptional (json: JsonObject) (name: string) value create =
match value with
| Some item -> json[name] <- create item
| None -> ()
let private jsonOptions () =
let options = JsonSerializerOptions()
options.PropertyNamingPolicy <- JsonNamingPolicy.CamelCase
options.WriteIndented <- false
options
let toJson event =
let wire = toWire event
let json = JsonObject()
json["kind"] <- JsonValue.Create(wire.Kind)
json["at"] <- JsonValue.Create(wire.At)
json["projectId"] <- JsonValue.Create(wire.ProjectId)
json["runId"] <- JsonValue.Create(wire.RunId)
json["baseId"] <- JsonValue.Create(wire.BaseId)
json["targetId"] <- JsonValue.Create(wire.TargetId)
addOptional json "totalSteps" wire.TotalSteps (fun value -> JsonValue.Create(value))
addOptional json "stepId" wire.StepId (fun value -> JsonValue.Create(value))
addOptional json "stepIndex" wire.StepIndex (fun value -> JsonValue.Create(value))
addOptional json "message" wire.Message (fun value -> JsonValue.Create(value))
addOptional json "artifactPath" wire.ArtifactPath (fun value -> JsonValue.Create(value))
addOptional json "manifestPath" wire.ManifestPath (fun value -> JsonValue.Create(value))
json.ToJsonString(jsonOptions())
#endif
[<CLIMutable>]
type ArtifactChange =
{ ObjectId: string
Kind: string
Summary: string }
[<CLIMutable>]
type RenderArtifact =
{ ArtifactPath: string
Sha256: string
Bytes: int64
ExportedAt: DateTimeOffset }
[<CLIMutable>]
type StepArtifact =
{ StepId: string
StepIndex: int
ArtifactPath: string
LogPath: string
Sha256: string
Bytes: int64
ExportedAt: DateTimeOffset
ObjectIds: string array
VerifiedObjectIds: string array
Changes: ArtifactChange array
Render: RenderArtifact option }
[<CLIMutable>]
type ArtifactManifest =
{ SchemaVersion: int
ProjectId: string
RunId: string
BaseId: string
TargetId: string
Status: string
StartedAt: DateTimeOffset
CompletedAt: DateTimeOffset
BlenderVersion: string
Steps: StepArtifact array }
[<CLIMutable>]
type RunSnapshot =
{ ProjectId: string
RunId: string
BaseId: string
TargetId: string
Status: string
CurrentStepId: string option
CurrentStepIndex: int
TotalSteps: int
LastHeartbeatAt: DateTimeOffset option
UpdatedAt: DateTimeOffset
Message: string
ManifestPath: string option
ManifestUrl: string option
Error: string option
IsStale: bool }
|