1module Concourse exposing
2    ( AuthSession
3    , AuthToken
4    , Build
5    , BuildDuration
6    , BuildId
7    , BuildName
8    , BuildPlan
9    , BuildPrep
10    , BuildPrepStatus(..)
11    , BuildResources
12    , BuildResourcesInput
13    , BuildResourcesOutput
14    , BuildStep(..)
15    , CSRFToken
16    , Cause
17    , Check
18    , CheckIdentifier
19    , CheckStatus(..)
20    , ClusterInfo
21    , DatabaseID
22    , HookedPlan
23    , Job
24    , JobBuildIdentifier
25    , JobIdentifier
26    , JobInput
27    , JobName
28    , JobOutput
29    , JsonValue(..)
30    , Metadata
31    , MetadataField
32    , Pipeline
33    , PipelineGroup
34    , PipelineIdentifier
35    , PipelineName
36    , Resource
37    , ResourceIdentifier
38    , Team
39    , TeamName
40    , User
41    , Version
42    , VersionedResource
43    , VersionedResourceIdentifier
44    , csrfTokenHeaderName
45    , customDecoder
46    , decodeAuthToken
47    , decodeBuild
48    , decodeBuildPlan
49    , decodeBuildPrep
50    , decodeBuildResources
51    , decodeCause
52    , decodeCheck
53    , decodeInfo
54    , decodeJob
55    , decodeMetadata
56    , decodePipeline
57    , decodeResource
58    , decodeTeam
59    , decodeUser
60    , decodeVersion
61    , decodeVersionedResource
62    , emptyBuildResources
63    , encodeBuild
64    , encodeJob
65    , encodePipeline
66    , encodeTeam
67    , mapBuildPlan
68    , retrieveCSRFToken
69    )
70
71import Array exposing (Array)
72import Concourse.BuildStatus exposing (BuildStatus)
73import Dict exposing (Dict)
74import Json.Decode
75import Json.Decode.Extra exposing (andMap)
76import Json.Encode
77import Json.Encode.Extra
78import Time
79
80
81
82-- AuthToken
83
84
85type alias AuthToken =
86    String
87
88
89type alias DatabaseID =
90    Int
91
92
93decodeAuthToken : Json.Decode.Decoder AuthToken
94decodeAuthToken =
95    customDecoder
96        (Json.Decode.succeed (\a b -> ( a, b ))
97            |> andMap (Json.Decode.field "type" Json.Decode.string)
98            |> andMap (Json.Decode.field "value" Json.Decode.string)
99        )
100        authTokenFromTuple
101
102
103authTokenFromTuple : ( String, String ) -> Result Json.Decode.Error AuthToken
104authTokenFromTuple ( t, token ) =
105    case t of
106        "Bearer" ->
107            Ok token
108
109        _ ->
110            Err <| Json.Decode.Failure "unknown token type" <| Json.Encode.string token
111
112
113
114-- CSRF token
115
116
117type alias CSRFToken =
118    String
119
120
121csrfTokenHeaderName : String
122csrfTokenHeaderName =
123    "X-Csrf-Token"
124
125
126retrieveCSRFToken : Dict String String -> Result String CSRFToken
127retrieveCSRFToken headers =
128    Dict.get (String.toLower csrfTokenHeaderName) (keysToLower headers) |> Result.fromMaybe "error CSRFToken not found"
129
130
131keysToLower : Dict String a -> Dict String a
132keysToLower =
133    Dict.fromList << List.map fstToLower << Dict.toList
134
135
136fstToLower : ( String, a ) -> ( String, a )
137fstToLower ( x, y ) =
138    ( String.toLower x, y )
139
140
141type alias AuthSession =
142    { authToken : AuthToken
143    , csrfToken : CSRFToken
144    }
145
146
147
148-- Build
149
150
151type alias BuildId =
152    Int
153
154
155type alias BuildName =
156    String
157
158
159type alias JobBuildIdentifier =
160    { teamName : TeamName
161    , pipelineName : PipelineName
162    , jobName : JobName
163    , buildName : BuildName
164    }
165
166
167type alias Build =
168    { id : BuildId
169    , name : BuildName
170    , job : Maybe JobIdentifier
171    , status : BuildStatus
172    , duration : BuildDuration
173    , reapTime : Maybe Time.Posix
174    }
175
176
177type alias BuildDuration =
178    { startedAt : Maybe Time.Posix
179    , finishedAt : Maybe Time.Posix
180    }
181
182
183encodeBuild : Build -> Json.Encode.Value
184encodeBuild build =
185    Json.Encode.object
186        ([ ( "id", build.id |> Json.Encode.int ) |> Just
187         , ( "name", build.name |> Json.Encode.string ) |> Just
188         , optionalField "team_name" Json.Encode.string (build.job |> Maybe.map .teamName)
189         , optionalField "pipeline_name" Json.Encode.string (build.job |> Maybe.map .pipelineName)
190         , optionalField "job_name" Json.Encode.string (build.job |> Maybe.map .jobName)
191         , ( "status", build.status |> Concourse.BuildStatus.encodeBuildStatus ) |> Just
192         , optionalField "start_time" (secondsFromDate >> Json.Encode.int) build.duration.startedAt
193         , optionalField "end_time" (secondsFromDate >> Json.Encode.int) build.duration.finishedAt
194         , optionalField "reap_time" (secondsFromDate >> Json.Encode.int) build.reapTime
195         ]
196            |> List.filterMap identity
197        )
198
199
200encodeMaybeBuild : Maybe Build -> Json.Encode.Value
201encodeMaybeBuild maybeBuild =
202    case maybeBuild of
203        Nothing ->
204            Json.Encode.null
205
206        Just build ->
207            encodeBuild build
208
209
210decodeBuild : Json.Decode.Decoder Build
211decodeBuild =
212    Json.Decode.succeed Build
213        |> andMap (Json.Decode.field "id" Json.Decode.int)
214        |> andMap (Json.Decode.field "name" Json.Decode.string)
215        |> andMap
216            (Json.Decode.maybe
217                (Json.Decode.succeed JobIdentifier
218                    |> andMap (Json.Decode.field "team_name" Json.Decode.string)
219                    |> andMap (Json.Decode.field "pipeline_name" Json.Decode.string)
220                    |> andMap (Json.Decode.field "job_name" Json.Decode.string)
221                )
222            )
223        |> andMap (Json.Decode.field "status" Concourse.BuildStatus.decodeBuildStatus)
224        |> andMap
225            (Json.Decode.succeed BuildDuration
226                |> andMap (Json.Decode.maybe (Json.Decode.field "start_time" (Json.Decode.map dateFromSeconds Json.Decode.int)))
227                |> andMap (Json.Decode.maybe (Json.Decode.field "end_time" (Json.Decode.map dateFromSeconds Json.Decode.int)))
228            )
229        |> andMap (Json.Decode.maybe (Json.Decode.field "reap_time" (Json.Decode.map dateFromSeconds Json.Decode.int)))
230
231
232
233-- BuildPrep
234
235
236type alias BuildPrep =
237    { pausedPipeline : BuildPrepStatus
238    , pausedJob : BuildPrepStatus
239    , maxRunningBuilds : BuildPrepStatus
240    , inputs : Dict String BuildPrepStatus
241    , inputsSatisfied : BuildPrepStatus
242    , missingInputReasons : Dict String String
243    }
244
245
246type BuildPrepStatus
247    = BuildPrepStatusUnknown
248    | BuildPrepStatusBlocking
249    | BuildPrepStatusNotBlocking
250
251
252decodeBuildPrep : Json.Decode.Decoder BuildPrep
253decodeBuildPrep =
254    Json.Decode.succeed BuildPrep
255        |> andMap (Json.Decode.field "paused_pipeline" decodeBuildPrepStatus)
256        |> andMap (Json.Decode.field "paused_job" decodeBuildPrepStatus)
257        |> andMap (Json.Decode.field "max_running_builds" decodeBuildPrepStatus)
258        |> andMap (Json.Decode.field "inputs" <| Json.Decode.dict decodeBuildPrepStatus)
259        |> andMap (Json.Decode.field "inputs_satisfied" decodeBuildPrepStatus)
260        |> andMap (defaultTo Dict.empty <| Json.Decode.field "missing_input_reasons" <| Json.Decode.dict Json.Decode.string)
261
262
263decodeBuildPrepStatus : Json.Decode.Decoder BuildPrepStatus
264decodeBuildPrepStatus =
265    customDecoder Json.Decode.string <|
266        \status ->
267            case status of
268                "unknown" ->
269                    Ok BuildPrepStatusUnknown
270
271                "blocking" ->
272                    Ok BuildPrepStatusBlocking
273
274                "not_blocking" ->
275                    Ok BuildPrepStatusNotBlocking
276
277                unknown ->
278                    Err <| Json.Decode.Failure "unknown build preparation status" <| Json.Encode.string unknown
279
280
281
282-- BuildResources
283
284
285type alias BuildResources =
286    { inputs : List BuildResourcesInput
287    , outputs : List BuildResourcesOutput
288    }
289
290
291type alias BuildResourcesInput =
292    { name : String
293    , version : Version
294    , firstOccurrence : Bool
295    }
296
297
298type alias BuildResourcesOutput =
299    { name : String
300    , version : Version
301    }
302
303
304emptyBuildResources : BuildResources
305emptyBuildResources =
306    { inputs = []
307    , outputs = []
308    }
309
310
311decodeBuildResources : Json.Decode.Decoder BuildResources
312decodeBuildResources =
313    Json.Decode.succeed BuildResources
314        |> andMap (Json.Decode.field "inputs" <| Json.Decode.list decodeResourcesInput)
315        |> andMap (Json.Decode.field "outputs" <| Json.Decode.list decodeResourcesOutput)
316
317
318decodeResourcesInput : Json.Decode.Decoder BuildResourcesInput
319decodeResourcesInput =
320    Json.Decode.succeed BuildResourcesInput
321        |> andMap (Json.Decode.field "name" Json.Decode.string)
322        |> andMap (Json.Decode.field "version" decodeVersion)
323        |> andMap (Json.Decode.field "first_occurrence" Json.Decode.bool)
324
325
326decodeResourcesOutput : Json.Decode.Decoder BuildResourcesOutput
327decodeResourcesOutput =
328    Json.Decode.succeed BuildResourcesOutput
329        |> andMap (Json.Decode.field "name" Json.Decode.string)
330        |> andMap (Json.Decode.field "version" <| Json.Decode.dict Json.Decode.string)
331
332
333
334-- BuildPlan
335
336
337type alias BuildPlan =
338    { id : String
339    , step : BuildStep
340    }
341
342
343mapBuildPlan : (BuildPlan -> a) -> BuildPlan -> List a
344mapBuildPlan fn plan =
345    fn plan
346        :: (case plan.step of
347                BuildStepTask _ ->
348                    []
349
350                BuildStepSetPipeline _ ->
351                    []
352
353                BuildStepLoadVar _ ->
354                    []
355
356                BuildStepArtifactInput _ ->
357                    []
358
359                BuildStepPut _ ->
360                    []
361
362                BuildStepGet _ _ ->
363                    []
364
365                BuildStepArtifactOutput _ ->
366                    []
367
368                BuildStepAggregate plans ->
369                    List.concatMap (mapBuildPlan fn) (Array.toList plans)
370
371                BuildStepInParallel plans ->
372                    List.concatMap (mapBuildPlan fn) (Array.toList plans)
373
374                BuildStepAcross { steps } ->
375                    List.concatMap (mapBuildPlan fn)
376                        (steps |> List.map Tuple.second)
377
378                BuildStepDo plans ->
379                    List.concatMap (mapBuildPlan fn) (Array.toList plans)
380
381                BuildStepOnSuccess { step, hook } ->
382                    mapBuildPlan fn step ++ mapBuildPlan fn hook
383
384                BuildStepOnFailure { step, hook } ->
385                    mapBuildPlan fn step ++ mapBuildPlan fn hook
386
387                BuildStepOnAbort { step, hook } ->
388                    mapBuildPlan fn step ++ mapBuildPlan fn hook
389
390                BuildStepOnError { step, hook } ->
391                    mapBuildPlan fn step ++ mapBuildPlan fn hook
392
393                BuildStepEnsure { step, hook } ->
394                    mapBuildPlan fn step ++ mapBuildPlan fn hook
395
396                BuildStepTry step ->
397                    mapBuildPlan fn step
398
399                BuildStepRetry plans ->
400                    List.concatMap (mapBuildPlan fn) (Array.toList plans)
401
402                BuildStepTimeout step ->
403                    mapBuildPlan fn step
404           )
405
406
407type alias StepName =
408    String
409
410
411type BuildStep
412    = BuildStepTask StepName
413    | BuildStepSetPipeline StepName
414    | BuildStepLoadVar StepName
415    | BuildStepArtifactInput StepName
416    | BuildStepGet StepName (Maybe Version)
417    | BuildStepArtifactOutput StepName
418    | BuildStepPut StepName
419    | BuildStepAggregate (Array BuildPlan)
420    | BuildStepInParallel (Array BuildPlan)
421    | BuildStepAcross AcrossPlan
422    | BuildStepDo (Array BuildPlan)
423    | BuildStepOnSuccess HookedPlan
424    | BuildStepOnFailure HookedPlan
425    | BuildStepOnAbort HookedPlan
426    | BuildStepOnError HookedPlan
427    | BuildStepEnsure HookedPlan
428    | BuildStepTry BuildPlan
429    | BuildStepRetry (Array BuildPlan)
430    | BuildStepTimeout BuildPlan
431
432
433type alias HookedPlan =
434    { step : BuildPlan
435    , hook : BuildPlan
436    }
437
438
439type JsonValue
440    = JsonString String
441    | JsonNumber Float
442    | JsonObject (List ( String, JsonValue ))
443    | JsonArray (List JsonValue)
444    | JsonRaw Json.Decode.Value
445
446
447decodeJsonValue : Json.Decode.Decoder JsonValue
448decodeJsonValue =
449    Json.Decode.oneOf
450        [ Json.Decode.keyValuePairs decodeSimpleJsonValue |> Json.Decode.map JsonObject
451        , Json.Decode.list decodeSimpleJsonValue |> Json.Decode.map JsonArray
452        , decodeSimpleJsonValue
453        ]
454
455
456decodeSimpleJsonValue : Json.Decode.Decoder JsonValue
457decodeSimpleJsonValue =
458    Json.Decode.oneOf
459        [ Json.Decode.string |> Json.Decode.map JsonString
460        , Json.Decode.float |> Json.Decode.map JsonNumber
461        , Json.Decode.value |> Json.Decode.map JsonRaw
462        ]
463
464
465type alias AcrossPlan =
466    { vars : List String
467    , steps : List ( List JsonValue, BuildPlan )
468    }
469
470
471decodeBuildPlan : Json.Decode.Decoder BuildPlan
472decodeBuildPlan =
473    Json.Decode.at [ "plan" ] <|
474        decodeBuildPlan_
475
476
477decodeBuildPlan_ : Json.Decode.Decoder BuildPlan
478decodeBuildPlan_ =
479    Json.Decode.succeed BuildPlan
480        |> andMap (Json.Decode.field "id" Json.Decode.string)
481        |> andMap
482            (Json.Decode.oneOf
483                -- buckle up
484                [ Json.Decode.field "task" <|
485                    lazy (\_ -> decodeBuildStepTask)
486                , Json.Decode.field "get" <|
487                    lazy (\_ -> decodeBuildStepGet)
488                , Json.Decode.field "artifact_input" <|
489                    lazy (\_ -> decodeBuildStepArtifactInput)
490                , Json.Decode.field "put" <|
491                    lazy (\_ -> decodeBuildStepPut)
492                , Json.Decode.field "artifact_output" <|
493                    lazy (\_ -> decodeBuildStepArtifactOutput)
494                , Json.Decode.field "dependent_get" <|
495                    lazy (\_ -> decodeBuildStepGet)
496                , Json.Decode.field "aggregate" <|
497                    lazy (\_ -> decodeBuildStepAggregate)
498                , Json.Decode.field "in_parallel" <|
499                    lazy (\_ -> decodeBuildStepInParallel)
500                , Json.Decode.field "do" <|
501                    lazy (\_ -> decodeBuildStepDo)
502                , Json.Decode.field "on_success" <|
503                    lazy (\_ -> decodeBuildStepOnSuccess)
504                , Json.Decode.field "on_failure" <|
505                    lazy (\_ -> decodeBuildStepOnFailure)
506                , Json.Decode.field "on_abort" <|
507                    lazy (\_ -> decodeBuildStepOnAbort)
508                , Json.Decode.field "on_error" <|
509                    lazy (\_ -> decodeBuildStepOnError)
510                , Json.Decode.field "ensure" <|
511                    lazy (\_ -> decodeBuildStepEnsure)
512                , Json.Decode.field "try" <|
513                    lazy (\_ -> decodeBuildStepTry)
514                , Json.Decode.field "retry" <|
515                    lazy (\_ -> decodeBuildStepRetry)
516                , Json.Decode.field "timeout" <|
517                    lazy (\_ -> decodeBuildStepTimeout)
518                , Json.Decode.field "set_pipeline" <|
519                    lazy (\_ -> decodeBuildSetPipeline)
520                , Json.Decode.field "load_var" <|
521                    lazy (\_ -> decodeBuildStepLoadVar)
522                , Json.Decode.field "across" <|
523                    lazy (\_ -> decodeBuildStepAcross)
524                ]
525            )
526
527
528decodeBuildStepTask : Json.Decode.Decoder BuildStep
529decodeBuildStepTask =
530    Json.Decode.succeed BuildStepTask
531        |> andMap (Json.Decode.field "name" Json.Decode.string)
532
533
534decodeBuildStepArtifactInput : Json.Decode.Decoder BuildStep
535decodeBuildStepArtifactInput =
536    Json.Decode.succeed BuildStepArtifactInput
537        |> andMap (Json.Decode.field "name" Json.Decode.string)
538
539
540decodeBuildStepGet : Json.Decode.Decoder BuildStep
541decodeBuildStepGet =
542    Json.Decode.succeed BuildStepGet
543        |> andMap (Json.Decode.field "name" Json.Decode.string)
544        |> andMap (Json.Decode.maybe <| Json.Decode.field "version" decodeVersion)
545
546
547decodeBuildStepArtifactOutput : Json.Decode.Decoder BuildStep
548decodeBuildStepArtifactOutput =
549    Json.Decode.succeed BuildStepArtifactOutput
550        |> andMap (Json.Decode.field "name" Json.Decode.string)
551
552
553decodeBuildStepPut : Json.Decode.Decoder BuildStep
554decodeBuildStepPut =
555    Json.Decode.succeed BuildStepPut
556        |> andMap (Json.Decode.field "name" Json.Decode.string)
557
558
559decodeBuildStepAggregate : Json.Decode.Decoder BuildStep
560decodeBuildStepAggregate =
561    Json.Decode.succeed BuildStepAggregate
562        |> andMap (Json.Decode.array (lazy (\_ -> decodeBuildPlan_)))
563
564
565decodeBuildStepInParallel : Json.Decode.Decoder BuildStep
566decodeBuildStepInParallel =
567    Json.Decode.succeed BuildStepInParallel
568        |> andMap (Json.Decode.field "steps" <| Json.Decode.array (lazy (\_ -> decodeBuildPlan_)))
569
570
571decodeBuildStepDo : Json.Decode.Decoder BuildStep
572decodeBuildStepDo =
573    Json.Decode.succeed BuildStepDo
574        |> andMap (Json.Decode.array (lazy (\_ -> decodeBuildPlan_)))
575
576
577decodeBuildStepOnSuccess : Json.Decode.Decoder BuildStep
578decodeBuildStepOnSuccess =
579    Json.Decode.map BuildStepOnSuccess
580        (Json.Decode.succeed HookedPlan
581            |> andMap (Json.Decode.field "step" <| lazy (\_ -> decodeBuildPlan_))
582            |> andMap (Json.Decode.field "on_success" <| lazy (\_ -> decodeBuildPlan_))
583        )
584
585
586decodeBuildStepOnFailure : Json.Decode.Decoder BuildStep
587decodeBuildStepOnFailure =
588    Json.Decode.map BuildStepOnFailure
589        (Json.Decode.succeed HookedPlan
590            |> andMap (Json.Decode.field "step" <| lazy (\_ -> decodeBuildPlan_))
591            |> andMap (Json.Decode.field "on_failure" <| lazy (\_ -> decodeBuildPlan_))
592        )
593
594
595decodeBuildStepOnAbort : Json.Decode.Decoder BuildStep
596decodeBuildStepOnAbort =
597    Json.Decode.map BuildStepOnAbort
598        (Json.Decode.succeed HookedPlan
599            |> andMap (Json.Decode.field "step" <| lazy (\_ -> decodeBuildPlan_))
600            |> andMap (Json.Decode.field "on_abort" <| lazy (\_ -> decodeBuildPlan_))
601        )
602
603
604decodeBuildStepOnError : Json.Decode.Decoder BuildStep
605decodeBuildStepOnError =
606    Json.Decode.map BuildStepOnError
607        (Json.Decode.succeed HookedPlan
608            |> andMap (Json.Decode.field "step" <| lazy (\_ -> decodeBuildPlan_))
609            |> andMap (Json.Decode.field "on_error" <| lazy (\_ -> decodeBuildPlan_))
610        )
611
612
613decodeBuildStepEnsure : Json.Decode.Decoder BuildStep
614decodeBuildStepEnsure =
615    Json.Decode.map BuildStepEnsure
616        (Json.Decode.succeed HookedPlan
617            |> andMap (Json.Decode.field "step" <| lazy (\_ -> decodeBuildPlan_))
618            |> andMap (Json.Decode.field "ensure" <| lazy (\_ -> decodeBuildPlan_))
619        )
620
621
622decodeBuildStepTry : Json.Decode.Decoder BuildStep
623decodeBuildStepTry =
624    Json.Decode.succeed BuildStepTry
625        |> andMap (Json.Decode.field "step" <| lazy (\_ -> decodeBuildPlan_))
626
627
628decodeBuildStepRetry : Json.Decode.Decoder BuildStep
629decodeBuildStepRetry =
630    Json.Decode.succeed BuildStepRetry
631        |> andMap (Json.Decode.array (lazy (\_ -> decodeBuildPlan_)))
632
633
634decodeBuildStepTimeout : Json.Decode.Decoder BuildStep
635decodeBuildStepTimeout =
636    Json.Decode.succeed BuildStepTimeout
637        |> andMap (Json.Decode.field "step" <| lazy (\_ -> decodeBuildPlan_))
638
639
640decodeBuildSetPipeline : Json.Decode.Decoder BuildStep
641decodeBuildSetPipeline =
642    Json.Decode.succeed BuildStepSetPipeline
643        |> andMap (Json.Decode.field "name" Json.Decode.string)
644
645
646decodeBuildStepLoadVar : Json.Decode.Decoder BuildStep
647decodeBuildStepLoadVar =
648    Json.Decode.succeed BuildStepLoadVar
649        |> andMap (Json.Decode.field "name" Json.Decode.string)
650
651
652decodeBuildStepAcross : Json.Decode.Decoder BuildStep
653decodeBuildStepAcross =
654    Json.Decode.map BuildStepAcross
655        (Json.Decode.succeed AcrossPlan
656            |> andMap
657                (Json.Decode.field "vars" <|
658                    Json.Decode.list <|
659                        Json.Decode.field "name" Json.Decode.string
660                )
661            |> andMap
662                (Json.Decode.field "steps" <|
663                    Json.Decode.list <|
664                        Json.Decode.map2 Tuple.pair
665                            (Json.Decode.field "values" <| Json.Decode.list decodeJsonValue)
666                            (Json.Decode.field "step" decodeBuildPlan_)
667                )
668        )
669
670
671
672-- Info
673
674
675type alias ClusterInfo =
676    { version : String
677    , clusterName : String
678    }
679
680
681decodeInfo : Json.Decode.Decoder ClusterInfo
682decodeInfo =
683    Json.Decode.succeed ClusterInfo
684        |> andMap (Json.Decode.field "version" Json.Decode.string)
685        |> andMap (defaultTo "" <| Json.Decode.field "cluster_name" Json.Decode.string)
686
687
688
689-- Job
690
691
692type alias JobName =
693    String
694
695
696type alias JobIdentifier =
697    { teamName : TeamName
698    , pipelineName : PipelineName
699    , jobName : JobName
700    }
701
702
703type alias Job =
704    { name : JobName
705    , pipelineName : PipelineName
706    , teamName : TeamName
707    , nextBuild : Maybe Build
708    , finishedBuild : Maybe Build
709    , transitionBuild : Maybe Build
710    , paused : Bool
711    , disableManualTrigger : Bool
712    , inputs : List JobInput
713    , outputs : List JobOutput
714    , groups : List String
715    }
716
717
718type alias JobInput =
719    { name : String
720    , resource : String
721    , passed : List String
722    , trigger : Bool
723    }
724
725
726type alias JobOutput =
727    { name : String
728    , resource : String
729    }
730
731
732encodeJob : Job -> Json.Encode.Value
733encodeJob job =
734    Json.Encode.object
735        [ ( "name", job.name |> Json.Encode.string )
736        , ( "pipeline_name", job.pipelineName |> Json.Encode.string )
737        , ( "team_name", job.teamName |> Json.Encode.string )
738        , ( "next_build", job.nextBuild |> encodeMaybeBuild )
739        , ( "finished_build", job.finishedBuild |> encodeMaybeBuild )
740        , ( "transition_build", job.finishedBuild |> encodeMaybeBuild )
741        , ( "paused", job.paused |> Json.Encode.bool )
742        , ( "disable_manual_trigger", job.paused |> Json.Encode.bool )
743        , ( "disable_manual_trigger", job.disableManualTrigger |> Json.Encode.bool )
744        , ( "inputs", job.inputs |> Json.Encode.list encodeJobInput )
745        , ( "outputs", job.outputs |> Json.Encode.list encodeJobOutput )
746        , ( "groups", job.groups |> Json.Encode.list Json.Encode.string )
747        ]
748
749
750decodeJob : Json.Decode.Decoder Job
751decodeJob =
752    Json.Decode.succeed Job
753        |> andMap (Json.Decode.field "name" Json.Decode.string)
754        |> andMap (Json.Decode.field "pipeline_name" Json.Decode.string)
755        |> andMap (Json.Decode.field "team_name" Json.Decode.string)
756        |> andMap (Json.Decode.maybe (Json.Decode.field "next_build" decodeBuild))
757        |> andMap (Json.Decode.maybe (Json.Decode.field "finished_build" decodeBuild))
758        |> andMap (Json.Decode.maybe (Json.Decode.field "transition_build" decodeBuild))
759        |> andMap (defaultTo False <| Json.Decode.field "paused" Json.Decode.bool)
760        |> andMap (defaultTo False <| Json.Decode.field "disable_manual_trigger" Json.Decode.bool)
761        |> andMap (defaultTo [] <| Json.Decode.field "inputs" <| Json.Decode.list decodeJobInput)
762        |> andMap (defaultTo [] <| Json.Decode.field "outputs" <| Json.Decode.list decodeJobOutput)
763        |> andMap (defaultTo [] <| Json.Decode.field "groups" <| Json.Decode.list Json.Decode.string)
764
765
766encodeJobInput : JobInput -> Json.Encode.Value
767encodeJobInput jobInput =
768    Json.Encode.object
769        [ ( "name", jobInput.name |> Json.Encode.string )
770        , ( "resource", jobInput.resource |> Json.Encode.string )
771        , ( "passed", jobInput.passed |> Json.Encode.list Json.Encode.string )
772        , ( "trigger", jobInput.trigger |> Json.Encode.bool )
773        ]
774
775
776decodeJobInput : Json.Decode.Decoder JobInput
777decodeJobInput =
778    Json.Decode.succeed JobInput
779        |> andMap (Json.Decode.field "name" Json.Decode.string)
780        |> andMap (Json.Decode.field "resource" Json.Decode.string)
781        |> andMap (defaultTo [] <| Json.Decode.field "passed" <| Json.Decode.list Json.Decode.string)
782        |> andMap (defaultTo False <| Json.Decode.field "trigger" Json.Decode.bool)
783
784
785encodeJobOutput : JobOutput -> Json.Encode.Value
786encodeJobOutput jobOutput =
787    Json.Encode.object
788        [ ( "name", jobOutput.name |> Json.Encode.string )
789        , ( "resource", jobOutput.resource |> Json.Encode.string )
790        ]
791
792
793decodeJobOutput : Json.Decode.Decoder JobOutput
794decodeJobOutput =
795    Json.Decode.succeed JobOutput
796        |> andMap (Json.Decode.field "name" Json.Decode.string)
797        |> andMap (Json.Decode.field "resource" Json.Decode.string)
798
799
800
801-- Pipeline
802
803
804type alias PipelineName =
805    String
806
807
808type alias PipelineIdentifier =
809    { teamName : TeamName
810    , pipelineName : PipelineName
811    }
812
813
814type alias Pipeline =
815    { id : Int
816    , name : PipelineName
817    , paused : Bool
818    , archived : Bool
819    , public : Bool
820    , teamName : TeamName
821    , groups : List PipelineGroup
822    , backgroundImage : Maybe String
823    }
824
825
826type alias PipelineGroup =
827    { name : String
828    , jobs : List String
829    , resources : List String
830    }
831
832
833encodePipeline : Pipeline -> Json.Encode.Value
834encodePipeline pipeline =
835    Json.Encode.object
836        [ ( "id", pipeline.id |> Json.Encode.int )
837        , ( "name", pipeline.name |> Json.Encode.string )
838        , ( "paused", pipeline.paused |> Json.Encode.bool )
839        , ( "archived", pipeline.archived |> Json.Encode.bool )
840        , ( "public", pipeline.public |> Json.Encode.bool )
841        , ( "team_name", pipeline.teamName |> Json.Encode.string )
842        , ( "groups", pipeline.groups |> Json.Encode.list encodePipelineGroup )
843        , ( "display", Json.Encode.object [ ( "background_image", pipeline.backgroundImage |> Json.Encode.Extra.maybe Json.Encode.string ) ] )
844        ]
845
846
847decodePipeline : Json.Decode.Decoder Pipeline
848decodePipeline =
849    Json.Decode.succeed Pipeline
850        |> andMap (Json.Decode.field "id" Json.Decode.int)
851        |> andMap (Json.Decode.field "name" Json.Decode.string)
852        |> andMap (Json.Decode.field "paused" Json.Decode.bool)
853        |> andMap (Json.Decode.field "archived" Json.Decode.bool)
854        |> andMap (Json.Decode.field "public" Json.Decode.bool)
855        |> andMap (Json.Decode.field "team_name" Json.Decode.string)
856        |> andMap (defaultTo [] <| Json.Decode.field "groups" (Json.Decode.list decodePipelineGroup))
857        |> andMap (Json.Decode.maybe (Json.Decode.at [ "display", "background_image" ] Json.Decode.string))
858
859
860encodePipelineGroup : PipelineGroup -> Json.Encode.Value
861encodePipelineGroup pipelineGroup =
862    Json.Encode.object
863        [ ( "name", pipelineGroup.name |> Json.Encode.string )
864        , ( "jobs", pipelineGroup.jobs |> Json.Encode.list Json.Encode.string )
865        , ( "resources", pipelineGroup.resources |> Json.Encode.list Json.Encode.string )
866        ]
867
868
869decodePipelineGroup : Json.Decode.Decoder PipelineGroup
870decodePipelineGroup =
871    Json.Decode.succeed PipelineGroup
872        |> andMap (Json.Decode.field "name" Json.Decode.string)
873        |> andMap (defaultTo [] <| Json.Decode.field "jobs" <| Json.Decode.list Json.Decode.string)
874        |> andMap (defaultTo [] <| Json.Decode.field "resources" <| Json.Decode.list Json.Decode.string)
875
876
877
878-- Resource
879
880
881type alias Resource =
882    { teamName : String
883    , pipelineName : String
884    , name : String
885    , icon : Maybe String
886    , failingToCheck : Bool
887    , checkError : String
888    , checkSetupError : String
889    , lastChecked : Maybe Time.Posix
890    , pinnedVersion : Maybe Version
891    , pinnedInConfig : Bool
892    , pinComment : Maybe String
893    }
894
895
896type alias ResourceIdentifier =
897    { teamName : String
898    , pipelineName : String
899    , resourceName : String
900    }
901
902
903type alias CheckIdentifier =
904    { teamName : String
905    , pipelineName : String
906    , resourceName : String
907    , checkID : Int
908    }
909
910
911type alias VersionedResource =
912    { id : Int
913    , version : Version
914    , metadata : Metadata
915    , enabled : Bool
916    }
917
918
919type alias VersionedResourceIdentifier =
920    { teamName : String
921    , pipelineName : String
922    , resourceName : String
923    , versionID : Int
924    }
925
926
927type alias Check =
928    { id : Int
929    , status : CheckStatus
930    , createTime : Maybe Time.Posix
931    , startTime : Maybe Time.Posix
932    , endTime : Maybe Time.Posix
933    , checkError : Maybe String
934    }
935
936
937type CheckStatus
938    = Started
939    | Succeeded
940    | Errored
941
942
943decodeResource : Json.Decode.Decoder Resource
944decodeResource =
945    Json.Decode.succeed Resource
946        |> andMap (Json.Decode.field "team_name" Json.Decode.string)
947        |> andMap (Json.Decode.field "pipeline_name" Json.Decode.string)
948        |> andMap (Json.Decode.field "name" Json.Decode.string)
949        |> andMap (Json.Decode.maybe (Json.Decode.field "icon" Json.Decode.string))
950        |> andMap (defaultTo False <| Json.Decode.field "failing_to_check" Json.Decode.bool)
951        |> andMap (defaultTo "" <| Json.Decode.field "check_error" Json.Decode.string)
952        |> andMap (defaultTo "" <| Json.Decode.field "check_setup_error" Json.Decode.string)
953        |> andMap (Json.Decode.maybe (Json.Decode.field "last_checked" (Json.Decode.map dateFromSeconds Json.Decode.int)))
954        |> andMap (Json.Decode.maybe (Json.Decode.field "pinned_version" decodeVersion))
955        |> andMap (defaultTo False <| Json.Decode.field "pinned_in_config" Json.Decode.bool)
956        |> andMap (Json.Decode.maybe (Json.Decode.field "pin_comment" Json.Decode.string))
957
958
959decodeVersionedResource : Json.Decode.Decoder VersionedResource
960decodeVersionedResource =
961    Json.Decode.succeed VersionedResource
962        |> andMap (Json.Decode.field "id" Json.Decode.int)
963        |> andMap (Json.Decode.field "version" decodeVersion)
964        |> andMap (defaultTo [] (Json.Decode.field "metadata" decodeMetadata))
965        |> andMap (Json.Decode.field "enabled" Json.Decode.bool)
966
967
968decodeCheck : Json.Decode.Decoder Check
969decodeCheck =
970    Json.Decode.succeed Check
971        |> andMap (Json.Decode.field "id" Json.Decode.int)
972        |> andMap (Json.Decode.field "status" decodeCheckStatus)
973        |> andMap (Json.Decode.maybe (Json.Decode.field "create_time" (Json.Decode.map dateFromSeconds Json.Decode.int)))
974        |> andMap (Json.Decode.maybe (Json.Decode.field "start_time" (Json.Decode.map dateFromSeconds Json.Decode.int)))
975        |> andMap (Json.Decode.maybe (Json.Decode.field "end_time" (Json.Decode.map dateFromSeconds Json.Decode.int)))
976        |> andMap (Json.Decode.maybe (Json.Decode.field "check_error" Json.Decode.string))
977
978
979decodeCheckStatus : Json.Decode.Decoder CheckStatus
980decodeCheckStatus =
981    Json.Decode.string
982        |> Json.Decode.andThen
983            (\status ->
984                case status of
985                    "started" ->
986                        Json.Decode.succeed Started
987
988                    "succeeded" ->
989                        Json.Decode.succeed Succeeded
990
991                    "errored" ->
992                        Json.Decode.succeed Errored
993
994                    unknown ->
995                        Json.Decode.fail <| "unknown check status: " ++ unknown
996            )
997
998
999
1000-- Version
1001
1002
1003type alias Version =
1004    Dict String String
1005
1006
1007decodeVersion : Json.Decode.Decoder Version
1008decodeVersion =
1009    Json.Decode.dict Json.Decode.string
1010
1011
1012
1013-- Metadata
1014
1015
1016type alias Metadata =
1017    List MetadataField
1018
1019
1020type alias MetadataField =
1021    { name : String
1022    , value : String
1023    }
1024
1025
1026decodeMetadata : Json.Decode.Decoder (List MetadataField)
1027decodeMetadata =
1028    Json.Decode.list decodeMetadataField
1029
1030
1031decodeMetadataField : Json.Decode.Decoder MetadataField
1032decodeMetadataField =
1033    Json.Decode.succeed MetadataField
1034        |> andMap (Json.Decode.field "name" Json.Decode.string)
1035        |> andMap (Json.Decode.field "value" Json.Decode.string)
1036
1037
1038
1039-- Team
1040
1041
1042type alias TeamName =
1043    String
1044
1045
1046type alias Team =
1047    { id : Int
1048    , name : TeamName
1049    }
1050
1051
1052encodeTeam : Team -> Json.Encode.Value
1053encodeTeam team =
1054    Json.Encode.object
1055        [ ( "id", team.id |> Json.Encode.int )
1056        , ( "name", team.name |> Json.Encode.string )
1057        ]
1058
1059
1060decodeTeam : Json.Decode.Decoder Team
1061decodeTeam =
1062    Json.Decode.succeed Team
1063        |> andMap (Json.Decode.field "id" Json.Decode.int)
1064        |> andMap (Json.Decode.field "name" Json.Decode.string)
1065
1066
1067
1068-- User
1069
1070
1071type alias User =
1072    { id : String
1073    , userName : String
1074    , name : String
1075    , email : String
1076    , isAdmin : Bool
1077    , teams : Dict String (List String)
1078    }
1079
1080
1081decodeUser : Json.Decode.Decoder User
1082decodeUser =
1083    Json.Decode.succeed User
1084        |> andMap (Json.Decode.field "user_id" Json.Decode.string)
1085        |> andMap (Json.Decode.field "user_name" Json.Decode.string)
1086        |> andMap (Json.Decode.field "name" Json.Decode.string)
1087        |> andMap (Json.Decode.field "email" Json.Decode.string)
1088        |> andMap (Json.Decode.field "is_admin" Json.Decode.bool)
1089        |> andMap (Json.Decode.field "teams" (Json.Decode.dict (Json.Decode.list Json.Decode.string)))
1090
1091
1092
1093-- Cause
1094
1095
1096type alias Cause =
1097    { versionedResourceID : Int
1098    , buildID : Int
1099    }
1100
1101
1102decodeCause : Json.Decode.Decoder Cause
1103decodeCause =
1104    Json.Decode.succeed Cause
1105        |> andMap (Json.Decode.field "versioned_resource_id" Json.Decode.int)
1106        |> andMap (Json.Decode.field "build_id" Json.Decode.int)
1107
1108
1109
1110-- Helpers
1111
1112
1113dateFromSeconds : Int -> Time.Posix
1114dateFromSeconds =
1115    Time.millisToPosix << (*) 1000
1116
1117
1118secondsFromDate : Time.Posix -> Int
1119secondsFromDate =
1120    Time.posixToMillis >> (\m -> m // 1000)
1121
1122
1123lazy : (() -> Json.Decode.Decoder a) -> Json.Decode.Decoder a
1124lazy thunk =
1125    customDecoder Json.Decode.value
1126        (\js -> Json.Decode.decodeValue (thunk ()) js)
1127
1128
1129defaultTo : a -> Json.Decode.Decoder a -> Json.Decode.Decoder a
1130defaultTo default =
1131    Json.Decode.map (Maybe.withDefault default) << Json.Decode.maybe
1132
1133
1134customDecoder : Json.Decode.Decoder b -> (b -> Result Json.Decode.Error a) -> Json.Decode.Decoder a
1135customDecoder decoder toResult =
1136    Json.Decode.andThen
1137        (\a ->
1138            case toResult a of
1139                Ok b ->
1140                    Json.Decode.succeed b
1141
1142                Err err ->
1143                    Json.Decode.fail <| Json.Decode.errorToString err
1144        )
1145        decoder
1146
1147
1148optionalField : String -> (a -> Json.Encode.Value) -> Maybe a -> Maybe ( String, Json.Encode.Value )
1149optionalField field encoder =
1150    Maybe.map (\val -> ( field, encoder val ))
1151