let
Response = Web.Contents(
"https://api.exchangerate.dev",
[
RelativePath = "v1/latest/USD",
Query = [symbols = "JPY,KRW,EUR"],
ManualStatusHandling = {429}
]
),
Status = try Value.Metadata(Response)[Response.Status] otherwise 200,
Checked = if Status <> 200 then
error "The FX endpoint returned HTTP " & Text.From(Status)
else Response,
Json = Json.Document(Checked),
Rates = try Json[rates] otherwise error "The response has no rates field",
Sources = try Json[sources] otherwise error "The response has no sources field",
UpdatedAt = try Json[data_updated_at] otherwise error "The response has no data_updated_at field",
Session = try Json[market_session] otherwise error "The response has no market_session field",
RequireRate = (rates as record, code as text) as number =>
let value = try Record.Field(rates, code) otherwise null
in if value is number and value > 0 then value else error (code & " is missing or invalid"),
RequireSource = (sources as record, code as text) as text =>
let value = try Record.Field(sources, code) otherwise null
in if value is text and Text.Length(value) > 0 then value else error (code & " source is missing or invalid"),
RequiredMetadata = if UpdatedAt is text and Text.Length(UpdatedAt) > 0 and Session is text and Text.Length(Session) > 0 then true else error "Required metadata is missing or invalid",
Rows = if RequiredMetadata then Table.FromRecords({
[Currency = "JPY", Rate = RequireRate(Rates, "JPY"), Source = RequireSource(Sources, "JPY"), DataUpdatedAt = UpdatedAt, MarketSession = Session],
[Currency = "KRW", Rate = RequireRate(Rates, "KRW"), Source = RequireSource(Sources, "KRW"), DataUpdatedAt = UpdatedAt, MarketSession = Session],
[Currency = "EUR", Rate = RequireRate(Rates, "EUR"), Source = RequireSource(Sources, "EUR"), DataUpdatedAt = UpdatedAt, MarketSession = Session]
}) else error "Required metadata is missing or invalid",
Typed = Table.TransformColumnTypes(Rows, {{"Currency", type text}, {"Rate", type number}, {"Source", type text}, {"DataUpdatedAt", type datetimezone}, {"MarketSession", type text}})
in
Typed