Order
An order service, because what is a shop without actual orders to be shipped?
The Order service is part of the ACME Fitness Serverless Shop. The goal of this specific service is to interact with the catalog, front-end, and make calls to the order services.
Prerequisites
Deploying
With Pulumi (using SQS for eventing)
To deploy the Order Service you'll need a Pulumi account. Once you have your Pulumi account and configured the Pulumi CLI, you can initialize a new stack using the Pulumi templates in the pulumi folder.
cd pulumi
pulumi stack init <your pulumi org>/acmeserverless-order/dev
Pulumi is configured using a file called Pulumi.dev.yaml
. A sample configuration is available in the Pulumi directory. You can rename Pulumi.dev.yaml.sample
to Pulumi.dev.yaml
and update the variables accordingly. Alternatively, you can change variables directly in the main.go file in the pulumi directory. The configuration contains:
config:
aws:region: us-west-2 ## The region you want to deploy to
awsconfig:generic:
sentrydsn: ## The DSN to connect to Sentry
accountid: ## Your AWS Account ID
wavefronturl: ## The URL of your Wavefront instance
wavefronttoken: ## Your Wavefront API token
awsconfig:tags:
author: retgits ## The author, you...
feature: acmeserverless
team: vcs ## The team you're on
version: 0.2.0 ## The version
To create the Pulumi stack, and create the Order service, run pulumi up
.
If you want to keep track of the resources in Pulumi, you can add tags to your stack as well.
pulumi stack tag set app:name acmeserverless
pulumi stack tag set app:feature acmeserverless-order
pulumi stack tag set app:domain order
Clone this repository
git clone https://github.com/retgits/acme-serverless-order
cd acme-serverless-order
Get the Go Module dependencies
go get ./...
Change directories to the cloudformation folder
cd ./cloudformation
If your event bus is not called acmeserverless, update the name of the feature
parameter in the template.yaml
file. Now you can build and deploy the Lambda function:
make build
make deploy
Testing
To test, you can use the SQS or EventBridge test apps in the acme-serverless repo.
API
GET /order/all
Get all orders in the system
curl --request GET \
--url https://<id>.execute-api.us-west-2.amazonaws.com/Prod/order/all
[
{
"userid": "8888",
"firstname": "John",
"lastname": "Blaze",
"address": {
"street": "20 Riding Lane Av",
"city": "San Francisco",
"zip": "10201",
"state": "CA",
"country": "USA"
},
"email": "jblaze@marvel.com",
"delivery": "UPS/FEDEX",
"card": {
"type": "amex/visa/mastercard/bahubali",
"number": "34983479798",
"expMonth": "12",
"expYear": "21",
"ccv": "123"
},
"cart": [
{
"id": "1234",
"description": "redpants",
"quantity": "1",
"price": "4"
},
{
"id": "5678",
"description": "bluepants",
"quantity": "1",
"price": "4"
}
],
"total": "100"
}
]
GET /order/:userid
Get all orders for a specific userid
curl --request GET \
--url https://<id>.execute-api.us-west-2.amazonaws.com/Prod/order/8888
[
{
"userid": "8888",
"firstname": "John",
"lastname": "Blaze",
"address": {
"street": "20 Riding Lane Av",
"city": "San Francisco",
"zip": "10201",
"state": "CA",
"country": "USA"
},
"email": "jblaze@marvel.com",
"delivery": "UPS/FEDEX",
"card": {
"type": "amex/visa/mastercard/bahubali",
"number": "34983479798",
"expMonth": "12",
"expYear": "21",
"ccv": "123"
},
"cart": [
{
"id": "1234",
"description": "redpants",
"quantity": "1",
"price": "4"
},
{
"id": "5678",
"description": "bluepants",
"quantity": "1",
"price": "4"
}
],
"total": "100"
}
]
POST /order/add/:userid
Add order for a specific user and run payment
curl --request GET \
--url https://<id>.execute-api.us-west-2.amazonaws.com/Prod/order/add/8888 \
--header 'content-type: application/json' \
--data '{
"userid": "8888",
"firstname": "John",
"lastname": "Blaze",
"address": {
"street": "20 Riding Lane Av",
"city": "San Francisco",
"zip": "10201",
"state": "CA",
"country": "USA"
},
"email": "jblaze@marvel.com",
"delivery": "UPS/FEDEX",
"card": {
"type": "amex/visa/mastercard/bahubali",
"number": "34983479798",
"expMonth": "12",
"expYear": "21",
"ccv": "123"
},
"cart": [
{
"id": "1234",
"description": "redpants",
"quantity": "1",
"price": "4"
},
{
"id": "5678",
"description": "bluepants",
"quantity": "1",
"price": "4"
}
],
"total": "100"
}'
The payload to add an order is
{
"userid": "8888",
"firstname": "John",
"lastname": "Blaze",
"address": {
"street": "20 Riding Lane Av",
"city": "San Francisco",
"zip": "10201",
"state": "CA",
"country": "USA"
},
"email": "jblaze@marvel.com",
"delivery": "UPS/FEDEX",
"card": {
"type": "amex/visa/mastercard/bahubali",
"number": "34983479798",
"expMonth": "12",
"expYear": "21",
"ccv": "123"
},
"cart": [
{
"id": "1234",
"description": "redpants",
"quantity": "1",
"price": "4"
},
{
"id": "5678",
"description": "bluepants",
"quantity": "1",
"price": "4"
}
],
"total": "100"
}
When the payment is successful an HTTP/200 message is returned
{
"order_id": "ea5ed52e-3c4e-11e9-9aff-e62b216188c4",
"payment": {
"amount": "",
"message": "pending payment",
"success": "false",
"transactionID": ""
},
"userid": "7892"
}
Events
The events for all of ACME Serverless Fitness Shop are structured as
{
"metadata": { // Metadata for all services
"domain": "Order", // Domain represents the the event came from (like Payment or Order)
"source": "CLI", // Source represents the function the event came from (like ValidateCreditCard or SubmitOrder)
"type": "ShipmentRequested", // Type respresents the type of event this is (like CreditCardValidated)
"status": "success" // Status represents the current status of the event (like Success)
},
"data": {} // The actual payload of the event
}
Add an order
These are events that are emitted by the lambda-<eventing option>-add
functions:
{
"metadata": {
"domain": "Order",
"source": "AddOrder",
"type": "PaymentRequested",
"status": "success"
},
"data": {
"orderID": "12345",
"card": {
"Type": "Visa",
"Number": "4222222222222",
"ExpiryYear": 2022,
"ExpiryMonth": 12,
"CVV": "123"
},
"total": "123"
}
}
Ship an order
These are events that trigger the lambda-<eventing option>-ship
functions:
{
"metadata": {
"domain": "Payment",
"source": "ValidateCreditCard",
"type": "CreditCardValidated",
"status": "success"
},
"data": {
"success": "true",
"status": 200,
"message": "transaction successful",
"amount": 123,
"transactionID": "3f846704-af12-4ea9-a98c-8d7b37e10b54"
}
}
After updating the Order storage, the following event is sent:
{
"metadata": {
"domain": "Order",
"source": "ShipOrder",
"type": "ShipmentRequested",
"status": "success"
},
"data": {
"_id": "12345",
"delivery": "UPS/FEDEX"
}
}
Update order status
These are events that trigger the lambda-<eventing option>-update
functions:
After the shipment has been sent:
{
"metadata": {
"domain": "Shipment",
"source": "SendShipment",
"type": "SentShipment",
"status": "success"
},
"data": {
"trackingNumber": "6bc3b96b-19e9-42d3-bff8-91a2c431d1d6",
"orderNumber": "12345",
"status": "shipped - pending delivery"
}
}
After delivery:
{
"metadata": {
"domain": "Shipment",
"source": "SendShipment",
"type": "DeliveredShipment",
"status": "success"
},
"data": {
"trackingNumber": "6bc3b96b-19e9-42d3-bff8-91a2c431d1d6",
"orderNumber": "12345",
"status": "delivered"
}
}
Building for Google Cloud Run
If you have Docker installed locally, you can use docker build
to create a container which can be used to try out the order service locally and for Google Cloud Run.
To build your container image using Docker:
Run the command:
VERSION=`git describe --tags --always --dirty="-dev"`
docker build -f ./cmd/cloudrun-order-http/Dockerfile . -t gcr.io/[PROJECT-ID]/order:$VERSION
Replace [PROJECT-ID]
with your Google Cloud project ID
If you have not yet configured Docker to use the gcloud command-line tool to authenticate requests to Container Registry, do so now using the command:
gcloud auth configure-docker
You need to do this before you can push or pull images using Docker. You only need to do it once.
Push the container image to Container Registry:
docker push gcr.io/[PROJECT-ID]/order:$VERSION
The container relies on the environment variables:
- SENTRY_DSN: The DSN to connect to Sentry
- K_SERVICE: The name of the service (in Google Cloud Run this variable is automatically set, defaults to
order
if not set)
- VERSION: The version you're running (will default to
dev
if not set)
- PORT: The port number the service will listen on (will default to
8080
if not set)
- STAGE: The environment in which you're running
- WAVEFRONT_TOKEN: The token to connect to Wavefront
- WAVEFRONT_URL: The URL to connect to Wavefront (will default to
debug
if not set)
- MONGO_USERNAME: The username to connect to MongoDB
- MONGO_PASSWORD: The password to connect to MongoDB
- MONGO_HOSTNAME: The hostname of the MongoDB server
- MONGO_PORT: The port number of the MongoDB server
A docker run
, with all options, is:
docker run --rm -it -p 8080:8080 -e SENTRY_DSN=abcd -e K_SERVICE=order \
-e VERSION=$VERSION -e PORT=8080 -e STAGE=dev -e WAVEFRONT_URL=https://my-url.wavefront.com \
-e WAVEFRONT_TOKEN=efgh -e MONGO_USERNAME=admin -e MONGO_PASSWORD=admin \
-e MONGO_HOSTNAME=localhost -e MONGO_PORT=27017 gcr.io/[PROJECT-ID]/order:$VERSION
Replace [PROJECT-ID]
with your Google Cloud project ID
Troubleshooting
In case the API Gateway responds with {"message":"Forbidden"}
, there is likely an issue with the deployment of the API Gateway. To solve this problem, you can use the AWS CLI. To confirm this, run aws apigateway get-deployments --rest-api-id <rest-api-id>
. If that returns no deployments, you can create a deployment for the prod stage with aws apigateway create-deployment --rest-api-id <rest-api-id> --stage-name prod --stage-description 'Prod Stage' --description 'deployment to the prod stage'
.
Contributing
Pull requests are welcome. For major changes, please open an issue first to discuss what you would like to change.
Please make sure to update tests as appropriate.
License
See the LICENSE file in the repository