如何防止 graphql 错误并通过模式中的标量类型进行变异?

How to prevent graphql error and do mutation by scalar type in the schema?

我有标量类型“ArbitraryObject”,它是对象的自定义类型,可以获取任何键。我可以通过这种类型成功获取数据,但无法创建或更新项目我收到此错误:“字段“参数”不能有选择,因为类型“ArbitraryObject”没有子字段。”。代码:“GRAPHQL_VALIDATION_FAILED”。如何避免此错误并能够进行突变?

import * as log from "../../logger"
import { getGrpcRequestContext } from "../../clients"
import { struct } from "pb-util"
import { GraphQLScalarType } from "graphql"
import { Kind } from "graphql/language"

export const typeDefs = `
    scalar ArbitraryObject

    enum FieldTransformOperation {
        COPY
        EXTRACT_SUBSTRING
        EXTRACT_REGULAR_EXPRESSION
    }
    
    type ParametersTransform {
        preTagField: String
        substringReplacements: ArbitraryObject
        groupNamesToTagsMap: ArbitraryObject
        replacementGroupsMap: ArbitraryObject
        targetGroup: String
    }

    type FieldTransform {
        sourceKey: String
        operation: FieldTransformOperation
        parameters: ParametersTransform
    }

    type Ingest {
        name: String
        requiredKeys: [String]
        fieldTransforms: [FieldTransform]
    }  

    input PolicyByNameInput {
        name: String!
    }

    input ParametersTransformInput {
        preTagField: String
        substringReplacements: ArbitraryObject
        groupNamesToTagsMap: ArbitraryObject
        replacementGroupsMap: ArbitraryObject
        targetGroup: String
    }

    input FieldTransformInput {
        sourceKey: String
        operation: FieldTransformOperation
        parameters: ParametersTransformInput
    }
    
    input IngestInput {
        name: String
        requiredKeys: [String]
        fieldTransforms: [FieldTransformInput]
    }

    extend type Query {
        defaultIngest(input: PolicyByNameInput): Ingest
    }

    extend type Mutation {
        createIngest(input: IngestInput): Ingest
        updateIngest(input: IngestInput): Ingest
    }
`

// custom Object type for the possibility to have Object with any keys
const ObjectScalarType = () => new GraphQLScalarType({
    name: 'ArbitraryObject',
    description: 'Arbitrary object',
    parseValue: (value) => {
        try {
            return JSON.stringify(value)
        } catch (e) {
            log.contextLogger(e).error(e, "Failed to parse field transform additional data")
            return null
        }
    },
    serialize: (value) => {
            if (value === 'object' || value.length) {
                return value
            } else {
                try {
                   return  JSON.parse(value)
                } catch (e) {
                    log.contextLogger(e).error(e, "Failed to serialize field transform additional data")
                    return null
                }
            }
    },
    parseLiteral: (ast) => {
        switch (ast.kind) {
            case Kind.STRING: return JSON.parse(ast.value)
            case Kind.OBJECT: throw new Error(`failed to parse arbitrary object for ObjectScalarType`)
            default: return null
        }
    }
})

async function getDefaultIngest(name, ctx) {
    log.contextLogger(ctx).debug({ name }, "Getting default policy ingest")
    const requestContext = getGrpcRequestContext(ctx)
    try {
        return await ctx.dataSources.policySvc.GetDefaultIngest(
            name, requestContext
        )
    } catch (e) {
        log.contextLogger(ctx).warn(e, "Failed to get default policy ingest")
    }
}

async function createIngestRequest(ingest, ctx) {
    log.contextLogger(ctx).debug({ ingest }, "Creating custom policy ingest")
    const requestContext = getGrpcRequestContext(ctx)
    try {
        return await ctx.dataSources.policySvc.CreateIngest(
            { ingest }, requestContext
        )
    } catch (e) {
        log.contextLogger(ctx).warn(e, "Failed to create policy ingest")
    }
}

async function updateIngestRequest(ingest, ctx) {
    log.contextLogger(ctx).debug({ ingest }, "Updating custom policy ingest")
    const requestContext = getGrpcRequestContext(ctx)
    try {
        return await ctx.dataSources.policySvc.UpdateIngest(
            { ingest }, requestContext
        )
    } catch (e) {
        log.contextLogger(ctx).warn(e, "Failed to update policy ingest")
    }
}

// encode/decode for grcp struct "parameters"
function getFormattedIngest(data, action) {
    const ingest = data
    if(ingest.fieldTransforms && ingest.fieldTransforms.length) {
        ingest.fieldTransforms = ingest.fieldTransforms.map(field => {
            if(field.parameters) {
                const decodedParams = struct[action](field.parameters)
                return {...field, parameters: decodedParams}
            } else {
                return field
            }
        })
    }
    return ingest
}

export const resolvers = {
    Query: {
        defaultIngest: async (_, { input }, ctx) => {
            const response = await getDefaultIngest(input, ctx)
            return response ? getFormattedIngest(response.ingest, "decode") : null
        },
    Mutation: {
        createIngest: async (_, { input }, ctx) => {
            const formattedInput = getFormattedIngest(input, "encode")
            const response = await createIngestRequest(formattedInput, ctx)
            return response ? response.ingest : null
        },
        updateIngest: async (_, { input }, ctx) => {
            const formattedInput = getFormattedIngest(input, "encode")
            const response = await updateIngestRequest(formattedInput, ctx)
            return response ? response.ingest : null
        },
    },
    ArbitraryObject: () => ObjectScalarType,
}

阿波罗查询

gql`query ${scope}Ingest($input: PolicyByNameInput){
                policy: ${scope}Ingest(input: $input) {
                    name
                    requiredKeys
                    fieldTransforms {
                        sourceKey
                        operation
                        parameters {
                            preTagField
                            targetGroup
                            substringReplacements
                            groupNamesToTagsMap
                            replacementGroupsMap
                        }
                    }
            }`

我对 ScalarTypes 的了解不多,但请尝试将其直接添加到您的解析器映射中。

   export const resolvers = {
    ArbitraryObject: new GraphQLScalarType({
      name: 'ArbitraryObject',
      description: 'Arbitrary object',
      parseValue: (value) => {
        try {
            return JSON.stringify(value)
        } catch (e) {
            log.contextLogger(e).error(e, "Failed to parse field transform additional data")
            return null
        }
    },
    serialize: (value) => {
            if (value === 'object' || value.length) {
                return value
            } else {
                try {
                   return  JSON.parse(value)
                } catch (e) {
                    log.contextLogger(e).error(e, "Failed to serialize field transform additional data")
                    return null
                }
            }
    },
    parseLiteral: (ast) => {
        switch (ast.kind) {
            case Kind.STRING: return JSON.parse(ast.value)
            case Kind.OBJECT: throw new Error(`failed to parse arbitrary object for ObjectScalarType`)
            default: return null
        }
    }
    }),
    ...