github.com/pf-qiu/concourse/v6@v6.7.3-0.20201207032516-1f455d73275f/web/elm/src/Api/Pagination.elm (about) 1 module Api.Pagination exposing (params, parseLinks, parsePagination) 2 3 import Concourse.Pagination 4 exposing 5 ( Direction(..) 6 , Page 7 , Paginated 8 , Pagination 9 ) 10 import Dict 11 import Http 12 import Json.Decode 13 import List.Extra 14 import Maybe.Extra exposing (orElse) 15 import Parser 16 exposing 17 ( (|.) 18 , (|=) 19 , Parser 20 , backtrackable 21 , chompWhile 22 , getChompedString 23 , keyword 24 , map 25 , oneOf 26 , run 27 , spaces 28 , succeed 29 , symbol 30 ) 31 import String 32 import Url 33 import Url.Builder 34 import Url.Parser exposing (parse, query) 35 import Url.Parser.Query as Query 36 37 38 params : Maybe Page -> List Url.Builder.QueryParameter 39 params p = 40 case p of 41 Just { direction, limit } -> 42 (case direction of 43 From from -> 44 [ Url.Builder.int "from" from ] 45 46 To to -> 47 [ Url.Builder.int "to" to ] 48 49 ToMostRecent -> 50 [] 51 ) 52 ++ [ Url.Builder.int "limit" limit ] 53 54 Nothing -> 55 [] 56 57 58 parsePagination : 59 Json.Decode.Decoder a 60 -> Http.Response String 61 -> Result String (Paginated a) 62 parsePagination decoder response = 63 response.body 64 |> Json.Decode.decodeString (Json.Decode.list decoder) 65 |> Result.mapError Json.Decode.errorToString 66 |> Result.map 67 (\content -> 68 { content = content, pagination = parseLinks response } 69 ) 70 71 72 parseLinks : Http.Response String -> Pagination 73 parseLinks = 74 .headers 75 >> Dict.toList 76 >> List.Extra.find (Tuple.first >> String.toLower >> (==) "link") 77 >> Maybe.map Tuple.second 78 >> Maybe.andThen (run pagination >> Result.toMaybe) 79 >> Maybe.withDefault { previousPage = Nothing, nextPage = Nothing } 80 81 82 pagination : Parser Pagination 83 pagination = 84 let 85 entry rel = 86 backtrackable <| 87 succeed parsePage 88 |. symbol "<" 89 |= getChompedString (chompWhile <| (/=) '>') 90 |. symbol ">" 91 |. symbol ";" 92 |. spaces 93 |. keyword "rel" 94 |. symbol "=" 95 |. symbol "\"" 96 |. keyword rel 97 |. symbol "\"" 98 in 99 oneOf 100 [ succeed (\p n -> { previousPage = p, nextPage = n }) 101 |= entry previousRel 102 |. symbol "," 103 |. spaces 104 |= entry nextRel 105 , succeed (\n p -> { previousPage = p, nextPage = n }) 106 |= entry nextRel 107 |. symbol "," 108 |. spaces 109 |= entry previousRel 110 , succeed (\p -> { previousPage = p, nextPage = Nothing }) 111 |= entry previousRel 112 , succeed (\n -> { previousPage = Nothing, nextPage = n }) 113 |= entry nextRel 114 ] 115 116 117 previousRel : String 118 previousRel = 119 "previous" 120 121 122 nextRel : String 123 nextRel = 124 "next" 125 126 127 parsePage : String -> Maybe Page 128 parsePage url = 129 let 130 tryParam param = 131 url 132 |> Url.fromString 133 -- for some reason, the `query` function returns parsers that 134 -- only work when the path is empty. This is probably a bug: 135 -- https://github.com/elm/url/issues/17 136 |> Maybe.map (\u -> { u | path = "" }) 137 |> Maybe.andThen (parse <| query <| Query.int param) 138 |> Maybe.withDefault Nothing 139 140 tryDirection dir = 141 tryParam 142 >> Maybe.map 143 (\n -> 144 { direction = dir n 145 , limit = tryParam "limit" |> Maybe.withDefault 0 146 } 147 ) 148 in 149 tryDirection From "from" |> orElse (tryDirection To "to")