biocad / openapi3

OpenAPI 3.0 data model
BSD 3-Clause "New" or "Revised" License
39 stars 53 forks source link

How to implement allOperations for optics? #85

Closed tchoutri closed 1 year ago

tchoutri commented 1 year ago

I am trying to reimplement this HasOpenApi instance with Optics instead Lens. Most of the transition is pretty smooth, the usual "replacing (.) with (%)", and using the correct overloaded labels.

So far this looks like:

-- OpenApi instance
instance (HasOpenApi api, KnownPaginationPageSize settings) =>
         HasOpenApi (PaginationParameters settings :> api) where
    toOpenApi _ = toOpenApi (Proxy @api)
        & #allOperations % #parameters <>~ [O.Inline offsetParam, O.Inline limitParam]
      where
        offsetParam :: O.Param
        limitParam :: O.Param
        offsetParam = mempty
            & #name .~ "offset"
            & #description ?~
                "Pagination parameter. How many items to skip from the beginning."
            & #required ?~ False
            & #in .~ O.ParamQuery
            & #schema ?~ O.Inline offsetParamSchema
        offsetParamSchema = mempty
            & #type ?~ O.OpenApiInteger
            & #format ?~ "int32"

        limitParam = mempty
            & #name .~ "limit"
            & #description ?~ mconcat
                [ "Pagination parameter. Maximum number of items to return.\n"
                , defaultPageSizeDesc
                ]
            & #required ?~ False
            & #in .~ O.ParamQuery
            & #schema ?~ O.Inline limitParamSchema
        limitParamSchema = mempty
            & #type ?~ O.OpenApiInteger
            & #format ?~ "int32"
            & #pattern ?~ "^\\d*[1-9]\\d*$"
        defaultPageSizeDesc :: Text
        defaultPageSizeDesc = case settingDefPageSize @settings of
          Nothing -> "By default, no limit will be applied."
          Just s  -> "Defaults to " <> Text.pack (show s) <> "."

(<>~) :: (Is k A_Setter, Semigroup b) => Optic k is s t b b -> b -> s -> t
l <>~ n = over l (<> n)
infixr 4 <>~

My last problem is the #allOperations label. It is:

allOperations :: Traversal' OpenApi Operation
allOperations = paths.traverse.template

And this Traversal is obviously a lens thing and this is where my knowledge of lens & optics falls short. How could I re-implement it with optics?

arybczak commented 1 year ago

Adding

instance
  ( a ~ Operation, b ~ Operation
  ) => LabelOptic "allOperations" A_Traversal OpenApi OpenApi a b where
  labelOptic = #paths % traversed % gplate
  {-# INLINE labelOptic #-}

to OpenApi.Optics should do it.

tchoutri commented 1 year ago

It's perfect, thank you very much!!