Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
41 commits
Select commit Hold shift + click to select a range
b9f353d
amazonka: Generate and store idempotency tokens into requests
endgame Nov 1, 2025
238c06b
gen: Make instance FromJSON (RefF ()) explicit
endgame Nov 8, 2025
91fae37
gen: Remove redundant case
endgame Nov 9, 2025
ed3a590
amazonka-core: typo
endgame Nov 9, 2025
61a2c5d
gen: Rename some fields for clarity
endgame Nov 9, 2025
a05717e
gen: Extrace AWSRequest generation into its own module
endgame Nov 9, 2025
29152fe
gen(AWSRequest): be explicit that we consume `Metadata f`
endgame Nov 9, 2025
77b8880
gen(Metadata): Use ApplicativeDo idiom for FromJSON instance
endgame Nov 9, 2025
b1c27b6
gen: Extract request function generation
endgame Nov 10, 2025
43f7bc6
gen (AWSRequest): Push all of the request declaration into requestF
endgame Nov 10, 2025
32b70dd
gen(AWSRequest): Start converting to depend on responseType (Id)
endgame Nov 11, 2025
9707eb0
gen(AWSRequest): Unpick some more of the logic
endgame Jan 30, 2026
38e1133
gen: Small tidyups
endgame Mar 14, 2026
6510528
amazonka-core/gen: Rename `receiveBody` -> `receiveStreamingBody`
endgame Mar 14, 2026
82de6c5
amazonka-backupstorage: regenerating service
endgame Mar 20, 2026
9d496e7
amazonka-codeartifact: regenerating service
endgame Mar 20, 2026
3d3d8a3
amazonka-ebs: regenerating service
endgame Mar 20, 2026
2ad86cd
amazonka-glacier: regenerating service
endgame Mar 20, 2026
744b0b5
amazonka-kinesis-video-archived-media: regenerating service
endgame Mar 20, 2026
aa41583
amazonka-kinesis-video-media: regenerating service
endgame Mar 20, 2026
13e5655
amazonka-lakeformation: regenerating service
endgame Mar 20, 2026
538d365
amazonka-lex-runtime: regenerating service
endgame Mar 20, 2026
5aefa1d
amazonka-medialive: regenerating service
endgame Mar 20, 2026
fca8804
amazonka-mediastore-dataplane: regenerating service
endgame Mar 20, 2026
7f54b48
amazonka-omics: regenerating service
endgame Mar 20, 2026
f021ebf
amazonka-polly: regenerating service
endgame Mar 20, 2026
d9144af
amazonka-s3: regenerating service
endgame Mar 20, 2026
ba61dc6
amazonka-sagemaker-geospatial: regenerating service
endgame Mar 20, 2026
06229e3
amazonka-workmailmessageflow: regenerating service
endgame Mar 20, 2026
0ce8e07
gen: Chip `ParseStreamingBody` out from `AWSRequest`
endgame Mar 20, 2026
463aaf2
gen: simplify generation of header parsing
endgame Mar 28, 2026
6264122
gen: Define field parser for parsing response headers
endgame Mar 28, 2026
d6fabea
gen: Simplify `Location` type
endgame Mar 28, 2026
c36eec7
gen: Rename receiver function type
endgame Mar 28, 2026
47cfa66
gen: Add explicit constructor for `receiveBytes` parsing
endgame Mar 29, 2026
66b8f81
gen: Chip out parsing of fieldwise (wrapped) XML
endgame Mar 31, 2026
055e8f1
gen: Chip out check for receiveEmpty
endgame Mar 31, 2026
f255c3d
gen: Chip out fieldwise JSON parsing
endgame Apr 23, 2026
b45c03e
gen: Complete the removal of `responseF`
endgame Apr 23, 2026
5e0510b
gen: Remove dead code in `parseStatusE` and inline
endgame Apr 23, 2026
9e7315b
fixup! gen: Define field parser for parsing response headers
endgame Apr 23, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion gen/amazonka-gen.cabal
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,7 @@ library
Gen.AST.Data.Field
Gen.AST.Data.Instance
Gen.AST.Data.Syntax
Gen.AST.Data.Syntax.AWSRequest
Gen.AST.Override
Gen.AST.Prefix
Gen.AST.Subst
Expand Down Expand Up @@ -124,13 +125,13 @@ library
Gen.Types.URI
Gen.Types.Waiter
Gen.WordFrequency
Paths_amazonka_gen

executable gen
import: base
hs-source-dirs: bin
main-is: gen.hs
ghc-options: -threaded -rtsopts "-with-rtsopts=-A128m -I0 -qg0"
other-modules: Paths_amazonka_gen
build-depends:
, amazonka-gen
, filepath
Expand Down
102 changes: 100 additions & 2 deletions gen/src/Gen/AST/Data.hs
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ module Gen.AST.Data
)
where

import Control.Comonad (extract)
import qualified Control.Lens as Lens
import qualified Control.Monad.Trans.State as State
import qualified Data.ByteString.Char8 as ByteString.Char8
Expand All @@ -21,8 +22,9 @@ import qualified Data.Text.Lazy as Text.Lazy
import Gen.AST.Data.Field
import Gen.AST.Data.Instance
import Gen.AST.Data.Syntax as Syntax
import qualified Gen.AST.Data.Syntax.AWSRequest as AWSRequest
import Gen.Prelude
import Gen.Types
import Gen.Types hiding (method)
import qualified Language.Haskell.Exts as Exts
import Language.Haskell.Exts.Pretty (Pretty)

Expand All @@ -41,7 +43,103 @@ operationData cfg m o = do

xis <- addInstances xa xs <$> requestInsts m (_opName o) h xr xs

cls <- pp Print $ requestD cfg m h (xr, xis) (yr, ys)
let requestFunction = method <> format
where
method = methodToText $ _method h
format =
case (mapMaybe fromInstance xis, _method h, m ^. protocol) of
(f : _, _, _) -> f
([], POST, Query) -> "Query"
([], POST, EC2) -> "Query"
_ -> ""
fromInstance = \case
ToBody {} -> Just "Body"
ToJSON {} -> Just "JSON"
ToElement {} -> Just "XML"
_ -> Nothing

responseReceiver
| null ys = AWSRequest.ReceiveNull
| isShared . extract $ yr ^. refAnn,
all fieldBody ys = case m ^. protocol of
APIGateway -> AWSRequest.ReceiveJsonAll
JSON -> AWSRequest.ReceiveJsonAll
RestJSON -> AWSRequest.ReceiveJsonAll
EC2 -> AWSRequest.ReceiveXmlAll wrapper
Query -> AWSRequest.ReceiveXmlAll wrapper
RestXML -> AWSRequest.ReceiveXmlAll wrapper
-- FIXME: take method into account for responses, such as HEAD
-- etc, particuarly when the body might be totally empty.
| any fieldStream ys =
AWSRequest.ReceiveStreamingBody responseFieldParsers
| any fieldLitPayload ys =
AWSRequest.ReceiveBytes responseFieldParsers
| -- Check if we should parse wrapped XML before considering
-- ReceiveEmpty, because fieldBody is false for fields
-- parsed from within wrapped XML.
isXml && isJust wrapper =
AWSRequest.ReceiveXml wrapper responseFieldParsers
| -- Then check for responses that don't use the field body,
-- to avoid trying to `receiveXml` on API calls where AWS
-- might send us an empty body.
not $ any fieldBody ys =
AWSRequest.ReceiveEmpty responseFieldParsers
| -- Finally, check for unwrapped XML, parsed fieldwise.
isXml && isNothing wrapper =
AWSRequest.ReceiveXml wrapper responseFieldParsers
| isJson =
AWSRequest.ReceiveJson responseFieldParsers
| otherwise =
error "Gen.AST.Data.operationData(responseReceiver): don't know how to parse"
where
wrapper = yr ^. refResultWrapper

isJson = case m ^. protocol of
APIGateway -> True
JSON -> True
RestJSON -> True
EC2 -> False
Query -> False
RestXML -> False

isXml = case m ^. protocol of
APIGateway -> False
JSON -> False
RestJSON -> False
EC2 -> True
Query -> True
RestXML -> True

responseFieldParsers =
ys <&> \f ->
case fieldLocation f of
Just Headers -> AWSRequest.ParseHeader hName hParser
where
hName = memberName (m ^. protocol) Output f
hParser = case typeOf f of
TMap {} -> AWSRequest.HeaderFieldMap
TMaybe {} -> AWSRequest.HeaderFieldOptional
_ -> AWSRequest.HeaderFieldRequired
_ -> AWSRequest.FigureTheFieldOut f

cls <-
pp Print $
AWSRequest.instanceD
AWSRequest.Config
{ requestType = identifier xr,
-- Lookup a specific operationPlugins key before checking
-- for the wildcard.
requestOperationPlugins =
fromMaybe [] $
(cfg ^. operationPlugins . Lens.at (identifier xr))
<|> (cfg ^. operationPlugins . Lens.at (mkId "*")),
requestFunction,
responseType = identifier yr,
responseReceiver,
serviceConfig = m ^. serviceConfig
}
(m ^. metadata)
ys
mpage <- pagerFields m o >>= traverse (pp Print . pagerD xn)

yis' <- renderInsts p yn (responseInsts ys)
Expand Down
10 changes: 2 additions & 8 deletions gen/src/Gen/AST/Data/Field.hs
Original file line number Diff line number Diff line change
Expand Up @@ -50,13 +50,7 @@ instance TypeOf Field where
TList {} -> True
_ -> False

isHeader =
fieldLocation f
`elem` map
Just
[ Headers,
Header
]
isHeader = fieldLocation f == Just Headers

ref = f ^. fieldRef
typ = fmap unBase64 (typeOf ref)
Expand All @@ -71,7 +65,7 @@ instance HasInfo Field where
-- FIXME: Can just add the metadata to field as well since
-- the protocol/timestamp are passed in everywhere in the .Syntax module.
mkFields ::
HasMetadata a Identity =>
(HasMetadata a Identity) =>
a ->
Solved ->
StructF (Shape Solved) ->
Expand Down
59 changes: 30 additions & 29 deletions gen/src/Gen/AST/Data/Instance.hs
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ data Inst
| ToBody Field
| IsHashable [Field]
| IsNFData [Field]
deriving (Show)

instance ToJSON Inst where
toJSON = Aeson.toJSON . instToText
Expand Down Expand Up @@ -91,7 +92,7 @@ responseInsts fs
(not . null -> stream, _) = List.partition fieldStream (notLocated fs)

requestInsts ::
HasMetadata a f =>
(HasMetadata a f) =>
a ->
Id ->
HTTP ->
Expand Down Expand Up @@ -129,7 +130,7 @@ requestInsts m oname h r fs = do
where
merged xs =
let ys =
map Right (satisfies [Querystring] fs) <> xs
map Right (satisfies [QueryString] fs) <> xs
++ map Left protocolQuery
in case List.find f is of
Just (ToQuery zs) -> ToQuery (ys <> zs)
Expand All @@ -142,32 +143,32 @@ requestInsts m oname h r fs = do
replaceXML is
| all nonEmptyXML is = pure $! filter anyXML is
| otherwise =
case ( r ^? refXMLNamespace . Lens._Just . xmlUri,
r ^. refLocationName,
listToMaybe (mapMaybe findElement is)
) of
-- 1. If there's an xmlNamespace and/or locationName on the ref,
-- it should define separate ToXML + ToElement instances
(ns, Just e, _) ->
pure $! ToElement (ns <|> m ^. xmlNamespace) (Left e) : is
-- 2. Otherwise, a single field should be found in the ToXML instance
-- and lifted to a single ToElement instance.
(_, _, Just f) ->
pure $! ToElement ns (Right f) : filter anyXML is
where
ns =
m ^. xmlNamespace
<|> f ^? fieldRef . refXMLNamespace . Lens._Just . xmlUri

-- 3. Unknown.
(ns, e, _) ->
Left $
"String determining root ToElement instance: "
++ Text.unpack (memberId n)
++ ", namespace: "
++ show ns
++ ", locationName: "
++ show e
case ( r ^? refXMLNamespace . Lens._Just . xmlUri,
r ^. refLocationName,
listToMaybe (mapMaybe findElement is)
) of
-- 1. If there's an xmlNamespace and/or locationName on the ref,
-- it should define separate ToXML + ToElement instances
(ns, Just e, _) ->
pure $! ToElement (ns <|> m ^. xmlNamespace) (Left e) : is
-- 2. Otherwise, a single field should be found in the ToXML instance
-- and lifted to a single ToElement instance.
(_, _, Just f) ->
pure $! ToElement ns (Right f) : filter anyXML is
where
ns =
m ^. xmlNamespace
<|> f ^? fieldRef . refXMLNamespace . Lens._Just . xmlUri

-- 3. Unknown.
(ns, e, _) ->
Left $
"String determining root ToElement instance: "
++ Text.unpack (memberId n)
++ ", namespace: "
++ show ns
++ ", locationName: "
++ show e
where
nonEmptyXML = notXML True
anyXML = notXML False
Expand Down Expand Up @@ -216,7 +217,7 @@ requestInsts m oname h r fs = do
v = ("Version", Just version)

headers :: [Field]
headers = satisfies [Header, Headers] fs
headers = [f | f <- fs, fieldLocation f == Just Headers]

target = (<> ("." <> action)) <$> m ^. targetPrefix

Expand Down
Loading
Loading