当前位置: 首页 > 知识库问答 >
问题:

GraphQL SPQR扩展输入对象的变异参数

诸葛令
2023-03-14

能够扩展现有的类型是非常棒的,因为它允许代码模块化和权限分离。关于如何在查询中扩展对象输出,我已经找到了很好的例子(见下文),但没有很好的方法来扩展给定对象的输入。

为了示例起见,假设我们有一个类User

class User {
    String firstName;
    String lastName;
}

如果我们声明一个bean,我们可以进行如下查询:

/**
 * This is valid and can be invoked using
 * query {
 *     user(id=1) {
 *         firstName
 *         lastName
 *     }
 * }
 */
@GraphQLQuery(name = "user")
public User getUser(@GraphQLArgument(name = "id") long id) {

}

然后在另一个bean bean中,我们可以扩展用户

    /**
     * <<this currently works>>
     * So now this query becomes valid
     * query {
     *     user(id=1) {
     *        firstName
     *        lastName
     *        address {    <-- this is not a top level, but extends User
     *            streetNam
     *        }
     *     }
     * }
     */
    @GraphQLQuery(name = "address")
    public Address getUserAddress(@GraphQLContext User) {

    }

同样对于突变,我们可以定义:

    /**
     * <<this currently works>>
     * This can be invoked using:
     * mutation {
     *     addUser(user :{
     *         firstName: "John"
     *         lastName: "Smith"
     *      })
     *      fistName
     * }
     */
     @GraphQLMutation(name = "addUser")
     public User addUser(@GraphQLArgument(name = "user") User user) {

     }

现在,我尝试添加地址,方法与我们为查询添加地址的方式相同,但添加的参数必须是用户的输入参数。以下仍然在一些bean中声明

    /**
     * << this is what I am trying to achieve>>
     * I want to be able to invoke the following query and not having to declare 'Address' inside of 'User' class.
     * mutation {
     *  addUser(user :{
     *      firstName: "John"
     *      lastName: "Smith"
     *      address: {    <-- being able to pass address as argument now, and be part of user.
     *          streetName: "1 str"
     *      }
     *      })
     *        fistName
     *  }
     */
    // e.g. something like ...
    @GraphQLInputField(name = "address")
    public void addAddressToUser(@GraphQLContext User user, @GraphQLArgument Address address) {

    }

共有1个答案

汲昊空
2023-03-14


我使用GraphQL-SPQR 0.9.8(我将在几天内发布)。您可以在0.9.7中实现相同的功能,只是不太符合人体工程学。

@Test
public void testSchema() {
    GraphQLSchema schema = new GraphQLSchemaGenerator()
            // Ignore extra fields (like "address") when deserializing
            .withValueMapperFactory(JacksonValueMapperFactory.builder()
                    .withConfigurers(conf -> conf.getObjectMapper().configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false))
                    .build())
            .withInputFieldBuilders((conf, defaults) -> defaults.prepend(new UserInputBuilder(defaults.getFirstOfType(JacksonValueMapper.class))))
            .withArgumentInjectors(new UserInjector())
            .withOperationsFromSingleton(new TestService())
            .generate();

    GraphQL exe = GraphQL.newGraphQL(schema).build();
    ExecutionResult result = exe.execute("{user(in: {name: \"A. Man\", address: {type: \"home\", street: {name: \"Fakestreet\", number: 123}}}) {name, street {number}}}");
}


public class TestService {

    @GraphQLQuery //or mutation, no difference
    public User user(User in) {
        return in;
    }
}

// Redefines how User objects are deserizalized
public static class UserInjector extends InputValueDeserializer {

    @Override
    public Object getArgumentValue(ArgumentInjectorParams params) {
        User user = (User) super.getArgumentValue(params);
        Map<?, ?> rawInput = (Map<?, ?>) params.getInput();
        Address address = params.getResolutionEnvironment().valueMapper.fromInput(rawInput.get("address"), GenericTypeReflector.annotate(Address.class));
        // Preprocess the address in any way you need, here I just extract the street
        user.setStreet(address.getStreet());
        return user;
    }

    @Override
    public boolean supports(AnnotatedType type) {
        return GenericTypeReflector.isSuperType(User.class, type.getType());
    }
}

//Redefines the way User input type is mapped
public static class UserInputBuilder implements InputFieldBuilder {

    private final InputFieldBuilder original;

    public UserInputBuilder(InputFieldBuilder original) {
        this.original = original;
    }

    @Override
    public Set<InputField> getInputFields(InputFieldBuilderParams params) {
        Set<InputField> fields = original.getInputFields(params);
        // Add the extra "address" field you want
        fields.add(new InputField("address", "User's home address", GenericTypeReflector.annotate(Address.class), null, null));
        return fields;
    }

    @Override
    public boolean supports(AnnotatedType type) {
        return GenericTypeReflector.isSuperType(User.class, type.getType());
    }
}

public class User {

    private String name;
    private Street street;

    public User(String name, Street street) {
        this.name = name;
        this.street = street;
    }

    public String getName() {
        return name;
    }

    public Street getStreet() {
        return street;
    }

    @GraphQLIgnore //can be filtered out in a different way, without touching this class
    public void setStreet(Street street) {
        this.street = street;
    }
}

public class Address {

    private Street street;
    private String type;

    public Address(Street street, String type) {
        this.street = street;
        this.type = type;
    }

    public Street getStreet() {
        return street;
    }

    public String getType() {
        return type;
    }
}

也就是说,在Jackson(或Gson,无论您使用什么)中注册一个自定义反序列化程序并跳过自定义的ArgumentInjector可能会更容易。

 类似资料:
  • 属性的简洁表示法 ES6允许直接写入变量和函数,作为对象的属性和方法。这样的书写更加简洁。 var foo = 'bar'; var baz = {foo}; baz // {foo: "bar"} // 等同于 var baz = {foo: foo}; 上面代码表明,ES6允许在对象之中,只写属性名,不写属性值。这时,属性值等于属性名所代表的变量。下面是另一个例子。 function f(

  • 本文向大家介绍详解JS中Array对象扩展与String对象扩展,包括了详解JS中Array对象扩展与String对象扩展的使用技巧和注意事项,需要的朋友参考一下 废话不多说了,直接给大家上array对象扩展代码了,具体代码如下所示: 下面是string对象扩展代码如下所示:

  • 问题 你想要扩展一个类来增加新的函数或者替换旧的。 解决方案 使用 :: 把你的新函数分配到对象或者类的原型中。 String::capitalize = () -> (this.split(/\s+/).map (word) -> word[0].toUpperCase() + word[1..-1].toLowerCase()).join ' ' "foo bar baz".ca

  • name String version String

  • 问题内容: 我想知道是否有可能扩展Angular的输入指令?我想将一些侦听器附加到页面上的所有输入字段。我认为您可以使用来装饰现有模块,但我不知道如何使用指令(更确切地说是输入指令)来完成此操作。 那么,谁能将我推向正确的方向呢?一些例子? 编辑 到目前为止,这是我的指令: 在我看来,可以将其添加到输入字段中,如下所示: 缺点是,对于每个输入字段,我都必须在代码中手动附加此侦听器。因此,更改现有的

  • 我有一个使用、和运行的项目,使用这个版本我的测试很好,100%通过。 但现在我要迁移到,因为它与Browserstack完全集成,Behat2不支持Browserstack标志,而且集成性很差。 我已经更改了composer文件并更新了project,但当我运行测试时,它在上返回了一个异常。 查看页面对象文档,我没有看到任何关于setPageObjectFatory的内容,这是不需要的。在conf