1

I'm currently facing an issue when trying to get my standalone WireMock to match a GET request with a certain path pattern using a regex with a negative lookahead:

{
    "request": {
      "method": "GET",
      "urlPathPattern": "\/my\/interesting\/path\/(\\?![0-9]*$)(\b[0-9A-Z]{11}\b)"
    },
    "response": {
      "status": 200,
      "body": "",
      "headers": {
        "Content-Type": "application/json"
      }
    }
  }

When checking the WireMock logs, a near miss is logged. As suggested by WireMock, I escaped the question mark operator within my regex with a double backslash. Though, this did not help either.

I expect the url path pattern to match urls that ends with an alphanumeric, eleven character uppercase string, such as:

http://myapp:8080/my/interesting/path/ABCDEF12345

I've already checked if my regex is valid and matches the cases that I would expect it to, which it does.

What might be of use: I'm using WireMock version 2.33.2 (docker image wiremock/wiremock:2.33.2)

Martin H.
  • 538
  • 1
  • 7
  • 21
  • I think there is a typo in the syntax for the lookahead `\/my\/interesting\/path\/(?![0-9]*$)([0-9A-Z]{11})\b` See https://regex101.com/r/Lz031p/1 – The fourth bird Jul 22 '22 at 14:20
  • Both your and my version of regex seem to work according to regex101. I tried your version of the regex as well, but it did not help. I'm still receiving a `URL does not match. When using a regex, "?" should be "\\?"` error. – Martin H. Jul 22 '22 at 14:32
  • In the question there is `(\\?![0-9]*$)` in the pattern, but the lookahead syntax is `(?![0-9]*$)` Did you try it like that? Are you sure the lookahead is supported? Is there documentation or do you know the regex flavor? – The fourth bird Jul 22 '22 at 14:33
  • This does not match 11 chars in the group, but if the lookahead does not work you can match at least a single char A-Z `\/my\/interesting\/path\/([0-9]*[A-Z][A-Z0-9]*)\b` – The fourth bird Jul 22 '22 at 14:38
  • yes, I tried the version suggested by WireMock (with double back slashes in front of the question mark in the lookahead) as well as your version. – Martin H. Jul 22 '22 at 14:38
  • Note sure, but if it supports the Java engine `"/my/interesting/path/(?![0-9]*$)(\\b[0-9A-Z]{11}\\b)";` – The fourth bird Jul 22 '22 at 14:55
  • To me it seems like an issue of the way I'm currently specifying the regex for WireMock to be able to interprete/handle it correctly. – Martin H. Jul 23 '22 at 10:53

1 Answers1

3

If I understand you right, you need to match an 11-character string at the end, that consists of uppercase letters and digits only but does not consist of digits only, right? If WireMock's regex engine does not support negative lookahead - and that's what it looks like, if it doesn't match your regex but isn’t all that surprising neither, since there are quite a view regex implementations that do not support look-aheads), you have two choices.

  1. You can create 11 different possible endings and put them together with | looking for a letter at each of the 11 positions like this:

/my/interesting/path/([A-Z][A-Z0-9]{10}|[A-Z0-9][A-Z][A-Z0-9]{9}|[A-Z0-9]{2}[A-Z][A-Z0-9]{8}|[A-Z0-9]{3}[A-Z][A-Z0-9]{7}|[A-Z0-9]{4}[A-Z][A-Z0-9]{6}|[A-Z0-9]{5}[A-Z][A-Z0-9]{5}|[A-Z0-9]{6}[A-Z][A-Z0-9]{4}|[A-Z0-9]{7}[A-Z][A-Z0-9]{3}|[A-Z0-9]{8}[A-Z][A-Z0-9]{2}|[A-Z0-9]{9}[A-Z][A-Z0-9]|[A-Z0-9]{10}[A-Z])$

or

  1. You use priorities and define three matches (taking your comment of not allowing 11 digits to match a digit-only ending into account) like this:
{
    "priority": 1,
    "request": {
      "method": "GET",
      "urlPathPattern": "/my/interesting/path/[0-9]{11}$"
    },
    "response": {
      "status": 404,
      "body": "",
      "headers": {}
    }
  }

and

{
    "priority": 2,
    "request": {
      "method": "GET",
      "urlPathPattern": "/my/interesting/path/[0-9]*$"
    },
    "response": {
      "status": 200,
      "body": "whatever is necessary for the digits-only or empty url",
      "headers": {
        "Content-Type": "application/json"
      }
    }
  }

and

{
    "priority": 3,
    "request": {
      "method": "GET",
      "urlPathPattern": "/my/interesting/path/[0-9A-Z]{11}$"
    },
    "response": {
      "status": 200,
      "body": "",
      "headers": {
        "Content-Type": "application/json"
      }
    }
  }

The first match (priority 1) will pick up any URL that ends in 11 digits so that the second one is never tried for 11 digits. The third match (priority 3) will then only be tried, if the first one (priority 1) and second one (priority 2) did not match, thus guaranteeing there are not just digits if the third one matches.

The priority field is documented in the 'stubbing' part of the WireMock documentation: https://wiremock.org/docs/stubbing/

Hope that get's you going...

Frank
  • 884
  • 2
  • 10
  • Hey Frank, yes you perceived my question the right way. I was hoping on a solution that gets the negative lookahead to work. Seems a bit odd to me that WireMock is not able to interpret it correctly. Regarding your suggestions: Option 2 is not working for my case, as the endpoint can potentially be called with numbers consisting of 11 digits, resulting in the example with `priority: 1` to match, which I don't want to happen. Though, suggested solution 1 seems like an adequate alternative for the negative lookahead. It would help if you could supply the full regex in that case :) – Martin H. Jul 30 '22 at 10:42
  • You could define 3 priority matches: 1. 11 digits, 2. any number of digits, 3. the 11 alphanumeric. That would avoid the second matching 11 digits. As for the 1. option‘s full regex… I’ll edit my answer accordingly. ;-) – Frank Jul 30 '22 at 16:17
  • Thanks Frank, the regex in option 1 seems to be perfectly fit my needs! Option 2 needs a little tweak to make it work for my case. For instance, I'd need `/my/interesting/path/12345678901` to result in a 404 response instead of a 200. You can think of it in way that the system is being asked about a product which is in a product category that is has no knowledge about, hence returning a 404 which stands for "I can't find the product you are looking for". In contrast to that, `/my/interesting/path/AB345678901` needs to result in a match returning a 200 as it knows about those products. – Martin H. Jul 30 '22 at 20:26
  • 1
    @MartinH. Not sure what the problem is with option 2 then (just changed my answer to return 404 in case of `/my/interesting/path/AB345678901`) but when the regex from Option 1 is doing what you need, then that’s all that counts. – Frank Jul 31 '22 at 04:53
  • It wasn't really a problem. I just needed different results regarding response codes for the same REST endpoint depending on the type of product number being supplied (11 character digits only vs. 11 character alphanumeric). The example with `"priority": 2` could also return a 404 for my specific case. The way you adjusted option 2 in your answer now is fine as well though. The way you used priorities to solve the problem was an eye opener for me and that solution works just as good as option 1. Appreciate that you came up with two solutions for this issue. – Martin H. Jul 31 '22 at 10:23