Is there a way to use @stoplight/prism-cli to validate response without using client?

The validation logic of stoplight/prism is extremely powerful. I’m looking for other ways to access this logic without going through the full http-client that the prism-cli provides (see documentation).

I want to take a response object (either created myself, or generated from some other tool) and apply prism’s validation logic against it (without needing to actually create a client server). I’m looking at the validators within the GitHub repo and trying to figure out how things plug together, but it’s been slow moving. Figured I’d ask the experts to see if this is possible.

The primary use case is to take advantage of prism validation when stubbing requests in Cypress. Cypress allows you to easily stub a response to a route, but the downside is not knowing if it’s realistic. Combining cypress’s stubbing capabilities with prism would be a killer combination.

Hey @tylerscottnielsen

I have a good and a bad news.

The bad news is that unfortunately Prism’s validators — although powerful as you claim (and thank you for that) are unjustifiably complected.

Not too sure how much you’ve been able to look into the source code; in the odd chances you did, you might have realised that the validator is complected with the deserialisation logic:


This is a leftover of the previous V3 version that was taking in place; you can find indeed how much I’ve been struggling with this looking at this article I wrote. (P.S: I suggest to read the entire series).


The good news — I’m done to look at this code and crying every single time; so while it is not a top priority, I have started to work on this:

The first two steps have been to get rid of the class based approach for the validators as well as for the deserialisers:


Once these have been merged, I suspect it’s going to be kind of easy to break the two pieces and make a single, simple interface these two components would talk between each other.

I suggest you to subscribe to the relative issue and monitor these PRs. I am confident we can get to something usable in a reasonable amount of time.

Hopefully that clarifies a little bit the situation.

Cheers,

V.

Hey @vncz - thanks for the reply and all the information. I actually was successful in pulling out the various classes I needed for this to work along cypress. Happy to say I’m able to stub out responses to my hearts content with the assurance that Prism will tell me if I’ve done something out of line from the spec!

I ended up pulling the getOperationsFromSpec method. From there I transform the httpRequest object from Cypress to adhere to the one that your router expects to get the matched operation. After that, I use the validateInput and ValidateOutput functions to get my results. Now I have a method that takes the cypress request and the spec, and it returns the prismDiagnostic array. It ended up being a little digging, but actually not that bad in the end.

I was going to post the solution back here or write up an article, since as I said, this is a pretty powerful combination. Even Cypress’s own documentation says the tradeoff between using the live services and using stubs is knowing you’re adhering to the spec. Now you can do both (mostly).

I’ll probably wait until those refactorings are done though, since it will likely change what I’m doing. Maybe at that point I can write a simple plugin to install to help people use.

Thanks for the info!

Oh ok interesting, I guess you were not only looking for validation but as well deserialisation.

Can you show some code just to see what you did? Curious to see how you dealt with the Either instance returned by the validation process.

Sure thing, here is the function handles the validation from the spec using the cypress request. The logic to hook into cypress’s stubbing utilizes this to do validation. Handling the Either took me a while, as I wasn’t familiar with this pattern/library. Also, note that as Cypress intentionally doesn’t allow async/await (due to the way they use the promises), I’m not using that language feature here.

import { validateOutput, validateInput } from '@stoplight/prism-http/dist/validator';
import { getHttpOperationsFromSpec } from '@stoplight/prism-cli/dist/operations';
import route from '@stoplight/prism-http/dist/router';
import { fold } from 'fp-ts/lib/Either';
import { pipe } from 'fp-ts/function';
import { ICypressXhrHttpRequest } from '.';
import { HttpMethod, IHttpOperation, IHttpResponse } from '@stoplight/types';
import { IHttpUrl } from '@stoplight/prism-http/dist/types';
import { IPrismDiagnostic } from '@stoplight/prism-core/dist/types';
import { Promise } from 'bluebird';

export function validateResponse(xhr: ICypressXhrHttpRequest, spec: string): Promise<IPrismDiagnostic[]> {
  return new Promise<IPrismDiagnostic[]>(resolve => {
    // This is a helper function to use the cypress request in a way prism will understand.
    getOperation(xhr, spec).then(matchedOperation => {
      const validationResult = validateOutput({
        resource: matchedOperation,
        element: { statusCode: xhr.status, ...xhr.response },
      });
      console.log(JSON.stringify(validationResult, null, 2));

      pipe(
        validationResult,
        fold(
          error => resolve(error),
          success => resolve([])
        )
      );
    });
  });
}

Basically, I’m just resolving the either to Either return the array of IPrismDiagnostic objects, or an empty array. The calling code treats an empty array as no validation errors.

Ok nice — you have correctly folded over the Either monad instance. Great to see this.

Not too sure if you’ve subscribed to the two PRs, but the first one is in already and likely the second one will make it tomorrow morning.

Cheers!

Yep, I’m subscribed - you’re speedy! Thanks for all the cleanup. I’ll let you know if I run into any issues following the new structure once I upgrade. Might take me a while as I moved on to a few other things that are high priority, but I’ll come back around in the next few weeks to update this.