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

Adds toString() to filter factories and predicate factories. #785

Merged
merged 14 commits into from Jul 23, 2019

Conversation

spencergibb
Copy link
Member

This allows for easier debugging and visibility into configuration.

Predicates needed custom implementations of and() or() and not() so
that toString() could function.

fixes gh-784

@codecov-io
Copy link

codecov-io commented Jan 16, 2019

Codecov Report

Merging #785 into 2.1.x will decrease coverage by 6.35%.
The diff coverage is n/a.

Impacted file tree graph

@@             Coverage Diff             @@
##             2.1.x     #785      +/-   ##
===========================================
- Coverage     73.1%   66.74%   -6.36%     
+ Complexity    1056       90     -966     
===========================================
  Files          135        8     -127     
  Lines         4064      415    -3649     
  Branches       318       32     -286     
===========================================
- Hits          2971      277    -2694     
+ Misses         925      117     -808     
+ Partials       168       21     -147
Impacted Files Coverage Δ Complexity Δ
...loud/gateway/filter/headers/HttpHeadersFilter.java
...mework/cloud/gateway/config/GatewayProperties.java
.../gateway/handler/RoutePredicateHandlerMapping.java
...teway/config/PropertiesRouteDefinitionLocator.java
...rk/cloud/gateway/event/EnableBodyCachingEvent.java
...ork/cloud/gateway/config/HttpClientProperties.java
...oud/gateway/filter/ratelimit/RedisRateLimiter.java
...d/gateway/filter/WebClientWriteResponseFilter.java
...mework/cloud/gateway/support/TimeoutException.java
...mework/cloud/gateway/event/PredicateArgsEvent.java
... and 76 more

Continue to review full report at Codecov.

Legend - Click here to learn more
Δ = absolute <relative> (impact), ø = not affected, ? = missing data
Powered by Codecov. Last update 19494b2...792f7cd. Read the comment docs.

@ryanjbaxter
Copy link
Contributor

LGTM so far

This allows for easier debugging and visibility into configuration.

Predicates needed custom implementations of and() or() and not() so
that toString() could function.

fixes gh-784
@spencergibb spencergibb changed the base branch from master to 2.1.x July 22, 2019 18:11
@spencergibb spencergibb changed the title WIP, DO NOT MERGE: Adds toString() to filter factories and predicate factories. Adds toString() to filter factories and predicate factories. Jul 22, 2019
@spencergibb
Copy link
Member Author

spencergibb commented Jul 22, 2019

// 20190723103423
// http://localhost:9000/actuator/gateway/routes

[
  {
    "predicate": "Hosts: [**.example.org]",
    "route_id": "host_example_to_httpbin",
    "filters": [
      "[[AddResponseHeader X-Response-Default-Foo = 'Default-Bar'], order = 1]",
      "[[PrefixPath prefix = '/httpbin'], order = 2]"
    ],
    "uri": "lb://testservice",
    "order": 0
  },
  {
    "predicate": "(((((((Hosts: [**.foo.org] && Paths: [/headers], match trailing slash: true) && Method: GET) && Header: X-Request-Id regexp=\\d+) && Query: param=foo regexp=ba.) && Query: param=baz regexp=null) && Cookie: name=chocolate regexp=ch.p) && After: 1900-01-20T17:42:47.789-07:00[America/Denver])",
    "route_id": "host_foo_path_headers_to_httpbin",
    "filters": [
      "[[AddResponseHeader X-Response-Default-Foo = 'Default-Bar'], order = 1]",
      "[[AddResponseHeader X-Response-Foo = 'Bar'], order = 1]",
      "[[PrefixPath prefix = '/httpbin'], order = 2]"
    ],
    "uri": "lb://testservice",
    "order": 0
  },
  {
    "predicate": "(Hosts: [**.addrequestheader.org] && Paths: [/headers], match trailing slash: true)",
    "route_id": "add_request_header_test",
    "filters": [
      "[[AddResponseHeader X-Response-Default-Foo = 'Default-Bar'], order = 1]",
      "[[AddRequestHeader X-Request-Foo = 'Bar'], order = 1]",
      "[[PrefixPath prefix = '/httpbin'], order = 2]"
    ],
    "uri": "lb://testservice",
    "order": 0
  },
  {
    "predicate": "(Hosts: [**.addrequestparameter.org] && Paths: [/get], match trailing slash: true)",
    "route_id": "add_request_parameter_test",
    "filters": [
      "[[AddResponseHeader X-Response-Default-Foo = 'Default-Bar'], order = 1]",
      "[[AddRequestParameter foo = 'bar'], order = 1]",
      "[[PrefixPath prefix = '/httpbin'], order = 2]"
    ],
    "uri": "lb://testservice",
    "order": 0
  },
  {
    "predicate": "(Hosts: [**.addresponseheader.org] && Paths: [/headers], match trailing slash: true)",
    "route_id": "add_response_header_test",
    "filters": [
      "[[AddResponseHeader X-Response-Default-Foo = 'Default-Bar'], order = 1]",
      "[[AddResponseHeader X-Request-Foo = 'Bar'], order = 1]",
      "[[PrefixPath prefix = '/httpbin'], order = 2]"
    ],
    "uri": "lb://testservice",
    "order": 0
  },
  {
    "predicate": "(Hosts: [**.deduperesponseheader.org] && Paths: [/headers], match trailing slash: true)",
    "route_id": "dedupe_response_header_test",
    "filters": [
      "[[AddResponseHeader X-Response-Default-Foo = 'Default-Bar'], order = 1]",
      "[[AddResponseHeader Access-Control-Allow-Credentials = 'true'], order = 1]",
      "[[PrefixPath prefix = '/httpbin'], order = 2]",
      "[[AddResponseHeader Access-Control-Allow-Credentials = 'false'], order = 2]",
      "[[AddResponseHeader Access-Control-Allow-Origin = 'https://musk.mars'], order = 3]",
      "[[AddResponseHeader Access-Control-Allow-Origin = '*'], order = 4]",
      "[[AddResponseHeader Scout-Cookie = 'Thin Mints'], order = 5]",
      "[[AddResponseHeader Scout-Cookie = 'S'mores'], order = 6]",
      "[[AddResponseHeader Next-Week-Lottery-Numbers = '4'], order = 7]",
      "[[AddResponseHeader Next-Week-Lottery-Numbers = '2'], order = 8]",
      "[[AddResponseHeader Next-Week-Lottery-Numbers = '2'], order = 9]",
      "[[AddResponseHeader Next-Week-Lottery-Numbers = '42'], order = 10]",
      "[[DedupeResponseHeader Access-Control-Allow-Credentials Access-Control-Allow-Origin = RETAIN_FIRST], order = 11]",
      "[[DedupeResponseHeader Scout-Cookie = RETAIN_LAST], order = 12]",
      "[[DedupeResponseHeader Next-Week-Lottery-Numbers = RETAIN_UNIQUE], order = 13]"
    ],
    "uri": "lb://testservice",
    "order": 0
  },
  {
    "predicate": "(Hosts: [**.rewriteresponseheader.org] && Paths: [/headers], match trailing slash: true)",
    "route_id": "rewrite_response_header_test",
    "filters": [
      "[[AddResponseHeader X-Response-Default-Foo = 'Default-Bar'], order = 1]",
      "[[AddResponseHeader X-Request-Foo = '/42?user=ford&password=omg!what&flag=true'], order = 1]",
      "[[PrefixPath prefix = '/httpbin'], order = 2]",
      "[[RewriteResponseHeader name = 'X-Request-Foo', regexp = 'password=[^&]+', replacement = 'password=***'], order = 2]"
    ],
    "uri": "lb://testservice",
    "order": 0
  },
  {
    "predicate": "Hosts: [**.forward.org]",
    "route_id": "forward_test",
    "filters": [
      "[[AddResponseHeader X-Response-Default-Foo = 'Default-Bar'], order = 1]",
      "[[PrefixPath prefix = '/httpbin'], order = 2]"
    ],
    "uri": "forward:/localcontroller",
    "order": 0
  },
  {
    "predicate": "Hosts: [**.hostpatternarg.org]",
    "route_id": "host_backwards_compatible_test",
    "filters": [
      "[[AddResponseHeader X-Response-Default-Foo = 'Default-Bar'], order = 1]",
      "[[PrefixPath prefix = '/httpbin'], order = 2]"
    ],
    "uri": "lb://testservice",
    "order": 0
  },
  {
    "predicate": "Hosts: [**.hostpatternshortcut.org]",
    "route_id": "host_backwards_compatible_shortcut_test",
    "filters": [
      "[[AddResponseHeader X-Response-Default-Foo = 'Default-Bar'], order = 1]",
      "[[PrefixPath prefix = '/httpbin'], order = 2]"
    ],
    "uri": "lb://testservice",
    "order": 0
  },
  {
    "predicate": "Hosts: [**.hostmulti1.org, **.hostmulti2.org]",
    "route_id": "host_multi_test",
    "filters": [
      "[[AddResponseHeader X-Response-Default-Foo = 'Default-Bar'], order = 1]",
      "[[PrefixPath prefix = '/httpbin'], order = 2]"
    ],
    "uri": "lb://testservice",
    "order": 0
  },
  {
    "predicate": "Hosts: [**.hystrixfailure.org]",
    "route_id": "hystrix_failure_test",
    "filters": [
      "[[AddResponseHeader X-Response-Default-Foo = 'Default-Bar'], order = 1]",
      "[[Hystrix name = 'failcmd', fallback = [null]], order = 1]",
      "[[PrefixPath prefix = '/httpbin'], order = 2]"
    ],
    "uri": "lb://testservice",
    "order": 0
  },
  {
    "predicate": "Hosts: [**.hystrixfallback.org]",
    "route_id": "hystrix_fallback_test",
    "filters": [
      "[[AddResponseHeader X-Response-Default-Foo = 'Default-Bar'], order = 1]",
      "[[Hystrix name = 'fallbackcmd', fallback = forward:/fallbackcontroller], order = 1]",
      "[[PrefixPath prefix = '/httpbin'], order = 2]"
    ],
    "uri": "lb://testservice",
    "order": 0
  },
  {
    "predicate": "Hosts: [**.hystrixexceptionfallback.org]",
    "route_id": "hystrix_exception_fallback_test",
    "filters": [
      "[[AddResponseHeader X-Response-Default-Foo = 'Default-Bar'], order = 1]",
      "[[Hystrix name = 'fallbackcmd', fallback = forward:/exceptionFallback], order = 1]",
      "[[PrefixPath prefix = '/httpbin'], order = 2]"
    ],
    "uri": "lb://testservice",
    "order": 0
  },
  {
    "predicate": "Hosts: [**.hystrixsuccess.org]",
    "route_id": "hystrix_success_test",
    "filters": [
      "[[AddResponseHeader X-Response-Default-Foo = 'Default-Bar'], order = 1]",
      "[[Hystrix name = 'successcmd', fallback = [null]], order = 1]",
      "[[PrefixPath prefix = '/httpbin'], order = 2]"
    ],
    "uri": "lb://testservice",
    "order": 0
  },
  {
    "predicate": "Hosts: [**.loadbalancerclient.org]",
    "route_id": "load_balancer_client_test",
    "filters": [
      "[[AddResponseHeader X-Response-Default-Foo = 'Default-Bar'], order = 1]",
      "[[PrefixPath prefix = '/httpbin'], order = 2]"
    ],
    "uri": "lb://testservice",
    "order": 0
  },
  {
    "predicate": "Hosts: [**.loadbalancerclientempty.org]",
    "route_id": "load_balancer_client_empty_test",
    "filters": [
      "[[AddResponseHeader X-Response-Default-Foo = 'Default-Bar'], order = 1]",
      "[[PrefixPath prefix = '/httpbin'], order = 2]"
    ],
    "uri": "lb://serviceshouldnotexistanywhere",
    "order": 0
  },
  {
    "predicate": "(Method: GET && Hosts: [**.method.org])",
    "route_id": "method_test",
    "filters": [
      "[[AddResponseHeader X-Response-Default-Foo = 'Default-Bar'], order = 1]",
      "[[PrefixPath prefix = '/httpbin'], order = 2]"
    ],
    "uri": "lb://testservice",
    "order": 0
  },
  {
    "predicate": "(Hosts: [**.notbetween.org] && Between: 1000-03-13T10:39:37.518-04:56:02[America/New_York] and 1002-03-13T10:39:37.518-04:56:02[America/New_York])",
    "route_id": "not_between_test",
    "filters": [
      "[[AddResponseHeader X-Response-Default-Foo = 'Default-Bar'], order = 1]",
      "[[PrefixPath prefix = '/httpbin'], order = 2]"
    ],
    "uri": "lb://testservice",
    "order": 0
  },
  {
    "predicate": "(Paths: [/{org}/{scope}/function], match trailing slash: false && Hosts: [**.path.org])",
    "route_id": "path_test",
    "filters": [
      "[[AddResponseHeader X-Response-Default-Foo = 'Default-Bar'], order = 1]",
      "[[SetPath template = '/anything/{org}{scope}'], order = 1]",
      "[[PrefixPath prefix = '/httpbin'], order = 2]"
    ],
    "uri": "lb://testservice",
    "order": 0
  },
  {
    "predicate": "(Paths: [/{org}/{scope}/function], match trailing slash: true && Hosts: [**.path.org])",
    "route_id": "path_test_two",
    "filters": [
      "[[AddResponseHeader X-Response-Default-Foo = 'Default-Bar'], order = 1]",
      "[[SetStatus status = '404'], order = 1]",
      "[[PrefixPath prefix = '/httpbin'], order = 2]"
    ],
    "uri": "lb://testservice",
    "order": 0
  },
  {
    "predicate": "(Hosts: [**.pathmulti.org] && Paths: [/anything/multi1{num}, /anything/multi2{num}], match trailing slash: true)",
    "route_id": "path_multi",
    "filters": [
      "[[AddResponseHeader X-Response-Default-Foo = 'Default-Bar'], order = 1]",
      "[[SetPath template = '/anything/multi{num}'], order = 1]",
      "[[PrefixPath prefix = '/httpbin'], order = 2]"
    ],
    "uri": "lb://testservice",
    "order": 0
  },
  {
    "predicate": "Hosts: [**.redirectto.org]",
    "route_id": "redirect_to_test",
    "filters": [
      "[[AddResponseHeader X-Response-Default-Foo = 'Default-Bar'], order = 1]",
      "[[RedirectTo 302 = https://example.org], order = 1]",
      "[[PrefixPath prefix = '/httpbin'], order = 2]"
    ],
    "uri": "lb://testservice",
    "order": 0
  },
  {
    "predicate": "(Hosts: [**.removerequestheader.org] && Paths: [/headers], match trailing slash: true)",
    "route_id": "remove_request_header_test",
    "filters": [
      "[[AddResponseHeader X-Response-Default-Foo = 'Default-Bar'], order = 1]",
      "[[RemoveRequestHeader name = 'X-Request-Foo'], order = 1]",
      "[[PrefixPath prefix = '/httpbin'], order = 2]"
    ],
    "uri": "lb://testservice",
    "order": 0
  },
  {
    "predicate": "(Hosts: [**.removereresponseheader.org] && Paths: [/headers], match trailing slash: true)",
    "route_id": "remove_response_header_test",
    "filters": [
      "[[AddResponseHeader X-Response-Default-Foo = 'Default-Bar'], order = 1]",
      "[[AddResponseHeader X-Request-Foo = 'Bar'], order = 1]",
      "[[PrefixPath prefix = '/httpbin'], order = 2]",
      "[[RemoveResponseHeader name = 'X-Request-Foo'], order = 2]"
    ],
    "uri": "lb://testservice",
    "order": 0
  },
  {
    "predicate": "Paths: [/retry], match trailing slash: true",
    "route_id": "retry_test",
    "filters": [
      "[[AddResponseHeader X-Response-Default-Foo = 'Default-Bar'], order = 1]",
      "[[Retry retries = 3, series = list[SERVER_ERROR], statuses = list[[empty]], methods = list[GET], exceptions = list[IOException, TimeoutException]], order = 1]",
      "[[PrefixPath prefix = '/httpbin'], order = 2]"
    ],
    "uri": "lb://testservice",
    "order": 0
  },
  {
    "predicate": "Hosts: [**.retrypostconfig.org]",
    "route_id": "retry_post_test",
    "filters": [
      "[[AddResponseHeader X-Response-Default-Foo = 'Default-Bar'], order = 1]",
      "[[Retry retries = 3, series = list[SERVER_ERROR], statuses = list[[empty]], methods = list[GET, POST], exceptions = list[IOException, TimeoutException]], order = 1]",
      "[[PrefixPath prefix = '/httpbin'], order = 2]"
    ],
    "uri": "lb://testservice",
    "order": 0
  },
  {
    "predicate": "(Hosts: [**.secureheaders.org] && Paths: [/headers], match trailing slash: true)",
    "route_id": "secure_headers_test",
    "filters": [
      "[[AddResponseHeader X-Response-Default-Foo = 'Default-Bar'], order = 1]",
      "[[SecureHeaders], order = 1]",
      "[[PrefixPath prefix = '/httpbin'], order = 2]"
    ],
    "uri": "lb://testservice",
    "order": 0
  },
  {
    "predicate": "(Hosts: [**.setpath.org] && Paths: [/foo/{segment}], match trailing slash: true)",
    "route_id": "set_path_test",
    "filters": [
      "[[AddResponseHeader X-Response-Default-Foo = 'Default-Bar'], order = 1]",
      "[[SetPath template = '/{segment}'], order = 1]",
      "[[PrefixPath prefix = '/httpbin'], order = 2]"
    ],
    "uri": "lb://testservice",
    "order": 0
  },
  {
    "predicate": "Hosts: [{subdomain}.setpathhost.org]",
    "route_id": "set_path_host_test",
    "filters": [
      "[[AddResponseHeader X-Response-Default-Foo = 'Default-Bar'], order = 1]",
      "[[SetPath template = '/{subdomain}'], order = 1]",
      "[[PrefixPath prefix = '/httpbin'], order = 2]"
    ],
    "uri": "lb://testservice",
    "order": 0
  },
  {
    "predicate": "(Hosts: [**.stripprefix.org] && Paths: [/foo/**], match trailing slash: true)",
    "route_id": "strip_prefix_test",
    "filters": [
      "[[AddResponseHeader X-Response-Default-Foo = 'Default-Bar'], order = 1]",
      "[[StripPrefix parts = 2], order = 1]",
      "[[PrefixPath prefix = '/httpbin'], order = 2]"
    ],
    "uri": "lb://testservice",
    "order": 0
  },
  {
    "predicate": "(Hosts: [**.setreresponseheader.org] && Paths: [/headers], match trailing slash: true)",
    "route_id": "set_response_header_test",
    "filters": [
      "[[AddResponseHeader X-Response-Default-Foo = 'Default-Bar'], order = 1]",
      "[[AddResponseHeader X-Request-Foo = 'Bar1'], order = 1]",
      "[[PrefixPath prefix = '/httpbin'], order = 2]",
      "[[AddResponseHeader X-Request-Foo = 'Bar2'], order = 2]",
      "[[SetResponseHeader X-Request-Foo = 'Bar'], order = 3]"
    ],
    "uri": "lb://testservice",
    "order": 0
  },
  {
    "predicate": "(Hosts: [**.setstatusint.org] && Paths: [/headers], match trailing slash: true)",
    "route_id": "set_status_int_test",
    "filters": [
      "[[AddResponseHeader X-Response-Default-Foo = 'Default-Bar'], order = 1]",
      "[[SetStatus status = '401'], order = 1]",
      "[[PrefixPath prefix = '/httpbin'], order = 2]"
    ],
    "uri": "lb://testservice",
    "order": 0
  },
  {
    "predicate": "(Hosts: [**.setstatusstring.org] && Paths: [/headers], match trailing slash: true)",
    "route_id": "set_status_string_test",
    "filters": [
      "[[AddResponseHeader X-Response-Default-Foo = 'Default-Bar'], order = 1]",
      "[[SetStatus status = 'BAD_REQUEST'], order = 1]",
      "[[PrefixPath prefix = '/httpbin'], order = 2]"
    ],
    "uri": "lb://testservice",
    "order": 0
  },
  {
    "predicate": "(Hosts: [**.baz.org] && RemoteAddrs: [127.0.0.1/24])",
    "route_id": "rewrite_path_test",
    "filters": [
      "[[AddResponseHeader X-Response-Default-Foo = 'Default-Bar'], order = 1]",
      "[[RewritePath /foo/(?<segment>.*) = '/${segment}'], order = 1]",
      "[[PrefixPath prefix = '/httpbin'], order = 2]",
      "[[AddRequestHeader X-Request-Foo = 'Bar'], order = 2]",
      "[[AddRequestHeader X-Request-Baz = 'Bat'], order = 3]"
    ],
    "uri": "lb://testservice",
    "order": 0
  },
  {
    "predicate": "(Hosts: [**.weighthigh.org] && Weight: group1 8)",
    "route_id": "weight_high_test",
    "filters": [
      "[[AddResponseHeader X-Response-Default-Foo = 'Default-Bar'], order = 1]",
      "[[PrefixPath prefix = '/httpbin'], order = 2]"
    ],
    "uri": "lb://testservice",
    "order": 0
  },
  {
    "predicate": "Header: Foo regexp=.*",
    "route_id": "header_test",
    "filters": [
      "[[AddResponseHeader X-Response-Default-Foo = 'Default-Bar'], order = 1]",
      "[[PrefixPath prefix = '/httpbin'], order = 2]"
    ],
    "uri": "lb://testservice",
    "order": 0
  },
  {
    "predicate": "(Hosts: [**.changeuri.org] && Header: X-CF-Forwarded-Url regexp=null)",
    "route_id": "change_uri_test",
    "filters": [
      "[[AddResponseHeader X-Response-Default-Foo = 'Default-Bar'], order = 1]",
      "[[PrefixPath prefix = '/httpbin'], order = 2]",
      "[RequestHeaderToRequestUri name = 'X-CF-Forwarded-Url']"
    ],
    "uri": "lb://testservice",
    "order": 0
  },
  {
    "predicate": "((Header: X-CF-Forwarded-Url regexp=.* && Header: X-CF-Proxy-Signature regexp=.*) && Header: X-CF-Proxy-Metadata regexp=.*)",
    "route_id": "cloudfoundry_routeservice_test",
    "filters": [
      "[[AddResponseHeader X-Response-Default-Foo = 'Default-Bar'], order = 1]",
      "[[PrefixPath prefix = '/httpbin'], order = 2]",
      "[RequestHeaderToRequestUri name = 'X-CF-Forwarded-Url']"
    ],
    "uri": "lb://testservice",
    "order": 0
  },
  {
    "predicate": "Paths: [/**], match trailing slash: true",
    "route_id": "default_path_to_httpbin",
    "filters": [
      "[[AddResponseHeader X-Response-Default-Foo = 'Default-Bar'], order = 1]",
      "[[PrefixPath prefix = '/httpbin'], order = 2]"
    ],
    "uri": "lb://testservice",
    "order": 10000
  }
]

@spencergibb
Copy link
Member Author

spencergibb commented Jul 22, 2019

// 20190723103321
// http://localhost:9000/actuator/gateway/routes/host_foo_path_headers_to_httpbin

{
  "predicate": "(((((((Hosts: [**.foo.org] && Paths: [/headers], match trailing slash: true) && Method: GET) && Header: X-Request-Id regexp=\\d+) && Query: param=foo regexp=ba.) && Query: param=baz regexp=null) && Cookie: name=chocolate regexp=ch.p) && After: 1900-01-20T17:42:47.789-07:00[America/Denver])",
  "route_id": "host_foo_path_headers_to_httpbin",
  "filters": [
    "[[AddResponseHeader X-Response-Default-Foo = 'Default-Bar'], order = 1]",
    "[[AddResponseHeader X-Response-Foo = 'Bar'], order = 1]",
    "[[PrefixPath prefix = '/httpbin'], order = 2]"
  ],
  "uri": "lb://testservice",
  "order": 0
}

@spencergibb
Copy link
Member Author

@ryanjbaxter are you sure you read all of the changes? ;-)

@ryanjbaxter
Copy link
Contributor

@spencergibb with a fine tooth comb!!!

Think this is one of those PRs where I am going to have to trust you ;)

But I do like the output!

@spencergibb
Copy link
Member Author

The first commit has the meat. The rest is updating all filters and predicates to add toString().

Copy link
Contributor

@TYsewyn TYsewyn left a comment

Choose a reason for hiding this comment

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

Other than those 2 comments I got nothing else! 👍

@spencergibb spencergibb merged commit 1cf449c into 2.1.x Jul 23, 2019
@spencergibb spencergibb deleted the filter-predicate-to-string branch July 23, 2019 15:31
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.

None yet

4 participants