Skip to content
Finn edited this page Sep 5, 2016 · 4 revisions

For the API calls that the mobile makes, see here. https://github.com/Pear-Trading/Foodloop-Mobile/wiki/API-Calls

This will outline the general flow of the server code based on the type of post submitted to it.
It should be noted that in all of the HTTP posts that at the end or in certain points, it will return a true or false response back to the client. How the client responds is typically with a thanking message if the response is "True" and with an error message if it is "False" to the user. These can be seen with a Mojo::JSON->true/false throughout the code.

App Start

The server is initialised and will connect to the database if the config is correct.

my $config = plugin Config => {file => 'myapp.conf'};
my $dbh = DBI->connect($config->{dsn},$config->{user},$config->{pass}) or die "Could not connect";

Initial Token Registration

When the user is given the token, they then submit initially that token to the app. At first, the code receives the token in json format from an HTTP post to "/token".
my $json = $self->req->json; my $account = $self->get_account_by_token( $json->{token} );
The code then in a sub "get_account_by_token" pulls from the database the "username" and "keyused" field from the database in the matching row of the key that was pulled. This gives us data in "username" and "keyused".

sub get_account_by_username {
  my ( $self, $username ) = @_;
  return $self->db->selectrow_hashref(
    'SELECT keyused, username FROM accounts WHERE username = ?',
    {},
    $username,
  );
}

The code will then check to see if the username from the data is undefined (blank so non-existent) or if the key is defined as already having been used. If either of those are true, it will return an error and stop the operation, otherwise the code continues as though nothing has occurred.

if ( ! defined $account || $account->{keyused} eq 't' ) {
   return $self->render( json => {
      success => Mojo::JSON->false,
      message => 'Token is invalid or has already been used',
    });
}

Having passed this check, the code will send back to the app response data to the HTTP post with the username that was pulled earlier.

return $self->render( json => {
  username => $account->{username},
     success => Mojo::JSON->true,
  });
};

User Registration

The app will proceed to registering user details after the token has been submitted and the given username returned to the user client side. The client then fills in their remaining user data in a form, which is then HTTP posted back to the server in "/upload". The server code in this specified post will receive the data in json format, then will define to "account" the username, similarly to how the token key was in the token checking.

my $json = $self->req->json;
my $account = $self->get_account_by_username( $json->{username} );

Next, an if and else statement is used to first check if the username is a defined one, for whatever reason in case an http submit is received where there is an undefined username to this http post, followed by a check to see if the key has been used already.

unless ( defined $account ) {
    return $self->render( json => {
      success => Mojo::JSON->false,
      message => 'Username not recognised, has your token expired?',
    });
  } elsif ( $account->{keyused} eq 't' ) {
    return $self->render( json => {
  success => Mojo::JSON->false,
      message => 'Token has already been used',
});

Assuming those checks have passed, it proceeds into preparing the insert statement into the database of the remaining userdata row which matches the username, followed by executing the insert statement.

my $insert = $self->db->prepare("UPDATE accounts SET 'name' = ?, email = ?, postcode = ?, age = ?, gender = ?, grouping = ?, password = ?, keyused = ? WHERE username = ?");
  $insert->execute(
        @{$json}{ qw/ name email postcode age gender grouping password / }, 'True', $account->{username},
);

User data editing

It may also be a case later the user may wish to edit some of their details using the User Edit page in the app. If this occurs, the user will submit a form in the client app, then will send in a json these changed details along with the username of the user for it to be matched to in json format with an HTTP post to "/edit".
The rest of the code hereon in matches that of user registration earlier, however at the point of checking is the key has been used in the elseif, it will check if the key has been used and if it hasn't, it will say that users key has not been used yet.

} elsif ( $account->{keyused} ne 't' ) {
    return $self->render( json => {
      success => Mojo::JSON->false,
      message => 'Token has not been used yet!',
    });
}

The data is then sent to the database and updates the relevant row.

my $insert = $self->db->prepare("UPDATE accounts SET 'name' = ?, postcode = ?, age = ?, gender = ?, WHERE username = ?");
  $insert->execute(
    @{$json}{ qw/ name postcode age gender / }, $account->{username},
  );

Receipt uploading

The most complex part of the app but also the most important is uploading the receipts themselves. The client app will send the image and a number of parameters with that image upon upload to the server in an HTTP post to "/upload" using the apps own image file upload code, which the app all receives in one go. A key is added to the upload URL at the end of it, which is designated in config.js client-side. The code at first checks to see if the key sent along with it matches that username as an extra measure.

# Fetch parameters to write to DB
  my $key = $self->param('key');
# This will include an if function to see if key matches
  unless ($key eq $config->{key}) {
   return $self->render( json => { success => Mojo::JSON->false }, status => 403 );
  }

Firstly, all the parameters that were sent along with the image are defined, with the username of the user that sent them, the company at which they spent at and how much they spent.

my $username = $self->param('username');
my $company = $self->param('company');
my $currency = $self->param('currency');

Next, the image is as part of the client sent with headers such as what file type it is and the like. This is used to first pull out the image type, then checked to see if it is the right file type to be an image, throwing it out the operation if it is the wrong image type. It will then rewrite the name of the file to be a timestamp with the extension attached, to keep it a valid file.

my $file = $self->req->upload('file');
# Get image type and check extension
  my $headers = $file->headers->content_type;
# Is content type wrong?
 if ($headers ne 'image/jpeg') {
    return $self->render( json => {
      success => Mojo::JSON->false,
      message => 'Wrong image extension!',
    });
  };
# Rewrite header data
  my $ext = '.jpg';
  my $uuid = Data::UUID->new->create_str;
  my $filename = $uuid . $ext;

The file is then moved into a directory on the server, where it is stored ad infinitum.

$file->move_to('images/' . $filename);

Having added the file to the server in the relevant folder, the insert statement into the database is prepared. Inserted into the "foodloop" table is a row with the username, the other parameters aforementioned and the filename, so that the relevant file is effectively indexed in the row. After statement preparation, it is then executed.

my $insert = $self->db->prepare('INSERT INTO foodloop (username, company, currency, filename) VALUES (?,?,?,?)');
$insert->execute($username, $company, $currency, $filename);