Skip to content

Commit

Permalink
Migrate cadl-ranch engine and supporting package (#4127)
Browse files Browse the repository at this point in the history
#4015

---------

Signed-off-by: dependabot[bot] <[email protected]>
Co-authored-by: Timothee Guerin <[email protected]>
Co-authored-by: Timothee Guerin <[email protected]>
Co-authored-by: m-nash <[email protected]>
Co-authored-by: Kyle Zhang <[email protected]>
Co-authored-by: Kyle Zhang <[email protected]>
Co-authored-by: Mario Guerra <[email protected]>
Co-authored-by: Crystal YU <[email protected]>
Co-authored-by: Jorge Rangel <[email protected]>
Co-authored-by: Mark Cowlishaw <[email protected]>
Co-authored-by: Srikanta <[email protected]>
Co-authored-by: Christopher Radek <[email protected]>
Co-authored-by: Christopher Radek <[email protected]>
Co-authored-by: Nisha Bhatia <[email protected]>
Co-authored-by: Wei Hu <[email protected]>
Co-authored-by: iscai-msft <[email protected]>
Co-authored-by: iscai-msft <[email protected]>
Co-authored-by: JoshLove-msft <[email protected]>
Co-authored-by: Dapeng Zhang <[email protected]>
Co-authored-by: Dapeng Zhang <[email protected]>
Co-authored-by: ShivangiReja <[email protected]>
Co-authored-by: ShivangiReja <[email protected]>
Co-authored-by: Mingzhe Huang <[email protected]>
Co-authored-by: Mingzhe Huang (from Dev Box) <[email protected]>
Co-authored-by: Allen Zhang <[email protected]>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Pan Shao <[email protected]>
Co-authored-by: Pan Shao <[email protected]>
Co-authored-by: Weidong Xu <[email protected]>
Co-authored-by: Xiaofei Cao <[email protected]>
Co-authored-by: Alan Zimmer <[email protected]>
Co-authored-by: Alitzel Mendez <[email protected]>
Co-authored-by: Rodge Fu <[email protected]>
  • Loading branch information
1 parent 66e847c commit 448a492
Show file tree
Hide file tree
Showing 84 changed files with 4,220 additions and 0 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -135,6 +135,7 @@ lib-cov

# Coverage directory used by tools like istanbul
coverage
!packages/spec-core/src/coverage

# nyc test coverage
.nyc_output
Expand Down
2 changes: 2 additions & 0 deletions packages/spec-api/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
# @typespec/spec-api

55 changes: 55 additions & 0 deletions packages/spec-api/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
{
"name": "@typespec/spec-api",
"version": "0.1.0",
"description": "Spec api to implement mock api",
"main": "dist/index.js",
"type": "module",
"private": true,
"scripts": {
"watch": "tsc -p ./tsconfig.build.json --watch",
"build": "tsc -p ./tsconfig.build.json",
"clean": "rimraf dist/ temp/",
"test": "vitest run"
},
"engines": {
"node": ">=18.0.0"
},
"repository": {
"type": "git",
"url": "git+https://github.com/microsoft/typespec.git"
},
"author": "Microsoft",
"license": "MIT",
"bugs": {
"url": "https://github.com/microsoft/typespec/issues"
},
"homepage": "https://github.com/microsoft/typespec#readme",
"dependencies": {
"body-parser": "^1.20.2",
"deep-equal": "^2.2.0",
"express": "^4.19.2",
"express-promise-router": "^4.1.1",
"morgan": "^1.10.0",
"multer": "^1.4.5-lts.1",
"picocolors": "~1.1.0",
"winston": "^3.8.2",
"prettier": "~3.3.3",
"xml2js": "^0.5.0",
"yargs": "~17.7.2"
},
"devDependencies": {
"@types/body-parser": "^1.19.2",
"@types/deep-equal": "^1.0.1",
"@types/express": "^4.17.17",
"@types/morgan": "^1.9.4",
"@types/multer": "^1.4.10",
"@types/node": "~22.5.4",
"@types/xml2js": "^0.4.11",
"@types/yargs": "~17.0.33",
"rimraf": "~6.0.1",
"typescript": "~5.6.2",
"vitest": "^2.1.0",
"@vitest/coverage-v8": "^2.1.0",
"@vitest/ui": "^2.1.0"
}
}
106 changes: 106 additions & 0 deletions packages/spec-api/src/expectation.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
import deepEqual from "deep-equal";
import {
validateBodyEmpty,
validateBodyEquals,
validateBodyNotEmpty,
validateCoercedDateBodyEquals,
validateHeader,
validateQueryParam,
validateRawBodyEquals,
validateXmlBodyEquals,
} from "./request-validations.js";
import { CollectionFormat, RequestExt } from "./types.js";
import { ValidationError } from "./validation-error.js";

/**
* Class containing all the expectations that can be run on the request.
*/
export class RequestExpectation {
public constructor(private originalRequest: RequestExt) {}
/**
* Expect the raw body of the request to match the given string.
* @param rawBody Raw request body.
* @throws {ValidationError} if there is an error.
*/
public rawBodyEquals(expectedRawBody: string | Buffer | undefined): void {
validateRawBodyEquals(this.originalRequest, expectedRawBody);
}

/**
* Expect the body of the request to match the given object.
* @param rawBody Raw request body.
* @throws {ValidationError} if there is an error.
*/
public bodyEquals(expectedRawBody: unknown | undefined): void {
validateBodyEquals(this.originalRequest, expectedRawBody);
}

/**
* Expect the coerced body of the request to match the given object.
* @param rawBody Raw request body.
* @throws {ValidationError} if there is an error.
*/
public coercedBodyEquals(expectedRawBody: unknown | undefined): void {
validateCoercedDateBodyEquals(this.originalRequest, expectedRawBody);
}

/**
* Expect the body of the request to be empty.
* @throws {ValidationError} if there is an error.
*/
public bodyEmpty(): void {
validateBodyEmpty(this.originalRequest);
}

/**
* Expect the body of the request to be not empty.
* @throws {ValidationError} if there is an error.
*/
public bodyNotEmpty(): void {
validateBodyNotEmpty(this.originalRequest);
}

/**
* Expect the header of the request contains the expected key/value pair
* @param headerName Key expected in header
* @param expectedValue Values expected in header
* @throws {ValidationError} if there is an error.
*/
public containsHeader(headerName: string, expectedValue: string): void {
validateHeader(this.originalRequest, headerName, expectedValue);
}

/**
* Expect the query string of the request contains the expected name/value pair.
* @param paramName Name of the query parameter.
* @param expectedValue Value expected of the query parameter.
*/
public containsQueryParam(
paramName: string,
expectedValue: string | string[],
collectionFormat?: CollectionFormat,
): void {
validateQueryParam(this.originalRequest, paramName, expectedValue, collectionFormat);
}

/**
* Check if two requests are equal
* @param actual Actual value
* @param expected Expected value
*/
public deepEqual(actual: unknown, expected: unknown, message = "Values not deep equal"): void {
if (!deepEqual(actual, expected, { strict: true })) {
throw new ValidationError(message, expected, actual);
}
}

/**
* Expect the body of the request to be semantically equivalent to the provided XML string.
* The XML declaration prefix will automatically be added to expectedBody.
* @param expectedBody expected value of request body.
* @throws {ValidationError} if there is an error.
*/
public xmlBodyEquals(expectedBody: string): void {
validateXmlBodyEquals(this.originalRequest, expectedBody);
}
}
39 changes: 39 additions & 0 deletions packages/spec-api/src/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
export { MockRequest } from "./mock-request.js";
export {
BODY_EMPTY_ERROR_MESSAGE,
BODY_NOT_EMPTY_ERROR_MESSAGE,
BODY_NOT_EQUAL_ERROR_MESSAGE,
validateBodyEmpty,
validateBodyEquals,
validateBodyNotEmpty,
validateCoercedDateBodyEquals,
validateHeader,
validateQueryParam,
validateRawBodyEquals,
validateValueFormat,
validateXmlBodyEquals,
} from "./request-validations.js";
export { json, xml } from "./response-utils.js";
export { mockapi } from "./routes.js";
export { WithKeysScenarioExpect, passOnCode, passOnSuccess, withKeys } from "./scenarios.js";
export {
CollectionFormat,
Fail,
HttpMethod,
KeyedMockApi,
KeyedMockRequestHandler,
KeyedMockResponse,
MockApi,
MockApiForHandler,
MockRequestHandler,
MockResponse,
MockResponseBody,
PassByKeyScenario,
PassOnCodeScenario,
PassOnSuccessScenario,
RequestExt,
ScenarioMockApi,
ScenarioPassCondition,
SimpleMockRequestHandler,
} from "./types.js";
export { ValidationError } from "./validation-error.js";
31 changes: 31 additions & 0 deletions packages/spec-api/src/mock-request.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
import { RequestExpectation } from "./expectation.js";
import { RequestExt } from "./types.js";

export class MockRequest {
public readonly expect: RequestExpectation;

public readonly baseUrl: string;
public readonly headers: { [key: string]: string };
public readonly query: { [key: string]: string | string[] };
public readonly params: { [key: string]: string };
public readonly body: any;
public readonly files?:
| {
[fieldname: string]: Express.Multer.File[];
}
| Express.Multer.File[]
| undefined;

public constructor(public originalRequest: RequestExt) {
this.baseUrl = getRequestBaseUrl(originalRequest);
this.expect = new RequestExpectation(originalRequest);
this.headers = originalRequest.headers as { [key: string]: string };
this.query = originalRequest.query as { [key: string]: string };
this.params = originalRequest.params as { [key: string]: string };
this.body = originalRequest.body;
this.files = originalRequest.files;
}
}

const getRequestBaseUrl = (request: RequestExt): string =>
`${request.protocol}://${request.get("host")}`;
Loading

0 comments on commit 448a492

Please sign in to comment.