Skip to content
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

Add concept of syncing-network and use to filter actions resolvers and commands #1047

Open
wants to merge 5 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
import { Logger } from '@graphprotocol/common-ts'
import { DataTypes, QueryInterface } from 'sequelize'

interface MigrationContext {
queryInterface: QueryInterface
logger: Logger
}

interface Context {
context: MigrationContext
}

export async function up({ context }: Context): Promise<void> {
const { queryInterface, logger } = context

logger.debug(`Checking if actions table exists`)
const tables = await queryInterface.showAllTables()
if (!tables.includes('Actions')) {
logger.info(`Actions table does not exist, migration not necessary`)
return
}

logger.debug(`Checking if 'Actions' table needs to be migrated`)
const table = await queryInterface.describeTable('Actions')
const syncingNetworkColumn = table.syncingNetwork
if (syncingNetworkColumn) {
logger.info(
`'syncingNetwork' columns already exist, migration not necessary`,
)
return
}

logger.info(`Add 'syncingNetwork' column to 'Actions' table`)
await queryInterface.addColumn('Actions', 'syncingNetwork', {
type: DataTypes.BOOLEAN,
defaultValue: false,
})
}

export async function down({ context }: Context): Promise<void> {
const { queryInterface, logger } = context

return await queryInterface.sequelize.transaction({}, async transaction => {
const tables = await queryInterface.showAllTables()

if (tables.includes('Actions')) {
logger.info(`Remove 'syncingNetwork' column`)
await context.queryInterface.removeColumn('Actions', 'syncingNetwork', {
transaction,
})
}
})
}
16 changes: 14 additions & 2 deletions packages/indexer-cli/src/actions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import {
nullPassThrough,
OrderDirection,
parseBoolean,
validateNetworkIdentifier,
validateSupportedNetworkIdentifier,
} from '@graphprotocol/indexer-common'
import { validatePOI, validateRequiredParams } from './command-helpers'
import gql from 'graphql-tag'
Expand Down Expand Up @@ -47,6 +47,7 @@ export async function buildActionInput(
status,
priority,
protocolNetwork,
syncingNetwork: 'unknown',
}
case ActionType.UNALLOCATE: {
let poi = actionParams.param2
Expand All @@ -64,6 +65,7 @@ export async function buildActionInput(
status,
priority,
protocolNetwork,
syncingNetwork: 'unknown',
}
}
case ActionType.REALLOCATE: {
Expand All @@ -83,6 +85,7 @@ export async function buildActionInput(
status,
priority,
protocolNetwork,
syncingNetwork: 'unknown',
}
}
}
Expand Down Expand Up @@ -142,6 +145,8 @@ export function buildActionFilter(
status: string | undefined,
source: string | undefined,
reason: string | undefined,
protocolNetwork: string | undefined,
syncingNetwork: string | undefined,
): ActionFilter {
const filter: ActionFilter = {}
if (id) {
Expand All @@ -159,6 +164,12 @@ export function buildActionFilter(
if (reason) {
filter.reason = reason
}
if (protocolNetwork) {
filter.protocolNetwork = protocolNetwork
}
if (syncingNetwork) {
filter.syncingNetwork = syncingNetwork
}
if (Object.keys(filter).length === 0) {
throw Error(
`No action filter provided, please specify at least one filter using ['--id', '--type', '--status', '--source', '--reason']`,
Expand Down Expand Up @@ -212,7 +223,7 @@ const ACTION_PARAMS_PARSERS: Record<keyof ActionUpdateInput, (x: never) => any>
type: x => validateActionType(x),
status: x => validateActionStatus(x),
reason: nullPassThrough,
protocolNetwork: x => validateNetworkIdentifier(x),
protocolNetwork: x => validateSupportedNetworkIdentifier(x),
}

/**
Expand Down Expand Up @@ -399,6 +410,7 @@ export async function fetchActions(
) {
id
protocolNetwork
syncingNetwork
type
allocationID
deploymentID
Expand Down
41 changes: 39 additions & 2 deletions packages/indexer-cli/src/command-helpers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,10 @@ export enum OutputFormat {
import yaml from 'yaml'
import { GluegunParameters, GluegunPrint } from 'gluegun'
import { utils } from 'ethers'
import { validateNetworkIdentifier } from '@graphprotocol/indexer-common'
import {
validateNetworkIdentifier,
validateSupportedNetworkIdentifier,
} from '@graphprotocol/indexer-common'

export const fixParameters = (
parameters: GluegunParameters,
Expand Down Expand Up @@ -237,7 +240,7 @@ export function extractProtocolNetworkOption(
const input = (network ?? n) as string

try {
return validateNetworkIdentifier(input)
return validateSupportedNetworkIdentifier(input)
} catch (parseError) {
throw new Error(`Invalid value for the option '--network'. ${parseError}`)
}
Expand All @@ -251,3 +254,37 @@ export function requireProtocolNetworkOption(options: { [key: string]: any }): s
}
return protocolNetwork
}

export function extractSyncingNetworkOption(
options: {
[key: string]: any
},
required = false,
): string | undefined {
const { s, syncing } = options

// Tries to extract the --network option from Gluegun options.
// Throws if required is set to true and the option is not found.
if (!s && !syncing) {
if (required) {
throw new Error("The option '--syncing' is required")
} else {
return undefined
}
}

// Check for invalid usage
const allowedUsages =
(s === undefined && typeof syncing === 'string') ||
(syncing === undefined && typeof s === 'string')
if (!allowedUsages) {
throw new Error("Invalid usage of the option '--network'")
}
const input = (syncing ?? s) as string

try {
return validateNetworkIdentifier(input)
} catch (parseError) {
throw new Error(`Invalid value for the option '--syncing'. ${parseError}`)
}
}
24 changes: 18 additions & 6 deletions packages/indexer-cli/src/commands/indexer/actions/approve.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,18 +8,22 @@ import {
printObjectOrArray,
parseOutputFormat,
extractProtocolNetworkOption,
extractSyncingNetworkOption,
} from '../../../command-helpers'
import { approveActions, fetchActions } from '../../../actions'
import { ActionStatus, resolveChainAlias } from '@graphprotocol/indexer-common'

const HELP = `
${chalk.bold('graph indexer actions approve')} [options] [<actionID1> ...]
${chalk.bold('graph indexer actions approve')} [options] queued --network <networkName>
${chalk.bold(
'graph indexer actions approve',
)} [options] queued --network <networkName> --syncing <networkName>

${chalk.dim('Options:')}

-h, --help Show usage information
-n, --network <STRING> Filter action selection by their protocol network (mainnet, arbitrum-one, sepolia, arbitrum-sepolia)
-n, --network <STRING> Filter actions by their protocol network (mainnet, arbitrum-one, sepolia, arbitrum-sepolia)
-s, --syncing <STRING> Filter (optional) by the syncing network (see https://thegraph.com/networks/ for supported networks)
-o, --output table|json|yaml Choose the output format: table (default), JSON, or YAML
`

Expand Down Expand Up @@ -47,12 +51,17 @@ module.exports = {
return
}

const protocolNetwork = extractProtocolNetworkOption(parameters.options)
let numericActionIDs: number[]
let protocolNetwork: string | undefined = undefined
let syncingNetwork: string | undefined = undefined

const config = loadValidatedConfig()
const client = await createIndexerManagementClient({ url: config.api })
try {
protocolNetwork = extractProtocolNetworkOption(parameters.options)

syncingNetwork = extractSyncingNetworkOption(parameters.options)

if (!actionIDs || actionIDs.length === 0) {
throw Error(`Missing required argument: 'actionID'`)
}
Expand All @@ -67,6 +76,7 @@ module.exports = {
const queuedActions = await fetchActions(client, {
status: ActionStatus.QUEUED,
protocolNetwork,
syncingNetwork,
})

numericActionIDs = queuedActions.map(action => action.id)
Expand Down Expand Up @@ -101,15 +111,17 @@ module.exports = {
const queuedAction = await approveActions(client, numericActionIDs)

// Format Actions 'protocolNetwork' field to display human-friendly chain aliases instead of CAIP2-IDs
queuedAction.forEach(
action => (action.protocolNetwork = resolveChainAlias(action.protocolNetwork)),
)
queuedAction.forEach(action => {
action.protocolNetwork = resolveChainAlias(action.protocolNetwork)
action.syncingNetwork = resolveChainAlias(action.syncingNetwork)
})

actionSpinner.succeed(`Actions approved`)
printObjectOrArray(print, outputFormat, queuedAction, [
'id',
'type',
'protocolNetwork',
'syncingNetwork',
'deploymentID',
'allocationID',
'amount',
Expand Down
67 changes: 52 additions & 15 deletions packages/indexer-cli/src/commands/indexer/actions/delete.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,19 +3,29 @@ import chalk from 'chalk'

import { loadValidatedConfig } from '../../../config'
import { createIndexerManagementClient } from '../../../client'
import { fixParameters } from '../../../command-helpers'
import {
extractProtocolNetworkOption,
extractSyncingNetworkOption,
fixParameters,
} from '../../../command-helpers'
import { deleteActions, fetchActions } from '../../../actions'

const HELP = `
${chalk.bold('graph indexer actions delete')} [options] all
${chalk.bold('graph indexer actions delete')} [options] [<actionID1> ...]
${chalk.bold('graph indexer actions delete')} [options]

${chalk.dim('Options:')}

-h, --help Show usage information
-n, --network <networkName> Filter by protocol network (mainnet, arbitrum-one, sepolia, arbitrum-sepolia)
-s, --syncing <networkName> Filter by the syncing network (see https://thegraph.com/networks/ for supported networks)
--status queued|approved|pending|success|failed|canceled Filter by status
-o, --output table|json|yaml Choose the output format: table (default), JSON, or YAML
`
function isNumber(value?: string | number): boolean {
return value != null && value !== '' && !isNaN(Number(value.toString()))
}

module.exports = {
name: 'delete',
Expand All @@ -32,18 +42,37 @@ module.exports = {
const outputFormat = o || output || 'table'
const toHelp = help || h || undefined

let protocolNetwork: string | undefined = undefined
let syncingNetwork: string | undefined = undefined
let deleteType: 'ids' | 'all' | 'filter' = 'filter'

if (toHelp) {
inputSpinner.stopAndPersist({ symbol: '💁', text: HELP })
return
}

try {
protocolNetwork = extractProtocolNetworkOption(parameters.options)

syncingNetwork = extractSyncingNetworkOption(parameters.options)

if (!['json', 'yaml', 'table'].includes(outputFormat)) {
throw Error(
`Invalid output format "${outputFormat}", must be one of ['json', 'yaml', 'table']`,
)
}

if (
!status &&
!syncingNetwork &&
!protocolNetwork &&
(!actionIDs || actionIDs.length === 0)
) {
throw Error(
`Required at least one argument: actionID(s), 'all', '--status' filter, '--network' filter, or '--syncing' filter`,
)
}

if (
status &&
!['queued', 'approved', 'pending', 'success', 'failed', 'canceled'].includes(
Expand All @@ -55,18 +84,22 @@ module.exports = {
)
}

if (actionIDs[0] == 'all') {
if (status || actionIDs.length > 1) {
if (actionIDs && actionIDs[0] == 'all') {
deleteType = 'all'
if (status || protocolNetwork || syncingNetwork || actionIDs.length > 1) {
throw Error(
`Invalid query, cannot specify '--status' filter or multiple ids in addition to 'action = all'`,
`Invalid query, cannot specify '--status'|'--network'|'--syncing' filters or action ids in addition to 'action = all'`,
)
}
}

if (!status && (!actionIDs || actionIDs.length === 0)) {
throw Error(
`Required at least one argument: actionID(s), 'all', or '--status' filter`,
)
if (actionIDs && isNumber(actionIDs[0])) {
deleteType = 'ids'
if (status || protocolNetwork || syncingNetwork || actionIDs.length > 1) {
throw Error(
`Invalid query, cannot specify '--status'|'--network'|'--syncing' filters or action ids in addition to 'action = all'`,
)
}
}

inputSpinner.succeed('Processed input parameters')
Expand All @@ -81,13 +114,17 @@ module.exports = {
try {
const config = loadValidatedConfig()
const client = await createIndexerManagementClient({ url: config.api })

const numericActionIDs: number[] =
actionIDs[0] == 'all'
? (await fetchActions(client, {})).map(action => action.id)
: status
? (await fetchActions(client, { status })).map(action => action.id)
: actionIDs.map(action => +action)
let numericActionIDs: number[] = []

if (deleteType === 'all') {
numericActionIDs = (await fetchActions(client, {})).map(action => action.id)
} else if (deleteType === 'filter') {
numericActionIDs = (
await fetchActions(client, { status, protocolNetwork, syncingNetwork })
).map(action => action.id)
} else if (deleteType === 'ids') {
numericActionIDs = actionIDs.map(action => +action)
}

const numDeleted = await deleteActions(client, numericActionIDs)

Expand Down
Loading
Loading