diff --git a/examples/Model.elm b/examples/Model.elm index 35412ff..37138ba 100644 --- a/examples/Model.elm +++ b/examples/Model.elm @@ -52,6 +52,7 @@ type alias Model = , currentTime : Posix , sort : Sort , maybeLimit : Maybe Int + , totalRecords : Int } @@ -65,6 +66,7 @@ type Msg -- Use by TimeAgo to display human friendly timestamps | Tick Posix -- Kinto API requests + | CountRecordsResponse (Result Kinto.Error Int) | FetchRecordResponse (Result Kinto.Error Record) | FetchRecords | FetchNextRecords @@ -97,7 +99,7 @@ type alias Flags = init : Flags -> ( Model, Cmd Msg ) init flags = ( initialModel - , Cmd.batch [ fetchRecordList initialModel ] + , fetchData initialModel ) @@ -124,6 +126,7 @@ initialModel = , currentTime = Time.millisToPosix 0 , sort = Desc "last_modified" , maybeLimit = Just 5 + , totalRecords = 0 } @@ -140,6 +143,12 @@ update msg ({ clientFormData } as model) = Tick newTime -> ( { model | currentTime = newTime }, Cmd.none ) + CountRecordsResponse (Ok totalRecords) -> + ( { model | totalRecords = totalRecords }, Cmd.none ) + + CountRecordsResponse (Err error) -> + model |> updateError error + FetchRecords -> ( { model | maybePager = @@ -151,7 +160,7 @@ update msg ({ clientFormData } as model) = Nothing , maybeError = Nothing } - , fetchRecordList model + , fetchData model ) FetchNextRecords -> @@ -169,7 +178,7 @@ update msg ({ clientFormData } as model) = | formData = recordToFormData record , maybeError = Nothing } - , Cmd.none + , countRecords model ) FetchRecordResponse (Err error) -> @@ -197,7 +206,7 @@ update msg ({ clientFormData } as model) = | maybePager = model.maybePager |> Maybe.map (addRecordToPager record) , maybeError = Nothing } - , Cmd.none + , countRecords model ) CreateRecordResponse (Err error) -> @@ -207,7 +216,7 @@ update msg ({ clientFormData } as model) = ( model, fetchRecord model.maybeClient recordId ) EditRecordResponse (Ok _) -> - ( model, fetchRecordList model ) + ( model, fetchData model ) EditRecordResponse (Err error) -> model |> updateError error @@ -226,7 +235,7 @@ update msg ({ clientFormData } as model) = Nothing , maybeError = Nothing } - , Cmd.none + , fetchData model ) DeleteRecordResponse (Err error) -> @@ -300,13 +309,13 @@ update msg ({ clientFormData } as model) = updated = { model | sort = sort, maybePager = Nothing } in - ( updated, fetchRecordList updated ) + ( updated, fetchData updated ) NewLimit newLimit -> ( { model | maybeLimit = String.toInt newLimit }, Cmd.none ) Limit -> - ( { model | maybePager = Nothing }, fetchRecordList model ) + ( { model | maybePager = Nothing }, fetchData model ) UpdateClientServer server -> ( { model | clientFormData = { clientFormData | server = server } }, Cmd.none ) @@ -328,7 +337,7 @@ update msg ({ clientFormData } as model) = newModel = { model | maybePager = Nothing, maybeClient = client } in - ( newModel, fetchRecordList newModel ) + ( newModel, fetchData newModel ) updateError : Kinto.Error -> Model -> ( Model, Cmd Msg ) @@ -449,6 +458,23 @@ fetchNextRecordList pager = Cmd.none +fetchData : Model -> Cmd Msg +fetchData model = + Cmd.batch [ fetchRecordList model, countRecords model ] + + +countRecords : Model -> Cmd Msg +countRecords { maybeClient } = + case maybeClient of + Just client -> + client + |> Kinto.count recordResource CountRecordsResponse + |> Kinto.send + + Nothing -> + Cmd.none + + fetchRecordList : Model -> Cmd Msg fetchRecordList { maybeClient, sort, maybeLimit } = let diff --git a/examples/View.elm b/examples/View.elm index 8294336..8f247bc 100644 --- a/examples/View.elm +++ b/examples/View.elm @@ -116,7 +116,7 @@ errorNotif error = view : Model -> Html.Html Msg -view { maybeError, maybeClient, maybePager, formData, clientFormData, currentTime, sort, maybeLimit } = +view { maybeError, maybeClient, maybePager, formData, clientFormData, currentTime, sort, maybeLimit, totalRecords } = let limit = maybeLimit @@ -155,7 +155,7 @@ view { maybeError, maybeClient, maybePager, formData, clientFormData, currentTim , case maybePager of Just pager -> Html.p [] - [ Html.text <| String.fromInt pager.total ++ " records in this collection." ] + [ Html.text <| String.fromInt totalRecords ++ " records in this collection." ] Nothing -> Html.text "" diff --git a/src/Kinto.elm b/src/Kinto.elm index b02be3c..9524029 100644 --- a/src/Kinto.elm +++ b/src/Kinto.elm @@ -2,7 +2,7 @@ module Kinto exposing ( Client, client, Auth(..), headersForAuth, Resource, bucketResource, collectionResource, recordResource, decodeData, encodeData, errorDecoder, errorToString , Request, withQueryParam , get, create, update, replace, delete - , getList + , getList, count , Pager, emptyPager, updatePager, loadNextPage , sort, limit, filter, Filter(..) , Endpoint(..), endpointUrl, ErrorDetail, Error(..), expectJson, expectPagerJson @@ -51,7 +51,7 @@ Item endpoints are: ## Resource list requests -@docs getList +@docs getList, count ### Paginated list @@ -244,12 +244,15 @@ encodeData encoder = {-| A stateful accumulator for a paginated list of objects. + +**Note:** as of 8.0.0, the `total` field has been removed from the record. You +must use the [`count`](#count) function to retrieve the total number of records. + -} type alias Pager a = { client : Client , objects : List a , decoder : Decode.Decoder (List a) - , total : Int , nextPage : Maybe Url } @@ -264,7 +267,6 @@ emptyPager clientInstance resource = { client = clientInstance , objects = [] , decoder = resource.listDecoder - , total = 0 , nextPage = Nothing } @@ -278,8 +280,7 @@ to the previous list. updatePager : Pager a -> Pager a -> Pager a updatePager nextPager previousPager = { previousPager - | total = nextPager.total - , nextPage = nextPager.nextPage + | nextPage = nextPager.nextPage , objects = previousPager.objects ++ nextPager.objects } @@ -448,16 +449,10 @@ expectPagerJson toMsg clientInstance decoder = nextPage = Dict.get "next-page" headers - total = - Dict.get "total-records" headers - |> Maybe.map (String.toInt >> Maybe.withDefault 0) - |> Maybe.withDefault 0 - createPager objects = { client = clientInstance , objects = objects , decoder = decoder - , total = total , nextPage = nextPage } in @@ -481,6 +476,26 @@ expectPagerJson toMsg clientInstance decoder = NetworkError anyError |> Err +{-| Extract the value of the `Total-Records` header +-} +expectTotalCount : (Result Error Int -> msg) -> Http.Expect msg +expectTotalCount toMsg = + Http.expectStringResponse toMsg <| + \response -> + case response of + Http.BadStatus_ { statusCode, statusText } body -> + Err <| extractKintoError statusCode statusText body + + Http.GoodStatus_ { headers } _ -> + Dict.get "total-records" headers + |> Maybe.andThen String.toInt + |> Maybe.withDefault 0 + |> Ok + + anyError -> + NetworkError anyError |> Err + + extractKintoError : StatusCode -> StatusMsg -> String -> Error extractKintoError statusCode statusMsg body = case Decode.decodeString errorDecoder body of @@ -702,6 +717,20 @@ get resource itemId toMsg clientInstance = |> HttpBuilder.withExpect (expectJson toMsg resource.itemDecoder) +{-| Count the number of records within a given collection + + client + |> count resource CountReceived + +-} +count : Resource a -> (Result Error Int -> msg) -> Client -> Request msg +count resource toMsg clientInstance = + endpointUrl clientInstance.baseUrl resource.listEndpoint + |> HttpBuilder.head + |> HttpBuilder.withHeaders clientInstance.headers + |> HttpBuilder.withExpect (expectTotalCount toMsg) + + {-| Create a GET request on one of the plural endpoints. As lists are always possibly paginated, When the request is succesful, a `Pager` is attached to the reponse message. @@ -715,8 +744,7 @@ getList resource toMsg clientInstance = endpointUrl clientInstance.baseUrl resource.listEndpoint |> HttpBuilder.get |> HttpBuilder.withHeaders clientInstance.headers - |> HttpBuilder.withExpect - (expectPagerJson toMsg clientInstance resource.listDecoder) + |> HttpBuilder.withExpect (expectPagerJson toMsg clientInstance resource.listDecoder) {-| If a pager has a `nextPage`, creates a GET request to retrieve the next page of objects.