Skip to content

Commit

Permalink
Merge pull request #1 from vmilovanovicc/backend
Browse files Browse the repository at this point in the history
Backend functionalities
  • Loading branch information
vmilovanovicc authored Oct 5, 2023
2 parents 2a8ade1 + 91ec0ca commit cbaa5c9
Show file tree
Hide file tree
Showing 15 changed files with 1,019 additions and 0 deletions.
99 changes: 99 additions & 0 deletions .github/workflows/checks.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
name: Code Quality Checks

on:
push:
branches:
- '*'
pull_request:
branches:
- main

jobs:
check:
runs-on: ubuntu-latest

steps:
- name: Checkout code
uses: actions/checkout@v3

# Install Tools
- name: Set up Go
uses: actions/setup-go@v4
with:
go-version: '>=1.20.3'

- name: Set up Terraform
uses: hashicorp/setup-terraform@v2

- name: Configure AWS Credentials
uses: aws-actions/configure-aws-credentials@v2
with:
aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }}
aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
aws-region: eu-central-1

# Validate Go Code
- name: Check Go code formatting
run: gofmt -l . | tee fmt.log && test ! -s fmt.log

- name: Install Go dependencies
run: go mod download

- name: Build Go code
run: go build -v ./...

- name: Run tests (if applicable)
run: go test -v ./...

- name: Run Code Coverage
run: go test -cover ./...

# Validate Terraform Code
- name: Check Terraform code formatting
run: terraform fmt -check=true -recursive
continue-on-error: true

- name: Validate Terraform configuration
run: terraform validate
continue-on-error: true

# Secrets & Code Vulnerabilities
- name: Check for secrets
if: github.event_name == 'push' || (github.event_name == 'pull_request' && github.ref == 'refs/heads/main')
uses: trufflesecurity/TruffleHog-Enterprise-Github-Action@main
with:
args: --fail-verified ${{ github.ref }} HEAD

- name: Golang - Run Snyk to check for vulnerabilities
uses: snyk/actions/golang@master
continue-on-error: true # To make sure that SARIF upload gets called
env:
SNYK_TOKEN: ${{ secrets.SNYK_TOKEN }}
with:
args: --sarif-file-output=snyk_go.sarif

- name: Terraform - Run Snyk to check for vulnerabilities
uses: snyk/actions/iac@master
continue-on-error: true
env:
SNYK_TOKEN: ${{ secrets.SNYK_TOKEN }}
with:
args: --sarif-file-output=snyk_tf.sarif


















6 changes: 6 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -19,3 +19,9 @@

# Go workspace file
go.work


# Terraform

# IDE
.idea/
91 changes: 91 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,2 +1,93 @@
# zobot
Chatbot Translator App with AWS, Golang and Terraform

<img alt="ProjectStatus" src="https://img.shields.io/badge/IN%20PROGRESS-097969?style=for-the-badge&logo=IN%20PROGRESS&logoColor=FFF">

![GitHub language count](https://img.shields.io/github/languages/count/vmilovanovicc/zobot)
![GitHub top language](https://img.shields.io/github/languages/top/vmilovanovicc/zobot)
![GitHub Workflow Status (with event)](https://img.shields.io/github/actions/workflow/status/vmilovanovicc/zobot/checks.yaml)
![GitHub pull requests](https://img.shields.io/github/issues-pr/vmilovanovicc/:zobot)
![GitHub](https://img.shields.io/github/license/vmilovanovicc/zobot)
![GitHub last commit (by committer)](https://img.shields.io/github/last-commit/vmilovanovicc/zobot)
![Go Version](https://img.shields.io/github/go-mod/go-version/vmilovanovicc/zobot)

<br/>
<img alt="Go" src="https://img.shields.io/badge/go-%2300ADD8.svg?style=for-the-badge&logo=go&logoColor=white">
<img alt="AWS" src="https://img.shields.io/badge/AWS-%23FF9900.svg?style=for-the-badge&logo=amazon-aws&logoColor=white">
<img alt="Terraform" src="https://img.shields.io/badge/terraform-%235835CC.svg?style=for-the-badge&logo=terraform&logoColor=white">

## Prerequisites

| Tools | Version |
|-----------|-----------|
| Go | `>= 1.20.3` |
| Terraform | `>= 1.5.0` |

## AWS Services

| Service | Description |
|--------------------|----------------------------------------------------------|
| Amazon Comprehend | Detect the language source |
| Amazon Translate | Translate user-generated content |
| Amazon Polly | Turn text into life-like speech |
| Amazon Lex | Build bots with Conversational AI |
| Amazon S3 | Object storage |
| AWS Lambda | Run code without provisioning or managing infrastructure |

---

# What is zobot?

**zobot** is a multilingual chatbot application powered by AWS and written in Go. It uses Amazon Web Services to interact with users, translate text and turn it into lifelike audio.

## HLA

![zobot diagram](assets/zobot_hla.png)

### How zobot works?
- You talk to **zobot** by interacting with Amazon Lex bot.
- With AWS Comprehend, **zobot** detects the language you're using.
- **zobot** uses Amazon Translate to translate your words into a desired language.
- With AWS Polly, **zobot** transforms the translated text with the correct pronunciation into a lifelike speech.
- **zobot** keeps all audio messages in an S3 bucket.

#### Supported Languages
- English
- Spanish
- French

---

## Testing
Unit tests and [mocks](/backend/mock_client_ops.go) can be found [here](/backend).

---

# References

### AWS SDK for Go

- [AWS SDK Go v2 Reference](https://pkg.go.dev/github.com/aws/aws-sdk-go-v2#section-readme)
- [`translate` API client](https://pkg.go.dev/github.com/aws/aws-sdk-go-v2/service/translate)
- [`polly` API client](https://pkg.go.dev/github.com/aws/aws-sdk-go-v2/service/polly)
- [`s3` API client](https://pkg.go.dev/github.com/aws/aws-sdk-go-v2/service/s3)


### AWS Documentation

- [Amazon Comprehend](https://aws.amazon.com/comprehend/)
- [Amazon Translate](https://aws.amazon.com/translate/)
- [Amazon Polly](https://aws.amazon.com/polly/)
- [Amazon Lex](https://aws.amazon.com/lex/)
- [Amazon S3](https://aws.amazon.com/s3/)
- [AWS Lambda](https://aws.amazon.com/lambda/)

- [Amazon - Translate Supported Languages](https://docs.aws.amazon.com/translate/latest/dg/what-is-languages.html)
- [Amazon Polly - Voice List](https://docs.aws.amazon.com/polly/latest/dg/voicelist.html)
- [Amazon Polly - Supported Languages](https://docs.aws.amazon.com/polly/latest/dg/SupportedLanguage.html)

### Other
- [trufflehog](https://github.com/trufflesecurity/trufflehog) - Secrets/Security Scan
- [snyk](https://github.com/snyk/actions) - Code Vulnerabilities Scan

---
Binary file added assets/zobot_hla.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
19 changes: 19 additions & 0 deletions backend/config.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
package backend

import (
"context"
"github.com/aws/aws-sdk-go-v2/aws"
"github.com/aws/aws-sdk-go-v2/config"
"log"
)

var region = "eu-central-1"

// LoadAWSConfig loads the Shared AWS Configuration (~/.aws/config)
func LoadAWSConfig() (cfg aws.Config) {
cfg, err := config.LoadDefaultConfig(context.TODO(), config.WithRegion(region))
if err != nil {
log.Fatalf("failed to load configuration, %v\n", err)
}
return cfg
}
122 changes: 122 additions & 0 deletions backend/mock_client_ops.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,122 @@
package backend

// For unit testing, mock out the SDK.
// Using Go interfaces, instead of concrete service client.

import (
"context"
"github.com/aws/aws-sdk-go-v2/aws"
v4 "github.com/aws/aws-sdk-go-v2/aws/signer/v4"
"github.com/aws/aws-sdk-go-v2/service/polly"
typesPolly "github.com/aws/aws-sdk-go-v2/service/polly/types"
"github.com/aws/aws-sdk-go-v2/service/s3"
typesS3 "github.com/aws/aws-sdk-go-v2/service/s3/types"
"github.com/aws/aws-sdk-go-v2/service/translate"
"strings"
"time"
)

//////////////////////////////////////////
// TranslateText Mock
//////////////////////////////////////////

type TranslateTranslateTextAPI interface {
TranslateText(ctx context.Context, params *translate.TranslateTextInput, optFns ...func(*translate.Options)) (*translate.TranslateTextOutput, error)
}

func TranslateTextFromTranslate(ctx context.Context, api TranslateTranslateTextAPI, text, language string) (string, error) {
sourceLanguageCode := "auto"
response, err := api.TranslateText(ctx, &translate.TranslateTextInput{
Text: aws.String(text),
TargetLanguageCode: aws.String(language),
SourceLanguageCode: aws.String(sourceLanguageCode),
})
if err != nil {
return "", err
}
return *response.TranslatedText, nil
}

//////////////////////////////////////////
// CreateBucket Mock
//////////////////////////////////////////

type S3CreateBucketAPI interface {
CreateBucket(ctx context.Context, params *s3.CreateBucketInput, optFns ...func(*s3.Options)) (*s3.CreateBucketOutput, error)
}

func CreateBucketFromS3(ctx context.Context, api S3CreateBucketAPI, bucketName string) (string, error) {
response, err := api.CreateBucket(ctx, &s3.CreateBucketInput{
Bucket: aws.String(bucketName),
CreateBucketConfiguration: &typesS3.CreateBucketConfiguration{
LocationConstraint: typesS3.BucketLocationConstraint(region),
},
})
if err != nil {
return "", err
}
return *response.Location, nil
}

//////////////////////////////////////////
// PresignGetObject Mock
//////////////////////////////////////////

type S3PresignGetObjectAPI interface {
GetPresignedURL(ctx context.Context, params *s3.GetObjectInput, optFns ...func(options *s3.PresignOptions)) (*v4.PresignedHTTPRequest, error)
}

func GetPresignedURLFromS3(ctx context.Context, api S3PresignGetObjectAPI, bucketName, objectName string, expires time.Time) (string, error) {
response, err := api.GetPresignedURL(ctx, &s3.GetObjectInput{
Bucket: aws.String(bucketName),
Key: aws.String(objectName),
ResponseExpires: aws.Time(expires),
})
if err != nil {
return "", err
}
return response.URL, nil
}

//////////////////////////////////////////
// StartSpeechSynthesisTask Mock
//////////////////////////////////////////

type PollyStartSpeechSynthesisTaskAPI interface {
StartSpeechSynthesisTask(ctx context.Context, params *polly.StartSpeechSynthesisTaskInput, optFns ...func(*polly.Options)) (*polly.StartSpeechSynthesisTaskOutput, error)
}

func StartSpeechSynthesisTaskFromPolly(ctx context.Context, api PollyStartSpeechSynthesisTaskAPI, text, bucketName, languageCode, targetVoice string) (string, string, error) {
response, err := api.StartSpeechSynthesisTask(ctx, &polly.StartSpeechSynthesisTaskInput{
Text: aws.String(text),
OutputS3BucketName: aws.String(bucketName),
VoiceId: typesPolly.VoiceId(targetVoice),
LanguageCode: typesPolly.LanguageCode(languageCode),
OutputFormat: typesPolly.OutputFormatMp3,
Engine: typesPolly.EngineStandard,
})
if err != nil {
return "", "", err
}
outputURI := strings.Split(*response.SynthesisTask.OutputUri, "/")
objectName := outputURI[len(outputURI)-1]
return *response.SynthesisTask.TaskId, objectName, nil
}

//////////////////////////////////////////
// GetSpeechSynthesisTask Mock
//////////////////////////////////////////

type PollyGetSpeechSynthesisTaskAPI interface {
GetSpeechSynthesisTask(ctx context.Context, params *polly.GetSpeechSynthesisTaskInput, optFns ...func(*polly.Options)) (*polly.GetSpeechSynthesisTaskOutput, error)
}

func GetSpeechSynthesisTaskFromPolly(ctx context.Context, api PollyGetSpeechSynthesisTaskAPI, taskId string) (typesPolly.TaskStatus, error) {
response, err := api.GetSpeechSynthesisTask(ctx, &polly.GetSpeechSynthesisTaskInput{
TaskId: aws.String(taskId),
})
if err != nil {
return "", err
}
return response.SynthesisTask.TaskStatus, nil
}
Loading

0 comments on commit cbaa5c9

Please sign in to comment.