Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,5 +5,10 @@ Welcome to the Headstorm interview challenge! This repository is designed for c
### Challenges
These are domain specific problems that can be submitted individually. You can choose from backend, frontend, databases, or data-science. You can submit a PR for one, many, or all the challenges.

*** SERVER RUNS ON PORT 3000 ***
Frontend Challenge -> Contact page available at /contact.html
Backend Challenge -> requires a json input with key "numbers" and value being a list of integers
Database Challenge -> In order to start migration function hit endpoint /migrate with GET

### Interviews
These are language specific interview questions and you can choose the language in which you implement your solution.
29 changes: 29 additions & 0 deletions backend_challenge.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
Relational Data Model




Customers
+--------------+--------------+--------------+--------------+-------------+------------+
|RecordID |Name |Cell |Work |Email |Address |
+--------------+--------------+--------------+--------------+-------------+------------+
^
|
|
|
Options |
+--------------+--------------------+---------------------+
|RecordID |Basic Widget Order |Advanced Widget Order|
+--------------+--------------------+---------------------+
^
|
|
|
Plans |
+--------------+--------------------+
|RecordID |Protection Plan |
+--------------+--------------------+

INSERT INTO Customers(RecordID, Name, Cell, Work, Email, Address) Values(v1, v2, v3, v4, v5, v6)
INSERT INTO Options(RecordID, Basic Widget Order, Advanced Widget Order) Values(v1, v2, v3)
INSERT INTO Plans(RecordID, Protection Plan) Values(v1, v2)
59 changes: 59 additions & 0 deletions customers.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
{
"customers" : [
{
"RecordID" : 1,
"Name" : "Joe Smith",
"CellPhone" : "405.867.5309",
"WorkPhone" : "405.867.5309",
"Email" : "[email protected]",
"Address" : "123 Vic Way, Dallas TX 75001",
"BasicWidgetOrder" : 37,
"AdvancedWidgetOrder" : 12,
"ProtectionPlan" : "True"
},
{
"RecordID" : 2,
"Name" : "Luke Skywalker",
"CellPhone" : "405.867.5309",
"WorkPhone" : "405.867.5309",
"Email" : "[email protected]",
"Address" : "123 Vic Way, Dallas TX 75001",
"BasicWidgetOrder" : 37,
"AdvancedWidgetOrder" : 12,
"ProtectionPlan" : "False"
},
{
"RecordID" : 3,
"Name" : "Darth Vader",
"CellPhone" : "405.867.5309",
"WorkPhone" : "405.867.5309",
"Email" : "[email protected]",
"Address" : "Death Star, Space",
"BasicWidgetOrder" : 37,
"AdvancedWidgetOrder" : 12,
"ProtectionPlan" : "False"
},
{
"RecordID" : 4,
"Name" : "Han Solo",
"CellPhone" : "405.867.5309",
"WorkPhone" : "405.867.5309",
"Email" : "[email protected]",
"Address" : "123 test Way, Dallas TX 75001",
"BasicWidgetOrder" : 37,
"AdvancedWidgetOrder" : 12,
"ProtectionPlan" : "False"
},
{
"RecordID" : 5,
"Name" : "Chewbacca",
"CellPhone" : "405.867.5309",
"WorkPhone" : "405.867.5309",
"Email" : "[email protected]",
"Address" : "123 fake Way, Dallas TX 75001",
"BasicWidgetOrder" : 37,
"AdvancedWidgetOrder" : 12,
"ProtectionPlan" : "False"
}
]
}
Binary file added main.exe
Binary file not shown.
163 changes: 163 additions & 0 deletions main.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,163 @@
package main

import (
"encoding/json"
"fmt"
"io/ioutil"
"os"
"sort"
"strings"

"github.com/gofiber/fiber/v2"
)

//DATABASE CHALLENGE STRUCTS

type Customers struct {
Customers []Customer `json:"customers"`
}

type Customer struct {
RecordID int `json:"RecordID"`
Name string `json:"Name"`
Cell string `json:"CellPhone"`
Work string `json:"WorkPhone"`
Email string `json:"Email"`
Address string `json:"Address"`
BasicWidgetOrder int `json:"BasicWidgetOrder"`
AdvancedWidgetOrder int `json:"AdvancedWidgetOrder"`
ProtectionPlan string `json:"ProtectionPlan"`
}

//BACKEND CHALLENGE STRUCTS

type ErrorResponse struct {
Status string `json:"status"`
StatusCode string `json:"status_code"`
Message string `json:"message"`
}

type SuccessResponse struct {
Status string `json:"status"`
StatusCode string `json:"status_code"`
Data []int `json:"data"`
}

type NumberList struct {
Numbers []int `json:"numbers"`
}

var n NumberList

func main() {
app := fiber.New()

//Create struct from list of numbers sent through POST
app.Post("/data", func(c *fiber.Ctx) error {
c.Accepts("application/json")

//error if not sending a list of ints
if err := c.BodyParser(&n); err != nil {
e := ErrorResponse{
Status: "ERROR",
StatusCode: "INCORRECT-TYPE",
Message: "Numbers can only accept a list of integers",
}
fmt.Println(err)
return c.JSON(e)
}
//error if list is less thatn 500
if len(n.Numbers) < 500 {
e := ErrorResponse{
Status: "ERROR",
StatusCode: "INCORRECT-SIZE",
Message: "The list of numbers sent is under the required amount of 500.",
}
return c.JSON(e)
}
//error if list is greater than 500
if len(n.Numbers) > 500 {
e := ErrorResponse{
Status: "ERROR",
StatusCode: "INCORRECT-SIZE",
Message: "The list of numbers sent is over the required amount of 500.",
}
return c.JSON(e)
}

success := SuccessResponse{
Status: "success",
StatusCode: "RECIEVED",
Data: n.Numbers,
}
return c.JSON(success)
})

//GET list of numbers from previous post request
app.Get("/data", func(c *fiber.Ctx) error {
//error if array of 500 ints does not exist or is not the correct size
if len(n.Numbers) != 500 {
e := ErrorResponse{
Status: "ERROR",
StatusCode: "INCORRECT-SIZE",
Message: "The list of numbers does not meet the required size amount of 500. Please send a new list.",
}
return c.JSON(e)
}
//sort array from previous request
sort.Slice(n.Numbers, func(i, j int) bool {
return n.Numbers[i] < n.Numbers[j]
})

success := SuccessResponse{
Status: "success",
StatusCode: "SORTED",
Data: n.Numbers,
}
return c.JSON(success) // => ✋ register
})

app.Get("/migrate", func(c *fiber.Ctx) error {
//Migrate data
fmt.Println("Migrating Customers")
dbMigrate()
return c.JSON("Migrating Customers...")
})

//serve all static public files
app.Static("/", "./public")
//listen on port 3000
app.Listen(":3000")

}

func dbMigrate() {
var customers Customers
//open json file
jsonFile, err := os.Open("customers.json")
if err != nil {
fmt.Println(err)
}
fmt.Println("Successfully Opened customers.json")
//defer close to parse later
defer jsonFile.Close()

byteVal, _ := ioutil.ReadAll(jsonFile)
json.Unmarshal(byteVal, &customers)

for _, c := range customers.Customers {
fmt.Printf("*** MIGRATION FOR CUSTOMER# %v ***\n", c.RecordID)
//insertion for customer table//
fmt.Printf("INSERT INTO customers (record_id, name, cell_phone, work_phone, email, address) VALUES (%v, %v, %v, %v, %v, %v)\n", c.RecordID, c.Name, c.Cell, c.Work, c.Email, c.Address)
//insertion for options table//
fmt.Printf("INSERT INTO options (record_id, basic_widget_order, advanced_widget_order) VALUES (%v, %v, %v)\n", c.RecordID, c.BasicWidgetOrder, c.AdvancedWidgetOrder)
//convert boolean protection plan value to int
prot_plan := 0
if strings.ToLower(c.ProtectionPlan) == "true" {
prot_plan = 1
}
//insertion for plans table//
fmt.Printf("INSERT INTO plans (record_id, protection_plan) VALUES (%v, %v)\n", c.RecordID, prot_plan)
fmt.Printf("\n")
}
}
109 changes: 109 additions & 0 deletions public/contact.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<link rel="shortcut icon" href="https://headstorm.com/wp-content/uploads/2020/09/Headstorm_bolt_512-150x150.png">
<title>Headstorm</title>
<!-- Bootstrap -->
<link href="https://cdn.jsdelivr.net/npm/[email protected]/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-EVSTQN3/azprG1Anm3QDgpJLIm9Nao0Yz1ztcQTwFspd3yD65VohhpuuCOmLASjC" crossorigin="anonymous">
<script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/js/bootstrap.bundle.min.js" integrity="sha384-MrcW6ZMFYlzcLA8Nl+NtUVF0sA7MsXsP1UyJoMp4YLEuNSfAP+JcXn/tWtIaxVXM" crossorigin="anonymous"></script>
<!-- Google RECAPTCHA -->
<script src="https://www.google.com/recaptcha/api.js?render=6LeIxAcTAAAAAGG-vFI1TnRWxMZNFuojJ4WifJWe"></script>
<!-- jQuery -->
<script src="https://code.jquery.com/jquery-3.6.0.slim.min.js" integrity="sha256-u7e5khyithlIdTpu22PHhENmPcRdFiHRjhAuHcs05RI=" crossorigin="anonymous"></script>
<!-- Custom JS -->
<script src="js/main.js"></script>
<style>
body,html {
background-color: #f3f3f3;
height: 100%;
}
.card {
margin: auto;
margin-top: 50px;
width: 60%;
height: auto;
}
#message {
height: 200px;
max-height: 200px;
margin-top: 20px;
margin-bottom: 20px;
}
#submit {
margin-top: 20px
}
@media only screen and (max-width: 900px) {
.card {
width: 90%;
}
}

</style>
</head>
<body>
<nav class="navbar navbar-dark bg-dark">
<div class="container-fluid">
<a class="navbar-brand" href="#">
<img src="https://headstorm.com/wp-content/uploads/2020/09/Headstorm_bolt_512-150x150.png" alt="" width="30" height="30" class="d-inline-block align-text-top">
Headstorm
</a>
</div>
</nav>
<div class="card">
<div class="card-body">
<h5 class="card-title">Contact Us</h5>
<form onsubmit="event.preventDefault(); $.fn.validate(this);">
<div class="row">
<div class="col-6">
<label for="firstname" class="form-label">First Name</label>
<input type="text" class="form-control" id="firstname" required>
</div>
<div class="col-6">
<label for="lastname" class="form-label">Last Name</label>
<input type="text" class="form-control" id="lastname" required>
</div>
</div>
<div class="form-text"></div>
<div class="row">
<div class="col-6">
<label for="email" class="form-label">Email</label>
<input type="email" class="form-control" id="email" aria-describedby="emailHelp" required>

</div>
<div class="col-6">
<label for="phone" class="form-label">Phone</label>
<input type="text" class="form-control" id="phone" required>
</div>
<div id="formHelp" class="form-text">We'll never share your contact information with anyone else.</div>
</div>
<div class="row">
<div class="col">
<div class="form-floating">
<textarea class="form-control" placeholder="Leave a message here" id="message" required></textarea>
<label for="message">Message</label>
</div>
</div>
</div>

<div class="g-recaptcha" data-callback="checkToken" data-sitekey="6LeIxAcTAAAAAJcZVRqyHh71UMIEGNQ_MXjiZKhI"></div>
<button disabled id="submit" class="btn btn-primary">Submit</button>
</form>
</div>
</div>


<script type="text/javascript">
var submission = {}
function checkToken(token) {
console.log(token);
if(token) {
submission["token"] = token;
document.getElementById('submit').disabled = false;
}
}
</script>
</body>
</html>
Loading