Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Adds Initial Service to Get Posts (#1838)
* Initial Posts Service Draft * Updated redis container name, removed second ports, and updated redis url * Added initial code to test /posts endpoint * Refactored and added jest config settings for test cases * Recovered and fixed lost files issue * Finished tests for posts service * Revert "Finished tests for posts service" This reverts commit abfc7f5. * Revert "Added initial code to test /posts endpoint" This reverts commit a0b9ad2. * Added install script for posts service * Updated tests, package.json, fixed Docker typo * Added missing - to Dockerfile * Removed unused files, and updated docker, and env file * Updated Dockerfile, jest, and added back supertest * Added post service env info to staging and production env files
- Loading branch information
Showing
20 changed files
with
789 additions
and
1 deletion.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,6 @@ | ||
.dockerignore | ||
node_modules | ||
npm-debug.log | ||
Dockerfile | ||
.git | ||
.gitignore |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,13 @@ | ||
FROM node:lts-alpine as base | ||
|
||
RUN apk add dumb-init | ||
|
||
WORKDIR /app | ||
|
||
COPY --chown=node:node . . | ||
|
||
RUN npm install --only=production --no-package-lock | ||
|
||
USER node | ||
|
||
CMD ["dumb-init", "node", "src/server.js"] |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,34 @@ | ||
# Image Service | ||
|
||
The Post Service parses posts from user's feeds to display them on Telescope. | ||
|
||
## Install | ||
|
||
``` | ||
npm install | ||
``` | ||
|
||
## Usage | ||
|
||
``` | ||
# normal mode | ||
npm start | ||
# dev mode with automatic restarts | ||
npm run dev | ||
``` | ||
|
||
By default the server is running on <http://localhost:5555/>. | ||
|
||
### Examples | ||
|
||
- `GET /posts` - Returns the 10 latests posts parsed | ||
|
||
- `GET /posts/:id` - Returns information about a specific post | ||
|
||
- `GET /healthcheck` - returns `{ "status": "ok" }` if everything is running properly | ||
|
||
## Docker | ||
|
||
- To build and tag: `docker build . -t telescope_posts_svc:latest` | ||
- To run locally: `docker run -p 5555:5555 telescope_posts_svc:latest` |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,8 @@ | ||
const baseConfig = require('../../../jest.config.base'); | ||
|
||
module.exports = { | ||
...baseConfig, | ||
rootDir: '../../..', | ||
testMatch: ['<rootDir>/src/api/posts/test/*.test.js'], | ||
collectCoverageFrom: ['<rootDir>/src.api/posts/src/**/*.js'], | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,10 @@ | ||
const path = require('path'); | ||
const result = require('dotenv').config({ | ||
path: path.join(__dirname, '../env.development'), | ||
}); | ||
|
||
process.env = { ...process.env, MOCK_REDIS: '1' }; | ||
|
||
if (result.error) { | ||
throw result.error; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,28 @@ | ||
{ | ||
"name": "@senecacdot/posts-service", | ||
"version": "1.0.0", | ||
"private": true, | ||
"description": "A service for retrieving posts", | ||
"scripts": { | ||
"start": "node src/server.js" | ||
}, | ||
"repository": "Seneca-CDOT/telescope", | ||
"license": "BSD-2-Clause", | ||
"bugs": { | ||
"url": "https://github.com/Seneca-CDOT/telescope/issues" | ||
}, | ||
"homepage": "https://github.com/Seneca-CDOT/telescope#readme", | ||
"engines": { | ||
"node": ">=12.0.0" | ||
}, | ||
"dependencies": { | ||
"@senecacdot/satellite": "^1.x", | ||
"express-validator": "^6.10.0", | ||
"ioredis": "^4.22.0", | ||
"ioredis-mock": "^5.2.4", | ||
"jsdom": "^16.4.0" | ||
}, | ||
"devDependencies": { | ||
"supertest": "^6.1.3" | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,99 @@ | ||
const { hash } = require('@senecacdot/satellite'); | ||
const { getPost, addPost } = require('../storage'); | ||
const textParser = require('../text-parser'); | ||
|
||
/** | ||
* Makes sure that a given date can be constructed as a Date object | ||
* Returns a constructed Date object, if possible | ||
* Otherwise throws an Error | ||
* @param {Object} date an Object to construct as a Date object | ||
* @param {Date} [fallbackDate] an optional second Date to construct in case the first fails to do so | ||
*/ | ||
function ensureDate(date, fallbackDate) { | ||
if ( | ||
date && | ||
(Object.prototype.toString.call(date) === '[object String]' || | ||
(Object.prototype.toString.call(date) === '[object Date]' && !Number.isNaN(date))) | ||
) { | ||
return new Date(date); | ||
} | ||
if (Object.prototype.toString.call(fallbackDate) === '[object Date]') { | ||
return new Date(fallbackDate); | ||
} | ||
|
||
throw new Error(`post has an invalid date: ${date}'`); | ||
} | ||
|
||
class Post { | ||
constructor(title, html, datePublished, dateUpdated, postUrl, guid, feed) { | ||
// Use the post's guid as our unique identifier | ||
this.id = hash(guid); | ||
this.title = title; | ||
this.html = html; | ||
this.published = ensureDate(datePublished); | ||
this.updated = ensureDate(dateUpdated, datePublished); | ||
this.url = postUrl; | ||
this.guid = guid; | ||
this.feed = feed; | ||
} | ||
|
||
/** | ||
* Save the current Post to the database, swapping the feed's id | ||
* for the entire Feed object. | ||
* Returns a Promise. | ||
*/ | ||
save() { | ||
return addPost({ | ||
...this, | ||
feed: this.feed.id, | ||
}); | ||
} | ||
|
||
/** | ||
* Generate the plain text version of this post on demand vs. storing | ||
*/ | ||
get text() { | ||
return textParser(this.html); | ||
} | ||
|
||
get author() { | ||
return this.feed.author; | ||
} | ||
|
||
/** | ||
* Creates a new Post object by extracting data from the given post-like object. | ||
* @param {Object} postData - an Object containing the necessary fields. The | ||
* feed property can be an id or a full Feed Object. | ||
* Returns the newly created Post's id. | ||
*/ | ||
static async create(postData) { | ||
// If we only have a feed id, get the full Feed Object instead. | ||
const post = new Post( | ||
postData.title, | ||
postData.html, | ||
postData.published, | ||
postData.updated, | ||
postData.url, | ||
postData.guid | ||
); | ||
await post.save(); | ||
return post.id; | ||
} | ||
|
||
/** | ||
* Returns a Post from the database using the given id | ||
* @param {String} id - the id of a post (hashed guid) to get from Redis. | ||
*/ | ||
static async byId(id) { | ||
const data = await getPost(id); | ||
// No post found using this id | ||
if (!(data && data.id)) { | ||
return null; | ||
} | ||
|
||
const post = new Post(data.title, data.html, data.published, data.updated, data.url, data.guid); | ||
return post; | ||
} | ||
} | ||
|
||
module.exports = Post; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,9 @@ | ||
const { Satellite } = require('@senecacdot/satellite'); | ||
|
||
const posts = require('./routes/posts'); | ||
|
||
const service = new Satellite(); | ||
|
||
service.router.use('/', posts); | ||
|
||
module.exports = service; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,36 @@ | ||
const Redis = require('ioredis'); | ||
const MockRedis = require('ioredis-mock'); | ||
const { logger } = require('@senecacdot/satellite'); | ||
|
||
// If you need to set the Redis URL, do it in REDIS_URL | ||
const redisUrl = 'http://redis:6379'; | ||
|
||
// Set MOCK_REDIS=1 to mock, MOCK_REDIS= to use real redis | ||
const useMockRedis = process.env.MOCK_REDIS; | ||
|
||
// RedisConstructor is one of Redis or MockRedis | ||
const RedisConstructor = useMockRedis ? MockRedis : Redis; | ||
|
||
function createRedisClient() { | ||
try { | ||
const { port, host } = new URL(redisUrl); | ||
return new RedisConstructor(port, host, { password: process.env.REDIS_PASSWORD }); | ||
} catch (error) { | ||
const message = `Unable to parse port and host from "${redisUrl}"`; | ||
logger.error({ error }, message); | ||
throw new Error(message); | ||
} | ||
} | ||
|
||
// If using MockRedis, shim info() until https://github.com/stipsan/ioredis-mock/issues/841 ships | ||
if (useMockRedis && typeof MockRedis.prototype.info !== 'function') { | ||
logger.debug('Shimming MockRedis info() method'); | ||
MockRedis.prototype.info = () => Promise.resolve('redis_version:999.999.999'); | ||
} | ||
|
||
module.exports = { | ||
// If callers need to create a new redis instance, they'll use the ctor | ||
createRedisClient, | ||
// Otherwise they can use this shared instance (most should use this) | ||
redis: createRedisClient(), | ||
}; |
Oops, something went wrong.