P.S: I never used rails nor ruby, docker, elastic search before now, the project runs good if you're using running local 'elasticseach' and 'rabbitmq' CLI commands, but with docker i faced some issues connecting to mysql database!

P.S: i'm using the latest version of docker and docker compose, the command runs docker compose up --build --force-recreate, build flag is used to run dockerfile when building images, force-recreate flag is used to recreate the image if you have it already existing on your device.

Challenge Statement

We will only write docker-compose up to run the whole stack.

It’s required to build a chat system. The system should allow creating new applications where each application will have a token(generated by the system) and a name(provided by the client). The token is the identifier that devices use to send chats to that application. Each application can have many chats. a chat should have a number. Numbering of chats in each application starts from 1 and no 2 chats in the same application may have the same number. The number of the chat should be returned in the chat creation request. A chat contains messages and messages have numbers that start from 1 for each chat. The number of the message should also be returned in the message creation request. The client should never see the ID of any of the entities. The client identifies the application by its token and the chat by its number along with the application token.

We need you to create a RESTful API to simulate this behavior: * create an application with unique generated token by code and name by user. * create a chat in a certain application. * create a message in a certain chat. * get chats in an application. * get messages in a chat. * search for a message parial or full body with elasticsearch.

The requirements:

* Elasticsearch with partial filtering for messaging allowing partial and full text search.

* Try to minimize the queries and avoid writing directly to MySQL while serving the
requests

* You should add a Readme containing instructions to run your code.

* You should use Ruby on Rails(V5)

* Use MySQL as your main datastore. You’re allowed to use any other component you
need along with MySQL. You may want to check out REDIS.

Bonus:

* Write APIs in golang
* Write specs for your code.

My Approach

Installing ruby on rails V5 using RVM, and creating an API boiler plate with this tutorial https://www.youtube.com/watch?v=QojnRc7SS9o&t=125s

Create three database models:
    apps
    chats
    messages

Commands for generating database models:

$ rails g model apps token:string name:text chats_count:integer

$ rails g model chats app_token:string chat_id:integer messages_count:integer

$ rails g model messages chat_id:integer body:text app_token:string

=========================================================================================

A design choice here was to add app_token in each chat record instead of making a multi-variate attribute in apps table, so that we can check if chat_id exists for app_token without the need to iterate through all chat ids for a specific application!

  1. The system should allow creating new applications where each application will have a token(generated by the system) and a name(provided by the client). The token is the identifier that devices use to send chats to that application.

    -- created an end-point for creating new applications, accepts 'name' as a body parameter from user, then generate a unique random hex string to be a primary key for each application.

  2. Each application can have many chats. a chat should have a number. Numbering of chats in each application starts from 1 and no 2 chats in the same application may have the same number

    -- created and end-point for registering a new chat using 'app_token' as a parameter, then get 'chats_count' attribute for that application and assign the chat_id attribute to be 'chats_count+1', then no chats in the same applications will have the same id

  3. A chat contains messages and messages have numbers that start from 1 for each chat. The number of the message should also be returned in the message creation request

    -- created an end-point for registering a new message to a specific chat taking app_token, chat_id and body of the message as a parameter, returning number of messages of the chat record.

  4. Add an endpoint for searching through messages of a specific chat. It should be able to partially match messages’ bodies. You must use ElasticSearch for this.

    --configured elasticsearch with the messages model to update it-self as the messages table be modified/added to/ deleted, it uses Ngrams (trigrams my case) to compare texts, i used it with message body as that's the text we want to search, it breaks each word in N parts ( three my case ) so it could compare words together.

  5. The applications table should contain a column called chats_count that contains the number of chats for this application. Similarly, the chats table should contain a column called messages_count that contains the number of messages in this chat.

    -- configured above when creating database models.

  6. Try to minimize the queries and avoid writing directly to MySQL while serving the requests(especially for the chats and messages creation endpoints). You can use a queuing system to achieve that.

    -- configured rabbitmq service for that, a rabbitmq publisher is called when adding a new application to our system.

  7. Your app should be containerized. We should only write docker-compose up to run the whole stack.

    -- configured docker-compose.yml file and Dockerfile to containarize the whole stack (rabbitmq, rails, elasticsearch, mysql), although i couldn't connect to mysql correctly using docker.

========================================================================================

Create a Docker Compose file:

Elasticsearch container.
Rabbitmq container.
Rails container.
mysql container.

getting ready out of the box images for rabbitmq, elasticsearch, mysql, looking online to see how others could containarize rails app with multiple images made it easier.

Getting rails image to connect with mysql image: - when running $(docker compose up --build --force-recreate) in terminal all images run correctly, but rails app can't connect to mysql database.

- tried multiple solutions available online like `stackoverflow` and others..., but no solution worked although i could connect to mysql with terminal meaning the image is running correct, and couldn't find issue with my configurations, i'm sure that there's a solution somewhere but i couldn't get it in time.

List of POSTMAN requests and curl commands to test the app:

I exposed rails api on port 3000 and elasticsearch on port 9200

  1. Create New application

POST request.

URL: http://localhost:3000/api/v1/applications

BODY PARAMS: name

  1. update application name

PUT request.

URL: http://localhost:3001/applications/<app_token>

BODY PARAMS: app_token, name

  1. get application

GET request.

URL: http://localhost:3000/api/v1/applications/<app_token>

BODY PARAMS: app_token

  1. create a new chat

POST request.

URL: http://localhost:3000/api/v1/chats

BODY PARAMS: app_token #that you want to assign the chat to

  1. get all application chats

GET request.

URL: http://localhost:3000/api/v1/chats/<app_token>

BODY PARAMS: app_token

  1. create a new message

POST request.

URL: http://localhost:3000/api/v1/messages

BODY PARAMS: app_token, chat_id, body

  1. get all message in a chat

GET request.

URL: http://localhost:3000/api/v1/messages

BODY PARAMS: app_token, chat_id.

  1. search a message (partially or fully)

curl -X POST
http://localhost:9200/messages/_search
-H 'cache-control: no-cache'
-H 'content-type: application/json'
-d '{
"query": { "match": { "body": 'text' } } }'

TODO

* fix rails image connection to sql image.
* write testcases to address all scenarios.