Creating GMail in 15 minutes with "pure Magic"
Watch me re-create GMail in 15 minutes using nothing but "pure Magic"
Join the DZone community and get the full member experience.
Join For FreeI 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!
xxxxxxxxxx
create database magic_mail;
use magic_mail;
create table emails (
id int(11) not null auto_increment,
subject varchar(2048) null,
body text null,
direction varchar(20) not null,
primary key (id)
);
create table contacts (
id int(11) not null auto_increment,
email varchar(2048) null,
primary key (id)
);
create table emails_contacts (
email_id int(11) not null,
contact_id int(11) not null,
primary key (email_id, contact_id),
key email_id_key (email_id),
key contact_id_key (contact_id),
constraint email_fky foreign key (email_id) references emails (id) on delete cascade,
constraint contact_fky foreign key (contact_id) references contacts (id) on delete cascade
);
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!
xxxxxxxxxx
wait.mysql.connect:magic_mail
wait.mail.pop3.fetch
.lambda
unwrap:x:+/*
.content
from:x:@.message/*/from/0
subject:x: .message/*/subject
body:x:@.message/**/entity/**/content/[0,1]
.sender-id
wait.set-value:x: .sender-id
wait.mysql.scalar:select id from contacts where email =
x: .content/*/from :
wait.if
eq
get-value:x:@.sender-id
.
.lambda
wait.set-value:x:@.sender-id
wait.mysql.scalar:insert into contacts (email) values (@email); select last_insert_id();
@email:x:@.content/*/from
wait.mysql.scalar:insert into emails (subject, body, direction) values ( , , 'received'); select last_insert_id();
x: .content/*/subject :
@body:x:@.content/*/body
wait.mysql.execute:insert into emails_contacts (email_id, contact_id) values ( , )
x: .mysql.scalar :
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!
xxxxxxxxxx
"smtp":{
"host":"smtp.gmail.com",
"port":465,
"secure":true,
"username":"testing.anarchy@gmail.com",
"password":"ThisIsARubbishPassword",
"from": {
"name":"Testing Anarchy",
"address":"testing.anarchy@gmail.com"
}
},
"pop3":{
"host":"pop.gmail.com",
"port":995,
"secure":true,
"username":"testing.anarchy@gmail.com",
"password":"ThisIsARubbishPassword",
"from": {
"name":"Testing “Anarchy",
"address":"testing.anarchy”@gmail.com"
}
},
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.
xxxxxxxxxx
.arguments
to:string
subject:string
body:string
auth.ticket.verify:root
unwrap:x:+/**
wait.mail.smtp.send
message
to
:x:@.arguments/*/to
subject:x: .arguments/*/subject
entity:text/plain
content:x:@.arguments/*/body
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.
xxxxxxxxxx
public close(data: any) {
if (data) {
this.service.send_email_Post({
to: this.recipient,
subject: data.subject,
body: data.body
}).subscribe(res => {
this.dialogRef.close(data);
}, error => {
this.snackBar.open(error.error.message, 'ok');
});
} else {
this.dialogRef.close();
}
}
Then I slightly modified the HTML to become the following.
xxxxxxxxxx
<mat-form-field
class="entity-edit-field-full">
<input
matInput
placeholder="Recipient"
[(ngModel)]="recipient"
autocomplete="off">
</mat-form-field>
<mat-form-field
*ngIf="canEditColumn('subject')"
class="entity-edit-field-full">
<input
matInput
type="text"
placeholder="subject"
[(ngModel)]="data.entity.subject"
autocomplete="off">
</mat-form-field>
<mat-form-field
*ngIf="canEditColumn('body')"
class="entity-edit-field-full">
<textarea
matInput
rows="10"
type="text"
placeholder="body"
[(ngModel)]="data.entity.body"
autocomplete="off">
</textarea>
</mat-form-field>
<mat-form-field
*ngIf="canEditColumn('direction')"
class="entity-edit-field-hidden">
<input
matInput
type="text"
placeholder="direction"
[(ngModel)]="data.entity.direction"
autocomplete="off">
</mat-form-field>
And then I modified its scss file to become the following.
xxxxxxxxxx
.entity-edit-field-full {
width: 100%;
}
.entity-edit-field-hidden {
display: none;
}
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!
Opinions expressed by DZone contributors are their own.
Trending
-
How To Manage Vulnerabilities in Modern Cloud-Native Applications
-
How To Scan and Validate Image Uploads in Java
-
Application Architecture Design Principles
-
Integrating AWS With Salesforce Using Terraform
Comments