private static void validate(String path) { HttpPathValidator.validate(HttpPath.of(path)); } }
@Override public void validate(ServiceDefinition definition) { Multimap<String, String> pathToEndpoints = ArrayListMultimap.create(); definition.getEndpoints().stream().forEach(entry -> { String methodPath = entry.getHttpMethod().get() + " " + entry.getHttpPath().get(); // normalize all path parameter variables and regular expressions because all path args are treated // as identical for comparisons (paths cannot differ only in the name/regular expression of a path // variable) methodPath = PATHVAR_PATTERN.matcher(methodPath).replaceAll("{arg}"); pathToEndpoints.put(methodPath, entry.getEndpointName().get()); }); pathToEndpoints.keySet().stream().sorted().forEachOrdered(key -> { Collection<String> endpoints = pathToEndpoints.get(key); Preconditions.checkState(endpoints.size() <= 1, "Endpoint \"%s\" is defined by multiple endpoints: %s", key, endpoints.toString()); }); } }
private static HttpPath parseHttpPath( com.palantir.conjure.parser.services.EndpointDefinition def, PathString basePath) { HttpPath httpPath = HttpPath.of(basePath.resolve(def.http().path()).toString()); HttpPathValidator.validate(httpPath); return httpPath; }
@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 testPathParamValidatorMissingParams() { EndpointDefinition.Builder definition = EndpointDefinition.builder() .endpointName(ENDPOINT_NAME) .httpMethod(HttpMethod.GET) .httpPath(HttpPath.of("/a/path/{paramName}")); assertThatThrownBy(() -> EndpointDefinitionValidator.validateAll(definition.build(), emptyDealiasingVisitor)) .isInstanceOf(IllegalStateException.class) .hasMessage("Path parameters defined path template but not present in endpoint: [paramName]"); }
Path path = Paths.get(httpPath.get()); Preconditions.checkArgument(path.isAbsolute(), "Conjure paths must be absolute, i.e., start with '/': %s", path);
@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 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]"); }
@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 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 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 @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); }
private EndpointDefinition createEndpoint(ParameterType paramType) { ArgumentDefinition arg = ArgumentDefinition.builder() .argName(PARAMETER_NAME) .paramType(paramType) .type(Type.primitive(PrimitiveType.INTEGER)) .build(); EndpointDefinition definition = EndpointDefinition.builder() .httpMethod(HttpMethod.POST) .httpPath(HttpPath.of("/a/path")) .args(arg) .endpointName(EndpointName.of("test")) .build(); EndpointDefinitionValidator.validateAll(definition, dealiasingVisitor); return definition; } }