Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[feature] Candidate Postings #42

Open
wants to merge 2 commits into
base: main
Choose a base branch
from
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
210 changes: 209 additions & 1 deletion pkg/data/data.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,13 +27,31 @@ type Job struct {
PublishedAt time.Time `db:"published_at"`
}

type Role struct {
ID string `db:"id"`
Name string `db:"name"`
Email string `db:"email"`
Phone sql.NullString `db:"phone"`
Role string `db:"role"`
Resume string `db:"resume"`
Linkedin sql.NullString `db:"linkedin"`
Website sql.NullString `db:"website"`
Github sql.NullString `db:"github"`
CompLow sql.NullString `db:"comp_low"`
CompHigh sql.NullString `db:"comp_high"`
PublishedAt time.Time `db:"published_at"`
}

const (
ErrNoPosition = "Must provide a Position"
ErrNoOrganization = "Must provide a Organization"
ErrNoEmail = "Must provide an Email Address"
ErrInvalidUrl = "Must provide a valid Url"
ErrInvalidEmail = "Must provide a valid Email"
ErrNoUrlOrDescription = "Must provide either a Url or a Description"
ErrNoName = "Must provide a Name"
ErrNoRole = "Must provide a Role"
ErrNoResume = "Must provide a Resume"
)

func (job *Job) Update(newParams NewJob) {
Expand All @@ -47,6 +65,30 @@ func (job *Job) Update(newParams NewJob) {
job.Description.Valid = newParams.Description != ""
}

func (role *Role) Update(newParams NewRole) {
role.Name = newParams.Name
role.Role = newParams.Role
role.Resume = newParams.Resume

role.Phone.String = newParams.Phone
role.Phone.Valid = newParams.Phone != ""

role.Linkedin.String = newParams.Linkedin
role.Linkedin.Valid = newParams.Linkedin != ""

role.Website.String = newParams.Website
role.Website.Valid = newParams.Website != ""

role.Github.String = newParams.Github
role.Github.Valid = newParams.Github != ""

role.CompLow.String = newParams.CompLow
role.CompLow.Valid = newParams.CompLow != ""

role.CompHigh.String = newParams.CompHigh
role.CompHigh.Valid = newParams.CompHigh != ""
}

func (job *Job) RenderDescription() (string, error) {
if !job.Description.Valid {
return "", nil
Expand All @@ -65,7 +107,27 @@ func (job *Job) RenderDescription() (string, error) {

var b bytes.Buffer
if err := markdown.Convert([]byte(job.Description.String), &b); err != nil {
return "", fmt.Errorf("failed to convert job descroption to markdown (job id: %s): %w", job.ID, err)
return "", fmt.Errorf("failed to convert job description to markdown (job id: %s): %w", job.ID, err)
}

return b.String(), nil
}

func (role *Role) RenderResume() (string, error) {
markdown := goldmark.New(
goldmark.WithExtensions(
extension.NewLinkify(
extension.WithLinkifyAllowedProtocols([][]byte{
[]byte("http:"),
[]byte("https:"),
}),
),
),
)

var b bytes.Buffer
if err := markdown.Convert([]byte(role.Resume), &b); err != nil {
return "", fmt.Errorf("failed to convert resume to markdown (role id: %s): %w", role.ID, err)
}

return b.String(), nil
Expand All @@ -78,6 +140,13 @@ func (job *Job) Save(db *sqlx.DB) (sql.Result, error) {
)
}

func (role *Role) Save(db *sqlx.DB) (sql.Result, error) {
return db.Exec(
"UPDATE roles SET name = $1, phone = $2, role = $3, resume = $4, linkedin = $5, website = $6, github = $7, comp_low = $8, comp_high = $9 WHERE id = $10",
role.Name, role.Phone, role.Role, role.Resume, role.Linkedin, role.Website, role.Github, role.CompLow, role.CompHigh, role.ID,
)
}

func (job *Job) AuthSignature(secret string) string {
input := fmt.Sprintf(
"%s:%s:%s",
Expand All @@ -92,6 +161,21 @@ func (job *Job) AuthSignature(secret string) string {
return base64.URLEncoding.EncodeToString(hash.Sum(nil))
}

func (role *Role) AuthSignature(secret string) string {
input := fmt.Sprintf(
"%s:%s:%s:%s",
role.ID,
role.Email,
role.PublishedAt.String(),
secret,
)

hash := hmac.New(sha256.New, []byte(secret))
hash.Write([]byte(input))

return base64.URLEncoding.EncodeToString(hash.Sum(nil))
}

func GetAllJobs(db *sqlx.DB) ([]Job, error) {
var jobs []Job

Expand All @@ -114,6 +198,28 @@ func GetJob(id string, db *sqlx.DB) (Job, error) {
return job, nil
}

func GetAllRoles(db *sqlx.DB) ([]Role, error) {
var roles []Role

err := db.Select(&roles, "SELECT * FROM roles ORDER BY published_at DESC")
if err != nil && !errors.Is(err, sql.ErrNoRows) {
return roles, err
}

return roles, nil
}

func GetRole(id string, db *sqlx.DB) (Role, error) {
var role Role

err := db.Get(&role, "SELECT * FROM roles WHERE id = $1", id)
if err != nil && !errors.Is(err, sql.ErrNoRows) {
return role, err
}

return role, nil
}

type NewJob struct {
Position string `form:"position"`
Organization string `form:"organization"`
Expand Down Expand Up @@ -153,6 +259,64 @@ func (newJob *NewJob) Validate(update bool) map[string]string {
return errs
}

type NewRole struct {
Name string `form:"name"`
Email string `form:"email"`
Phone string `form:"phone"`
Role string `form:"role"`
Resume string `form:"resume"`
Linkedin string `form:"linkedin"`
Website string `form:"website"`
Github string `form:"github"`
CompLow string `form:"complow"`
CompHigh string `form:"comphigh"`
}

func (newJob *NewRole) Validate(update bool) map[string]string {
errs := make(map[string]string)

if newJob.Name == "" {
errs["name"] = ErrNoName
}

if !update {
if newJob.Email == "" {
errs["email"] = ErrNoEmail
} else if _, err := mail.ParseAddress(newJob.Email); err != nil {
// TODO: Maybe do more than just validate email format?
errs["email"] = ErrInvalidEmail
}
}

if newJob.Role == "" {
errs["role"] = ErrNoRole
}

if newJob.Resume == "" {
errs["resume"] = ErrNoResume
}

if newJob.Linkedin != "" {
if _, err := url.ParseRequestURI(newJob.Linkedin); err != nil {
errs["linkedin"] = ErrInvalidUrl
}
}

if newJob.Website != "" {
if _, err := url.ParseRequestURI(newJob.Website); err != nil {
errs["website"] = ErrInvalidUrl
}
}

if newJob.Github != "" {
if _, err := url.ParseRequestURI(newJob.Github); err != nil {
errs["github"] = ErrInvalidUrl
}
}

return errs
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thoughts on adding a validation for both compensation fields to be present? Either both missing, or both provided.

}

func (newJob *NewJob) SaveToDB(db *sqlx.DB) (Job, error) {
query := `INSERT INTO jobs
(position, organization, url, description, email)
Expand All @@ -179,3 +343,47 @@ func (newJob *NewJob) SaveToDB(db *sqlx.DB) (Job, error) {
}
return job, nil
}

func (newRole *NewRole) SaveToDB(db *sqlx.DB) (Role, error) {
query := `INSERT INTO roles
(name, email, phone, role, resume, linkedin, website, github, comp_low, comp_high)
VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10)
RETURNING *`

params := []interface{}{
newRole.Name,
newRole.Email,
sql.NullString{
String: newRole.Phone,
Valid: newRole.Phone != "",
},
newRole.Role,
newRole.Resume,
sql.NullString{
String: newRole.Linkedin,
Valid: newRole.Linkedin != "",
},
sql.NullString{
String: newRole.Website,
Valid: newRole.Website != "",
},
sql.NullString{
String: newRole.Github,
Valid: newRole.Github != "",
},
sql.NullString{
String: newRole.CompLow,
Valid: newRole.CompLow != "",
},
sql.NullString{
String: newRole.CompHigh,
Valid: newRole.CompHigh != "",
},
}

var role Role
if err := db.QueryRowx(query, params...).StructScan(&role); err != nil {
return role, err
}
return role, nil
}
71 changes: 70 additions & 1 deletion pkg/data/data_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import (
"testing"
)

func TestValidate(t *testing.T) {
func TestJobValidate(t *testing.T) {
testJob := &NewJob{
Position: "test position",
Organization: "test org",
Expand Down Expand Up @@ -38,3 +38,72 @@ func TestValidate(t *testing.T) {
t.Error("bad email, should show an error - result was=", result["email"])
}
}

func TestRoleValidate(t *testing.T) {
testRole := &NewRole{
Name: "test testington",
Email: "[email protected]",
Phone: "316-555-5555",
Role: "any",
Resume: "#wow\n\nhire this person",
Linkedin: "https://linkedin.com/in/testtestingtonsupreme",
Website: "https://www.example.com",
Github: "https://www.github.com/example",
CompLow: "10,000",
CompHigh: "1,000,000",
}

// test valid name
result := testRole.Validate(false)
if result["name"] == "Must provide a Name" {
t.Error("valid name, should have no error - result was=", result["name"])
}

// test valid email format
if result["email"] == "Must provide a valid Email" {
t.Error("valid email, should have no error - result was=", result["email"])
}

// test valid role
if result["role"] == "Must provide a Role" {
t.Error("valid email, should have no error - result was=", result["role"])
}

// test valid role
if result["resume"] == "Must provide a Resume" {
t.Error("valid email, should have no error - result was=", result["resume"])
}

// test valid urls
if result["linkedin"] == "Must provide a valid Url" {
t.Error("valid url, should have no error - result was=", result["linkedin"])
}
if result["website"] == "Must provide a valid Url" {
t.Error("valid url, should have no error - result was=", result["website"])
}
if result["github"] == "Must provide a valid Url" {
t.Error("valid url, should have no error - result was=", result["github"])
}

// test bad url format
testRole.Linkedin = "https//test.com/"
testRole.Website = "https//test.com/"
testRole.Github = "https//test.com/"
result = testRole.Validate(false)
if result["linkedin"] != "Must provide a valid Url" {
t.Error("bad url, should show an error - result was=", result["linkedin"])
}
if result["website"] != "Must provide a valid Url" {
t.Error("bad url, should show an error - result was=", result["website"])
}
if result["github"] != "Must provide a valid Url" {
t.Error("bad url, should show an error - result was=", result["github"])
}

// test bad email format
testRole.Email = "testtest.com"
result = testRole.Validate(false)
if result["email"] != "Must provide a valid Email" {
t.Error("bad email, should show an error - result was=", result["email"])
}
}
Loading