Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

NIFI-5214 Added REST LookupService #2723

Closed
wants to merge 22 commits into from

Conversation

MikeThomsen
Copy link
Contributor

@MikeThomsen MikeThomsen commented May 19, 2018

Thank you for submitting a contribution to Apache NiFi.

In order to streamline the review of the contribution we ask you
to ensure the following steps have been taken:

For all changes:

  • Is there a JIRA ticket associated with this PR? Is it referenced
    in the commit message?

  • Does your PR title start with NIFI-XXXX where XXXX is the JIRA number you are trying to resolve? Pay particular attention to the hyphen "-" character.

  • Has your PR been rebased against the latest commit within the target branch (typically master)?

  • Is your initial contribution a single, squashed commit?

For code changes:

  • Have you ensured that the full suite of tests is executed via mvn -Pcontrib-check clean install at the root nifi folder?
  • Have you written or updated unit tests to verify your changes?
  • If adding new dependencies to the code, are these dependencies licensed in a way that is compatible for inclusion under ASF 2.0?
  • If applicable, have you updated the LICENSE file, including the main LICENSE file under nifi-assembly?
  • If applicable, have you updated the NOTICE file, including the main NOTICE file found under nifi-assembly?
  • If adding new Properties, have you added .displayName in addition to .name (programmatic access) for each of the new properties?

For documentation related changes:

  • Have you ensured that format looks appropriate for the output in which it is rendered?

Note:

Please ensure that once the PR is submitted, you check travis-ci for build issues and submit an update to your PR as soon as possible.

@ottobackwards
Copy link
Contributor

This is cool, I will definitely make an aws web api version of this after it and my pr lands.
I think that any rest service needs to support the options that InvokeHttp supports. Proxies etc. This doesn't seem to do that.

@MikeThomsen
Copy link
Contributor Author

InvokeHttp has a lot of options, so that might be worth splitting it into an initial merge and then an improvement that comes in 1.8.

@ottobackwards
Copy link
Contributor

Don't I know it. If you want to do this as a 'series' of prs, I think you should document that in jira with tasks representing each PR and note it clearly in the pr.

That will set the right context for the reviewers.

I chose with my InvokeAwsGatewayApi processor to make sure that I replicated the InvokeHttp test suite. I would suggest that the end goal here would be to have and extensive set of tests adapted from that suite for the reader.

@MikeThomsen
Copy link
Contributor Author

@ottobackwards I grabbed a bunch of code from InvokeHttp and did a little copy pasta. Please take a look when you get a chance. I think moving the copy pasta into a shared utility package will need to be a separate ticket in order to keep the ticket scope down.

@MikeThomsen
Copy link
Contributor Author

@ottobackwards I think I might just go ahead and refactor that code now and get it done in this PR.

@MikeThomsen
Copy link
Contributor Author

@ottobackwards @ijokarumawak Added support for the new ProxyConfigurationService. Would appreciate a review.

Copy link
Contributor

@ottobackwards ottobackwards left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

First couple of things

import java.util.Optional;
import java.util.Set;
import java.util.stream.Collectors;

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should this be tagged HTTP?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Probably

if (proxyConfigurationService != null) {
setProxy(builder);
}

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why are we getting the SSL_CONTEXT_SERVICE twice?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copy pasta

@ottobackwards
Copy link
Contributor

@MikeThomsen
Here are the properties exposed for configuration of InvokeHttp:

 public static final List<PropertyDescriptor> PROPERTIES = Collections.unmodifiableList(Arrays.asList(
            PROP_METHOD,
            PROP_URL,
            PROP_SSL_CONTEXT_SERVICE,
            PROP_CONNECT_TIMEOUT,
            PROP_READ_TIMEOUT,
            PROP_DATE_HEADER,
            PROP_FOLLOW_REDIRECTS,
            PROP_ATTRIBUTES_TO_SEND,
            PROP_BASIC_AUTH_USERNAME,
            PROP_BASIC_AUTH_PASSWORD,
            PROP_PROXY_HOST,
            PROP_PROXY_PORT,
            PROP_PROXY_TYPE,
            PROP_PROXY_USER,
            PROP_PROXY_PASSWORD,
            PROP_PUT_OUTPUT_IN_ATTRIBUTE,
            PROP_PUT_ATTRIBUTE_MAX_LENGTH,
            PROP_DIGEST_AUTH,
            PROP_OUTPUT_RESPONSE_REGARDLESS,
            PROP_TRUSTED_HOSTNAME,
            PROP_ADD_HEADERS_TO_REQUEST,
            PROP_CONTENT_TYPE,
            PROP_SEND_BODY,
            PROP_USE_CHUNKED_ENCODING,
            PROP_PENALIZE_NO_RETRY));

Of these, I wonder if we should consider for the rest lookup
PROP_CONNECT_TIMEOUT,
PROP_READ_TIMEOUT,
PROP_DATE_HEADER,
PROP_FOLLOW_REDIRECTS,
PROP_ATTRIBUTES_TO_SEND,
PROP_TRUSTED_HOSTNAME,
PROP_ADD_HEADERS_TO_REQUEST
In whatever form makes sense for the lookup service.

For example: what if my lookup service is someone else's API and I need to send custom headers and api keys?

@ottobackwards
Copy link
Contributor

I almost wonder if there should be an http rest connection service

@MikeThomsen
Copy link
Contributor Author

@ottobackwards I'll add user-defined headers and basic auth support, but that's it until there's a discussion around a HTTP Connection Service. I think that would be the place to go crazy on the options, not the lookup service.

@ottobackwards
Copy link
Contributor

@MikeThomsen I agree. I haven't seen any conversation on it, but it seems more and more obvious, given the way things are going right?

Copy link
Member

@ijokarumawak ijokarumawak left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@MikeThomsen This new capability would be really useful. Thanks! I looked through the new lines of code and posted few comments. Please check those.

@@ -0,0 +1,42 @@
<!DOCTYPE html>
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@MikeThomsen additionalDetails.html should be in a directory src/main/resources/docs/qualifiedClassName/ not in docs.qualifiedClassName. Please check additional docs can be accessed from component's usage page from NiFi UI.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done.

e -> e.getKey(),
e -> e.getValue().toString()
));
RecordReader reader = readerFactory.createRecordReader(variables, is, getLogger());
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please use try-with-resource statement, similar to what AbstractRouteRecord does:

try (final RecordReader reader = readerFactory.createRecordReader(originalAttributes, in, getLogger())) {

e -> e.getValue().toString()
));
RecordReader reader = readerFactory.createRecordReader(variables, is, getLogger());
Record record = reader.nextRecord();
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We should handle the case where record is null.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ok, not sure how to trick it into doing null, but I put a check for that and made the optional be empty and added a new unit test to handle empty records (ex. badly defined schema has no fields from a rest response).


def coordinates = [
"schema.name": "simple",
"endpoint": server.url + "/simple",
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In real use-cases, do we expect user to set 'server.url' at a LookupRecord processor's user defined property?

As an alternative approach, I'd add endpoint url at RestLookupService to define a template string to resolve target endpoint, and let callers such as LookupRecord to pass required variables to complete the target endpoint.

For example:

  • At RestLookupService
    • Endpoint URL: https://some-api.example.com/buckets/${bucketId}/items/${itemId}
  • At LookupRecord processor
    • bucketId as user-defined-property: ./bucketId (a record path to get a value)
    • itemId as user-defined-property: ./itemId (a record path to get a value)

By doing so, it also supports passing the entire endpoint URL:

  • At RestLookupService
    • Endpoint URL: ${endpoint}
  • At LookupRecord processor
    • endpoint as user-defined-property: a record path to construct an URL

I'd expect RestLookupService to have more control on REST related configurations, and callers just pass variables to make actual requests. How do you think?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I like that. How about this breakdown:

  1. endpoint is a template string set on a property descriptor and we use an EL compiler to generate the endpoint on the fly with those lookup coordinates.
  2. Add direct_endpoint which is a treated as literal value to override that if present.
  3. If both are present, throw an exception.

Thoughts?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@MikeThomsen I've replied to your question on the dev ML. I think endpoint property can be used both cases where user want to use EL or just a literal endpoint. Because Query.prepare method can return the configured literal String if it does not contain any EL.

        final Map<String, String> map = Collections.singletonMap("name", "John Smith");
        PreparedQuery query = Query.prepare("${name}-${name:length()}");
        String result = query.evaluateExpressions(map, null);
        System.out.println(result);

        query = Query.prepare("name-name:length()");
        result = query.evaluateExpressions(map, null);
        System.out.println(result);
        final Map<String, String> map = Collections.singletonMap("name", "John Smith");
        PreparedQuery query = Query.prepare("${name}-${name:length()}");
        String result = query.evaluateExpressions(map, null);
        System.out.println(result);

        query = Query.prepare("name-name:length()");
        result = query.evaluateExpressions(map, null);
        System.out.println(result);

The code prints:

John Smith-10
name-name:length()

@MikeThomsen
Copy link
Contributor Author

@ijokarumawak I can't believe I missed your comments. Will try to get to those today.

@MikeThomsen
Copy link
Contributor Author

@ijokarumawak Your first feedback points were merged in, let me know what you think of the last item and it should be easy to get done.

Copy link
Contributor

@markap14 markap14 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@MikeThomsen this is a good idea - i think it will be extremely helpful for a lot of use cases. I left several inline comments. A couple I think are actually bugs (a 'fall through' case if RecordPath doesn't match and a threading issue). The others are mostly just regarding general NiFi coding conventions. Thanks!

.description("An optional record path that can be used to define where in a record to get the real data to merge " +
"into the record set to be enriched. See documentation for examples of when this might be useful.")
.expressionLanguageSupported(ExpressionLanguageScope.VARIABLE_REGISTRY)
.addValidator(Validator.VALID)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should use a RecordPath validator, no?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ok. Didn't know it even existed :)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done.

.description("The record reader to use for loading the payload and handling it as a record set.")
.expressionLanguageSupported(ExpressionLanguageScope.NONE)
.identifiesControllerService(RecordReaderFactory.class)
.addValidator(Validator.VALID)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No need for the explicit validator when identifying controller services.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done.

getHeaders(context);
}

private Map<String, String> headers;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Typical convention in NiFi is to add member variables at the top of the class before constructors.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done.

private void getHeaders(ConfigurationContext context) {
headers = new HashMap<>();
for (PropertyDescriptor descriptor : context.getProperties().keySet()) {
if (descriptor.getName().startsWith("header.")) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If dynamic properties are used for specifying headers to use, why not just let the property name be the name of the header? I.e., why require a "header." prefix? We should also document how dynamic properties are used via the @dynamicproperties annotation.


Record record = handleResponse(is, coordinates);

return record != null
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can just use Optional.ofNullable(record) here

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done.

} else if (value instanceof Map) {
temp = new MapRecord(schema, (Map<String, Object>) value);
} else {
temp = new MapRecord(schema, new HashMap<String, Object>() {{
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We should avoid the creation of an anonymous inner class here and instead just create the MapRecord and then call put()

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done.

FieldValue fieldValue = fv.get();
RecordSchema schema = new SimpleRecordSchema(Arrays.asList(fieldValue.getField()));
String[] parts = recordPath.getPath().split("/");
String last = parts[parts.length - 1];
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think this is actually what we want here. What if the path is something like /myField[./second/third = 'hello'] - in that case, last will equal third = 'hello']

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Great point. I think I'm going to have to make another property to handle this instead.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done.

return record;
} catch (Exception ex) {
is.close();
throw new RuntimeException(ex);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Would avoid wrapping checked exceptions that are known to be thrown by method (such as IOException) in a RuntimeException. Am guessing you can just use throw ex because all checked exceptions that can be caught are likely already declared for the method but not sure off the top of my head.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done.

return request.build();
}

private String basicUser;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We should avoid member variables throughout code and try to concentrate them at the top of the class. Additionally, these 3 member variables are not protected so the class is not thread-safe. Need to ensure that all ControllerServices are thread-safe.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think I fixed it.


@Override
public Set<String> getRequiredKeys() {
return new HashSet<String>(){{
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We should avoid the creation of anonymous inner classes when we can. We can also make this an UnmodifiableSet and just create a single instance of it as a member variable instead of constantly creating the Set

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done.

@MikeThomsen
Copy link
Contributor Author

@markap14 I think your concerns are addressed now.

@ijokarumawak I tried out the expression language APIs and found that I can't just submit a Map, so I'm going to hold off on providing a dynamic template URL for now. That could be be a good improvement to add after the expression language compiler api gets updated to support generic maps as well (if it ever happens).

Copy link
Member

@ijokarumawak ijokarumawak left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@MikeThomsen Commented on the use of EL with a Map above, please check that out. Also, found the use of company.com, let's change it to example.com.

Do you think making the URL as template can be included in this PR? I will wait in case if you want to do that with this PR. Please let me know, once I got your confirmation, I will do the final review & testing before merging.

Thanks!

inner: [
"username": "jane.doe",
"password": "testing7890",
"email": "jane.doe@company.com"
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The domain company.com actually exists. Please change it to example.com.

def record = response.get()
Assert.assertEquals("jane.doe", record.getAsString("username"))
Assert.assertEquals("testing7890", record.getAsString("password"))
Assert.assertEquals("jane.doe@company.com", record.getAsString("email"))
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The domain company.com actually exists. Please change it to example.com.

@MikeThomsen
Copy link
Contributor Author

@ijokarumawak I'll take a stab at adding the template url option.

@@ -277,6 +280,20 @@ private void setProxy(OkHttpClient.Builder builder) {
}
}

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should this handle AttributeExpressionLanguageParsingException? Is there any validation that can be done?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think so because the EL support is enabled on the fly by the user, and any user who is enterprising enough to do that should be responsible for validating it. There's no good fallback option short of having them specify yet another key, and IMO that's overkill.

e -> e.getKey(),
e -> e.getValue().toString()
));
final PreparedQuery query = Query.prepare((String)coordinates.get(ENDPOINT_KEY));
Copy link
Member

@ijokarumawak ijokarumawak Jun 5, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@MikeThomsen Thanks for adding this! This gets closer to what I was imagined. Excuse me if I was not clear enough, but from a user's stand point, I'd like to configure this ENDPOINT_KEY value at a RestLookupService property rather than passing it via coordinates.

The template it self can be configured at the service statically, but the variables to resolve a complete endpoint URL can be passed as coordinates.

Because, I think people would use one RestLookupService to lookup some endpoints (can be more than 1) of a particular Web service API. If they need to deal with different Web services, e.g. Twitter, Facebook and Salesforce ...etc they would use 3 RestLookupService, and each one can have the endpoint template defined.

Further more, if we implemented like that, we can cache the PreparedQuery instance without parsing the EL every time. Also, no need to have ENDPOINT_TEMPLATE_KEY coordinate to tell if it contains EL or not because the PreparedQuery returns the original String if it does not contains EL.

How do you think?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think that would work.

Copy link
Member

@ijokarumawak ijokarumawak left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@MikeThomsen I was able to use RestLookupService from LookupRecord processor. However, found several places for further improvements. Please check the comments. Thanks!

<version>1.7.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>nifi-standard-web-utils</artifactId>
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think extracting TestServer.java was not necessary, and I'd avoid doing this in this PR. Because:

  • It makes this PR to touch many files unnecessarily in terms of its objective.
  • Having test scope dependency to nifi-standard-processors should be fine to reuse TestServer.
  • Even if we do extract TestServer, the module name nifi-standard-web-utils will not be appropriate to contain Test utility class. It should be named more clearly to state that this module is for test. nifi-mock is a good example.

Do you have a strong motivation to do this?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah. I can see this functionality being reused in plenty of places and don't think we should tie the transitive dependencies of the standard package's test scope into other packages as that could complicate things down the road. I'll change the module name to nifi-standard-web-test-utils. How about that?

.description("The username to be used by the client to authenticate against the Remote URL. Cannot include control characters (0-31), ':', or DEL (127).")
.required(false)
.addValidator(StandardValidators.createRegexMatchingValidator(Pattern.compile("^[\\x20-\\x39\\x3b-\\x7e\\x80-\\xff]+$")))
.build();
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This should support EL with Variable Registry.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done.

.required(false)
.sensitive(true)
.addValidator(StandardValidators.createRegexMatchingValidator(Pattern.compile("^[\\x20-\\x7e\\x80-\\xff]+$")))
.build();
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This should support EL with Variable Registry.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done.

// If the username/password properties are set then check if digest auth is being used
if (!authUser.isEmpty() && isDigest) {
final String authPass = trimToEmpty(context.getProperty(PROP_BASIC_AUTH_PASSWORD).getValue());
this.basicPass = authPass;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this.basicPass should be set regardless of isDigest, similar to this.basicUser. Having it here will leave basicPass unset if isDigest is false. Is this correct?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done.

public Optional<Record> lookup(Map<String, Object> coordinates) throws LookupFailureException {
final String endpoint = determineEndpoint(coordinates);
final String mimeType = (String)coordinates.get(MIME_TYPE_KEY);
final String method = (String)coordinates.get(METHOD_KEY);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Using getOrDefault(METHOD_KEY, "get") would be more user friendly.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A value is required because it's a required key, so I don't think that's necessary.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

When I used the lookup service from LookupRecord, I felt passing a literal value as RecordPath was a bit tricky. I had to use concat('get').

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@MikeThomsen RequiredKeys is not mandatory, it can be an empty Set. Also, this is a Lookup service. Get should be the default method like the famous cURL command.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I can see how that would be awkward. Do you want to handle that with a combination of property + lookup key (latter overriding the former) or defer to fixing LookupRecord?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I want RestLookupService to have 'get' as default value. It will cover 80% of use-cases I assume. I will send a PR to your PR and if it looks fine, then I'd give my +1 so that you can merge.

<p>Headers are supported using dynamic properties. Just add a dynamic property and the name will be the header name and the value will be the value for the header. Expression language
powered by input from the variable registry is supported.</p>
<h2>Dynamic URLs</h2>
<p>The URL property supports expression language in a non-standard way: through the lookup key/value pairs configured on the processor. The configuration specified by the user will be passed
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd omit the expression, 'in a non-standard way', because I envision this pattern will be used more frequently and I'm planning to add copy FlowFile attribute to coordinates capability at LookupRecord processor

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done.

@DynamicProperty(name = "*", value = "*", description = "All dynamic properties are added as HTTP headers with the name " +
"as the header name and the value as the header value.")
})
public class RestLookupService extends AbstractControllerService implements LookupService<Record> {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

implements RecordLookupService is better.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done.

Set<String> _keys = new HashSet<>();
_keys.add(MIME_TYPE_KEY);
_keys.add(METHOD_KEY);
KEYS = Collections.unmodifiableSet(_keys);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

MIME_TYPE is only used for put and post requests and should be optional. Also if we support 'get' is the default method, then this controller service does not have any required KEYS.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That's a fair point, and it needs validation on the verbs.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done.

@MikeThomsen
Copy link
Contributor Author

Other than the issue w/ literals you cited, looks ready to merge IMO.

@MikeThomsen
Copy link
Contributor Author

@ijokarumawak merged your PR. You can merge at any time.

@MikeThomsen
Copy link
Contributor Author

@ijokarumawak the AWS gateway tests are broken because I moved test server. I'll do a patch for that.

@MikeThomsen
Copy link
Contributor Author

@ijokarumawak I accidentally squashed the changes that @markap14 requested into your commit.

@ijokarumawak
Copy link
Member

@MikeThomsen that's ok, I think it's time to squash all commits into one, I'm fine to lose my commit histories. Now we have #2777 merged into master, so we can utilize it. Can you update RestLookupService to do that? Like I did it locally here. I am +1 once this gets done.
ijokarumawak@f97924e

MikeThomsen and others added 22 commits June 14, 2018 06:47
1. Added 'Base URL' property to address environment specific part of URL.
2. Removed 'Record Path Property Name' property, because the name of
a resulted record field of a record path can be obtained by field name.
3. Lower cased HTTP method name should be used throughout.
4. Added mimeType require check when body is specified.
5. Added debug log to print HTTP response code.
6. Prepare for NIFI-5287.
7. Fixed that mime.type being used regardless of whether body is
specified or not, caused NullPointerException when 'mime.type' is not
specified when it is not required.
8. Updated documentation.
- Use PropertyValue instead of PreparedQuery to utilize Variable
Registry.
- Removed BASE_URL because Variable Registry can be used at URL
@MikeThomsen
Copy link
Contributor Author

@ijokarumawak Added a new commit that should get it to close out.

@ijokarumawak
Copy link
Member

@MikeThomsen I squashed all commits into one, did manual test. LGTM, +1. merging to master! Thank you!

@asfgit asfgit closed this in be63378 Jun 15, 2018
@MikeThomsen MikeThomsen deleted the NIFI-5214 branch August 14, 2024 21:14
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants