Skip to content
This repository has been archived by the owner on May 14, 2020. It is now read-only.

Commit

Permalink
feat: allow individual article retrieval
Browse files Browse the repository at this point in the history
Generates a unique IRI for each article, and allows them to be accessed individually.
  • Loading branch information
nlisgo authored and thewilkybarkid committed Jan 23, 2020
1 parent 852e951 commit c25a094
Show file tree
Hide file tree
Showing 6 changed files with 93 additions and 3 deletions.
2 changes: 2 additions & 0 deletions src/router.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import { AppServiceContext, AppState } from './app';
import Routes from './routes';
import addArticle from './routes/add-article';
import apiDocumentation from './routes/api-documentation';
import article from './routes/article';
import articleList from './routes/article-list';
import entryPoint from './routes/entry-point';

Expand All @@ -12,6 +13,7 @@ export default (): Router<AppState, AppServiceContext> => {
router.get(Routes.ApiDocumentation, '/doc', apiDocumentation());
router.get(Routes.ArticleList, '/articles', articleList());
router.post(Routes.AddArticle, '/articles', addArticle());
router.get(Routes.Article, '/articles/:id', article());
router.get(Routes.EntryPoint, '/', entryPoint());

return router;
Expand Down
4 changes: 2 additions & 2 deletions src/routes/add-article.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ export default (): AppMiddleware => (
throw new createHttpError.BadRequest(`Article must have at least one ${termToString(schema('name'))}`);
}

const newId = namedNode(uniqueString());
const newId = namedNode(url.resolve(request.origin, router.url(Routes.Article, uniqueString())));

[...request.dataset].forEach((originalQuad: Quad): void => {
let newQuad: Quad;
Expand All @@ -46,7 +46,7 @@ export default (): AppMiddleware => (
await articles.set(newId, request.dataset);

response.status = CREATED;
response.set('Location', url.resolve(request.origin, router.url(Routes.ArticleList)));
response.set('Location', newId.value);

await next();
}
Expand Down
28 changes: 28 additions & 0 deletions src/routes/article.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
import createHttpError from 'http-errors';
import { OK } from 'http-status-codes';
import { Next } from 'koa';
import url from 'url';
import { AppContext, AppMiddleware } from '../app';
import ArticleNotFound from '../errors/article-not-found';

export default (): AppMiddleware => (
async ({
articles, dataFactory: { namedNode }, request, response,
}: AppContext, next: Next): Promise<void> => {
const id = namedNode(url.resolve(request.origin, request.path));

try {
response.dataset = await articles.get(id);
} catch (error) {
if (error instanceof ArticleNotFound) {
throw new createHttpError.NotFound(error.message);
}

throw error;
}

response.status = OK;

await next();
}
);
1 change: 1 addition & 0 deletions src/routes/index.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
enum Routes {
'AddArticle' = 'add-article',
'ApiDocumentation' = 'api-documentation',
'Article' = 'article',
'ArticleList' = 'article-list',
'EntryPoint' = 'entry-point',
}
Expand Down
2 changes: 1 addition & 1 deletion test/routes/add-article.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,11 +30,11 @@ describe('add article', (): void => {
const response = await makeRequest(createArticle({ id, name }), undefined, articles);

expect(response.status).toBe(CREATED);
expect(response.get('Location')).toBe('http://example.com/path-to/article-list');
expect(await articles.count()).toBe(1);

const [newId, dataset] = (await all(articles))[0];

expect(response.get('Location')).toBe(newId.value);
expect(dataset.has(quad(newId, schema('name'), name))).toBe(true);
});

Expand Down
59 changes: 59 additions & 0 deletions test/routes/article.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
import { namedNode } from '@rdfjs/data-model';
import createHttpError from 'http-errors';
import { OK } from 'http-status-codes';
import 'jest-rdf';
import { Response } from 'koa';
import InMemoryArticles from '../../src/adaptors/in-memory-articles';
import Articles from '../../src/articles';
import { WithDataset } from '../../src/middleware/dataset';
import article from '../../src/routes/article';
import createContext from '../context';
import createArticle from '../create-article';
import runMiddleware, { NextMiddleware } from '../middleware';

const makeRequest = async (
path: string, articles?: Articles, next?: NextMiddleware,
): Promise<WithDataset<Response>> => (
runMiddleware(article(), createContext({ articles, path }), next)
);

describe('article', (): void => {
it('should return a successful response', async (): Promise<void> => {
const id = namedNode('http://example.com/path-to/article/one');
const articles = new InMemoryArticles();
await articles.set(id, createArticle({ id }));

const response = await makeRequest('path-to/article/one', articles);

expect(response.status).toBe(OK);
});

it('should return the article', async (): Promise<void> => {
const id = namedNode('http://example.com/path-to/article/one');
const articles = new InMemoryArticles();
const article1 = createArticle({ id });
await articles.set(id, article1);

const response = await makeRequest('path-to/article/one', articles);

expect([...response.dataset]).toEqualRdfQuadArray([...article1]);
});

it('should throw an error if article is not found', async (): Promise<void> => {
const response = makeRequest('path-to/article/not-found');

await expect(response).rejects.toBeInstanceOf(createHttpError.NotFound);
await expect(response).rejects.toHaveProperty('message', 'Article http://example.com/path-to/article/not-found could not be found');
});

it('should call the next middleware', async (): Promise<void> => {
const id = namedNode('http://example.com/path-to/article/one');
const articles = new InMemoryArticles();
await articles.set(id, createArticle({ id }));
const next = jest.fn();

await makeRequest('path-to/article/one', articles, next);

expect(next).toHaveBeenCalledTimes(1);
});
});

0 comments on commit c25a094

Please sign in to comment.