Class WireMockStubScanner

java.lang.Object
com.arc_e_tect.gradle.mirage.scan.WireMockStubScanner

public class WireMockStubScanner extends Object
Scans WireMock stub mapping files (*.json) for the request each one stubs, treating every stubbed request as an implemented endpoint.

Stub files are data, not code - this is a plain line/regex scan, deliberately without a JSON parser or the WireMock library itself on the classpath. One file is expected to describe one stub, the common convention for files stored under a mappings directory: the first method field and the first url/urlPath/urlPattern/ urlPathPattern/urlPathTemplate field found in the file are used.

urlPathTemplate is WireMock's path-template matcher, and already uses the OpenAPI-style "{name}" placeholders a declared/implemented endpoint's path uses, so it is read as a template as it stands.

urlPattern/urlPathPattern are WireMock regular expressions, not Spring/ OpenAPI path templates - see normalizePattern(String) for how a regex path-variable segment (e.g. "[0-9]+") is recognised and rewritten into the "{id}"-style placeholder a declared/implemented endpoint's path actually uses, so this scanner's stub evidence can be recognised as evidence for that endpoint instead of accumulating as a permanently unmatched, orphaned path in its own right.

url/urlPath are literal WireMock matchers, never regular expressions. When the scanner is given the declared endpoints, a literal path is first matched against them - see WireMockStubScanner(List, String) - and recorded as the declared template it is an instance of. Only a literal path that matches no declared template falls back to the heuristic below: a purely numeric segment (e.g. "700336" in "/persons/700336") is, just as heuristically, rewritten into a "{id}" placeholder too, by normalizeLiteral(String): a hand-written literal stub overwhelmingly picks an arbitrary example value to stand in for a path variable, the same way normalizePattern(String) already treats a regular expression's own numeric-id shape. This is a deliberately imprecise heuristic, not a certainty - a genuinely numeric literal path segment that isn't a path variable at all (e.g. a year in a URL) is indistinguishable from an example id by shape alone. Matching the declared templates first records it as its declared literal path; only a path declared nowhere, such as one only a controller implements, is rewritten the same way, which then fails to match that segment's own, genuinely literal, implemented path instead. See the plugin README's "Matching a stub's id segment to its declared/implemented endpoint's path variable" section for the accepted trade-off this represents.

  • Constructor Summary

    Constructors
    Constructor
    Description
    Creates a new WireMockStubScanner with no declared endpoints to match literal paths against: a literal path's segments are rewritten by the digits-only heuristic alone.
    WireMockStubScanner(List<com.arc_e_tect.gradle.detector.core.openapi.DescribedEndpoint> declared, String basePath)
    Creates a new WireMockStubScanner that matches a literal url/urlPath against declared before falling back to the digits-only heuristic.
  • Method Summary

    Modifier and Type
    Method
    Description
    List<com.arc_e_tect.gradle.detector.core.model.Endpoint>
    scan(File rootDir)
    Scans every .json file under rootDir, recursively, for the stubbed method and URL.

    Methods inherited from class java.lang.Object

    clone, equals, finalize, getClass, hashCode, notify, notifyAll, toString, wait, wait, wait
  • Constructor Details

    • WireMockStubScanner

      public WireMockStubScanner()
      Creates a new WireMockStubScanner with no declared endpoints to match literal paths against: a literal path's segments are rewritten by the digits-only heuristic alone.
    • WireMockStubScanner

      public WireMockStubScanner(List<com.arc_e_tect.gradle.detector.core.openapi.DescribedEndpoint> declared, String basePath)
      Creates a new WireMockStubScanner that matches a literal url/urlPath against declared before falling back to the digits-only heuristic.

      A literal path is recorded as a declared template of the stub's own method when it is an instance of it, segment by segment, with a declared placeholder accepting any value - so "/v1/users/aaaa" is recorded as "/v1/users/{username}". When it is an instance of several, the one routing would pick wins: at the first segment where two templates differ, a literal segment beats a placeholder, so "/users/me" is recorded as a declared "/users/me" rather than "/users/{id}". Where the stub's metadata names an operation, the declared operation with that operationId is preferred, as long as the path is an instance of its template.

      Parameters:
      declared - the declared endpoints, from the OpenAPI documentation; their paths never include a base path
      basePath - the base path a stub's path may carry in front of a declared path, or null when there is none; a matched path keeps it, so it is stripped the same way as every other stub path
  • Method Details

    • scan

      public List<com.arc_e_tect.gradle.detector.core.model.Endpoint> scan(File rootDir) throws IOException
      Scans every .json file under rootDir, recursively, for the stubbed method and URL.
      Parameters:
      rootDir - directory to search; may not exist, in which case an empty list is returned
      Returns:
      one Endpoint per stub file that has both a recognised method and URL
      Throws:
      IOException - if a stub file cannot be read