Skip to content
Gaetan Duron edited this page Jun 8, 2018 · 19 revisions

matcha-logo

Welcome to the Matcha wiki!

Summary


Introduction

This Wiki is here to help contributors work independently and easily use others tools. Every change to the architecture or creation of new tools is documented here.

API

To setup Matcha on your computer, you will need to create a Postgresql database, a cloudinary account and a facebook app.

Postgresql

I hosted my database on heroku, I would recommend you to do so too.

How to do this:

1 - Go to heroku.com and create a new server

2 - Add to your heroku server a Postgresql database (this will give you a database url)

3 - Create a file at the root of the project with this command and don't forget to replace the url:

echo 'DATABASE_URL=your-database-url' > .env.development

4 - You now have to use this schema in the database https://github.com/gaeduron/Matcha/blob/master/server/postgresql/databaseSchema.js

To do so use the heroku pg CLI to connect to your database and register the schema

Cloudinary

1 - create an account

2 - change in this file the cloudinary URL

src/components/photo/PhotoUploader.js

line 187 : const url = https://api.cloudinary.com/v1_1/${cloudName}/upload;

Facebook

  • create an app and get your keys once more

.env.development

You should have a .env.development like this

%>cat .env.development

DATABASE_URL=your-url

FB_CLIENT_SECRET=your-key

Server

HTTP is only used to send the React application, and to start the Websocket connexion (HTTP handshake). All subsequent requests to the server will use WebSockets.

Architecture

architecture-schema

Socket

Everything start with a socket listener:

  1. Located at “matcha/server/socket/listeners/yourListener.js”.
  2. This socket listener call the associated Action function.
  3. Look for errors in the Action response ex: { error: 'Oups, something went wrong' }
  4. Log and Emit, the Action response. Errors are always emitted to 'notify_error'.

Action

The Action function manage all the Steps:

  1. Located at “matcha/server/actions/yourAction.js”.
  2. It import all needed Models, like the user model.
  3. Manage the data flow between each steps.
  4. Check for errors at each step, any occurring error is immediately returned
  5. Log step description before each steps
  6. Return the final result of the operation

Models

Steps are so tiny that you will never have to refactor:

  1. Located at “matcha/server/models/”

  2. Models are imported with the path of their directory ex: “matcha/server/models/user”

  3. They have two sub-directory, steps and paths:

    *steps: Minimalist functions, highly re-useable. In Actions, each new function created should be thought as a step and coded that way. ex: findUserByID.js or hashPassword.js

    *paths: General functions, exist only if needed in 2 or more action, always replace multiples functions defined in steps. ex: find.jd (will find any user matching the given object)

  4. You can access any steps or paths with yourModel.yourStep(object);

    ex: user.updateConnection({id: 42, connected: 1312358dgadg78 })

  5. Arguments are objects that will be destructured

    ex: const updateConnection = ({ id, connected }) => { ... };

  6. The return value is an object with only the new data return { result: true };

  7. Models folder organisation goes like this:

user_model

postgresql

not much to say

Tools

Logger

Logger use winston package, you can see the documentation here. We use Logger to log info, errors, warning, and success or failure messages from the Actions. Logged errors are also saved in a log file.

Logger work like this:

logger.info('Socket is saved'); => [2017-11-29T17:16:07.101Z] [info]: Socket is saved

MyErrors

MyError is a core tool in the server Actions. It is used to create error that can be returned and emitted to the client. It also log the error in the console.

To use it you need to import "matcha/server/errors" Here is an exemple:

const myError = require('../../../errors');

const error = {
   tooLong: myError.newFailure({
      log: length => `User email is too long (${length}) characters`,
      message: length => `Your email is too long (${length}),
      254 characters max.`,
   }),
   format: myError.newFailure({
      log: 'User email contain forbidden characters',
      message: 'Your email should respect asked format',
   }),
};

const validateEmail = ({ email }) => {
   const regex = /^[a-zA-Z0-9.!#$%&'*+/=?^_`{|}~-]+@[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}
                   [a-zA-Z0-9])?(?:\.[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)*$/;

   if (email.length > 254) { return error.tooLong(email.length); }
   if (!regex.test(email)) { return error.format(); }
   return null;
};

Client

Notifications

You can trigger notifications from the server with websocket. All you need to do is emit on the socket one of theses events:

Success

Event name: "notificationSuccess"

Argument: string ex:"Password reset successful !"

Exemple:

	socket.emit(
		'notification_success',
		'Password reset successful, you can now login !'
	);

Info

Event name: "notificationInfo"

Argument: string ex: "Please check your inbox"

Exemple:

	socket.emit(
		'notificationInfo',
		'Please check your inbox'
	);

Warning

Event name: "notificationWarning"

Argument: string ex: "Your data is outdated"

Exemple:

	socket.emit(
		'notificationWarning',
		'Your data is outdated'
	);

Error

Event name: "notificationError"

Argument: string ex: "Wrong password"

Exemple:

	socket.emit(
		'notificationError',
		'Wrong Password'
	);

Visit

Event name: "notificationVisit"

Argument:

	{
		login: **string** user login,
		profilePicture: **string** profile picture url
	}

Exemple:

	socket.emit(
		'notificationInfo',
		{
			login,
			profilePicture,
		},
	);

Like

Event name: "notificationLike"

Argument: { login: string user login, profilePicture: string profile picture url }

Exemple:

	socket.emit(
		'notificationLike',
		{
			login,
			profilePicture,
		},
	);

Unlike

Event name: "notificationUnlike"

Argument:

	{
		login: **string** user login,
		profilePicture: **string** profile picture url
	}

Exemple:

	socket.emit(
		'notificationUnlike',
		{
			login,
			profilePicture,
		},
	);

Match

Event name: "notificationMatch"

Argument:

	{
		login: **string** user login,
		profilePicture: **string** profile picture url
	}

Exemple:

	socket.emit(
		'notificationMatch',
		{
			login,
			profilePicture,
		},
	);

Chat

Event name: "notificationChat"

Argument:

{
	login: "string" user login,
	message: "string" user message,
	profilePicture: "string" profile picture url
}

Exemple:

socket.emit(
   'notificationChat',
   {
      login,
      message,
      profilePicture,
   },
);