-
Notifications
You must be signed in to change notification settings - Fork 55
Refactor duplicate event registration error handling #987
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
Changes from all commits
6427379
784da71
65d3697
9cbeadb
af1c34d
4dc0a64
f059333
850ccf1
f1c4073
7baf435
7e0f733
74f4d4f
bd2dd9a
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 |
|---|---|---|
| @@ -1,7 +1,7 @@ | ||
| type eventRegistration = { | ||
| handler: option<Internal.handler>, | ||
| contractRegister: option<Internal.contractRegister>, | ||
| eventOptions: option<Internal.eventOptions<Js.Json.t>>, | ||
| eventOptions: option<Internal.eventOptions<Internal.eventFilters>>, | ||
| } | ||
|
|
||
| let empty = { | ||
|
|
@@ -175,6 +175,7 @@ let getContractRegister = (~contractName, ~eventName) => | |
| let getEventFilters = (~contractName, ~eventName) => | ||
| get(~contractName, ~eventName).eventOptions | ||
| ->Belt.Option.flatMap(value => value.eventFilters) | ||
| ->(Utils.magic: option<Internal.eventFilters> => option<Js.Json.t>) | ||
|
|
||
| let isWildcard = (~contractName, ~eventName) => | ||
| get(~contractName, ~eventName).eventOptions | ||
|
|
@@ -187,26 +188,49 @@ let hasRegistration = (~contractName, ~eventName) => { | |
| } | ||
|
|
||
| type eventNamespace = {contractName: string, eventName: string} | ||
| exception DuplicateEventRegistration(eventNamespace) | ||
|
|
||
| let raiseDuplicateRegistration = (~contractName, ~eventName, ~msg, ~logger) => { | ||
| let fullMsg = msg ++ " for " ++ contractName ++ "." ++ eventName | ||
| Logging.createChildFrom(~logger, ~params={contractName, eventName})->Logging.childError(fullMsg) | ||
| Js.Exn.raiseError(fullMsg) | ||
| } | ||
|
|
||
| let eventFiltersMatch = (a: option<Internal.eventFilters>, b: option<Internal.eventFilters>) => { | ||
| switch (a, b) { | ||
| | (None, None) => true | ||
| | (Some(Static(a)), Some(Static(b))) => a == b | ||
| | (Some(Dynamic(a)), Some(Dynamic(b))) => a === b | ||
| | _ => false | ||
| } | ||
| } | ||
|
|
||
| let eventOptionsMatch = ( | ||
| existing: option<Internal.eventOptions<Internal.eventFilters>>, | ||
| incoming: option<Internal.eventOptions<Internal.eventFilters>>, | ||
| ) => { | ||
| switch (existing, incoming) { | ||
| | (None, None) => true | ||
| | (Some(a), Some(b)) => | ||
| a.wildcard === b.wildcard && eventFiltersMatch(a.eventFilters, b.eventFilters) | ||
| | _ => false | ||
| } | ||
|
Comment on lines
+207
to
+216
Contributor
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: #!/bin/bash
# Find eventOptions type definition and eventFilters references
rg -n "type eventOptions" -g '*.res' -A 5Repository: enviodev/hyperindex Length of output: 378 🏁 Script executed: #!/bin/bash
# Search for eventFilters usage and definition
rg -n "eventFilters" -g '*.res' -B 2 -A 2Repository: enviodev/hyperindex Length of output: 11204 🏁 Script executed: #!/bin/bash
# Check the Internal module to find the actual type definition
fd -e res -e resi | head -20 | xargs grep -l "eventOptions\|eventFilters" 2>/dev/nullRepository: enviodev/hyperindex Length of output: 98 Use deep equality or stable comparison for The 🤖 Prompt for AI Agents |
||
| } | ||
|
|
||
| let setEventOptions = (~contractName, ~eventName, ~eventOptions, ~logger=Logging.getLogger()) => { | ||
| switch eventOptions { | ||
| | Some(value) => | ||
| let value = | ||
| value->(Utils.magic: Internal.eventOptions<'eventFilters> => Internal.eventOptions<Js.Json.t>) | ||
| value->(Utils.magic: Internal.eventOptions<'eventFilters> => Internal.eventOptions<Internal.eventFilters>) | ||
| let t = get(~contractName, ~eventName) | ||
| switch t.eventOptions { | ||
| | None => set(~contractName, ~eventName, {...t, eventOptions: Some(value)}) | ||
| | Some(existingValue) => | ||
| if ( | ||
| existingValue.wildcard !== value.wildcard || | ||
| // TODO: Can improve the check by using deepEqual | ||
| existingValue.eventFilters !== value.eventFilters | ||
| ) { | ||
| let eventNamespace = {contractName, eventName} | ||
| DuplicateEventRegistration(eventNamespace)->ErrorHandling.mkLogAndRaise( | ||
| ~logger=Logging.createChildFrom(~logger, ~params=eventNamespace), | ||
| ~msg="Duplicate eventOptions in handlers not allowed", | ||
| if !eventOptionsMatch(Some(existingValue), Some(value)) { | ||
| raiseDuplicateRegistration( | ||
| ~contractName, | ||
| ~eventName, | ||
| ~msg="Cannot register handler with different options. Make sure all handlers for the same event use identical options (wildcard, eventFilters)", | ||
| ~logger, | ||
| ) | ||
| } | ||
| } | ||
|
|
@@ -217,46 +241,79 @@ let setEventOptions = (~contractName, ~eventName, ~eventOptions, ~logger=Logging | |
| let setHandler = (~contractName, ~eventName, handler, ~eventOptions, ~logger=Logging.getLogger()) => { | ||
| withRegistration(_registration => { | ||
| let t = get(~contractName, ~eventName) | ||
| let newHandler = handler->(Utils.magic: Internal.genericHandler<'args> => Internal.handler) | ||
| switch t.handler { | ||
| | None => | ||
| setEventOptions(~contractName, ~eventName, ~eventOptions, ~logger) | ||
| let t = get(~contractName, ~eventName) | ||
| set(~contractName, ~eventName, { | ||
| ...t, | ||
| handler: handler | ||
| ->(Utils.magic: Internal.genericHandler<'args> => Internal.handler) | ||
| ->Some, | ||
| handler: Some(newHandler), | ||
| }) | ||
| | Some(_) => | ||
| let eventNamespace = {contractName, eventName} | ||
| DuplicateEventRegistration(eventNamespace)->ErrorHandling.mkLogAndRaise( | ||
| ~logger=Logging.createChildFrom(~logger, ~params=eventNamespace), | ||
| ~msg="Duplicate registration of event handlers not allowed", | ||
| ) | ||
| | Some(prevHandler) => | ||
|
coderabbitai[bot] marked this conversation as resolved.
|
||
| let incomingEventOptions = | ||
| eventOptions->Belt.Option.map(v => | ||
| v->(Utils.magic: Internal.eventOptions<'eventFilters> => Internal.eventOptions<Internal.eventFilters>) | ||
| ) | ||
| if eventOptionsMatch(t.eventOptions, incomingEventOptions) { | ||
| let composedHandler: Internal.handler = async args => { | ||
| await prevHandler(args) | ||
| await newHandler(args) | ||
| } | ||
| set(~contractName, ~eventName, { | ||
| ...t, | ||
| handler: Some(composedHandler), | ||
| }) | ||
| } else { | ||
| raiseDuplicateRegistration( | ||
| ~contractName, | ||
| ~eventName, | ||
| ~msg="Cannot register a second handler with different options. Make sure all handlers for the same event use identical options (wildcard, eventFilters)", | ||
| ~logger, | ||
| ) | ||
| } | ||
| } | ||
|
|
||
| setEventOptions(~contractName, ~eventName, ~eventOptions, ~logger) | ||
| }) | ||
| } | ||
|
|
||
| let setContractRegister = (~contractName, ~eventName, contractRegister, ~eventOptions, ~logger=Logging.getLogger()) => { | ||
| withRegistration(_registration => { | ||
| let t = get(~contractName, ~eventName) | ||
| let newContractRegister = contractRegister->( | ||
| Utils.magic: Internal.genericContractRegister< | ||
| Internal.genericContractRegisterArgs<'event, 'context>, | ||
| > => Internal.contractRegister | ||
| ) | ||
| switch t.contractRegister { | ||
| | None => | ||
| setEventOptions(~contractName, ~eventName, ~eventOptions, ~logger) | ||
| let t = get(~contractName, ~eventName) | ||
| set(~contractName, ~eventName, { | ||
| ...t, | ||
| contractRegister: contractRegister->( | ||
| Utils.magic: Internal.genericContractRegister< | ||
| Internal.genericContractRegisterArgs<'event, 'context>, | ||
| > => Internal.contractRegister | ||
| )->Some, | ||
| contractRegister: Some(newContractRegister), | ||
| }) | ||
| | Some(_) => | ||
| let eventNamespace = {contractName, eventName} | ||
| DuplicateEventRegistration(eventNamespace)->ErrorHandling.mkLogAndRaise( | ||
| ~logger=Logging.createChildFrom(~logger, ~params=eventNamespace), | ||
| ~msg="Duplicate contractRegister handlers not allowed", | ||
| ) | ||
| | Some(prevContractRegister) => | ||
| let incomingEventOptions = | ||
| eventOptions->Belt.Option.map(v => | ||
| v->(Utils.magic: Internal.eventOptions<'eventFilters> => Internal.eventOptions<Internal.eventFilters>) | ||
| ) | ||
| if eventOptionsMatch(t.eventOptions, incomingEventOptions) { | ||
| let composedContractRegister: Internal.contractRegister = async args => { | ||
| await prevContractRegister(args) | ||
| await newContractRegister(args) | ||
| } | ||
| set(~contractName, ~eventName, { | ||
| ...t, | ||
| contractRegister: Some(composedContractRegister), | ||
| }) | ||
| } else { | ||
| raiseDuplicateRegistration( | ||
| ~contractName, | ||
| ~eventName, | ||
| ~msg="Cannot register a second contractRegister with different options. Make sure all handlers for the same event use identical options (wildcard, eventFilters)", | ||
| ~logger, | ||
| ) | ||
| } | ||
| } | ||
| setEventOptions(~contractName, ~eventName, ~eventOptions, ~logger) | ||
| }) | ||
| } | ||
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.
Use an object literal for logger params payload.
~params={contractName, eventName}is a record literal; prefer a JS object for payloads passed between functions.🛠️ Suggested change
🤖 Prompt for AI Agents