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
20 changes: 12 additions & 8 deletions Makefile
Original file line number Diff line number Diff line change
@@ -1,16 +1,20 @@

GO_BUILD := GOOS=linux GOARCH=arm64 go build -tags lambda.norpc

.PHONY: install
install:
cd client && npm install

.PHONY: build
build:
cd client && npx ng build

.PHONY: build-prod
build-prod:
.PHONY: build-ui
build-ui:
cd client && npx ng build --configuration production

.PHONY: build-api
build-api:
cd api && \
$(GO_BUILD) -o ./dist/getProductsList/bootstrap ./cmd/getProductsList/main.go && \
$(GO_BUILD) -o ./dist/getProductsById/bootstrap ./cmd/getProductsById/main.go

.PHONY: deploy
deploy: build-prod
cd infra && npx cdk deploy $(if $(PROFILE),--profile $(PROFILE),)
deploy: build-ui build-api
cd infra && npx cdk deploy --all --require-approval never $(if $(PROFILE),--profile $(PROFILE),)
13 changes: 13 additions & 0 deletions api/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
Build:

```
GOOS=linux GOARCH=arm64 go build -tags lambda.norpc -o ./dist/getProductsList/bootstrap ./cmd/getProductsList/main.go
GOOS=linux GOARCH=arm64 go build -tags lambda.norpc -o ./dist/getProductsById/bootstrap ./cmd/getProductsById/main.go

```

Deploy:

```
cdk deploy DeployAPIStack
```
11 changes: 11 additions & 0 deletions api/cmd/getProductsById/main.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
package main

import (
"aws-practitioner-for-js/internal/products"

"github.com/aws/aws-lambda-go/lambda"
)

func main() {
lambda.Start(products.GetProductById)
}
11 changes: 11 additions & 0 deletions api/cmd/getProductsList/main.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
package main

import (
"aws-practitioner-for-js/internal/products"

"github.com/aws/aws-lambda-go/lambda"
)

func main() {
lambda.Start(products.GetProductList)
}
103 changes: 103 additions & 0 deletions api/docs/swagger.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
openapi: 3.0.0
info:
title: Product Service API
description: API for managing products in the shop application
version: 1.0.0

servers:
- url: https://api.shop-angular-cloudfront.tech
description: Production
- url: http://localhost:4200
description: Local development

paths:
/products:
get:
summary: Get all products
description: Returns a list of all available products
operationId: getProductsList
responses:
"200":
description: A list of products
content:
application/json:
schema:
type: array
items:
$ref: "#/components/schemas/Product"
"500":
description: Internal server error
content:
application/json:
schema:
$ref: "#/components/schemas/Error"

/products/{productId}:
get:
summary: Get product by ID
description: Returns a single product by its ID
operationId: getProductById
parameters:
- name: productId
in: path
required: true
description: The product ID (UUID)
schema:
type: string
format: uuid
example: 7567ec4b-b10c-48c5-9345-fc73c48a80aa
responses:
"200":
description: Product found
content:
application/json:
schema:
$ref: "#/components/schemas/Product"
"404":
description: Product not found
content:
application/json:
schema:
$ref: "#/components/schemas/Error"
"500":
description: Internal server error
content:
application/json:
schema:
$ref: "#/components/schemas/Error"

components:
schemas:
Product:
type: object
required:
- id
- title
- description
- price
- count
properties:
id:
type: string
format: uuid
example: 7567ec4b-b10c-48c5-9345-fc73c48a80aa
title:
type: string
example: ProductOne
description:
type: string
example: Short Product Description1
price:
type: number
format: double
example: 2.4
count:
type: integer
example: 4

Error:
type: object
properties:
error:
type: string
example: not found
5 changes: 5 additions & 0 deletions api/go.mod
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
module aws-practitioner-for-js

go 1.26.1

require github.com/aws/aws-lambda-go v1.54.0
10 changes: 10 additions & 0 deletions api/go.sum
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
github.com/aws/aws-lambda-go v1.54.0 h1:EGYpdyRGF88xszqlGcBewz811mJeRS+maNlLZXFheII=
github.com/aws/aws-lambda-go v1.54.0/go.mod h1:dpMpZgvWx5vuQJfBt0zqBha60q7Dd7RfgJv23DymV8A=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/stretchr/testify v1.7.2 h1:4jaiDzPyXQvSd7D0EjG45355tLlV3VOECpq10pLC+8s=
github.com/stretchr/testify v1.7.2/go.mod h1:R6va5+xMeoiuVRoj+gSkQ7d3FALtqAAGI1FQKckRals=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
34 changes: 34 additions & 0 deletions api/internal/products/handler.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
package products

import (
"aws-practitioner-for-js/internal/response"
"context"
"net/http"

"github.com/aws/aws-lambda-go/events"
)

func GetProductList(ctx context.Context, request events.APIGatewayProxyRequest) (events.APIGatewayProxyResponse, error) {
products, err := GetProducts()
if err != nil {
return response.ErrInternalServer(), err
}
return response.JSON(http.StatusOK, products)
}

func GetProductById(ctx context.Context, request events.APIGatewayProxyRequest) (events.APIGatewayProxyResponse, error) {
productId := request.PathParameters["productId"]

products, err := GetProducts()
if err != nil {
return response.ErrInternalServer(), err
}

for _, p := range products {
if p.ID == productId {
return response.JSON(http.StatusOK, p)
}
}

return response.ErrNotFound(), nil
}
94 changes: 94 additions & 0 deletions api/internal/products/handler_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
package products

import (
"context"
"encoding/json"
"net/http"
"testing"

"github.com/aws/aws-lambda-go/events"
)

func TestGetProductList(t *testing.T) {
ctx := context.Background()
request := events.APIGatewayProxyRequest{}

response, err := GetProductList(ctx, request)

if err != nil {
t.Fatalf("expected no error, got %v", err)
}

if response.StatusCode != http.StatusOK {
t.Errorf("expected status 200, got %d", response.StatusCode)
}

var products []Product
err = json.Unmarshal([]byte(response.Body), &products)
if err != nil {
t.Fatalf("failed to unmarshal response body: %v", err)
}

if len(products) == 0 {
t.Error("expected non-empty product list")
}
}

func TestGetProductById_Success(t *testing.T) {
ctx := context.Background()
productId := "7567ec4b-b10c-48c5-9345-fc73c48a80aa" // ProductOne from products.json
request := events.APIGatewayProxyRequest{
PathParameters: map[string]string{
"productId": productId,
},
}

response, err := GetProductById(ctx, request)

if err != nil {
t.Fatalf("expected no error, got %v", err)
}

if response.StatusCode != http.StatusOK {
t.Errorf("expected status 200, got %d", response.StatusCode)
}

var product Product
err = json.Unmarshal([]byte(response.Body), &product)
if err != nil {
t.Fatalf("failed to unmarshal response body: %v", err)
}

if product.ID != productId {
t.Errorf("expected product ID %s, got %s", productId, product.ID)
}

if product.Title != "ProductOne" {
t.Errorf("expected product title 'ProductOne', got '%s'", product.Title)
}
}

func TestGetProductById_NotFound(t *testing.T) {
ctx := context.Background()
productId := "non-existent-id"
request := events.APIGatewayProxyRequest{
PathParameters: map[string]string{
"productId": productId,
},
}

response, err := GetProductById(ctx, request)

if err != nil {
t.Fatalf("expected no error, got %v", err)
}

if response.StatusCode != http.StatusNotFound {
t.Errorf("expected status 404, got %d", response.StatusCode)
}

expectedBody := `{"error":"not found"}`
if response.Body != expectedBody {
t.Errorf("expected body %s, got %s", expectedBody, response.Body)
}
}
28 changes: 28 additions & 0 deletions api/internal/products/model.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
package products

import (
_ "embed"
"encoding/json"
)

//go:embed products.json
var productsJSON []byte

type Product struct {
ID string `json:"id"`
Title string `json:"title"`
Description string `json:"description"`
Price float64 `json:"price"`
Count int `json:"count"`
}

func GetProducts() ([]Product, error) {
var products []Product
err := json.Unmarshal(productsJSON, &products)

if err != nil {
return nil, err
}

return products, nil
}
Loading