I've started with Elm and want to get images from Trefle API when searched for plant but I keep getting the "Could not fetch image" error. I do use my own access token but I replaced it here with "MY_API_TOKEN" to keep it private. Can you help me to understand the problem in my code?
I've tried to get a more detailed error message but then my code crashed because I am quite new with using elm and in the web development field.
module Main exposing (..)
import Browser
import Html exposing (Html, button, div, img, input, text)
import Html.Attributes exposing (placeholder, src, type_)
import Html.Events exposing (onClick, onInput)
import Http
import Json.Decode exposing (Decoder, field, list, string)
import Maybe exposing (withDefault, map)
type alias Model =
{ searchTerm : String
, imageUrl : String
, error : String
}
init : () -> ( Model, Cmd Msg )
init _ =
( { searchTerm = "", imageUrl = "", error = "" }, Cmd.none )
type Msg
= UpdateSearchTerm String
| FetchImage
| ReceiveImage (Result Http.Error String)
update : Msg -> Model -> ( Model, Cmd Msg )
update msg model =
case msg of
UpdateSearchTerm term ->
( { model | searchTerm = term }, Cmd.none )
FetchImage ->
( model, fetchPlantImage model.searchTerm )
ReceiveImage result ->
case result of
Ok url ->
( { model | imageUrl = url, error = "" }, Cmd.none )
Err _ ->
( { model | error = "Failed to fetch image." }, Cmd.none )
view : Model -> Html Msg
view model =
div []
[ input [ placeholder "Enter plant name", onInput UpdateSearchTerm ] []
, button [ onClick FetchImage ] [ text "Search" ]
, if model.imageUrl /= "" then
img [ src model.imageUrl ] []
else
text ""
, if model.error /= "" then
div [] [ text model.error ]
else
text ""
]
fetchPlantImage : String -> Cmd Msg
fetchPlantImage searchTerm =
let
url =
"https://trefle.io/api/v1/plants/search?token=MY_API_TOKEN&q=" ++ searchTerm
decodeImageUrl : Decoder String
decodeImageUrl =
field "data" (list (field "image_url" string))
|> Json.Decode.map (withDefault "" << map identity << List.head)
in
Http.get
{ url = url
, expect = Http.expectJson ReceiveImage decodeImageUrl
}
main : Program () Model Msg
main =
Browser.element
{ init = init
, update = update
, subscriptions = \_ -> Sub.none
, view = view
}
It appears trefle.io's SSL certificate has expired, so it's likely a problem on their end.
The first thing I did was update your HTTP response handler to be more explicit when reporting the error (instead of just "Failed to fetch image.")
...
update : Msg -> Model -> ( Model, Cmd Msg )
update msg model =
case msg of
...
ReceiveImage result ->
case result of
Ok url ->
( { model | imageUrl = url, error = "" }, Cmd.none )
Err err ->
( { model
| error =
case err of
Http.BadUrl errorMessage ->
errorMessage
Http.Timeout ->
"Timeout"
Http.NetworkError ->
"Network error"
Http.BadStatus status ->
"Bad status " ++ String.fromInt status
Http.BadBody errorMessage ->
errorMessage
}
, Cmd.none
)
...
Ellie: https://ellie-app.com/rqZy7NHmGrGa1
Once you do this, you'll see that the actual error is:
Network error
Implying the service is inaccessible.
Next I just attempted to access that URL using curl
, which reports an error with the server's SSL certificat:
% curl "https://trefle.io/api/v1/plants/search?token=MY_API_TOKEN&q=coconut"
curl: (60) SSL certificate problem: certificate has expired
More details here: https://curl.haxx.se/docs/sslcerts.html
curl performs SSL certificate verification by default, using a "bundle"
of Certificate Authority (CA) public keys (CA certs). If the default
bundle file isn't adequate, you can specify an alternate file
using the --cacert option.
If this HTTPS server uses a certificate signed by a CA represented in
the bundle, the certificate verification probably failed due to a
problem with the certificate (it might be expired, or the name might
not match the domain name in the URL).
If you'd like to turn off curl's verification of the certificate, use
the -k (or --insecure) option.
HTTPS-proxy has similar options --proxy-cacert and --proxy-insecure.