private static List<ArgumentDefinition> parseArgs( Map<ParameterName, com.palantir.conjure.parser.services.ArgumentDefinition> args, HttpPath httpPath, ReferenceTypeResolver typeResolver) { ImmutableList.Builder<ArgumentDefinition> resultBuilder = ImmutableList.builder(); for (Map.Entry<com.palantir.conjure.parser.services.ParameterName, com.palantir.conjure.parser.services.ArgumentDefinition> entry : args.entrySet()) { com.palantir.conjure.parser.services.ArgumentDefinition original = entry.getValue(); ArgumentName argName = ArgumentName.of(entry.getKey().name()); ParameterType paramType = parseParameterType(original, argName, httpPath); ArgumentDefinition.Builder builder = ArgumentDefinition.builder() .argName(argName) .type(original.type().visit(new ConjureTypeParserVisitor(typeResolver))) .paramType(paramType) .docs(original.docs().map(Documentation::of)) .markers(parseMarkers(original.markers(), typeResolver)); resultBuilder.add(builder.build()); } return resultBuilder.build(); }
@Override public void validate(EndpointDefinition definition) { definition.getArgs().forEach(arg -> { Matcher matcher = ANCHORED_PATTERN.matcher(arg.getArgName().get()); Preconditions.checkState(matcher.matches(), "Parameter names in endpoint paths and service definitions must match pattern %s: %s", ANCHORED_PATTERN, arg.getArgName().get()); }); } }
@Test public void testSingleBodyParamValidator() { EndpointDefinition.Builder definition = EndpointDefinition.builder() .args(BODY_ARG_BUILDER.argName(ArgumentName.of("bodyArg1")).build()) .args(BODY_ARG_BUILDER.argName(ArgumentName.of("bodyArg2")).build()) .endpointName(ENDPOINT_NAME) .httpMethod(HttpMethod.GET) .httpPath(HttpPath.of("/a/path")); assertThatThrownBy(() -> EndpointDefinitionValidator.validateAll(definition.build(), emptyDealiasingVisitor)) .isInstanceOf(IllegalStateException.class) .hasMessage("Endpoint cannot have multiple body parameters: [bodyArg1, bodyArg2]"); }
@Override public void validate(EndpointDefinition definition) { Set<ArgumentName> pathParamIds = new HashSet<>(); definition.getArgs().stream() .filter(entry -> entry.getParamType().accept(ParameterTypeVisitor.IS_PATH)) .forEach(entry -> { boolean added = pathParamIds.add(entry.getArgName()); Preconditions.checkState(added, "Path parameter with identifier \"%s\" is defined multiple times for endpoint", entry.getArgName().get()); }); Set<ArgumentName> pathArgs = HttpPathValidator.pathArgs(definition.getHttpPath().get()); Set<ArgumentName> extraParams = Sets.difference(pathParamIds, pathArgs); Preconditions.checkState(extraParams.isEmpty(), "Path parameters defined in endpoint but not present in path template: %s. " + "Note that the `param-id` is no longer supported and the path template name is always " + "used instead. So make sure the path template name matches the path parameter defined " + "in endpoint.", extraParams); Set<ArgumentName> missingParams = Sets.difference(pathArgs, pathParamIds); Preconditions.checkState(missingParams.isEmpty(), "Path parameters defined path template but not present in endpoint: %s", missingParams); } }
@Test public void testNoGetBodyValidator() { EndpointDefinition.Builder definition = EndpointDefinition.builder() .args(BODY_ARG_BUILDER.argName(ArgumentName.of("bodyArg")).build()) .endpointName(ENDPOINT_NAME) .httpMethod(HttpMethod.GET) .httpPath(HttpPath.of("/a/path")); assertThatThrownBy(() -> EndpointDefinitionValidator.validateAll(definition.build(), emptyDealiasingVisitor)) .isInstanceOf(IllegalStateException.class) .hasMessage(String.format( "Endpoint cannot be a GET and contain a body: method: %s, path: %s", HttpMethod.GET, "/a/path")); }
private static ParameterType parseParameterType( com.palantir.conjure.parser.services.ArgumentDefinition argumentDef, ArgumentName argName, HttpPath httpPath) { Set<ArgumentName> args = HttpPathValidator.pathArgs(httpPath.get()); switch (argumentDef.paramType()) { case AUTO: // AUTO type if (args.contains(argName)) { // argument exists in request line -- it is a path arg return ParameterType.path(PathParameterType.of()); } else { // argument does not exist in request line -- it is a body arg return ParameterType.body(BodyParameterType.of()); } case HEADER: String headerParamId = argumentDef.paramId().map(id -> id.name()).orElse(argName.get()); return ParameterType.header(HeaderParameterType.of(ParameterId.of(headerParamId))); case PATH: return ParameterType.path(PathParameterType.of()); case BODY: return ParameterType.body(BodyParameterType.of()); case QUERY: String queryParamId = argumentDef.paramId().map(id -> id.name()).orElse(argName.get()); return ParameterType.query(QueryParameterType.of(ParameterId.of(queryParamId))); default: throw new IllegalArgumentException("Unknown parameter type: " + argumentDef.paramType()); } }
@Test public void testArgumentTypeValidator() { EndpointDefinition.Builder definition = EndpointDefinition.builder() .args(ImmutableList.of(ArgumentDefinition.builder() .argName(ArgumentName.of("testArg")) .type(Type.primitive(PrimitiveType.BINARY)) .paramType(ParameterType.header(HeaderParameterType.of(ParameterId.of("testArg")))) .build())) .endpointName(ENDPOINT_NAME) .httpMethod(HttpMethod.GET) .httpPath(HttpPath.of("/a/path")); assertThatThrownBy(() -> EndpointDefinitionValidator.validateAll(definition.build(), emptyDealiasingVisitor)) .isInstanceOf(IllegalArgumentException.class) .hasMessage("Non body parameters cannot be of the 'binary' type: 'testArg' is not allowed"); }
@Test public void testPathParamValidatorUniquePathParams() { ArgumentDefinition paramDefinition1 = ArgumentDefinition.builder() .argName(ArgumentName.of("paramName")) .type(Type.primitive(PrimitiveType.STRING)) .paramType(ParameterType.path(PathParameterType.of())) .build(); ArgumentDefinition paramDefinition2 = ArgumentDefinition.builder() .argName(ArgumentName.of("paramName")) .type(Type.primitive(PrimitiveType.STRING)) .paramType(ParameterType.path(PathParameterType.of())) .build(); EndpointDefinition.Builder definition = EndpointDefinition.builder() .args(ImmutableList.of(paramDefinition1, paramDefinition2)) .endpointName(ENDPOINT_NAME) .httpMethod(HttpMethod.GET) .httpPath(HttpPath.of("/a/path")); assertThatThrownBy(() -> EndpointDefinitionValidator.validateAll(definition.build(), emptyDealiasingVisitor)) .isInstanceOf(IllegalStateException.class) .hasMessage("Path parameter with identifier \"paramName\" is defined multiple times for endpoint"); }
@Test public void testComplexHeaderObject() { TypeName typeName = TypeName.of("SomeObject", "com.palantir.foo"); EndpointDefinition.Builder definition = EndpointDefinition.builder() .args(ArgumentDefinition.builder() .argName(ArgumentName.of("someName")) .type(Type.reference(typeName)) .paramType(ParameterType.header(HeaderParameterType.of(ParameterId.of("SomeId")))) .build()) .endpointName(ENDPOINT_NAME) .httpMethod(HttpMethod.GET) .httpPath(HttpPath.of("/a/path")); DealiasingTypeVisitor dealiasingVisitor = new DealiasingTypeVisitor(ImmutableMap.of( typeName, TypeDefinition.object(ObjectDefinition.of(typeName, ImmutableList.of(), Documentation.of(""))) )); assertThatThrownBy(() -> EndpointDefinitionValidator.validateAll(definition.build(), dealiasingVisitor)) .isInstanceOf(IllegalStateException.class) .hasMessage("Header parameters must be enums, primitives, aliases or optional primitive:" + " \"someName\" is not allowed"); } }
private EndpointDefinition.Builder createEndpoint(String paramName) { ArgumentDefinition arg = ArgumentDefinition.builder() .paramType(ParameterType.body(BodyParameterType.of())) .type(Type.primitive(PrimitiveType.STRING)) .argName(ArgumentName.of(paramName)) .build(); return EndpointDefinition.builder() .httpMethod(HttpMethod.POST) .httpPath(HttpPath.of("/a/path")) .args(ImmutableList.of(arg)) .endpointName(EndpointName.of("test")); } }
@Test public void testComplexHeader() { EndpointDefinition.Builder definition = EndpointDefinition.builder() .args(ArgumentDefinition.builder() .argName(ArgumentName.of("someName")) .type(Type.list(ListType.builder().itemType(Type.primitive(PrimitiveType.STRING)).build())) .paramType(ParameterType.header(HeaderParameterType.of(ParameterId.of("someId")))) .build()) .endpointName(ENDPOINT_NAME) .httpMethod(HttpMethod.GET) .httpPath(HttpPath.of("/a/path")); assertThatThrownBy(() -> EndpointDefinitionValidator.validateAll(definition.build(), emptyDealiasingVisitor)) .isInstanceOf(IllegalStateException.class) .hasMessage("Header parameters must be enums, primitives, aliases or optional primitive:" + " \"someName\" is not allowed"); }
@Test public void testPathParamValidatorExtraParams() { ArgumentDefinition paramDefinition = ArgumentDefinition.builder() .type(Type.primitive(PrimitiveType.STRING)) .argName(ArgumentName.of("paramName")) .paramType(ParameterType.path(PathParameterType.of())) .build(); EndpointDefinition.Builder definition = EndpointDefinition.builder() .args(paramDefinition) .endpointName(ENDPOINT_NAME) .httpMethod(HttpMethod.GET) .httpPath(HttpPath.of("/a/path")); assertThatThrownBy(() -> EndpointDefinitionValidator.validateAll(definition.build(), emptyDealiasingVisitor)) .isInstanceOf(IllegalStateException.class) .hasMessageContaining( "Path parameters defined in endpoint but not present in path template: [paramName]"); }
@Test @SuppressWarnings("CheckReturnValue") public void testArgumentBodyTypeValidator() { EndpointDefinition.Builder definition = EndpointDefinition.builder() .args(ImmutableList.of(ArgumentDefinition.builder() .argName(ArgumentName.of("testArg")) .type(Type.primitive(PrimitiveType.BINARY)) .paramType(ParameterType.body(BodyParameterType.of())) .build())) .endpointName(ENDPOINT_NAME) .httpMethod(HttpMethod.POST) .httpPath(HttpPath.of("/a/path")); // Should not throw exception EndpointDefinitionValidator.validateAll(definition.build(), emptyDealiasingVisitor); }