release version 4
This commit is contained in:
parent
a1651c914e
commit
33e99c167d
@ -18,3 +18,4 @@ WALLET_ENABLED=false
|
||||
WALLET_URL=https://conectathon-balancer.izer.tech
|
||||
WALLET_IDENTIFIER=test
|
||||
WALLET_API_KEY=""
|
||||
ICVP_VALIDATOR_URL=http://lacpass.create.cl:7089
|
||||
|
||||
@ -24,6 +24,7 @@ type Config struct {
|
||||
WalletUrl string
|
||||
WalletIdentifier string
|
||||
WalletAPIKey string
|
||||
ICVPValidatorUrl string
|
||||
}
|
||||
|
||||
func LoadConfig() Config {
|
||||
@ -39,13 +40,14 @@ func LoadConfig() Config {
|
||||
AuthEmailClientID: "app",
|
||||
FhirBaseUrl: "http://lacpass.create.cl:8080",
|
||||
VhlBaseUrl: "http://lacpass.create.cl:8182",
|
||||
FhirMediatorBaseUrl: "http://lacpass.create.cl:3000/",
|
||||
FhirMediatorBaseUrl: "http://lacpass.create.cl:3000",
|
||||
APISwagger: false,
|
||||
LogLevel: "info",
|
||||
WalletEnabled: false,
|
||||
WalletUrl: "https://conectathon-balancer.izer.tech/",
|
||||
WalletIdentifier: "test",
|
||||
WalletAPIKey: "",
|
||||
ICVPValidatorUrl: "http://lacpass.create.cl:7089",
|
||||
}
|
||||
|
||||
if serverPort, exists := os.LookupEnv("API_PORT"); exists {
|
||||
@ -112,5 +114,9 @@ func LoadConfig() Config {
|
||||
cfg.WalletAPIKey = walletAPIKey
|
||||
}
|
||||
|
||||
if icvpValidatorUrl, exists := os.LookupEnv("ICVP_VALIDATOR_URL"); exists {
|
||||
cfg.ICVPValidatorUrl = icvpValidatorUrl
|
||||
}
|
||||
|
||||
return cfg
|
||||
}
|
||||
|
||||
@ -140,11 +140,12 @@ func (a *App) loadIpsRoute(router chi.Router) {
|
||||
}
|
||||
|
||||
func (a *App) loadVhlRoute(router chi.Router) {
|
||||
r := vhlClient.NewClient(a.config.VhlBaseUrl)
|
||||
r := vhlClient.NewClient(a.config.VhlBaseUrl, a.config.ICVPValidatorUrl)
|
||||
s := vhlCore.NewService(&r)
|
||||
h := vhlHandler.NewHandler(&s)
|
||||
router.Post("/", h.Create)
|
||||
router.Post("/fetch", h.Get)
|
||||
router.Post("/validate", h.Validate)
|
||||
}
|
||||
|
||||
func (a *App) loadWalletRoutes(router chi.Router) {
|
||||
|
||||
@ -12,7 +12,7 @@ services:
|
||||
environment:
|
||||
API_PORT: ${API_PORT:-3000}
|
||||
AUTH_INTERNAL_URL: ${AUTH_INTERNAL_URL:-http://auth:8080}
|
||||
AUTH_HOSTNAME: ${KEYCLOAK_URL:-http://localhost:9083}
|
||||
AUTH_HOSTNAME: ${KEYCLOAK_HOSTNAME:-http://localhost:9083}
|
||||
AUTH_REALM: ${KEYCLOAK_REALM:-lacpass}
|
||||
AUTH_CLIENT_ID: ${AUTH_CLIENT_ID:-admin-cli}
|
||||
# Need to set this after creating a client for Keycloak Admin API access, using service account
|
||||
@ -27,6 +27,7 @@ services:
|
||||
WALLET_URL: ${WALLET_URL:-https://conectathon-balancer.izer.tech/}
|
||||
WALLET_IDENTIFIER: ${WALLET_IDENTIFIER:-test}
|
||||
WALLET_API_KEY: ${WALLET_API_KEY:-}
|
||||
ICVP_VALIDATOR_URL: ${ICVP_VALIDATOR_URL:-http://lacpass.create.cl:7089}
|
||||
ports:
|
||||
- "9081:3000"
|
||||
healthcheck:
|
||||
|
||||
@ -40,16 +40,21 @@ And then go to the `Users` tab and create a new user:
|
||||

|
||||
|
||||
> In the compose we have a mail-catcher container running on port 25 that will show you any email sent by keycloak to
|
||||
the users registered. This emails will not be sent out is just for development.
|
||||
> the users registered. This emails will not be sent out is just for development.
|
||||
|
||||
Once the user is created, we can use the helper script to get an access token from Keycloak:
|
||||
|
||||
```bash
|
||||
sh scripts/auth.sh
|
||||
sh scripts/auth.sh access-token
|
||||
```
|
||||
|
||||
The access token will show after the command, like this:
|
||||
|
||||
```bash
|
||||
Successfully logged in!
|
||||
Access Token:
|
||||
XXXXX.VVVV.BBBB
|
||||
```
|
||||
|
||||
For this to work we need to define both `KEYCLOAK_DEFAULT_USER_EMAIL` and `KEYCLOAK_DEFAULT_USER_PASSWORD` in our `.env`
|
||||
file.
|
||||
|
||||
|
||||
|
||||
|
||||
@ -54,3 +54,6 @@ Fhir server endpoint for FHIR IPS managment. Default: `http://lacpass.create.cl:
|
||||
|
||||
`VHL_BASE_URL`
|
||||
VHL server endpoint for VHL QR generation, validation and retrieve. Default: `http://lacpass.create.cl:8182`
|
||||
|
||||
`ICVP_VALIDATOR_URL`
|
||||
Endpoint to validate the QR content of ICVPs not linked to an IPS. Default: `http://lacpass.create.cl:7089`
|
||||
|
||||
@ -100,3 +100,49 @@ To do this:
|
||||
4. On the bottom set your STMP credentials in the **Connection & Authentication** section and save.
|
||||
|
||||

|
||||
|
||||
## Enable HTTP access
|
||||
|
||||
> [!WARNING]
|
||||
> Do not use HTTP in Production, since this will expose users data. We recomended always use a secure HTTPS endpoint.
|
||||
|
||||
By default Keycloak supports to be hosted only in a HTTPS endpoint. If you host it in a HTTP endpoint, a error message will be displayed when trying to login
|
||||
into the webclient, and when trying yo authenticate an user.
|
||||
|
||||
To disable the required HTTPS, please add to `docker/compose.yaml` this environment variables for `auth`
|
||||
service, under the `environments` section:
|
||||
|
||||
```yaml
|
||||
environment:
|
||||
#...
|
||||
KC_HOSTNAME_STRICT: false
|
||||
KC_HOSTNAME_STRICT_HTTPS: false
|
||||
```
|
||||
|
||||
This will tell keycloak to not require a HTTPS connection.
|
||||
But if you already build and started your keycloak service, this parameter is already saved
|
||||
into keycloak's database. To override it we need to go into the `auth-db` container.
|
||||
|
||||
To get inside the `auth-db` container, please run the next command in the project root folder:
|
||||
|
||||
```bash
|
||||
docker compose exec -it auth-db sh
|
||||
```
|
||||
|
||||
This will start initialize a shell console inside the container. Then to connect to the
|
||||
database run the following command, where `<POSTGRES_USER>` is the enviroment variable in your `.env` file:
|
||||
|
||||
```bash
|
||||
psql -U <POSTGRES_USER> keycloak
|
||||
```
|
||||
|
||||
This will connect you into a Postgres console inside the keycloak database. Finally run
|
||||
|
||||
```sql
|
||||
UPDATE realm SET ssl_required='NONE' WHERE name='master';
|
||||
UPDATE realm SET ssl_required='NONE' WHERE name='lacpass';
|
||||
```
|
||||
|
||||
This will update the keycloak parameters to not required HTTPS.
|
||||
|
||||
To exit just run `exit` twice, one to get out of Postgres and a second time to get out of the container.
|
||||
|
||||
@ -12,17 +12,20 @@ import (
|
||||
type ClientInterface interface {
|
||||
GetDocumentReference(identifier string) (*Bundle, error)
|
||||
GetIpsBundle(url string) (map[string]interface{}, error)
|
||||
GetIpsICVP(idBundle string, immunizationId *string) (string, error)
|
||||
}
|
||||
|
||||
type IpsClient struct {
|
||||
Client *http.Client
|
||||
BaseURL string
|
||||
MediatorBaseURL string
|
||||
}
|
||||
|
||||
func NewClient(baseURL string) IpsClient {
|
||||
func NewClient(baseURL string, mediatorBaseURL string) IpsClient {
|
||||
return IpsClient{
|
||||
Client: &http.Client{},
|
||||
BaseURL: baseURL,
|
||||
MediatorBaseURL: mediatorBaseURL,
|
||||
}
|
||||
}
|
||||
|
||||
@ -62,3 +65,12 @@ func (c *IpsClient) GetIpsBundle(url string) (map[string]interface{}, error) {
|
||||
Err: fmt.Errorf("failed to get document reference"),
|
||||
}
|
||||
}
|
||||
|
||||
func (c *IpsClient) GetIpsICVP(idBundle string, immunizationId *string) (string, error) {
|
||||
// TODO: To be implemented by the participant
|
||||
return nil, &errors.HttpError{
|
||||
StatusCode: 500,
|
||||
Body: []map[string]interface{}{{"error": "Not implemented error", "message": "this method is not implemented yet"}},
|
||||
Err: fmt.Errorf("failed to get document reference"),
|
||||
}
|
||||
}
|
||||
@ -7,6 +7,7 @@ import (
|
||||
"ips-lacpass-backend/internal/ips/client"
|
||||
customErrors "ips-lacpass-backend/pkg/errors"
|
||||
authMiddleware "ips-lacpass-backend/pkg/middleware"
|
||||
"log/slog"
|
||||
"slices"
|
||||
"sort"
|
||||
"strings"
|
||||
@ -143,6 +144,113 @@ func removeDuplicates(entries []Entry) []Entry {
|
||||
return result
|
||||
}
|
||||
|
||||
func findCountryInBundle(bundle Bundle) string {
|
||||
for _, entry := range bundle.Entry {
|
||||
if entry.Resource == nil {
|
||||
slog.Warn("No resource found in bundle: ", entry.FullURL)
|
||||
continue
|
||||
}
|
||||
|
||||
rtype, ok := entry.Resource["resourceType"]
|
||||
if !ok || rtype == nil || rtype != "Organization" {
|
||||
slog.Warn("Resource type is not an Organization: ", entry.FullURL)
|
||||
continue
|
||||
}
|
||||
|
||||
addressEntry, ok := entry.Resource["address"]
|
||||
if !ok || addressEntry == nil {
|
||||
slog.Warn("Resource has has noo Address: ", entry.FullURL)
|
||||
continue
|
||||
}
|
||||
|
||||
addresses, ok := entry.Resource["address"].([]interface{})
|
||||
if !ok || len(addresses) == 0 {
|
||||
slog.Warn("Resource has no address: ", entry.FullURL)
|
||||
continue
|
||||
}
|
||||
|
||||
address, ok := addresses[0].(map[string]interface{})
|
||||
if !ok || address == nil {
|
||||
slog.Warn("Resource address is not a map: ", entry.FullURL)
|
||||
continue
|
||||
}
|
||||
|
||||
country, ok := address["country"]
|
||||
if !ok || country == nil {
|
||||
slog.Warn("Resource address has no country: ", entry.FullURL)
|
||||
continue
|
||||
}
|
||||
|
||||
countryCode, ok := country.(string)
|
||||
if !ok {
|
||||
slog.Warn("Resource country is not a string: ", entry.FullURL)
|
||||
continue
|
||||
}
|
||||
return countryCode
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func findOrganizationEntries(bundle Bundle) []Entry {
|
||||
var entries []Entry
|
||||
for _, entry := range bundle.Entry {
|
||||
if entry.Resource == nil {
|
||||
slog.Warn("No resource found in bundle: ", entry.FullURL)
|
||||
continue
|
||||
}
|
||||
|
||||
rtype, ok := entry.Resource["resourceType"]
|
||||
if !ok || rtype == nil || rtype != "Organization" {
|
||||
continue
|
||||
}
|
||||
entries = append(entries, entry)
|
||||
}
|
||||
return entries
|
||||
}
|
||||
|
||||
func addOriginExtension(resource map[string]interface{}, bundleID string, country string) {
|
||||
if resource == nil {
|
||||
return
|
||||
}
|
||||
|
||||
originExtension := map[string]interface{}{
|
||||
"url": "http://lacpass.org/fhir/StructureDefinition/resource-origin",
|
||||
"extension": []map[string]interface{}{
|
||||
{
|
||||
"url": "bundleId",
|
||||
"valueString": bundleID,
|
||||
},
|
||||
{
|
||||
"url": "country",
|
||||
"valueString": country,
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
extensions, ok := resource["extension"].([]interface{})
|
||||
if !ok {
|
||||
extensions = []interface{}{}
|
||||
}
|
||||
|
||||
// Avoid adding duplicate extensions
|
||||
for _, ext := range extensions {
|
||||
extMap, ok := ext.(map[string]interface{})
|
||||
if !ok {
|
||||
continue
|
||||
}
|
||||
|
||||
url, ok := extMap["url"].(string)
|
||||
if !ok || url != "http://lacpass.org/fhir/StructureDefinition/resource-origin" {
|
||||
continue
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
extensions = append(extensions, originExtension)
|
||||
resource["extension"] = extensions
|
||||
}
|
||||
|
||||
func (is *IpsService) MergeIPS(ctx context.Context, currentIpsBundle map[string]interface{}, newIpsBundle map[string]interface{}) (map[string]interface{}, error) {
|
||||
var currIPS, newIPS Bundle
|
||||
if err := mapstructure.Decode(currentIpsBundle, &currIPS); err != nil {
|
||||
@ -161,6 +269,17 @@ func (is *IpsService) MergeIPS(ctx context.Context, currentIpsBundle map[string]
|
||||
}
|
||||
}
|
||||
|
||||
currentCountry := findCountryInBundle(currIPS)
|
||||
newCountry := findCountryInBundle(newIPS)
|
||||
|
||||
for i := range currIPS.Entry {
|
||||
addOriginExtension(currIPS.Entry[i].Resource, currIPS.ID, currentCountry)
|
||||
}
|
||||
|
||||
for i := range newIPS.Entry {
|
||||
addOriginExtension(newIPS.Entry[i].Resource, newIPS.ID, newCountry)
|
||||
}
|
||||
|
||||
curComp, err := getIPSComposition(currIPS.Entry)
|
||||
if err != nil {
|
||||
return nil, &customErrors.HttpError{
|
||||
@ -275,6 +394,16 @@ func (is *IpsService) MergeIPS(ctx context.Context, currentIpsBundle map[string]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Add original Organization resources from both IPSs
|
||||
currIPSOrganizations := findOrganizationEntries(currIPS)
|
||||
newIPSOrganizations := findOrganizationEntries(newIPS)
|
||||
for _, org := range newIPSOrganizations {
|
||||
mergedIPS.Entry = append(mergedIPS.Entry, org)
|
||||
}
|
||||
for _, org := range currIPSOrganizations {
|
||||
mergedIPS.Entry = append(mergedIPS.Entry, org)
|
||||
}
|
||||
mergedIPS.Entry = removeDuplicates(mergedIPS.Entry)
|
||||
|
||||
jsonData, err = json.Marshal(mergedIPS)
|
||||
|
||||
@ -14,12 +14,14 @@ import (
|
||||
type VhlClient struct {
|
||||
Client *http.Client
|
||||
BaseURL string
|
||||
ICVPValidatorUrl string
|
||||
}
|
||||
|
||||
func NewClient(baseURL string) VhlClient {
|
||||
func NewClient(baseURL string, icvpValidatorUrl string) VhlClient {
|
||||
return VhlClient{
|
||||
Client: &http.Client{},
|
||||
BaseURL: baseURL,
|
||||
ICVPValidatorUrl: icvpValidatorUrl,
|
||||
}
|
||||
}
|
||||
|
||||
@ -49,3 +51,71 @@ func (c *VhlClient) GetIpsUrl(ctx context.Context, shLink string, passCode strin
|
||||
Err: fmt.Errorf("failed to get document reference"),
|
||||
}
|
||||
}
|
||||
|
||||
func (c *VhlClient) ICVPValidate(ctx context.Context, qrData string) (*ICVPQRValidationResponse, error) {
|
||||
r := ICVPQrValidationRequest{
|
||||
IncludeRaw: true,
|
||||
QRData: qrData,
|
||||
}
|
||||
body, err := json.Marshal(r)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to marshal user payload: %w", err)
|
||||
}
|
||||
|
||||
vu := fmt.Sprintf("%s/decode/hcert", c.ICVPValidatorUrl)
|
||||
req, err := http.NewRequest("POST", vu, bytes.NewBuffer(body))
|
||||
if err != nil {
|
||||
return nil, &errors.HttpError{
|
||||
StatusCode: 500,
|
||||
Body: []map[string]interface{}{{"error": "internal_error", "message": "Failed to create request"}},
|
||||
Err: err,
|
||||
}
|
||||
}
|
||||
req.Header.Set("Content-Type", "application/json")
|
||||
|
||||
resp, err := c.Client.Do(req)
|
||||
|
||||
if err != nil {
|
||||
return nil, &errors.HttpError{
|
||||
StatusCode: 502,
|
||||
Body: []map[string]interface{}{{"error": "service_unavailable", "message": "Failed to connect to VHL service"}},
|
||||
Err: err,
|
||||
}
|
||||
}
|
||||
defer utils.CloseBody(resp.Body)
|
||||
|
||||
if resp.StatusCode != http.StatusOK {
|
||||
bodyBytes, err := io.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
return nil, &errors.HttpError{
|
||||
StatusCode: resp.StatusCode,
|
||||
Body: []map[string]interface{}{{"error": "internal_error", "message": "Failed to read response body"}},
|
||||
Err: err,
|
||||
}
|
||||
}
|
||||
return nil, &errors.HttpError{
|
||||
StatusCode: resp.StatusCode,
|
||||
Body: []map[string]interface{}{{"error": "service_error", "message": string(bodyBytes)}},
|
||||
Err: fmt.Errorf("unexpected status code %d: %s", resp.StatusCode, string(bodyBytes)),
|
||||
}
|
||||
}
|
||||
|
||||
bb, err := io.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
return nil, &errors.HttpError{
|
||||
StatusCode: 500,
|
||||
Body: []map[string]interface{}{{"error": "internal_error", "message": "Failed to read response body"}},
|
||||
Err: err,
|
||||
}
|
||||
}
|
||||
var valResp ICVPQRValidationResponse
|
||||
err = json.Unmarshal(bb, &valResp)
|
||||
if err != nil {
|
||||
return nil, &errors.HttpError{
|
||||
StatusCode: 500,
|
||||
Body: []map[string]interface{}{{"error": "internal_error", "message": "Failed to read parse response body"}},
|
||||
Err: err,
|
||||
}
|
||||
}
|
||||
return &valResp, nil
|
||||
}
|
||||
|
||||
@ -14,6 +14,11 @@ type QrValidationRequest struct {
|
||||
QRCodeContent string `json:"qrCodeContent,required"`
|
||||
}
|
||||
|
||||
type ICVPQrValidationRequest struct {
|
||||
IncludeRaw bool `json:"include_raw,required"`
|
||||
QRData string `json:"qr_data,required"`
|
||||
}
|
||||
|
||||
type ValidationResponseStep struct {
|
||||
Step string `json:"step,omitempty"`
|
||||
Status string `json:"status,omitempty"`
|
||||
@ -48,3 +53,73 @@ type VhlManifestResponseFile struct {
|
||||
ContentType string `json:"contentType,omitempty"`
|
||||
Location string `json:"location,required"`
|
||||
}
|
||||
|
||||
// ----------------
|
||||
// ICVP Validation response strucs
|
||||
type ICVPQRValidationResponse struct {
|
||||
COSE COSEData `json:"cose"`
|
||||
Diagnostics DiagnosticsData `json:"diagnostics"`
|
||||
HCERT interface{} `json:"hcert"` // Can be null, use interface{}
|
||||
Payload PayloadData `json:"payload"`
|
||||
}
|
||||
|
||||
type COSEData struct {
|
||||
Raw RawData `json:"_raw"`
|
||||
KidB64 string `json:"kid_b64"`
|
||||
KidHex string `json:"kid_hex"`
|
||||
Protected ProtectedData `json:"protected"`
|
||||
Signature string `json:"signature"`
|
||||
Unprotected map[string]interface{} `json:"unprotected"` // Empty object, use map[string]interface{}
|
||||
}
|
||||
|
||||
type RawData struct {
|
||||
PayloadBstr string `json:"payload_bstr"`
|
||||
ProtectedBstr string `json:"protected_bstr"`
|
||||
Signature string `json:"signature"`
|
||||
}
|
||||
|
||||
type ProtectedData struct {
|
||||
Key1 int `json:"1"` // The key is the number '1'
|
||||
Key4 Key4Data `json:"4"` // The key is the number '4'
|
||||
}
|
||||
|
||||
type Key4Data struct {
|
||||
B64 string `json:"_b64"`
|
||||
}
|
||||
|
||||
type DiagnosticsData struct {
|
||||
Base45DecodedLen int `json:"base45_decoded_len"`
|
||||
ZlibDecompressedLen int `json:"zlib_decompressed_len"`
|
||||
}
|
||||
|
||||
type PayloadData struct {
|
||||
Key260 Payload260Data `json:"-260"` // The key is the number '-260'
|
||||
Key1 string `json:"1"` // The key is the number '1'
|
||||
Key6 int `json:"6"` // The key is the number '6'
|
||||
}
|
||||
|
||||
type Payload260Data struct {
|
||||
Key6 InnerPayloadData `json:"-6"` // The key is the number '-6'
|
||||
}
|
||||
|
||||
type InnerPayloadData struct {
|
||||
DOB string `json:"dob"`
|
||||
N string `json:"n"`
|
||||
NDT string `json:"ndt"`
|
||||
NID string `json:"nid"`
|
||||
NTL string `json:"ntl"`
|
||||
S string `json:"s"`
|
||||
V VaccinationData `json:"v"`
|
||||
}
|
||||
|
||||
type VaccinationData struct {
|
||||
BO string `json:"bo"`
|
||||
CN string `json:"cn"`
|
||||
DT string `json:"dt"`
|
||||
IS string `json:"is"`
|
||||
VLE string `json:"vle"`
|
||||
VLS string `json:"vls"`
|
||||
VP string `json:"vp"`
|
||||
}
|
||||
|
||||
//--------------------
|
||||
|
||||
@ -78,3 +78,11 @@ func (vs *VhlService) GetQrIps(ctx context.Context, qrData string, passCode stri
|
||||
}
|
||||
return ipsBundle, nil
|
||||
}
|
||||
|
||||
func (vs *VhlService) GetICVPValidation(ctx context.Context, qrData string) (*client.ICVPQRValidationResponse, error) {
|
||||
validationData, err := vs.Client.ICVPValidate(ctx, qrData)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return validationData, nil
|
||||
}
|
||||
|
||||
@ -31,6 +31,10 @@ type VhlGetRequest struct {
|
||||
PassCode string `json:"pass_code,omitempty"`
|
||||
}
|
||||
|
||||
type ICVPValidateRequest struct {
|
||||
Data string `json:"data,require"`
|
||||
}
|
||||
|
||||
type VhlResponse struct {
|
||||
Data string `json:"data"`
|
||||
Payload map[string]interface{} `json:"payload"`
|
||||
@ -168,3 +172,65 @@ func (vh *Handler) Get(w http.ResponseWriter, r *http.Request) {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
// Validate ICVP data for ICVP that dont come from a IPS
|
||||
//
|
||||
// @Summary Validate ICVP.
|
||||
// @Description Validate ICVP data. Usefull for ICVPs not linked to a IPS.
|
||||
// @Tags IPS FHIR
|
||||
// @Accept json
|
||||
// @Produce json
|
||||
//
|
||||
// @Security ApiKeyAuth
|
||||
//
|
||||
// @Param data body ICVPValidateRequest true "Data parameters"
|
||||
//
|
||||
// @Success 200 {object} any
|
||||
// @Failure 400
|
||||
// @Failure 404
|
||||
// @Failure 500
|
||||
// @Router /qr/validate [post]
|
||||
func (vh *Handler) Validate(w http.ResponseWriter, r *http.Request) {
|
||||
ctx := r.Context()
|
||||
|
||||
// TODO throw correct error body
|
||||
var body ICVPValidateRequest
|
||||
if err := json.NewDecoder(r.Body).Decode(&body); err != nil {
|
||||
w.WriteHeader(http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
|
||||
icvpValidationResponseData, err := vh.Service.GetICVPValidation(ctx, body.Data)
|
||||
if err != nil {
|
||||
var httpErr *customErrors.HttpError
|
||||
if errors.As(err, &httpErr) {
|
||||
res, err := json.Marshal(httpErr.Body)
|
||||
if err != nil {
|
||||
http.Error(w, "Failed to encode error response", http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
w.WriteHeader(httpErr.StatusCode)
|
||||
_, err = w.Write(res)
|
||||
if err != nil {
|
||||
http.Error(w, "Failed to write response", http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
} else {
|
||||
http.Error(w, err.Error(), http.StatusInternalServerError)
|
||||
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
res, err := json.Marshal(icvpValidationResponseData)
|
||||
if err != nil {
|
||||
http.Error(w, "Failed to encode response", http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
w.WriteHeader(http.StatusOK)
|
||||
_, err = w.Write(res)
|
||||
if err != nil {
|
||||
http.Error(w, "Failed to write response", http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user