I have the following code:
import Control.Lens ((&), (.~), (?~), (%~))
import Data.Swagger (Swagger)
import Data.Swagger.Lens (paths, operationId, info, description)
import qualified Data.HashMap.Strict.InsOrd as InsOrdHashMap
import Data.Text (Text(..), pack)
genOpIds :: Swagger -> Swagger
genOpIds = paths %~ InsOrdHashMap.mapWithKey (\k v -> v & operationId ?~ (pack "hello"))
Generating the following compile error:
• No instance for (Data.Swagger.Lens.HasOperationId
Data.Swagger.Internal.PathItem (Maybe Text))
arising from a use of ‘operationId’
• In the first argument of ‘(?~)’, namely ‘operationId’
In the second argument of ‘(&)’, namely
‘operationId ?~ (pack "hello")’
In the expression: v & operationId ?~ (pack "hello")
|
12 | genOpIds = paths %~ InsOrdHashMap.mapWithKey (\k v -> v & operationId ?~ (pack "hello"))
| ^^^^^^^^^^^
I'm finding it a bit difficult to diagnose, as I think makeLenses is used to generate the class instances. I'm struggling a bit to see how this is different to the following (which compiles fine):
writeInfoTitle :: Swagger -> Swagger
writeInfoTitle = info.description ?~ (pack "whatever description")
as the type hierarchy involved is similar at the bottom. I suspect there's a fundamental bit of understanding I'm missing here, but I've read the lens tutorial, including the section on traversals; various bits of the servant swagger code, and some other examples and haven't managed to figure this out.
To recreate the error message easily, you could clone the following repo and stack build
:
https://github.com/msk-/improved-spork
Disclaimer : I never used Swagger.
However, looking at the documentation for it, I can see the following element in the Swagger type :
_swaggerPaths :: InsOrdHashMap FilePath PathItem
- corresponding to the paths
Lens.
Looking at the definition of PathItem
reveals that it is not an instance of HasOperationId
- it itself is composed of
PathItem
_pathItemGet :: Maybe Operation
_pathItemPut :: Maybe Operation
_pathItemPost :: Maybe Operation
_pathItemDelete :: Maybe Operation
_pathItemOptions :: Maybe Operation
_pathItemHead :: Maybe Operation
_pathItemPatch :: Maybe Operation
_pathItemParameters :: [Referenced Param]
which would seem to suggest that what you are missing a Lens composition in
(\k v -> v & (??? . operationId) ?~ (pack "hello"))
; something similar to
(\k v -> v & (itemGet . operationId) ?~ (pack "hello"))
EDIT :
It looks like what you are trying to achieve (setting up a simple path operation) can be easily done by using the Monoid
instance of the PathItem
record, like this:
genOpsId = paths %~ InsOrdHashMap.mapWithKey (\k v -> v & get ?~ (mempty & operationId ?~ (pack "hello")))
EDIT#2:
As requested, here is one way to iterate over the fields of the PathItem
record and set the operationId of those that are present.
For the record, I'm pretty sure there's a way, way, way cleaner way to do this, but here goes.
dokey :: PathItem -> PathItem
dokey v = foldl
(\acc nv -> acc & nv %~ (fmap $ operationId ?~ (pack "hello")))
v
[get, put, post, delete, options, head_, patch]
genOpIds :: Swagger -> Swagger
genOpIds = paths %~ InsOrdHashMap.mapWithKey (\k -> doKey)
Hope it helps! :)