Challenge #3: Parsing Responses

Your Rot13Client makes an HTTP request, but it doesn’t do anything with the response. In this challenge, you’ll parse the response and return the encoded text to your caller. You’ll only handle the happy path for this challenge.

Instructions

1. Update the transformAsync() test helper to support specifying HTTP parameters:

  1. Update the transformAsync() to the following signature:
    const { response, httpRequests } = await transformAsync({
    	port,                 // the port to pass to rot13Client.transformAsync()
    	text,                 // the text to pass to rot13Client.transformAsync()
    	correlationId,        // the correlation ID to pass to rot13Client.transformAsync()
    	rot13ServiceStatus,   // the HTTP status returned by the simulated ROT-13 service
    	rot13ServiceHeaders,  // the HTTP headers returned by the simulated ROT-13 service
    	rot13ServiceBody,     // the HTTP body returned by the simulated ROT-13 service
    });
  2. Provide defaults for the transformAsync() parameters:
    const { response, httpRequests } = await transformAsync({
    	port = IRRELEVANT_PORT,
    	text = IRRELEVANT_TEXT,
    	correlationId = IRRELEVANT_CORRELATION_ID,
    	rot13ServiceStatus = VALID_ROT13_STATUS,
    	rot13ServiceHeaders = VALID_ROT13_HEADERS,
    	rot13ServiceBody = VALID_ROT13_BODY,
    } = {});
  3. Configure the /rot13/transform endpoint to return VALID_ROT13_STATUS, VALID_ROT13_HEADERS, and VALID_ROT13_BODY.

2. Implement the "parses response" test:

  1. Assert that the rot13Client.transformAsync() response is VALID_RESPONSE.

3. Update rot13Client.transformAsync():

  1. Parse HTTP response’s body and return the transformed text. The body is a JSON-encoded object:
    {	transformed: "the encoded text" }
  2. Don’t worry about edge cases—just program the happy path.

Remember to commit your changes when you’re done.

API Documentation

const client = HttpClient.createNull({ endpoints });

Create a Nulled HttpClient that simulates a response to the specified endpoints. The name of each endpoint is the URL and the value is an array of simulated responses. Each response consists of status, headers, and body. The next response in the array is returned for each request.

const client = HttpClient.createNull({
	"/rot13/transform": [{
		status,   // number
		headers,  // object with a name/value pair for each header
		body,     // string
	}],
});
  • endpoints (number) - simulated responses for each endpoint
  • returns httpClient (HttpClient) - the HTTP client
const object = JSON.parse(json);

Convert a JSON-encoded string to a variable (typically an object).

  • json (string) - the JSON string
  • returns object (object) - the variable
  • throws (Error) - throws an error if the string could not be parsed
assert.equal(actual, expected);

Assert that two variables are strictly equal (===). This is usually used for primitive types. To compare objects and arrays, including their contents, use assert.deepEqual() instead.

  • actual (any) - the actual value
  • expected (any) - the expected value
const VALID_ROT13_STATUS = 200;

This constant is available in the test code. Use it when you need to simulate a valid HTTP response from the ROT-13 service.

const VALID_ROT13_HEADERS = { "content-type": "application/json" };

This constant is available in the test code. Use it when you need to simulate a valid HTTP response from the ROT-13 service.

const VALID_ROT13_BODY = JSON.stringify({ transformed: VALID_RESPONSE });

This constant is available in the test code. Use it when you need to simulate a valid HTTP response from the ROT-13 service.

const VALID_RESPONSE = "transformed_text";

This constant is available in the test code. It corresponds to the response rot13Client.transformAsync() should return when it receives VALID_ROT13_BODY from the ROT-13 service.

JavaScript Primers

Hints

Modify the transformAsync() helper:

1
The transformAsync() helper needs to return a response variable that contains the result of calling rot13Client.transformAsync().
You can use object shorthand to do so.
async function transformAsync({ port, text, correlationId }) {
	const httpClient = HttpClient.createNull();
	const httpRequests = httpClient.trackRequests();
	const rot13Client = new Rot13Client(httpClient);

	const response = await rot13Client.transformAsync(port, text, correlationId);

	return { response, httpRequests };
}
2
Add the ROT-13 parameters to the helper. (rot13ServiceStatus, rot13ServiceHeaders, and rot13ServiceBody.) Add the default function parameters at the same time.
async function transformAsync({
	port = IRRELEVANT_PORT,
	text = IRRELEVANT_TEXT,
	correlationId = IRRELEVANT_CORRELATION_ID,
	rot13ServiceStatus = VALID_ROT13_STATUS,
	rot13ServiceHeaders = VALID_ROT13_HEADERS,
	rot13ServiceBody = VALID_ROT13_BODY,
} = {}) {
	const httpClient = HttpClient.createNull();
	const httpRequests = httpClient.trackRequests();
	const rot13Client = new Rot13Client(httpClient);

	const response = await rot13Client.transformAsync(port, text, correlationId);

	return { response, httpRequests };
}

TypeScript no longer needs type annotations, because of the defaults, so the TypeScript code is the same as the JavaScript code.

3
Configure the HTTP response.
You can do that with HttpClient.createNull().
It’s configured with an object that has endpoints (URLs) as keys and an array of responses as values. The endpoint to configure is /rot13/transform.
async function transformAsync({
	port = IRRELEVANT_PORT,
	text = IRRELEVANT_TEXT,
	correlationId = IRRELEVANT_CORRELATION_ID,
	rot13ServiceStatus = VALID_ROT13_STATUS,
	rot13ServiceHeaders = VALID_ROT13_HEADERS,
	rot13ServiceBody = VALID_ROT13_BODY,
} = {}) {
	const httpClient = HttpClient.createNull({
		"/rot13/transform": {
			status: rot13ServiceStatus,
			headers: rot13ServiceHeaders,
			body: rot13ServiceBody,
		},
	});
	const httpRequests = httpClient.trackRequests();
	const rot13Client = new Rot13Client(httpClient);

	const response = await rot13Client.transformAsync(port, text, correlationId);

	return { response, httpRequests };
}

Implement the "parses response" test:

4
Call the production code in your test.
You can use the transformAsync() helper.
Although the values needed by the test are the same as the helper’s defaults, that’s just a coincidence. Protect against future changes and make your test more clear by making the values explicit.
it("parses response", async () => {
	const { response } = await transformAsync({
		rot13ServiceStatus: VALID_ROT13_STATUS,
		rot13ServiceHeaders: VALID_ROT13_HEADERS,
		rot13ServiceBody: VALID_ROT13_BODY,
	});
});
5
You’re ready to assert that the reponse is correct.
You can use assert.equal() to make the comparison.
it("parses response", async () => {
	const { response } = await transformAsync({
		rot13ServiceStatus: VALID_ROT13_STATUS,
		rot13ServiceHeaders: VALID_ROT13_HEADERS,
		rot13ServiceBody: VALID_ROT13_BODY,
	});

	assert.equal(response, VALID_RESPONSE);
});
6
The test is ready to run.
It should fail, saying that it got an undefined response.

That’s because the production code isn’t returning a value.

Implement the production code:

7
Your production code needs to parse and return the HTTP response.
The response is a JSON-encoded object containing a transformed field.
You can use JSON.parse() to convert the string into an object.
async transformAsync(port, text, correlationId) {
	const response = await this._httpClient.requestAsync({
		host: HOST,
		port,
		method: "POST",
		path: TRANSFORM_ENDPOINT,
		headers: {
			"content-type": "application/json",
			"x-correlation-id": correlationId,
		},
		body: JSON.stringify({ text }),
	});

	const parsedBody = JSON.parse(response.body);
	return parsedBody.transformed;
}

Complete Solution

Test code:
it("parses response", async () => {
	const { response } = await transformAsync({
		rot13ServiceStatus: VALID_ROT13_STATUS,
		rot13ServiceHeaders: VALID_ROT13_HEADERS,
		rot13ServiceBody: VALID_ROT13_BODY,
	});

	assert.equal(response, VALID_RESPONSE);
});

// ...

async function transformAsync({
	port = IRRELEVANT_PORT,
	text = IRRELEVANT_TEXT,
	correlationId = IRRELEVANT_CORRELATION_ID,
	rot13ServiceStatus = VALID_ROT13_STATUS,
	rot13ServiceHeaders = VALID_ROT13_HEADERS,
	rot13ServiceBody = VALID_ROT13_BODY,
} = {}) {
	const httpClient = HttpClient.createNull({
		"/rot13/transform": {
			status: rot13ServiceStatus,
			headers: rot13ServiceHeaders,
			body: rot13ServiceBody,
		},
	});
	const httpRequests = httpClient.trackRequests();
	const rot13Client = new Rot13Client(httpClient);

	const response = await rot13Client.transformAsync(port, text, correlationId);

	return { response, httpRequests };
}
Production code (JavaScript):
async transformAsync(port, text, correlationId) {
	const response = await this._httpClient.requestAsync({
		host: HOST,
		port,
		method: "POST",
		path: TRANSFORM_ENDPOINT,
		headers: {
			"content-type": "application/json",
			"x-correlation-id": correlationId,
		},
		body: JSON.stringify({ text }),
	});

	const parsedBody = JSON.parse(response.body);
	return parsedBody.transformed;
}
Production code (TypeScript):
async transformAsync(
	port: number,
	text: string,
	correlationId: string,
): Promise<string> {
	const response = await this._httpClient.requestAsync({
		host: HOST,
		port,
		method: "POST",
		path: TRANSFORM_ENDPOINT,
		headers: {
			"content-type": "application/json",
			"x-correlation-id": correlationId,
		},
		body: JSON.stringify({ text }),
	});

	const parsedBody = JSON.parse(response.body);
	return parsedBody.transformed;
}

Next challenge

Return to module overview