Commit 8413d240 authored by Fabrizio Ruggeri's avatar Fabrizio Ruggeri
Browse files

Accept auto

parent 68709140
......@@ -21,6 +21,7 @@ export interface Operation {
req: ServerRequest;
}) => Promise<Sharp>;
params: Array<unknown>;
skipCache?: boolean;
}
export type Normalizer<O extends RawOperation = RawOperation> = (
......
......@@ -28,6 +28,7 @@ describe('Output', () => {
name: 'o',
op: expect.any(Function),
params: ['jpg'],
skipCache: false,
},
])
);
......@@ -40,6 +41,7 @@ describe('Output', () => {
name: 'o',
op: expect.any(Function),
params: ['jpeg'],
skipCache: false,
},
]);
});
......@@ -58,6 +60,7 @@ describe('Output', () => {
name: 'o',
op: expect.any(Function),
params: ['png'],
skipCache: false,
},
])
);
......@@ -77,6 +80,7 @@ describe('Output', () => {
name: 'o',
op: expect.any(Function),
params: ['webp'],
skipCache: false,
},
])
);
......@@ -96,6 +100,7 @@ describe('Output', () => {
name: 'o',
op: expect.any(Function),
params: ['tiff'],
skipCache: false,
},
])
);
......@@ -106,4 +111,37 @@ describe('Output', () => {
await op({ image: sharp, otherOps: [], req });
expect(sharp.tiff).toHaveBeenCalledTimes(1);
});
test('output to auto', async () => {
const [{ op }] = o({ operation: 'o', value: 'auto' });
await op({ image: sharp, otherOps: [], req });
expect(sharp.webp).toHaveBeenCalledTimes(0);
});
test('output to auto when webp is accepted', async () => {
const [{ op }] = o({ operation: 'o', value: 'auto' });
await op({
image: sharp,
otherOps: [],
req: {
...req,
headers: { accept: 'image/webp' },
} as ServerRequest,
});
expect(sharp.webp).toHaveBeenCalledTimes(1);
});
test('if output is auto, cache is skipped', async () => {
const result = o({ operation: 'o', value: 'auto' });
expect(result).toEqual(
expect.objectContaining([
{
name: 'o',
op: expect.any(Function),
params: ['auto'],
skipCache: true,
},
])
);
});
});
......@@ -6,6 +6,8 @@ interface ORawOp extends RawOperation {
value: string;
}
const WEBP_ACCEPT_REGEXP = /image\/webp/i;
const o: Normalizer<ORawOp> = ({ value }) => {
const format = cohercer(
value,
......@@ -13,7 +15,7 @@ const o: Normalizer<ORawOp> = ({ value }) => {
'output.html'
)
.toString()
.enum(['original', 'jpg', 'jpeg', 'png', 'webp', 'tiff'])
.enum(['original', 'jpg', 'jpeg', 'png', 'webp', 'tiff', 'auto'])
.value()
.toLowerCase() as typeof value;
......@@ -29,6 +31,14 @@ const o: Normalizer<ORawOp> = ({ value }) => {
case 'tiff':
fn = async ({ image }) => image[format]();
break;
case 'auto':
fn = async ({ image, req }) => {
if (WEBP_ACCEPT_REGEXP.test(req.headers?.accept || '')) {
return image.webp();
}
return image;
};
break;
default:
return [];
}
......@@ -38,6 +48,7 @@ const o: Normalizer<ORawOp> = ({ value }) => {
name: 'o',
op: fn,
params: [format],
skipCache: format === 'auto',
},
];
};
......
......@@ -3,12 +3,13 @@ import { Context } from '..';
import sharp from 'sharp';
import imageLoader from '../utils/imageLoader';
import { stringifyParams } from '../utils/misc';
import normalizer from '../normalizers';
import normalizer, { Operation } from '../normalizers';
import { ServerRequest } from 'microrouter';
export interface PipelineResult {
data: Buffer;
format?: string;
skipCache?: boolean;
}
export type Pipeline = (opt: {
......@@ -17,6 +18,12 @@ export type Pipeline = (opt: {
req: ServerRequest;
}) => Promise<PipelineResult>;
/**
* Returns true if the operations provide a non-cachable result
*/
const shouldSkipChache = (operations: Operation[]) =>
operations.findIndex((o) => o.skipCache) !== -1;
const pipelineCreator = (context: Context): Pipeline => {
const loader = imageLoader(context);
const logger = context.logger;
......@@ -29,7 +36,6 @@ const pipelineCreator = (context: Context): Pipeline => {
const buffer = await loader.get(url);
const image = sharp(buffer);
const operations = normalize([...defaultOperations, ...rawOperations]);
const result = await operations.reduce(
async (acc, { name, op, params }) => {
if (logger.isLevelEnabled('debug')) {
......@@ -48,6 +54,7 @@ const pipelineCreator = (context: Context): Pipeline => {
return {
data,
format: info.format,
skipCache: shouldSkipChache(operations),
};
};
};
......
......@@ -31,8 +31,10 @@ const indexRoute = (context: Context) => {
logger.debug(rawOperations, 'Raw operations');
const result = await pipeline({ url: imageUrl, rawOperations, req });
await sender.sendImage(result, req, res);
await cache.set(url, result);
await sender.sendImage(result, req, res, result.skipCache);
if (!result.skipCache) {
await cache.set(url, result);
}
};
return handler;
......
......@@ -36,12 +36,13 @@ const senderCreator = (config: Config) => {
sendImage: async (
resource: SendArtifact,
req: ServerRequest,
res: ServerResponse
res: ServerResponse,
skipCache = false
) => {
const send = sendFactory(req);
switch (resource.type || 'buffer') {
case 'buffer': {
if (cacheControlHeader) {
if (!skipCache && cacheControlHeader) {
res.setHeader('cache-control', cacheControlHeader);
}
res.setHeader('Content-Type', await getMimeType(resource));
......
This source diff could not be displayed because it is too large. You can view the blob instead.
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment