У меня есть scalar type "ArbitraryObject" это кастомный тип для того, что бы иметь возможность пулучать любые ключи в объекте. Я успешно получаю данные на get, но не могу создать или обновить элемент, получаю такую ошибку: "Field "parameters" must not have a selection since type "ArbitraryObject" has no subfields.". code: "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,
}
Appolo query
gql`query ${scope}Ingest($input: PolicyByNameInput){
policy: ${scope}Ingest(input: $input) {
name
requiredKeys
fieldTransforms {
sourceKey
operation
parameters {
preTagField
targetGroup
substringReplacements
groupNamesToTagsMap
replacementGroupsMap
}
}
}`