Skip to content

Commit

Permalink
feat: use proper file extension based on content-type (#36)
Browse files Browse the repository at this point in the history
  • Loading branch information
antoinechalifour authored Aug 24, 2019
1 parent 2ac020f commit f5fbade
Show file tree
Hide file tree
Showing 12 changed files with 584 additions and 47 deletions.
13 changes: 8 additions & 5 deletions bin/memento.js
Original file line number Diff line number Diff line change
@@ -1,15 +1,18 @@
#!/usr/bin/env node

/* eslint-disable @typescript-eslint/no-var-requires */
process.env.NODE_ENV = 'production';
process.env.NODE_ENV = 'development';

const { Memento, createCli } = require('../dist');
const { Memento, createCli, runMigrations } = require('../dist');

const memento = Memento();

memento.run().then(() => {
createCli({ container: memento.container }).show();
});
memento
.run()
.then(() => runMigrations(memento.container))
.then(() => {
createCli({ container: memento.container }).show();
});

function stopServer() {
memento.stop();
Expand Down
2 changes: 2 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@
"devDependencies": {
"@release-it/conventional-changelog": "^1.1.0",
"@types/axios": "^0.14.0",
"@types/content-type": "^1.1.3",
"@types/cosmiconfig": "^5.0.3",
"@types/fs-extra": "^8.0.0",
"@types/jest": "^24.0.17",
Expand Down Expand Up @@ -81,6 +82,7 @@
"awilix-koa": "4.0.0",
"axios": "0.19.0",
"chalk": "2.4.2",
"content-type": "^1.0.4",
"cosmiconfig": "5.2.1",
"fs-extra": "8.1.0",
"koa": "2.8.1",
Expand Down
3 changes: 2 additions & 1 deletion src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import { createContainer, asClass, asValue } from 'awilix';

import { createCli } from './cli';
import { createApp } from './app';
import { runMigrations } from './migrations';
import { configuration } from './configuration';
import {
RespondToRequest,
Expand Down Expand Up @@ -59,4 +60,4 @@ export function Memento({ cacheDirectory }: MementoOptions = {}) {
};
}

export { createCli };
export { createCli, runMigrations };
7 changes: 4 additions & 3 deletions src/infrastructure/repository/RequestRepositoryFile.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ describe('persistResponseForRequest', () => {
const CASES = [
'APPLICATION/JSON',
'application/json',
'application/json; chartset=UTF-8;',
'application/json; charset=utf-8',
];

CASES.forEach(contentType => {
Expand Down Expand Up @@ -92,6 +92,7 @@ describe('persistResponseForRequest', () => {
const CASES = [
'APPLICATION/XML',
'application/xml',
'application/xml; charset=utf-8',
'TEXT/XML',
'text/xml',
];
Expand Down Expand Up @@ -203,7 +204,7 @@ describe('persistResponseForRequest', () => {
`${OUTPUT_DIRECTORY}/get__text-${inputRequest.id}/metadata.json`
);
const bodyContent = await fs.readFile(
`${OUTPUT_DIRECTORY}/get__text-${inputRequest.id}/body.txt`,
`${OUTPUT_DIRECTORY}/get__text-${inputRequest.id}/body`,
'utf-8'
);

Expand Down Expand Up @@ -241,7 +242,7 @@ describe('persistResponseForRequest', () => {
`${OUTPUT_DIRECTORY}/get__really_long_url-${inputRequest.id}/metadata.json`
);
const bodyContent = await fs.readFile(
`${OUTPUT_DIRECTORY}/get__really_long_url-${inputRequest.id}/body.txt`,
`${OUTPUT_DIRECTORY}/get__really_long_url-${inputRequest.id}/body`,
'utf-8'
);

Expand Down
37 changes: 7 additions & 30 deletions src/infrastructure/repository/RequestRepositoryFile.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,11 @@
import path from 'path';
import fs from 'fs-extra';

import { getProjectDirectory, getRequestDirectory } from '../../utils/path';
import {
getProjectDirectory,
getRequestDirectory,
getFileExtension,
} from '../../utils/path';
import { RequestRepository } from '../../domain/repository';
import { Request, Response } from '../../domain/entity';

Expand All @@ -10,25 +14,6 @@ interface Dependencies {
cacheDirectory: string;
}

function isJson(contentType: string | undefined) {
return (
contentType && contentType.toLowerCase().indexOf('application/json') >= 0
);
}

function isXml(contentType: string | undefined) {
if (!contentType) {
return false;
}

const lowerCaseContentType = contentType.toLowerCase();

return (
lowerCaseContentType.indexOf('application/xml') >= 0 ||
lowerCaseContentType.indexOf('text/xml') >= 0
);
}

export class RequestRepositoryFile implements RequestRepository {
private targetUrl: string;
private cacheDirectory: string;
Expand Down Expand Up @@ -141,19 +126,11 @@ export class RequestRepositoryFile implements RequestRepository {
private async getResponseBodyFilePath(request: Request) {
const metadata = await this.getRequestMetaData(request);
const contentType = metadata.responseHeaders['content-type'];
let fileExtension: string;

if (isJson(contentType)) {
fileExtension = 'json';
} else if (isXml(contentType)) {
fileExtension = 'xml';
} else {
fileExtension = 'txt';
}
const fileExtension = getFileExtension(contentType);

return path.join(
this.getRequestDirectoryPath(request),
`body.${fileExtension}`
`body${fileExtension}`
);
}

Expand Down
195 changes: 195 additions & 0 deletions src/migrations/cache-fs/1-txt-to-proper-file-type.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,195 @@
import fs from 'fs-extra';
import path from 'path';

import { RequestRepositoryFile } from '../../infrastructure/repository';
import { Request, Response } from '../../domain/entity';
import { moveTxtToProperFileTypeMigration } from './1-txt-to-proper-file-type';

const MEMENTO_CACHE_DIR = path.join(__dirname, '../../../.memento-test-cache');
const OUTPUT_DIRECTORY = `${MEMENTO_CACHE_DIR}/https___pokeapi-co_api_v2`;
let requestRepository: RequestRepositoryFile;

function getOutputFilePath(fileName: string) {
return path.join(OUTPUT_DIRECTORY, fileName);
}

afterAll(() => {
fs.removeSync(MEMENTO_CACHE_DIR);
});

beforeEach(() => {
fs.removeSync(MEMENTO_CACHE_DIR);

requestRepository = new RequestRepositoryFile({
targetUrl: 'https://pokeapi.co/api/v2',
cacheDirectory: MEMENTO_CACHE_DIR,
});
});

describe('text/plain requests migration', () => {
beforeEach(async () => {
const request = new Request('GET', '/text', {}, '');
const response = new Response(
200,
{
'content-type': 'text/plain',
},
Buffer.from('Ok'),
0
);

await requestRepository.persistResponseForRequest(request, response);
});

it('should not move the body file', async () => {
// When
await moveTxtToProperFileTypeMigration({
targetUrl: 'https://pokeapi.co/api/v2',
cacheDirectory: MEMENTO_CACHE_DIR,
requestRepository,
});

// Then
const olfFileStillExists = await fs.pathExists(
getOutputFilePath(
'get__text-74121b3875b9d4d16c7e9dfd80bd90ff50da5d86/body.txt'
)
);

// Assert that fold files have been moved to new files.
expect(olfFileStillExists).toBe(true);
});
});

describe('application/json requests migration', () => {
beforeEach(async () => {
const request = new Request('GET', '/json', {}, '');
const response = new Response(
200,
{
'content-type': 'application/json',
},
Buffer.from('{"name": "John Doe"}'),
0
);

await requestRepository.persistResponseForRequest(request, response);

await fs.move(
getOutputFilePath(
'get__json-728ad90473b5366f44ebc49a57da2c5df837d040/body.json'
),
getOutputFilePath(
'get__json-728ad90473b5366f44ebc49a57da2c5df837d040/body.txt'
)
);
});

it('should move the body file to body.json', async () => {
// When
await moveTxtToProperFileTypeMigration({
targetUrl: 'https://pokeapi.co/api/v2',
cacheDirectory: MEMENTO_CACHE_DIR,
requestRepository,
});

// Then
const oldFileExists = await fs.pathExists(
getOutputFilePath(
'get__json-728ad90473b5366f44ebc49a57da2c5df837d040/body.txt'
)
);
const newFileExists = await fs.pathExists(
getOutputFilePath(
'get__json-728ad90473b5366f44ebc49a57da2c5df837d040/body.json'
)
);

// Assert that fold files have been moved to new files.
expect(oldFileExists).toBe(false);
expect(newFileExists).toBe(true);
});
});

describe('application/octet-stream request migration', () => {
beforeEach(async () => {
const request = new Request('GET', '/octet-stream', {}, '');
const response = new Response(
200,
{
'content-type': 'application/octet-stream',
},
Buffer.from('something'),
0
);

await requestRepository.persistResponseForRequest(request, response);

await fs.move(
getOutputFilePath(
'get__octet-stream-d1f560b23c4db2f2a2e0dfc1bfa32592d4370cb5/body'
),
getOutputFilePath(
'get__octet-stream-d1f560b23c4db2f2a2e0dfc1bfa32592d4370cb5/body.txt'
)
);
});

it('should move the body file to body', async () => {
// When
await moveTxtToProperFileTypeMigration({
targetUrl: 'https://pokeapi.co/api/v2',
cacheDirectory: MEMENTO_CACHE_DIR,
requestRepository,
});

// Then
const oldFileExists = await fs.pathExists(
getOutputFilePath(
'get__octet-stream-d1f560b23c4db2f2a2e0dfc1bfa32592d4370cb5/body.txt'
)
);
const newFileExists = await fs.pathExists(
getOutputFilePath(
'get__octet-stream-d1f560b23c4db2f2a2e0dfc1bfa32592d4370cb5/body'
)
);
// Assert that fold files have been moved to new files.
expect(oldFileExists).toBe(false);
expect(newFileExists).toBe(true);
});
});

describe('requests respecting the new format', () => {
beforeEach(async () => {
const request = new Request('GET', '/new-format', {}, '');
const response = new Response(
200,
{
'content-type': 'application.xml',
},
Buffer.from('<text>something</text>'),
0
);

await requestRepository.persistResponseForRequest(request, response);
});

it('should not move the body file', async () => {
// When
await moveTxtToProperFileTypeMigration({
targetUrl: 'https://pokeapi.co/api/v2',
cacheDirectory: MEMENTO_CACHE_DIR,
requestRepository,
});

// Then
const oldFileExists = await fs.pathExists(
getOutputFilePath(
'get__new-format-bb4032a14daf7b56f8f9f4312ce139543bc9a281'
)
);

expect(oldFileExists).toBe(true);
});
});
44 changes: 44 additions & 0 deletions src/migrations/cache-fs/1-txt-to-proper-file-type.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
import path from 'path';
import fs from 'fs-extra';

import { RequestRepository } from '../../domain/repository';
import { getFileExtension, getRequestDirectory } from '../../utils/path';

export interface Dependencies {
targetUrl: string;
cacheDirectory: string;
requestRepository: RequestRepository;
}

export async function moveTxtToProperFileTypeMigration({
targetUrl,
cacheDirectory,
requestRepository,
}: Dependencies) {
const allRequests = await requestRepository.getAllRequests();

for (const request of allRequests) {
const requestDirectory = getRequestDirectory(
cacheDirectory,
targetUrl,
request
);
const metadatafile = await fs.readJson(
path.join(requestDirectory, 'metadata.json')
);

const contentType = metadatafile.responseHeaders['content-type'];
const newFileExtension = getFileExtension(contentType);

const oldBodyFile = path.join(requestDirectory, 'body.txt');
const newBodyFile = path.join(requestDirectory, `body${newFileExtension}`);

if (oldBodyFile !== newBodyFile) {
const hasOldBodyFile = await fs.pathExists(oldBodyFile);

if (hasOldBodyFile) {
await fs.move(oldBodyFile, newBodyFile);
}
}
}
}
Loading

0 comments on commit f5fbade

Please sign in to comment.