Permalink
Show file tree
Hide file tree
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Showing
6 changed files
with
489 additions
and
0 deletions.
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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,2 @@ | ||
/node_modules | ||
/docs |
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,135 @@ | ||
'use strict' | ||
|
||
const sqlite = require('sqlite-async') | ||
|
||
/** | ||
* Class representing images in the database. | ||
*/ | ||
class Image { | ||
|
||
/**Constructs database with the appropriate table for article images. | ||
* @constructor | ||
* @param {string} [dbName=':memory:'] - database for the website | ||
*/ | ||
constructor(dbName = ':memory:') { | ||
return (async() => { | ||
this.db = await sqlite.open(dbName) | ||
const sql = `CREATE TABLE IF NOT EXISTS images ( | ||
id INTEGER PRIMARY KEY AUTOINCREMENT, | ||
image_path TEXT, | ||
image_description TEXT);` | ||
await this.db.run(sql) | ||
return this | ||
})() | ||
} | ||
|
||
/** | ||
* Adds filename of selected file to the database. | ||
* @param {string} filename | ||
* @param {string} [description] | ||
* @returns {Promise<true>} confirmation for insertion of filename in database | ||
*/ | ||
async add(filename, description) { | ||
try{ | ||
if(filename.length === 0) throw new Error('filename is missing') | ||
let sql = `SELECT COUNT(id) as records from images WHERE image_path = '${filename}';` | ||
const data = await this.db.get(sql) | ||
if(data.records !== 0) throw new Error('filename is already in use') | ||
if(description === undefined) { | ||
sql = `INSERT INTO images (image_path) values ('${filename}');` | ||
await this.db.run(sql) | ||
return true | ||
} else { | ||
sql = `INSERT INTO images (image_path, image_description) values ('${filename}', '${description}');` | ||
await this.db.run(sql) | ||
return true | ||
} | ||
} catch(err) { | ||
throw err | ||
} | ||
} | ||
|
||
/** | ||
* Retrieves details of an image | ||
* @param {Integer} key - id of the image | ||
* @returns {Promise<imageData>} | ||
*/ | ||
async get(key) { | ||
try{ | ||
if(key === undefined) throw new Error('key is not defined') | ||
let sql = `SELECT COUNT(id) as records FROM images WHERE id = ${key};` | ||
let data = await this.db.get(sql) | ||
if(data.records === 0) throw new Error('file does not exist') | ||
sql = `SELECT image_path, image_description | ||
FROM images WHERE id = ${key};` | ||
data = await this.db.get(sql) | ||
return data | ||
} catch(err) { | ||
throw err | ||
} | ||
} | ||
|
||
/** | ||
* Retrieves details of all stored images | ||
* @returns {Promise<imageData[]>} - array of image data | ||
*/ | ||
async getAll() { | ||
const sql = `SELECT image_path as path, | ||
image_description as description | ||
FROM images;` | ||
const data = await this.db.all(sql) | ||
return data | ||
} | ||
|
||
/** | ||
* Rename an existing file | ||
* @param {string} targetFile - full path of file | ||
* @param {string} newName - new filename | ||
* @returns {Promise<true>} confirmation for file being renamed | ||
*/ | ||
async rename(targetFile, newName) { | ||
try{ | ||
if(targetFile.length === 0) throw new Error('file not selected') | ||
if(newName.length === 0) throw new Error('new name not stated') | ||
let sql = `SELECT COUNT(id) as records FROM images WHERE image_path = '${targetFile}';` | ||
const data = await this.db.get(sql) | ||
if(data.records === 0) throw new Error('file is not found') | ||
sql = `UPDATE images SET image_path = '${newName}' WHERE image_path = '${targetFile}';` | ||
await this.db.run(sql) | ||
return true | ||
} catch(err) { | ||
throw err | ||
} | ||
} | ||
|
||
/** | ||
* Delete an exisitng image file based on image id | ||
* @param {integer} key | ||
* @returns {Promise<true>} confirmation for image deleted | ||
*/ | ||
async delete(key) { | ||
try{ | ||
if(key === undefined) throw new Error('key is not defined') | ||
let sql = `SELECT COUNT(id) as records FROM images WHERE id = ${key};` | ||
const data = await this.db.get(sql) | ||
if(data.records === 0) throw new Error('file is not found') | ||
sql = `DELETE FROM images WHERE id = ${key}` | ||
await this.db.run(sql) | ||
return true | ||
} catch(err) { | ||
throw err | ||
} | ||
} | ||
|
||
/** | ||
* Count the sum of images | ||
* @returns {Promise<integer>} number representing amount of stored images | ||
*/ | ||
async count() { | ||
const sql = 'SELECT COUNT(id) as records FROM images' | ||
const data = await this.db.get(sql) | ||
return data.records | ||
} | ||
} | ||
|
||
module.exports = Image |
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,68 @@ | ||
'use strict' | ||
|
||
const fs = require('fs') | ||
const util = require('util') | ||
const mime = require('mime-types') | ||
|
||
//util.promisify used to make convert callback functions to promises | ||
const copy = util.promisify(fs.copyFile) | ||
const unlink = util.promisify(fs.unlink) | ||
const exists = util.promisify(fs.exists) | ||
|
||
/** | ||
* Allow users to upload images | ||
* @param {string} path - path of file | ||
* @param {string} mimeType - mime type of file | ||
* @param {string} fileName | ||
* @param {string} newPath - new path of file | ||
* @returns {Promise<boolean>} true if no errors | ||
*/ | ||
const uploadPicture = async(path, mimeType, fileName, newPath) => { | ||
try{ | ||
if(path.length === 0) throw new Error('path is blank') | ||
if(mimeType.length === 0) throw new Error('mimeType is blank') | ||
const extension = mime.extension(mimeType) | ||
const acceptedExtension = ['jpg', 'png', 'webp', 'tiff', 'gif', 'svg'] | ||
if(acceptedExtension.includes(extension) === false) throw new Error('file type is not suitable for upload') | ||
const newFile = `${newPath}${fileName}.${extension}` | ||
await copy(path, newFile) | ||
return true | ||
} catch(err) { | ||
throw err | ||
} | ||
} | ||
|
||
/** | ||
* Delete an existing image file | ||
* @param {string} targetFile - full path of an existing file | ||
* @returns {Promise<boolean>} true if no errors | ||
*/ | ||
const deletePicture = async(targetFile) => { | ||
try{ | ||
if(await exists(`${targetFile}`) === false) throw new Error('image file does not exist') | ||
await unlink(`${targetFile}`) | ||
return true | ||
} catch(err) { | ||
throw err | ||
} | ||
} | ||
|
||
/** | ||
* Rename the image file | ||
* @param {string} targetFile - full path of existing file | ||
* @param {string} newName - new path for target file | ||
* @returns {Promise<boolean>} true if no errors | ||
*/ | ||
const renamePicture = async(targetFile, newName) => { | ||
try{ | ||
if(await exists(`${targetFile}`) === false) throw new Error('file does not exist') | ||
const name = String(newName) | ||
await copy(`${targetFile}`, `${name}`) | ||
await unlink(`${targetFile}`) | ||
return true | ||
} catch(err) { | ||
throw err | ||
} | ||
} | ||
|
||
module.exports = {uploadPicture, deletePicture, renamePicture} |
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,16 @@ | ||
/** @typedef {Object} userData | ||
* @property {string} id | ||
* @property {string} user - username | ||
* @property {string} password | ||
* @property {string} email | ||
* @property {string} location | ||
* @property {string} profileImg - profile picture | ||
* @property {string} profileTxt - profile summary | ||
* @property {string} registeredDate | ||
*/ | ||
|
||
/** | ||
* @typedef {imageData} | ||
* @property {string} image_path | ||
* @property {string} image_description | ||
*/ |
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,172 @@ | ||
'use strict' | ||
|
||
const Image = require('../modules/image') | ||
|
||
describe('add()', () => { | ||
test('return error if filename is empty', async done => { | ||
expect.assertions(1) | ||
const image = await new Image() | ||
await expect(image.add('')) | ||
.rejects.toEqual(Error('filename is missing')) | ||
done() | ||
}) | ||
|
||
test('return error if filename exists in database', async done => { | ||
expect.assertions(1) | ||
const image = await new Image() | ||
await image.add('public/avatars/adrian.png') | ||
await expect(image.add('public/avatars/adrian.png')) | ||
.rejects.toEqual(Error('filename is already in use')) | ||
done() | ||
}) | ||
test('add a single item', async done => { | ||
expect.assertions(1) | ||
const image = await new Image() | ||
await image.add('public/avatars/adrian.png') | ||
const count = await image.count() | ||
expect(count).toEqual(1) | ||
done() | ||
}) | ||
|
||
test('add both filename and description to the database', async done => { | ||
expect.assertions(2) | ||
const image = await new Image() | ||
const described = await image.add('public/avatars/adrian.png', 'Images of Adrian') | ||
const count = await image.count() | ||
expect(count).toEqual(1) | ||
expect(described).toEqual(true) | ||
done() | ||
}) | ||
}) | ||
|
||
describe('get()', () => { | ||
test('return error if parameters are not stated', async done => { | ||
expect.assertions(1) | ||
const image = await new Image() | ||
await expect(image.get()) | ||
.rejects.toEqual(Error('key is not defined')) | ||
done() | ||
}) | ||
|
||
test('return error if file does not exist', async done => { | ||
expect.assertions(1) | ||
const image = await new Image() | ||
await expect(image.get(1)) | ||
.rejects.toEqual(Error('file does not exist')) | ||
done() | ||
}) | ||
|
||
test('return the filename and description based on input', async done => { | ||
expect.assertions(2) | ||
const image = await new Image() | ||
await image.add('public/avatars/adrian.png', 'Image of Adrian') | ||
const result = await image.get(1) | ||
expect(result.image_path).toEqual('public/avatars/adrian.png') | ||
expect(result.image_description).toEqual('Image of Adrian') | ||
done() | ||
}) | ||
}) | ||
describe('getAll()', () => { | ||
|
||
test('return empty array if there are no data stored', async done => { | ||
expect.assertions(1) | ||
const image = await new Image() | ||
const results = await image.getAll() | ||
expect(results).toEqual([]) | ||
done() | ||
}) | ||
|
||
test('return all image filenames and descriptions', async done => { | ||
expect.assertions(2) | ||
const image = await new Image() | ||
await image.add('public/avatars/adrian.png', 'Image of Adrian') | ||
const results = await image.getAll() | ||
expect(results[0].path).toEqual('public/avatars/adrian.png') | ||
expect(results[0].description).toEqual('Image of Adrian') | ||
done() | ||
}) | ||
}) | ||
|
||
describe('rename()', () => { | ||
|
||
test('return error if filename is empty', async done => { | ||
expect.assertions(1) | ||
const image = await new Image() | ||
await expect(image.rename('', 'public/avatars/adrian.png')) | ||
.rejects.toEqual(Error('file not selected')) | ||
done() | ||
}) | ||
|
||
test('return error if new name is empty', async done => { | ||
expect.assertions(1) | ||
const image = await new Image() | ||
await expect(image.rename('public/avatars/adrian.png', '')) | ||
.rejects.toEqual(Error('new name not stated')) | ||
done() | ||
}) | ||
|
||
test('return true when image_path is renamed', async done => { | ||
expect.assertions(1) | ||
const image = await new Image() | ||
await image.add('public/avatars/adrian.png') | ||
const renamed = await image.rename('public/avatars/adrian.png', 'public/avatars/adrian1.png') | ||
expect(renamed).toEqual(true) | ||
done() | ||
}) | ||
|
||
test('return error when image_path is not found', async done => { | ||
expect.assertions(1) | ||
const image = await new Image() | ||
await expect( image.rename('public/avatars/adrian.png', 'public/avatars/adrian1.png')) | ||
.rejects.toEqual(Error('file is not found')) | ||
done() | ||
}) | ||
}) | ||
|
||
describe('delete()', () => { | ||
|
||
test('return error when file id is not defined', async done => { | ||
expect.assertions(1) | ||
const image = await new Image() | ||
await expect(image.delete()) | ||
.rejects.toEqual(Error('key is not defined')) | ||
done() | ||
}) | ||
|
||
test('return error when file id does not exist', async done => { | ||
expect.assertions(1) | ||
const image = await new Image() | ||
await expect(image.delete(1)) | ||
.rejects.toEqual(Error('file is not found')) | ||
done() | ||
}) | ||
|
||
test('delete a single item from database', async done => { | ||
expect.assertions(1) | ||
const image = await new Image() | ||
await image.add('public/avatars/adrian.png') | ||
const deleted = await image.delete(1) | ||
expect(deleted).toEqual(true) | ||
done() | ||
}) | ||
}) | ||
|
||
describe('count()', () => { | ||
test('return the amount of images in database', async done => { | ||
expect.assertions(1) | ||
const image = await new Image() | ||
await image.add('public/avatars/adrian.png') | ||
await image.add('public/avatars/adrian1.png') | ||
const amount = await image.count() | ||
expect(amount).toEqual(2) | ||
done() | ||
}) | ||
|
||
test('return zero if no images are stored', async done => { | ||
expect.assertions(1) | ||
const image = await new Image() | ||
const amount = await image.count() | ||
expect(amount).toEqual(0) | ||
done() | ||
}) | ||
}) |
Oops, something went wrong.