-
Notifications
You must be signed in to change notification settings - Fork 0
Interactive discovery #123
base: main
Are you sure you want to change the base?
Changes from all commits
084d897
730b600
3b6cc3e
0fa6507
30d3cf2
8b18dad
1bcec31
588da41
6eb89b6
0a2fef0
626753b
8dbfdbe
d9ebf15
ad9dd09
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,5 +1,6 @@ | ||
import { assert } from '@l2beat/backend-tools' | ||
import { DiscoveryOutput } from '@l2beat/discovery-types' | ||
import { parse as parseWithComments } from 'comment-json' | ||
import { readdirSync } from 'fs' | ||
import { readFile } from 'fs/promises' | ||
import { parse, ParseError } from 'jsonc-parser' | ||
|
@@ -119,4 +120,37 @@ export class ConfigReader { | |
|
||
return projects | ||
} | ||
|
||
async readRawConfigWithComments( | ||
name: string, | ||
chain: string, | ||
): Promise<RawDiscoveryConfig> { | ||
assert( | ||
fileExistsCaseSensitive(`discovery/${name}`), | ||
'Project not found, check if case matches', | ||
) | ||
assert( | ||
fileExistsCaseSensitive(`discovery/${name}/${chain}`), | ||
'Chain not found in project, check if case matches', | ||
) | ||
|
||
const contents = await readFile( | ||
`discovery/${name}/${chain}/config.jsonc`, | ||
'utf-8', | ||
) | ||
const parsed: unknown = parseWithComments(contents) | ||
|
||
// Parsing via Zod would effectively remove symbols and thus comments | ||
assertDiscoveryConfig(parsed) | ||
|
||
assert(parsed.chain === chain, 'Chain mismatch in config.jsonc') | ||
|
||
return parsed | ||
} | ||
} | ||
Comment on lines
+123
to
+150
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. There is duplication of reading the config in here now. We should extract the common part, just call |
||
|
||
function assertDiscoveryConfig( | ||
config: unknown, | ||
): asserts config is RawDiscoveryConfig { | ||
RawDiscoveryConfig.parse(config) | ||
} |
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. Can be easily tested |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,153 @@ | ||
import { ContractParameters, DiscoveryOutput } from '@l2beat/discovery-types' | ||
import { assign, parse, stringify } from 'comment-json' | ||
import * as fs from 'fs/promises' | ||
|
||
import { ContractOverrides } from './DiscoveryOverrides' | ||
import { | ||
MutableDiscoveryOverrides, | ||
MutableOverride, | ||
} from './MutableDiscoveryOverrides' | ||
import { RawDiscoveryConfig } from './RawDiscoveryConfig' | ||
|
||
export class DiscoveryOverridesBuilder { | ||
private readonly mutableOverrides: MutableDiscoveryOverrides | ||
|
||
constructor( | ||
private readonly output: DiscoveryOutput, | ||
private readonly rawConfigWithComments: RawDiscoveryConfig, | ||
) { | ||
this.mutableOverrides = new MutableDiscoveryOverrides( | ||
this.rawConfigWithComments, | ||
) | ||
} | ||
|
||
getContracts(): ContractParameters[] { | ||
return [...this.output.contracts] | ||
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. Why do we need to spread it and later create a new array if the |
||
} | ||
|
||
getWatchMode(contract: ContractParameters): string[] { | ||
const isDiscoveryIgnored = this.getIgnoreDiscovery(contract) | ||
|
||
if (isDiscoveryIgnored) { | ||
return [] | ||
} | ||
|
||
const overrides = this.getSafeOverride(contract) | ||
|
||
const ignoredInWatchMode = overrides?.ignoreInWatchMode ?? [] | ||
|
||
return ignoredInWatchMode | ||
} | ||
|
||
getIgnoredRelatives(contract: ContractParameters): string[] { | ||
const overrides = this.getSafeOverride(contract) | ||
|
||
const ignoredRelatives = overrides?.ignoreRelatives ?? [] | ||
|
||
return ignoredRelatives | ||
} | ||
|
||
getIgnoredMethods(contract: ContractParameters): string[] { | ||
const overrides = this.getSafeOverride(contract) | ||
const ignoredMethods = overrides?.ignoreMethods ?? [] | ||
|
||
return ignoredMethods | ||
} | ||
|
||
getIgnoreDiscovery(contract: ContractParameters): boolean { | ||
const overrides = this.getSafeOverride(contract) | ||
|
||
return overrides?.ignoreDiscovery ?? false | ||
} | ||
|
||
setOverride(contract: ContractParameters, override: MutableOverride): void { | ||
// Optimistically set overrides | ||
this.mutableOverrides.set(contract, override) | ||
|
||
const isDiscoveryIgnored = this.getIgnoreDiscovery(contract) | ||
const ignoredInWatchMode = this.getWatchMode(contract) | ||
const ignoredMethods = this.getIgnoredMethods(contract) | ||
const ignoredRelatives = this.getIgnoredRelatives(contract) | ||
|
||
// Wipe all overrides if discovery is ignored | ||
if (isDiscoveryIgnored) { | ||
this.mutableOverrides.set(contract, { | ||
ignoreDiscovery: true, | ||
ignoreInWatchMode: [], | ||
ignoreMethods: [], | ||
ignoreRelatives: [], | ||
}) | ||
return | ||
} | ||
|
||
// Exclude ignoreMethods from watch mode and relatives completely | ||
const validWatchMode = ignoredInWatchMode.filter( | ||
(method) => !ignoredMethods.includes(method), | ||
) | ||
|
||
const validRelatives = ignoredRelatives.filter( | ||
(method) => !ignoredMethods.includes(method), | ||
) | ||
|
||
this.mutableOverrides.set(contract, { | ||
ignoreInWatchMode: validWatchMode, | ||
ignoreRelatives: validRelatives, | ||
}) | ||
} | ||
|
||
/** | ||
* Do not replace whole file, just read most-recent raw, replace and save overrides | ||
*/ | ||
async flushOverrides(): Promise<void> { | ||
const path = `discovery/${this.output.name}/${this.output.chain}/config.jsonc` | ||
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. Why are we not using |
||
|
||
const fileContents = await fs.readFile(path, 'utf8') | ||
|
||
const parsed = parse(fileContents) as RawDiscoveryConfig | null | ||
|
||
if (this.mutableOverrides.config.overrides) { | ||
assign(parsed, { overrides: this.mutableOverrides.config.overrides }) | ||
} | ||
|
||
if (this.mutableOverrides.config.names) { | ||
assign(parsed, { names: this.mutableOverrides.config.names }) | ||
} | ||
|
||
await fs.writeFile(path, stringify(parsed, null, 2)) | ||
} | ||
|
||
private getOverrideIdentity(contract: ContractParameters): string { | ||
const hasName = Boolean( | ||
this.mutableOverrides.config.names?.[contract.address.toString()], | ||
) | ||
|
||
if (hasName) { | ||
return contract.name | ||
} | ||
|
||
return contract.address.toString() | ||
} | ||
|
||
private getSafeOverride( | ||
contract: ContractParameters, | ||
): ContractOverrides | null { | ||
const addressOrName = this.getOverrideIdentity(contract) | ||
|
||
try { | ||
return this.mutableOverrides.get(addressOrName) | ||
} catch { | ||
return null | ||
} | ||
} | ||
|
||
public isCustomHandler( | ||
contract: ContractParameters, | ||
property: string, | ||
): boolean { | ||
const addressOrName = this.getOverrideIdentity(contract) | ||
|
||
return Object.keys( | ||
this.mutableOverrides.get(addressOrName).fields ?? {}, | ||
).includes(property) | ||
} | ||
} |
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.
We already import
jsonc-parser
, either don't usecomment-json
or replacejsonc-parser
with it