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

Better URL Pattern Matching #47

Closed
MPiccinato opened this issue Apr 6, 2016 · 5 comments · Fixed by #64
Closed

Better URL Pattern Matching #47

MPiccinato opened this issue Apr 6, 2016 · 5 comments · Fixed by #64

Comments

@MPiccinato
Copy link

I have setup a handful of routes such as:

 api.configureTransformer("/products/filters") { returns Filter }
 api.configureTransformer("/products/*") { returns Product }
 api.configureTransformer("/products/*/market") { returns Market }

When the '/products/filters' resource is loaded it handles it then passes 'Filter' objects into the 'products/*' handler which expects a JSON (SwiftyJSON) object. So of course the 'WrongTypeInTranformerPipeline' error pops up.

It would be nice to either allow a ResponseTransformer to say "no more transforming, we are done" or allow for better pattern matching in the URLs such as specifying the wildcard item should be an INT.

@o15a3d4l11s2
Copy link

+1

@pcantrell
Copy link
Member

Yes, this is a bit of a headache with the current configuration approach.

For right now, you can use the configuration flavor that takes an arbitrary predicate to make more fine-grained matches. For example (just a sketch, untested):

api.configureTransformer({ $0.path?.rangeOfString("/products/\\d+$", options: .RegularExpressionSearch) != nil }) {  }
api.configureTransformer("/products/filters") {  }
api.configureTransformer("/products/*/market") {  }

(Ugh, that Foundation regex API. Hmm, Siesta could — should — make NSRegularExpression implement ConfigurationPatternConvertible.)


The most obvious better solution is a “first match wins” policy in the manner of a routing engine, but that doesn’t work because configuration blocks aren’t mutually exclusive: you may want to have several independent config passes match the same resource.

The “no more transforming, we’re done” approach would solve this immediate problem, but feels like it won’t generalize well, and will lead to more special cases.

A slightly more generic version of it I’ve considered would allow you to give configuration blocks a key, where a new config block replaces one with the same key if it exists:

api.configureTransformer("/products/*", inSlot: .ModelTransform) {  }
api.configureTransformer("/products/filters", inSlot: .ModelTransform) {  }  // replaces first one
api.configureTransformer("/products/**", inSlot: .FastTimeout) {  }  // does not override first one

This would help #32 play out right. The implementation details get messy, though, and I’m not sure it’s all that easy to understand.

Another approach would be to create configuration groups, within which the first match wins:

api.configurationGroup { group in
  group.configureTransformer("/products/filters") {  }
  group.configureTransformer("/products/*") {  }
}

Again, though, messy details and maybe hard to follow.

@enricode
Copy link

I'm running in a simpler problem that maybe has already a solution that I can't see.

I have two endpoints:

  • products/
  • products/[id]

Configuring them like:

service.configureTransformer("products/") {
...
}
service.configureTransformer("products/*") {
...
}

Siesta catches the latter also on "products/" call (and it's correct, I think).
Changing the endpoint "products/" to "products" brings an OAuth 401 because Authorization header isn't added to request and I don't really know why (maybe it's a bug?)*

So the only solution that came to my mind was:

service.configureTransformer(ProductIntPattern()) { ... }

struct ProductIntPattern: ConfigurationPatternConvertible
{
  func configurationPattern(service: Service) -> NSURL -> Bool {
    return {
      if let components = $0.pathComponents where components.count > 1 {
        return (components[components.count - 2] == "products" && Int(components.last!) != nil)
      }
      return false
    }
  }
  var configurationPatternDescription: String {
    return ""
  }
}

That really isn't so good as something like:
service.configureTransformer("products/:id")


* authorization is configured in this way

service.configure {
  $0.config.headers["Authorization"] = self.authHeader
}

@Alex293
Copy link
Contributor

Alex293 commented May 17, 2016

Hi, first great work ; this lib is really changing the way we do networks in iOS apps !

Any updates on this issue ?
i.e. I will have to use urls like /users/* /users/1 and /users/1/comments and I don't want to have to handle comments in /users/* resource. Should I use your proposal with reg ex (will it work) ?

@pcantrell
Copy link
Member

pcantrell commented Jun 21, 2016

This should now work if you pull the bleeding edge from master:

api.configureTransformer("/products/*") { returns Product }
api.configureTransformer("/products/filters") { returns Filter }  // overrides previous; order matters!
api.configureTransformer("/products/*/market") { returns Market }  // not matched by either of previous 2 rules

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 a pull request may close this issue.

5 participants