Skip to content

Commit 665f9c6

Browse files
committed
Support the new scanCVDocument rt-cv route
1 parent 1f6d9fd commit 665f9c6

File tree

3 files changed

+213
-12
lines changed

3 files changed

+213
-12
lines changed

api.go

Lines changed: 45 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -120,31 +120,68 @@ func (a *API) SetCredentials(credentialsList []SetCredentialsArg) error {
120120
}
121121

122122
// Get makes a get request to RT-CV
123-
func (c *serverConn) Get(path string, unmarshalResInto interface{}) error {
124-
return c.DoRequest("GET", path, nil, unmarshalResInto)
123+
func (c *serverConn) Get(path string, unmarshalResInto any) error {
124+
req, err := c.prepairJSONReq("GET", path, nil)
125+
if err != nil {
126+
return err
127+
}
128+
129+
return c.DoRequest(req, unmarshalResInto)
125130
}
126131

127132
// Post makes a post request to RT-CV
128-
func (c *serverConn) Post(path string, body interface{}, unmarshalResInto interface{}) error {
129-
return c.DoRequest("POST", path, body, unmarshalResInto)
133+
func (c *serverConn) Post(path string, body any, unmarshalResInto any) error {
134+
req, err := c.prepairJSONReq("POST", path, body)
135+
if err != nil {
136+
return err
137+
}
138+
139+
return c.DoRequest(req, unmarshalResInto)
130140
}
131141

132-
// DoRequest makes a http request to RT-CV
133-
func (c *serverConn) DoRequest(method, path string, body, unmarshalResInto interface{}) error {
142+
func (c *serverConn) prepairJSONReq(method, path string, body any) (*http.Request, error) {
134143
var reqBody io.ReadCloser
135144
if body != nil {
136145
reqBodyBytes, err := json.Marshal(body)
137146
if err != nil {
138-
return err
147+
return nil, err
139148
}
140149
reqBody = ioutil.NopCloser(bytes.NewBuffer(reqBodyBytes))
141150
}
151+
142152
req, err := http.NewRequest(method, c.serverLocation+path, reqBody)
143153
if err != nil {
144-
return err
154+
return nil, err
145155
}
146156

147157
req.Header.Add("Content-Type", "application/json")
158+
159+
return req, err
160+
}
161+
162+
// Post makes a post request to RT-CV with formdata instaid of json data
163+
func (c *serverConn) PostFormData(path string, form io.Reader, boundry string, unmarshalResInto any) error {
164+
req, err := c.prepairFormReq("POST", path, form, boundry)
165+
if err != nil {
166+
return err
167+
}
168+
169+
return c.DoRequest(req, unmarshalResInto)
170+
}
171+
172+
func (c *serverConn) prepairFormReq(method, path string, body io.Reader, boundry string) (*http.Request, error) {
173+
req, err := http.NewRequest(method, c.serverLocation+path, body)
174+
if err != nil {
175+
return nil, err
176+
}
177+
178+
req.Header.Add("Content-Type", "multipart/form-data; boundary="+boundry)
179+
180+
return req, err
181+
}
182+
183+
// DoRequest makes a http request to RT-CV
184+
func (c *serverConn) DoRequest(req *http.Request, unmarshalResInto any) error {
148185
if c.authHeaderValue != "" {
149186
req.Header.Add("Authorization", c.authHeaderValue)
150187
}

send_full_cv.go

Lines changed: 128 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,128 @@
1+
package main
2+
3+
import (
4+
"bytes"
5+
"encoding/json"
6+
"errors"
7+
"fmt"
8+
"io"
9+
"mime/multipart"
10+
"net/textproto"
11+
12+
"github.com/valyala/fasthttp"
13+
)
14+
15+
func parseSendFullCvRequest(req *fasthttp.Request) (func(conn serverConn, resp any) error, *CVMetadata, error) {
16+
// Parse the multipart form data
17+
form, err := req.MultipartForm()
18+
if err != nil {
19+
return nil, nil, err
20+
}
21+
22+
cvFiles, ok := form.File["cv"]
23+
if !ok {
24+
return nil, nil, errors.New(`no "cv" form file provided`)
25+
}
26+
27+
// Get the cv file
28+
switch len(cvFiles) {
29+
case 0:
30+
return nil, nil, errors.New("no cv provided")
31+
case 1:
32+
// Good
33+
default:
34+
return nil, nil, errors.New("you can only provide one cv")
35+
}
36+
file := cvFiles[0]
37+
38+
metadataValues, ok := form.Value["metadata"]
39+
if !ok {
40+
return nil, nil, errors.New(`no "metadata" form value provided`)
41+
}
42+
43+
switch len(metadataValues) {
44+
case 0:
45+
return nil, nil, errors.New("no metadata provided")
46+
case 1:
47+
// Good
48+
default:
49+
return nil, nil, errors.New("you can only provide one metadata value")
50+
}
51+
52+
metadata := CVMetadata{}
53+
54+
err = json.Unmarshal([]byte(metadataValues[0]), &metadata)
55+
if err != nil {
56+
return nil, nil, fmt.Errorf("invalid metadata, err: %s", err)
57+
}
58+
59+
buff := bytes.NewBuffer(nil)
60+
creationForm := multipart.NewWriter(buff)
61+
62+
boundry := creationForm.Boundary()
63+
defer creationForm.Close()
64+
65+
err = creationForm.WriteField("metadata", metadataValues[0])
66+
if err != nil {
67+
return nil, nil, err
68+
}
69+
err = passtroughCVFile(file, creationForm)
70+
if err != nil {
71+
return nil, nil, err
72+
}
73+
74+
return func(conn serverConn, resp any) error {
75+
return conn.PostFormData("/api/v1/scraper/scanCVDocument", bytes.NewBuffer(buff.Bytes()), boundry, resp)
76+
}, &metadata, nil
77+
}
78+
79+
func passtroughCVFile(uploadedFile *multipart.FileHeader, creationForm *multipart.Writer) error {
80+
h := make(textproto.MIMEHeader)
81+
h.Set("Content-Disposition", `form-data; name="cv"; filename="cv.pdf"`)
82+
uploadedCVFileHeader := uploadedFile.Header.Get("Content-Type")
83+
if uploadedCVFileHeader == "" {
84+
uploadedCVFileHeader = "application/octet-stream"
85+
}
86+
h.Set("Content-Type", uploadedCVFileHeader)
87+
cvFileWriter, err := creationForm.CreatePart(h)
88+
89+
if err != nil {
90+
return err
91+
}
92+
93+
f, err := uploadedFile.Open()
94+
if err != nil {
95+
return err
96+
}
97+
defer f.Close()
98+
99+
_, err = io.Copy(cvFileWriter, f)
100+
return err
101+
}
102+
103+
// CVMetadata is the metadata of a CV
104+
type CVMetadata struct {
105+
ReferenceNumber string `json:"referenceNumber,omitempty"`
106+
Link *string `json:"link,omitempty"`
107+
CreatedAt *string `json:"createdAt,omitempty"`
108+
LastChanged *string `json:"lastChanged,omitempty"`
109+
PersonalDetails PersonalDetails `json:"personalDetails"`
110+
}
111+
112+
// PersonalDetails contains the personal details of a CV
113+
type PersonalDetails struct {
114+
Initials string `json:"initials,omitempty"`
115+
FirstName string `json:"firstName,omitempty"`
116+
SurNamePrefix string `json:"surNamePrefix,omitempty"`
117+
SurName string `json:"surName,omitempty"`
118+
DateOfBirth string `json:"dob,omitempty"`
119+
Gender string `json:"gender,omitempty"`
120+
StreetName string `json:"streetName,omitempty"`
121+
HouseNumber string `json:"houseNumber,omitempty"`
122+
HouseNumberSuffix string `json:"houseNumberSuffix,omitempty"`
123+
Zip string `json:"zip,omitempty"`
124+
City string `json:"city,omitempty"`
125+
Country string `json:"country,omitempty"`
126+
PhoneNumber string `json:"phoneNumber,omitempty"`
127+
Email string `json:"email,omitempty"`
128+
}

webserver.go

Lines changed: 40 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -20,11 +20,14 @@ func startWebserver(env Env, api *API, loginUsers []EnvUser) string {
2020

2121
requestHandler := func(ctx *fasthttp.RequestCtx) {
2222
path := string(ctx.Path())
23-
body := ctx.Request.Body()
23+
body := func() []byte {
24+
return ctx.Request.Body()
25+
}
26+
2427
switch path {
2528
case "/send_cv":
2629
cvForChecking := StrippedCV{}
27-
err := json.Unmarshal(body, &cvForChecking)
30+
err := json.Unmarshal(body(), &cvForChecking)
2831
if err != nil {
2932
errorResp(ctx, 400, "invalid CV")
3033
return
@@ -48,7 +51,7 @@ func startWebserver(env Env, api *API, loginUsers []EnvUser) string {
4851
api.SetCacheEntry(cvForChecking.ReferenceNumber, time.Hour*72)
4952
hasMatch = true
5053
} else {
51-
scanCVBody := json.RawMessage(append(append([]byte(`{"cv":`), body...), '}'))
54+
scanCVBody := json.RawMessage(append(append([]byte(`{"cv":`), body()...), '}'))
5255

5356
for idx, conn := range api.connections {
5457
var response struct {
@@ -71,10 +74,43 @@ func startWebserver(env Env, api *API, loginUsers []EnvUser) string {
7174
}
7275
}
7376

77+
ctx.Response.AppendBodyString("true")
78+
case "/send_full_cv":
79+
send, cv, err := parseSendFullCvRequest(&ctx.Request)
80+
if err != nil {
81+
errorResp(ctx, 400, err.Error())
82+
return
83+
}
84+
85+
hasMatch := false
86+
if api.MockMode {
87+
api.SetCacheEntry(cv.ReferenceNumber, time.Hour*72)
88+
hasMatch = true
89+
} else {
90+
for idx, conn := range api.connections {
91+
var response struct {
92+
HasMatches bool `json:"hasMatches"`
93+
}
94+
95+
err = send(conn, &response)
96+
if err != nil {
97+
errorResp(ctx, 500, err.Error())
98+
return
99+
}
100+
101+
if idx == api.primaryConnection {
102+
hasMatch = response.HasMatches
103+
if hasMatch {
104+
// Only cache the CVs that where matched to something
105+
api.SetCacheEntry(cv.ReferenceNumber, time.Hour*72) // 3 days
106+
}
107+
}
108+
}
109+
}
74110
ctx.Response.AppendBodyString("true")
75111
case "/cvs_list":
76112
cvs := []StrippedCVWithOriginal{}
77-
err := json.Unmarshal(body, &cvs)
113+
err := json.Unmarshal(body(), &cvs)
78114
if err != nil {
79115
errorResp(ctx, 400, "invalid CV")
80116
return

0 commit comments

Comments
 (0)