Lola is news application.
This application is designed to post and read the news. Read news can everybody. Users need to register to post and comment news.
News has categories and tags.
To post news user must be an author. Only admin can make a user an author.
To make post author should create draft at first and then publish it.
To update post author should update draft of this post and publish it.
Draft of post create automatically after publishing post. (Author can find it at page with their drafts).
You can install app with:
$ git clone https://github.com/JekaGrib/lola.git
And then in app directory:
$ stack build
Application use PostgreSQL as database.
Before start using app you should create new database and enter details in Configuration
To create database structure you can use file "dbStructure.sql" or use migrations Psql command:
$ \i ./dbStructure.sql;
If you want create DB structure automatically. You can use argument "sm" or "structureMigrate". It will execute script "dbStructure.sql".
$ stack exec lola-exe sm
If you want fill your DB with test data. You can use argument "tm" or "testMigrate". It will execute scripts from "testsMigrations" folder in alphabetical order.
$ stack exec lola-exe tm
If you want execute some other PostgreSQL migrations for change DB structure later, you should put this scripts to "migrations" folder and use "migrate" or "m" argument when run application. It will execute scripts from "migrations" folder in alphabetical order.
$ stack exec lola-exe m
This three(or two) arguments can be used together
$ stack exec lola-exe m sm tm
Program will execute them in the following order: First dbStructure migrations, then test migrations, then other migrations.
First migration, executed with arguments, create in DB table schema_migrations, where you can see all migrations history.
Every time you try execute migrations at startup, terminal will output all migrations history of DB(that was executed with arguments at startup and that schema_migrations table in DB contains). Last executed migrations should be in the end of history list.
If you try execute script, that has already been executed, it wouldn`t run. You can follow it in migrations history in table schema_migrations in DB or in terminal(if you try to execute some migrations).
Application has several default db entities:
- default picture - picture of deleted user
- default user - deleted user
- default author - deleted author
- default category - deleted category
You can create yours default entities or use folder "testMigrations" with migrations examples.
Aftеr creating entities enter ids in Configuration
Before start, you should rename "example.config" to "postApp.config". Then you should make changes in this file.
There is table with descriptions of each values in configuration file, that should be replace to your values.
Section | Value | Description |
---|---|---|
Server | host | Server host |
Server | port | Server port |
Database | host | Database host |
Database | port | Database port |
Database | dbname | Database name |
Database | password | Database password |
defaultValues | defaultPictureId | Id picture of deleted user |
defaultValues | defaultUserId | Id of deleted user |
defaultValues | defaultAuthorId | Id of deleted author |
defaultValues | defaultCategoryId | Id of deleted category |
LimitNumbers | commentNumberLimit | The number of comments given in the response at a time |
LimitNumbers | draftNumberLimit | The number of drafts given in the response at a time |
LimitNumbers | postNumberLimit | The number of posts given in the response at a time |
log | logLevel | The logging level is specified here. The log will only display entries of this level and levels above. It can be one of four levels: DEBUG,INFO,WARNING,ERROR. More information here |
You can run App WITHOUT migrations:
$ stack exec lola-exe
You can run App WITH migrations:
$ stack exec lola-exe m
INT - number from 0 to 9223372036854775805 for id and from 0 to 100000 for page
Success answers:
- For endpoint GET :
- Status 200 OK with resource entity in request body.
- For endpoint POST :
- Status 201 Created with Location of created entity in Headers and with created id in request body.
- For endpoint PUT (Put only update resource, NOT create new ) :
- Status 200 OK with entity of resource in request body.
- For endpoint DELETE :
- Status 204 No Data
Fail answers:
- Status 400 BadRequest with JSON body {«ok»:«false»,«info»:«Some error info»}
- Status 401 Unauthorized with JSON body {«ok»:«false»,«info»:«Some error info»}
- Status 404 (resource or resource entity doesn`t exist)
- Status 413 Request Body Too Large
- Status 414 Request-URI Too Long
- Status 500 Internal server error
- Status 501 Not implemented with JSON body {«ok»:«false»,«info»:«Some error info»}
Methods:
-
User methods :
- To create user:
POST /users
Query parameters:
- password TXT (max50char)
- first_name TXT (max50char)
- last_name TXT (max50char)
- user_pic_id INT
Json answer example:
{"status":"created","user_id":7, "token":"abc"}
- To create admin:
POST /admins
Query parameters:
- create_admin_key TXT (max50char)
- password TXT (max50char)
- first_name TXT (max50char)
- last_name TXT (max50char)
- user_pic_id INT
Json answer example:
{"status":"created","user_id":7, "token":"abc"}
- To get user:
GET /users/INT(user_id)
Json answer example:
{"user_id":4,"first_name":"Petronella","last_name":"Gillingham","user_pic_id":235,"user_pic_url":"http://localhost:3000/pictures/235","user_create_date":"2021-08-01"}
- To delete user:
DELETE /users/user_id INT
Query parameters:
- token TXT (admin token)
- To log in:
POST /logIn
Query parameters:
- user_id INT
- password TXT (max50char)
Json answer example:
{"token":"abc"}
- To create user:
-
Author methods:
- To create author:
POST /authors
Query parameters:
- user_id INT
- author_info TXT (max500char)
- token TXT (admin token)
Json answer example:
{"status":"created","author_id":7}
- To get author:
GET /authors/INT(author_id)
Query parameters:
- token TXT (admin token)
Json answer example:
{"author_id":153,"author_info":"dimonnnn","user_id":400}
- To update author:
PUT /authors/INT(author_id)
Query parameters:
- user_id INT
- author_info TXT (max500char)
- token TXT (admin token)
Json answer example:
{"author_id":153,"author_info":"dimonnnn","user_id":400}
- To delete author:
DELETE /authors/INT(author_id)
Query parameters:
- token TXT (admin token)
- To create author:
-
Category methods :
- To create category:
POST /categories
Query parameters:
- category_name TXT (max50char)
- super_category_id INT optional
- token TXT (admin token)
Json answer example:
{"status":"created","category_id":7}
- To get category:
GET /categories/INT(category_id)
Json answer example:
{"category_id":53,"category_name":"primis","sub_categories":[61],"super_category":{"category_id":7,"category_name":"ut","sub_categories":[52,53]}}
- To update category:
PUT /categories/category_id INT
Query parameters:
- category_name TXT (max50char)
- super_category_id INT optional
- token TXT (admin token)
Json answer example:
{"category_id":201,"category_name":"dim","sub_categories":[11,17]}
- To delete category:
DELETE /categories/category_id INT
Query parameters:
- token TXT (admin token)
- To create category:
-
Tag methods :
- To create tag:
POST /tags
Query parameters:
- tag_name TXT (max50char)
- token TXT (admin token)
Json answer example:
{"status":"created","tag_id":7}
- To get tag:
GET /tags/INT(tag_id)
Json answer example:
{"tag_id":7,"tag_name":"facilisi"}
- To update tag:
PUT /tags/tag_id INT
Query parameters:
- tag_name TXT (max50char)
- token TXT (admin token)
Json answer example:
{"tag_id":151,"tag_name":"snow"}
- To delete tag:
DELETE /tags/tag_id INT
Query parameters:
- token TXT (admin token)
- To create tag:
-
Pictures methods :
- To load picture from url:
POST /pictures
Query parameters:
- pic_url TXT
- To get picture:
GET /pictures/INT(pic_id)
Answer will be picture
- To load picture from url:
-
Draft methods:
- To create new draft:
POST /drafts
User should be author.
Query parameters:
- token TXT (user/admin token) JSON parameters:
- draft_name TXT (max50char)
- draft_category_id INT
- draft_text TXT (max10000char)
- draft_main_pic_id INT
- draft_tags_ids INT ARRAY
- draft_pics_ids INT ARRAY
Request example:
{ "draft_name": "rock", "draft_category_id": 3, "draft_text": "heyhey", "draft_main_pic_id": 501, "draft_tags_ids" : [ 1, 2, 4 ], "draft_pics_ids": [ 5,4,3] }
Json answer example:
{"status":"created","draft_id":7}
- To publish draft (create post(if it is new draft) or update post(if it is post`s draft)):
POST drafts/draft_id INT/posts
User should be draft author.
Query parameters:
- token TXT (user/admin token)
Json answer example :
{"status":"published","post_id":7}
- To get draft:
GET /drafts/draft_id INT
User should be draft author.
Query parameters:
- token TXT (user/admin token)
Json answer example:
{"draft_id":155,"post_id":20,"author":{"author_id":27,"author_info":"info","user_id":27},"draft_name":"aname","draft_category":{"category_id":50,"category_name":"enim","sub_categories":[65,93],"super_category":{"category_id":3,"category_name":"quisque","sub_categories":[33,50,60]}}},"draft_text":"itext","draft_main_pic_id":462,"draft_main_pic_url":"http://localhost:3000/pictures/462","draft_pics":[{"pic_id":279,"pic_url":"http://localhost:3000/pictures/279"},{"pic_id":133,"pic_url":"http://localhost:3000/pictures/133"}],"draft_tags":[{"tag_id":98,"tag_name":"ut"},{"tag_id":71,"tag_name":"quisque"}]}
- To get several drafts for author:
GET /drafts
User should be author, gets only his drafts.
Query parameters:
- page INT
- token TXT (user/admin token)
Json answer example:
{"page":2,"drafts":[{"draft_id":136,"post_id":"NULL","author":{"author_id":27,"author_info":"info","user_id":27},"draft_name":"cname","draft_category":{"category_id":17,"category_name":"varius","sub_categories":[42,51]}}}},"draft_text":"lorem quisque","draft_main_pic_id":66,"draft_main_pic_url":"http://localhost:3000/pictures/66","draft_pics":[{"pic_id":152,"pic_url":"http://localhost:3000/pictures/152"},{"pic_id":245,"pic_url":"http://localhost:3000/pictures/245"}],"draft_tags":[{"tag_id":13,"tag_name":"faucibus"},{"tag_id":50,"tag_name":"vitae"}]},{"draft_id":52,"post_id":"NULL","author":{"author_id":27,"author_info":"info","user_id":27},"draft_name":"id lobortis","draft_category":{"category_id":29,"category_name":"sollicitudin","sub_categories":[62],"super_category":{"category_id":11,"category_name":"id","sub_categories":[27,29,36,38,47]}}}}},"draft_text":"text","draft_main_pic_id":202,"draft_main_pic_url":"http://localhost:3000/pictures/202","draft_pics":[{"pic_id":500,"pic_url":"http://localhost:3000/pictures/500"}],"draft_tags":[{"tag_id":20,"tag_name":"mauris"},{"tag_id":100,"tag_name":"ac"}]}
- To update draft:
PUT drafts/INT(draft_id).
User should be draft author.
Query parameters:
- token TXT (user/admin token)
JSON parameters: - draft_name TXT (max50char)
- draft_category_id INT
- draft_text TXT (max10000char)
- draft_main_pic_id INT
- draft_tags_ids INT ARRAY
- draft_pics_ids INT ARRAY
Json answer example:
{"draft_id":152,"post_id":"NULL","author":{"author_id":27,"author_info":"info,"user_id":27},"draft_name":"rock","draft_category":{"category_id":3,"category_name":"quisque","sub_categories":[33,50,60]},"draft_text":"text","draft_main_pic_id":420,"draft_main_pic_url":"http://localhost:3000/pictures/420","draft_pics":[{"pic_id":5,"pic_url":"http://localhost:3000/pictures/5"},{"pic_id":42,"pic_url":"http://localhost:3000/pictures/42"}],"draft_tags":[{"tag_id":1,"tag_name":"interdum"},{"tag_id":27,"tag_name":"eu"}]}
- To delete draft:
DELETE /drafts/draft_id INT
User should be draft author.
Query parameters:
- token TXT (user/admin token)
- To create new draft:
-
Post methods:
- To get one post:
GET /posts/INT(post_id)
Json answer example:
{"post_id":7,"author":{"author_id":2,"author_info":"info","user_id":2},"post_name":"name","post_create_date":"2018-06-26","post_category":{"category_id":16,"category_name":"odio","sub_categories":[25,26]},"post_text":"text","post_main_pic_id":164,"post_main_pic_url":"http://localhost:3000/pictures/164","post_pics":[{"pic_id":338,"pic_url":"http://localhost:3000/pictures/338"},{"pic_id":356,"pic_url":"http://localhost:3000/pictures/356"}],"post_tags":[{"tag_id":103,"tag_name":"consequat"},{"tag_id":118,"tag_name":"cum"}]}
- To get several posts:
GET /posts
Query parameters required :
- page INT
Query parameters optional: - Filter Query parameters:
- Only one of three date filters:
- created_at DATE (Format yyyy-mm-dd, example 2020-02-02)
- created_at_gt DATE (Format yyyy-mm-dd, example 2020-02-02)
- created_at_lt DATE (Format yyyy-mm-dd, example 2020-02-02)
- Only one of three tag filters:
- tag INT
- tags_in ARRAY of INT
- tags_all ARRAY of INT
- Only one of three include filters:
- name_in TXT
- text_in TXT
- everywhere_in TXT (everywhere search in post text, post name, authors first name, category name, tag name)
- category_id INT
- author_name TXT
- Only one of three date filters:
- Sort Query parameters:
Default sort — DESC by date,DECS by post_id. Sort value can be only «asc» or «desc». If there are more then one sort Query parameters, sort priority corresponds to the order in which the parameter appears in the query string. Two last extra sort layers is always by date and post id (DESC by default or ASC — if there is «sort_by_date=asc» in query string in any place)- sort_by_pics_number TXT (asc/desc)
- sort_by_category TXT (asc/desc)
- sort_by_author TXT (asc/desc)
- sort_by_date TXT (asc/desc)
Json answer example:
{"page":1,"posts":[{"post_id":40,"author":{"author_id":35,"author_info":"info","user_id":35},"post_name":"name","post_create_date":"2021-02-17","post_category":{"category_id":15,"category_name":"donec","sub_categories":[37,46,58]}}}}},"post_text":"text","post_main_pic_id":18,"post_main_pic_url":"http://localhost:3000/pictures/18","post_pics":[{"pic_id":40,"pic_url":"http://localhost:3000/pictures/40"},{"pic_id":31,"pic_url":"http://localhost:3000/pictures/31"}],"post_tags":[{"tag_id":6,"tag_name":"eget"},{"tag_id":63,"tag_name":"quis"}]},{"post_id":16,"author":{"author_id":33,"author_info":"info","user_id":33},"post_name":"name","post_create_date":"2021-02-02","post_category":{"category_id":15,"category_name":"donec","sub_categories":[37,46,58]}}},"post_text":"text","post_main_pic_id":96,"post_main_pic_url":"http://localhost:3000/pictures/96","post_pics":[{"pic_id":114,"pic_url":"http://localhost:3000/pictures/114"},{"pic_id":421,"pic_url":"http://localhost:3000/pictures/421"}],"post_tags":[{"tag_id":29,"tag_name":"amet"},{"tag_id":67,"tag_name":"cras"}]}]}
- To get one post:
-
Comment methods:
- To create comment:
POST comments/post_id INT
Query parameters:
- comment_text TXT (max500char)
- token TXT (user/admin token)
Json answer example:
{"status":"created","comment_id":7}
- To get one comment:
GET /comments/INT(comment_id)
Json answer example:
{"comment_id":1001,"comment_text":"cool","post_id":16,"user_id":27}
- To get several comments for post:
GET /comments
Query parameters:
- post_id INT
- page INT
Json answer example:
{"page":1,"post_id":17,"comments":[{"comment_id":971,"comment_text":"text","user_id":489},{"comment_id":952,"comment_text":"text","user_id":348}]}
- To update comment:
PUT /comments/comment_id INT
User should be comment author.
Query parameters:
- comment_text TXT(max500char)
- token TXT (user/admin token)
Json answer example:
{"comment_id":1001,"comment_text":"cool","post_id":16,"user_id":27}
- To deleteComment.
DELETE /comments/comment_id INT
User should be admin or comment/post author.
Query parameters:
- token TXT (user/admin token)
- To create comment:
There are 4 logging levels from lowest to highest:
- DEBUG
- INFO
- WARNING
- ERROR
The logging level is specified in Configuration. The log will only display entries of this level and levels above.
Description of project sections:
- App - module with main app functionality. Here actions are selected depending on the request. Has handler.
- Methods - section, that contain modules with handlers from all methods. Module "Methods.hs" has handler, that combines all methods.
- General methods handlers:
- Admin
- Author
- Category
- Comment
- Draft
- Picture
- Post
- Tag
- User
- Common methods handlers(can be used in several general methods handlers):
- Auth
- Exist
- DeleteMany
- MakeCatResp
- General methods handlers:
- Psql - folder for work with database:
- "ToQuery" section - parsing db requests
- "Selecty.hs" - parsing db responses
- "Methods" folder - all IO methods for corresponding handlers.
- "Migration.hs" - work with psql migrations.
- Api - response and request parsing.
- Conf - configuratuion parsing and db connection.
- Logger - logging.
- Error - work with exceptions.
- TryRead - some more parsing functions.
- Types - some more project types.
You can test app with:
$ stack test
Modules, which has handlers, have unit-tests:
- General methods handlers:
- Admin
- Author
- Category
- Comment
- Draft
- Picture
- Post
- Tag
- User
- Common methods handlers:
- Auth
- MakeCatResp
All database wrong answers (multiple,emty) are tested at Tag and Picture modules. In other modules only success and some local errors are tested.
For E2E tests with database you can use folder "scripts". Also you can use SQL migration scripts from folder "testMigrations".