{{announcement.body}}
{{announcement.title}}

Creating GMail in 15 minutes with "pure Magic"

DZone 's Guide to

Creating GMail in 15 minutes with "pure Magic"

Watch me re-create GMail in 15 minutes using nothing but "pure Magic"

· Web Dev Zone ·
Free Resource

I wanted to have some more advanced use cases for Magic, so I figured what could possibly be more insulting, than me implementing GMail in 15 minutes - A software system probably dozens of developers have maintained, for some roughly 15 years? So I did it ...

The thing about GMail, or rather its core, is that fundamentally it's just a POP3 server and an SMTP server, and the ability to send and retrieve emails over these two standards. In addition to saving emails sent and received into a database. So obviously, we'll need a database. Check!

MySQL
 




xxxxxxxxxx
1
28


 
1
create database magic_mail;
2
use magic_mail;
3
 
          
4
create table emails (
5
  id int(11) not null auto_increment,
6
  subject varchar(2048) null,
7
  body text null,
8
  direction varchar(20) not null,
9
  primary key (id)
10
);
11
 
          
12
create table contacts (
13
  id int(11) not null auto_increment,
14
  email varchar(2048) null,
15
  primary key (id)
16
);
17
 
          
18
create table emails_contacts (
19
  email_id int(11) not null,
20
  contact_id int(11) not null,
21
  primary key (email_id, contact_id),
22
  key email_id_key (email_id),
23
  key contact_id_key (contact_id),
24
  constraint email_fky foreign key (email_id) references emails (id) on delete cascade,
25
  constraint contact_fky foreign key (contact_id) references contacts (id) on delete cascade
26
);


Execute the above SQL script, using Magic, the same way I do in the above video, and we're all set. Of course, I'm not creating a multi user enterprise GMail system, which you can understand by the above script - But modifying the above script to become a multi user solution, could probably be done in 30 minutes or so. If you wish to try this out, you'd probably need some sort of "account settings" table, and probably you'd want to add the tables to your main magic database, and create some sort of referential integrity from your "users" table to your account settings table, in addition to your emails/contacts table(s). The above SQL script creates the following 3 tables though.

  • emails (email content)
  • contacts (people sending me emails)
  • emails_contacts (many 2 many relationship between the two above tables)

Now, if I can create a background thread, fetching emails from my POP3 server, pushing emails into the above database tables - We're already a long way, right? Check!

Plain Text
 




xxxxxxxxxx
1
26


1
wait.mysql.connect:magic_mail
2
   wait.mail.pop3.fetch
3
      .lambda
4
         unwrap:x:+/*
5
         .content
6
            from:x:@.message/*/from/0
7
            subject:x:@.message/*/subject
8
            body:x:@.message/**/entity/**/content/[0,1]
9
         .sender-id
10
         wait.set-value:x:@.sender-id
11
            wait.mysql.scalar:select id from contacts where email = @email
12
               @email:x:@.content/*/from
13
         wait.if
14
            eq
15
               get-value:x:@.sender-id
16
               .
17
            .lambda
18
               wait.set-value:x:@.sender-id
19
                  wait.mysql.scalar:insert into contacts (email) values (@email); select last_insert_id();
20
                     @email:x:@.content/*/from
21
         wait.mysql.scalar:insert into emails (subject, body, direction) values (@subject, @body, 'received'); select last_insert_id();
22
            @subject:x:@.content/*/subject
23
            @body:x:@.content/*/body
24
         wait.mysql.execute:insert into emails_contacts (email_id, contact_id) values (@email, @contact)
25
            @email:x:@wait.mysql.scalar
26
            @contact:x:@.sender-id


The above is the Hyperlambda we wrap into a scheduled task in the video, which is periodically polling emails from my POP3 server every 5 seconds. It basically just polls emails from my POP3 server, and insert emails into the emails table, and the sender's email address into my contacts table. In addition, it creates an association between the contacts and the emails table for me. It's important you create a scheduled task containing the above Hyperlambda though. You can see a screenshot of how below.

In addition we'll need to configure our SMTP and POP3 server settings, right? Check!

JSON
 




xxxxxxxxxx
1
23


 
1
    "smtp":{
2
      "host":"smtp.gmail.com",
3
      "port":465,
4
      "secure":true,
5
      "username":"testing.anarchy@gmail.com",
6
      "password":"ThisIsARubbishPassword",
7
      "from": {
8
        "name":"Testing Anarchy",
9
        "address":"testing.anarchy@gmail.com"
10
      }
11
    },
12
    "pop3":{
13
      "host":"pop.gmail.com",
14
      "port":995,
15
      "secure":true,
16
      "username":"testing.anarchy@gmail.com",
17
      "password":"ThisIsARubbishPassword",
18
      "from": {
19
        "name":"Testing “Anarchy",
20
        "address":"testing.anarchy”@gmail.com"
21
      }
22
    },


Stuff the above into your appsettings.json file, and you have configured your POP3 and SMTP settings. Notice, you'll have to exchange the actual POP3/SMTP settings, but if you're using GMail for POP3 and SMTP, all you have to do is to exchange your username and password. And yes, I did change my own GMail password ... ;)

In addition you'll need to grant access to your GMail account using POP3, which you can see how to do in the above video. Make sure you also open your GMail account for "insecure apps", which is a really bad name for allowing direct POP3 and SMTP access may I add. The only thing remaining is to create a secured HTTP REST endpoint for actually being able to send emails, which you can see the code for below, and we're almost completely done.

Plain Text
 




xxxxxxxxxx
1
14


 
1
.arguments
2
   to:string
3
   subject:string
4
   body:string
5
auth.ticket.verify:root
6
unwrap:x:+/**
7
wait.mail.smtp.send
8
   message
9
      to
10
         :x:@.arguments/*/to
11
      subject:x:@.arguments/*/subject
12
      entity:text/plain
13
         content:x:@.arguments/*/body
14
 
          


The above code, is the Hyperlambda I put into my "modules/magic_mail/send-email.post.hl" file in the above video. Now we can scaffold our app, which ends up looking roughly like this once we're done.

Notice, the only change I actually applied to the scaffolded app, was to slightly modify its "edit.emails.component.ts" file. The change I applied, was the following TypeScript code.

TypeScript
 




xxxxxxxxxx
1
16


 
1
  public close(data: any) {
2
    if (data) {
3
      this.service.send_email_Post({
4
        to: this.recipient,
5
        subject: data.subject,
6
        body: data.body
7
      }).subscribe(res => {
8
        this.dialogRef.close(data);
9
      }, error => {
10
        this.snackBar.open(error.error.message, 'ok');
11
      });
12
    } else {
13
      this.dialogRef.close();
14
    }
15
  }
16
 
          


Then I slightly modified the HTML to become the following.

HTML
 




xxxxxxxxxx
1
44


 
1
  <mat-form-field
2
    class="entity-edit-field-full">
3
    <input
4
      matInput
5
      placeholder="Recipient"
6
      [(ngModel)]="recipient"
7
      autocomplete="off">
8
  </mat-form-field>
9
 
          
10
  <mat-form-field
11
    *ngIf="canEditColumn('subject')"
12
    class="entity-edit-field-full">
13
    <input
14
      matInput
15
      type="text"
16
      placeholder="subject"
17
      [(ngModel)]="data.entity.subject"
18
      autocomplete="off">
19
  </mat-form-field>
20
 
          
21
  <mat-form-field
22
    *ngIf="canEditColumn('body')"
23
    class="entity-edit-field-full">
24
    <textarea
25
      matInput
26
      rows="10"
27
      type="text"
28
      placeholder="body"
29
      [(ngModel)]="data.entity.body"
30
      autocomplete="off">
31
    </textarea>
32
  </mat-form-field>
33
 
          
34
  <mat-form-field
35
    *ngIf="canEditColumn('direction')"
36
    class="entity-edit-field-hidden">
37
    <input
38
      matInput
39
      type="text"
40
      placeholder="direction"
41
      [(ngModel)]="data.entity.direction"
42
      autocomplete="off">
43
  </mat-form-field>
44
 
          


And then I modified its scss file to become the following.

SCSS
 




xxxxxxxxxx
1


1
 .entity-edit-field-full {
2
     width: 100%;
3
 }
4
 
          
5
 .entity-edit-field-hidden {
6
     display: none;
7
 }


And we're done. Basically, 80% of the (most important) features from GMail, allowing you to retrieve, send, and read emails, in an Angular + .Net Core Web application.

Insulting GMail's developers - Check!

Topics:
angular, dotnet

Opinions expressed by DZone contributors are their own.

{{ parent.title || parent.header.title}}

{{ parent.tldr }}

{{ parent.urlSource.name }}