<repo_name>thetangram/composer<file_sep>/CONTRIBUTING.md
Contributing to The Tangram Composer
====================================

(Based on https://github.com/atom/atom/blob/master/CONTRIBUTING.md)

Code of Conduct
---------------

This project and everyone participating in it is governed by [The Tangram Code of Conduct](CODE_OF_CONDUCT.md). By participating, you are expected to uphold this code. Please report unacceptable behavior to [<EMAIL>](mailto:<EMAIL>).


How can I contribute
--------------------

### Reporting bugs

### Suggesting Enhancements

### Writting documentation and Translations


Styleguides
-----------

### Git Commit Messages

Please follow the recomendations from [how to Write a Git Commit Message](https://chris.beams.io/posts/git-commit/) document.

### Go Styleguide

### Documentation Styleguide
<file_sep>/README.md
[![Build Status](https://travis-ci.org/thetangram/composer.svg)](https://travis-ci.org/thetangram/composer)
[![GoDoc](https://godoc.org/github.com/thetangram/composer?status.svg)](https://godoc.org/github.com/thetangram/composer)
[![License](https://img.shields.io/github/license/mashape/apistatus.svg)](https://opensource.org/licenses/MIT)
[![Go Report Card](https://goreportcard.com/badge/thetangram/composer)](https://goreportcard.com/report/thetangram/composer)

The Tangram Composer 
====================

The Tangram Composer is a server/edge side HTML composition service. Its main goal is solving the microservices frontend issue faced when you build a distributed microservices system, each microservice with it's own lifecycle and rollout. In this scenario it's usual to build the user interface as a monolith (aka. BFF), aggregator of microservices data. This model creates user interface-microservices one to one dependencies, so in a lot of cases the rollout of each piece is bound, then the service autonomy is harder and going to continous deployment can be compromissed.

### Features

  - Compose HTML based on HTML components/microservices
  - 100% stateless service.
  - Can be deplloyed as stand alone service or in a serverless architecture.
  - Request headers and cookies passthrough to components.
  - Request headers and cookies filtering to components.
  - Component response headers merging.
  - Component meta information, scripts and stylesheets merging. 
  - Concurrent composition.
  - Stand alone and container based artifacts.
  - ... TBD

### Documentation

You can find all the project documentation in [the documentation folder](./docs). We recommend you to start reading the [project description](./docs/description.md) and the [glossary](./docs/glossary.md). 

### About the name

[From Wikipedia](https://en.wikipedia.org/wiki/Tangram) *The tangram (Chinese: 七巧板; pinyin: qīqiǎobǎn; literally: "seven boards of skill") is a dissection puzzle consisting of seven flat shapes, called tans, which are put together to form shapes. The objective of the puzzle is to form a specific shape (given only an outline or silhouette) using all seven pieces, which may not overlap.*


Getting Started
---------------

### Requisites

In order to build The Tangram Composer you need [Go](https://golang.org), [Make](https://www.gnu.org/software/make/), [Dep](https://github.com/golang/dep), [Docker](https://www.docker.com/) and [zip](). The complete list of tools:

  - **[Go](https://golang.org) 1.9+**, as programming language.
  - **[Make](https://www.gnu.org/software/make/)**, as build automation tool.
  - **[Dep](https://github.com/golang/dep)**, as dependency management tool.
  - **[Docker](https://www.docker.com/) 17.09+**, to build container. 
  - **Zip**, to package *AWS Lambda deployment*.


### Project structure

This project follows the [Standard Go Project Layout](https://github.com/golang-standards/project-layout).


### Building 

This project uses `make` as build automation tool. The `Makefile` **rules** are:

  - **`clean`**, to remove all binaries from `dist/` directory.
  - **`compile`** (default rule), to complile the code.
  - **`run`**, to compile and run the standalone version.
  - **`test`**, to run the test and code coverage.
  - **`build`**, to compile and genrate the final binary artifacts. THis artifacts are full-independent binary files, and also ave some optimizations.
  - **`package`**, to generate the **docker image** (installed in the local repository) and the **AWS Lamdba** package.


Other projects like this
------------------------

This is not the one and only composition solution. We have a lot of friends out there, giving us a lot of inspiration. Take a look on them.

  - [Compoxure](https://github.com/tes/compoxure) is a composition middleware that acts as a proxy replacement for ESI os SSI for backend services and compose fragments from microservices into the response
  - [Skipper](https://github.com/zalando/skipper) is the Zalando's HTTP router and reverse proxy for service composition.
  - [Convergent UI](https://github.com/acesinc/convergent-ui) is a special Zuul Filter that aims to provide a solution to the Distributed Composition problem faced when building a GUI within a Micro Services Architecture. 
<file_sep>/Gopkg.toml

[[dependencies]]
  branch = "master"
  name = "github.com/aws/aws-lambda-go"

[[dependencies]]
  branch = "master"
  name = "github.com/gorilla/mux"
<file_sep>/cmd/lambda/main.go
package main

import (
	"fmt"
	"log"
	"time"

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

// Application build details
var (
	version   = "development"
	build     = "undefined"
	buildDate = "undefined"
)

func main() {
	fmt.Println("The Tangram Composer Lambda launcher")
	log.Printf("\tversion:      %s\n", version)
	log.Printf("\tbuild:        %s\n", build)
	log.Printf("\tbuild date:   %s\n", buildDate)
	log.Printf("\tstartup date: %s\n", time.Now().Format(time.RFC3339))

	lambda.Start(Handler)
}

// Handler is your Lambda function handler
// It uses Amazon API Gateway request/responses provided by the aws-lambda-go/events package,
// However you could use other event sources (S3, Kinesis etc), or JSON-decoded primitive types such as 'string'.
func Handler(request events.APIGatewayProxyRequest) (events.APIGatewayProxyResponse, error) {
	// stdout and stderr are sent to AWS CloudWatch Logs
	log.Printf("Processing Lambdevents.APIGatewayProxyResponsea request %s\n", request.RequestContext.RequestID)
	return events.APIGatewayProxyResponse{
		Body:       "Not implemented yet",
		StatusCode: 200,
	}, nil
}
<file_sep>/Makefile
version=$(shell cat VERSION)
build=$(shell git rev-parse --short HEAD)
build-date=$(shell date --rfc-3339=seconds)
build-target-dir=dist
docker-image=jomoespe/tangram


compile:
	@go build ./...

run:
	@go run cmd/tangramd/main.go

test:
	@echo "Testing..." 
	@go test -cover ./...


clean: 
	@echo "Cleaning..." 
	@rm --force --recursive dist 


build: clean test
	@echo "Building Tangramd service..." 
	@CGO_ENABLED=0 GOOS=linux go build -a \
									   -installsuffix cgo \
	                                   -ldflags "-s -w \
	                                             -X 'main.version=$(version)' \
	                                             -X 'main.build=$(build)' \
	                                             -X 'main.buildDate=$(build-date)'"  \
	                                   -o $(build-target-dir)/tangramd \
									   cmd/tangramd/main.go

	@echo "Building Tangramd AWS Lambda..." 
	@go build -o $(build-target-dir)/tangram-aws cmd/lambda/main.go


package: build
	@echo "Packaging Docker container..."
	@docker build --rm \
	              --build-arg version=$(version) \
				  --file build/package/Dockerfile \
				  --tag "$(docker-image):$(version)" .
	@docker tag "$(docker-image):$(version)" "$(docker-image):latest"

	@echo "Packaging AWS Lamdba function..."
	@zip -r -q $(build-target-dir)/deployment.zip $(build-target-dir)/tangram-aws config/
<file_sep>/cmd/tangramd/main.go
package main

import (
	"context"
	"fmt"
	"log"
	"net/http"
	"os"
	"os/signal"
	"syscall"
	"time"

	"github.com/gorilla/mux"
)

type apiServer struct {
	server *http.Server
}

// Application exit status codes
const (
	successExitStatus            = 0
	errorStartingHTTPServer      = 1
	errorLoadingConfig           = 2
	errorStopintServerStatusCode = 3
)

// Configuration values
const (
	defaultAddress          = ":2018"
	defaultShutdownTimeout  = 2 * time.Millisecond
	defaultHTTPReadTimeout  = 500 * time.Millisecond
	defaultHTTPWriteTimeout = 500 * time.Second
)

// Application build details
var (
	version   = "development"
	build     = "undefined"
	buildDate = "undefined"
)

func main() {
	log.Println("The Tangram Composer")
	log.Printf("\tversion:      %s\n", version)
	log.Printf("\tbuild:        %s\n", build)
	log.Printf("\tbuild date:   %s\n", buildDate)
	log.Printf("\tstartup date: %s\n", time.Now().Format(time.RFC3339))

	apiserver := apiServer{}
	apiserver.startHTTPServer()
	apiserver.waitAndShutdown(defaultShutdownTimeout)
}

func (s *apiServer) startHTTPServer() {
	// configure HTTP server, register application status entrypoints and routes
	r := mux.NewRouter()
	s.server = &http.Server{
		Handler:      r,
		Addr:         defaultAddress,
		ReadTimeout:  defaultHTTPReadTimeout,
		WriteTimeout: defaultHTTPWriteTimeout,
	}

	r.HandleFunc("/healthy", healthyHandler).Methods("GET")
	r.HandleFunc("/", handler).Methods("GET")

	go func() {
		log.Printf("Listening on %s\n", defaultAddress)
		if err := s.server.ListenAndServe(); err != nil {
			log.Printf("Cannot start HTTP server. Error: %s", err)
			os.Exit(errorStartingHTTPServer)
		}
	}()
}

func (s *apiServer) waitAndShutdown(timeout time.Duration) {
	// deal with Ctrl+C (SIGTERM) and graceful shutdown
	stop := make(chan os.Signal, 1)
	signal.Notify(stop, os.Interrupt, syscall.SIGTERM)
	<-stop

	ctx, cancel := context.WithTimeout(context.Background(), timeout)
	defer cancel()

	log.Printf("Shutting down, with a timeout of %s...\n", timeout)
	if err := s.server.Shutdown(ctx); err != nil {
		log.Printf("Error stopping http server. Error: %v\n", err)
		os.Exit(errorStopintServerStatusCode)
	}
	log.Println("The Tangram Composer stoped")
	os.Exit(successExitStatus)
}

func handler(w http.ResponseWriter, r *http.Request) {
	w.WriteHeader(http.StatusOK)
	fmt.Fprint(w, "Not implemented yet")
}

func healthyHandler(w http.ResponseWriter, r *http.Request) {
	w.WriteHeader(http.StatusOK)
	fmt.Fprint(w, "OK")
}
<file_sep>/docs/description.md
The Tangram Composition Description
===================================

The Tangram Composer is a server/edge side HTML composition service. It tries  to solve the microservices frontend problem faced when you build a microservices distributed system, each microservice with it's own lifecycle and rollout. In this scenario it's usual to build the user system's user interface as a monolith, aggregator of microservices data. This model creates user interface-microservices one to one dependencies, so in a lot os cases the rollout of each piece is bound, then the service autonomy is harder and going to continous deployment is compromissed.


## TBD & Ideas

> The Tangram Composer is a solution to the Distributed Composition problem faced when building a GUI within a Micro Services Architecture. 

> Tangram is an HTTP router and reverse proxy working as edge-side/server-side HTML composition service. Is designed to work as stateless service.

> An edge-side microfrontends composition solution.

> [MDN - Using data attributes](https://developer.mozilla.org/en-US/docs/Learn/HTML/Howto/Use_data_attributes) 
> [Unidirectional User Interface Architectures](http://staltz.com/unidirectional-user-interface-architectures.html)