Denim Demon
Denim Demon

Reputation: 734

Elm: Iterate list performing multiple HTTP requests

I'm wondering if I can get some help with iterating a list of groups, making a POST request for each group to create a 'room', iterating the users for each group and making a POST request to assign them to this specific room.

I have the following model.

model = {
  groups = [
    {
      title = "Foo"
      , users = [
        { name = "Joe" }
        , { name = "Mary" }
      ]
    },
    {
      title = "Bar"
      , users = [
        { name = "Jill" }
        , { name = "Jack" }
      ]
    }
  ]
}

The desired result is that the room Foo was created and Joe and Mary were assigned, and Bar was created and Jill and Jack were assigned.

The view, for now, would just be a simple button that triggers an action.

div []
  [ button [ onClick InviteUsersToRoom ] [ text "Invite users to room" ] ]

I've created 2 POST requests:

  1. createRoom: take a title, create a room using the title and return the room_id

  2. addUser: take a room_id and a user's name, add the the users to the room and return the status of ok

example:

-- create a room for each group
-- passing in `title` as the room name
-- which will return the room id from `decodeCreateRoomResponse`

createRoom : String -> String -> Cmd Msg
createRoom title =
  Task.perform
    CreateRoomsFail
    CreateRoomsSuccess
    (Http.post
      decodeCreateRoomResponse
      ("https://some_api?room=" ++ title)
      Http.empty
    )


decodeCreateRoomResponse : Json.Decoder String
decodeCreateRoomResponse =
  Json.at ["room", "id"] Json.string


-- add a user to a room using a `room_id` and the user's name
-- returns a bool from `decodeAddUserResponse`

addUser : String -> String -> Cmd Msg
addUser room_id user =
  Task.perform
    AddUserFail
    AddUserSuccess
    (Http.post
      decodeCreateChannelResponse
      ("https://some_api?room=" ++ room_id ++ "&user=" ++ user)
      Http.empty
    )


decodeAddUserResponse : Json.Decoder String
decodeAddUserResponse =
  Json.at ["ok"] Json.bool

I'm wondering how you'd go about stitching this up altogether, so that onclick :

  1. iterate each group
  2. make the POST to create the Room
  3. take the room_id from the response and iterate the users
  4. POST the room_id and the users name

Any help is appreciated.

Upvotes: 1

Views: 430

Answers (1)

Chad Gilbert
Chad Gilbert

Reputation: 36375

You've got a few scattered errors that I won't explicitly point out because the compiler will help you, but you're off to a good start. You already have some Http handling Cmds built up so you just need to wire things up with your update function.

Let's define your Model explicitly (you may already be doing this but it isn't in your example):

type alias User =
  { name : String }

type alias Group =
  { title : String
  , users : List User
  }

type alias Model =
  { groups : List Group }

Based off your functions, here's how I interpret your Msg type, with one small change which is to add a list of users as a parameter to CreateRoomsSuccess.

type Msg
  = InviteUsersToRoom
  | CreateRoomsFail Http.Error
  | CreateRoomsSuccess (List User) String
  | AddUserFail Http.Error
  | AddUserSuccess Bool

Now we can tweak createRoom in order to pass along the list of users to create. Note that this isn't creating any users at this time. It is using currying to create a partially-applied function so that when the CreateRoomsSuccess case is handled in the update function, it already has the list of users that need to be created (rather than having to look them up in the model list):

createRoom : Group -> Cmd Msg
createRoom group =
  Task.perform
    CreateRoomsFail
    (CreateRoomsSuccess group.users)
    (Http.post
      decodeCreateRoomResponse
      ("https://some_api?room=" ++ group.title)
      Http.empty
    )

To create the list of rooms, you simply map the list of groups to a list of Cmds that perform the post. This will happen when the button is clicked:

case action of
  InviteUsersToRoom ->
    model ! List.map createRoom model.groups
    ...

You'll have to implement the update cases for when errors occur. Next up, we have to handle the CreateRoomsSuccess message. This is where you'll need to look up the list of users for a group. Again, you'll map over the function you already created that handles the Http task:

case action of
  ...
  CreateRoomsSuccess users roomID ->
    model ! List.map (addUser roomID << .name) users
  ...

You'll have to handle the AddUserFail and AddUserSuccess cases, but the above examples should help you understand how to post multiple messages and act accordingly based on the success or failure of each.

Upvotes: 1

Related Questions