-
Notifications
You must be signed in to change notification settings - Fork 8
Backend/fix/ondc_compliance_changes #1230
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -32,21 +32,48 @@ class IsBecknAPIError e where | |||||||||||||||||||||||||||||||||||||
| toPath :: e -> Maybe Text | ||||||||||||||||||||||||||||||||||||||
| toPath _ = Nothing | ||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||
| toOndcErrorCode :: e -> Maybe Text | ||||||||||||||||||||||||||||||||||||||
| toOndcErrorCode _ = Nothing | ||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||
| newtype BecknAPIError = BecknAPIError Error.Error | ||||||||||||||||||||||||||||||||||||||
| deriving (Generic, Eq, Show) | ||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||
| -- ONDC TRV10 v2.1.0 shape: `{message: {ack: {status: "NACK"}}, error: {code, message}}`. | ||||||||||||||||||||||||||||||||||||||
| -- The error object carries only `code` and `message` — internal `type` and `path` | ||||||||||||||||||||||||||||||||||||||
| -- remain in the Haskell Error record for logs/metrics but are not emitted. Optional | ||||||||||||||||||||||||||||||||||||||
| -- `"response"` wrapping for deployments that need it is handled by WAI middleware at | ||||||||||||||||||||||||||||||||||||||
| -- the HTTP edge, not here. | ||||||||||||||||||||||||||||||||||||||
| instance FromJSON BecknAPIError where | ||||||||||||||||||||||||||||||||||||||
| parseJSON (Object v) = BecknAPIError <$> v .: "error" | ||||||||||||||||||||||||||||||||||||||
| parseJSON (Object v) = do | ||||||||||||||||||||||||||||||||||||||
| -- Accept both unwrapped (spec) and wrapped `{response: {...}}` bodies in case a peer | ||||||||||||||||||||||||||||||||||||||
| -- echoes back our wrapped output. | ||||||||||||||||||||||||||||||||||||||
| inner <- (v .: "response") <|> pure v | ||||||||||||||||||||||||||||||||||||||
| errObj <- inner .: "error" | ||||||||||||||||||||||||||||||||||||||
| errCode <- errObj .: "code" | ||||||||||||||||||||||||||||||||||||||
| errMsg <- errObj .:? "message" | ||||||||||||||||||||||||||||||||||||||
| pure $ | ||||||||||||||||||||||||||||||||||||||
| BecknAPIError | ||||||||||||||||||||||||||||||||||||||
| Error.Error | ||||||||||||||||||||||||||||||||||||||
| { Error._type = Error.INTERNAL_ERROR, | ||||||||||||||||||||||||||||||||||||||
| Error.code = errCode, | ||||||||||||||||||||||||||||||||||||||
| Error.path = Nothing, | ||||||||||||||||||||||||||||||||||||||
| Error.message = errMsg | ||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||
| parseJSON invalid = | ||||||||||||||||||||||||||||||||||||||
| prependFailure | ||||||||||||||||||||||||||||||||||||||
| "Parsing BecknAPIError failed, " | ||||||||||||||||||||||||||||||||||||||
| (typeMismatch "Object" invalid) | ||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||
| instance ToJSON BecknAPIError where | ||||||||||||||||||||||||||||||||||||||
| toJSON (BecknAPIError err) = object ["message" .= ack, "error" .= err] | ||||||||||||||||||||||||||||||||||||||
| where | ||||||||||||||||||||||||||||||||||||||
| ack = object ["ack" .= status] | ||||||||||||||||||||||||||||||||||||||
| status = object ["status" .= ("NACK" :: Text)] | ||||||||||||||||||||||||||||||||||||||
| toJSON (BecknAPIError err) = | ||||||||||||||||||||||||||||||||||||||
| object | ||||||||||||||||||||||||||||||||||||||
| [ "message" .= object ["ack" .= object ["status" .= ("NACK" :: Text)]], | ||||||||||||||||||||||||||||||||||||||
| "error" | ||||||||||||||||||||||||||||||||||||||
| .= object | ||||||||||||||||||||||||||||||||||||||
| [ "code" .= Error.code err, | ||||||||||||||||||||||||||||||||||||||
| "message" .= Error.message err | ||||||||||||||||||||||||||||||||||||||
| ] | ||||||||||||||||||||||||||||||||||||||
|
Comment on lines
+68
to
+75
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🧩 Analysis chain🏁 Script executed: # Check if file exists and read the relevant section
fd -t f "BecknAPIError.hs" | head -1Repository: nammayatri/shared-kernel Length of output: 146 🏁 Script executed: # Read the file to see the actual code and context
cat -n lib/mobility-core/src/Kernel/Types/Error/BaseError/HTTPError/BecknAPIError.hsRepository: nammayatri/shared-kernel Length of output: 3418 🏁 Script executed: # Find the Error type definition to check the message field type
rg -A 10 "data Error" lib/mobility-core/src/Kernel/Types/Error/BaseError/HTTPError/ --type hsRepository: nammayatri/shared-kernel Length of output: 50 🏁 Script executed: # Find the Error type definition
fd -t f "Error.hs" lib/mobility-core/src/Kernel/Types/Beckn/ | head -5Repository: nammayatri/shared-kernel Length of output: 118 🏁 Script executed: # Search for Error type definition in the Beckn types
rg -A 20 "^data Error\b" lib/mobility-core/src/Kernel/Types/Beckn/ --type hsRepository: nammayatri/shared-kernel Length of output: 1657 🏁 Script executed: # Verify the aeson pattern for conditional object fields
# Search for similar patterns with maybe and object construction
rg -B 3 -A 3 "maybe \[\]" lib/mobility-core/src/ --type hs | head -30Repository: nammayatri/shared-kernel Length of output: 2834 🏁 Script executed: # Check aeson imports and verify the object API in this file
rg "from Data.Aeson" lib/mobility-core/src/Kernel/Types/Error/BaseError/HTTPError/BecknAPIError.hsRepository: nammayatri/shared-kernel Length of output: 50 🌐 Web query:
💡 Result: In Haskell's aeson library, use Maybe for optional fields in data types. Aeson provides built-in FromJSON and ToJSON instances for Maybe a when a has instances. For manual FromJSON instances on records: - Use o .:? "field" :: Parser (Maybe a) for optional fields (returns Nothing if key missing or value fails to parse as a). - To handle explicit null as Nothing (especially post-aeson 0.10), use fmap join (o .:? "field") or o .:? "field" >>= parseJSON for Maybe (Maybe a), or o .:? "field" .!= Nothing. - For required fields, use o .: "field". Example: data Person = Person { name :: String, age :: Maybe Int } deriving Show instance FromJSON Person where parseJSON = withObject "Person" $ \o -> Person <$> o .: "name" <*> o .:? "age" decode "{"name":"Joe"}" :: Maybe Person yields Just (Person "Joe" Nothing). decode "{"name":"Joe","age":30}" yields Just (Person "Joe" (Just 30)). decode "{"name":"Joe","age":null}" also yields Just (Person "Joe" Nothing) due to Maybe's parseJSON handling Null. For automatic derivation (GHC.Generics): deriving (Generic) instance ToJSON Person where toJSON = genericToJSON defaultOptions { omitNothingFields = True } instance FromJSON Person where parseJSON = genericParseJSON defaultOptions { omitNothingFields = True, allowOmittedFields = True } omitNothingFields (default False) omits Nothing Maybe fields during encoding (no "field":null). allowOmittedFields (recent, default True) allows missing fields during parsing, defaulting Maybe fields to Nothing. In aeson 2.2+, supports more types like First/Last, newtypes. For Template Haskell: $(deriveJSON defaultOptions { omitNothingFields = True } ''Person) encode (Person "Joe" Nothing) yields "{"name":"Joe"}" (no age field). Citations:
Don't emit Line 74 serializes a Suggested fix toJSON (BecknAPIError err) =
object
- [ "message" .= object ["ack" .= object ["status" .= ("NACK" :: Text)]],
- "error"
- .= object
- [ "code" .= Error.code err,
- "message" .= Error.message err
- ]
- ]
+ [ "message" .= object ["ack" .= object ["status" .= ("NACK" :: Text)]],
+ "error"
+ .= object
+ ( [ "code" .= Error.code err
+ ]
+ <> maybe [] (\msg -> ["message" .= msg]) (Error.message err)
+ )
+ ]📝 Committable suggestion
Suggested change
🤖 Prompt for AI Agents |
||||||||||||||||||||||||||||||||||||||
| ] | ||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||
| instance FromResponse BecknAPIError where | ||||||||||||||||||||||||||||||||||||||
| fromResponse = fromJsonResponse | ||||||||||||||||||||||||||||||||||||||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🧩 Analysis chain
🏁 Script executed:
cat -n lib/mobility-core/src/Kernel/Types/Beckn/Ack.hs | head -150Repository: nammayatri/shared-kernel
Length of output: 5068
🏁 Script executed:
fd -type f BError.hs | head -10Repository: nammayatri/shared-kernel
Length of output: 239
🏁 Script executed:
Repository: nammayatri/shared-kernel
Length of output: 50
🏁 Script executed:
Repository: nammayatri/shared-kernel
Length of output: 120
🏁 Script executed:
cat -n ./lib/mobility-core/src/Kernel/Types/Beckn/Error.hs | head -100Repository: nammayatri/shared-kernel
Length of output: 2133
Emit optional error message only when present; don't serialize
Nothingasnull.Line 113 serializes
BError.message errdirectly, which causesNothingto become"message": nullin the JSON output. However, the FromJSON parser uses optional field parsing (.:?on line 91), and the schema declares"message"as optional (only"code"is required on line 66). For consistency, omit the field entirely when the message is absent rather than emittingnull.Suggested fix
toJSON (Nack err) = object [ "message" .= object ["ack" .= object ["status" .= ("NACK" :: Text)]], "error" - .= object - [ "code" .= BError.code err, - "message" .= BError.message err - ] + .= object + ( [ "code" .= BError.code err + ] + <> maybe [] (\msg -> ["message" .= msg]) (BError.message err) + ) ]🤖 Prompt for AI Agents