Lets have an imaginary node core function that throws on invalid input:
function crop(image, x1, y1, x2, y2) {
// throws if x1, y1, x2, y2 out of range for the image.
}
We wrote the following classes using this function:
var Transforms = {};
class ImageCropper() {
constructor(range) {
this._range = range
}
// Checks whether the range passed to the constructor above is out of bounds
isValidOn(image) {
var range = this._range;
return 0 <= range.x1 && range.x1 < range.x2 && range.x2 <= image.width &&
0 <= range.y1 && range.y1 < range.y2 && range.y2 <= image.height
}
applyTo(image) {
if (!this.isValidOn(image)) {
throw new RangeError("Crop coordinates out of bounds")
}
var r = this._range
// checks input before passing it to the node function.
return crop(image, r.x1, r.y1, r.x2, r.y2)
}
}
Transforms.ImageCropper = ImageCropper;
class FacedetectCropper extends ImageCropper {
autoDetectFace(image) {
// runs costly algorithm and returns face range
}
applyTo(image) {
// Assume autoDetectFace always comes up with a valid range
// so we don't need to check the input.
this._range = this.autoDetectFace(image);
return super.applyTo(image);
}
}
Transforms.FacedetectCropper = FacedetectCropper;
We also have the following function that applies multiple transforms, but doesn't check the inputs before passing them to the applyTo
methods. We might need to fix this function:
function applyTransforms(transforms, image) {
for (var k = 0; k < transforms.length; ++k) {
image = transforms[k].applyTo(image);
}
return image;
}
And finally, we have a function that sends a http response, applying a face detection crop first, then an additional user-defined crop range
edit: We have a function that responds to a user request to transform an image
// Assume that we already passed through basic request validator for request.body params
// So all transforms are of valid type and have valid options for that route.
function runUserTransforms(request, response) {
var transforms = request.body.transforms.map(t => new Transforms[t.type](t.options)));
if ( ... write missing validation here ...) {
response.answer(400, "Invalid combination of transforms specified!");
}
else response.answer(200, {..content type}, applyTransforms(transforms, request.body.image))
}
The challenge is:
- Write the code to check the inputs in
runUserTransforms
before passing them toapplyTransforms
Assume that FacedetectCropper always detects valid coordinates
Points to consider:
- If the request specifies
[FacedetectCropper, ImageCropper(range)]
, FacedetectCropper might crop the image to be too small for the subsequent ImageCropperrange
- FacedetectCropper needs an actual image as input, even just to run the detection and produce coordinates. Consider the implications of that on a request like this:
[ImageCropper(range), FacedetectCropper, ImageCropper(range)]
,
@spion , though yes, in all these cases you need to calculate the costly operation at least once, prior to application, since that costly operation is what determines if the input is valid or not. Since this can be cached/saved in various ways and it must be performed prior to full application anyway, this isn't really a problem.