diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..1d74e21 --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +.vscode/ diff --git a/chapter-04/database.sql b/chapter-04/database.sql new file mode 100644 index 0000000..4331708 --- /dev/null +++ b/chapter-04/database.sql @@ -0,0 +1,19 @@ +CREATE DATABASE IF NOT EXISTS chapter04; + +USE chapter04; + +CREATE TABLE IF NOT EXISTS chapter04.people ( + name VARCHAR(100), + title VARCHAR(10), + description VARCHAR(100), + PRIMARY KEY (name) +); + +DELETE FROM chapter04.people; + +INSERT INTO chapter04.people VALUES ('Gru', 'Felonius', 'Where are the minions?'); +INSERT INTO chapter04.people VALUES ('Nefario', 'Dr.', 'Why ... why are you so old?'); +INSERT INTO chapter04.people VALUES ('Agnes', '', 'Your unicorn is so fluffy!'); +INSERT INTO chapter04.people VALUES ('Edith', '', "Don't touch anything!"); +INSERT INTO chapter04.people VALUES ('Vector', '', 'Committing crimes with both direction and magnitude!'); +INSERT INTO chapter04.people VALUES ('Dave', 'Minion', 'Ngaaahaaa! Patalaki patalaku Big Boss!!'); diff --git a/chapter-04/go/.gitignore b/chapter-04/go/.gitignore new file mode 100644 index 0000000..48b8bf9 --- /dev/null +++ b/chapter-04/go/.gitignore @@ -0,0 +1 @@ +vendor/ diff --git a/chapter-04/go/Gopkg.lock b/chapter-04/go/Gopkg.lock new file mode 100644 index 0000000..c32bcc2 --- /dev/null +++ b/chapter-04/go/Gopkg.lock @@ -0,0 +1,110 @@ +# This file is autogenerated, do not edit; changes may be undone by the next 'dep ensure'. + + +[[projects]] + branch = "master" + digest = "1:c46fd324e7902268373e1b337436a6377c196e2dbd7b35624c6256d29d494e78" + name = "github.com/codahale/hdrhistogram" + packages = ["."] + pruneopts = "" + revision = "3a0bb77429bd3a61596f5e8a3172445844342120" + +[[projects]] + digest = "1:c07de423ca37dc2765396d6971599ab652a339538084b9b58c9f7fc533b28525" + name = "github.com/go-sql-driver/mysql" + packages = ["."] + pruneopts = "" + revision = "d523deb1b23d913de5bdada721a6071e71283618" + version = "v1.4.0" + +[[projects]] + branch = "master" + digest = "1:de4b7f3ed313c8403ff5e06133ac29e8222931872e0c9f2ac52630000386126a" + name = "github.com/opentracing-contrib/go-stdlib" + packages = ["nethttp"] + pruneopts = "" + revision = "07a764486eb10927e8cf38337918a40d430524ee" + +[[projects]] + digest = "1:78fb99d6011c2ae6c72f3293a83951311147b12b06a5ffa43abf750c4fab6ac5" + name = "github.com/opentracing/opentracing-go" + packages = [ + ".", + "ext", + "log", + ] + pruneopts = "" + revision = "1949ddbfd147afd4d964a9f00b24eb291e0e7c38" + version = "v1.0.2" + +[[projects]] + digest = "1:7365acd48986e205ccb8652cc746f09c8b7876030d53710ea6ef7d0bd0dcd7ca" + name = "github.com/pkg/errors" + packages = ["."] + pruneopts = "" + revision = "645ef00459ed84a119197bfb8d8205042c6df63d" + version = "v0.8.0" + +[[projects]] + digest = "1:acdc8a34ebf61772eed1f00cad6667f37b8f2618bf958eb59d3e28c690703ad8" + name = "github.com/uber/jaeger-client-go" + packages = [ + ".", + "config", + "internal/baggage", + "internal/baggage/remote", + "internal/spanlog", + "internal/throttler", + "internal/throttler/remote", + "log", + "rpcmetrics", + "thrift", + "thrift-gen/agent", + "thrift-gen/baggage", + "thrift-gen/jaeger", + "thrift-gen/sampling", + "thrift-gen/zipkincore", + "utils", + ] + pruneopts = "" + revision = "b043381d944715b469fd6b37addfd30145ca1758" + version = "v2.14.0" + +[[projects]] + digest = "1:aa1598d34009b45ce74fdabdd25e4258d7923d1e1b418d4c98482e79607cb9b0" + name = "github.com/uber/jaeger-lib" + packages = ["metrics"] + pruneopts = "" + revision = "ed3a127ec5fef7ae9ea95b01b542c47fbd999ce5" + version = "v1.5.0" + +[[projects]] + branch = "master" + digest = "1:4f7d853903a2a32b60c99f92492752a52bf9b56425258afbed5cfc2ee86cd216" + name = "golang.org/x/net" + packages = ["context"] + pruneopts = "" + revision = "aaf60122140d3fcf75376d319f0554393160eb50" + +[[projects]] + digest = "1:c1771ca6060335f9768dff6558108bc5ef6c58506821ad43377ee23ff059e472" + name = "google.golang.org/appengine" + packages = ["cloudsql"] + pruneopts = "" + revision = "b1f26356af11148e710935ed1ac8a7f5702c7612" + version = "v1.1.0" + +[solve-meta] + analyzer-name = "dep" + analyzer-version = 1 + input-imports = [ + "github.com/go-sql-driver/mysql", + "github.com/opentracing-contrib/go-stdlib/nethttp", + "github.com/opentracing/opentracing-go", + "github.com/opentracing/opentracing-go/ext", + "github.com/opentracing/opentracing-go/log", + "github.com/uber/jaeger-client-go", + "github.com/uber/jaeger-client-go/config", + ] + solver-name = "gps-cdcl" + solver-version = 1 diff --git a/chapter-04/go/Gopkg.toml b/chapter-04/go/Gopkg.toml new file mode 100644 index 0000000..9cfb4fb --- /dev/null +++ b/chapter-04/go/Gopkg.toml @@ -0,0 +1,11 @@ +[[constraint]] + name = "github.com/uber/jaeger-client-go" + version = "^2.14.0" + +[[constraint]] + name = "github.com/go-sql-driver/mysql" + version = "1.4.0" + +[[constraint]] + name = "github.com/opentracing-contrib/go-stdlib" + branch = "master" diff --git a/chapter-04/go/exercise1/hello.go b/chapter-04/go/exercise1/hello.go new file mode 100644 index 0000000..a81bb2f --- /dev/null +++ b/chapter-04/go/exercise1/hello.go @@ -0,0 +1,57 @@ +package main + +import ( + "log" + "net/http" + "strings" + + "github.com/PacktPublishing/Distributed-Tracing/chapter-04/go/exercise1/people" +) + +var repo *people.Repository + +func main() { + repo = people.NewRepository() + defer repo.Close() + + http.HandleFunc("/sayHello/", handleSayHello) + + log.Print("Listening on http://localhost:8080/") + log.Fatal(http.ListenAndServe(":8080", nil)) +} + +func handleSayHello(w http.ResponseWriter, r *http.Request) { + name := strings.TrimPrefix(r.URL.Path, "/sayHello/") + greeting, err := SayHello(name) + if err != nil { + http.Error(w, err.Error(), http.StatusInternalServerError) + return + } + w.Write([]byte(greeting)) +} + +// SayHello creates a greeting for the named person. +func SayHello(name string) (string, error) { + person, err := repo.GetPerson(name) + if err != nil { + return "", err + } + return FormatGreeting( + person.Name, + person.Title, + person.Description, + ), nil +} + +// FormatGreeting combines information about a person into a greeting string. +func FormatGreeting(name, title, description string) string { + response := "Hello, " + if title != "" { + response += title + " " + } + response += name + "!" + if description != "" { + response += " " + description + } + return response +} diff --git a/chapter-04/go/exercise1/people/repository.go b/chapter-04/go/exercise1/people/repository.go new file mode 100644 index 0000000..eeeb817 --- /dev/null +++ b/chapter-04/go/exercise1/people/repository.go @@ -0,0 +1,65 @@ +package people + +import ( + "database/sql" + "log" + + _ "github.com/go-sql-driver/mysql" + + "github.com/PacktPublishing/Distributed-Tracing/chapter-04/go/lib/model" +) + +const dburl = "root:mysqlpwd@tcp(127.0.0.1:3306)/chapter04" + +// Repository retrieves information about people. +type Repository struct { + db *sql.DB +} + +// NewRepository creates a new Repository backed by MySQL database. +func NewRepository() *Repository { + db, err := sql.Open("mysql", dburl) + if err != nil { + log.Fatal(err) + } + err = db.Ping() + if err != nil { + log.Fatalf("Cannot ping the db: %v", err) + } + return &Repository{ + db: db, + } +} + +// GetPerson tries to find the person in the database by name. +// If not found, it still returns a Person object with only name +// field populated. +func (r *Repository) GetPerson(name string) (model.Person, error) { + query := "select title, description from people where name = ?" + rows, err := r.db.Query(query, name) + if err != nil { + return model.Person{}, err + } + defer rows.Close() + + for rows.Next() { + var title, descr string + err := rows.Scan(&title, &descr) + if err != nil { + return model.Person{}, err + } + return model.Person{ + Name: name, + Title: title, + Description: descr, + }, nil + } + return model.Person{ + Name: name, + }, nil +} + +// Close calls close on the underlying db connection. +func (r *Repository) Close() { + r.db.Close() +} diff --git a/chapter-04/go/exercise2/hello.go b/chapter-04/go/exercise2/hello.go new file mode 100644 index 0000000..40b41f4 --- /dev/null +++ b/chapter-04/go/exercise2/hello.go @@ -0,0 +1,80 @@ +package main + +import ( + "log" + "net/http" + "strings" + + opentracing "github.com/opentracing/opentracing-go" + otlog "github.com/opentracing/opentracing-go/log" + + "github.com/PacktPublishing/Distributed-Tracing/chapter-04/go/exercise2/people" + "github.com/PacktPublishing/Distributed-Tracing/chapter-04/go/lib/tracing" +) + +var repo *people.Repository +var tracer opentracing.Tracer + +func main() { + repo = people.NewRepository() + defer repo.Close() + + tr, closer := tracing.Init("go-2-hello") + defer closer.Close() + tracer = tr + + http.HandleFunc("/sayHello/", handleSayHello) + + log.Print("Listening on http://localhost:8080/") + log.Fatal(http.ListenAndServe(":8080", nil)) +} + +func handleSayHello(w http.ResponseWriter, r *http.Request) { + span := tracer.StartSpan("say-hello") + defer span.Finish() + + name := strings.TrimPrefix(r.URL.Path, "/sayHello/") + greeting, err := SayHello(name, span) + if err != nil { + span.SetTag("error", true) + span.LogFields(otlog.Error(err)) + http.Error(w, err.Error(), http.StatusInternalServerError) + return + } + + span.SetTag("response", greeting) + w.Write([]byte(greeting)) +} + +// SayHello creates a greeting for the named person. +func SayHello(name string, span opentracing.Span) (string, error) { + person, err := repo.GetPerson(name) + if err != nil { + return "", err + } + + span.LogKV( + "name", person.Name, + "title", person.Title, + "description", person.Description, + ) + + return FormatGreeting( + person.Name, + person.Title, + person.Description, + ), nil +} + +// FormatGreeting combines information about a person into a greeting string. +func FormatGreeting(name, title, description string) string { + response := "Hello, " + if title != "" { + response += title + " " + } + response += name + "!" + if description != "" { + response += " " + description + } + return response +} diff --git a/chapter-04/go/exercise2/people/repository.go b/chapter-04/go/exercise2/people/repository.go new file mode 100644 index 0000000..eeeb817 --- /dev/null +++ b/chapter-04/go/exercise2/people/repository.go @@ -0,0 +1,65 @@ +package people + +import ( + "database/sql" + "log" + + _ "github.com/go-sql-driver/mysql" + + "github.com/PacktPublishing/Distributed-Tracing/chapter-04/go/lib/model" +) + +const dburl = "root:mysqlpwd@tcp(127.0.0.1:3306)/chapter04" + +// Repository retrieves information about people. +type Repository struct { + db *sql.DB +} + +// NewRepository creates a new Repository backed by MySQL database. +func NewRepository() *Repository { + db, err := sql.Open("mysql", dburl) + if err != nil { + log.Fatal(err) + } + err = db.Ping() + if err != nil { + log.Fatalf("Cannot ping the db: %v", err) + } + return &Repository{ + db: db, + } +} + +// GetPerson tries to find the person in the database by name. +// If not found, it still returns a Person object with only name +// field populated. +func (r *Repository) GetPerson(name string) (model.Person, error) { + query := "select title, description from people where name = ?" + rows, err := r.db.Query(query, name) + if err != nil { + return model.Person{}, err + } + defer rows.Close() + + for rows.Next() { + var title, descr string + err := rows.Scan(&title, &descr) + if err != nil { + return model.Person{}, err + } + return model.Person{ + Name: name, + Title: title, + Description: descr, + }, nil + } + return model.Person{ + Name: name, + }, nil +} + +// Close calls close on the underlying db connection. +func (r *Repository) Close() { + r.db.Close() +} diff --git a/chapter-04/go/exercise3a/hello.go b/chapter-04/go/exercise3a/hello.go new file mode 100644 index 0000000..64519cf --- /dev/null +++ b/chapter-04/go/exercise3a/hello.go @@ -0,0 +1,89 @@ +package main + +import ( + "log" + "net/http" + "strings" + + opentracing "github.com/opentracing/opentracing-go" + otlog "github.com/opentracing/opentracing-go/log" + + "github.com/PacktPublishing/Distributed-Tracing/chapter-04/go/exercise3a/people" + "github.com/PacktPublishing/Distributed-Tracing/chapter-04/go/lib/tracing" +) + +var repo *people.Repository + +func main() { + tracer, closer := tracing.Init("go-3-hello") + defer closer.Close() + opentracing.SetGlobalTracer(tracer) + + repo = people.NewRepository() + defer repo.Close() + + http.HandleFunc("/sayHello/", handleSayHello) + + log.Print("Listening on http://localhost:8080/") + log.Fatal(http.ListenAndServe(":8080", nil)) +} + +func handleSayHello(w http.ResponseWriter, r *http.Request) { + span := opentracing.GlobalTracer().StartSpan("say-hello") + defer span.Finish() + + name := strings.TrimPrefix(r.URL.Path, "/sayHello/") + greeting, err := SayHello(name, span) + if err != nil { + span.SetTag("error", true) + span.LogFields(otlog.Error(err)) + http.Error(w, err.Error(), http.StatusInternalServerError) + return + } + + span.SetTag("response", greeting) + w.Write([]byte(greeting)) +} + +// SayHello creates a greeting for the named person. +func SayHello(name string, span opentracing.Span) (string, error) { + person, err := repo.GetPerson(name, span) + if err != nil { + return "", err + } + + span.LogKV( + "name", person.Name, + "title", person.Title, + "description", person.Description, + ) + + return FormatGreeting( + person.Name, + person.Title, + person.Description, + span, + ), nil +} + +// FormatGreeting combines information about a person into a greeting string. +func FormatGreeting( + name, title, description string, + span opentracing.Span, +) string { + span = opentracing.GlobalTracer().StartSpan( + "format-greeting", + opentracing.ChildOf(span.Context()), + ) + defer span.Finish() + + response := "Hello, " + if title != "" { + response += title + " " + } + response += name + "!" + if description != "" { + response += " " + description + } + return response +} diff --git a/chapter-04/go/exercise3a/people/repository.go b/chapter-04/go/exercise3a/people/repository.go new file mode 100644 index 0000000..96c6e97 --- /dev/null +++ b/chapter-04/go/exercise3a/people/repository.go @@ -0,0 +1,77 @@ +package people + +import ( + "database/sql" + "log" + + _ "github.com/go-sql-driver/mysql" + opentracing "github.com/opentracing/opentracing-go" + + "github.com/PacktPublishing/Distributed-Tracing/chapter-04/go/lib/model" +) + +const dburl = "root:mysqlpwd@tcp(127.0.0.1:3306)/chapter04" + +// Repository retrieves information about people. +type Repository struct { + db *sql.DB +} + +// NewRepository creates a new Repository backed by MySQL database. +func NewRepository() *Repository { + db, err := sql.Open("mysql", dburl) + if err != nil { + log.Fatal(err) + } + err = db.Ping() + if err != nil { + log.Fatalf("Cannot ping the db: %v", err) + } + return &Repository{ + db: db, + } +} + +// GetPerson tries to find the person in the database by name. +// If not found, it still returns a Person object with only name +// field populated. +func (r *Repository) GetPerson( + name string, + span opentracing.Span, +) (model.Person, error) { + query := "select title, description from people where name = ?" + + span = opentracing.GlobalTracer().StartSpan( + "get-person", + opentracing.ChildOf(span.Context()), + opentracing.Tag{Key: "db.statement", Value: query}, + ) + defer span.Finish() + + rows, err := r.db.Query(query, name) + if err != nil { + return model.Person{}, err + } + defer rows.Close() + + for rows.Next() { + var title, descr string + err := rows.Scan(&title, &descr) + if err != nil { + return model.Person{}, err + } + return model.Person{ + Name: name, + Title: title, + Description: descr, + }, nil + } + return model.Person{ + Name: name, + }, nil +} + +// Close calls close on the underlying db connection. +func (r *Repository) Close() { + r.db.Close() +} diff --git a/chapter-04/go/exercise3b/hello.go b/chapter-04/go/exercise3b/hello.go new file mode 100644 index 0000000..5ce23ab --- /dev/null +++ b/chapter-04/go/exercise3b/hello.go @@ -0,0 +1,91 @@ +package main + +import ( + "context" + "log" + "net/http" + "strings" + + opentracing "github.com/opentracing/opentracing-go" + otlog "github.com/opentracing/opentracing-go/log" + + "github.com/PacktPublishing/Distributed-Tracing/chapter-04/go/exercise3b/people" + "github.com/PacktPublishing/Distributed-Tracing/chapter-04/go/lib/tracing" +) + +var repo *people.Repository + +func main() { + tracer, closer := tracing.Init("go-3-hello") + defer closer.Close() + opentracing.SetGlobalTracer(tracer) + + repo = people.NewRepository() + defer repo.Close() + + http.HandleFunc("/sayHello/", handleSayHello) + + log.Print("Listening on http://localhost:8080/") + log.Fatal(http.ListenAndServe(":8080", nil)) +} + +func handleSayHello(w http.ResponseWriter, r *http.Request) { + span := opentracing.GlobalTracer().StartSpan("say-hello") + defer span.Finish() + ctx := opentracing.ContextWithSpan(r.Context(), span) + + name := strings.TrimPrefix(r.URL.Path, "/sayHello/") + greeting, err := SayHello(ctx, name) + if err != nil { + span.SetTag("error", true) + span.LogFields(otlog.Error(err)) + http.Error(w, err.Error(), http.StatusInternalServerError) + return + } + + span.SetTag("response", greeting) + w.Write([]byte(greeting)) +} + +// SayHello creates a greeting for the named person. +func SayHello(ctx context.Context, name string) (string, error) { + person, err := repo.GetPerson(ctx, name) + if err != nil { + return "", err + } + + opentracing.SpanFromContext(ctx).LogKV( + "name", person.Name, + "title", person.Title, + "description", person.Description, + ) + + return FormatGreeting( + ctx, + person.Name, + person.Title, + person.Description, + ), nil +} + +// FormatGreeting combines information about a person into a greeting string. +func FormatGreeting( + ctx context.Context, + name, title, description string, +) string { + span, ctx := opentracing.StartSpanFromContext( + ctx, + "format-greeting", + ) + defer span.Finish() + + response := "Hello, " + if title != "" { + response += title + " " + } + response += name + "!" + if description != "" { + response += " " + description + } + return response +} diff --git a/chapter-04/go/exercise3b/people/repository.go b/chapter-04/go/exercise3b/people/repository.go new file mode 100644 index 0000000..948f8cf --- /dev/null +++ b/chapter-04/go/exercise3b/people/repository.go @@ -0,0 +1,78 @@ +package people + +import ( + "context" + "database/sql" + "log" + + _ "github.com/go-sql-driver/mysql" + opentracing "github.com/opentracing/opentracing-go" + + "github.com/PacktPublishing/Distributed-Tracing/chapter-04/go/lib/model" +) + +const dburl = "root:mysqlpwd@tcp(127.0.0.1:3306)/chapter04" + +// Repository retrieves information about people. +type Repository struct { + db *sql.DB +} + +// NewRepository creates a new Repository backed by MySQL database. +func NewRepository() *Repository { + db, err := sql.Open("mysql", dburl) + if err != nil { + log.Fatal(err) + } + err = db.Ping() + if err != nil { + log.Fatalf("Cannot ping the db: %v", err) + } + return &Repository{ + db: db, + } +} + +// GetPerson tries to find the person in the database by name. +// If not found, it still returns a Person object with only name +// field populated. +func (r *Repository) GetPerson( + ctx context.Context, + name string, +) (model.Person, error) { + query := "select title, description from people where name = ?" + + span, ctx := opentracing.StartSpanFromContext( + ctx, + "get-person", + opentracing.Tag{Key: "db.statement", Value: query}, + ) + defer span.Finish() + + rows, err := r.db.QueryContext(ctx, query, name) + if err != nil { + return model.Person{}, err + } + defer rows.Close() + + for rows.Next() { + var title, descr string + err := rows.Scan(&title, &descr) + if err != nil { + return model.Person{}, err + } + return model.Person{ + Name: name, + Title: title, + Description: descr, + }, nil + } + return model.Person{ + Name: name, + }, nil +} + +// Close calls close on the underlying db connection. +func (r *Repository) Close() { + r.db.Close() +} diff --git a/chapter-04/go/exercise4a/bigbrother/main.go b/chapter-04/go/exercise4a/bigbrother/main.go new file mode 100644 index 0000000..4c719b5 --- /dev/null +++ b/chapter-04/go/exercise4a/bigbrother/main.go @@ -0,0 +1,54 @@ +package main + +import ( + "encoding/json" + "log" + "net/http" + "strings" + + opentracing "github.com/opentracing/opentracing-go" + otlog "github.com/opentracing/opentracing-go/log" + + "github.com/PacktPublishing/Distributed-Tracing/chapter-04/go/exercise4a/people" + "github.com/PacktPublishing/Distributed-Tracing/chapter-04/go/lib/tracing" +) + +var repo *people.Repository + +func main() { + tracer, closer := tracing.Init("go-4-bigbrother") + defer closer.Close() + opentracing.SetGlobalTracer(tracer) + + repo = people.NewRepository() + defer repo.Close() + + http.HandleFunc("/getPerson/", handleGetPerson) + + log.Print("Listening on http://localhost:8081/") + log.Fatal(http.ListenAndServe(":8081", nil)) +} + +func handleGetPerson(w http.ResponseWriter, r *http.Request) { + span := opentracing.GlobalTracer().StartSpan("/getPerson") + defer span.Finish() + ctx := opentracing.ContextWithSpan(r.Context(), span) + + name := strings.TrimPrefix(r.URL.Path, "/getPerson/") + person, err := repo.GetPerson(ctx, name) + if err != nil { + span.SetTag("error", true) + span.LogFields(otlog.Error(err)) + http.Error(w, err.Error(), http.StatusInternalServerError) + return + } + + span.LogKV( + "name", person.Name, + "title", person.Title, + "description", person.Description, + ) + + bytes, _ := json.Marshal(person) + w.Write(bytes) +} diff --git a/chapter-04/go/exercise4a/formatter/main.go b/chapter-04/go/exercise4a/formatter/main.go new file mode 100644 index 0000000..9032c1e --- /dev/null +++ b/chapter-04/go/exercise4a/formatter/main.go @@ -0,0 +1,57 @@ +package main + +import ( + "context" + "log" + "net/http" + + opentracing "github.com/opentracing/opentracing-go" + + "github.com/PacktPublishing/Distributed-Tracing/chapter-04/go/lib/tracing" +) + +func main() { + tracer, closer := tracing.Init("go-4-formatter") + defer closer.Close() + opentracing.SetGlobalTracer(tracer) + + http.HandleFunc("/formatGreeting", handleFormatGreeting) + + log.Print("Listening on http://localhost:8082/") + log.Fatal(http.ListenAndServe(":8082", nil)) +} + +func handleFormatGreeting(w http.ResponseWriter, r *http.Request) { + span := opentracing.GlobalTracer().StartSpan("/formatGreeting") + defer span.Finish() + ctx := opentracing.ContextWithSpan(r.Context(), span) + + name := r.FormValue("name") + title := r.FormValue("title") + descr := r.FormValue("description") + + greeting := FormatGreeting(ctx, name, title, descr) + w.Write([]byte(greeting)) +} + +// FormatGreeting combines information about a person into a greeting string. +func FormatGreeting( + ctx context.Context, + name, title, description string, +) string { + span, ctx := opentracing.StartSpanFromContext( + ctx, + "format-greeting", + ) + defer span.Finish() + + response := "Hello, " + if title != "" { + response += title + " " + } + response += name + "!" + if description != "" { + response += " " + description + } + return response +} diff --git a/chapter-04/go/exercise4a/hello.go b/chapter-04/go/exercise4a/hello.go new file mode 100644 index 0000000..99456d4 --- /dev/null +++ b/chapter-04/go/exercise4a/hello.go @@ -0,0 +1,81 @@ +package main + +import ( + "context" + "encoding/json" + "log" + "net/http" + "net/url" + "strings" + + opentracing "github.com/opentracing/opentracing-go" + otlog "github.com/opentracing/opentracing-go/log" + + "github.com/PacktPublishing/Distributed-Tracing/chapter-04/go/lib/http" + "github.com/PacktPublishing/Distributed-Tracing/chapter-04/go/lib/model" + "github.com/PacktPublishing/Distributed-Tracing/chapter-04/go/lib/tracing" +) + +func main() { + tracer, closer := tracing.Init("go-4-hello") + defer closer.Close() + opentracing.SetGlobalTracer(tracer) + + http.HandleFunc("/sayHello/", handleSayHello) + + log.Print("Listening on http://localhost:8080/") + log.Fatal(http.ListenAndServe(":8080", nil)) +} + +func handleSayHello(w http.ResponseWriter, r *http.Request) { + span := opentracing.GlobalTracer().StartSpan("say-hello") + defer span.Finish() + ctx := opentracing.ContextWithSpan(r.Context(), span) + + name := strings.TrimPrefix(r.URL.Path, "/sayHello/") + greeting, err := SayHello(ctx, name) + if err != nil { + span.SetTag("error", true) + span.LogFields(otlog.Error(err)) + http.Error(w, err.Error(), http.StatusInternalServerError) + return + } + + span.SetTag("response", greeting) + w.Write([]byte(greeting)) +} + +// SayHello creates a greeting for the named person. +func SayHello(ctx context.Context, name string) (string, error) { + person, err := getPerson(ctx, name) + if err != nil { + return "", err + } + + return formatGreeting(ctx, person) +} + +func getPerson(ctx context.Context, name string) (*model.Person, error) { + res, err := xhttp.Get("http://localhost:8081/getPerson/" + name) + if err != nil { + return nil, err + } + var person model.Person + if err := json.Unmarshal(res, &person); err != nil { + return nil, err + } + return &person, nil +} + +func formatGreeting(ctx context.Context, person *model.Person) (string, error) { + v := url.Values{} + v.Set("name", person.Name) + v.Set("title", person.Title) + v.Set("description", person.Description) + url := "http://localhost:8082/formatGreeting?" + v.Encode() + res, err := xhttp.Get(url) + if err != nil { + return "", err + } + return string(res), nil +} diff --git a/chapter-04/go/exercise4a/people/repository.go b/chapter-04/go/exercise4a/people/repository.go new file mode 100644 index 0000000..948f8cf --- /dev/null +++ b/chapter-04/go/exercise4a/people/repository.go @@ -0,0 +1,78 @@ +package people + +import ( + "context" + "database/sql" + "log" + + _ "github.com/go-sql-driver/mysql" + opentracing "github.com/opentracing/opentracing-go" + + "github.com/PacktPublishing/Distributed-Tracing/chapter-04/go/lib/model" +) + +const dburl = "root:mysqlpwd@tcp(127.0.0.1:3306)/chapter04" + +// Repository retrieves information about people. +type Repository struct { + db *sql.DB +} + +// NewRepository creates a new Repository backed by MySQL database. +func NewRepository() *Repository { + db, err := sql.Open("mysql", dburl) + if err != nil { + log.Fatal(err) + } + err = db.Ping() + if err != nil { + log.Fatalf("Cannot ping the db: %v", err) + } + return &Repository{ + db: db, + } +} + +// GetPerson tries to find the person in the database by name. +// If not found, it still returns a Person object with only name +// field populated. +func (r *Repository) GetPerson( + ctx context.Context, + name string, +) (model.Person, error) { + query := "select title, description from people where name = ?" + + span, ctx := opentracing.StartSpanFromContext( + ctx, + "get-person", + opentracing.Tag{Key: "db.statement", Value: query}, + ) + defer span.Finish() + + rows, err := r.db.QueryContext(ctx, query, name) + if err != nil { + return model.Person{}, err + } + defer rows.Close() + + for rows.Next() { + var title, descr string + err := rows.Scan(&title, &descr) + if err != nil { + return model.Person{}, err + } + return model.Person{ + Name: name, + Title: title, + Description: descr, + }, nil + } + return model.Person{ + Name: name, + }, nil +} + +// Close calls close on the underlying db connection. +func (r *Repository) Close() { + r.db.Close() +} diff --git a/chapter-04/go/exercise4b/bigbrother/main.go b/chapter-04/go/exercise4b/bigbrother/main.go new file mode 100644 index 0000000..4cc658c --- /dev/null +++ b/chapter-04/go/exercise4b/bigbrother/main.go @@ -0,0 +1,62 @@ +package main + +import ( + "encoding/json" + "log" + "net/http" + "strings" + + opentracing "github.com/opentracing/opentracing-go" + otlog "github.com/opentracing/opentracing-go/log" + + "github.com/PacktPublishing/Distributed-Tracing/chapter-04/go/exercise4b/people" + "github.com/PacktPublishing/Distributed-Tracing/chapter-04/go/lib/tracing" +) + +var repo *people.Repository + +func main() { + tracer, closer := tracing.Init("go-4-bigbrother") + defer closer.Close() + opentracing.SetGlobalTracer(tracer) + + repo = people.NewRepository() + defer repo.Close() + + http.HandleFunc("/getPerson/", handleGetPerson) + + log.Print("Listening on http://localhost:8081/") + log.Fatal(http.ListenAndServe(":8081", nil)) +} + +func handleGetPerson(w http.ResponseWriter, r *http.Request) { + spanCtx, _ := opentracing.GlobalTracer().Extract( + opentracing.HTTPHeaders, + opentracing.HTTPHeadersCarrier(r.Header), + ) + span := opentracing.GlobalTracer().StartSpan( + "/getPerson", + opentracing.ChildOf(spanCtx), + ) + defer span.Finish() + + ctx := opentracing.ContextWithSpan(r.Context(), span) + + name := strings.TrimPrefix(r.URL.Path, "/getPerson/") + person, err := repo.GetPerson(ctx, name) + if err != nil { + span.SetTag("error", true) + span.LogFields(otlog.Error(err)) + http.Error(w, err.Error(), http.StatusInternalServerError) + return + } + + span.LogKV( + "name", person.Name, + "title", person.Title, + "description", person.Description, + ) + + bytes, _ := json.Marshal(person) + w.Write(bytes) +} diff --git a/chapter-04/go/exercise4b/formatter/main.go b/chapter-04/go/exercise4b/formatter/main.go new file mode 100644 index 0000000..468cb07 --- /dev/null +++ b/chapter-04/go/exercise4b/formatter/main.go @@ -0,0 +1,66 @@ +package main + +import ( + "context" + "log" + "net/http" + + opentracing "github.com/opentracing/opentracing-go" + ottag "github.com/opentracing/opentracing-go/ext" + + "github.com/PacktPublishing/Distributed-Tracing/chapter-04/go/lib/tracing" +) + +func main() { + tracer, closer := tracing.Init("go-4-formatter") + defer closer.Close() + opentracing.SetGlobalTracer(tracer) + + http.HandleFunc("/formatGreeting", handleFormatGreeting) + + log.Print("Listening on http://localhost:8082/") + log.Fatal(http.ListenAndServe(":8082", nil)) +} + +func handleFormatGreeting(w http.ResponseWriter, r *http.Request) { + spanCtx, _ := opentracing.GlobalTracer().Extract( + opentracing.HTTPHeaders, + opentracing.HTTPHeadersCarrier(r.Header), + ) + span := opentracing.GlobalTracer().StartSpan( + "/formatGreeting", + ottag.RPCServerOption(spanCtx), + ) + defer span.Finish() + + ctx := opentracing.ContextWithSpan(r.Context(), span) + + name := r.FormValue("name") + title := r.FormValue("title") + descr := r.FormValue("description") + + greeting := FormatGreeting(ctx, name, title, descr) + w.Write([]byte(greeting)) +} + +// FormatGreeting combines information about a person into a greeting string. +func FormatGreeting( + ctx context.Context, + name, title, description string, +) string { + span, ctx := opentracing.StartSpanFromContext( + ctx, + "format-greeting", + ) + defer span.Finish() + + response := "Hello, " + if title != "" { + response += title + " " + } + response += name + "!" + if description != "" { + response += " " + description + } + return response +} diff --git a/chapter-04/go/exercise4b/hello.go b/chapter-04/go/exercise4b/hello.go new file mode 100644 index 0000000..82d3e0c --- /dev/null +++ b/chapter-04/go/exercise4b/hello.go @@ -0,0 +1,115 @@ +package main + +import ( + "context" + "encoding/json" + "log" + "net/http" + "net/url" + "strings" + + opentracing "github.com/opentracing/opentracing-go" + ottag "github.com/opentracing/opentracing-go/ext" + otlog "github.com/opentracing/opentracing-go/log" + + "github.com/PacktPublishing/Distributed-Tracing/chapter-04/go/lib/http" + "github.com/PacktPublishing/Distributed-Tracing/chapter-04/go/lib/model" + "github.com/PacktPublishing/Distributed-Tracing/chapter-04/go/lib/tracing" +) + +func main() { + tracer, closer := tracing.Init("go-4-hello") + defer closer.Close() + opentracing.SetGlobalTracer(tracer) + + http.HandleFunc("/sayHello/", handleSayHello) + + log.Print("Listening on http://localhost:8080/") + log.Fatal(http.ListenAndServe(":8080", nil)) +} + +func handleSayHello(w http.ResponseWriter, r *http.Request) { + spanCtx, _ := opentracing.GlobalTracer().Extract( + opentracing.HTTPHeaders, + opentracing.HTTPHeadersCarrier(r.Header), + ) + span := opentracing.GlobalTracer().StartSpan( + "say-hello", + ottag.RPCServerOption(spanCtx), + ) + defer span.Finish() + ctx := opentracing.ContextWithSpan(r.Context(), span) + + name := strings.TrimPrefix(r.URL.Path, "/sayHello/") + greeting, err := SayHello(ctx, name) + if err != nil { + span.SetTag("error", true) + span.LogFields(otlog.Error(err)) + http.Error(w, err.Error(), http.StatusInternalServerError) + return + } + + span.SetTag("response", greeting) + w.Write([]byte(greeting)) +} + +// SayHello creates a greeting for the named person. +func SayHello(ctx context.Context, name string) (string, error) { + person, err := getPerson(ctx, name) + if err != nil { + return "", err + } + + return formatGreeting(ctx, person) +} + +func getPerson(ctx context.Context, name string) (*model.Person, error) { + url := "http://localhost:8081/getPerson/" + name + res, err := get(ctx, "getPerson", url) + if err != nil { + return nil, err + } + + var person model.Person + if err := json.Unmarshal(res, &person); err != nil { + return nil, err + } + return &person, nil +} + +func formatGreeting( + ctx context.Context, + person *model.Person, +) (string, error) { + v := url.Values{} + v.Set("name", person.Name) + v.Set("title", person.Title) + v.Set("description", person.Description) + url := "http://localhost:8082/formatGreeting?" + v.Encode() + res, err := get(ctx, "formatGreeting", url) + if err != nil { + return "", err + } + return string(res), nil +} + +func get(ctx context.Context, operationName, url string) ([]byte, error) { + req, err := http.NewRequest("GET", url, nil) + if err != nil { + return nil, err + } + + span, ctx := opentracing.StartSpanFromContext(ctx, operationName) + defer span.Finish() + + ottag.SpanKindRPCClient.Set(span) + ottag.HTTPUrl.Set(span, url) + ottag.HTTPMethod.Set(span, "GET") + opentracing.GlobalTracer().Inject( + span.Context(), + opentracing.HTTPHeaders, + opentracing.HTTPHeadersCarrier(req.Header), + ) + + return xhttp.Do(req) +} diff --git a/chapter-04/go/exercise4b/people/repository.go b/chapter-04/go/exercise4b/people/repository.go new file mode 100644 index 0000000..948f8cf --- /dev/null +++ b/chapter-04/go/exercise4b/people/repository.go @@ -0,0 +1,78 @@ +package people + +import ( + "context" + "database/sql" + "log" + + _ "github.com/go-sql-driver/mysql" + opentracing "github.com/opentracing/opentracing-go" + + "github.com/PacktPublishing/Distributed-Tracing/chapter-04/go/lib/model" +) + +const dburl = "root:mysqlpwd@tcp(127.0.0.1:3306)/chapter04" + +// Repository retrieves information about people. +type Repository struct { + db *sql.DB +} + +// NewRepository creates a new Repository backed by MySQL database. +func NewRepository() *Repository { + db, err := sql.Open("mysql", dburl) + if err != nil { + log.Fatal(err) + } + err = db.Ping() + if err != nil { + log.Fatalf("Cannot ping the db: %v", err) + } + return &Repository{ + db: db, + } +} + +// GetPerson tries to find the person in the database by name. +// If not found, it still returns a Person object with only name +// field populated. +func (r *Repository) GetPerson( + ctx context.Context, + name string, +) (model.Person, error) { + query := "select title, description from people where name = ?" + + span, ctx := opentracing.StartSpanFromContext( + ctx, + "get-person", + opentracing.Tag{Key: "db.statement", Value: query}, + ) + defer span.Finish() + + rows, err := r.db.QueryContext(ctx, query, name) + if err != nil { + return model.Person{}, err + } + defer rows.Close() + + for rows.Next() { + var title, descr string + err := rows.Scan(&title, &descr) + if err != nil { + return model.Person{}, err + } + return model.Person{ + Name: name, + Title: title, + Description: descr, + }, nil + } + return model.Person{ + Name: name, + }, nil +} + +// Close calls close on the underlying db connection. +func (r *Repository) Close() { + r.db.Close() +} diff --git a/chapter-04/go/exercise5/bigbrother/main.go b/chapter-04/go/exercise5/bigbrother/main.go new file mode 100644 index 0000000..53658c9 --- /dev/null +++ b/chapter-04/go/exercise5/bigbrother/main.go @@ -0,0 +1,62 @@ +package main + +import ( + "encoding/json" + "log" + "net/http" + "strings" + + opentracing "github.com/opentracing/opentracing-go" + otlog "github.com/opentracing/opentracing-go/log" + + "github.com/PacktPublishing/Distributed-Tracing/chapter-04/go/exercise5/people" + "github.com/PacktPublishing/Distributed-Tracing/chapter-04/go/lib/tracing" +) + +var repo *people.Repository + +func main() { + tracer, closer := tracing.Init("go-5-bigbrother") + defer closer.Close() + opentracing.SetGlobalTracer(tracer) + + repo = people.NewRepository() + defer repo.Close() + + http.HandleFunc("/getPerson/", handleGetPerson) + + log.Print("Listening on http://localhost:8081/") + log.Fatal(http.ListenAndServe(":8081", nil)) +} + +func handleGetPerson(w http.ResponseWriter, r *http.Request) { + spanCtx, _ := opentracing.GlobalTracer().Extract( + opentracing.HTTPHeaders, + opentracing.HTTPHeadersCarrier(r.Header), + ) + span := opentracing.GlobalTracer().StartSpan( + "/getPerson", + opentracing.ChildOf(spanCtx), + ) + defer span.Finish() + + ctx := opentracing.ContextWithSpan(r.Context(), span) + + name := strings.TrimPrefix(r.URL.Path, "/getPerson/") + person, err := repo.GetPerson(ctx, name) + if err != nil { + span.SetTag("error", true) + span.LogFields(otlog.Error(err)) + http.Error(w, err.Error(), http.StatusInternalServerError) + return + } + + span.LogKV( + "name", person.Name, + "title", person.Title, + "description", person.Description, + ) + + bytes, _ := json.Marshal(person) + w.Write(bytes) +} diff --git a/chapter-04/go/exercise5/formatter/main.go b/chapter-04/go/exercise5/formatter/main.go new file mode 100644 index 0000000..3dc7156 --- /dev/null +++ b/chapter-04/go/exercise5/formatter/main.go @@ -0,0 +1,70 @@ +package main + +import ( + "context" + "log" + "net/http" + + opentracing "github.com/opentracing/opentracing-go" + ottag "github.com/opentracing/opentracing-go/ext" + + "github.com/PacktPublishing/Distributed-Tracing/chapter-04/go/lib/tracing" +) + +func main() { + tracer, closer := tracing.Init("go-5-formatter") + defer closer.Close() + opentracing.SetGlobalTracer(tracer) + + http.HandleFunc("/formatGreeting", handleFormatGreeting) + + log.Print("Listening on http://localhost:8082/") + log.Fatal(http.ListenAndServe(":8082", nil)) +} + +func handleFormatGreeting(w http.ResponseWriter, r *http.Request) { + spanCtx, _ := opentracing.GlobalTracer().Extract( + opentracing.HTTPHeaders, + opentracing.HTTPHeadersCarrier(r.Header), + ) + span := opentracing.GlobalTracer().StartSpan( + "/formatGreeting", + ottag.RPCServerOption(spanCtx), + ) + defer span.Finish() + + ctx := opentracing.ContextWithSpan(r.Context(), span) + + name := r.FormValue("name") + title := r.FormValue("title") + descr := r.FormValue("description") + + greeting := FormatGreeting(ctx, name, title, descr) + w.Write([]byte(greeting)) +} + +// FormatGreeting combines information about a person into a greeting string. +func FormatGreeting( + ctx context.Context, + name, title, description string, +) string { + span, ctx := opentracing.StartSpanFromContext( + ctx, + "format-greeting", + ) + defer span.Finish() + + greeting := span.BaggageItem("greeting") + if greeting == "" { + greeting = "Hello" + } + response := greeting + ", " + if title != "" { + response += title + " " + } + response += name + "!" + if description != "" { + response += " " + description + } + return response +} diff --git a/chapter-04/go/exercise5/hello.go b/chapter-04/go/exercise5/hello.go new file mode 100644 index 0000000..941e5f6 --- /dev/null +++ b/chapter-04/go/exercise5/hello.go @@ -0,0 +1,115 @@ +package main + +import ( + "context" + "encoding/json" + "log" + "net/http" + "net/url" + "strings" + + opentracing "github.com/opentracing/opentracing-go" + ottag "github.com/opentracing/opentracing-go/ext" + otlog "github.com/opentracing/opentracing-go/log" + + "github.com/PacktPublishing/Distributed-Tracing/chapter-04/go/lib/http" + "github.com/PacktPublishing/Distributed-Tracing/chapter-04/go/lib/model" + "github.com/PacktPublishing/Distributed-Tracing/chapter-04/go/lib/tracing" +) + +func main() { + tracer, closer := tracing.Init("go-5-hello") + defer closer.Close() + opentracing.SetGlobalTracer(tracer) + + http.HandleFunc("/sayHello/", handleSayHello) + + log.Print("Listening on http://localhost:8080/") + log.Fatal(http.ListenAndServe(":8080", nil)) +} + +func handleSayHello(w http.ResponseWriter, r *http.Request) { + spanCtx, _ := opentracing.GlobalTracer().Extract( + opentracing.HTTPHeaders, + opentracing.HTTPHeadersCarrier(r.Header), + ) + span := opentracing.GlobalTracer().StartSpan( + "say-hello", + ottag.RPCServerOption(spanCtx), + ) + defer span.Finish() + ctx := opentracing.ContextWithSpan(r.Context(), span) + + name := strings.TrimPrefix(r.URL.Path, "/sayHello/") + greeting, err := SayHello(ctx, name) + if err != nil { + span.SetTag("error", true) + span.LogFields(otlog.Error(err)) + http.Error(w, err.Error(), http.StatusInternalServerError) + return + } + + span.SetTag("response", greeting) + w.Write([]byte(greeting)) +} + +// SayHello creates a greeting for the named person. +func SayHello(ctx context.Context, name string) (string, error) { + person, err := getPerson(ctx, name) + if err != nil { + return "", err + } + + return formatGreeting(ctx, person) +} + +func getPerson(ctx context.Context, name string) (*model.Person, error) { + url := "http://localhost:8081/getPerson/" + name + res, err := get(ctx, "getPerson", url) + if err != nil { + return nil, err + } + + var person model.Person + if err := json.Unmarshal(res, &person); err != nil { + return nil, err + } + return &person, nil +} + +func formatGreeting( + ctx context.Context, + person *model.Person, +) (string, error) { + v := url.Values{} + v.Set("name", person.Name) + v.Set("title", person.Title) + v.Set("description", person.Description) + url := "http://localhost:8082/formatGreeting?" + v.Encode() + res, err := get(ctx, "formatGreeting", url) + if err != nil { + return "", err + } + return string(res), nil +} + +func get(ctx context.Context, operationName, url string) ([]byte, error) { + req, err := http.NewRequest("GET", url, nil) + if err != nil { + return nil, err + } + + span, ctx := opentracing.StartSpanFromContext(ctx, operationName) + defer span.Finish() + + ottag.SpanKindRPCClient.Set(span) + ottag.HTTPUrl.Set(span, url) + ottag.HTTPMethod.Set(span, "GET") + opentracing.GlobalTracer().Inject( + span.Context(), + opentracing.HTTPHeaders, + opentracing.HTTPHeadersCarrier(req.Header), + ) + + return xhttp.Do(req) +} diff --git a/chapter-04/go/exercise5/people/repository.go b/chapter-04/go/exercise5/people/repository.go new file mode 100644 index 0000000..948f8cf --- /dev/null +++ b/chapter-04/go/exercise5/people/repository.go @@ -0,0 +1,78 @@ +package people + +import ( + "context" + "database/sql" + "log" + + _ "github.com/go-sql-driver/mysql" + opentracing "github.com/opentracing/opentracing-go" + + "github.com/PacktPublishing/Distributed-Tracing/chapter-04/go/lib/model" +) + +const dburl = "root:mysqlpwd@tcp(127.0.0.1:3306)/chapter04" + +// Repository retrieves information about people. +type Repository struct { + db *sql.DB +} + +// NewRepository creates a new Repository backed by MySQL database. +func NewRepository() *Repository { + db, err := sql.Open("mysql", dburl) + if err != nil { + log.Fatal(err) + } + err = db.Ping() + if err != nil { + log.Fatalf("Cannot ping the db: %v", err) + } + return &Repository{ + db: db, + } +} + +// GetPerson tries to find the person in the database by name. +// If not found, it still returns a Person object with only name +// field populated. +func (r *Repository) GetPerson( + ctx context.Context, + name string, +) (model.Person, error) { + query := "select title, description from people where name = ?" + + span, ctx := opentracing.StartSpanFromContext( + ctx, + "get-person", + opentracing.Tag{Key: "db.statement", Value: query}, + ) + defer span.Finish() + + rows, err := r.db.QueryContext(ctx, query, name) + if err != nil { + return model.Person{}, err + } + defer rows.Close() + + for rows.Next() { + var title, descr string + err := rows.Scan(&title, &descr) + if err != nil { + return model.Person{}, err + } + return model.Person{ + Name: name, + Title: title, + Description: descr, + }, nil + } + return model.Person{ + Name: name, + }, nil +} + +// Close calls close on the underlying db connection. +func (r *Repository) Close() { + r.db.Close() +} diff --git a/chapter-04/go/exercise6/bigbrother/main.go b/chapter-04/go/exercise6/bigbrother/main.go new file mode 100644 index 0000000..b084334 --- /dev/null +++ b/chapter-04/go/exercise6/bigbrother/main.go @@ -0,0 +1,51 @@ +package main + +import ( + "encoding/json" + "net/http" + "strings" + + opentracing "github.com/opentracing/opentracing-go" + otlog "github.com/opentracing/opentracing-go/log" + + "github.com/PacktPublishing/Distributed-Tracing/chapter-04/go/exercise6/othttp" + "github.com/PacktPublishing/Distributed-Tracing/chapter-04/go/exercise6/people" + "github.com/PacktPublishing/Distributed-Tracing/chapter-04/go/lib/tracing" +) + +var repo *people.Repository + +func main() { + tracer, closer := tracing.Init("go-6-bigbrother") + defer closer.Close() + opentracing.SetGlobalTracer(tracer) + + repo = people.NewRepository() + defer repo.Close() + + http.HandleFunc("/getPerson/", handleGetPerson) + othttp.ListenAndServe(":8081", "/getPerson") +} + +func handleGetPerson(w http.ResponseWriter, r *http.Request) { + span := opentracing.SpanFromContext(r.Context()) + + name := strings.TrimPrefix(r.URL.Path, "/getPerson/") + person, err := repo.GetPerson(r.Context(), name) + + if err != nil { + span.SetTag("error", true) + span.LogFields(otlog.Error(err)) + http.Error(w, err.Error(), http.StatusInternalServerError) + return + } + + span.LogKV( + "name", person.Name, + "title", person.Title, + "description", person.Description, + ) + + bytes, _ := json.Marshal(person) + w.Write(bytes) +} diff --git a/chapter-04/go/exercise6/formatter/main.go b/chapter-04/go/exercise6/formatter/main.go new file mode 100644 index 0000000..8e9fd5d --- /dev/null +++ b/chapter-04/go/exercise6/formatter/main.go @@ -0,0 +1,51 @@ +package main + +import ( + "context" + "net/http" + + opentracing "github.com/opentracing/opentracing-go" + + "github.com/PacktPublishing/Distributed-Tracing/chapter-04/go/exercise6/othttp" + "github.com/PacktPublishing/Distributed-Tracing/chapter-04/go/lib/tracing" +) + +func main() { + tracer, closer := tracing.Init("go-6-formatter") + defer closer.Close() + opentracing.SetGlobalTracer(tracer) + + http.HandleFunc("/formatGreeting", handleFormatGreeting) + othttp.ListenAndServe(":8082", "/formatGreeting") +} + +func handleFormatGreeting(w http.ResponseWriter, r *http.Request) { + name := r.FormValue("name") + title := r.FormValue("title") + descr := r.FormValue("description") + + greeting := FormatGreeting(r.Context(), name, title, descr) + w.Write([]byte(greeting)) +} + +// FormatGreeting combines information about a person into a greeting string. +func FormatGreeting( + ctx context.Context, + name, title, description string, +) string { + span := opentracing.SpanFromContext(ctx) + + greeting := span.BaggageItem("greeting") + if greeting == "" { + greeting = "Hello" + } + response := greeting + ", " + if title != "" { + response += title + " " + } + response += name + "!" + if description != "" { + response += " " + description + } + return response +} diff --git a/chapter-04/go/exercise6/hello.go b/chapter-04/go/exercise6/hello.go new file mode 100644 index 0000000..96ed837 --- /dev/null +++ b/chapter-04/go/exercise6/hello.go @@ -0,0 +1,98 @@ +package main + +import ( + "context" + "encoding/json" + "net/http" + "net/url" + "strings" + + "github.com/opentracing-contrib/go-stdlib/nethttp" + opentracing "github.com/opentracing/opentracing-go" + otlog "github.com/opentracing/opentracing-go/log" + + "github.com/PacktPublishing/Distributed-Tracing/chapter-04/go/exercise6/othttp" + "github.com/PacktPublishing/Distributed-Tracing/chapter-04/go/lib/http" + "github.com/PacktPublishing/Distributed-Tracing/chapter-04/go/lib/model" + "github.com/PacktPublishing/Distributed-Tracing/chapter-04/go/lib/tracing" +) + +var client = &http.Client{Transport: &nethttp.Transport{}} + +func main() { + tracer, closer := tracing.Init("go-6-hello") + defer closer.Close() + opentracing.SetGlobalTracer(tracer) + + http.HandleFunc("/sayHello/", handleSayHello) + othttp.ListenAndServe(":8080", "/sayHello") +} + +func handleSayHello(w http.ResponseWriter, r *http.Request) { + span := opentracing.SpanFromContext(r.Context()) + + name := strings.TrimPrefix(r.URL.Path, "/sayHello/") + greeting, err := SayHello(r.Context(), name) + if err != nil { + span.SetTag("error", true) + span.LogFields(otlog.Error(err)) + http.Error(w, err.Error(), http.StatusInternalServerError) + return + } + + span.SetTag("response", greeting) + w.Write([]byte(greeting)) +} + +// SayHello creates a greeting for the named person. +func SayHello(ctx context.Context, name string) (string, error) { + person, err := getPerson(ctx, name) + if err != nil { + return "", err + } + + return formatGreeting(ctx, person) +} + +func getPerson(ctx context.Context, name string) (*model.Person, error) { + url := "http://localhost:8081/getPerson/" + name + res, err := get(ctx, "getPerson", url) + if err != nil { + return nil, err + } + + var person model.Person + if err := json.Unmarshal(res, &person); err != nil { + return nil, err + } + return &person, nil +} + +func formatGreeting( + ctx context.Context, + person *model.Person, +) (string, error) { + v := url.Values{} + v.Set("name", person.Name) + v.Set("title", person.Title) + v.Set("description", person.Description) + url := "http://localhost:8082/formatGreeting?" + v.Encode() + res, err := get(ctx, "formatGreeting", url) + if err != nil { + return "", err + } + return string(res), nil +} + +func get(ctx context.Context, operationName, url string) ([]byte, error) { + req, err := http.NewRequest("GET", url, nil) + if err != nil { + return nil, err + } + req = req.WithContext(ctx) + + req, ht := nethttp.TraceRequest(opentracing.GlobalTracer(), req) + defer ht.Finish() + + return xhttp.DoWithClient(req, client) +} diff --git a/chapter-04/go/exercise6/othttp/middleware.go b/chapter-04/go/exercise6/othttp/middleware.go new file mode 100644 index 0000000..7308fcf --- /dev/null +++ b/chapter-04/go/exercise6/othttp/middleware.go @@ -0,0 +1,23 @@ +package othttp + +import ( + "log" + "net/http" + + "github.com/opentracing-contrib/go-stdlib/nethttp" + "github.com/opentracing/opentracing-go" +) + +// ListenAndServe starts an instrumented server with a single endpoint. +func ListenAndServe(hostPort string, endpoint string) { + mw := nethttp.Middleware( + opentracing.GlobalTracer(), + http.DefaultServeMux, + nethttp.OperationNameFunc(func(r *http.Request) string { + return "HTTP " + r.Method + ":" + endpoint + }), + ) + + log.Print("Listening on http://" + hostPort) + log.Fatal(http.ListenAndServe(hostPort, mw)) +} diff --git a/chapter-04/go/exercise6/people/repository.go b/chapter-04/go/exercise6/people/repository.go new file mode 100644 index 0000000..948f8cf --- /dev/null +++ b/chapter-04/go/exercise6/people/repository.go @@ -0,0 +1,78 @@ +package people + +import ( + "context" + "database/sql" + "log" + + _ "github.com/go-sql-driver/mysql" + opentracing "github.com/opentracing/opentracing-go" + + "github.com/PacktPublishing/Distributed-Tracing/chapter-04/go/lib/model" +) + +const dburl = "root:mysqlpwd@tcp(127.0.0.1:3306)/chapter04" + +// Repository retrieves information about people. +type Repository struct { + db *sql.DB +} + +// NewRepository creates a new Repository backed by MySQL database. +func NewRepository() *Repository { + db, err := sql.Open("mysql", dburl) + if err != nil { + log.Fatal(err) + } + err = db.Ping() + if err != nil { + log.Fatalf("Cannot ping the db: %v", err) + } + return &Repository{ + db: db, + } +} + +// GetPerson tries to find the person in the database by name. +// If not found, it still returns a Person object with only name +// field populated. +func (r *Repository) GetPerson( + ctx context.Context, + name string, +) (model.Person, error) { + query := "select title, description from people where name = ?" + + span, ctx := opentracing.StartSpanFromContext( + ctx, + "get-person", + opentracing.Tag{Key: "db.statement", Value: query}, + ) + defer span.Finish() + + rows, err := r.db.QueryContext(ctx, query, name) + if err != nil { + return model.Person{}, err + } + defer rows.Close() + + for rows.Next() { + var title, descr string + err := rows.Scan(&title, &descr) + if err != nil { + return model.Person{}, err + } + return model.Person{ + Name: name, + Title: title, + Description: descr, + }, nil + } + return model.Person{ + Name: name, + }, nil +} + +// Close calls close on the underlying db connection. +func (r *Repository) Close() { + r.db.Close() +} diff --git a/chapter-04/go/lib/http/client.go b/chapter-04/go/lib/http/client.go new file mode 100644 index 0000000..70c47a6 --- /dev/null +++ b/chapter-04/go/lib/http/client.go @@ -0,0 +1,44 @@ +package xhttp + +import ( + "fmt" + "io/ioutil" + "net/http" +) + +// Get executes an HTTP GET request and returns the response body. +// Any errors or non-200 status code result in an error. +func Get(url string) ([]byte, error) { + req, err := http.NewRequest("GET", url, nil) + if err != nil { + return nil, err + } + return Do(req) +} + +// Do executes an HTTP request and returns the response body. +// Any errors or non-200 status code result in an error. +func Do(req *http.Request) ([]byte, error) { + return DoWithClient(req, http.DefaultClient) +} + +// DoWithClient executes an HTTP request and returns the response body. +// Any errors or non-200 status code result in an error. +func DoWithClient(req *http.Request, client *http.Client) ([]byte, error) { + resp, err := client.Do(req) + if err != nil { + return nil, err + } + defer resp.Body.Close() + + body, err := ioutil.ReadAll(resp.Body) + if err != nil { + return nil, err + } + + if resp.StatusCode != 200 { + return nil, fmt.Errorf("StatusCode: %d, Body: %s", resp.StatusCode, body) + } + + return body, nil +} diff --git a/chapter-04/go/lib/model/person.go b/chapter-04/go/lib/model/person.go new file mode 100644 index 0000000..b7c529e --- /dev/null +++ b/chapter-04/go/lib/model/person.go @@ -0,0 +1,8 @@ +package model + +// Person represents a person. +type Person struct { + Name string + Title string + Description string +} diff --git a/chapter-04/go/lib/tracing/init.go b/chapter-04/go/lib/tracing/init.go new file mode 100644 index 0000000..4e4623e --- /dev/null +++ b/chapter-04/go/lib/tracing/init.go @@ -0,0 +1,32 @@ +package tracing + +import ( + "io" + "log" + + opentracing "github.com/opentracing/opentracing-go" + jaeger "github.com/uber/jaeger-client-go" + config "github.com/uber/jaeger-client-go/config" +) + +// Init returns an instance of Jaeger Tracer that samples 100% +// of traces and logs all spans to stdout. +func Init(service string) (opentracing.Tracer, io.Closer) { + cfg := &config.Configuration{ + Sampler: &config.SamplerConfig{ + Type: "const", + Param: 1, + }, + Reporter: &config.ReporterConfig{ + LogSpans: true, + }, + } + tracer, closer, err := cfg.New( + service, + config.Logger(jaeger.StdLogger), + ) + if err != nil { + log.Fatalf("ERROR: cannot init Jaeger: %v", err) + } + return tracer, closer +} diff --git a/chapter-04/java/.gitignore b/chapter-04/java/.gitignore new file mode 100644 index 0000000..d11829c --- /dev/null +++ b/chapter-04/java/.gitignore @@ -0,0 +1,4 @@ +.classpath +.project +.settings/ +target/ diff --git a/chapter-04/java/.mvn/wrapper/.gitignore b/chapter-04/java/.mvn/wrapper/.gitignore new file mode 100644 index 0000000..e72f5e8 --- /dev/null +++ b/chapter-04/java/.mvn/wrapper/.gitignore @@ -0,0 +1 @@ +maven-wrapper.jar diff --git a/chapter-04/java/.mvn/wrapper/MavenWrapperDownloader.java b/chapter-04/java/.mvn/wrapper/MavenWrapperDownloader.java new file mode 100755 index 0000000..fa4f7b4 --- /dev/null +++ b/chapter-04/java/.mvn/wrapper/MavenWrapperDownloader.java @@ -0,0 +1,110 @@ +/* +Licensed to the Apache Software Foundation (ASF) under one +or more contributor license agreements. See the NOTICE file +distributed with this work for additional information +regarding copyright ownership. The ASF licenses this file +to you under the Apache License, Version 2.0 (the +"License"); you may not use this file except in compliance +with the License. You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, +software distributed under the License is distributed on an +"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +KIND, either express or implied. See the License for the +specific language governing permissions and limitations +under the License. +*/ + +import java.net.*; +import java.io.*; +import java.nio.channels.*; +import java.util.Properties; + +public class MavenWrapperDownloader { + + /** + * Default URL to download the maven-wrapper.jar from, if no 'downloadUrl' is provided. + */ + private static final String DEFAULT_DOWNLOAD_URL = + "https://repo.maven.apache.org/maven2/io/takari/maven-wrapper/0.4.2/maven-wrapper-0.4.2.jar"; + + /** + * Path to the maven-wrapper.properties file, which might contain a downloadUrl property to + * use instead of the default one. + */ + private static final String MAVEN_WRAPPER_PROPERTIES_PATH = + ".mvn/wrapper/maven-wrapper.properties"; + + /** + * Path where the maven-wrapper.jar will be saved to. + */ + private static final String MAVEN_WRAPPER_JAR_PATH = + ".mvn/wrapper/maven-wrapper.jar"; + + /** + * Name of the property which should be used to override the default download url for the wrapper. + */ + private static final String PROPERTY_NAME_WRAPPER_URL = "wrapperUrl"; + + public static void main(String args[]) { + System.out.println("- Downloader started"); + File baseDirectory = new File(args[0]); + System.out.println("- Using base directory: " + baseDirectory.getAbsolutePath()); + + // If the maven-wrapper.properties exists, read it and check if it contains a custom + // wrapperUrl parameter. + File mavenWrapperPropertyFile = new File(baseDirectory, MAVEN_WRAPPER_PROPERTIES_PATH); + String url = DEFAULT_DOWNLOAD_URL; + if(mavenWrapperPropertyFile.exists()) { + FileInputStream mavenWrapperPropertyFileInputStream = null; + try { + mavenWrapperPropertyFileInputStream = new FileInputStream(mavenWrapperPropertyFile); + Properties mavenWrapperProperties = new Properties(); + mavenWrapperProperties.load(mavenWrapperPropertyFileInputStream); + url = mavenWrapperProperties.getProperty(PROPERTY_NAME_WRAPPER_URL, url); + } catch (IOException e) { + System.out.println("- ERROR loading '" + MAVEN_WRAPPER_PROPERTIES_PATH + "'"); + } finally { + try { + if(mavenWrapperPropertyFileInputStream != null) { + mavenWrapperPropertyFileInputStream.close(); + } + } catch (IOException e) { + // Ignore ... + } + } + } + System.out.println("- Downloading from: : " + url); + + File outputFile = new File(baseDirectory.getAbsolutePath(), MAVEN_WRAPPER_JAR_PATH); + if(!outputFile.getParentFile().exists()) { + if(!outputFile.getParentFile().mkdirs()) { + System.out.println( + "- ERROR creating output direcrory '" + outputFile.getParentFile().getAbsolutePath() + "'"); + } + } + System.out.println("- Downloading to: " + outputFile.getAbsolutePath()); + try { + downloadFileFromURL(url, outputFile); + System.out.println("Done"); + System.exit(0); + } catch (Throwable e) { + System.out.println("- Error downloading"); + e.printStackTrace(); + System.exit(1); + } + } + + private static void downloadFileFromURL(String urlString, File destination) throws Exception { + URL website = new URL(urlString); + ReadableByteChannel rbc; + rbc = Channels.newChannel(website.openStream()); + FileOutputStream fos = new FileOutputStream(destination); + fos.getChannel().transferFrom(rbc, 0, Long.MAX_VALUE); + fos.close(); + rbc.close(); + } + +} diff --git a/chapter-04/java/.mvn/wrapper/maven-wrapper.properties b/chapter-04/java/.mvn/wrapper/maven-wrapper.properties new file mode 100755 index 0000000..00d32aa --- /dev/null +++ b/chapter-04/java/.mvn/wrapper/maven-wrapper.properties @@ -0,0 +1 @@ +distributionUrl=https://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.5.4/apache-maven-3.5.4-bin.zip \ No newline at end of file diff --git a/chapter-04/java/mvnw b/chapter-04/java/mvnw new file mode 100755 index 0000000..5551fde --- /dev/null +++ b/chapter-04/java/mvnw @@ -0,0 +1,286 @@ +#!/bin/sh +# ---------------------------------------------------------------------------- +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +# ---------------------------------------------------------------------------- + +# ---------------------------------------------------------------------------- +# Maven2 Start Up Batch script +# +# Required ENV vars: +# ------------------ +# JAVA_HOME - location of a JDK home dir +# +# Optional ENV vars +# ----------------- +# M2_HOME - location of maven2's installed home dir +# MAVEN_OPTS - parameters passed to the Java VM when running Maven +# e.g. to debug Maven itself, use +# set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000 +# MAVEN_SKIP_RC - flag to disable loading of mavenrc files +# ---------------------------------------------------------------------------- + +if [ -z "$MAVEN_SKIP_RC" ] ; then + + if [ -f /etc/mavenrc ] ; then + . /etc/mavenrc + fi + + if [ -f "$HOME/.mavenrc" ] ; then + . "$HOME/.mavenrc" + fi + +fi + +# OS specific support. $var _must_ be set to either true or false. +cygwin=false; +darwin=false; +mingw=false +case "`uname`" in + CYGWIN*) cygwin=true ;; + MINGW*) mingw=true;; + Darwin*) darwin=true + # Use /usr/libexec/java_home if available, otherwise fall back to /Library/Java/Home + # See https://developer.apple.com/library/mac/qa/qa1170/_index.html + if [ -z "$JAVA_HOME" ]; then + if [ -x "/usr/libexec/java_home" ]; then + export JAVA_HOME="`/usr/libexec/java_home`" + else + export JAVA_HOME="/Library/Java/Home" + fi + fi + ;; +esac + +if [ -z "$JAVA_HOME" ] ; then + if [ -r /etc/gentoo-release ] ; then + JAVA_HOME=`java-config --jre-home` + fi +fi + +if [ -z "$M2_HOME" ] ; then + ## resolve links - $0 may be a link to maven's home + PRG="$0" + + # need this for relative symlinks + while [ -h "$PRG" ] ; do + ls=`ls -ld "$PRG"` + link=`expr "$ls" : '.*-> \(.*\)$'` + if expr "$link" : '/.*' > /dev/null; then + PRG="$link" + else + PRG="`dirname "$PRG"`/$link" + fi + done + + saveddir=`pwd` + + M2_HOME=`dirname "$PRG"`/.. + + # make it fully qualified + M2_HOME=`cd "$M2_HOME" && pwd` + + cd "$saveddir" + # echo Using m2 at $M2_HOME +fi + +# For Cygwin, ensure paths are in UNIX format before anything is touched +if $cygwin ; then + [ -n "$M2_HOME" ] && + M2_HOME=`cygpath --unix "$M2_HOME"` + [ -n "$JAVA_HOME" ] && + JAVA_HOME=`cygpath --unix "$JAVA_HOME"` + [ -n "$CLASSPATH" ] && + CLASSPATH=`cygpath --path --unix "$CLASSPATH"` +fi + +# For Mingw, ensure paths are in UNIX format before anything is touched +if $mingw ; then + [ -n "$M2_HOME" ] && + M2_HOME="`(cd "$M2_HOME"; pwd)`" + [ -n "$JAVA_HOME" ] && + JAVA_HOME="`(cd "$JAVA_HOME"; pwd)`" + # TODO classpath? +fi + +if [ -z "$JAVA_HOME" ]; then + javaExecutable="`which javac`" + if [ -n "$javaExecutable" ] && ! [ "`expr \"$javaExecutable\" : '\([^ ]*\)'`" = "no" ]; then + # readlink(1) is not available as standard on Solaris 10. + readLink=`which readlink` + if [ ! `expr "$readLink" : '\([^ ]*\)'` = "no" ]; then + if $darwin ; then + javaHome="`dirname \"$javaExecutable\"`" + javaExecutable="`cd \"$javaHome\" && pwd -P`/javac" + else + javaExecutable="`readlink -f \"$javaExecutable\"`" + fi + javaHome="`dirname \"$javaExecutable\"`" + javaHome=`expr "$javaHome" : '\(.*\)/bin'` + JAVA_HOME="$javaHome" + export JAVA_HOME + fi + fi +fi + +if [ -z "$JAVACMD" ] ; then + if [ -n "$JAVA_HOME" ] ; then + if [ -x "$JAVA_HOME/jre/sh/java" ] ; then + # IBM's JDK on AIX uses strange locations for the executables + JAVACMD="$JAVA_HOME/jre/sh/java" + else + JAVACMD="$JAVA_HOME/bin/java" + fi + else + JAVACMD="`which java`" + fi +fi + +if [ ! -x "$JAVACMD" ] ; then + echo "Error: JAVA_HOME is not defined correctly." >&2 + echo " We cannot execute $JAVACMD" >&2 + exit 1 +fi + +if [ -z "$JAVA_HOME" ] ; then + echo "Warning: JAVA_HOME environment variable is not set." +fi + +CLASSWORLDS_LAUNCHER=org.codehaus.plexus.classworlds.launcher.Launcher + +# traverses directory structure from process work directory to filesystem root +# first directory with .mvn subdirectory is considered project base directory +find_maven_basedir() { + + if [ -z "$1" ] + then + echo "Path not specified to find_maven_basedir" + return 1 + fi + + basedir="$1" + wdir="$1" + while [ "$wdir" != '/' ] ; do + if [ -d "$wdir"/.mvn ] ; then + basedir=$wdir + break + fi + # workaround for JBEAP-8937 (on Solaris 10/Sparc) + if [ -d "${wdir}" ]; then + wdir=`cd "$wdir/.."; pwd` + fi + # end of workaround + done + echo "${basedir}" +} + +# concatenates all lines of a file +concat_lines() { + if [ -f "$1" ]; then + echo "$(tr -s '\n' ' ' < "$1")" + fi +} + +BASE_DIR=`find_maven_basedir "$(pwd)"` +if [ -z "$BASE_DIR" ]; then + exit 1; +fi + +########################################################################################## +# Extension to allow automatically downloading the maven-wrapper.jar from Maven-central +# This allows using the maven wrapper in projects that prohibit checking in binary data. +########################################################################################## +if [ -r "$BASE_DIR/.mvn/wrapper/maven-wrapper.jar" ]; then + if [ "$MVNW_VERBOSE" = true ]; then + echo "Found .mvn/wrapper/maven-wrapper.jar" + fi +else + if [ "$MVNW_VERBOSE" = true ]; then + echo "Couldn't find .mvn/wrapper/maven-wrapper.jar, downloading it ..." + fi + jarUrl="https://repo.maven.apache.org/maven2/io/takari/maven-wrapper/0.4.2/maven-wrapper-0.4.2.jar" + while IFS="=" read key value; do + case "$key" in (wrapperUrl) jarUrl="$value"; break ;; + esac + done < "$BASE_DIR/.mvn/wrapper/maven-wrapper.properties" + if [ "$MVNW_VERBOSE" = true ]; then + echo "Downloading from: $jarUrl" + fi + wrapperJarPath="$BASE_DIR/.mvn/wrapper/maven-wrapper.jar" + + if command -v wget > /dev/null; then + if [ "$MVNW_VERBOSE" = true ]; then + echo "Found wget ... using wget" + fi + wget "$jarUrl" -O "$wrapperJarPath" + elif command -v curl > /dev/null; then + if [ "$MVNW_VERBOSE" = true ]; then + echo "Found curl ... using curl" + fi + curl -o "$wrapperJarPath" "$jarUrl" + else + if [ "$MVNW_VERBOSE" = true ]; then + echo "Falling back to using Java to download" + fi + javaClass="$BASE_DIR/.mvn/wrapper/MavenWrapperDownloader.java" + if [ -e "$javaClass" ]; then + if [ ! -e "$BASE_DIR/.mvn/wrapper/MavenWrapperDownloader.class" ]; then + if [ "$MVNW_VERBOSE" = true ]; then + echo " - Compiling MavenWrapperDownloader.java ..." + fi + # Compiling the Java class + ("$JAVA_HOME/bin/javac" "$javaClass") + fi + if [ -e "$BASE_DIR/.mvn/wrapper/MavenWrapperDownloader.class" ]; then + # Running the downloader + if [ "$MVNW_VERBOSE" = true ]; then + echo " - Running MavenWrapperDownloader.java ..." + fi + ("$JAVA_HOME/bin/java" -cp .mvn/wrapper MavenWrapperDownloader "$MAVEN_PROJECTBASEDIR") + fi + fi + fi +fi +########################################################################################## +# End of extension +########################################################################################## + +export MAVEN_PROJECTBASEDIR=${MAVEN_BASEDIR:-"$BASE_DIR"} +if [ "$MVNW_VERBOSE" = true ]; then + echo $MAVEN_PROJECTBASEDIR +fi +MAVEN_OPTS="$(concat_lines "$MAVEN_PROJECTBASEDIR/.mvn/jvm.config") $MAVEN_OPTS" + +# For Cygwin, switch paths to Windows format before running java +if $cygwin; then + [ -n "$M2_HOME" ] && + M2_HOME=`cygpath --path --windows "$M2_HOME"` + [ -n "$JAVA_HOME" ] && + JAVA_HOME=`cygpath --path --windows "$JAVA_HOME"` + [ -n "$CLASSPATH" ] && + CLASSPATH=`cygpath --path --windows "$CLASSPATH"` + [ -n "$MAVEN_PROJECTBASEDIR" ] && + MAVEN_PROJECTBASEDIR=`cygpath --path --windows "$MAVEN_PROJECTBASEDIR"` +fi + +WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain + +exec "$JAVACMD" \ + $MAVEN_OPTS \ + -classpath "$MAVEN_PROJECTBASEDIR/.mvn/wrapper/maven-wrapper.jar" \ + "-Dmaven.home=${M2_HOME}" "-Dmaven.multiModuleProjectDirectory=${MAVEN_PROJECTBASEDIR}" \ + ${WRAPPER_LAUNCHER} $MAVEN_CONFIG "$@" diff --git a/chapter-04/java/mvnw.cmd b/chapter-04/java/mvnw.cmd new file mode 100755 index 0000000..48363fa --- /dev/null +++ b/chapter-04/java/mvnw.cmd @@ -0,0 +1,161 @@ +@REM ---------------------------------------------------------------------------- +@REM Licensed to the Apache Software Foundation (ASF) under one +@REM or more contributor license agreements. See the NOTICE file +@REM distributed with this work for additional information +@REM regarding copyright ownership. The ASF licenses this file +@REM to you under the Apache License, Version 2.0 (the +@REM "License"); you may not use this file except in compliance +@REM with the License. You may obtain a copy of the License at +@REM +@REM http://www.apache.org/licenses/LICENSE-2.0 +@REM +@REM Unless required by applicable law or agreed to in writing, +@REM software distributed under the License is distributed on an +@REM "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +@REM KIND, either express or implied. See the License for the +@REM specific language governing permissions and limitations +@REM under the License. +@REM ---------------------------------------------------------------------------- + +@REM ---------------------------------------------------------------------------- +@REM Maven2 Start Up Batch script +@REM +@REM Required ENV vars: +@REM JAVA_HOME - location of a JDK home dir +@REM +@REM Optional ENV vars +@REM M2_HOME - location of maven2's installed home dir +@REM MAVEN_BATCH_ECHO - set to 'on' to enable the echoing of the batch commands +@REM MAVEN_BATCH_PAUSE - set to 'on' to wait for a key stroke before ending +@REM MAVEN_OPTS - parameters passed to the Java VM when running Maven +@REM e.g. to debug Maven itself, use +@REM set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000 +@REM MAVEN_SKIP_RC - flag to disable loading of mavenrc files +@REM ---------------------------------------------------------------------------- + +@REM Begin all REM lines with '@' in case MAVEN_BATCH_ECHO is 'on' +@echo off +@REM set title of command window +title %0 +@REM enable echoing my setting MAVEN_BATCH_ECHO to 'on' +@if "%MAVEN_BATCH_ECHO%" == "on" echo %MAVEN_BATCH_ECHO% + +@REM set %HOME% to equivalent of $HOME +if "%HOME%" == "" (set "HOME=%HOMEDRIVE%%HOMEPATH%") + +@REM Execute a user defined script before this one +if not "%MAVEN_SKIP_RC%" == "" goto skipRcPre +@REM check for pre script, once with legacy .bat ending and once with .cmd ending +if exist "%HOME%\mavenrc_pre.bat" call "%HOME%\mavenrc_pre.bat" +if exist "%HOME%\mavenrc_pre.cmd" call "%HOME%\mavenrc_pre.cmd" +:skipRcPre + +@setlocal + +set ERROR_CODE=0 + +@REM To isolate internal variables from possible post scripts, we use another setlocal +@setlocal + +@REM ==== START VALIDATION ==== +if not "%JAVA_HOME%" == "" goto OkJHome + +echo. +echo Error: JAVA_HOME not found in your environment. >&2 +echo Please set the JAVA_HOME variable in your environment to match the >&2 +echo location of your Java installation. >&2 +echo. +goto error + +:OkJHome +if exist "%JAVA_HOME%\bin\java.exe" goto init + +echo. +echo Error: JAVA_HOME is set to an invalid directory. >&2 +echo JAVA_HOME = "%JAVA_HOME%" >&2 +echo Please set the JAVA_HOME variable in your environment to match the >&2 +echo location of your Java installation. >&2 +echo. +goto error + +@REM ==== END VALIDATION ==== + +:init + +@REM Find the project base dir, i.e. the directory that contains the folder ".mvn". +@REM Fallback to current working directory if not found. + +set MAVEN_PROJECTBASEDIR=%MAVEN_BASEDIR% +IF NOT "%MAVEN_PROJECTBASEDIR%"=="" goto endDetectBaseDir + +set EXEC_DIR=%CD% +set WDIR=%EXEC_DIR% +:findBaseDir +IF EXIST "%WDIR%"\.mvn goto baseDirFound +cd .. +IF "%WDIR%"=="%CD%" goto baseDirNotFound +set WDIR=%CD% +goto findBaseDir + +:baseDirFound +set MAVEN_PROJECTBASEDIR=%WDIR% +cd "%EXEC_DIR%" +goto endDetectBaseDir + +:baseDirNotFound +set MAVEN_PROJECTBASEDIR=%EXEC_DIR% +cd "%EXEC_DIR%" + +:endDetectBaseDir + +IF NOT EXIST "%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config" goto endReadAdditionalConfig + +@setlocal EnableExtensions EnableDelayedExpansion +for /F "usebackq delims=" %%a in ("%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config") do set JVM_CONFIG_MAVEN_PROPS=!JVM_CONFIG_MAVEN_PROPS! %%a +@endlocal & set JVM_CONFIG_MAVEN_PROPS=%JVM_CONFIG_MAVEN_PROPS% + +:endReadAdditionalConfig + +SET MAVEN_JAVA_EXE="%JAVA_HOME%\bin\java.exe" +set WRAPPER_JAR="%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.jar" +set WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain + +set DOWNLOAD_URL="https://repo.maven.apache.org/maven2/io/takari/maven-wrapper/0.4.2/maven-wrapper-0.4.2.jar" +FOR /F "tokens=1,2 delims==" %%A IN (%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.properties) DO ( + IF "%%A"=="wrapperUrl" SET DOWNLOAD_URL=%%B +) + +@REM Extension to allow automatically downloading the maven-wrapper.jar from Maven-central +@REM This allows using the maven wrapper in projects that prohibit checking in binary data. +if exist %WRAPPER_JAR% ( + echo Found %WRAPPER_JAR% +) else ( + echo Couldn't find %WRAPPER_JAR%, downloading it ... + echo Downloading from: %DOWNLOAD_URL% + powershell -Command "(New-Object Net.WebClient).DownloadFile('%DOWNLOAD_URL%', '%WRAPPER_JAR%')" + echo Finished downloading %WRAPPER_JAR% +) +@REM End of extension + +%MAVEN_JAVA_EXE% %JVM_CONFIG_MAVEN_PROPS% %MAVEN_OPTS% %MAVEN_DEBUG_OPTS% -classpath %WRAPPER_JAR% "-Dmaven.multiModuleProjectDirectory=%MAVEN_PROJECTBASEDIR%" %WRAPPER_LAUNCHER% %MAVEN_CONFIG% %* +if ERRORLEVEL 1 goto error +goto end + +:error +set ERROR_CODE=1 + +:end +@endlocal & set ERROR_CODE=%ERROR_CODE% + +if not "%MAVEN_SKIP_RC%" == "" goto skipRcPost +@REM check for post script, once with legacy .bat ending and once with .cmd ending +if exist "%HOME%\mavenrc_post.bat" call "%HOME%\mavenrc_post.bat" +if exist "%HOME%\mavenrc_post.cmd" call "%HOME%\mavenrc_post.cmd" +:skipRcPost + +@REM pause the script if MAVEN_BATCH_PAUSE is set to 'on' +if "%MAVEN_BATCH_PAUSE%" == "on" pause + +if "%MAVEN_TERMINATE_CMD%" == "on" exit %ERROR_CODE% + +exit /B %ERROR_CODE% diff --git a/chapter-04/java/pom.xml b/chapter-04/java/pom.xml new file mode 100644 index 0000000..07e2a70 --- /dev/null +++ b/chapter-04/java/pom.xml @@ -0,0 +1,78 @@ + + + 4.0.0 + + com.packt + distributed-tracing + 0.0.1-SNAPSHOT + jar + + OpenTracing Instrumentation + Chapter 4 - OpenTracing Instrumentation + + + org.springframework.boot + spring-boot-starter-parent + 2.0.4.RELEASE + + + + + + UTF-8 + UTF-8 + 1.8 + exercise1.HelloApp + + + + + org.springframework.boot + spring-boot-starter-web + + + org.springframework.boot + spring-boot-starter-data-jpa + + + mysql + mysql-connector-java + + + io.opentracing + opentracing-api + 0.31.0 + + + + io.jaegertracing + jaeger-client + 0.31.0 + + + + org.springframework.boot + spring-boot-starter-test + test + + + + + + + org.springframework.boot + spring-boot-maven-plugin + + ${main.class} + + + + + diff --git a/chapter-04/java/src/main/java/exercise1/HelloApp.java b/chapter-04/java/src/main/java/exercise1/HelloApp.java new file mode 100644 index 0000000..8bd21f5 --- /dev/null +++ b/chapter-04/java/src/main/java/exercise1/HelloApp.java @@ -0,0 +1,16 @@ +package exercise1; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.boot.autoconfigure.domain.EntityScan; +import org.springframework.data.jpa.repository.config.EnableJpaRepositories; + +@EnableJpaRepositories("lib.people") +@EntityScan("lib.people") +@SpringBootApplication +public class HelloApp { + + public static void main(String[] args) { + SpringApplication.run(HelloApp.class, args); + } +} diff --git a/chapter-04/java/src/main/java/exercise1/HelloController.java b/chapter-04/java/src/main/java/exercise1/HelloController.java new file mode 100644 index 0000000..85fa934 --- /dev/null +++ b/chapter-04/java/src/main/java/exercise1/HelloController.java @@ -0,0 +1,45 @@ +package exercise1; + +import java.util.Optional; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.RestController; + +import lib.people.Person; +import lib.people.PersonRepository; + +@RestController +public class HelloController { + + @Autowired + private PersonRepository personRepository; + + @GetMapping("/sayHello/{name}") + public String sayHello(@PathVariable String name) { + Person person = getPerson(name); + String response = formatGreeting(person); + return response; + } + + private Person getPerson(String name) { + Optional personOpt = personRepository.findById(name); + if (personOpt.isPresent()) { + return personOpt.get(); + } + return new Person(name); + } + + private String formatGreeting(Person person) { + String response = "Hello, "; + if (!person.getTitle().isEmpty()) { + response += person.getTitle() + " "; + } + response += person.getName() + "!"; + if (!person.getDescription().isEmpty()) { + response += " " + person.getDescription(); + } + return response; + } +} diff --git a/chapter-04/java/src/main/java/exercise2/HelloApp.java b/chapter-04/java/src/main/java/exercise2/HelloApp.java new file mode 100644 index 0000000..6beafab --- /dev/null +++ b/chapter-04/java/src/main/java/exercise2/HelloApp.java @@ -0,0 +1,28 @@ +package exercise2; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.boot.autoconfigure.domain.EntityScan; +import org.springframework.context.annotation.Bean; +import org.springframework.data.jpa.repository.config.EnableJpaRepositories; + +import io.jaegertracing.Configuration; +import io.jaegertracing.Configuration.ReporterConfiguration; +import io.jaegertracing.Configuration.SamplerConfiguration; + +@EnableJpaRepositories("lib.people") +@EntityScan("lib.people") +@SpringBootApplication +public class HelloApp { + + @Bean + public io.opentracing.Tracer initTracer() { + SamplerConfiguration samplerConfig = new SamplerConfiguration().withType("const").withParam(1); + ReporterConfiguration reporterConfig = new ReporterConfiguration().withLogSpans(true); + return new Configuration("java-2-hello").withSampler(samplerConfig).withReporter(reporterConfig).getTracer(); + } + + public static void main(String[] args) { + SpringApplication.run(HelloApp.class, args); + } +} diff --git a/chapter-04/java/src/main/java/exercise2/HelloController.java b/chapter-04/java/src/main/java/exercise2/HelloController.java new file mode 100644 index 0000000..88bd984 --- /dev/null +++ b/chapter-04/java/src/main/java/exercise2/HelloController.java @@ -0,0 +1,65 @@ +package exercise2; + +import java.util.LinkedHashMap; +import java.util.Map; +import java.util.Optional; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.RestController; + +import io.opentracing.Span; +import io.opentracing.Tracer; +import lib.people.Person; +import lib.people.PersonRepository; + +@RestController +public class HelloController { + + @Autowired + private PersonRepository personRepository; + + @Autowired + private Tracer tracer; + + @GetMapping("/sayHello/{name}") + public String sayHello(@PathVariable String name) { + Span span = tracer.buildSpan("say-hello").start(); + try { + Person person = getPerson(name); + Map fields = new LinkedHashMap<>(); + fields.put("name", person.getName()); + fields.put("title", person.getTitle()); + fields.put("description", person.getDescription()); + span.log(fields); + + String response = formatGreeting(person); + span.setTag("response", response); + + return response; + } finally { + span.finish(); + } + } + + private Person getPerson(String name) { + Optional personOpt = personRepository.findById(name); + if (personOpt.isPresent()) { + return personOpt.get(); + } + return new Person(name); + } + + private String formatGreeting(Person person) { + String response = "Hello, "; + if (!person.getTitle().isEmpty()) { + response += person.getTitle() + " "; + } + response += person.getName() + "!"; + if (!person.getDescription().isEmpty()) { + response += " " + person.getDescription(); + } + return response; + } +} diff --git a/chapter-04/java/src/main/java/exercise3a/HelloApp.java b/chapter-04/java/src/main/java/exercise3a/HelloApp.java new file mode 100644 index 0000000..62b42fe --- /dev/null +++ b/chapter-04/java/src/main/java/exercise3a/HelloApp.java @@ -0,0 +1,28 @@ +package exercise3a; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.boot.autoconfigure.domain.EntityScan; +import org.springframework.context.annotation.Bean; +import org.springframework.data.jpa.repository.config.EnableJpaRepositories; + +import io.jaegertracing.Configuration; +import io.jaegertracing.Configuration.ReporterConfiguration; +import io.jaegertracing.Configuration.SamplerConfiguration; + +@EnableJpaRepositories("lib.people") +@EntityScan("lib.people") +@SpringBootApplication +public class HelloApp { + + @Bean + public io.opentracing.Tracer initTracer() { + SamplerConfiguration samplerConfig = new SamplerConfiguration().withType("const").withParam(1); + ReporterConfiguration reporterConfig = new ReporterConfiguration().withLogSpans(true); + return new Configuration("java-3-hello").withSampler(samplerConfig).withReporter(reporterConfig).getTracer(); + } + + public static void main(String[] args) { + SpringApplication.run(HelloApp.class, args); + } +} diff --git a/chapter-04/java/src/main/java/exercise3a/HelloController.java b/chapter-04/java/src/main/java/exercise3a/HelloController.java new file mode 100644 index 0000000..4b8498b --- /dev/null +++ b/chapter-04/java/src/main/java/exercise3a/HelloController.java @@ -0,0 +1,76 @@ +package exercise3a; + +import java.util.LinkedHashMap; +import java.util.Map; +import java.util.Optional; + +import io.opentracing.Tracer; +import io.opentracing.Span; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.RestController; + +import lib.people.Person; +import lib.people.PersonRepository; + +@RestController +public class HelloController { + + @Autowired + private PersonRepository personRepository; + + @Autowired + private Tracer tracer; + + @GetMapping("/sayHello/{name}") + public String sayHello(@PathVariable String name) { + Span span = tracer.buildSpan("say-hello").start(); + try { + Person person = getPerson(name, span); + Map fields = new LinkedHashMap<>(); + fields.put("name", person.getName()); + fields.put("title", person.getTitle()); + fields.put("description", person.getDescription()); + span.log(fields); + + String response = formatGreeting(person, span); + span.setTag("response", response); + + return response; + } finally { + span.finish(); + } + } + + private Person getPerson(String name, Span parent) { + Span span = tracer.buildSpan("get-person").asChildOf(parent).start(); + try { + Optional personOpt = personRepository.findById(name); + if (personOpt.isPresent()) { + return personOpt.get(); + } + return new Person(name); + } finally { + span.finish(); + } + } + + private String formatGreeting(Person person, Span parent) { + Span span = tracer.buildSpan("format-greeting").asChildOf(parent).start(); + try { + String response = "Hello, "; + if (!person.getTitle().isEmpty()) { + response += person.getTitle() + " "; + } + response += person.getName() + "!"; + if (!person.getDescription().isEmpty()) { + response += " " + person.getDescription(); + } + return response; + } finally { + span.finish(); + } + } +} diff --git a/chapter-04/java/src/main/java/exercise3b/HelloApp.java b/chapter-04/java/src/main/java/exercise3b/HelloApp.java new file mode 100644 index 0000000..0e6183a --- /dev/null +++ b/chapter-04/java/src/main/java/exercise3b/HelloApp.java @@ -0,0 +1,28 @@ +package exercise3b; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.boot.autoconfigure.domain.EntityScan; +import org.springframework.context.annotation.Bean; +import org.springframework.data.jpa.repository.config.EnableJpaRepositories; + +import io.jaegertracing.Configuration; +import io.jaegertracing.Configuration.ReporterConfiguration; +import io.jaegertracing.Configuration.SamplerConfiguration; + +@EnableJpaRepositories("lib.people") +@EntityScan("lib.people") +@SpringBootApplication +public class HelloApp { + + @Bean + public io.opentracing.Tracer initTracer() { + SamplerConfiguration samplerConfig = new SamplerConfiguration().withType("const").withParam(1); + ReporterConfiguration reporterConfig = new ReporterConfiguration().withLogSpans(true); + return new Configuration("java-3-hello").withSampler(samplerConfig).withReporter(reporterConfig).getTracer(); + } + + public static void main(String[] args) { + SpringApplication.run(HelloApp.class, args); + } +} diff --git a/chapter-04/java/src/main/java/exercise3b/HelloController.java b/chapter-04/java/src/main/java/exercise3b/HelloController.java new file mode 100644 index 0000000..4d1db8e --- /dev/null +++ b/chapter-04/java/src/main/java/exercise3b/HelloController.java @@ -0,0 +1,77 @@ +package exercise3b; + +import java.util.LinkedHashMap; +import java.util.Map; +import java.util.Optional; + +import io.opentracing.Tracer; +import io.opentracing.Scope; +import io.opentracing.Span; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.RestController; + +import lib.people.Person; +import lib.people.PersonRepository; + +@RestController +public class HelloController { + + @Autowired + private PersonRepository personRepository; + + @Autowired + private Tracer tracer; + + @GetMapping("/sayHello/{name}") + public String sayHello(@PathVariable String name) { + Span span = tracer.buildSpan("say-hello").start(); + try (Scope scope = tracer.scopeManager().activate(span, false)) { + Person person = getPerson(name); + Map fields = new LinkedHashMap<>(); + fields.put("name", person.getName()); + fields.put("title", person.getTitle()); + fields.put("description", person.getDescription()); + span.log(fields); + + String response = formatGreeting(person); + span.setTag("response", response); + + return response; + } finally { + span.finish(); + } + } + + private Person getPerson(String name) { + Span span = tracer.buildSpan("get-person").start(); + try (Scope scope = tracer.scopeManager().activate(span, false)) { + Optional personOpt = personRepository.findById(name); + if (personOpt.isPresent()) { + return personOpt.get(); + } + return new Person(name); + } finally { + span.finish(); + } + } + + private String formatGreeting(Person person) { + Span span = tracer.buildSpan("format-greeting").start(); + try (Scope scope = tracer.scopeManager().activate(span, false)) { + String response = "Hello, "; + if (!person.getTitle().isEmpty()) { + response += person.getTitle() + " "; + } + response += person.getName() + "!"; + if (!person.getDescription().isEmpty()) { + response += " " + person.getDescription(); + } + return response; + } finally { + span.finish(); + } + } +} diff --git a/chapter-04/java/src/main/java/exercise4a/HelloApp.java b/chapter-04/java/src/main/java/exercise4a/HelloApp.java new file mode 100644 index 0000000..9c01d46 --- /dev/null +++ b/chapter-04/java/src/main/java/exercise4a/HelloApp.java @@ -0,0 +1,31 @@ +package exercise4a; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.boot.web.client.RestTemplateBuilder; +import org.springframework.context.annotation.Bean; +import org.springframework.web.client.RestTemplate; + +import io.jaegertracing.Configuration; +import io.jaegertracing.Configuration.ReporterConfiguration; +import io.jaegertracing.Configuration.SamplerConfiguration; + +@SpringBootApplication +public class HelloApp { + + @Bean + public RestTemplate restTemplate(RestTemplateBuilder restTemplateBuilder) { + return restTemplateBuilder.build(); + } + + @Bean + public io.opentracing.Tracer initTracer() { + SamplerConfiguration samplerConfig = new SamplerConfiguration().withType("const").withParam(1); + ReporterConfiguration reporterConfig = new ReporterConfiguration().withLogSpans(true); + return new Configuration("java-4-hello").withSampler(samplerConfig).withReporter(reporterConfig).getTracer(); + } + + public static void main(String[] args) { + SpringApplication.run(HelloApp.class, args); + } +} diff --git a/chapter-04/java/src/main/java/exercise4a/HelloController.java b/chapter-04/java/src/main/java/exercise4a/HelloController.java new file mode 100644 index 0000000..b4304aa --- /dev/null +++ b/chapter-04/java/src/main/java/exercise4a/HelloController.java @@ -0,0 +1,76 @@ +package exercise4a; + +import java.net.URI; +import java.util.Collections; +import java.util.LinkedHashMap; +import java.util.Map; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.RestController; +import org.springframework.web.client.RestTemplate; +import org.springframework.web.util.UriComponentsBuilder; + +import io.opentracing.Scope; +import io.opentracing.Span; +import io.opentracing.Tracer; +import lib.people.Person; + +@RestController +public class HelloController { + + @Autowired + private RestTemplate restTemplate; + + @Autowired + private Tracer tracer; + + @GetMapping("/sayHello/{name}") + public String sayHello(@PathVariable String name) { + Span span = tracer.buildSpan("say-hello").start(); + try (Scope scope = tracer.scopeManager().activate(span, false)) { + Person person = getPerson(name); + Map fields = new LinkedHashMap<>(); + fields.put("name", person.getName()); + fields.put("title", person.getTitle()); + fields.put("description", person.getDescription()); + span.log(fields); + + String response = formatGreeting(person); + span.setTag("response", response); + + return response; + } finally { + span.finish(); + } + } + + private Person getPerson(String name) { + Span span = tracer.buildSpan("get-person").start(); + try (Scope scope = tracer.scopeManager().activate(span, false)) { + String url = "http://localhost:8081/getPerson/" + name; + ResponseEntity response = restTemplate.getForEntity(url, Person.class); + return response.getBody(); + } finally { + span.finish(); + } + } + + private String formatGreeting(Person person) { + Span span = tracer.buildSpan("format-greeting").start(); + try (Scope scope = tracer.scopeManager().activate(span, false)) { + URI uri = UriComponentsBuilder // + .fromHttpUrl("http://localhost:8082/formatGreeting") // + .queryParam("name", person.getName()) // + .queryParam("title", person.getTitle()) // + .queryParam("description", person.getDescription()) // + .build(Collections.emptyMap()); + ResponseEntity response = restTemplate.getForEntity(uri, String.class); + return response.getBody(); + } finally { + span.finish(); + } + } +} diff --git a/chapter-04/java/src/main/java/exercise4a/bigbrother/BBApp.java b/chapter-04/java/src/main/java/exercise4a/bigbrother/BBApp.java new file mode 100644 index 0000000..0b8003c --- /dev/null +++ b/chapter-04/java/src/main/java/exercise4a/bigbrother/BBApp.java @@ -0,0 +1,29 @@ +package exercise4a.bigbrother; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.boot.autoconfigure.domain.EntityScan; +import org.springframework.context.annotation.Bean; +import org.springframework.data.jpa.repository.config.EnableJpaRepositories; + +import io.jaegertracing.Configuration; +import io.jaegertracing.Configuration.ReporterConfiguration; +import io.jaegertracing.Configuration.SamplerConfiguration; + +@EnableJpaRepositories("lib.people") +@EntityScan("lib.people") +@SpringBootApplication +public class BBApp { + + @Bean + public io.opentracing.Tracer initTracer() { + SamplerConfiguration samplerConfig = new SamplerConfiguration().withType("const").withParam(1); + ReporterConfiguration reporterConfig = new ReporterConfiguration().withLogSpans(true); + return new Configuration("java-4-bigbrother").withSampler(samplerConfig).withReporter(reporterConfig).getTracer(); + } + + public static void main(String[] args) { + System.setProperty("server.port", "8081"); + SpringApplication.run(BBApp.class, args); + } +} diff --git a/chapter-04/java/src/main/java/exercise4a/bigbrother/BBController.java b/chapter-04/java/src/main/java/exercise4a/bigbrother/BBController.java new file mode 100644 index 0000000..9668efc --- /dev/null +++ b/chapter-04/java/src/main/java/exercise4a/bigbrother/BBController.java @@ -0,0 +1,56 @@ +package exercise4a.bigbrother; + +import java.util.LinkedHashMap; +import java.util.Map; +import java.util.Optional; + +import io.opentracing.Tracer; +import io.opentracing.Scope; +import io.opentracing.Span; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.RestController; + +import lib.people.Person; +import lib.people.PersonRepository; + +@RestController +public class BBController { + + @Autowired + private PersonRepository personRepository; + + @Autowired + private Tracer tracer; + + @GetMapping("/getPerson/{name}") + public Person getPerson(@PathVariable String name) { + Span span = tracer.buildSpan("/getPerson").start(); + try (Scope scope = tracer.scopeManager().activate(span, false)) { + Person person = loadPerson(name); + Map fields = new LinkedHashMap<>(); + fields.put("name", person.getName()); + fields.put("title", person.getTitle()); + fields.put("description", person.getDescription()); + span.log(fields); + return person; + } finally { + span.finish(); + } + } + + private Person loadPerson(String name) { + Span span = tracer.buildSpan("get-person").start(); + try (Scope scope = tracer.scopeManager().activate(span, false)) { + Optional personOpt = personRepository.findById(name); + if (personOpt.isPresent()) { + return personOpt.get(); + } + return new Person(name); + } finally { + span.finish(); + } + } +} diff --git a/chapter-04/java/src/main/java/exercise4a/formatter/FApp.java b/chapter-04/java/src/main/java/exercise4a/formatter/FApp.java new file mode 100644 index 0000000..5af3726 --- /dev/null +++ b/chapter-04/java/src/main/java/exercise4a/formatter/FApp.java @@ -0,0 +1,25 @@ +package exercise4a.formatter; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.context.annotation.Bean; + +import io.jaegertracing.Configuration; +import io.jaegertracing.Configuration.ReporterConfiguration; +import io.jaegertracing.Configuration.SamplerConfiguration; + +@SpringBootApplication +public class FApp { + + @Bean + public io.opentracing.Tracer initTracer() { + SamplerConfiguration samplerConfig = new SamplerConfiguration().withType("const").withParam(1); + ReporterConfiguration reporterConfig = new ReporterConfiguration().withLogSpans(true); + return new Configuration("java-4-formatter").withSampler(samplerConfig).withReporter(reporterConfig).getTracer(); + } + + public static void main(String[] args) { + System.setProperty("server.port", "8082"); + SpringApplication.run(FApp.class, args); + } +} diff --git a/chapter-04/java/src/main/java/exercise4a/formatter/FController.java b/chapter-04/java/src/main/java/exercise4a/formatter/FController.java new file mode 100644 index 0000000..6fe1144 --- /dev/null +++ b/chapter-04/java/src/main/java/exercise4a/formatter/FController.java @@ -0,0 +1,36 @@ +package exercise4a.formatter; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.bind.annotation.RestController; + +import io.opentracing.Scope; +import io.opentracing.Span; +import io.opentracing.Tracer; + +@RestController +public class FController { + + @Autowired + private Tracer tracer; + + @GetMapping("/formatGreeting") + public String formatGreeting(@RequestParam String name, @RequestParam String title, + @RequestParam String description) { + Span span = tracer.buildSpan("/formatGreeting").start(); + try (Scope scope = tracer.scopeManager().activate(span, false)) { + String response = "Hello, "; + if (!title.isEmpty()) { + response += title + " "; + } + response += name + "!"; + if (!description.isEmpty()) { + response += " " + description; + } + return response; + } finally { + span.finish(); + } + } +} diff --git a/chapter-04/java/src/main/java/exercise4b/HelloApp.java b/chapter-04/java/src/main/java/exercise4b/HelloApp.java new file mode 100644 index 0000000..f4db12f --- /dev/null +++ b/chapter-04/java/src/main/java/exercise4b/HelloApp.java @@ -0,0 +1,31 @@ +package exercise4b; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.boot.web.client.RestTemplateBuilder; +import org.springframework.context.annotation.Bean; +import org.springframework.web.client.RestTemplate; + +import io.jaegertracing.Configuration; +import io.jaegertracing.Configuration.ReporterConfiguration; +import io.jaegertracing.Configuration.SamplerConfiguration; + +@SpringBootApplication +public class HelloApp { + + @Bean + public RestTemplate restTemplate(RestTemplateBuilder restTemplateBuilder) { + return restTemplateBuilder.build(); + } + + @Bean + public io.opentracing.Tracer initTracer() { + SamplerConfiguration samplerConfig = new SamplerConfiguration().withType("const").withParam(1); + ReporterConfiguration reporterConfig = new ReporterConfiguration().withLogSpans(true); + return new Configuration("java-4-hello").withSampler(samplerConfig).withReporter(reporterConfig).getTracer(); + } + + public static void main(String[] args) { + SpringApplication.run(HelloApp.class, args); + } +} diff --git a/chapter-04/java/src/main/java/exercise4b/HelloController.java b/chapter-04/java/src/main/java/exercise4b/HelloController.java new file mode 100644 index 0000000..a61a19a --- /dev/null +++ b/chapter-04/java/src/main/java/exercise4b/HelloController.java @@ -0,0 +1,62 @@ +package exercise4b; + +import java.net.URI; +import java.util.Collections; +import java.util.LinkedHashMap; +import java.util.Map; + +import javax.servlet.http.HttpServletRequest; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.RestController; +import org.springframework.web.client.RestTemplate; +import org.springframework.web.util.UriComponentsBuilder; + +import io.opentracing.Scope; +import io.opentracing.Span; +import lib.people.Person; + +@RestController +public class HelloController extends TracedController { + + @Autowired + private RestTemplate restTemplate; + + @GetMapping("/sayHello/{name}") + public String sayHello(@PathVariable String name, HttpServletRequest request) { + Span span = startServerSpan("/sayHello", request); + try (Scope scope = tracer.scopeManager().activate(span, false)) { + Person person = getPerson(name); + Map fields = new LinkedHashMap<>(); + fields.put("name", person.getName()); + fields.put("title", person.getTitle()); + fields.put("description", person.getDescription()); + span.log(fields); + + String response = formatGreeting(person); + span.setTag("response", response); + + return response; + } finally { + span.finish(); + } + } + + private Person getPerson(String name) { + String url = "http://localhost:8081/getPerson/" + name; + URI uri = UriComponentsBuilder.fromHttpUrl(url).build(Collections.emptyMap()); + return get("get-person", uri, Person.class, restTemplate); + } + + private String formatGreeting(Person person) { + URI uri = UriComponentsBuilder // + .fromHttpUrl("http://localhost:8082/formatGreeting") // + .queryParam("name", person.getName()) // + .queryParam("title", person.getTitle()) // + .queryParam("description", person.getDescription()) // + .build(Collections.emptyMap()); + return get("format-greeting", uri, String.class, restTemplate); + } +} diff --git a/chapter-04/java/src/main/java/exercise4b/TracedController.java b/chapter-04/java/src/main/java/exercise4b/TracedController.java new file mode 100644 index 0000000..09b9881 --- /dev/null +++ b/chapter-04/java/src/main/java/exercise4b/TracedController.java @@ -0,0 +1,100 @@ +package exercise4b; + +import java.net.URI; +import java.util.Enumeration; +import java.util.Iterator; +import java.util.LinkedHashMap; +import java.util.Map; +import java.util.Map.Entry; + +import javax.servlet.http.HttpServletRequest; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.http.HttpEntity; +import org.springframework.http.HttpHeaders; +import org.springframework.http.HttpMethod; +import org.springframework.web.client.RestTemplate; + +import io.opentracing.Scope; +import io.opentracing.Span; +import io.opentracing.SpanContext; +import io.opentracing.Tracer; +import io.opentracing.propagation.Format; +import io.opentracing.propagation.TextMap; +import io.opentracing.tag.Tags; + +public class TracedController { + @Autowired + protected Tracer tracer; + + protected Span startServerSpan(String operationName, HttpServletRequest request) { + HttpServletRequestExtractAdapter carrier = new HttpServletRequestExtractAdapter(request); + SpanContext parent = tracer.extract(Format.Builtin.HTTP_HEADERS, carrier); + Span span = tracer.buildSpan(operationName).asChildOf(parent).start(); + Tags.SPAN_KIND.set(span, Tags.SPAN_KIND_SERVER); + return span; + } + + /** + * Execute HTTP GET request. + */ + protected T get(String operationName, URI uri, Class entityClass, RestTemplate restTemplate) { + Span span = tracer.buildSpan(operationName).start(); + try (Scope scope = tracer.scopeManager().activate(span, false)) { + Tags.SPAN_KIND.set(span, Tags.SPAN_KIND_CLIENT); + Tags.HTTP_URL.set(span, uri.toString()); + Tags.HTTP_METHOD.set(span, "GET"); + + HttpHeaders headers = new HttpHeaders(); + HttpHeaderInjectAdapter carrier = new HttpHeaderInjectAdapter(headers); + tracer.inject(span.context(), Format.Builtin.HTTP_HEADERS, carrier); + HttpEntity entity = new HttpEntity<>(headers); + return restTemplate.exchange(uri, HttpMethod.GET, entity, entityClass).getBody(); + } finally { + span.finish(); + } + } + + private static class HttpServletRequestExtractAdapter implements TextMap { + private final Map headers; + + HttpServletRequestExtractAdapter(HttpServletRequest request) { + this.headers = new LinkedHashMap<>(); + Enumeration keys = request.getHeaderNames(); + while (keys.hasMoreElements()) { + String key = keys.nextElement(); + String value = request.getHeader(key); + headers.put(key, value); + } + } + + @Override + public Iterator> iterator() { + return headers.entrySet().iterator(); + } + + @Override + public void put(String key, String value) { + throw new UnsupportedOperationException(); + } + } + + private static class HttpHeaderInjectAdapter implements TextMap { + private final HttpHeaders headers; + + HttpHeaderInjectAdapter(HttpHeaders headers) { + this.headers = headers; + } + + @Override + public Iterator> iterator() { + throw new UnsupportedOperationException(); + } + + @Override + public void put(String key, String value) { + headers.set(key, value); + } + } + +} diff --git a/chapter-04/java/src/main/java/exercise4b/bigbrother/BBApp.java b/chapter-04/java/src/main/java/exercise4b/bigbrother/BBApp.java new file mode 100644 index 0000000..3f7cd49 --- /dev/null +++ b/chapter-04/java/src/main/java/exercise4b/bigbrother/BBApp.java @@ -0,0 +1,29 @@ +package exercise4b.bigbrother; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.boot.autoconfigure.domain.EntityScan; +import org.springframework.context.annotation.Bean; +import org.springframework.data.jpa.repository.config.EnableJpaRepositories; + +import io.jaegertracing.Configuration; +import io.jaegertracing.Configuration.ReporterConfiguration; +import io.jaegertracing.Configuration.SamplerConfiguration; + +@EnableJpaRepositories("lib.people") +@EntityScan("lib.people") +@SpringBootApplication +public class BBApp { + + @Bean + public io.opentracing.Tracer initTracer() { + SamplerConfiguration samplerConfig = new SamplerConfiguration().withType("const").withParam(1); + ReporterConfiguration reporterConfig = new ReporterConfiguration().withLogSpans(true); + return new Configuration("java-4-bigbrother").withSampler(samplerConfig).withReporter(reporterConfig).getTracer(); + } + + public static void main(String[] args) { + System.setProperty("server.port", "8081"); + SpringApplication.run(BBApp.class, args); + } +} diff --git a/chapter-04/java/src/main/java/exercise4b/bigbrother/BBController.java b/chapter-04/java/src/main/java/exercise4b/bigbrother/BBController.java new file mode 100644 index 0000000..0b8b5ed --- /dev/null +++ b/chapter-04/java/src/main/java/exercise4b/bigbrother/BBController.java @@ -0,0 +1,54 @@ +package exercise4b.bigbrother; + +import java.util.LinkedHashMap; +import java.util.Map; +import java.util.Optional; + +import javax.servlet.http.HttpServletRequest; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.RestController; + +import exercise4b.TracedController; +import io.opentracing.Scope; +import io.opentracing.Span; +import lib.people.Person; +import lib.people.PersonRepository; + +@RestController +public class BBController extends TracedController { + + @Autowired + private PersonRepository personRepository; + + @GetMapping("/getPerson/{name}") + public Person getPerson(@PathVariable String name, HttpServletRequest request) { + Span span = startServerSpan("/getPerson", request); + try (Scope scope = tracer.scopeManager().activate(span, false)) { + Person person = loadPerson(name); + Map fields = new LinkedHashMap<>(); + fields.put("name", person.getName()); + fields.put("title", person.getTitle()); + fields.put("description", person.getDescription()); + span.log(fields); + return person; + } finally { + span.finish(); + } + } + + private Person loadPerson(String name) { + Span span = tracer.buildSpan("get-person").start(); + try (Scope scope = tracer.scopeManager().activate(span, false)) { + Optional personOpt = personRepository.findById(name); + if (personOpt.isPresent()) { + return personOpt.get(); + } + return new Person(name); + } finally { + span.finish(); + } + } +} diff --git a/chapter-04/java/src/main/java/exercise4b/formatter/FApp.java b/chapter-04/java/src/main/java/exercise4b/formatter/FApp.java new file mode 100644 index 0000000..8ffb30b --- /dev/null +++ b/chapter-04/java/src/main/java/exercise4b/formatter/FApp.java @@ -0,0 +1,25 @@ +package exercise4b.formatter; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.context.annotation.Bean; + +import io.jaegertracing.Configuration; +import io.jaegertracing.Configuration.ReporterConfiguration; +import io.jaegertracing.Configuration.SamplerConfiguration; + +@SpringBootApplication +public class FApp { + + @Bean + public io.opentracing.Tracer initTracer() { + SamplerConfiguration samplerConfig = new SamplerConfiguration().withType("const").withParam(1); + ReporterConfiguration reporterConfig = new ReporterConfiguration().withLogSpans(true); + return new Configuration("java-4-formatter").withSampler(samplerConfig).withReporter(reporterConfig).getTracer(); + } + + public static void main(String[] args) { + System.setProperty("server.port", "8082"); + SpringApplication.run(FApp.class, args); + } +} diff --git a/chapter-04/java/src/main/java/exercise4b/formatter/FController.java b/chapter-04/java/src/main/java/exercise4b/formatter/FController.java new file mode 100644 index 0000000..1c5e43b --- /dev/null +++ b/chapter-04/java/src/main/java/exercise4b/formatter/FController.java @@ -0,0 +1,34 @@ +package exercise4b.formatter; + +import javax.servlet.http.HttpServletRequest; + +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.bind.annotation.RestController; + +import exercise4b.TracedController; +import io.opentracing.Scope; +import io.opentracing.Span; + +@RestController +public class FController extends TracedController { + + @GetMapping("/formatGreeting") + public String formatGreeting(@RequestParam String name, @RequestParam String title, + @RequestParam String description, HttpServletRequest request) { + Span span = startServerSpan("/formatGreeting", request); + try (Scope scope = tracer.scopeManager().activate(span, false)) { + String response = "Hello, "; + if (!title.isEmpty()) { + response += title + " "; + } + response += name + "!"; + if (!description.isEmpty()) { + response += " " + description; + } + return response; + } finally { + span.finish(); + } + } +} diff --git a/chapter-04/java/src/main/java/exercise5/HelloApp.java b/chapter-04/java/src/main/java/exercise5/HelloApp.java new file mode 100644 index 0000000..ee668e4 --- /dev/null +++ b/chapter-04/java/src/main/java/exercise5/HelloApp.java @@ -0,0 +1,31 @@ +package exercise5; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.boot.web.client.RestTemplateBuilder; +import org.springframework.context.annotation.Bean; +import org.springframework.web.client.RestTemplate; + +import io.jaegertracing.Configuration; +import io.jaegertracing.Configuration.ReporterConfiguration; +import io.jaegertracing.Configuration.SamplerConfiguration; + +@SpringBootApplication +public class HelloApp { + + @Bean + public RestTemplate restTemplate(RestTemplateBuilder restTemplateBuilder) { + return restTemplateBuilder.build(); + } + + @Bean + public io.opentracing.Tracer initTracer() { + SamplerConfiguration samplerConfig = new SamplerConfiguration().withType("const").withParam(1); + ReporterConfiguration reporterConfig = new ReporterConfiguration().withLogSpans(true); + return new Configuration("java-5-hello").withSampler(samplerConfig).withReporter(reporterConfig).getTracer(); + } + + public static void main(String[] args) { + SpringApplication.run(HelloApp.class, args); + } +} diff --git a/chapter-04/java/src/main/java/exercise5/HelloController.java b/chapter-04/java/src/main/java/exercise5/HelloController.java new file mode 100644 index 0000000..1c2bf8d --- /dev/null +++ b/chapter-04/java/src/main/java/exercise5/HelloController.java @@ -0,0 +1,62 @@ +package exercise5; + +import java.net.URI; +import java.util.Collections; +import java.util.LinkedHashMap; +import java.util.Map; + +import javax.servlet.http.HttpServletRequest; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.RestController; +import org.springframework.web.client.RestTemplate; +import org.springframework.web.util.UriComponentsBuilder; + +import io.opentracing.Scope; +import io.opentracing.Span; +import lib.people.Person; + +@RestController +public class HelloController extends TracedController { + + @Autowired + private RestTemplate restTemplate; + + @GetMapping("/sayHello/{name}") + public String sayHello(@PathVariable String name, HttpServletRequest request) { + Span span = startServerSpan("/sayHello", request); + try (Scope scope = tracer.scopeManager().activate(span, false)) { + Person person = getPerson(name); + Map fields = new LinkedHashMap<>(); + fields.put("name", person.getName()); + fields.put("title", person.getTitle()); + fields.put("description", person.getDescription()); + span.log(fields); + + String response = formatGreeting(person); + span.setTag("response", response); + + return response; + } finally { + span.finish(); + } + } + + private Person getPerson(String name) { + String url = "http://localhost:8081/getPerson/" + name; + URI uri = UriComponentsBuilder.fromHttpUrl(url).build(Collections.emptyMap()); + return get("get-person", uri, Person.class, restTemplate); + } + + private String formatGreeting(Person person) { + URI uri = UriComponentsBuilder // + .fromHttpUrl("http://localhost:8082/formatGreeting") // + .queryParam("name", person.getName()) // + .queryParam("title", person.getTitle()) // + .queryParam("description", person.getDescription()) // + .build(Collections.emptyMap()); + return get("format-greeting", uri, String.class, restTemplate); + } +} diff --git a/chapter-04/java/src/main/java/exercise5/TracedController.java b/chapter-04/java/src/main/java/exercise5/TracedController.java new file mode 100644 index 0000000..0759666 --- /dev/null +++ b/chapter-04/java/src/main/java/exercise5/TracedController.java @@ -0,0 +1,100 @@ +package exercise5; + +import java.net.URI; +import java.util.Enumeration; +import java.util.Iterator; +import java.util.LinkedHashMap; +import java.util.Map; +import java.util.Map.Entry; + +import javax.servlet.http.HttpServletRequest; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.http.HttpEntity; +import org.springframework.http.HttpHeaders; +import org.springframework.http.HttpMethod; +import org.springframework.web.client.RestTemplate; + +import io.opentracing.Scope; +import io.opentracing.Span; +import io.opentracing.SpanContext; +import io.opentracing.Tracer; +import io.opentracing.propagation.Format; +import io.opentracing.propagation.TextMap; +import io.opentracing.tag.Tags; + +public class TracedController { + @Autowired + protected Tracer tracer; + + protected Span startServerSpan(String operationName, HttpServletRequest request) { + HttpServletRequestExtractAdapter carrier = new HttpServletRequestExtractAdapter(request); + SpanContext parent = tracer.extract(Format.Builtin.HTTP_HEADERS, carrier); + Span span = tracer.buildSpan(operationName).asChildOf(parent).start(); + Tags.SPAN_KIND.set(span, Tags.SPAN_KIND_SERVER); + return span; + } + + /** + * Execute HTTP GET request. + */ + protected T get(String operationName, URI uri, Class entityClass, RestTemplate restTemplate) { + Span span = tracer.buildSpan(operationName).start(); + try (Scope scope = tracer.scopeManager().activate(span, false)) { + Tags.SPAN_KIND.set(span, Tags.SPAN_KIND_CLIENT); + Tags.HTTP_URL.set(span, uri.toString()); + Tags.HTTP_METHOD.set(span, "GET"); + + HttpHeaders headers = new HttpHeaders(); + HttpHeaderInjectAdapter carrier = new HttpHeaderInjectAdapter(headers); + tracer.inject(span.context(), Format.Builtin.HTTP_HEADERS, carrier); + HttpEntity entity = new HttpEntity<>(headers); + return restTemplate.exchange(uri, HttpMethod.GET, entity, entityClass).getBody(); + } finally { + span.finish(); + } + } + + private static class HttpServletRequestExtractAdapter implements TextMap { + private final Map headers; + + HttpServletRequestExtractAdapter(HttpServletRequest request) { + this.headers = new LinkedHashMap<>(); + Enumeration keys = request.getHeaderNames(); + while (keys.hasMoreElements()) { + String key = keys.nextElement(); + String value = request.getHeader(key); + headers.put(key, value); + } + } + + @Override + public Iterator> iterator() { + return headers.entrySet().iterator(); + } + + @Override + public void put(String key, String value) { + throw new UnsupportedOperationException(); + } + } + + private static class HttpHeaderInjectAdapter implements TextMap { + private final HttpHeaders headers; + + HttpHeaderInjectAdapter(HttpHeaders headers) { + this.headers = headers; + } + + @Override + public Iterator> iterator() { + throw new UnsupportedOperationException(); + } + + @Override + public void put(String key, String value) { + headers.set(key, value); + } + } + +} diff --git a/chapter-04/java/src/main/java/exercise5/bigbrother/BBApp.java b/chapter-04/java/src/main/java/exercise5/bigbrother/BBApp.java new file mode 100644 index 0000000..187d606 --- /dev/null +++ b/chapter-04/java/src/main/java/exercise5/bigbrother/BBApp.java @@ -0,0 +1,29 @@ +package exercise5.bigbrother; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.boot.autoconfigure.domain.EntityScan; +import org.springframework.context.annotation.Bean; +import org.springframework.data.jpa.repository.config.EnableJpaRepositories; + +import io.jaegertracing.Configuration; +import io.jaegertracing.Configuration.ReporterConfiguration; +import io.jaegertracing.Configuration.SamplerConfiguration; + +@EnableJpaRepositories("lib.people") +@EntityScan("lib.people") +@SpringBootApplication +public class BBApp { + + @Bean + public io.opentracing.Tracer initTracer() { + SamplerConfiguration samplerConfig = new SamplerConfiguration().withType("const").withParam(1); + ReporterConfiguration reporterConfig = new ReporterConfiguration().withLogSpans(true); + return new Configuration("java-5-bigbrother").withSampler(samplerConfig).withReporter(reporterConfig).getTracer(); + } + + public static void main(String[] args) { + System.setProperty("server.port", "8081"); + SpringApplication.run(BBApp.class, args); + } +} diff --git a/chapter-04/java/src/main/java/exercise5/bigbrother/BBController.java b/chapter-04/java/src/main/java/exercise5/bigbrother/BBController.java new file mode 100644 index 0000000..d4e4e0a --- /dev/null +++ b/chapter-04/java/src/main/java/exercise5/bigbrother/BBController.java @@ -0,0 +1,54 @@ +package exercise5.bigbrother; + +import java.util.LinkedHashMap; +import java.util.Map; +import java.util.Optional; + +import javax.servlet.http.HttpServletRequest; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.RestController; + +import exercise5.TracedController; +import io.opentracing.Scope; +import io.opentracing.Span; +import lib.people.Person; +import lib.people.PersonRepository; + +@RestController +public class BBController extends TracedController { + + @Autowired + private PersonRepository personRepository; + + @GetMapping("/getPerson/{name}") + public Person getPerson(@PathVariable String name, HttpServletRequest request) { + Span span = startServerSpan("/getPerson", request); + try (Scope scope = tracer.scopeManager().activate(span, false)) { + Person person = loadPerson(name); + Map fields = new LinkedHashMap<>(); + fields.put("name", person.getName()); + fields.put("title", person.getTitle()); + fields.put("description", person.getDescription()); + span.log(fields); + return person; + } finally { + span.finish(); + } + } + + private Person loadPerson(String name) { + Span span = tracer.buildSpan("get-person").start(); + try (Scope scope = tracer.scopeManager().activate(span, false)) { + Optional personOpt = personRepository.findById(name); + if (personOpt.isPresent()) { + return personOpt.get(); + } + return new Person(name); + } finally { + span.finish(); + } + } +} diff --git a/chapter-04/java/src/main/java/exercise5/formatter/FApp.java b/chapter-04/java/src/main/java/exercise5/formatter/FApp.java new file mode 100644 index 0000000..aaee52b --- /dev/null +++ b/chapter-04/java/src/main/java/exercise5/formatter/FApp.java @@ -0,0 +1,25 @@ +package exercise5.formatter; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.context.annotation.Bean; + +import io.jaegertracing.Configuration; +import io.jaegertracing.Configuration.ReporterConfiguration; +import io.jaegertracing.Configuration.SamplerConfiguration; + +@SpringBootApplication +public class FApp { + + @Bean + public io.opentracing.Tracer initTracer() { + SamplerConfiguration samplerConfig = new SamplerConfiguration().withType("const").withParam(1); + ReporterConfiguration reporterConfig = new ReporterConfiguration().withLogSpans(true); + return new Configuration("java-5-formatter").withSampler(samplerConfig).withReporter(reporterConfig).getTracer(); + } + + public static void main(String[] args) { + System.setProperty("server.port", "8082"); + SpringApplication.run(FApp.class, args); + } +} diff --git a/chapter-04/java/src/main/java/exercise5/formatter/FController.java b/chapter-04/java/src/main/java/exercise5/formatter/FController.java new file mode 100644 index 0000000..f92890b --- /dev/null +++ b/chapter-04/java/src/main/java/exercise5/formatter/FController.java @@ -0,0 +1,38 @@ +package exercise5.formatter; + +import javax.servlet.http.HttpServletRequest; + +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.bind.annotation.RestController; + +import exercise5.TracedController; +import io.opentracing.Scope; +import io.opentracing.Span; + +@RestController +public class FController extends TracedController { + + @GetMapping("/formatGreeting") + public String formatGreeting(@RequestParam String name, @RequestParam String title, + @RequestParam String description, HttpServletRequest request) { + Span span = startServerSpan("/formatGreeting", request); + try (Scope scope = tracer.scopeManager().activate(span, false)) { + String greeting = tracer.activeSpan().getBaggageItem("greeting"); + if (greeting == null) { + greeting = "Hello"; + } + String response = greeting + ", "; + if (!title.isEmpty()) { + response += title + " "; + } + response += name + "!"; + if (!description.isEmpty()) { + response += " " + description; + } + return response; + } finally { + span.finish(); + } + } +} diff --git a/chapter-04/java/src/main/java/exercise6/HelloApp.java b/chapter-04/java/src/main/java/exercise6/HelloApp.java new file mode 100644 index 0000000..4b892fe --- /dev/null +++ b/chapter-04/java/src/main/java/exercise6/HelloApp.java @@ -0,0 +1,31 @@ +package exercise6; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.boot.web.client.RestTemplateBuilder; +import org.springframework.context.annotation.Bean; +import org.springframework.web.client.RestTemplate; + +import io.jaegertracing.Configuration; +import io.jaegertracing.Configuration.ReporterConfiguration; +import io.jaegertracing.Configuration.SamplerConfiguration; + +@SpringBootApplication +public class HelloApp { + + @Bean + public RestTemplate restTemplate(RestTemplateBuilder restTemplateBuilder) { + return restTemplateBuilder.build(); + } + + @Bean + public io.opentracing.Tracer initTracer() { + SamplerConfiguration samplerConfig = new SamplerConfiguration().withType("const").withParam(1); + ReporterConfiguration reporterConfig = new ReporterConfiguration().withLogSpans(true); + return new Configuration("java-6-hello").withSampler(samplerConfig).withReporter(reporterConfig).getTracer(); + } + + public static void main(String[] args) { + SpringApplication.run(HelloApp.class, args); + } +} diff --git a/chapter-04/java/src/main/java/exercise6/HelloController.java b/chapter-04/java/src/main/java/exercise6/HelloController.java new file mode 100644 index 0000000..996eadc --- /dev/null +++ b/chapter-04/java/src/main/java/exercise6/HelloController.java @@ -0,0 +1,61 @@ +package exercise6; + +import java.net.URI; +import java.util.Collections; +import java.util.LinkedHashMap; +import java.util.Map; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.RestController; +import org.springframework.web.client.RestTemplate; +import org.springframework.web.util.UriComponentsBuilder; + +import io.opentracing.Span; +import io.opentracing.Tracer; +import lib.people.Person; + +@RestController +public class HelloController { + + @Autowired + private RestTemplate restTemplate; + + @Autowired + private Tracer tracer; + + @GetMapping("/sayHello/{name}") + public String sayHello(@PathVariable String name) { + Span span = tracer.activeSpan(); + Person person = getPerson(name); + Map fields = new LinkedHashMap<>(); + fields.put("name", person.getName()); + fields.put("title", person.getTitle()); + fields.put("description", person.getDescription()); + span.log(fields); + + String response = formatGreeting(person); + span.setTag("response", response); + + return response; + } + + private Person getPerson(String name) { + String url = "http://localhost:8081/getPerson/" + name; + ResponseEntity response = restTemplate.getForEntity(url, Person.class); + return response.getBody(); + } + + private String formatGreeting(Person person) { + URI uri = UriComponentsBuilder // + .fromHttpUrl("http://localhost:8082/formatGreeting") // + .queryParam("name", person.getName()) // + .queryParam("title", person.getTitle()) // + .queryParam("description", person.getDescription()) // + .build(Collections.emptyMap()); + ResponseEntity response = restTemplate.getForEntity(uri, String.class); + return response.getBody(); + } +} diff --git a/chapter-04/java/src/main/java/exercise6/bigbrother/BBApp.java b/chapter-04/java/src/main/java/exercise6/bigbrother/BBApp.java new file mode 100644 index 0000000..4396939 --- /dev/null +++ b/chapter-04/java/src/main/java/exercise6/bigbrother/BBApp.java @@ -0,0 +1,29 @@ +package exercise6.bigbrother; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.boot.autoconfigure.domain.EntityScan; +import org.springframework.context.annotation.Bean; +import org.springframework.data.jpa.repository.config.EnableJpaRepositories; + +import io.jaegertracing.Configuration; +import io.jaegertracing.Configuration.ReporterConfiguration; +import io.jaegertracing.Configuration.SamplerConfiguration; + +@EnableJpaRepositories("lib.people") +@EntityScan("lib.people") +@SpringBootApplication +public class BBApp { + + @Bean + public io.opentracing.Tracer initTracer() { + SamplerConfiguration samplerConfig = new SamplerConfiguration().withType("const").withParam(1); + ReporterConfiguration reporterConfig = new ReporterConfiguration().withLogSpans(true); + return new Configuration("java-6-bigbrother").withSampler(samplerConfig).withReporter(reporterConfig).getTracer(); + } + + public static void main(String[] args) { + System.setProperty("server.port", "8081"); + SpringApplication.run(BBApp.class, args); + } +} diff --git a/chapter-04/java/src/main/java/exercise6/bigbrother/BBController.java b/chapter-04/java/src/main/java/exercise6/bigbrother/BBController.java new file mode 100644 index 0000000..0631076 --- /dev/null +++ b/chapter-04/java/src/main/java/exercise6/bigbrother/BBController.java @@ -0,0 +1,51 @@ +package exercise6.bigbrother; + +import java.util.LinkedHashMap; +import java.util.Map; +import java.util.Optional; + +import io.opentracing.Tracer; +import io.opentracing.Scope; +import io.opentracing.Span; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.RestController; + +import lib.people.Person; +import lib.people.PersonRepository; + +@RestController +public class BBController { + + @Autowired + private PersonRepository personRepository; + + @Autowired + private Tracer tracer; + + @GetMapping("/getPerson/{name}") + public Person getPerson(@PathVariable String name) { + Person person = loadPerson(name); + Map fields = new LinkedHashMap<>(); + fields.put("name", person.getName()); + fields.put("title", person.getTitle()); + fields.put("description", person.getDescription()); + tracer.activeSpan().log(fields); + return person; + } + + private Person loadPerson(String name) { + Span span = tracer.buildSpan("get-person").start(); + try (Scope scope = tracer.scopeManager().activate(span, false)) { + Optional personOpt = personRepository.findById(name); + if (personOpt.isPresent()) { + return personOpt.get(); + } + return new Person(name); + } finally { + span.finish(); + } + } +} diff --git a/chapter-04/java/src/main/java/exercise6/formatter/FApp.java b/chapter-04/java/src/main/java/exercise6/formatter/FApp.java new file mode 100644 index 0000000..fa03619 --- /dev/null +++ b/chapter-04/java/src/main/java/exercise6/formatter/FApp.java @@ -0,0 +1,25 @@ +package exercise6.formatter; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.context.annotation.Bean; + +import io.jaegertracing.Configuration; +import io.jaegertracing.Configuration.ReporterConfiguration; +import io.jaegertracing.Configuration.SamplerConfiguration; + +@SpringBootApplication +public class FApp { + + @Bean + public io.opentracing.Tracer initTracer() { + SamplerConfiguration samplerConfig = new SamplerConfiguration().withType("const").withParam(1); + ReporterConfiguration reporterConfig = new ReporterConfiguration().withLogSpans(true); + return new Configuration("java-6-formatter").withSampler(samplerConfig).withReporter(reporterConfig).getTracer(); + } + + public static void main(String[] args) { + System.setProperty("server.port", "8082"); + SpringApplication.run(FApp.class, args); + } +} diff --git a/chapter-04/java/src/main/java/exercise6/formatter/FController.java b/chapter-04/java/src/main/java/exercise6/formatter/FController.java new file mode 100644 index 0000000..2750248 --- /dev/null +++ b/chapter-04/java/src/main/java/exercise6/formatter/FController.java @@ -0,0 +1,23 @@ +package exercise6.formatter; + +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.bind.annotation.RestController; + +@RestController +public class FController { + + @GetMapping("/formatGreeting") + public String formatGreeting(@RequestParam String name, @RequestParam String title, + @RequestParam String description) { + String response = "Hello, "; + if (!title.isEmpty()) { + response += title + " "; + } + response += name + "!"; + if (!description.isEmpty()) { + response += " " + description; + } + return response; + } +} diff --git a/chapter-04/java/src/main/java/lib/people/Person.java b/chapter-04/java/src/main/java/lib/people/Person.java new file mode 100644 index 0000000..e26ebe1 --- /dev/null +++ b/chapter-04/java/src/main/java/lib/people/Person.java @@ -0,0 +1,46 @@ +package lib.people; + +import javax.persistence.Column; +import javax.persistence.Entity; +import javax.persistence.Id; +import javax.persistence.Table; + +@Entity +@Table(name = "people") +public class Person { + @Id + private String name; + + @Column(nullable = false) + private String title; + + @Column(nullable = false) + private String description; + + public Person() {} + + public Person(String name) { + this.name = name; + } + + /** + * @return the name + */ + public String getName() { + return name; + } + + /** + * @return the title + */ + public String getTitle() { + return title; + } + + /** + * @return the description + */ + public String getDescription() { + return description; + } +} diff --git a/chapter-04/java/src/main/java/lib/people/PersonRepository.java b/chapter-04/java/src/main/java/lib/people/PersonRepository.java new file mode 100644 index 0000000..7914f77 --- /dev/null +++ b/chapter-04/java/src/main/java/lib/people/PersonRepository.java @@ -0,0 +1,6 @@ +package lib.people; + +import org.springframework.data.repository.CrudRepository; + +public interface PersonRepository extends CrudRepository { +} diff --git a/chapter-04/java/src/main/resources/application.properties b/chapter-04/java/src/main/resources/application.properties new file mode 100644 index 0000000..8b12676 --- /dev/null +++ b/chapter-04/java/src/main/resources/application.properties @@ -0,0 +1,4 @@ +spring.jpa.hibernate.ddl-auto=none +spring.datasource.url=jdbc:mysql://localhost:3306/chapter04 +spring.datasource.username=root +spring.datasource.password=mysqlpwd diff --git a/chapter-04/python/.gitignore b/chapter-04/python/.gitignore new file mode 100644 index 0000000..3c188e7 --- /dev/null +++ b/chapter-04/python/.gitignore @@ -0,0 +1,3 @@ +env/ +*.pyc +__pycache__/ diff --git a/chapter-04/python/exercise1/__init__.py b/chapter-04/python/exercise1/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/chapter-04/python/exercise1/database.py b/chapter-04/python/exercise1/database.py new file mode 100644 index 0000000..e1310e6 --- /dev/null +++ b/chapter-04/python/exercise1/database.py @@ -0,0 +1,23 @@ +from sqlalchemy import create_engine +from sqlalchemy.ext.declarative import declarative_base +from sqlalchemy.orm import sessionmaker +from sqlalchemy.schema import Column +from sqlalchemy.types import String + + +db_url = 'mysql+pymysql://root:mysqlpwd@localhost:3306/chapter04' +engine = create_engine(db_url, echo=False) +Session = sessionmaker(bind=engine) +session = Session() +Base = declarative_base() + + +class Person(Base): + __tablename__ = 'people' + name = Column(String, primary_key=True) + title = Column(String) + description = Column(String) + + @staticmethod + def get(name): + return session.query(Person).get(name) diff --git a/chapter-04/python/exercise1/hello.py b/chapter-04/python/exercise1/hello.py new file mode 100644 index 0000000..bceeb82 --- /dev/null +++ b/chapter-04/python/exercise1/hello.py @@ -0,0 +1,38 @@ +from flask import Flask +from .database import Person + + +app = Flask('py-1-hello') + + +@app.route("/sayHello/") +def say_hello(name): + person = get_person(name) + resp = format_greeting( + name=person.name, + title=person.title, + description=person.description, + ) + return resp + + +def get_person(name): + person = Person.get(name) + if person is None: + person = Person() + person.name = name + return person + + +def format_greeting(name, title, description): + greeting = 'Hello, ' + if title: + greeting += title + ' ' + greeting += name + '!' + if description: + greeting += ' ' + description + return greeting + + +if __name__ == "__main__": + app.run(port=8080) diff --git a/chapter-04/python/exercise2/__init__.py b/chapter-04/python/exercise2/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/chapter-04/python/exercise2/database.py b/chapter-04/python/exercise2/database.py new file mode 100644 index 0000000..e1310e6 --- /dev/null +++ b/chapter-04/python/exercise2/database.py @@ -0,0 +1,23 @@ +from sqlalchemy import create_engine +from sqlalchemy.ext.declarative import declarative_base +from sqlalchemy.orm import sessionmaker +from sqlalchemy.schema import Column +from sqlalchemy.types import String + + +db_url = 'mysql+pymysql://root:mysqlpwd@localhost:3306/chapter04' +engine = create_engine(db_url, echo=False) +Session = sessionmaker(bind=engine) +session = Session() +Base = declarative_base() + + +class Person(Base): + __tablename__ = 'people' + name = Column(String, primary_key=True) + title = Column(String) + description = Column(String) + + @staticmethod + def get(name): + return session.query(Person).get(name) diff --git a/chapter-04/python/exercise2/hello.py b/chapter-04/python/exercise2/hello.py new file mode 100644 index 0000000..e5646c4 --- /dev/null +++ b/chapter-04/python/exercise2/hello.py @@ -0,0 +1,48 @@ +from flask import Flask +from .database import Person +from lib.tracing import init_tracer +import opentracing + + +app = Flask('py-2-hello') +init_tracer('py-2-hello') + + +@app.route("/sayHello/") +def say_hello(name): + with opentracing.tracer.start_span('say-hello') as span: + person = get_person(name, span) + resp = format_greeting( + name=person.name, + title=person.title, + description=person.description, + ) + span.set_tag('response', resp) + return resp + + +def get_person(name, span): + person = Person.get(name) + if person is None: + person = Person() + person.name = name + span.log_kv({ + 'name': person.name, + 'title': person.title, + 'description': person.description, + }) + return person + + +def format_greeting(name, title, description): + greeting = 'Hello, ' + if title: + greeting += title + ' ' + greeting += name + '!' + if description: + greeting += ' ' + description + return greeting + + +if __name__ == "__main__": + app.run(port=8080) diff --git a/chapter-04/python/exercise3a/__init__.py b/chapter-04/python/exercise3a/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/chapter-04/python/exercise3a/database.py b/chapter-04/python/exercise3a/database.py new file mode 100644 index 0000000..e1310e6 --- /dev/null +++ b/chapter-04/python/exercise3a/database.py @@ -0,0 +1,23 @@ +from sqlalchemy import create_engine +from sqlalchemy.ext.declarative import declarative_base +from sqlalchemy.orm import sessionmaker +from sqlalchemy.schema import Column +from sqlalchemy.types import String + + +db_url = 'mysql+pymysql://root:mysqlpwd@localhost:3306/chapter04' +engine = create_engine(db_url, echo=False) +Session = sessionmaker(bind=engine) +session = Session() +Base = declarative_base() + + +class Person(Base): + __tablename__ = 'people' + name = Column(String, primary_key=True) + title = Column(String) + description = Column(String) + + @staticmethod + def get(name): + return session.query(Person).get(name) diff --git a/chapter-04/python/exercise3a/hello.py b/chapter-04/python/exercise3a/hello.py new file mode 100644 index 0000000..95b489a --- /dev/null +++ b/chapter-04/python/exercise3a/hello.py @@ -0,0 +1,55 @@ +from flask import Flask +from .database import Person +from lib.tracing import init_tracer +import opentracing + + +app = Flask('py-3-hello') +init_tracer('py-3-hello') + + +@app.route("/sayHello/") +def say_hello(name): + with opentracing.tracer.start_span('say-hello') as span: + person = get_person(name, span) + resp = format_greeting( + name=person.name, + title=person.title, + description=person.description, + span=span, + ) + span.set_tag('response', resp) + return resp + + +def get_person(name, span): + with opentracing.tracer.start_span( + 'get-person', child_of=span, + ) as span: + person = Person.get(name) + if person is None: + person = Person() + person.name = name + span.log_kv({ + 'name': person.name, + 'title': person.title, + 'description': person.description, + }) + return person + + +def format_greeting(name, title, description, span): + with opentracing.tracer.start_span( + 'format-greeting', child_of=span, + ): + greeting = 'Hello, ' + if title: + greeting += title + ' ' + greeting += name + '!' + if description: + greeting += ' ' + description + return greeting + + +if __name__ == "__main__": + app.run(port=8080) diff --git a/chapter-04/python/exercise3b/__init__.py b/chapter-04/python/exercise3b/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/chapter-04/python/exercise3b/database.py b/chapter-04/python/exercise3b/database.py new file mode 100644 index 0000000..e1310e6 --- /dev/null +++ b/chapter-04/python/exercise3b/database.py @@ -0,0 +1,23 @@ +from sqlalchemy import create_engine +from sqlalchemy.ext.declarative import declarative_base +from sqlalchemy.orm import sessionmaker +from sqlalchemy.schema import Column +from sqlalchemy.types import String + + +db_url = 'mysql+pymysql://root:mysqlpwd@localhost:3306/chapter04' +engine = create_engine(db_url, echo=False) +Session = sessionmaker(bind=engine) +session = Session() +Base = declarative_base() + + +class Person(Base): + __tablename__ = 'people' + name = Column(String, primary_key=True) + title = Column(String) + description = Column(String) + + @staticmethod + def get(name): + return session.query(Person).get(name) diff --git a/chapter-04/python/exercise3b/hello.py b/chapter-04/python/exercise3b/hello.py new file mode 100644 index 0000000..bd95a51 --- /dev/null +++ b/chapter-04/python/exercise3b/hello.py @@ -0,0 +1,54 @@ +from flask import Flask +from .database import Person +from lib.tracing import init_tracer +import opentracing + + +app = Flask('py-3-hello') +init_tracer('py-3-hello') + + +@app.route("/sayHello/") +def say_hello(name): + with opentracing.tracer.start_active_span('say-hello') as scope: + person = get_person(name) + resp = format_greeting( + name=person.name, + title=person.title, + description=person.description, + ) + scope.span.set_tag('response', resp) + return resp + + +def get_person(name): + with opentracing.tracer.start_active_span( + 'get-person', + ) as scope: + person = Person.get(name) + if person is None: + person = Person() + person.name = name + scope.span.log_kv({ + 'name': person.name, + 'title': person.title, + 'description': person.description, + }) + return person + + +def format_greeting(name, title, description): + with opentracing.tracer.start_active_span( + 'format-greeting', + ): + greeting = 'Hello, ' + if title: + greeting += title + ' ' + greeting += name + '!' + if description: + greeting += ' ' + description + return greeting + + +if __name__ == "__main__": + app.run(port=8080) diff --git a/chapter-04/python/exercise4a/__init__.py b/chapter-04/python/exercise4a/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/chapter-04/python/exercise4a/bigbrother.py b/chapter-04/python/exercise4a/bigbrother.py new file mode 100644 index 0000000..d83248b --- /dev/null +++ b/chapter-04/python/exercise4a/bigbrother.py @@ -0,0 +1,32 @@ +from flask import Flask +import json +from .database import Person +from lib.tracing import init_tracer +import opentracing + + +app = Flask('py-4-bigbrother') +init_tracer('py-4-bigbrother') + + +@app.route("/getPerson/") +def get_person_http(name): + with opentracing.tracer.start_active_span('/getPerson') as scope: + person = Person.get(name) + if person is None: + person = Person() + person.name = name + scope.span.log_kv({ + 'name': person.name, + 'title': person.title, + 'description': person.description, + }) + return json.dumps({ + 'name': person.name, + 'title': person.title, + 'description': person.description, + }) + + +if __name__ == "__main__": + app.run(port=8081) diff --git a/chapter-04/python/exercise4a/database.py b/chapter-04/python/exercise4a/database.py new file mode 100644 index 0000000..e1310e6 --- /dev/null +++ b/chapter-04/python/exercise4a/database.py @@ -0,0 +1,23 @@ +from sqlalchemy import create_engine +from sqlalchemy.ext.declarative import declarative_base +from sqlalchemy.orm import sessionmaker +from sqlalchemy.schema import Column +from sqlalchemy.types import String + + +db_url = 'mysql+pymysql://root:mysqlpwd@localhost:3306/chapter04' +engine = create_engine(db_url, echo=False) +Session = sessionmaker(bind=engine) +session = Session() +Base = declarative_base() + + +class Person(Base): + __tablename__ = 'people' + name = Column(String, primary_key=True) + title = Column(String) + description = Column(String) + + @staticmethod + def get(name): + return session.query(Person).get(name) diff --git a/chapter-04/python/exercise4a/formatter.py b/chapter-04/python/exercise4a/formatter.py new file mode 100644 index 0000000..edfb2c1 --- /dev/null +++ b/chapter-04/python/exercise4a/formatter.py @@ -0,0 +1,38 @@ +from flask import Flask +from flask import request +from lib.tracing import init_tracer +import opentracing + + +app = Flask('py-4-formatter') +init_tracer('py-4-formatter') + + +@app.route("/formatGreeting") +def handle_format_greeting(): + with opentracing.tracer.start_active_span('/formatGreeting') as scope: + name = request.args.get('name') + title = request.args.get('title') + descr = request.args.get('description') + return format_greeting( + name=name, + title=title, + description=descr, + ) + + +def format_greeting(name, title, description): + with opentracing.tracer.start_active_span( + 'format-greeting', + ): + greeting = 'Hello, ' + if title: + greeting += title + ' ' + greeting += name + '!' + if description: + greeting += ' ' + description + return greeting + + +if __name__ == "__main__": + app.run(port=8082) diff --git a/chapter-04/python/exercise4a/hello.py b/chapter-04/python/exercise4a/hello.py new file mode 100644 index 0000000..a0235be --- /dev/null +++ b/chapter-04/python/exercise4a/hello.py @@ -0,0 +1,48 @@ +import json +import requests +import opentracing +from flask import Flask +from lib.tracing import init_tracer + + +app = Flask('py-4-hello') +init_tracer('py-4-hello') + + +@app.route("/sayHello/") +def say_hello(name): + with opentracing.tracer.start_active_span('say-hello') as scope: + person = get_person(name) + resp = format_greeting(person) + scope.span.set_tag('response', resp) + return resp + + +def get_person(name): + with opentracing.tracer.start_active_span( + 'get-person', + ) as scope: + url = 'http://localhost:8081/getPerson/%s' % name + r = requests.get(url) + assert r.status_code == 200 + person = json.loads(r.text) + scope.span.log_kv({ + 'name': person['name'], + 'title': person['title'], + 'description': person['description'], + }) + return person + + +def format_greeting(person): + with opentracing.tracer.start_active_span( + 'format-greeting', + ): + url = 'http://localhost:8082/formatGreeting' + r = requests.get(url, params=person) + assert r.status_code == 200 + return r.text + + +if __name__ == "__main__": + app.run(port=8080) diff --git a/chapter-04/python/exercise4b/__init__.py b/chapter-04/python/exercise4b/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/chapter-04/python/exercise4b/bigbrother.py b/chapter-04/python/exercise4b/bigbrother.py new file mode 100644 index 0000000..a630fd6 --- /dev/null +++ b/chapter-04/python/exercise4b/bigbrother.py @@ -0,0 +1,42 @@ +from flask import Flask +from flask import request +import json +from .database import Person +from lib.tracing import init_tracer +import opentracing +from opentracing.ext import tags + + +app = Flask('py-4-bigbrother') +init_tracer('py-4-bigbrother') + + +@app.route("/getPerson/") +def get_person_http(name): + span_ctx = opentracing.tracer.extract( + opentracing.Format.HTTP_HEADERS, + request.headers, + ) + with opentracing.tracer.start_active_span( + '/getPerson', + child_of=span_ctx, + tags={tags.SPAN_KIND: tags.SPAN_KIND_RPC_SERVER}, + ) as scope: + person = Person.get(name) + if person is None: + person = Person() + person.name = name + scope.span.log_kv({ + 'name': person.name, + 'title': person.title, + 'description': person.description, + }) + return json.dumps({ + 'name': person.name, + 'title': person.title, + 'description': person.description, + }) + + +if __name__ == "__main__": + app.run(port=8081) diff --git a/chapter-04/python/exercise4b/database.py b/chapter-04/python/exercise4b/database.py new file mode 100644 index 0000000..e1310e6 --- /dev/null +++ b/chapter-04/python/exercise4b/database.py @@ -0,0 +1,23 @@ +from sqlalchemy import create_engine +from sqlalchemy.ext.declarative import declarative_base +from sqlalchemy.orm import sessionmaker +from sqlalchemy.schema import Column +from sqlalchemy.types import String + + +db_url = 'mysql+pymysql://root:mysqlpwd@localhost:3306/chapter04' +engine = create_engine(db_url, echo=False) +Session = sessionmaker(bind=engine) +session = Session() +Base = declarative_base() + + +class Person(Base): + __tablename__ = 'people' + name = Column(String, primary_key=True) + title = Column(String) + description = Column(String) + + @staticmethod + def get(name): + return session.query(Person).get(name) diff --git a/chapter-04/python/exercise4b/formatter.py b/chapter-04/python/exercise4b/formatter.py new file mode 100644 index 0000000..2072597 --- /dev/null +++ b/chapter-04/python/exercise4b/formatter.py @@ -0,0 +1,47 @@ +from flask import Flask +from flask import request +from lib.tracing import init_tracer +import opentracing +from opentracing.ext import tags + + +app = Flask('py-4-formatter') +init_tracer('py-4-formatter') + + +@app.route("/formatGreeting") +def handle_format_greeting(): + span_ctx = opentracing.tracer.extract( + opentracing.Format.HTTP_HEADERS, + request.headers, + ) + with opentracing.tracer.start_active_span( + '/formatGreeting', + child_of=span_ctx, + tags={tags.SPAN_KIND: tags.SPAN_KIND_RPC_SERVER}, + ) as scope: + name = request.args.get('name') + title = request.args.get('title') + descr = request.args.get('description') + return format_greeting( + name=name, + title=title, + description=descr, + ) + + +def format_greeting(name, title, description): + with opentracing.tracer.start_active_span( + 'format-greeting', + ): + greeting = 'Hello, ' + if title: + greeting += title + ' ' + greeting += name + '!' + if description: + greeting += ' ' + description + return greeting + + +if __name__ == "__main__": + app.run(port=8082) diff --git a/chapter-04/python/exercise4b/hello.py b/chapter-04/python/exercise4b/hello.py new file mode 100644 index 0000000..59d207f --- /dev/null +++ b/chapter-04/python/exercise4b/hello.py @@ -0,0 +1,71 @@ +import json +import requests +from flask import Flask +from flask import request +from lib.tracing import init_tracer +import opentracing +from opentracing.ext import tags + + +app = Flask('py-4-hello') +init_tracer('py-4-hello') + + +@app.route("/sayHello/") +def say_hello(name): + span_ctx = opentracing.tracer.extract( + opentracing.Format.HTTP_HEADERS, + request.headers, + ) + with opentracing.tracer.start_active_span( + 'say-hello', + child_of=span_ctx, + tags={tags.SPAN_KIND: tags.SPAN_KIND_RPC_SERVER}, + ) as scope: + person = get_person(name) + resp = format_greeting(person) + scope.span.set_tag('response', resp) + return resp + + +def get_person(name): + with opentracing.tracer.start_active_span( + 'get-person', + ) as scope: + url = 'http://localhost:8081/getPerson/%s' % name + res = _get(url) + person = json.loads(res) + scope.span.log_kv({ + 'name': person['name'], + 'title': person['title'], + 'description': person['description'], + }) + return person + + +def format_greeting(person): + with opentracing.tracer.start_active_span( + 'format-greeting', + ): + url = 'http://localhost:8082/formatGreeting' + return _get(url, params=person) + + +def _get(url, params=None): + span = opentracing.tracer.active_span + span.set_tag(tags.HTTP_URL, url) + span.set_tag(tags.HTTP_METHOD, 'GET') + span.set_tag(tags.SPAN_KIND, tags.SPAN_KIND_RPC_CLIENT) + headers = {} + opentracing.tracer.inject( + span.context, + opentracing.Format.HTTP_HEADERS, + headers, + ) + r = requests.get(url, params=params, headers=headers) + assert r.status_code == 200 + return r.text + + +if __name__ == "__main__": + app.run(port=8080) diff --git a/chapter-04/python/exercise5/__init__.py b/chapter-04/python/exercise5/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/chapter-04/python/exercise5/bigbrother.py b/chapter-04/python/exercise5/bigbrother.py new file mode 100644 index 0000000..6689180 --- /dev/null +++ b/chapter-04/python/exercise5/bigbrother.py @@ -0,0 +1,42 @@ +from flask import Flask +from flask import request +import json +from .database import Person +from lib.tracing import init_tracer +import opentracing +from opentracing.ext import tags + + +app = Flask('py-5-bigbrother') +init_tracer('py-5-bigbrother') + + +@app.route("/getPerson/") +def get_person_http(name): + span_ctx = opentracing.tracer.extract( + opentracing.Format.HTTP_HEADERS, + request.headers, + ) + with opentracing.tracer.start_active_span( + '/getPerson', + child_of=span_ctx, + tags={tags.SPAN_KIND: tags.SPAN_KIND_RPC_SERVER}, + ) as scope: + person = Person.get(name) + if person is None: + person = Person() + person.name = name + scope.span.log_kv({ + 'name': person.name, + 'title': person.title, + 'description': person.description, + }) + return json.dumps({ + 'name': person.name, + 'title': person.title, + 'description': person.description, + }) + + +if __name__ == "__main__": + app.run(port=8081) diff --git a/chapter-04/python/exercise5/database.py b/chapter-04/python/exercise5/database.py new file mode 100644 index 0000000..e1310e6 --- /dev/null +++ b/chapter-04/python/exercise5/database.py @@ -0,0 +1,23 @@ +from sqlalchemy import create_engine +from sqlalchemy.ext.declarative import declarative_base +from sqlalchemy.orm import sessionmaker +from sqlalchemy.schema import Column +from sqlalchemy.types import String + + +db_url = 'mysql+pymysql://root:mysqlpwd@localhost:3306/chapter04' +engine = create_engine(db_url, echo=False) +Session = sessionmaker(bind=engine) +session = Session() +Base = declarative_base() + + +class Person(Base): + __tablename__ = 'people' + name = Column(String, primary_key=True) + title = Column(String) + description = Column(String) + + @staticmethod + def get(name): + return session.query(Person).get(name) diff --git a/chapter-04/python/exercise5/formatter.py b/chapter-04/python/exercise5/formatter.py new file mode 100644 index 0000000..b1d5170 --- /dev/null +++ b/chapter-04/python/exercise5/formatter.py @@ -0,0 +1,48 @@ +from flask import Flask +from flask import request +from lib.tracing import init_tracer +import opentracing +from opentracing.ext import tags + + +app = Flask('py-5-formatter') +init_tracer('py-5-formatter') + + +@app.route("/formatGreeting") +def handle_format_greeting(): + span_ctx = opentracing.tracer.extract( + opentracing.Format.HTTP_HEADERS, + request.headers, + ) + with opentracing.tracer.start_active_span( + '/formatGreeting', + child_of=span_ctx, + tags={tags.SPAN_KIND: tags.SPAN_KIND_RPC_SERVER}, + ) as scope: + name = request.args.get('name') + title = request.args.get('title') + descr = request.args.get('description') + return format_greeting( + name=name, + title=title, + description=descr, + ) + + +def format_greeting(name, title, description): + with opentracing.tracer.start_active_span( + 'format-greeting', + ) as scope: + greeting = scope.span.get_baggage_item('greeting') or 'Hello' + greeting += ', ' + if title: + greeting += title + ' ' + greeting += name + '!' + if description: + greeting += ' ' + description + return greeting + + +if __name__ == "__main__": + app.run(port=8082) diff --git a/chapter-04/python/exercise5/hello.py b/chapter-04/python/exercise5/hello.py new file mode 100644 index 0000000..313a66d --- /dev/null +++ b/chapter-04/python/exercise5/hello.py @@ -0,0 +1,71 @@ +import json +import requests +from flask import Flask +from flask import request +from lib.tracing import init_tracer +import opentracing +from opentracing.ext import tags + + +app = Flask('py-5-hello') +init_tracer('py-5-hello') + + +@app.route("/sayHello/") +def say_hello(name): + span_ctx = opentracing.tracer.extract( + opentracing.Format.HTTP_HEADERS, + request.headers, + ) + with opentracing.tracer.start_active_span( + 'say-hello', + child_of=span_ctx, + tags={tags.SPAN_KIND: tags.SPAN_KIND_RPC_SERVER}, + ) as scope: + person = get_person(name) + resp = format_greeting(person) + scope.span.set_tag('response', resp) + return resp + + +def get_person(name): + with opentracing.tracer.start_active_span( + 'get-person', + ) as scope: + url = 'http://localhost:8081/getPerson/%s' % name + res = _get(url) + person = json.loads(res) + scope.span.log_kv({ + 'name': person['name'], + 'title': person['title'], + 'description': person['description'], + }) + return person + + +def format_greeting(person): + with opentracing.tracer.start_active_span( + 'format-greeting', + ): + url = 'http://localhost:8082/formatGreeting' + return _get(url, params=person) + + +def _get(url, params=None): + span = opentracing.tracer.active_span + span.set_tag(tags.HTTP_URL, url) + span.set_tag(tags.HTTP_METHOD, 'GET') + span.set_tag(tags.SPAN_KIND, tags.SPAN_KIND_RPC_CLIENT) + headers = {} + opentracing.tracer.inject( + span.context, + opentracing.Format.HTTP_HEADERS, + headers, + ) + r = requests.get(url, params=params, headers=headers) + assert r.status_code == 200 + return r.text + + +if __name__ == "__main__": + app.run(port=8080) diff --git a/chapter-04/python/exercise6/__init__.py b/chapter-04/python/exercise6/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/chapter-04/python/exercise6/bigbrother.py b/chapter-04/python/exercise6/bigbrother.py new file mode 100644 index 0000000..dc21a5b --- /dev/null +++ b/chapter-04/python/exercise6/bigbrother.py @@ -0,0 +1,38 @@ +from flask import Flask +from flask import request +import json +from .database import Person +from lib.tracing import init_tracer, flask_to_scope +import opentracing +from opentracing.ext import tags +from opentracing_instrumentation.client_hooks import install_all_patches +from flask_opentracing import FlaskTracer + + +app = Flask('py-6-bigbrother') +init_tracer('py-6-bigbrother') +install_all_patches() +flask_tracer = FlaskTracer(opentracing.tracer, True, app) + + +@app.route("/getPerson/") +def get_person_http(name): + with flask_to_scope(flask_tracer, request) as scope: + person = Person.get(name) + if person is None: + person = Person() + person.name = name + opentracing.tracer.active_span.log_kv({ + 'name': person.name, + 'title': person.title, + 'description': person.description, + }) + return json.dumps({ + 'name': person.name, + 'title': person.title, + 'description': person.description, + }) + + +if __name__ == "__main__": + app.run(port=8081) diff --git a/chapter-04/python/exercise6/database.py b/chapter-04/python/exercise6/database.py new file mode 100644 index 0000000..e1310e6 --- /dev/null +++ b/chapter-04/python/exercise6/database.py @@ -0,0 +1,23 @@ +from sqlalchemy import create_engine +from sqlalchemy.ext.declarative import declarative_base +from sqlalchemy.orm import sessionmaker +from sqlalchemy.schema import Column +from sqlalchemy.types import String + + +db_url = 'mysql+pymysql://root:mysqlpwd@localhost:3306/chapter04' +engine = create_engine(db_url, echo=False) +Session = sessionmaker(bind=engine) +session = Session() +Base = declarative_base() + + +class Person(Base): + __tablename__ = 'people' + name = Column(String, primary_key=True) + title = Column(String) + description = Column(String) + + @staticmethod + def get(name): + return session.query(Person).get(name) diff --git a/chapter-04/python/exercise6/formatter.py b/chapter-04/python/exercise6/formatter.py new file mode 100644 index 0000000..0f3a5d2 --- /dev/null +++ b/chapter-04/python/exercise6/formatter.py @@ -0,0 +1,42 @@ +from flask import Flask +from flask import request +from lib.tracing import init_tracer, flask_to_scope +import opentracing +from opentracing.ext import tags +from flask_opentracing import FlaskTracer + + +app = Flask('py-6-formatter') +init_tracer('py-6-formatter') +flask_tracer = FlaskTracer(opentracing.tracer, True, app) + + +@app.route("/formatGreeting") +def handle_format_greeting(): + with flask_to_scope(flask_tracer, request) as scope: + name = request.args.get('name') + title = request.args.get('title') + descr = request.args.get('description') + return format_greeting( + name=name, + title=title, + description=descr, + ) + + +def format_greeting(name, title, description): + with opentracing.tracer.start_active_span( + 'format-greeting', + ) as scope: + greeting = scope.span.get_baggage_item('greeting') or 'Hello' + greeting += ', ' + if title: + greeting += title + ' ' + greeting += name + '!' + if description: + greeting += ' ' + description + return greeting + + +if __name__ == "__main__": + app.run(port=8082) diff --git a/chapter-04/python/exercise6/hello.py b/chapter-04/python/exercise6/hello.py new file mode 100644 index 0000000..0ba8555 --- /dev/null +++ b/chapter-04/python/exercise6/hello.py @@ -0,0 +1,58 @@ +import sys +import json +import requests +from flask import Flask +from flask import request +from lib.tracing import init_tracer, flask_to_scope +import opentracing +from opentracing.ext import tags +from opentracing_instrumentation.client_hooks import install_all_patches +from flask_opentracing import FlaskTracer + + +app = Flask('py-6-hello') +init_tracer('py-6-hello') +install_all_patches() +flask_tracer = FlaskTracer(opentracing.tracer, True, app) + + +@app.route("/sayHello/") +def say_hello(name): + with flask_to_scope(flask_tracer, request) as scope: + person = get_person(name) + resp = format_greeting(person) + opentracing.tracer.active_span.set_tag('response', resp) + return resp + + +def get_person(name): + with opentracing.tracer.start_active_span( + 'get-person', + ) as scope: + url = 'http://localhost:8081/getPerson/%s' % name + res = _get(url) + person = json.loads(res) + scope.span.log_kv({ + 'name': person['name'], + 'title': person['title'], + 'description': person['description'], + }) + return person + + +def format_greeting(person): + with opentracing.tracer.start_active_span( + 'format-greeting', + ): + url = 'http://localhost:8082/formatGreeting' + return _get(url, params=person) + + +def _get(url, params=None): + r = requests.get(url, params=params) + assert r.status_code == 200 + return r.text + + +if __name__ == "__main__": + app.run(port=8080) diff --git a/chapter-04/python/lib/__init__.py b/chapter-04/python/lib/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/chapter-04/python/lib/tracing.py b/chapter-04/python/lib/tracing.py new file mode 100644 index 0000000..e364fb2 --- /dev/null +++ b/chapter-04/python/lib/tracing.py @@ -0,0 +1,30 @@ +import logging +import opentracing +from jaeger_client import Config + + +def init_tracer(service): + logging.getLogger('').handlers = [] + logging.basicConfig(format='%(message)s', level=logging.DEBUG) + + config = Config( + config={ + 'sampler': { + 'type': 'const', + 'param': 1, + }, + 'logging': True, + 'reporter_batch_size': 1, + }, + service_name=service, + ) + + # this call sets global variable opentracing.tracer + config.initialize_tracer() + + +def flask_to_scope(flask_tracer, request): + return opentracing.tracer.scope_manager.activate( + flask_tracer.get_span(request), + False, + ) diff --git a/chapter-04/python/requirements.txt b/chapter-04/python/requirements.txt new file mode 100644 index 0000000..86c607b --- /dev/null +++ b/chapter-04/python/requirements.txt @@ -0,0 +1,20 @@ + +requests +flask +sqlalchemy==1.2.10 +PyMySQL==0.7.11 + +# We want something like: jaeger-client>=3.8,<4 +# But Scope Manager support not officially released in Jaeger at the time of writing. +# This is using branch 'feature/opentracing-2.0-support' in @yurishkuro's fork. +-e git+https://github.com/yurishkuro/jaeger-client-python.git@d631596415cb549a4e8314cc25d6c53fab710528#egg=jaeger-client + +# We want something like: opentracing_instrumentation>=2.2,<3 +# But Scope Manager support not officially released in opentracing_instrumentation at the time of writing. +# This is using branch 'ot_scopemanager_integration' in @yurishkuro's fork. +-e git+https://github.com/yurishkuro/opentracing-python-instrumentation.git@6bb5db3ea4596869d581513df34533140debb6e8#egg=opentracing_instrumentation + +# We want something like: Flask-Opentracing>=1,<2, +# But it does not yet support Scope Managers: https://github.com/opentracing-contrib/python-flask/issues/18 +# This is the head commit @ master at the time of writing. +-e git+https://github.com/opentracing-contrib/python-flask.git@c00398411d9c5f4f789e097ab68a6d186f8cbba3#egg=flask_opentracing diff --git a/chapter-05/.gitignore b/chapter-05/.gitignore new file mode 100644 index 0000000..080e5ca --- /dev/null +++ b/chapter-05/.gitignore @@ -0,0 +1,5 @@ +.classpath +.project +.settings/ +target/ + diff --git a/chapter-05/.mvn/wrapper/.gitignore b/chapter-05/.mvn/wrapper/.gitignore new file mode 100644 index 0000000..e72f5e8 --- /dev/null +++ b/chapter-05/.mvn/wrapper/.gitignore @@ -0,0 +1 @@ +maven-wrapper.jar diff --git a/chapter-05/.mvn/wrapper/MavenWrapperDownloader.java b/chapter-05/.mvn/wrapper/MavenWrapperDownloader.java new file mode 100755 index 0000000..fa4f7b4 --- /dev/null +++ b/chapter-05/.mvn/wrapper/MavenWrapperDownloader.java @@ -0,0 +1,110 @@ +/* +Licensed to the Apache Software Foundation (ASF) under one +or more contributor license agreements. See the NOTICE file +distributed with this work for additional information +regarding copyright ownership. The ASF licenses this file +to you under the Apache License, Version 2.0 (the +"License"); you may not use this file except in compliance +with the License. You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, +software distributed under the License is distributed on an +"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +KIND, either express or implied. See the License for the +specific language governing permissions and limitations +under the License. +*/ + +import java.net.*; +import java.io.*; +import java.nio.channels.*; +import java.util.Properties; + +public class MavenWrapperDownloader { + + /** + * Default URL to download the maven-wrapper.jar from, if no 'downloadUrl' is provided. + */ + private static final String DEFAULT_DOWNLOAD_URL = + "https://repo.maven.apache.org/maven2/io/takari/maven-wrapper/0.4.2/maven-wrapper-0.4.2.jar"; + + /** + * Path to the maven-wrapper.properties file, which might contain a downloadUrl property to + * use instead of the default one. + */ + private static final String MAVEN_WRAPPER_PROPERTIES_PATH = + ".mvn/wrapper/maven-wrapper.properties"; + + /** + * Path where the maven-wrapper.jar will be saved to. + */ + private static final String MAVEN_WRAPPER_JAR_PATH = + ".mvn/wrapper/maven-wrapper.jar"; + + /** + * Name of the property which should be used to override the default download url for the wrapper. + */ + private static final String PROPERTY_NAME_WRAPPER_URL = "wrapperUrl"; + + public static void main(String args[]) { + System.out.println("- Downloader started"); + File baseDirectory = new File(args[0]); + System.out.println("- Using base directory: " + baseDirectory.getAbsolutePath()); + + // If the maven-wrapper.properties exists, read it and check if it contains a custom + // wrapperUrl parameter. + File mavenWrapperPropertyFile = new File(baseDirectory, MAVEN_WRAPPER_PROPERTIES_PATH); + String url = DEFAULT_DOWNLOAD_URL; + if(mavenWrapperPropertyFile.exists()) { + FileInputStream mavenWrapperPropertyFileInputStream = null; + try { + mavenWrapperPropertyFileInputStream = new FileInputStream(mavenWrapperPropertyFile); + Properties mavenWrapperProperties = new Properties(); + mavenWrapperProperties.load(mavenWrapperPropertyFileInputStream); + url = mavenWrapperProperties.getProperty(PROPERTY_NAME_WRAPPER_URL, url); + } catch (IOException e) { + System.out.println("- ERROR loading '" + MAVEN_WRAPPER_PROPERTIES_PATH + "'"); + } finally { + try { + if(mavenWrapperPropertyFileInputStream != null) { + mavenWrapperPropertyFileInputStream.close(); + } + } catch (IOException e) { + // Ignore ... + } + } + } + System.out.println("- Downloading from: : " + url); + + File outputFile = new File(baseDirectory.getAbsolutePath(), MAVEN_WRAPPER_JAR_PATH); + if(!outputFile.getParentFile().exists()) { + if(!outputFile.getParentFile().mkdirs()) { + System.out.println( + "- ERROR creating output direcrory '" + outputFile.getParentFile().getAbsolutePath() + "'"); + } + } + System.out.println("- Downloading to: " + outputFile.getAbsolutePath()); + try { + downloadFileFromURL(url, outputFile); + System.out.println("Done"); + System.exit(0); + } catch (Throwable e) { + System.out.println("- Error downloading"); + e.printStackTrace(); + System.exit(1); + } + } + + private static void downloadFileFromURL(String urlString, File destination) throws Exception { + URL website = new URL(urlString); + ReadableByteChannel rbc; + rbc = Channels.newChannel(website.openStream()); + FileOutputStream fos = new FileOutputStream(destination); + fos.getChannel().transferFrom(rbc, 0, Long.MAX_VALUE); + fos.close(); + rbc.close(); + } + +} diff --git a/chapter-05/.mvn/wrapper/maven-wrapper.properties b/chapter-05/.mvn/wrapper/maven-wrapper.properties new file mode 100755 index 0000000..00d32aa --- /dev/null +++ b/chapter-05/.mvn/wrapper/maven-wrapper.properties @@ -0,0 +1 @@ +distributionUrl=https://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.5.4/apache-maven-3.5.4-bin.zip \ No newline at end of file diff --git a/chapter-05/Makefile b/chapter-05/Makefile new file mode 100644 index 0000000..3f61a39 --- /dev/null +++ b/chapter-05/Makefile @@ -0,0 +1,25 @@ +#all: compose chatapi webapp storage giphy + +CH6_EX ?= 1 +KSEND ?= sync + +GROUP_ID := com.packt.distributed-tracing-chapter-05 +JAEGER := \ + -DJAEGER_SAMPLER_TYPE=const \ + -DJAEGER_SAMPLER_PARAM=1 +JAEGER_CHAT_API := -DJAEGER_SERVICE_NAME=chat-api-$(CH6_EX) $(JAEGER) -DKSEND=$(KSEND) +JAEGER_STORAGE := -DJAEGER_SERVICE_NAME=storage-service-$(CH6_EX) $(JAEGER) +JAEGER_GIPHY := -DJAEGER_SERVICE_NAME=giphy-service-$(CH6_EX) $(JAEGER) + +compose: + docker-compose up +chatapi: + mvn $(JAEGER_CHAT_API) spring-boot:run -pl $(GROUP_ID):chat-api-$(CH6_EX) +storage: + mvn $(JAEGER_STORAGE) spring-boot:run -pl $(GROUP_ID):storage-service-$(CH6_EX) +giphy: + mvn $(JAEGER_GIPHY) spring-boot:run -pl $(GROUP_ID):giphy-service-$(CH6_EX) +build-webapp: + rm -rf webapp/dist webapp/public/* + (cd webapp && yarn && yarn build) + cp webapp/dist/{app.*.js,favicon.*.ico,index.html} webapp/public/ diff --git a/chapter-05/docker-compose.yml b/chapter-05/docker-compose.yml new file mode 100644 index 0000000..b13c451 --- /dev/null +++ b/chapter-05/docker-compose.yml @@ -0,0 +1,36 @@ +version: "3" +services: + kafka: + image: confluentinc/cp-kafka:5.0.0-2 + ports: + - 9092:9092 + environment: + KAFKA_ZOOKEEPER_CONNECT: zookeeper:2181 + KAFKA_ADVERTISED_LISTENERS: PLAINTEXT://localhost:9092 + KAFKA_OFFSETS_TOPIC_REPLICATION_FACTOR: 1 + KAFKA_BROKER_ID: 1 + restart: on-failure + zookeeper: + image: confluentinc/cp-zookeeper:5.0.0-2 + ports: + - 2181:2181 + environment: + ZOOKEEPER_CLIENT_PORT: 2181 + redis: + image: redis:alpine + ports: + - 6379:6379 + jaeger-all-in-one: + image: jaegertracing/all-in-one:1.6 + ports: + - "5775:5775/udp" + - "6831:6831/udp" + - "6832:6832/udp" + - "5778:5778" + - "14267" + - "14268:14268" + - "14269" + - "16686:16686" + - "16687" + - "9411:9411" + restart: on-failure diff --git a/chapter-05/exercise1/chat-api/pom.xml b/chapter-05/exercise1/chat-api/pom.xml new file mode 100644 index 0000000..41826ba --- /dev/null +++ b/chapter-05/exercise1/chat-api/pom.xml @@ -0,0 +1,57 @@ + + + 4.0.0 + + com.packt.distributed-tracing-chapter-05 + chat-api-1 + jar + + + com.packt.distributed-tracing-chapter-05 + exercise1 + 0.0.1-SNAPSHOT + ../ + + + + UTF-8 + UTF-8 + 1.8 + ${project.basedir}/.. + + + + + com.packt.distributed-tracing-chapter-05 + lib + + + org.springframework.boot + spring-boot-starter-web + + + io.opentracing.contrib + opentracing-spring-cloud-starter + + + io.opentracing.contrib + opentracing-spring-tracer-configuration-starter + + + io.jaegertracing + jaeger-client + + + + + + ../../webapp + + public/** + + + + + + diff --git a/chapter-05/exercise1/chat-api/src/main/java/chat/App.java b/chapter-05/exercise1/chat-api/src/main/java/chat/App.java new file mode 100644 index 0000000..2d14ede --- /dev/null +++ b/chapter-05/exercise1/chat-api/src/main/java/chat/App.java @@ -0,0 +1,21 @@ +package chat; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.ComponentScan; + +import lib.AppId; + +@SpringBootApplication +@ComponentScan(basePackages = {"lib", "chat"}) +public class App { + @Bean + public AppId appId() { + return new AppId("chat-api"); + } + + public static void main(String[] args) { + SpringApplication.run(App.class, args); + } +} \ No newline at end of file diff --git a/chapter-05/exercise1/chat-api/src/main/java/chat/ChatController.java b/chapter-05/exercise1/chat-api/src/main/java/chat/ChatController.java new file mode 100644 index 0000000..1d122f3 --- /dev/null +++ b/chapter-05/exercise1/chat-api/src/main/java/chat/ChatController.java @@ -0,0 +1,85 @@ +package chat; + +import java.util.List; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.Executor; + +import org.apache.kafka.clients.producer.ProducerRecord; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.annotation.Bean; +import org.springframework.http.HttpStatus; +import org.springframework.http.MediaType; +import org.springframework.http.ResponseEntity; +import org.springframework.kafka.core.KafkaTemplate; +import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor; +import org.springframework.stereotype.Controller; +import org.springframework.web.bind.annotation.CrossOrigin; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestMethod; +import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.bind.annotation.ResponseBody; +import org.springframework.web.bind.annotation.RestController; + +import lib.KafkaService; +import lib.Message; +import lib.RedisService; + +@CrossOrigin(maxAge = 3600) +@RestController +@Controller +public class ChatController { + + @Autowired + KafkaTemplate kafkaTemplate; + + @Autowired + RedisService redis; + + @Autowired + KafkaService kafka; + + @Bean + public Executor asyncExecutor() { + ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor(); + executor.setCorePoolSize(2); + executor.setMaxPoolSize(2); + executor.setQueueCapacity(10); + executor.setThreadNamePrefix("send-to-kafka-"); + executor.initialize(); + return executor; + } + + Executor executor1 = asyncExecutor(); + + @Autowired + Executor executor2; + + private String sendMode = System.getProperty("KSEND"); + + @RequestMapping(value = "/message", method = RequestMethod.GET) + public @ResponseBody List getMessages(@RequestParam(value = "room", defaultValue = "lobby") String room) + throws Exception { + List messages = redis.getMessages(room); + System.out.println("Retrieved " + messages.size() + " messages."); + return messages; + } + + @RequestMapping(value = "/message", consumes = { "application/json" }, produces = { + MediaType.APPLICATION_JSON_VALUE }, method = RequestMethod.POST) + public ResponseEntity postMessage(@RequestBody Message msg) throws Exception { + msg.init(); + System.out.println("Received message: " + msg); + if ("async1".equals(sendMode)) { + kafka.sendMessageAsync(msg, executor1); + System.out.println("Message sent async (executor1) to Kafka"); + } else if ("async2".equals(sendMode)) { + kafka.sendMessageAsync(msg, executor2); + System.out.println("Message sent async (executor2) to Kafka"); + } else { + kafka.sendMessage(msg); + System.out.println("Message sent sync to Kafka"); + } + return new ResponseEntity(msg, HttpStatus.OK); + } +} \ No newline at end of file diff --git a/chapter-05/exercise1/giphy-service/pom.xml b/chapter-05/exercise1/giphy-service/pom.xml new file mode 100644 index 0000000..4446c73 --- /dev/null +++ b/chapter-05/exercise1/giphy-service/pom.xml @@ -0,0 +1,42 @@ + + + 4.0.0 + + com.packt.distributed-tracing-chapter-05 + giphy-service-1 + jar + + + com.packt.distributed-tracing-chapter-05 + exercise1 + 0.0.1-SNAPSHOT + ../ + + + + UTF-8 + UTF-8 + 1.8 + ${project.basedir}/.. + + + + + com.packt.distributed-tracing-chapter-05 + lib + + + io.opentracing.contrib + opentracing-spring-cloud-starter + + + io.opentracing.contrib + opentracing-spring-tracer-configuration-starter + + + io.jaegertracing + jaeger-client + + + diff --git a/chapter-05/exercise1/giphy-service/src/main/java/giphy/App.java b/chapter-05/exercise1/giphy-service/src/main/java/giphy/App.java new file mode 100644 index 0000000..a9f89cb --- /dev/null +++ b/chapter-05/exercise1/giphy-service/src/main/java/giphy/App.java @@ -0,0 +1,61 @@ +package giphy; + +import org.apache.kafka.clients.producer.ProducerRecord; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.ComponentScan; +import org.springframework.kafka.annotation.EnableKafka; +import org.springframework.kafka.annotation.KafkaListener; +import org.springframework.kafka.core.KafkaTemplate; +import org.springframework.messaging.MessageHeaders; +import org.springframework.messaging.handler.annotation.Headers; +import org.springframework.messaging.handler.annotation.Payload; + +import io.opentracing.Scope; +import io.opentracing.Span; +import io.opentracing.Tracer; +import lib.AppId; +import lib.GiphyService; +import lib.KafkaService; +import lib.Message; + +@EnableKafka +@SpringBootApplication +@ComponentScan(basePackages="lib") +public class App { + @Bean + public AppId appId() { + return new AppId("giphy-service"); + } + + @Autowired + GiphyService giphy; + + @Autowired + KafkaService kafka; + + @Autowired + Tracer tracer; + + @KafkaListener(topics = "message") + public void process(@Payload Message message, @Headers MessageHeaders headers) throws Exception { + Span span = kafka.startConsumerSpan("process", headers); + try (Scope scope = tracer.scopeManager().activate(span, true)) { + System.out.println("Received message: " + message.message); + if (message.image == null && message.message.trim().startsWith("/giphy")) { + String query = message.message.split("/giphy")[1].trim(); + System.out.println("Giphy requested: " + query); + String gifUrl = giphy.query(query); + message.image =gifUrl; + kafka.sendMessage(message); + System.out.println("Updated message, url=" + gifUrl); + } + } + } + + public static void main(String[] args) throws Exception { + SpringApplication.run(App.class, args); + } +} diff --git a/chapter-05/exercise1/pom.xml b/chapter-05/exercise1/pom.xml new file mode 100644 index 0000000..c59b142 --- /dev/null +++ b/chapter-05/exercise1/pom.xml @@ -0,0 +1,24 @@ + + + 4.0.0 + + com.packt.distributed-tracing-chapter-05 + exercise1 + pom + + + chat-api + giphy-service + storage-service + + + + com.packt.distributed-tracing-chapter-05 + parent + 0.0.1-SNAPSHOT + ../ + + + + diff --git a/chapter-05/exercise1/storage-service/pom.xml b/chapter-05/exercise1/storage-service/pom.xml new file mode 100644 index 0000000..3d19a9b --- /dev/null +++ b/chapter-05/exercise1/storage-service/pom.xml @@ -0,0 +1,46 @@ + + + 4.0.0 + + com.packt.distributed-tracing-chapter-05 + storage-service-1 + jar + + + com.packt.distributed-tracing-chapter-05 + exercise1 + 0.0.1-SNAPSHOT + ../ + + + + UTF-8 + UTF-8 + 1.8 + ${project.basedir}/.. + + + + + com.packt.distributed-tracing-chapter-05 + lib + + + org.springframework + spring-web + + + io.opentracing.contrib + opentracing-spring-cloud-starter + + + io.opentracing.contrib + opentracing-spring-tracer-configuration-starter + + + io.jaegertracing + jaeger-client + + + diff --git a/chapter-05/exercise1/storage-service/src/main/java/storage/App.java b/chapter-05/exercise1/storage-service/src/main/java/storage/App.java new file mode 100644 index 0000000..83b9f9b --- /dev/null +++ b/chapter-05/exercise1/storage-service/src/main/java/storage/App.java @@ -0,0 +1,53 @@ +package storage; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.ComponentScan; +import org.springframework.kafka.annotation.EnableKafka; +import org.springframework.kafka.annotation.KafkaListener; +import org.springframework.messaging.MessageHeaders; +import org.springframework.messaging.handler.annotation.Headers; +import org.springframework.messaging.handler.annotation.Payload; + +import io.opentracing.Scope; +import io.opentracing.Span; +import io.opentracing.Tracer; +import lib.AppId; +import lib.KafkaService; +import lib.Message; +import lib.RedisService; + +@EnableKafka +@SpringBootApplication +@ComponentScan(basePackages="lib") +public class App { + @Autowired + RedisService redis; + + @Autowired + KafkaService kafka; + + @Autowired + Tracer tracer; + + @Bean + public AppId appId() { + return new AppId("storage-service"); + } + + @KafkaListener(topics = "message") + public void process(@Payload Message message, @Headers MessageHeaders headers) throws Exception { + Span span = kafka.startConsumerSpan("process", headers); + try (Scope scope = tracer.scopeManager().activate(span, true)) { + System.out.println("Received message: " + message.message); + redis.addMessage(message); + System.out.println("Added message to room."); + } + } + + public static void main(String[] args) throws Exception { + SpringApplication.run(App.class, args); + } +} diff --git a/chapter-05/lib/pom.xml b/chapter-05/lib/pom.xml new file mode 100644 index 0000000..f4d770f --- /dev/null +++ b/chapter-05/lib/pom.xml @@ -0,0 +1,56 @@ + + + 4.0.0 + + com.packt.distributed-tracing-chapter-05 + lib + + + com.packt.distributed-tracing-chapter-05 + parent + 0.0.1-SNAPSHOT + ../ + + + + UTF-8 + UTF-8 + 1.8 + + + + + org.springframework.boot + spring-boot-starter + + + org.springframework + spring-web + + + org.springframework.kafka + spring-kafka + + + org.apache.kafka + kafka-clients + + + com.fasterxml.jackson.core + jackson-databind + + + io.opentracing + opentracing-api + + + io.opentracing.contrib + opentracing-kafka-spring + + + io.opentracing.contrib + opentracing-redis-lettuce + + + diff --git a/chapter-05/lib/src/main/java/lib/AppId.java b/chapter-05/lib/src/main/java/lib/AppId.java new file mode 100644 index 0000000..324fdc6 --- /dev/null +++ b/chapter-05/lib/src/main/java/lib/AppId.java @@ -0,0 +1,9 @@ +package lib; + +public class AppId { + public final String name; + + public AppId(String name) { + this.name = name; + } +} \ No newline at end of file diff --git a/chapter-05/lib/src/main/java/lib/GiphyService.java b/chapter-05/lib/src/main/java/lib/GiphyService.java new file mode 100644 index 0000000..de4b8ea --- /dev/null +++ b/chapter-05/lib/src/main/java/lib/GiphyService.java @@ -0,0 +1,69 @@ +package lib; + +import java.net.URI; +import java.util.Collections; +import java.util.List; +import java.util.stream.Collectors; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.annotation.Bean; +import org.springframework.http.ResponseEntity; +import org.springframework.stereotype.Service; +import org.springframework.web.client.RestTemplate; +import org.springframework.web.util.UriComponentsBuilder; + +@Service +public class GiphyService { + private final static String GIPHY_URL = "http://api.giphy.com/v1/gifs/search"; + private final static String GIPHY_API_TOKEN = "DhFQzq6E4uSzDgx6FFmTC0xqV0iFYDFK"; + + @Bean + public RestTemplate restTemplate() { + return new RestTemplate(); + } + + @Autowired + RestTemplate restTemplate; + + public String query(String query) { + try { + URI uri = UriComponentsBuilder // + .fromHttpUrl(GIPHY_URL) // + .queryParam("q", query) // + .queryParam("api_key", GIPHY_API_TOKEN) // + .queryParam("limit", "10") // + .queryParam("rating", "pg") // + .build(Collections.emptyMap()); + ResponseEntity response = restTemplate.getForEntity(uri, Response.class); + List urls = response.getBody().data.stream().filter( + d -> d.images != null && d.images.original != null && d.images.original.url != null + ).map(d -> d.images.original.url).collect(Collectors.toList()); + if (urls.isEmpty()) { + return null; + } + System.out.println("Giphy returned " + urls.size() + " images for " + query); + int pick = (int)(Math.random() * urls.size()); + String url = urls.get(pick); + return url; + } catch (Exception e) { + e.printStackTrace(); + } + return null; + } + + public static class Response { + public List data; + } + + public static class Data { + public Images images; + } + + public static class Images { + public Image original; + } + + public static class Image { + public String url; + } +} \ No newline at end of file diff --git a/chapter-05/lib/src/main/java/lib/KafkaConfig.java b/chapter-05/lib/src/main/java/lib/KafkaConfig.java new file mode 100644 index 0000000..23b888f --- /dev/null +++ b/chapter-05/lib/src/main/java/lib/KafkaConfig.java @@ -0,0 +1,81 @@ +package lib; + +import java.net.InetAddress; +import java.util.HashMap; +import java.util.Map; + +import org.apache.kafka.clients.consumer.ConsumerConfig; +import org.apache.kafka.clients.producer.ProducerConfig; +import org.apache.kafka.common.serialization.StringDeserializer; +import org.apache.kafka.common.serialization.StringSerializer; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.kafka.config.ConcurrentKafkaListenerContainerFactory; +import org.springframework.kafka.core.ConsumerFactory; +import org.springframework.kafka.core.DefaultKafkaConsumerFactory; +import org.springframework.kafka.core.DefaultKafkaProducerFactory; +import org.springframework.kafka.core.KafkaTemplate; +import org.springframework.kafka.core.ProducerFactory; +import org.springframework.kafka.support.serializer.JsonDeserializer; +import org.springframework.kafka.support.serializer.JsonSerializer; + +import io.opentracing.Tracer; +import io.opentracing.contrib.kafka.spring.TracingConsumerFactory; +import io.opentracing.contrib.kafka.spring.TracingProducerFactory; + +@Configuration +public class KafkaConfig { + + @Autowired + AppId app; + + @Autowired + Tracer tracer; + + @Bean + public Object kafkaListenerContainerFactory() throws Exception { + ConcurrentKafkaListenerContainerFactory factory = // + new ConcurrentKafkaListenerContainerFactory<>(); + factory.setConsumerFactory(consumerFactory()); + return factory; + } + + @Bean + public KafkaTemplate kafkaTemplate() throws Exception { + return new KafkaTemplate<>(producerFactory()); + } + + private String clientId() throws Exception { + return InetAddress.getLocalHost().getHostName() + "-" + app.name; + } + + private ConsumerFactory consumerFactory() throws Exception { + Map props = new HashMap<>(); + props.put(ConsumerConfig.BOOTSTRAP_SERVERS_CONFIG, "localhost:9092"); + props.put(ConsumerConfig.CLIENT_ID_CONFIG, clientId()); + props.put(ConsumerConfig.GROUP_ID_CONFIG, app.name); + props.put(ConsumerConfig.AUTO_OFFSET_RESET_CONFIG, "earliest"); + props.put(ConsumerConfig.MAX_POLL_RECORDS_CONFIG, 1000); + props.put(ConsumerConfig.AUTO_COMMIT_INTERVAL_MS_CONFIG, "100"); + props.put(ConsumerConfig.ENABLE_AUTO_COMMIT_CONFIG, true); + props.put(ConsumerConfig.SESSION_TIMEOUT_MS_CONFIG, "15000"); + + return new TracingConsumerFactory<>( // + new DefaultKafkaConsumerFactory( // + props, // + new StringDeserializer(), // + new JsonDeserializer<>(Message.class))); + } + + private ProducerFactory producerFactory() throws Exception { + Map props = new HashMap<>(); + props.put(ProducerConfig.BOOTSTRAP_SERVERS_CONFIG, "localhost:9092"); + props.put(ProducerConfig.CLIENT_ID_CONFIG, clientId()); + ProducerFactory producer = // + new DefaultKafkaProducerFactory(props, // + new StringSerializer(), // + new JsonSerializer()); + return new TracingProducerFactory(producer, tracer); + } +} diff --git a/chapter-05/lib/src/main/java/lib/KafkaService.java b/chapter-05/lib/src/main/java/lib/KafkaService.java new file mode 100644 index 0000000..1855d28 --- /dev/null +++ b/chapter-05/lib/src/main/java/lib/KafkaService.java @@ -0,0 +1,88 @@ +package lib; + +import java.nio.charset.StandardCharsets; +import java.util.HashMap; +import java.util.Iterator; +import java.util.Map; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.Executor; + +import org.apache.kafka.clients.producer.ProducerRecord; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.kafka.core.KafkaTemplate; +import org.springframework.messaging.MessageHeaders; +import org.springframework.stereotype.Service; + +import io.opentracing.References; +import io.opentracing.Span; +import io.opentracing.SpanContext; +import io.opentracing.Tracer; +import io.opentracing.propagation.Format; +import io.opentracing.propagation.TextMap; + +@Service +public class KafkaService { + private static final String TOPIC = "message"; + + @Autowired + Tracer tracer; + + @Autowired + KafkaTemplate kafkaTemplate; + + public void sendMessage(Message message) throws Exception { + ProducerRecord record = new ProducerRecord<>(TOPIC, message); + kafkaTemplate.send(record).get(); + } + + public Span startConsumerSpan(String name, MessageHeaders headers) { + TextMap carrier = new MessageHeadersExtractAdapter(headers); + SpanContext parent = tracer.extract(Format.Builtin.TEXT_MAP, carrier); + return tracer.buildSpan(name) // + .addReference(References.FOLLOWS_FROM, parent) // + .start(); + } + + public void sendMessageAsync(Message message, Executor executor) throws Exception { + CompletableFuture.supplyAsync(() -> { + ProducerRecord record = new ProducerRecord<>(TOPIC, message); + kafkaTemplate.send(record); + return message.id; + }, executor).get(); + } + + /** + * An adapter from Spring MessageHeaders to OpenTracing TextMap carrier. It + * relies on the behavior of + * io.opentracing.contrib.kafka.spring.TracingConsumerFactory that stores the + * context of the "receive" span in the headers with "second_span_" prefix. + */ + private static class MessageHeadersExtractAdapter implements TextMap { + + private final Map map = new HashMap<>(); + + MessageHeadersExtractAdapter(MessageHeaders headers) { + for (Map.Entry header : headers.entrySet()) { + if (!header.getKey().startsWith("second_span_")) { + continue; + } + if (!(header.getValue() instanceof byte[])) { + continue; + } + String key = header.getKey().replaceFirst("^second_span_", ""); + String value = new String((byte[]) header.getValue(), StandardCharsets.UTF_8); + map.put(key, value); + } + } + + @Override + public Iterator> iterator() { + return map.entrySet().iterator(); + } + + @Override + public void put(String key, String value) { + throw new UnsupportedOperationException("should only be used with Tracer.extract()"); + } + } +} diff --git a/chapter-05/lib/src/main/java/lib/Message.java b/chapter-05/lib/src/main/java/lib/Message.java new file mode 100644 index 0000000..e874975 --- /dev/null +++ b/chapter-05/lib/src/main/java/lib/Message.java @@ -0,0 +1,31 @@ +package lib; + +import java.text.DateFormat; +import java.text.SimpleDateFormat; +import java.util.Date; +import java.util.TimeZone; +import java.util.UUID; + +public class Message { + public String event; + public String id; + public String author; + public String message; + public String room; + public String date; + public String image; + + public void init() { + this.id = UUID.randomUUID().toString(); + TimeZone tz = TimeZone.getTimeZone("UTC"); + // Quoted "Z" to indicate UTC, no timezone offset + DateFormat df = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss'Z'"); + df.setTimeZone(tz); + this.date = df.format(new Date()); + } + + @Override + public String toString() { + return this.id + " - " + this.message; + } +} \ No newline at end of file diff --git a/chapter-05/lib/src/main/java/lib/RedisConfig.java b/chapter-05/lib/src/main/java/lib/RedisConfig.java new file mode 100644 index 0000000..61baae6 --- /dev/null +++ b/chapter-05/lib/src/main/java/lib/RedisConfig.java @@ -0,0 +1,32 @@ +package lib; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +import io.lettuce.core.RedisClient; +import io.lettuce.core.api.StatefulRedisConnection; +import io.lettuce.core.api.sync.RedisCommands; +import io.opentracing.Tracer; +import io.opentracing.contrib.redis.lettuce.TracingStatefulRedisConnection; + +@Configuration +public class RedisConfig { + @Autowired + Tracer tracer; + + @Bean + public StatefulRedisConnection redisConn() { + RedisClient client = RedisClient.create("redis://localhost"); + return new TracingStatefulRedisConnection<>( // + client.connect(), tracer, false); + } + + @Autowired + StatefulRedisConnection redisConn; + + @Bean + public RedisCommands redisClientSync() { + return redisConn.sync(); + } +} diff --git a/chapter-05/lib/src/main/java/lib/RedisService.java b/chapter-05/lib/src/main/java/lib/RedisService.java new file mode 100644 index 0000000..9fd1616 --- /dev/null +++ b/chapter-05/lib/src/main/java/lib/RedisService.java @@ -0,0 +1,44 @@ +package lib; + +import java.time.Instant; +import java.util.ArrayList; +import java.util.List; + +import com.fasterxml.jackson.databind.ObjectMapper; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +import io.lettuce.core.api.sync.RedisCommands; + +@Service +public class RedisService { + @Autowired + RedisCommands syncCommands; + + public void addMessage(Message message) throws Exception { + ObjectMapper objectMapper = new ObjectMapper(); + String jsonString = objectMapper.writeValueAsString(message); + + syncCommands.set("message:" + message.id, jsonString); + + Long epoch = Instant.parse(message.date).getEpochSecond(); + syncCommands.zadd(message.room, epoch.doubleValue(), message.id); + } + + public List getMessages(String room) throws Exception { + List ids = syncCommands.zrange(room, 0, -1); + List messages = new ArrayList(ids.size()); + for (String id : ids) { + try { + String jsonString = syncCommands.get("message:" + id); + ObjectMapper objectMapper = new ObjectMapper(); + Message message = objectMapper.readValue(jsonString, Message.class); + messages.add(message); + } catch (Exception e) { + e.printStackTrace(); + } + } + return messages; + } +} diff --git a/chapter-05/mvnw b/chapter-05/mvnw new file mode 100755 index 0000000..5551fde --- /dev/null +++ b/chapter-05/mvnw @@ -0,0 +1,286 @@ +#!/bin/sh +# ---------------------------------------------------------------------------- +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +# ---------------------------------------------------------------------------- + +# ---------------------------------------------------------------------------- +# Maven2 Start Up Batch script +# +# Required ENV vars: +# ------------------ +# JAVA_HOME - location of a JDK home dir +# +# Optional ENV vars +# ----------------- +# M2_HOME - location of maven2's installed home dir +# MAVEN_OPTS - parameters passed to the Java VM when running Maven +# e.g. to debug Maven itself, use +# set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000 +# MAVEN_SKIP_RC - flag to disable loading of mavenrc files +# ---------------------------------------------------------------------------- + +if [ -z "$MAVEN_SKIP_RC" ] ; then + + if [ -f /etc/mavenrc ] ; then + . /etc/mavenrc + fi + + if [ -f "$HOME/.mavenrc" ] ; then + . "$HOME/.mavenrc" + fi + +fi + +# OS specific support. $var _must_ be set to either true or false. +cygwin=false; +darwin=false; +mingw=false +case "`uname`" in + CYGWIN*) cygwin=true ;; + MINGW*) mingw=true;; + Darwin*) darwin=true + # Use /usr/libexec/java_home if available, otherwise fall back to /Library/Java/Home + # See https://developer.apple.com/library/mac/qa/qa1170/_index.html + if [ -z "$JAVA_HOME" ]; then + if [ -x "/usr/libexec/java_home" ]; then + export JAVA_HOME="`/usr/libexec/java_home`" + else + export JAVA_HOME="/Library/Java/Home" + fi + fi + ;; +esac + +if [ -z "$JAVA_HOME" ] ; then + if [ -r /etc/gentoo-release ] ; then + JAVA_HOME=`java-config --jre-home` + fi +fi + +if [ -z "$M2_HOME" ] ; then + ## resolve links - $0 may be a link to maven's home + PRG="$0" + + # need this for relative symlinks + while [ -h "$PRG" ] ; do + ls=`ls -ld "$PRG"` + link=`expr "$ls" : '.*-> \(.*\)$'` + if expr "$link" : '/.*' > /dev/null; then + PRG="$link" + else + PRG="`dirname "$PRG"`/$link" + fi + done + + saveddir=`pwd` + + M2_HOME=`dirname "$PRG"`/.. + + # make it fully qualified + M2_HOME=`cd "$M2_HOME" && pwd` + + cd "$saveddir" + # echo Using m2 at $M2_HOME +fi + +# For Cygwin, ensure paths are in UNIX format before anything is touched +if $cygwin ; then + [ -n "$M2_HOME" ] && + M2_HOME=`cygpath --unix "$M2_HOME"` + [ -n "$JAVA_HOME" ] && + JAVA_HOME=`cygpath --unix "$JAVA_HOME"` + [ -n "$CLASSPATH" ] && + CLASSPATH=`cygpath --path --unix "$CLASSPATH"` +fi + +# For Mingw, ensure paths are in UNIX format before anything is touched +if $mingw ; then + [ -n "$M2_HOME" ] && + M2_HOME="`(cd "$M2_HOME"; pwd)`" + [ -n "$JAVA_HOME" ] && + JAVA_HOME="`(cd "$JAVA_HOME"; pwd)`" + # TODO classpath? +fi + +if [ -z "$JAVA_HOME" ]; then + javaExecutable="`which javac`" + if [ -n "$javaExecutable" ] && ! [ "`expr \"$javaExecutable\" : '\([^ ]*\)'`" = "no" ]; then + # readlink(1) is not available as standard on Solaris 10. + readLink=`which readlink` + if [ ! `expr "$readLink" : '\([^ ]*\)'` = "no" ]; then + if $darwin ; then + javaHome="`dirname \"$javaExecutable\"`" + javaExecutable="`cd \"$javaHome\" && pwd -P`/javac" + else + javaExecutable="`readlink -f \"$javaExecutable\"`" + fi + javaHome="`dirname \"$javaExecutable\"`" + javaHome=`expr "$javaHome" : '\(.*\)/bin'` + JAVA_HOME="$javaHome" + export JAVA_HOME + fi + fi +fi + +if [ -z "$JAVACMD" ] ; then + if [ -n "$JAVA_HOME" ] ; then + if [ -x "$JAVA_HOME/jre/sh/java" ] ; then + # IBM's JDK on AIX uses strange locations for the executables + JAVACMD="$JAVA_HOME/jre/sh/java" + else + JAVACMD="$JAVA_HOME/bin/java" + fi + else + JAVACMD="`which java`" + fi +fi + +if [ ! -x "$JAVACMD" ] ; then + echo "Error: JAVA_HOME is not defined correctly." >&2 + echo " We cannot execute $JAVACMD" >&2 + exit 1 +fi + +if [ -z "$JAVA_HOME" ] ; then + echo "Warning: JAVA_HOME environment variable is not set." +fi + +CLASSWORLDS_LAUNCHER=org.codehaus.plexus.classworlds.launcher.Launcher + +# traverses directory structure from process work directory to filesystem root +# first directory with .mvn subdirectory is considered project base directory +find_maven_basedir() { + + if [ -z "$1" ] + then + echo "Path not specified to find_maven_basedir" + return 1 + fi + + basedir="$1" + wdir="$1" + while [ "$wdir" != '/' ] ; do + if [ -d "$wdir"/.mvn ] ; then + basedir=$wdir + break + fi + # workaround for JBEAP-8937 (on Solaris 10/Sparc) + if [ -d "${wdir}" ]; then + wdir=`cd "$wdir/.."; pwd` + fi + # end of workaround + done + echo "${basedir}" +} + +# concatenates all lines of a file +concat_lines() { + if [ -f "$1" ]; then + echo "$(tr -s '\n' ' ' < "$1")" + fi +} + +BASE_DIR=`find_maven_basedir "$(pwd)"` +if [ -z "$BASE_DIR" ]; then + exit 1; +fi + +########################################################################################## +# Extension to allow automatically downloading the maven-wrapper.jar from Maven-central +# This allows using the maven wrapper in projects that prohibit checking in binary data. +########################################################################################## +if [ -r "$BASE_DIR/.mvn/wrapper/maven-wrapper.jar" ]; then + if [ "$MVNW_VERBOSE" = true ]; then + echo "Found .mvn/wrapper/maven-wrapper.jar" + fi +else + if [ "$MVNW_VERBOSE" = true ]; then + echo "Couldn't find .mvn/wrapper/maven-wrapper.jar, downloading it ..." + fi + jarUrl="https://repo.maven.apache.org/maven2/io/takari/maven-wrapper/0.4.2/maven-wrapper-0.4.2.jar" + while IFS="=" read key value; do + case "$key" in (wrapperUrl) jarUrl="$value"; break ;; + esac + done < "$BASE_DIR/.mvn/wrapper/maven-wrapper.properties" + if [ "$MVNW_VERBOSE" = true ]; then + echo "Downloading from: $jarUrl" + fi + wrapperJarPath="$BASE_DIR/.mvn/wrapper/maven-wrapper.jar" + + if command -v wget > /dev/null; then + if [ "$MVNW_VERBOSE" = true ]; then + echo "Found wget ... using wget" + fi + wget "$jarUrl" -O "$wrapperJarPath" + elif command -v curl > /dev/null; then + if [ "$MVNW_VERBOSE" = true ]; then + echo "Found curl ... using curl" + fi + curl -o "$wrapperJarPath" "$jarUrl" + else + if [ "$MVNW_VERBOSE" = true ]; then + echo "Falling back to using Java to download" + fi + javaClass="$BASE_DIR/.mvn/wrapper/MavenWrapperDownloader.java" + if [ -e "$javaClass" ]; then + if [ ! -e "$BASE_DIR/.mvn/wrapper/MavenWrapperDownloader.class" ]; then + if [ "$MVNW_VERBOSE" = true ]; then + echo " - Compiling MavenWrapperDownloader.java ..." + fi + # Compiling the Java class + ("$JAVA_HOME/bin/javac" "$javaClass") + fi + if [ -e "$BASE_DIR/.mvn/wrapper/MavenWrapperDownloader.class" ]; then + # Running the downloader + if [ "$MVNW_VERBOSE" = true ]; then + echo " - Running MavenWrapperDownloader.java ..." + fi + ("$JAVA_HOME/bin/java" -cp .mvn/wrapper MavenWrapperDownloader "$MAVEN_PROJECTBASEDIR") + fi + fi + fi +fi +########################################################################################## +# End of extension +########################################################################################## + +export MAVEN_PROJECTBASEDIR=${MAVEN_BASEDIR:-"$BASE_DIR"} +if [ "$MVNW_VERBOSE" = true ]; then + echo $MAVEN_PROJECTBASEDIR +fi +MAVEN_OPTS="$(concat_lines "$MAVEN_PROJECTBASEDIR/.mvn/jvm.config") $MAVEN_OPTS" + +# For Cygwin, switch paths to Windows format before running java +if $cygwin; then + [ -n "$M2_HOME" ] && + M2_HOME=`cygpath --path --windows "$M2_HOME"` + [ -n "$JAVA_HOME" ] && + JAVA_HOME=`cygpath --path --windows "$JAVA_HOME"` + [ -n "$CLASSPATH" ] && + CLASSPATH=`cygpath --path --windows "$CLASSPATH"` + [ -n "$MAVEN_PROJECTBASEDIR" ] && + MAVEN_PROJECTBASEDIR=`cygpath --path --windows "$MAVEN_PROJECTBASEDIR"` +fi + +WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain + +exec "$JAVACMD" \ + $MAVEN_OPTS \ + -classpath "$MAVEN_PROJECTBASEDIR/.mvn/wrapper/maven-wrapper.jar" \ + "-Dmaven.home=${M2_HOME}" "-Dmaven.multiModuleProjectDirectory=${MAVEN_PROJECTBASEDIR}" \ + ${WRAPPER_LAUNCHER} $MAVEN_CONFIG "$@" diff --git a/chapter-05/mvnw.cmd b/chapter-05/mvnw.cmd new file mode 100755 index 0000000..48363fa --- /dev/null +++ b/chapter-05/mvnw.cmd @@ -0,0 +1,161 @@ +@REM ---------------------------------------------------------------------------- +@REM Licensed to the Apache Software Foundation (ASF) under one +@REM or more contributor license agreements. See the NOTICE file +@REM distributed with this work for additional information +@REM regarding copyright ownership. The ASF licenses this file +@REM to you under the Apache License, Version 2.0 (the +@REM "License"); you may not use this file except in compliance +@REM with the License. You may obtain a copy of the License at +@REM +@REM http://www.apache.org/licenses/LICENSE-2.0 +@REM +@REM Unless required by applicable law or agreed to in writing, +@REM software distributed under the License is distributed on an +@REM "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +@REM KIND, either express or implied. See the License for the +@REM specific language governing permissions and limitations +@REM under the License. +@REM ---------------------------------------------------------------------------- + +@REM ---------------------------------------------------------------------------- +@REM Maven2 Start Up Batch script +@REM +@REM Required ENV vars: +@REM JAVA_HOME - location of a JDK home dir +@REM +@REM Optional ENV vars +@REM M2_HOME - location of maven2's installed home dir +@REM MAVEN_BATCH_ECHO - set to 'on' to enable the echoing of the batch commands +@REM MAVEN_BATCH_PAUSE - set to 'on' to wait for a key stroke before ending +@REM MAVEN_OPTS - parameters passed to the Java VM when running Maven +@REM e.g. to debug Maven itself, use +@REM set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000 +@REM MAVEN_SKIP_RC - flag to disable loading of mavenrc files +@REM ---------------------------------------------------------------------------- + +@REM Begin all REM lines with '@' in case MAVEN_BATCH_ECHO is 'on' +@echo off +@REM set title of command window +title %0 +@REM enable echoing my setting MAVEN_BATCH_ECHO to 'on' +@if "%MAVEN_BATCH_ECHO%" == "on" echo %MAVEN_BATCH_ECHO% + +@REM set %HOME% to equivalent of $HOME +if "%HOME%" == "" (set "HOME=%HOMEDRIVE%%HOMEPATH%") + +@REM Execute a user defined script before this one +if not "%MAVEN_SKIP_RC%" == "" goto skipRcPre +@REM check for pre script, once with legacy .bat ending and once with .cmd ending +if exist "%HOME%\mavenrc_pre.bat" call "%HOME%\mavenrc_pre.bat" +if exist "%HOME%\mavenrc_pre.cmd" call "%HOME%\mavenrc_pre.cmd" +:skipRcPre + +@setlocal + +set ERROR_CODE=0 + +@REM To isolate internal variables from possible post scripts, we use another setlocal +@setlocal + +@REM ==== START VALIDATION ==== +if not "%JAVA_HOME%" == "" goto OkJHome + +echo. +echo Error: JAVA_HOME not found in your environment. >&2 +echo Please set the JAVA_HOME variable in your environment to match the >&2 +echo location of your Java installation. >&2 +echo. +goto error + +:OkJHome +if exist "%JAVA_HOME%\bin\java.exe" goto init + +echo. +echo Error: JAVA_HOME is set to an invalid directory. >&2 +echo JAVA_HOME = "%JAVA_HOME%" >&2 +echo Please set the JAVA_HOME variable in your environment to match the >&2 +echo location of your Java installation. >&2 +echo. +goto error + +@REM ==== END VALIDATION ==== + +:init + +@REM Find the project base dir, i.e. the directory that contains the folder ".mvn". +@REM Fallback to current working directory if not found. + +set MAVEN_PROJECTBASEDIR=%MAVEN_BASEDIR% +IF NOT "%MAVEN_PROJECTBASEDIR%"=="" goto endDetectBaseDir + +set EXEC_DIR=%CD% +set WDIR=%EXEC_DIR% +:findBaseDir +IF EXIST "%WDIR%"\.mvn goto baseDirFound +cd .. +IF "%WDIR%"=="%CD%" goto baseDirNotFound +set WDIR=%CD% +goto findBaseDir + +:baseDirFound +set MAVEN_PROJECTBASEDIR=%WDIR% +cd "%EXEC_DIR%" +goto endDetectBaseDir + +:baseDirNotFound +set MAVEN_PROJECTBASEDIR=%EXEC_DIR% +cd "%EXEC_DIR%" + +:endDetectBaseDir + +IF NOT EXIST "%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config" goto endReadAdditionalConfig + +@setlocal EnableExtensions EnableDelayedExpansion +for /F "usebackq delims=" %%a in ("%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config") do set JVM_CONFIG_MAVEN_PROPS=!JVM_CONFIG_MAVEN_PROPS! %%a +@endlocal & set JVM_CONFIG_MAVEN_PROPS=%JVM_CONFIG_MAVEN_PROPS% + +:endReadAdditionalConfig + +SET MAVEN_JAVA_EXE="%JAVA_HOME%\bin\java.exe" +set WRAPPER_JAR="%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.jar" +set WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain + +set DOWNLOAD_URL="https://repo.maven.apache.org/maven2/io/takari/maven-wrapper/0.4.2/maven-wrapper-0.4.2.jar" +FOR /F "tokens=1,2 delims==" %%A IN (%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.properties) DO ( + IF "%%A"=="wrapperUrl" SET DOWNLOAD_URL=%%B +) + +@REM Extension to allow automatically downloading the maven-wrapper.jar from Maven-central +@REM This allows using the maven wrapper in projects that prohibit checking in binary data. +if exist %WRAPPER_JAR% ( + echo Found %WRAPPER_JAR% +) else ( + echo Couldn't find %WRAPPER_JAR%, downloading it ... + echo Downloading from: %DOWNLOAD_URL% + powershell -Command "(New-Object Net.WebClient).DownloadFile('%DOWNLOAD_URL%', '%WRAPPER_JAR%')" + echo Finished downloading %WRAPPER_JAR% +) +@REM End of extension + +%MAVEN_JAVA_EXE% %JVM_CONFIG_MAVEN_PROPS% %MAVEN_OPTS% %MAVEN_DEBUG_OPTS% -classpath %WRAPPER_JAR% "-Dmaven.multiModuleProjectDirectory=%MAVEN_PROJECTBASEDIR%" %WRAPPER_LAUNCHER% %MAVEN_CONFIG% %* +if ERRORLEVEL 1 goto error +goto end + +:error +set ERROR_CODE=1 + +:end +@endlocal & set ERROR_CODE=%ERROR_CODE% + +if not "%MAVEN_SKIP_RC%" == "" goto skipRcPost +@REM check for post script, once with legacy .bat ending and once with .cmd ending +if exist "%HOME%\mavenrc_post.bat" call "%HOME%\mavenrc_post.bat" +if exist "%HOME%\mavenrc_post.cmd" call "%HOME%\mavenrc_post.cmd" +:skipRcPost + +@REM pause the script if MAVEN_BATCH_PAUSE is set to 'on' +if "%MAVEN_BATCH_PAUSE%" == "on" pause + +if "%MAVEN_TERMINATE_CMD%" == "on" exit %ERROR_CODE% + +exit /B %ERROR_CODE% diff --git a/chapter-05/pom.xml b/chapter-05/pom.xml new file mode 100644 index 0000000..798f14c --- /dev/null +++ b/chapter-05/pom.xml @@ -0,0 +1,105 @@ + + + 4.0.0 + + com.packt.distributed-tracing-chapter-05 + parent + 0.0.1-SNAPSHOT + pom + + Tracing Talk + Chapter 6 - Tracing Kafka + + + exercise1 + lib + + + + org.springframework.boot + spring-boot-starter-parent + 2.0.4.RELEASE + + + + + + UTF-8 + UTF-8 + 1.8 + + + + + + ${project.groupId} + lib + ${project.version} + + + + org.springframework.boot + spring-boot-starter-web + 2.0.4.RELEASE + + + org.springframework.kafka + spring-kafka + 2.1.8.RELEASE + + + org.apache.kafka + kafka-clients + 2.0.0 + + + com.fasterxml.jackson.core + jackson-databind + 2.9.4 + + + io.opentracing + opentracing-api + 0.31.0 + + + io.opentracing.contrib + opentracing-spring-cloud-starter + 0.1.13 + + + io.opentracing.contrib + opentracing-spring-tracer-configuration-starter + 0.1.0 + + + io.opentracing.contrib + opentracing-kafka-spring + 0.0.14 + + + io.opentracing.contrib + opentracing-redis-lettuce + 0.0.5 + + + io.jaegertracing + jaeger-client + 0.31.0 + + + + + + + + + org.springframework.boot + spring-boot-maven-plugin + + + + + + diff --git a/chapter-05/webapp/.babelrc b/chapter-05/webapp/.babelrc new file mode 100644 index 0000000..4c946e2 --- /dev/null +++ b/chapter-05/webapp/.babelrc @@ -0,0 +1,4 @@ +{ + "plugins": ["transform-class-properties"], + "presets": ["react"] +} diff --git a/chapter-05/webapp/.gitignore b/chapter-05/webapp/.gitignore new file mode 100644 index 0000000..1b29d9e --- /dev/null +++ b/chapter-05/webapp/.gitignore @@ -0,0 +1,5 @@ +.cache/ +dist/ +node_modules/ +yarn-error.log + diff --git a/chapter-05/webapp/actions/Messages.js b/chapter-05/webapp/actions/Messages.js new file mode 100644 index 0000000..ea4d666 --- /dev/null +++ b/chapter-05/webapp/actions/Messages.js @@ -0,0 +1,39 @@ +import dayjs from "dayjs" + +const chatApiUrl = "http://localhost:8080" +// const chatApiUrl = "http://16f5b228.ngrok.io" + +export const postData = (url = "", data = {}) => { + // Default options are marked with * + return fetch(url, { + method: "POST", // *GET, POST, PUT, DELETE, etc. + mode: "cors", // no-cors, cors, *same-origin + // cache: "no-cache", // *default, no-cache, reload, force-cache, only-if-cached + // credentials: "same-origin", // include, same-origin, *omit + headers: { + "Content-Type": "application/json", + Accept: "application/json" + }, + redirect: "follow", // manual, *follow, error + referrer: "no-referrer", // no-referrer, *client + body: JSON.stringify(data) // body data type must match "Content-Type" header + }) + .then(response => { + if (response.status > 400) { + throw new Error("Failed status code: " + response.status) + } + return response + }) + .then(response => response.json()) // parses response to JSON +} + +export const sendMessage = (author, room, message) => { + return postData(`${chatApiUrl}/message`, { + author, + room, + message + }) +} + +export const getMessages = () => + fetch(`${chatApiUrl}/message`).then(response => response.json()) diff --git a/chapter-05/webapp/app.js b/chapter-05/webapp/app.js new file mode 100644 index 0000000..cb43299 --- /dev/null +++ b/chapter-05/webapp/app.js @@ -0,0 +1,95 @@ +import React, { Component } from "react" +import ReactDOM from "react-dom" +import ChatRoom from "./components/ChatRoom" +import UserInput from "./components/UserInput" +import { sendMessage, getMessages } from "./actions/Messages" +import { Page, Header, HorizontalLayout } from "./components/Styled" +import ErrorBoundary from "./components/ErrorBoundary" + +class App extends Component { + state = { + title: "Tracing Talk", + user: "guest-" + Math.floor(Math.random() * 1000), + room: { + name: "lobby", + messages: [ + { + id: "a", + date: "2018-08-16T08:00:31-04:00", + author: "Ralph", + message: "👋 Hey, there!" + }, + { + id: "b", + date: "2018-08-16T08:02:31-04:00", + author: "Johnny", + message: "Hello!", + image: "https://media.giphy.com/media/mIZ9rPeMKefm0/giphy.gif" + } + ] + } + } + + componentDidMount() { + this.refreshMessages() + this.interval = setInterval(() => { + this.refreshMessages() + }, 3000) + } + + componentWillUnmount() { + clearInterval(this.interval) + } + + refreshMessages() { + return getMessages().then(messages => + this.setState(state => { + state.room.messages = messages + return state + }) + ) + } + + userInputChange(e) { + this.setState({ user: e.target.value }) + } + + send(message) { + if (message) { + sendMessage(this.state.user, this.state.room.name, message).then( + savedMessage => { + this.setState(state => { + state.room.messages.push(savedMessage) + return state + }) + } + ) + } + } + + render() { + return ( + + + {this.state.title} + +
{this.state.title}
+ this.userInputChange(e)} + /> +
+ + this.send(message)} + /> + +
+
+ ) + } +} + +ReactDOM.render(, document.getElementById("app")) diff --git a/chapter-05/webapp/components/ChatInput.js b/chapter-05/webapp/components/ChatInput.js new file mode 100644 index 0000000..734f9f8 --- /dev/null +++ b/chapter-05/webapp/components/ChatInput.js @@ -0,0 +1,38 @@ +import React, { Component } from "react" +import { Button, MessageInput } from "./Styled" +export default class ChatInput extends Component { + state = { + message: "" + } + + buttonRef = React.createRef() + onChange(e) { + this.setState({ message: e.target.value }) + } + + sendMessage(e) { + e.preventDefault() + this.props.sendMessage(this.state.message) + this.setState({ message: "" }) + } + + componentDidUpdate() { + this.buttonRef.current.scrollIntoView({ behavior: "smooth" }) + } + + render() { + return ( +
this.sendMessage(e)}> + this.onChange(e)} + /> + + + ) + } +} diff --git a/chapter-05/webapp/components/ChatRoom.js b/chapter-05/webapp/components/ChatRoom.js new file mode 100644 index 0000000..eebb470 --- /dev/null +++ b/chapter-05/webapp/components/ChatRoom.js @@ -0,0 +1,24 @@ +import React, { Component } from "react" +import { RoomName, MessageList, HorizontalLayout } from "./Styled" +import ChatInput from "./ChatInput" +import Message from "./Message" + +export default class ChatRoom extends Component { + render() { + return ( +
+ {/*{this.props.room.name}*/} + + + {this.props.room.messages.map(message => ( + + ))} + + + + + +
+ ) + } +} diff --git a/chapter-05/webapp/components/ErrorBoundary.js b/chapter-05/webapp/components/ErrorBoundary.js new file mode 100644 index 0000000..1b4721b --- /dev/null +++ b/chapter-05/webapp/components/ErrorBoundary.js @@ -0,0 +1,21 @@ +import React, { Component } from "react" + +export default class ErrorBoundary extends Component { + constructor(props) { + super(props) + this.state = { hasError: false } + } + + componentDidCatch(error, info) { + // Display fallback UI + this.setState({ hasError: true }) + } + + render() { + if (this.state.hasError) { + // You can render any custom fallback UI + return

Something went wrong. Make sure chat-api server is running.

+ } + return this.props.children + } +} diff --git a/chapter-05/webapp/components/Message.js b/chapter-05/webapp/components/Message.js new file mode 100644 index 0000000..942d6a5 --- /dev/null +++ b/chapter-05/webapp/components/Message.js @@ -0,0 +1,28 @@ +import React, { Component } from "react" +import dayjs from "dayjs" +import relativeTime from "dayjs/plugin/relativeTime" +dayjs.extend(relativeTime) +import { + TimeAgo, + Author, + MessageText, + HorizontalLayout, + MessageWrapper, + Image, + ImageWrapper +} from "./Styled" + +export default ({ message }) => ( + + + + {message.author}{" "} + {message.message} + + {dayjs(message.date).fromNow()} + + + {message.image ? : null} + + +) diff --git a/chapter-05/webapp/components/Styled.js b/chapter-05/webapp/components/Styled.js new file mode 100644 index 0000000..352e1b2 --- /dev/null +++ b/chapter-05/webapp/components/Styled.js @@ -0,0 +1,120 @@ +import styled, { injectGlobal } from "styled-components" +const mainColor = "#0a5" +const secondaryColor = "white" +const pageWidth = "40em" + +injectGlobal` + html { + background-color: white; + color: black; + font-size: 1.3vw; + font-family: 'Lato', sans-serif; + } +` + +export const Space = styled.div` + width: ${({ marginLeft }) => marginLeft}; + /* margin-left: ${({ marginLeft }) => marginLeft}; */ +` + +export const Page = styled.div` + display: flex; + justify-content: center; + flex-direction: column; +` + +export const HorizontalLayout = styled.div` + display: flex; + flex-direction: row; + align-items: flex-end; + justify-content: ${({ justifyContent = "space-between" }) => justifyContent}; +` + +export const Header = styled.div` + font-size: 5em; + font-weight: bolder; + color: ${mainColor}; +` + +export const RoomName = styled.h2` + color: ${mainColor}; +` + +export const MessageList = styled.ul` + width: ${pageWidth} + list-style-type: none; + padding-left: 0; +` + +export const MessageWrapper = styled.li` + margin-bottom: 0.5em; + &:hover { + color: ${mainColor}; + } +` + +export const Image = styled.img` + height: 10em; +` + +export const ImageWrapper = styled.div` + margin-top: 0.5em; +` + +export const Author = styled.label` + color: ${mainColor}; + /* font-size: 0.8em; */ + height: 1.1em; + width: 6em; + text-overflow: ellipsis; + white-space: nowrap; + overflow: hidden; + line-height: 100%; + /* background: brown; */ + /* text-transform: uppercase; */ +` + +export const MessageText = styled.label` + font-weight: bolder; + height: 1.1em; + line-height: 100%; +` + +export const TimeAgo = styled.label` + font-size: 0.7em; + color: gray; + line-height: 100%; +` + +export const NicknameInput = styled.input` + margin: 0; + width: 100; + height: 26; +` + +export const MessageInput = styled.input` + margin-top: 0; + width: 46.8em; + height: 32px; + line-height: 32px; + padding-left: 8px; + border: 1px solid ${mainColor}; + font-family: "Lato", sans-serif; + font-size: 0.8em; + font-weight: bold; + vertical-align: bottom; +` + +export const Button = styled.button` + margin: 0; + width: 50px; + height: 32px; + line-height: 32px; + color: ${secondaryColor}; + background-color: ${mainColor}; + font-family: "Lato", sans-serif; + text-transform: uppercase; + letter-spacing: 0.2em; + border: 0; + padding: 0; +` diff --git a/chapter-05/webapp/components/UserInput.js b/chapter-05/webapp/components/UserInput.js new file mode 100644 index 0000000..2f37c47 --- /dev/null +++ b/chapter-05/webapp/components/UserInput.js @@ -0,0 +1,52 @@ +import React, { Component } from "react" +import styled from "styled-components" + +import { + NicknameInput, + Button, + Author, + HorizontalLayout, + Space +} from "./Styled" + +export default class UserInput extends Component { + state = { + isEditing: false + } + + userRef = React.createRef() + + isEditing(e) { + e.preventDefault() + this.setState(state => { + if (this.props.user) return { isEditing: !state.isEditing } + else return state + }) + } + + render() { + return ( +
this.isEditing(e)}> + + + + {this.state.isEditing ? ( + this.props.onChange(e)} + onFocus={e => this.userRef.current.select()} + /> + ) : ( + {this.props.user} + )} + + + +
+ ) + } +} diff --git a/chapter-05/webapp/favicon.ico b/chapter-05/webapp/favicon.ico new file mode 100644 index 0000000..9a0dd01 Binary files /dev/null and b/chapter-05/webapp/favicon.ico differ diff --git a/chapter-05/webapp/index.html b/chapter-05/webapp/index.html new file mode 100644 index 0000000..61bd997 --- /dev/null +++ b/chapter-05/webapp/index.html @@ -0,0 +1,16 @@ + + + + + + + + + + + +
+ + + + \ No newline at end of file diff --git a/chapter-05/webapp/package.json b/chapter-05/webapp/package.json new file mode 100644 index 0000000..420ea8d --- /dev/null +++ b/chapter-05/webapp/package.json @@ -0,0 +1,22 @@ +{ + "name": "webapp", + "version": "1.0.0", + "main": "index.js", + "license": "MIT", + "scripts": { + "dev": "parcel index.html", + "build": "parcel build index.html" + }, + "dependencies": { + "dayjs": "^1.7.5", + "formik": "^1.0.3", + "react": "^16.4.2", + "react-dom": "^16.4.2", + "styled-components": "^3.4.2" + }, + "devDependencies": { + "babel-plugin-transform-class-properties": "^6.24.1", + "babel-preset-react": "^6.24.1", + "parcel-bundler": "^1.9.7" + } +} diff --git a/chapter-05/webapp/public/app.6b3e1769.js b/chapter-05/webapp/public/app.6b3e1769.js new file mode 100644 index 0000000..b3defaa --- /dev/null +++ b/chapter-05/webapp/public/app.6b3e1769.js @@ -0,0 +1,80 @@ +parcelRequire=function(e,r,n,t){var i="function"==typeof parcelRequire&&parcelRequire,o="function"==typeof require&&require;function u(n,t){if(!r[n]){if(!e[n]){var f="function"==typeof parcelRequire&&parcelRequire;if(!t&&f)return f(n,!0);if(i)return i(n,!0);if(o&&"string"==typeof n)return o(n);var c=new Error("Cannot find module '"+n+"'");throw c.code="MODULE_NOT_FOUND",c}p.resolve=function(r){return e[n][1][r]||r};var l=r[n]=new u.Module(n);e[n][0].call(l.exports,p,l,l.exports,this)}return r[n].exports;function p(e){return u(p.resolve(e))}}u.isParcelRequire=!0,u.Module=function(e){this.id=e,this.bundle=u,this.exports={}},u.modules=e,u.cache=r,u.parent=i,u.register=function(r,n){e[r]=[function(e,r){r.exports=n},{}]};for(var f=0;fR.length&&R.push(e)}function A(e,t,r,n){var o=typeof e;"undefined"!==o&&"boolean"!==o||(e=null);var i=!1;if(null===e)i=!0;else switch(o){case"string":case"number":i=!0;break;case"object":switch(e.$$typeof){case u:case l:i=!0}}if(i)return r(n,e,""===t?"."+E(e,0):t),1;if(i=0,t=""===t?".":t+":",Array.isArray(e))for(var f=0;fthis.eventPool.length&&this.eventPool.push(e)}function xe(e){e.eventPool=[],e.getPooled=ke,e.release=we}r(be.prototype,{preventDefault:function(){this.defaultPrevented=!0;var e=this.nativeEvent;e&&(e.preventDefault?e.preventDefault():"unknown"!=typeof e.returnValue&&(e.returnValue=!1),this.isDefaultPrevented=a.thatReturnsTrue)},stopPropagation:function(){var e=this.nativeEvent;e&&(e.stopPropagation?e.stopPropagation():"unknown"!=typeof e.cancelBubble&&(e.cancelBubble=!0),this.isPropagationStopped=a.thatReturnsTrue)},persist:function(){this.isPersistent=a.thatReturnsTrue},isPersistent:a.thatReturnsFalse,destructor:function(){var e,t=this.constructor.Interface;for(e in t)this[e]=null;for(t=0;t=Se),Ue=String.fromCharCode(32),Fe={beforeInput:{phasedRegistrationNames:{bubbled:"onBeforeInput",captured:"onBeforeInputCapture"},dependencies:["compositionend","keypress","textInput","paste"]},compositionEnd:{phasedRegistrationNames:{bubbled:"onCompositionEnd",captured:"onCompositionEndCapture"},dependencies:"blur compositionend keydown keypress keyup mousedown".split(" ")},compositionStart:{phasedRegistrationNames:{bubbled:"onCompositionStart",captured:"onCompositionStartCapture"},dependencies:"blur compositionstart keydown keypress keyup mousedown".split(" ")},compositionUpdate:{phasedRegistrationNames:{bubbled:"onCompositionUpdate",captured:"onCompositionUpdateCapture"},dependencies:"blur compositionupdate keydown keypress keyup mousedown".split(" ")}},Ie=!1;function Me(e,t){switch(e){case"keyup":return-1!==Te.indexOf(t.keyCode);case"keydown":return 229!==t.keyCode;case"keypress":case"mousedown":case"blur":return!0;default:return!1}}function Re(e){return"object"==typeof(e=e.detail)&&"data"in e?e.data:null}var ze=!1;function Oe(e,t){switch(e){case"compositionend":return Re(t);case"keypress":return 32!==t.which?null:(Ie=!0,Ue);case"textInput":return(e=t.data)===Ue&&Ie?null:e;default:return null}}function De(e,t){if(ze)return"compositionend"===e||!_e&&Me(e,t)?(e=he(),me._root=null,me._startText=null,me._fallbackText=null,ze=!1,e):null;switch(e){case"paste":return null;case"keypress":if(!(t.ctrlKey||t.altKey||t.metaKey)||t.ctrlKey&&t.altKey){if(t.char&&1t}return!1}function Ut(e,t,n,r,a){this.acceptsBooleans=2===t||3===t||4===t,this.attributeName=r,this.attributeNamespace=a,this.mustUseProperty=n,this.propertyName=e,this.type=t}var Ft={};"children dangerouslySetInnerHTML defaultValue defaultChecked innerHTML suppressContentEditableWarning suppressHydrationWarning style".split(" ").forEach(function(e){Ft[e]=new Ut(e,0,!1,e,null)}),[["acceptCharset","accept-charset"],["className","class"],["htmlFor","for"],["httpEquiv","http-equiv"]].forEach(function(e){var t=e[0];Ft[t]=new Ut(t,1,!1,e[1],null)}),["contentEditable","draggable","spellCheck","value"].forEach(function(e){Ft[e]=new Ut(e,2,!1,e.toLowerCase(),null)}),["autoReverse","externalResourcesRequired","preserveAlpha"].forEach(function(e){Ft[e]=new Ut(e,2,!1,e,null)}),"allowFullScreen async autoFocus autoPlay controls default defer disabled formNoValidate hidden loop noModule noValidate open playsInline readOnly required reversed scoped seamless itemScope".split(" ").forEach(function(e){Ft[e]=new Ut(e,3,!1,e.toLowerCase(),null)}),["checked","multiple","muted","selected"].forEach(function(e){Ft[e]=new Ut(e,3,!0,e.toLowerCase(),null)}),["capture","download"].forEach(function(e){Ft[e]=new Ut(e,4,!1,e.toLowerCase(),null)}),["cols","rows","size","span"].forEach(function(e){Ft[e]=new Ut(e,6,!1,e.toLowerCase(),null)}),["rowSpan","start"].forEach(function(e){Ft[e]=new Ut(e,5,!1,e.toLowerCase(),null)});var It=/[\-:]([a-z])/g;function Mt(e){return e[1].toUpperCase()}function Rt(e,t,n,r){var a=Ft.hasOwnProperty(t)?Ft[t]:null;(null!==a?0===a.type:!r&&(2Rn.length&&Rn.push(e)}}}var Bn={get _enabled(){return On},setEnabled:Dn,isEnabled:function(){return On},trapBubbledEvent:Ln,trapCapturedEvent:An,dispatchEvent:Wn},Vn={},Hn=0,Qn="_reactListenersID"+(""+Math.random()).slice(2);function qn(e){return Object.prototype.hasOwnProperty.call(e,Qn)||(e[Qn]=Hn++,Vn[e[Qn]]={}),Vn[e[Qn]]}function Kn(e){for(;e&&e.firstChild;)e=e.firstChild;return e}function $n(e,t){var n,r=Kn(e);for(e=0;r;){if(3===r.nodeType){if(n=e+r.textContent.length,e<=t&&n>=t)return{node:r,offset:t-e};e=n}e:{for(;r;){if(r.nextSibling){r=r.nextSibling;break e}r=r.parentNode}r=void 0}r=Kn(r)}}function Yn(e){var t=e&&e.nodeName&&e.nodeName.toLowerCase();return t&&("input"===t&&("text"===e.type||"search"===e.type||"tel"===e.type||"url"===e.type||"password"===e.type)||"textarea"===t||"true"===e.contentEditable)}var Xn=n.canUseDOM&&"documentMode"in document&&11>=document.documentMode,Gn={select:{phasedRegistrationNames:{bubbled:"onSelect",captured:"onSelectCapture"},dependencies:"blur contextmenu focus keydown keyup mousedown mouseup selectionchange".split(" ")}},Zn=null,Jn=null,er=null,tr=!1;function nr(e,t){if(tr||null==Zn||Zn!==l())return null;var n=Zn;return"selectionStart"in n&&Yn(n)?n={start:n.selectionStart,end:n.selectionEnd}:window.getSelection?n={anchorNode:(n=window.getSelection()).anchorNode,anchorOffset:n.anchorOffset,focusNode:n.focusNode,focusOffset:n.focusOffset}:n=void 0,er&&o(er,n)?null:(er=n,(e=be.getPooled(Gn.select,Jn,e,t)).type="select",e.target=Zn,J(e),e)}var rr={eventTypes:Gn,extractEvents:function(e,t,n,r){var a,l=r.window===r?r.document:9===r.nodeType?r:r.ownerDocument;if(!(a=!l)){e:{l=qn(l),a=k.onSelect;for(var o=0;ot)){e=-1;for(var n=[],r=pr;null!==r;){var a=r.timeoutTime;-1!==a&&a<=t?n.push(r):-1!==a&&(-1===e||at&&(t=8),kr=t=t.length||c("93"),t=t[0]),n=""+t),null==n&&(n="")),e._wrapperState={initialValue:""+n}}function Ir(e,t){var n=t.value;null!=n&&((n=""+n)!==e.value&&(e.value=n),null==t.defaultValue&&(e.defaultValue=n)),null!=t.defaultValue&&(e.defaultValue=t.defaultValue)}function Mr(e){var t=e.textContent;t===e._wrapperState.initialValue&&(e.value=t)}var Rr={html:"http://www.w3.org/1999/xhtml",mathml:"http://www.w3.org/1998/Math/MathML",svg:"http://www.w3.org/2000/svg"};function zr(e){switch(e){case"svg":return"http://www.w3.org/2000/svg";case"math":return"http://www.w3.org/1998/Math/MathML";default:return"http://www.w3.org/1999/xhtml"}}function Or(e,t){return null==e||"http://www.w3.org/1999/xhtml"===e?zr(t):"http://www.w3.org/2000/svg"===e&&"foreignObject"===t?"http://www.w3.org/1999/xhtml":e}var Dr=void 0,Lr=function(e){return"undefined"!=typeof MSApp&&MSApp.execUnsafeLocalFunction?function(t,n,r,a){MSApp.execUnsafeLocalFunction(function(){return e(t,n)})}:e}(function(e,t){if(e.namespaceURI!==Rr.svg||"innerHTML"in e)e.innerHTML=t;else{for((Dr=Dr||document.createElement("div")).innerHTML=""+t+"",t=Dr.firstChild;e.firstChild;)e.removeChild(e.firstChild);for(;t.firstChild;)e.appendChild(t.firstChild)}});function Ar(e,t){if(t){var n=e.firstChild;if(n&&n===e.lastChild&&3===n.nodeType)return void(n.nodeValue=t)}e.textContent=t}var jr={animationIterationCount:!0,borderImageOutset:!0,borderImageSlice:!0,borderImageWidth:!0,boxFlex:!0,boxFlexGroup:!0,boxOrdinalGroup:!0,columnCount:!0,columns:!0,flex:!0,flexGrow:!0,flexPositive:!0,flexShrink:!0,flexNegative:!0,flexOrder:!0,gridRow:!0,gridRowEnd:!0,gridRowSpan:!0,gridRowStart:!0,gridColumn:!0,gridColumnEnd:!0,gridColumnSpan:!0,gridColumnStart:!0,fontWeight:!0,lineClamp:!0,lineHeight:!0,opacity:!0,order:!0,orphans:!0,tabSize:!0,widows:!0,zIndex:!0,zoom:!0,fillOpacity:!0,floodOpacity:!0,stopOpacity:!0,strokeDasharray:!0,strokeDashoffset:!0,strokeMiterlimit:!0,strokeOpacity:!0,strokeWidth:!0},Wr=["Webkit","ms","Moz","O"];function Br(e,t){for(var n in e=e.style,t)if(t.hasOwnProperty(n)){var r=0===n.indexOf("--"),a=n,l=t[n];a=null==l||"boolean"==typeof l||""===l?"":r||"number"!=typeof l||0===l||jr.hasOwnProperty(a)&&jr[a]?(""+l).trim():l+"px","float"===n&&(n="cssFloat"),r?e.setProperty(n,a):e[n]=a}}Object.keys(jr).forEach(function(e){Wr.forEach(function(t){t=t+e.charAt(0).toUpperCase()+e.substring(1),jr[t]=jr[e]})});var Vr=r({menuitem:!0},{area:!0,base:!0,br:!0,col:!0,embed:!0,hr:!0,img:!0,input:!0,keygen:!0,link:!0,meta:!0,param:!0,source:!0,track:!0,wbr:!0});function Hr(e,t,n){t&&(Vr[e]&&(null!=t.children||null!=t.dangerouslySetInnerHTML)&&c("137",e,n()),null!=t.dangerouslySetInnerHTML&&(null!=t.children&&c("60"),"object"==typeof t.dangerouslySetInnerHTML&&"__html"in t.dangerouslySetInnerHTML||c("61")),null!=t.style&&"object"!=typeof t.style&&c("62",n()))}function Qr(e,t){if(-1===e.indexOf("-"))return"string"==typeof t.is;switch(e){case"annotation-xml":case"color-profile":case"font-face":case"font-face-src":case"font-face-uri":case"font-face-format":case"font-face-name":case"missing-glyph":return!1;default:return!0}}var qr=a.thatReturns("");function Kr(e,t){var n=qn(e=9===e.nodeType||11===e.nodeType?e:e.ownerDocument);t=k[t];for(var r=0;r<\/script>",e=e.removeChild(e.firstChild)):e="string"==typeof t.is?n.createElement(e,{is:t.is}):n.createElement(e):e=n.createElementNS(r,e),e}function Yr(e,t){return(9===t.nodeType?t:t.ownerDocument).createTextNode(e)}function Xr(e,t,n,l){var o=Qr(t,n);switch(t){case"iframe":case"object":Ln("load",e);var i=n;break;case"video":case"audio":for(i=0;ida||(e.current=fa[da],fa[da]=null,da--)}function ha(e,t){fa[++da]=e.current,e.current=t}var va=pa(u),ga=pa(!1),ya=u;function ba(e){return wa(e)?ya:va.current}function ka(e,t){var n=e.type.contextTypes;if(!n)return u;var r=e.stateNode;if(r&&r.__reactInternalMemoizedUnmaskedChildContext===t)return r.__reactInternalMemoizedMaskedChildContext;var a,l={};for(a in n)l[a]=t[a];return r&&((e=e.stateNode).__reactInternalMemoizedUnmaskedChildContext=t,e.__reactInternalMemoizedMaskedChildContext=l),l}function wa(e){return 2===e.tag&&null!=e.type.childContextTypes}function xa(e){wa(e)&&(ma(ga,e),ma(va,e))}function Ca(e){ma(ga,e),ma(va,e)}function Ea(e,t,n){va.current!==u&&c("168"),ha(va,t,e),ha(ga,n,e)}function Ta(e,t){var n=e.stateNode,a=e.type.childContextTypes;if("function"!=typeof n.getChildContext)return t;for(var l in n=n.getChildContext())l in a||c("108",wt(e)||"Unknown",l);return r({},t,n)}function _a(e){if(!wa(e))return!1;var t=e.stateNode;return t=t&&t.__reactInternalMemoizedMergedChildContext||u,ya=va.current,ha(va,t,e),ha(ga,ga.current,e),!0}function Sa(e,t){var n=e.stateNode;if(n||c("169"),t){var r=Ta(e,ya);n.__reactInternalMemoizedMergedChildContext=r,ma(ga,e),ma(va,e),ha(va,r,e)}else ma(ga,e);ha(ga,t,e)}function Na(e,t,n,r){this.tag=e,this.key=n,this.sibling=this.child=this.return=this.stateNode=this.type=null,this.index=0,this.ref=null,this.pendingProps=t,this.memoizedState=this.updateQueue=this.memoizedProps=null,this.mode=r,this.effectTag=0,this.lastEffect=this.firstEffect=this.nextEffect=null,this.expirationTime=0,this.alternate=null}function Pa(e,t,n){var r=e.alternate;return null===r?((r=new Na(e.tag,t,e.key,e.mode)).type=e.type,r.stateNode=e.stateNode,r.alternate=e,e.alternate=r):(r.pendingProps=t,r.effectTag=0,r.nextEffect=null,r.firstEffect=null,r.lastEffect=null),r.expirationTime=n,r.child=e.child,r.memoizedProps=e.memoizedProps,r.memoizedState=e.memoizedState,r.updateQueue=e.updateQueue,r.sibling=e.sibling,r.index=e.index,r.ref=e.ref,r}function Ua(e,t,n){var r=e.type,a=e.key;if(e=e.props,"function"==typeof r)var l=r.prototype&&r.prototype.isReactComponent?2:0;else if("string"==typeof r)l=5;else switch(r){case ft:return Fa(e.children,t,n,a);case vt:l=11,t|=3;break;case dt:l=11,t|=2;break;case pt:return(r=new Na(15,e,a,4|t)).type=pt,r.expirationTime=n,r;case yt:l=16,t|=2;break;default:e:{switch("object"==typeof r&&null!==r?r.$$typeof:null){case mt:l=13;break e;case ht:l=12;break e;case gt:l=14;break e;default:c("130",null==r?r:typeof r,"")}l=void 0}}return(t=new Na(l,e,a,t)).type=r,t.expirationTime=n,t}function Fa(e,t,n,r){return(e=new Na(10,e,r,t)).expirationTime=n,e}function Ia(e,t,n){return(e=new Na(6,e,null,t)).expirationTime=n,e}function Ma(e,t,n){return(t=new Na(4,null!==e.children?e.children:[],e.key,t)).expirationTime=n,t.stateNode={containerInfo:e.containerInfo,pendingChildren:null,implementation:e.implementation},t}function Ra(e,t,n){return e={current:t=new Na(3,null,null,t?3:0),containerInfo:e,pendingChildren:null,earliestPendingTime:0,latestPendingTime:0,earliestSuspendedTime:0,latestSuspendedTime:0,latestPingedTime:0,pendingCommitExpirationTime:0,finishedWork:null,context:null,pendingContext:null,hydrate:n,remainingExpirationTime:0,firstBatch:null,nextScheduledRoot:null},t.stateNode=e}var za=null,Oa=null;function Da(e){return function(t){try{return e(t)}catch(e){}}}function La(e){if("undefined"==typeof __REACT_DEVTOOLS_GLOBAL_HOOK__)return!1;var t=__REACT_DEVTOOLS_GLOBAL_HOOK__;if(t.isDisabled||!t.supportsFiber)return!0;try{var n=t.inject(e);za=Da(function(e){return t.onCommitFiberRoot(n,e)}),Oa=Da(function(e){return t.onCommitFiberUnmount(n,e)})}catch(e){}return!0}function Aa(e){"function"==typeof za&&za(e)}function ja(e){"function"==typeof Oa&&Oa(e)}var Wa=!1;function Ba(e){return{expirationTime:0,baseState:e,firstUpdate:null,lastUpdate:null,firstCapturedUpdate:null,lastCapturedUpdate:null,firstEffect:null,lastEffect:null,firstCapturedEffect:null,lastCapturedEffect:null}}function Va(e){return{expirationTime:e.expirationTime,baseState:e.baseState,firstUpdate:e.firstUpdate,lastUpdate:e.lastUpdate,firstCapturedUpdate:null,lastCapturedUpdate:null,firstEffect:null,lastEffect:null,firstCapturedEffect:null,lastCapturedEffect:null}}function Ha(e){return{expirationTime:e,tag:0,payload:null,callback:null,next:null,nextEffect:null}}function Qa(e,t,n){null===e.lastUpdate?e.firstUpdate=e.lastUpdate=t:(e.lastUpdate.next=t,e.lastUpdate=t),(0===e.expirationTime||e.expirationTime>n)&&(e.expirationTime=n)}function qa(e,t,n){var r=e.alternate;if(null===r){var a=e.updateQueue,l=null;null===a&&(a=e.updateQueue=Ba(e.memoizedState))}else a=e.updateQueue,l=r.updateQueue,null===a?null===l?(a=e.updateQueue=Ba(e.memoizedState),l=r.updateQueue=Ba(r.memoizedState)):a=e.updateQueue=Va(l):null===l&&(l=r.updateQueue=Va(a));null===l||a===l?Qa(a,t,n):null===a.lastUpdate||null===l.lastUpdate?(Qa(a,t,n),Qa(l,t,n)):(Qa(a,t,n),l.lastUpdate=t)}function Ka(e,t,n){var r=e.updateQueue;null===(r=null===r?e.updateQueue=Ba(e.memoizedState):$a(e,r)).lastCapturedUpdate?r.firstCapturedUpdate=r.lastCapturedUpdate=t:(r.lastCapturedUpdate.next=t,r.lastCapturedUpdate=t),(0===r.expirationTime||r.expirationTime>n)&&(r.expirationTime=n)}function $a(e,t){var n=e.alternate;return null!==n&&t===n.updateQueue&&(t=e.updateQueue=Va(t)),t}function Ya(e,t,n,a,l,o){switch(n.tag){case 1:return"function"==typeof(e=n.payload)?e.call(o,a,l):e;case 3:e.effectTag=-1025&e.effectTag|64;case 0:if(null==(l="function"==typeof(e=n.payload)?e.call(o,a,l):e))break;return r({},a,l);case 2:Wa=!0}return a}function Xa(e,t,n,r,a){if(Wa=!1,!(0===t.expirationTime||t.expirationTime>a)){for(var l=(t=$a(e,t)).baseState,o=null,i=0,u=t.firstUpdate,c=l;null!==u;){var s=u.expirationTime;s>a?(null===o&&(o=u,l=c),(0===i||i>s)&&(i=s)):(c=Ya(e,t,u,c,n,r),null!==u.callback&&(e.effectTag|=32,u.nextEffect=null,null===t.lastEffect?t.firstEffect=t.lastEffect=u:(t.lastEffect.nextEffect=u,t.lastEffect=u))),u=u.next}for(s=null,u=t.firstCapturedUpdate;null!==u;){var f=u.expirationTime;f>a?(null===s&&(s=u,null===o&&(l=c)),(0===i||i>f)&&(i=f)):(c=Ya(e,t,u,c,n,r),null!==u.callback&&(e.effectTag|=32,u.nextEffect=null,null===t.lastCapturedEffect?t.firstCapturedEffect=t.lastCapturedEffect=u:(t.lastCapturedEffect.nextEffect=u,t.lastCapturedEffect=u))),u=u.next}null===o&&(t.lastUpdate=null),null===s?t.lastCapturedUpdate=null:e.effectTag|=32,null===o&&null===s&&(l=c),t.baseState=l,t.firstUpdate=o,t.firstCapturedUpdate=s,t.expirationTime=i,e.memoizedState=c}}function Ga(e,t){"function"!=typeof e&&c("191",e),e.call(t)}function Za(e,t,n){for(null!==t.firstCapturedUpdate&&(null!==t.lastUpdate&&(t.lastUpdate.next=t.firstCapturedUpdate,t.lastUpdate=t.lastCapturedUpdate),t.firstCapturedUpdate=t.lastCapturedUpdate=null),e=t.firstEffect,t.firstEffect=t.lastEffect=null;null!==e;){var r=e.callback;null!==r&&(e.callback=null,Ga(r,n)),e=e.nextEffect}for(e=t.firstCapturedEffect,t.firstCapturedEffect=t.lastCapturedEffect=null;null!==e;)null!==(t=e.callback)&&(e.callback=null,Ga(t,n)),e=e.nextEffect}function Ja(e,t){return{value:e,source:t,stack:xt(t)}}var el=pa(null),tl=pa(null),nl=pa(0);function rl(e){var t=e.type._context;ha(nl,t._changedBits,e),ha(tl,t._currentValue,e),ha(el,e,e),t._currentValue=e.pendingProps.value,t._changedBits=e.stateNode}function al(e){var t=nl.current,n=tl.current;ma(el,e),ma(tl,e),ma(nl,e),(e=e.type._context)._currentValue=n,e._changedBits=t}var ll={},ol=pa(ll),il=pa(ll),ul=pa(ll);function cl(e){return e===ll&&c("174"),e}function sl(e,t){ha(ul,t,e),ha(il,e,e),ha(ol,ll,e);var n=t.nodeType;switch(n){case 9:case 11:t=(t=t.documentElement)?t.namespaceURI:Or(null,"");break;default:t=Or(t=(n=8===n?t.parentNode:t).namespaceURI||null,n=n.tagName)}ma(ol,e),ha(ol,t,e)}function fl(e){ma(ol,e),ma(il,e),ma(ul,e)}function dl(e){il.current===e&&(ma(ol,e),ma(il,e))}function pl(e,t,n){var a=e.memoizedState;a=null==(t=t(n,a))?a:r({},a,t),e.memoizedState=a,null!==(e=e.updateQueue)&&0===e.expirationTime&&(e.baseState=a)}var ml={isMounted:function(e){return!!(e=e._reactInternalFiber)&&2===dn(e)},enqueueSetState:function(e,t,n){e=e._reactInternalFiber;var r=No(),a=Ha(r=_o(r,e));a.payload=t,null!=n&&(a.callback=n),qa(e,a,r),So(e,r)},enqueueReplaceState:function(e,t,n){e=e._reactInternalFiber;var r=No(),a=Ha(r=_o(r,e));a.tag=1,a.payload=t,null!=n&&(a.callback=n),qa(e,a,r),So(e,r)},enqueueForceUpdate:function(e,t){e=e._reactInternalFiber;var n=No(),r=Ha(n=_o(n,e));r.tag=2,null!=t&&(r.callback=t),qa(e,r,n),So(e,n)}};function hl(e,t,n,r,a,l){var i=e.stateNode;return e=e.type,"function"==typeof i.shouldComponentUpdate?i.shouldComponentUpdate(n,a,l):!e.prototype||!e.prototype.isPureReactComponent||(!o(t,n)||!o(r,a))}function vl(e,t,n,r){e=t.state,"function"==typeof t.componentWillReceiveProps&&t.componentWillReceiveProps(n,r),"function"==typeof t.UNSAFE_componentWillReceiveProps&&t.UNSAFE_componentWillReceiveProps(n,r),t.state!==e&&ml.enqueueReplaceState(t,t.state,null)}function gl(e,t){var n=e.type,r=e.stateNode,a=e.pendingProps,l=ba(e);r.props=a,r.state=e.memoizedState,r.refs=u,r.context=ka(e,l),null!==(l=e.updateQueue)&&(Xa(e,l,a,r,t),r.state=e.memoizedState),"function"==typeof(l=e.type.getDerivedStateFromProps)&&(pl(e,l,a),r.state=e.memoizedState),"function"==typeof n.getDerivedStateFromProps||"function"==typeof r.getSnapshotBeforeUpdate||"function"!=typeof r.UNSAFE_componentWillMount&&"function"!=typeof r.componentWillMount||(n=r.state,"function"==typeof r.componentWillMount&&r.componentWillMount(),"function"==typeof r.UNSAFE_componentWillMount&&r.UNSAFE_componentWillMount(),n!==r.state&&ml.enqueueReplaceState(r,r.state,null),null!==(l=e.updateQueue)&&(Xa(e,l,a,r,t),r.state=e.memoizedState)),"function"==typeof r.componentDidMount&&(e.effectTag|=4)}var yl=Array.isArray;function bl(e,t,n){if(null!==(e=n.ref)&&"function"!=typeof e&&"object"!=typeof e){if(n._owner){n=n._owner;var r=void 0;n&&(2!==n.tag&&c("110"),r=n.stateNode),r||c("147",e);var a=""+e;return null!==t&&null!==t.ref&&"function"==typeof t.ref&&t.ref._stringRef===a?t.ref:((t=function(e){var t=r.refs===u?r.refs={}:r.refs;null===e?delete t[a]:t[a]=e})._stringRef=a,t)}"string"!=typeof e&&c("148"),n._owner||c("254",e)}return e}function kl(e,t){"textarea"!==e.type&&c("31","[object Object]"===Object.prototype.toString.call(t)?"object with keys {"+Object.keys(t).join(", ")+"}":t,"")}function wl(e){function t(t,n){if(e){var r=t.lastEffect;null!==r?(r.nextEffect=n,t.lastEffect=n):t.firstEffect=t.lastEffect=n,n.nextEffect=null,n.effectTag=8}}function n(n,r){if(!e)return null;for(;null!==r;)t(n,r),r=r.sibling;return null}function r(e,t){for(e=new Map;null!==t;)null!==t.key?e.set(t.key,t):e.set(t.index,t),t=t.sibling;return e}function a(e,t,n){return(e=Pa(e,t,n)).index=0,e.sibling=null,e}function l(t,n,r){return t.index=r,e?null!==(r=t.alternate)?(r=r.index)h?(v=f,f=null):v=f.sibling;var g=p(a,f,i[h],u);if(null===g){null===f&&(f=v);break}e&&f&&null===g.alternate&&t(a,f),o=l(g,o,h),null===s?c=g:s.sibling=g,s=g,f=v}if(h===i.length)return n(a,f),c;if(null===f){for(;hv?(g=h,h=null):g=h.sibling;var b=p(a,h,y.value,u);if(null===b){h||(h=g);break}e&&h&&null===b.alternate&&t(a,h),o=l(b,o,v),null===f?s=b:f.sibling=b,f=b,h=g}if(y.done)return n(a,h),s;if(null===h){for(;!y.done;v++,y=i.next())null!==(y=d(a,y.value,u))&&(o=l(y,o,v),null===f?s=y:f.sibling=y,f=y);return s}for(h=r(a,h);!y.done;v++,y=i.next())null!==(y=m(h,a,v,y.value,u))&&(e&&null!==y.alternate&&h.delete(null===y.key?v:y.key),o=l(y,o,v),null===f?s=y:f.sibling=y,f=y);return e&&h.forEach(function(e){return t(a,e)}),s}return function(e,r,l,i){var u="object"==typeof l&&null!==l&&l.type===ft&&null===l.key;u&&(l=l.props.children);var s="object"==typeof l&&null!==l;if(s)switch(l.$$typeof){case ct:e:{for(s=l.key,u=r;null!==u;){if(u.key===s){if(10===u.tag?l.type===ft:u.type===l.type){n(e,u.sibling),(r=a(u,l.type===ft?l.props.children:l.props,i)).ref=bl(e,u,l),r.return=e,e=r;break e}n(e,u);break}t(e,u),u=u.sibling}l.type===ft?((r=Fa(l.props.children,e.mode,i,l.key)).return=e,e=r):((i=Ua(l,e.mode,i)).ref=bl(e,r,l),i.return=e,e=i)}return o(e);case st:e:{for(u=l.key;null!==r;){if(r.key===u){if(4===r.tag&&r.stateNode.containerInfo===l.containerInfo&&r.stateNode.implementation===l.implementation){n(e,r.sibling),(r=a(r,l.children||[],i)).return=e,e=r;break e}n(e,r);break}t(e,r),r=r.sibling}(r=Ma(l,e.mode,i)).return=e,e=r}return o(e)}if("string"==typeof l||"number"==typeof l)return l=""+l,null!==r&&6===r.tag?(n(e,r.sibling),(r=a(r,l,i)).return=e,e=r):(n(e,r),(r=Ia(l,e.mode,i)).return=e,e=r),o(e);if(yl(l))return h(e,r,l,i);if(kt(l))return v(e,r,l,i);if(s&&kl(e,l),void 0===l&&!u)switch(e.tag){case 2:case 1:c("152",(i=e.type).displayName||i.name||"Component")}return n(e,r)}}var xl=wl(!0),Cl=wl(!1),El=null,Tl=null,_l=!1;function Sl(e,t){var n=new Na(5,null,null,0);n.type="DELETED",n.stateNode=t,n.return=e,n.effectTag=8,null!==e.lastEffect?(e.lastEffect.nextEffect=n,e.lastEffect=n):e.firstEffect=e.lastEffect=n}function Nl(e,t){switch(e.tag){case 5:var n=e.type;return null!==(t=1!==t.nodeType||n.toLowerCase()!==t.nodeName.toLowerCase()?null:t)&&(e.stateNode=t,!0);case 6:return null!==(t=""===e.pendingProps||3!==t.nodeType?null:t)&&(e.stateNode=t,!0);default:return!1}}function Pl(e){if(_l){var t=Tl;if(t){var n=t;if(!Nl(e,t)){if(!(t=ca(n))||!Nl(e,t))return e.effectTag|=2,_l=!1,void(El=e);Sl(El,n)}El=e,Tl=sa(t)}else e.effectTag|=2,_l=!1,El=e}}function Ul(e){for(e=e.return;null!==e&&5!==e.tag&&3!==e.tag;)e=e.return;El=e}function Fl(e){if(e!==El)return!1;if(!_l)return Ul(e),_l=!0,!1;var t=e.type;if(5!==e.tag||"head"!==t&&"body"!==t&&!la(t,e.memoizedProps))for(t=Tl;t;)Sl(e,t),t=ca(t);return Ul(e),Tl=El?ca(e.stateNode):null,!0}function Il(){Tl=El=null,_l=!1}function Ml(e,t,n){Rl(e,t,n,t.expirationTime)}function Rl(e,t,n,r){t.child=null===e?Cl(t,null,n,r):xl(t,e.child,n,r)}function zl(e,t){var n=t.ref;(null===e&&null!==n||null!==e&&e.ref!==n)&&(t.effectTag|=128)}function Ol(e,t,n,r,a){zl(e,t);var l=0!=(64&t.effectTag);if(!n&&!l)return r&&Sa(t,!1),jl(e,t);n=t.stateNode,it.current=t;var o=l?null:n.render();return t.effectTag|=1,l&&(Rl(e,t,null,a),t.child=null),Rl(e,t,o,a),t.memoizedState=n.state,t.memoizedProps=n.props,r&&Sa(t,!0),t.child}function Dl(e){var t=e.stateNode;t.pendingContext?Ea(e,t.pendingContext,t.pendingContext!==t.context):t.context&&Ea(e,t.context,!1),sl(e,t.containerInfo)}function Ll(e,t,n,r){var a=e.child;for(null!==a&&(a.return=e);null!==a;){switch(a.tag){case 12:var l=0|a.stateNode;if(a.type===t&&0!=(l&n)){for(l=a;null!==l;){var o=l.alternate;if(0===l.expirationTime||l.expirationTime>r)l.expirationTime=r,null!==o&&(0===o.expirationTime||o.expirationTime>r)&&(o.expirationTime=r);else{if(null===o||!(0===o.expirationTime||o.expirationTime>r))break;o.expirationTime=r}l=l.return}l=null}else l=a.child;break;case 13:l=a.type===e.type?null:a.child;break;default:l=a.child}if(null!==l)l.return=a;else for(l=a;null!==l;){if(l===e){l=null;break}if(null!==(a=l.sibling)){a.return=l.return,l=a;break}l=l.return}a=l}}function Al(e,t,n){var r=t.type._context,a=t.pendingProps,l=t.memoizedProps,o=!0;if(ga.current)o=!1;else if(l===a)return t.stateNode=0,rl(t),jl(e,t);var i=a.value;if(t.memoizedProps=a,null===l)i=1073741823;else if(l.value===a.value){if(l.children===a.children&&o)return t.stateNode=0,rl(t),jl(e,t);i=0}else{var u=l.value;if(u===i&&(0!==u||1/u==1/i)||u!=u&&i!=i){if(l.children===a.children&&o)return t.stateNode=0,rl(t),jl(e,t);i=0}else if(i="function"==typeof r._calculateChangedBits?r._calculateChangedBits(u,i):1073741823,0===(i|=0)){if(l.children===a.children&&o)return t.stateNode=0,rl(t),jl(e,t)}else Ll(t,r,i,n)}return t.stateNode=i,rl(t),Ml(e,t,a.children),t.child}function jl(e,t){if(null!==e&&t.child!==e.child&&c("153"),null!==t.child){var n=Pa(e=t.child,e.pendingProps,e.expirationTime);for(t.child=n,n.return=t;null!==e.sibling;)e=e.sibling,(n=n.sibling=Pa(e,e.pendingProps,e.expirationTime)).return=t;n.sibling=null}return t.child}function Wl(e,t,n){if(0===t.expirationTime||t.expirationTime>n){switch(t.tag){case 3:Dl(t);break;case 2:_a(t);break;case 4:sl(t,t.stateNode.containerInfo);break;case 13:rl(t)}return null}switch(t.tag){case 0:null!==e&&c("155");var r=t.type,a=t.pendingProps,l=ba(t);return r=r(a,l=ka(t,l)),t.effectTag|=1,"object"==typeof r&&null!==r&&"function"==typeof r.render&&void 0===r.$$typeof?(l=t.type,t.tag=2,t.memoizedState=null!==r.state&&void 0!==r.state?r.state:null,"function"==typeof(l=l.getDerivedStateFromProps)&&pl(t,l,a),a=_a(t),r.updater=ml,t.stateNode=r,r._reactInternalFiber=t,gl(t,n),e=Ol(e,t,!0,a,n)):(t.tag=1,Ml(e,t,r),t.memoizedProps=a,e=t.child),e;case 1:return a=t.type,n=t.pendingProps,ga.current||t.memoizedProps!==n?(a=a(n,r=ka(t,r=ba(t))),t.effectTag|=1,Ml(e,t,a),t.memoizedProps=n,e=t.child):e=jl(e,t),e;case 2:if(a=_a(t),null===e)if(null===t.stateNode){var o=t.pendingProps,i=t.type;r=ba(t);var s=2===t.tag&&null!=t.type.contextTypes;o=new i(o,l=s?ka(t,r):u),t.memoizedState=null!==o.state&&void 0!==o.state?o.state:null,o.updater=ml,t.stateNode=o,o._reactInternalFiber=t,s&&((s=t.stateNode).__reactInternalMemoizedUnmaskedChildContext=r,s.__reactInternalMemoizedMaskedChildContext=l),gl(t,n),r=!0}else{i=t.type,r=t.stateNode,s=t.memoizedProps,l=t.pendingProps,r.props=s;var f=r.context;o=ka(t,o=ba(t));var d=i.getDerivedStateFromProps;(i="function"==typeof d||"function"==typeof r.getSnapshotBeforeUpdate)||"function"!=typeof r.UNSAFE_componentWillReceiveProps&&"function"!=typeof r.componentWillReceiveProps||(s!==l||f!==o)&&vl(t,r,l,o),Wa=!1;var p=t.memoizedState;f=r.state=p;var m=t.updateQueue;null!==m&&(Xa(t,m,l,r,n),f=t.memoizedState),s!==l||p!==f||ga.current||Wa?("function"==typeof d&&(pl(t,d,l),f=t.memoizedState),(s=Wa||hl(t,s,l,p,f,o))?(i||"function"!=typeof r.UNSAFE_componentWillMount&&"function"!=typeof r.componentWillMount||("function"==typeof r.componentWillMount&&r.componentWillMount(),"function"==typeof r.UNSAFE_componentWillMount&&r.UNSAFE_componentWillMount()),"function"==typeof r.componentDidMount&&(t.effectTag|=4)):("function"==typeof r.componentDidMount&&(t.effectTag|=4),t.memoizedProps=l,t.memoizedState=f),r.props=l,r.state=f,r.context=o,r=s):("function"==typeof r.componentDidMount&&(t.effectTag|=4),r=!1)}else i=t.type,r=t.stateNode,l=t.memoizedProps,s=t.pendingProps,r.props=l,f=r.context,o=ka(t,o=ba(t)),(i="function"==typeof(d=i.getDerivedStateFromProps)||"function"==typeof r.getSnapshotBeforeUpdate)||"function"!=typeof r.UNSAFE_componentWillReceiveProps&&"function"!=typeof r.componentWillReceiveProps||(l!==s||f!==o)&&vl(t,r,s,o),Wa=!1,f=t.memoizedState,p=r.state=f,null!==(m=t.updateQueue)&&(Xa(t,m,s,r,n),p=t.memoizedState),l!==s||f!==p||ga.current||Wa?("function"==typeof d&&(pl(t,d,s),p=t.memoizedState),(d=Wa||hl(t,l,s,f,p,o))?(i||"function"!=typeof r.UNSAFE_componentWillUpdate&&"function"!=typeof r.componentWillUpdate||("function"==typeof r.componentWillUpdate&&r.componentWillUpdate(s,p,o),"function"==typeof r.UNSAFE_componentWillUpdate&&r.UNSAFE_componentWillUpdate(s,p,o)),"function"==typeof r.componentDidUpdate&&(t.effectTag|=4),"function"==typeof r.getSnapshotBeforeUpdate&&(t.effectTag|=256)):("function"!=typeof r.componentDidUpdate||l===e.memoizedProps&&f===e.memoizedState||(t.effectTag|=4),"function"!=typeof r.getSnapshotBeforeUpdate||l===e.memoizedProps&&f===e.memoizedState||(t.effectTag|=256),t.memoizedProps=s,t.memoizedState=p),r.props=s,r.state=p,r.context=o,r=d):("function"!=typeof r.componentDidUpdate||l===e.memoizedProps&&f===e.memoizedState||(t.effectTag|=4),"function"!=typeof r.getSnapshotBeforeUpdate||l===e.memoizedProps&&f===e.memoizedState||(t.effectTag|=256),r=!1);return Ol(e,t,r,a,n);case 3:return Dl(t),null!==(a=t.updateQueue)?(r=null!==(r=t.memoizedState)?r.element:null,Xa(t,a,t.pendingProps,null,n),(a=t.memoizedState.element)===r?(Il(),e=jl(e,t)):(r=t.stateNode,(r=(null===e||null===e.child)&&r.hydrate)&&(Tl=sa(t.stateNode.containerInfo),El=t,r=_l=!0),r?(t.effectTag|=2,t.child=Cl(t,null,a,n)):(Il(),Ml(e,t,a)),e=t.child)):(Il(),e=jl(e,t)),e;case 5:return cl(ul.current),(a=cl(ol.current))!==(r=Or(a,t.type))&&(ha(il,t,t),ha(ol,r,t)),null===e&&Pl(t),a=t.type,s=t.memoizedProps,r=t.pendingProps,l=null!==e?e.memoizedProps:null,ga.current||s!==r||((s=1&t.mode&&!!r.hidden)&&(t.expirationTime=1073741823),s&&1073741823===n)?(s=r.children,la(a,r)?s=null:l&&la(a,l)&&(t.effectTag|=16),zl(e,t),1073741823!==n&&1&t.mode&&r.hidden?(t.expirationTime=1073741823,t.memoizedProps=r,e=null):(Ml(e,t,s),t.memoizedProps=r,e=t.child)):e=jl(e,t),e;case 6:return null===e&&Pl(t),t.memoizedProps=t.pendingProps,null;case 16:return null;case 4:return sl(t,t.stateNode.containerInfo),a=t.pendingProps,ga.current||t.memoizedProps!==a?(null===e?t.child=xl(t,null,a,n):Ml(e,t,a),t.memoizedProps=a,e=t.child):e=jl(e,t),e;case 14:return a=t.type.render,n=t.pendingProps,r=t.ref,ga.current||t.memoizedProps!==n||r!==(null!==e?e.ref:null)?(Ml(e,t,a=a(n,r)),t.memoizedProps=n,e=t.child):e=jl(e,t),e;case 10:return n=t.pendingProps,ga.current||t.memoizedProps!==n?(Ml(e,t,n),t.memoizedProps=n,e=t.child):e=jl(e,t),e;case 11:return n=t.pendingProps.children,ga.current||null!==n&&t.memoizedProps!==n?(Ml(e,t,n),t.memoizedProps=n,e=t.child):e=jl(e,t),e;case 15:return n=t.pendingProps,t.memoizedProps===n?e=jl(e,t):(Ml(e,t,n.children),t.memoizedProps=n,e=t.child),e;case 13:return Al(e,t,n);case 12:e:if(r=t.type,l=t.pendingProps,s=t.memoizedProps,a=r._currentValue,o=r._changedBits,ga.current||0!==o||s!==l){if(t.memoizedProps=l,null==(i=l.unstable_observedBits)&&(i=1073741823),t.stateNode=i,0!=(o&i))Ll(t,r,o,n);else if(s===l){e=jl(e,t);break e}n=(n=l.children)(a),t.effectTag|=1,Ml(e,t,n),e=t.child}else e=jl(e,t);return e;default:c("156")}}function Bl(e){e.effectTag|=4}var Vl=void 0,Hl=void 0,Ql=void 0;function ql(e,t){var n=t.pendingProps;switch(t.tag){case 1:return null;case 2:return xa(t),null;case 3:fl(t),Ca(t);var r=t.stateNode;return r.pendingContext&&(r.context=r.pendingContext,r.pendingContext=null),null!==e&&null!==e.child||(Fl(t),t.effectTag&=-3),Vl(t),null;case 5:dl(t),r=cl(ul.current);var a=t.type;if(null!==e&&null!=t.stateNode){var l=e.memoizedProps,o=t.stateNode,i=cl(ol.current);o=Gr(o,a,l,n,r),Hl(e,t,o,a,l,n,r,i),e.ref!==t.ref&&(t.effectTag|=128)}else{if(!n)return null===t.stateNode&&c("166"),null;if(e=cl(ol.current),Fl(t))n=t.stateNode,a=t.type,l=t.memoizedProps,n[j]=t,n[W]=l,r=Jr(n,a,l,e,r),t.updateQueue=r,null!==r&&Bl(t);else{(e=$r(a,n,r,e))[j]=t,e[W]=n;e:for(l=t.child;null!==l;){if(5===l.tag||6===l.tag)e.appendChild(l.stateNode);else if(4!==l.tag&&null!==l.child){l.child.return=l,l=l.child;continue}if(l===t)break;for(;null===l.sibling;){if(null===l.return||l.return===t)break e;l=l.return}l.sibling.return=l.return,l=l.sibling}Xr(e,a,n,r),aa(a,n)&&Bl(t),t.stateNode=e}null!==t.ref&&(t.effectTag|=128)}return null;case 6:if(e&&null!=t.stateNode)Ql(e,t,e.memoizedProps,n);else{if("string"!=typeof n)return null===t.stateNode&&c("166"),null;r=cl(ul.current),cl(ol.current),Fl(t)?(r=t.stateNode,n=t.memoizedProps,r[j]=t,ea(r,n)&&Bl(t)):((r=Yr(n,r))[j]=t,t.stateNode=r)}return null;case 14:case 16:case 10:case 11:case 15:return null;case 4:return fl(t),Vl(t),null;case 13:return al(t),null;case 12:return null;case 0:c("167");default:c("156")}}function Kl(e,t){var n=t.source;null===t.stack&&null!==n&&xt(n),null!==n&&wt(n),t=t.value,null!==e&&2===e.tag&&wt(e);try{t&&t.suppressReactErrorLogging||console.error(t)}catch(e){e&&e.suppressReactErrorLogging||console.error(e)}}function $l(e){var t=e.ref;if(null!==t)if("function"==typeof t)try{t(null)}catch(t){Eo(e,t)}else t.current=null}function Yl(e){switch("function"==typeof ja&&ja(e),e.tag){case 2:$l(e);var t=e.stateNode;if("function"==typeof t.componentWillUnmount)try{t.props=e.memoizedProps,t.state=e.memoizedState,t.componentWillUnmount()}catch(t){Eo(e,t)}break;case 5:$l(e);break;case 4:Zl(e)}}function Xl(e){return 5===e.tag||3===e.tag||4===e.tag}function Gl(e){e:{for(var t=e.return;null!==t;){if(Xl(t)){var n=t;break e}t=t.return}c("160"),n=void 0}var r=t=void 0;switch(n.tag){case 5:t=n.stateNode,r=!1;break;case 3:case 4:t=n.stateNode.containerInfo,r=!0;break;default:c("161")}16&n.effectTag&&(Ar(t,""),n.effectTag&=-17);e:t:for(n=e;;){for(;null===n.sibling;){if(null===n.return||Xl(n.return)){n=null;break e}n=n.return}for(n.sibling.return=n.return,n=n.sibling;5!==n.tag&&6!==n.tag;){if(2&n.effectTag)continue t;if(null===n.child||4===n.tag)continue t;n.child.return=n,n=n.child}if(!(2&n.effectTag)){n=n.stateNode;break e}}for(var a=e;;){if(5===a.tag||6===a.tag)if(n)if(r){var l=t,o=a.stateNode,i=n;8===l.nodeType?l.parentNode.insertBefore(o,i):l.insertBefore(o,i)}else t.insertBefore(a.stateNode,n);else r?(l=t,o=a.stateNode,8===l.nodeType?l.parentNode.insertBefore(o,l):l.appendChild(o)):t.appendChild(a.stateNode);else if(4!==a.tag&&null!==a.child){a.child.return=a,a=a.child;continue}if(a===e)break;for(;null===a.sibling;){if(null===a.return||a.return===e)return;a=a.return}a.sibling.return=a.return,a=a.sibling}}function Zl(e){for(var t=e,n=!1,r=void 0,a=void 0;;){if(!n){n=t.return;e:for(;;){switch(null===n&&c("160"),n.tag){case 5:r=n.stateNode,a=!1;break e;case 3:case 4:r=n.stateNode.containerInfo,a=!0;break e}n=n.return}n=!0}if(5===t.tag||6===t.tag){e:for(var l=t,o=l;;)if(Yl(o),null!==o.child&&4!==o.tag)o.child.return=o,o=o.child;else{if(o===l)break;for(;null===o.sibling;){if(null===o.return||o.return===l)break e;o=o.return}o.sibling.return=o.return,o=o.sibling}a?(l=r,o=t.stateNode,8===l.nodeType?l.parentNode.removeChild(o):l.removeChild(o)):r.removeChild(t.stateNode)}else if(4===t.tag?r=t.stateNode.containerInfo:Yl(t),null!==t.child){t.child.return=t,t=t.child;continue}if(t===e)break;for(;null===t.sibling;){if(null===t.return||t.return===e)return;4===(t=t.return).tag&&(n=!1)}t.sibling.return=t.return,t=t.sibling}}function Jl(e,t){switch(t.tag){case 2:break;case 5:var n=t.stateNode;if(null!=n){var r=t.memoizedProps;e=null!==e?e.memoizedProps:r;var a=t.type,l=t.updateQueue;t.updateQueue=null,null!==l&&(n[W]=r,Zr(n,l,a,e,r))}break;case 6:null===t.stateNode&&c("162"),t.stateNode.nodeValue=t.memoizedProps;break;case 3:case 15:case 16:break;default:c("163")}}function eo(e,t,n){(n=Ha(n)).tag=3,n.payload={element:null};var r=t.value;return n.callback=function(){ii(r),Kl(e,t)},n}function to(e,t,n){(n=Ha(n)).tag=3;var r=e.stateNode;return null!==r&&"function"==typeof r.componentDidCatch&&(n.callback=function(){null===bo?bo=new Set([this]):bo.add(this);var n=t.value,r=t.stack;Kl(e,t),this.componentDidCatch(n,{componentStack:null!==r?r:""})}),n}function no(e,t,n,r,a,l){n.effectTag|=512,n.firstEffect=n.lastEffect=null,r=Ja(r,n),e=t;do{switch(e.tag){case 3:return e.effectTag|=1024,void Ka(e,r=eo(e,r,l),l);case 2:if(t=r,n=e.stateNode,0==(64&e.effectTag)&&null!==n&&"function"==typeof n.componentDidCatch&&(null===bo||!bo.has(n)))return e.effectTag|=1024,void Ka(e,r=to(e,t,l),l)}e=e.return}while(null!==e)}function ro(e){switch(e.tag){case 2:xa(e);var t=e.effectTag;return 1024&t?(e.effectTag=-1025&t|64,e):null;case 3:return fl(e),Ca(e),1024&(t=e.effectTag)?(e.effectTag=-1025&t|64,e):null;case 5:return dl(e),null;case 16:return 1024&(t=e.effectTag)?(e.effectTag=-1025&t|64,e):null;case 4:return fl(e),null;case 13:return al(e),null;default:return null}}Vl=function(){},Hl=function(e,t,n){(t.updateQueue=n)&&Bl(t)},Ql=function(e,t,n,r){n!==r&&Bl(t)};var ao=oa(),lo=2,oo=ao,io=0,uo=0,co=!1,so=null,fo=null,po=0,mo=-1,ho=!1,vo=null,go=!1,yo=!1,bo=null;function ko(){if(null!==so)for(var e=so.return;null!==e;){var t=e;switch(t.tag){case 2:xa(t);break;case 3:fl(t),Ca(t);break;case 5:dl(t);break;case 4:fl(t);break;case 13:al(t)}e=e.return}fo=null,po=0,mo=-1,ho=!1,so=null,yo=!1}function wo(e){for(;;){var t=e.alternate,n=e.return,r=e.sibling;if(0==(512&e.effectTag)){t=ql(t,e,po);var a=e;if(1073741823===po||1073741823!==a.expirationTime){var l=0;switch(a.tag){case 3:case 2:var o=a.updateQueue;null!==o&&(l=o.expirationTime)}for(o=a.child;null!==o;)0!==o.expirationTime&&(0===l||l>o.expirationTime)&&(l=o.expirationTime),o=o.sibling;a.expirationTime=l}if(null!==t)return t;if(null!==n&&0==(512&n.effectTag)&&(null===n.firstEffect&&(n.firstEffect=e.firstEffect),null!==e.lastEffect&&(null!==n.lastEffect&&(n.lastEffect.nextEffect=e.firstEffect),n.lastEffect=e.lastEffect),1Lo)&&(Lo=e),e}function So(e,t){for(;null!==e;){if((0===e.expirationTime||e.expirationTime>t)&&(e.expirationTime=t),null!==e.alternate&&(0===e.alternate.expirationTime||e.alternate.expirationTime>t)&&(e.alternate.expirationTime=t),null===e.return){if(3!==e.tag)break;var n=e.stateNode;!co&&0!==po&&tKo&&c("185")}e=e.return}}function No(){return oo=oa()-ao,lo=2+(oo/10|0)}function Po(e){var t=uo;uo=2+25*(1+((No()-2+500)/25|0));try{return e()}finally{uo=t}}function Uo(e,t,n,r,a){var l=uo;uo=1;try{return e(t,n,r,a)}finally{uo=l}}var Fo=null,Io=null,Mo=0,Ro=void 0,zo=!1,Oo=null,Do=0,Lo=0,Ao=!1,jo=!1,Wo=null,Bo=null,Vo=!1,Ho=!1,Qo=!1,qo=null,Ko=1e3,$o=0,Yo=1;function Xo(e){if(0!==Mo){if(e>Mo)return;null!==Ro&&ua(Ro)}var t=oa()-ao;Mo=e,Ro=ia(Jo,{timeout:10*(e-2)-t})}function Go(e,t){if(null===e.nextScheduledRoot)e.remainingExpirationTime=t,null===Io?(Fo=Io=e,e.nextScheduledRoot=e):(Io=Io.nextScheduledRoot=e).nextScheduledRoot=Fo;else{var n=e.remainingExpirationTime;(0===n||t=Do)&&(!Ao||No()>=Do);)No(),ai(Oo,Do,!Ao),Zo();else for(;null!==Oo&&0!==Do&&(0===e||e>=Do);)ai(Oo,Do,!1),Zo();null!==Bo&&(Mo=0,Ro=null),0!==Do&&Xo(Do),Bo=null,Ao=!1,ri()}function ni(e,t){zo&&c("253"),Oo=e,Do=t,ai(e,t,!1),ei(),ri()}function ri(){if($o=0,null!==qo){var e=qo;qo=null;for(var t=0;tw&&(x=w,w=N,N=x),x=$n(_,N),C=$n(_,w),x&&C&&(1!==S.rangeCount||S.anchorNode!==x.node||S.anchorOffset!==x.offset||S.focusNode!==C.node||S.focusOffset!==C.offset)&&((E=document.createRange()).setStart(x.node,x.offset),S.removeAllRanges(),N>w?(S.addRange(E),S.extend(C.node,C.offset)):(E.setEnd(C.node,C.offset),S.addRange(E))))),S=[];for(N=_;N=N.parentNode;)1===N.nodeType&&S.push({element:N,left:N.scrollLeft,top:N.scrollTop});for("function"==typeof _.focus&&_.focus(),_=0;_Yo)&&(Ao=!0)}function ii(e){null===Oo&&c("246"),Oo.remainingExpirationTime=0,jo||(jo=!0,Wo=e)}function ui(e){null===Oo&&c("246"),Oo.remainingExpirationTime=e}function ci(e,t){var n=Vo;Vo=!0;try{return e(t)}finally{(Vo=n)||zo||ei()}}function si(e,t){if(Vo&&!Ho){Ho=!0;try{return e(t)}finally{Ho=!1}}return e(t)}function fi(e,t){zo&&c("187");var n=Vo;Vo=!0;try{return Uo(e,t)}finally{Vo=n,ei()}}function di(e,t,n){if(Qo)return e(t,n);Vo||zo||0===Lo||(ti(Lo,!1,null),Lo=0);var r=Qo,a=Vo;Vo=Qo=!0;try{return e(t,n)}finally{Qo=r,(Vo=a)||zo||ei()}}function pi(e){var t=Vo;Vo=!0;try{Uo(e)}finally{(Vo=t)||zo||ti(1,!1,null)}}function mi(e,t,n,r,a){var l=t.current;if(n){var o;e:{for(2===dn(n=n._reactInternalFiber)&&2===n.tag||c("170"),o=n;3!==o.tag;){if(wa(o)){o=o.stateNode.__reactInternalMemoizedMergedChildContext;break e}(o=o.return)||c("171")}o=o.stateNode.context}n=wa(n)?Ta(n,o):o}else n=u;return null===t.context?t.context=n:t.pendingContext=n,t=a,(a=Ha(r)).payload={element:e},null!==(t=void 0===t?null:t)&&(a.callback=t),qa(l,a,r),So(l,r),r}function hi(e){var t=e._reactInternalFiber;return void 0===t&&("function"==typeof e.render?c("188"):c("268",Object.keys(e))),null===(e=hn(t))?null:e.stateNode}function vi(e,t,n,r){var a=t.current;return mi(e,t,n,a=_o(No(),a),r)}function gi(e){if(!(e=e.current).child)return null;switch(e.child.tag){case 5:default:return e.child.stateNode}}function yi(e){var t=e.findFiberByHostInstance;return La(r({},e,{findHostInstanceByFiber:function(e){return null===(e=hn(e))?null:e.stateNode},findFiberByHostInstance:function(e){return t?t(e):null}}))}var bi={updateContainerAtExpirationTime:mi,createContainer:function(e,t,n){return Ra(e,t,n)},updateContainer:vi,flushRoot:ni,requestWork:Go,computeUniqueAsyncExpiration:To,batchedUpdates:ci,unbatchedUpdates:si,deferredUpdates:Po,syncUpdates:Uo,interactiveUpdates:di,flushInteractiveUpdates:function(){zo||0===Lo||(ti(Lo,!1,null),Lo=0)},flushControlled:pi,flushSync:fi,getPublicRootInstance:gi,findHostInstance:hi,findHostInstanceWithNoPortals:function(e){return null===(e=vn(e))?null:e.stateNode},injectIntoDevTools:yi};function ki(e,t,n){var r=3])/g,A=/([[}=:>])\s+/g,C=/(\{[^{]+?);(?=\})/g,w=/\s{2,}/g,v=/([^\(])(:+) */g,m=/[svh]\w+-[tblr]{2}/,x=/\(\s*(.*)\s*\)/g,$=/([\s\S]*?);/g,y=/-self|flex-/g,O=/[^]*?(:[rp][el]a[\w-]+)[^]*/,j=/stretch|:\s*\w+\-(?:conte|avail)/,z=/([^-])(image-set\()/,F="-webkit-",N="-moz-",S="-ms-",B=59,W=125,q=123,D=40,E=41,G=91,H=93,I=10,J=13,K=9,L=64,M=32,P=38,Q=45,R=95,T=42,U=44,V=58,X=39,Y=34,Z=47,_=62,ee=43,ae=126,ce=0,re=12,se=11,te=107,ie=109,ne=115,le=112,oe=111,fe=105,he=99,ue=100,de=112,be=1,ke=1,pe=0,ge=1,Ae=1,Ce=1,we=0,ve=0,me=0,xe=[],$e=[],ye=0,Oe=null,je=-2,ze=-1,Fe=0,Ne=1,Se=2,Be=3,We=0,qe=1,De="",Ee="",Ge="";function He(e,a,s,t,i){for(var n,l,f=0,h=0,u=0,d=0,g=0,A=0,C=0,w=0,m=0,$=0,y=0,O=0,j=0,z=0,R=0,we=0,$e=0,Oe=0,je=0,ze=s.length,Je=ze-1,Re="",Te="",Ue="",Ve="",Xe="",Ye="";R0&&(Te=Te.replace(r,"")),Te.trim().length>0)){switch(C){case M:case K:case B:case J:case I:break;default:Te+=s.charAt(R)}C=B}if(1===$e)switch(C){case q:case W:case B:case Y:case X:case D:case E:case U:$e=0;case K:case J:case I:case M:break;default:for($e=0,je=R,g=C,R--,C=B;je0&&(++R,C=g);case q:je=ze}}switch(C){case q:for(g=(Te=Te.trim()).charCodeAt(0),y=1,je=++R;R0&&(Te=Te.replace(r,"")),A=Te.charCodeAt(1)){case ue:case ie:case ne:case Q:n=a;break;default:n=xe}if(je=(Ue=He(a,n,Ue,A,i+1)).length,me>0&&0===je&&(je=Te.length),ye>0&&(n=Ie(xe,Te,Oe),l=Pe(Be,Ue,n,a,ke,be,je,A,i,t),Te=n.join(""),void 0!==l&&0===(je=(Ue=l.trim()).length)&&(A=0,Ue="")),je>0)switch(A){case ne:Te=Te.replace(x,Me);case ue:case ie:case Q:Ue=Te+"{"+Ue+"}";break;case te:Ue=(Te=Te.replace(b,"$1 $2"+(qe>0?De:"")))+"{"+Ue+"}",Ue=1===Ae||2===Ae&&Le("@"+Ue,3)?"@"+F+Ue+"@"+Ue:"@"+Ue;break;default:Ue=Te+Ue,t===de&&(Ve+=Ue,Ue="")}else Ue="";break;default:Ue=He(a,Ie(a,Te,Oe),Ue,t,i+1)}Xe+=Ue,O=0,$e=0,z=0,we=0,Oe=0,j=0,Te="",Ue="",C=s.charCodeAt(++R);break;case W:case B:if((je=(Te=(we>0?Te.replace(r,""):Te).trim()).length)>1)switch(0===z&&((g=Te.charCodeAt(0))===Q||g>96&&g<123)&&(je=(Te=Te.replace(" ",":")).length),ye>0&&void 0!==(l=Pe(Ne,Te,a,e,ke,be,Ve.length,t,i,t))&&0===(je=(Te=l.trim()).length)&&(Te="\0\0"),g=Te.charCodeAt(0),A=Te.charCodeAt(1),g){case ce:break;case L:if(A===fe||A===he){Ye+=Te+s.charAt(R);break}default:if(Te.charCodeAt(je-1)===V)break;Ve+=Ke(Te,g,A,Te.charCodeAt(2))}O=0,$e=0,z=0,we=0,Oe=0,Te="",C=s.charCodeAt(++R)}}switch(C){case J:case I:if(h+d+u+f+ve===0)switch($){case E:case X:case Y:case L:case ae:case _:case T:case ee:case Z:case Q:case V:case U:case B:case q:case W:break;default:z>0&&($e=1)}h===Z?h=0:ge+O===0&&t!==te&&Te.length>0&&(we=1,Te+="\0"),ye*We>0&&Pe(Fe,Te,a,e,ke,be,Ve.length,t,i,t),be=1,ke++;break;case B:case W:if(h+d+u+f===0){be++;break}default:switch(be++,Re=s.charAt(R),C){case K:case M:if(d+f+h===0)switch(w){case U:case V:case K:case M:Re="";break;default:C!==M&&(Re=" ")}break;case ce:Re="\\0";break;case re:Re="\\f";break;case se:Re="\\v";break;case P:d+h+f===0&&ge>0&&(Oe=1,we=1,Re="\f"+Re);break;case 108:if(d+h+f+pe===0&&z>0)switch(R-z){case 2:w===le&&s.charCodeAt(R-3)===V&&(pe=w);case 8:m===oe&&(pe=m)}break;case V:d+h+f===0&&(z=R);break;case U:h+u+d+f===0&&(we=1,Re+="\r");break;case Y:case X:0===h&&(d=d===C?0:0===d?C:d);break;case G:d+h+u===0&&f++;break;case H:d+h+u===0&&f--;break;case E:d+h+f===0&&u--;break;case D:if(d+h+f===0){if(0===O)switch(2*w+3*m){case 533:break;default:y=0,O=1}u++}break;case L:h+u+d+f+z+j===0&&(j=1);break;case T:case Z:if(d+f+u>0)break;switch(h){case 0:switch(2*C+3*s.charCodeAt(R+1)){case 235:h=Z;break;case 220:je=R,h=T}break;case T:C===Z&&w===T&&je+2!==R&&(33===s.charCodeAt(je+2)&&(Ve+=s.substring(je,R+1)),Re="",h=0)}}if(0===h){if(ge+d+f+j===0&&t!==te&&C!==B)switch(C){case U:case ae:case _:case ee:case E:case D:if(0===O){switch(w){case K:case M:case I:case J:Re+="\0";break;default:Re="\0"+Re+(C===U?"":"\0")}we=1}else switch(C){case D:z+7===R&&108===w&&(z=0),O=++y;break;case E:0==(O=--y)&&(we=1,Re+="\0")}break;case K:case M:switch(w){case ce:case q:case W:case B:case U:case re:case K:case M:case I:case J:break;default:0===O&&(we=1,Re+="\0")}}Te+=Re,C!==M&&C!==K&&($=C)}}m=w,w=C,R++}if(je=Ve.length,me>0&&0===je&&0===Xe.length&&0===a[0].length==!1&&(t!==ie||1===a.length&&(ge>0?Ee:Ge)===a[0])&&(je=a.join(",").length+2),je>0){if(n=0===ge&&t!==te?function(e){for(var a,c,s=0,t=e.length,i=Array(t);s1)){if(u=l.charCodeAt(l.length-1),d=c.charCodeAt(0),a="",0!==f)switch(u){case T:case ae:case _:case ee:case M:case D:break;default:a=" "}switch(d){case P:c=a+Ee;case ae:case _:case ee:case M:case E:case D:break;case G:c=a+c+Ee;break;case V:switch(2*c.charCodeAt(1)+3*c.charCodeAt(2)){case 530:if(Ce>0){c=a+c.substring(8,h-1);break}default:(f<1||n[f-1].length<1)&&(c=a+Ee+c)}break;case U:a="";default:c=h>1&&c.indexOf(":")>0?a+c.replace(v,"$1"+Ee+"$2"):a+c+Ee}l+=c}i[s]=l.replace(r,"").trim()}return i}(a):a,ye>0&&void 0!==(l=Pe(Se,Ve,n,e,ke,be,je,t,i,t))&&0===(Ve=l).length)return Ye+Ve+Xe;if(Ve=n.join(",")+"{"+Ve+"}",Ae*pe!=0){switch(2!==Ae||Le(Ve,2)||(pe=0),pe){case oe:Ve=Ve.replace(p,":"+N+"$1")+Ve;break;case le:Ve=Ve.replace(k,"::"+F+"input-$1")+Ve.replace(k,"::"+N+"$1")+Ve.replace(k,":"+S+"input-$1")+Ve}pe=0}}return Ye+Ve+Xe}function Ie(e,a,c){var r=a.trim().split(f),s=r,t=r.length,i=e.length;switch(i){case 0:case 1:for(var n=0,l=0===i?"":e[0]+" ";n0&&ge>0)return s.replace(u,"$1").replace(h,"$1"+Ge);break;default:return e.trim()+s.replace(h,"$1"+e.trim())}default:if(c*ge>0&&s.indexOf("\f")>0)return s.replace(h,(e.charCodeAt(0)===V?"":"$1")+e.trim())}return e+s}function Ke(e,a,c,r){var o,f=0,h=e+";",u=2*a+3*c+4*r;if(944===u)return function(e){var a=e.length,c=e.indexOf(":",9)+1,r=e.substring(0,c).trim(),s=e.substring(c,a-1).trim();switch(e.charCodeAt(9)*qe){case 0:break;case Q:if(110!==e.charCodeAt(10))break;default:for(var t=s.split((s="",n)),i=0,c=0,a=t.length;iL&&h<90||h>96&&h<123||h===R||h===Q&&o.charCodeAt(1)!==Q))switch(isNaN(parseFloat(o))+(-1!==o.indexOf("("))){case 1:switch(o){case"infinite":case"alternate":case"backwards":case"running":case"normal":case"forwards":case"both":case"none":case"linear":case"ease":case"ease-in":case"ease-out":case"ease-in-out":case"paused":case"reverse":case"alternate-reverse":case"inherit":case"initial":case"unset":case"step-start":case"step-end":break;default:o+=De}}f[c++]=o}s+=(0===i?"":",")+f.join(" ")}}return s=r+s+";",1===Ae||2===Ae&&Le(s,1)?F+s+s:s}(h);if(0===Ae||2===Ae&&!Le(h,1))return h;switch(u){case 1015:return 97===h.charCodeAt(10)?F+h+h:h;case 951:return 116===h.charCodeAt(3)?F+h+h:h;case 963:return 110===h.charCodeAt(5)?F+h+h:h;case 1009:if(100!==h.charCodeAt(4))break;case 969:case 942:return F+h+h;case 978:return F+h+N+h+h;case 1019:case 983:return F+h+N+h+S+h+h;case 883:return h.charCodeAt(8)===Q?F+h+h:h.indexOf("image-set(",11)>0?h.replace(z,"$1"+F+"$2")+h:h;case 932:if(h.charCodeAt(4)===Q)switch(h.charCodeAt(5)){case 103:return F+"box-"+h.replace("-grow","")+F+h+S+h.replace("grow","positive")+h;case 115:return F+h+S+h.replace("shrink","negative")+h;case 98:return F+h+S+h.replace("basis","preferred-size")+h}return F+h+S+h+h;case 964:return F+h+S+"flex-"+h+h;case 1023:if(99!==h.charCodeAt(8))break;return o=h.substring(h.indexOf(":",15)).replace("flex-","").replace("space-between","justify"),F+"box-pack"+o+F+h+S+"flex-pack"+o+h;case 1005:return t.test(h)?h.replace(s,":"+F)+h.replace(s,":"+N)+h:h;case 1e3:switch(f=(o=h.substring(13).trim()).indexOf("-")+1,o.charCodeAt(0)+o.charCodeAt(f)){case 226:o=h.replace(m,"tb");break;case 232:o=h.replace(m,"tb-rl");break;case 220:o=h.replace(m,"lr");break;default:return h}return F+h+S+o+h;case 1017:if(-1===h.indexOf("sticky",9))return h;case 975:switch(f=(h=e).length-10,u=(o=(33===h.charCodeAt(f)?h.substring(0,f):h).substring(e.indexOf(":",7)+1).trim()).charCodeAt(0)+(0|o.charCodeAt(7))){case 203:if(o.charCodeAt(8)<111)break;case 115:h=h.replace(o,F+o)+";"+h;break;case 207:case 102:h=h.replace(o,F+(u>102?"inline-":"")+"box")+";"+h.replace(o,F+o)+";"+h.replace(o,S+o+"box")+";"+h}return h+";";case 938:if(h.charCodeAt(5)===Q)switch(h.charCodeAt(6)){case 105:return o=h.replace("-items",""),F+h+F+"box-"+o+S+"flex-"+o+h;case 115:return F+h+S+"flex-item-"+h.replace(y,"")+h;default:return F+h+S+"flex-line-pack"+h.replace("align-content","").replace(y,"")+h}break;case 973:case 989:if(h.charCodeAt(3)!==Q||122===h.charCodeAt(4))break;case 931:case 953:if(!0===j.test(e))return 115===(o=e.substring(e.indexOf(":")+1)).charCodeAt(0)?Ke(e.replace("stretch","fill-available"),a,c,r).replace(":fill-available",":stretch"):h.replace(o,F+o)+h.replace(o,N+o.replace("fill-",""))+h;break;case 962:if(h=F+h+(102===h.charCodeAt(5)?S+h:"")+h,c+r===211&&105===h.charCodeAt(13)&&h.indexOf("transform",10)>0)return h.substring(0,h.indexOf(";",27)+1).replace(i,"$1"+F+"$2")+h}return h}function Le(e,a){var c=e.indexOf(1===a?":":"{"),r=e.substring(0,3!==a?c:10),s=e.substring(c+1,e.length-1);return Oe(2!==a?r:r.replace(O,"$1"),s,a)}function Me(e,a){var c=Ke(a,a.charCodeAt(0),a.charCodeAt(1),a.charCodeAt(2));return c!==a+";"?c.replace($," or ($1)").substring(4):"("+a+")"}function Pe(e,a,c,r,s,t,i,n,l,o){for(var f,h=0,u=a;h0&&(De=s.replace(d,t===G?"":"-")),t=1,1===ge?Ge=s:Ee=s;var i,n=[Ge];ye>0&&void 0!==(i=Pe(ze,c,n,n,ke,be,0,0,0,0))&&"string"==typeof i&&(c=i);var l=He(xe,n,c,0,0);return ye>0&&void 0!==(i=Pe(je,l,n,n,ke,be,l.length,0,0,0))&&"string"!=typeof(l=i)&&(t=0),De="",Ge="",Ee="",pe=0,ke=1,be=1,we*t==0?l:function(e){return e.replace(r,"").replace(g,"").replace(A,"$1").replace(C,"$1").replace(w," ")}(l)}return Te.use=function e(a){switch(a){case void 0:case null:ye=$e.length=0;break;default:switch(a.constructor){case Array:for(var c=0,r=a.length;c1)for(var n=1;n=0||Object.prototype.hasOwnProperty.call(e,r)&&(n[r]=e[r]);return n},x=function(e,t){if(!e)throw new ReferenceError("this hasn't been initialised - super() hasn't been called");return!t||"object"!=typeof t&&"function"!=typeof t?e:t},C=function(e){return"object"===(void 0===e?"undefined":m(e))&&e.constructor===Object},T=function e(t,r){var o=Object.keys(t).filter(function(e){var n=t[e];return null!=n&&!1!==n&&""!==n}).map(function(r){return C(t[r])?e(t[r],r):(0,n.default)(r)+": "+t[r]+";"}).join(" ");return r?r+" {\n "+o+"\n}":o},I=function e(t,n){return t.reduce(function(t,r){return null==r||!1===r||""===r?t:Array.isArray(r)?(t.push.apply(t,e(r,n)),t):r.hasOwnProperty("styledComponentId")?(t.push("."+r.styledComponentId),t):"function"==typeof r?(n?t.push.apply(t,e([r(n)],n)):t.push(r),t):(t.push(C(r)?T(r):r.toString()),t)},[])},O=/^\s*\/\/.*$/gm,k=new o.default({global:!1,cascade:!0,keyframe:!1,prefix:!1,compress:!1,semicolon:!0}),w=new o.default({global:!1,cascade:!0,keyframe:!1,prefix:!0,compress:!1,semicolon:!1}),M=[],j=function(e){if(-2===e){var t=M;return M=[],t}},R=(0,a.default)(function(e){M.push(e)});w.use([R,j]),k.use([R,j]);var E=function(e,t,n){var r=e.join("").replace(O,"");return w(n||!t?"":t,t&&n?n+" "+t+" { "+r+" }":r)},P=function(e){return k("",e)};function A(e){return"function"==typeof e&&"string"==typeof e.styledComponentId}function N(){0}var _=52,F=function(e){return String.fromCharCode(e+(e>25?39:97))},D=function(e){var t="",n=void 0;for(n=e;n>_;n=Math.floor(n/_))t=F(n%_)+t;return F(n%_)+t},L=function(e,t){for(var n=[e[0]],r=0,o=t.length;r1?t-1:0),r=1;r1?r-1:0),i=1;ir;o-=1)e.deleteRule(o)},ue=function(e){return"\n/* sc-component-id: "+e+" */\n"},ce=function(e,t){for(var n=0,r=0;r<=t;r+=1)n+=e[r];return n},le=function(e,t,n){var r=document.createElement("style");r.setAttribute(H,"");var o=Z();if(o&&r.setAttribute("nonce",o),r.appendChild(document.createTextNode("")),e&&!t)e.appendChild(r);else{if(!t||!e||!t.parentNode)throw new X(6);t.parentNode.insertBefore(r,n?t:t.nextSibling)}return r},pe=function(e,t){return function(n){var r=Z();return""}},de=function(e,t){return function(){var n,r=((n={})[H]=re(t),n),o=Z();return o&&(r.nonce=o),u.default.createElement("style",g({},r,{dangerouslySetInnerHTML:{__html:e()}}))}},he=function(e){return function(){return Object.keys(e)}},fe=function(e,t){var n=Object.create(null),r=Object.create(null),o=[],i=void 0!==t,a=!1,s=function(e){var t=r[e];return void 0!==t?t:(r[e]=o.length,o.push(0),te(n,e),r[e])},u=function(){var t=ie(e).cssRules,n="";for(var i in r){n+=ue(i);for(var a=r[i],s=ce(o,a),u=s-o[a];u0&&(a=!0,t().insertRules(r+"-import",f)),o[l]+=h,ee(n,r,c)},removeRules:function(s){var u=r[s];if(void 0!==u){var c=o[u],l=ie(e),p=ce(o,u);se(l,p,c),o[u]=0,te(n,s),i&&a&&t().removeRules(s+"-import")}},css:u,toHTML:pe(u,n),toElement:de(u,n),clone:function(){throw new X(5)}}},me=function(e){return document.createTextNode(ue(e))},ve=function(e,t){var n=Object.create(null),r=Object.create(null),o=void 0!==t,i=!1,a=function(t){var o=r[t];return void 0!==o?o:(r[t]=me(t),e.appendChild(r[t]),n[t]=Object.create(null),r[t])},s=function(){var e="";for(var t in r)e+=r[t].data;return e};return{clone:function(){throw new X(5)},css:s,getIds:he(r),hasNameForId:ne(n),insertMarker:a,insertRules:function(e,r,s){for(var u=a(e),c=[],l=r.length,p=0;p0&&(i=!0,t().insertRules(e+"-import",c))},removeRules:function(a){var s=r[a];if(void 0!==s){var u=me(a);e.replaceChild(u,s),r[a]=u,te(n,a),o&&i&&t().removeRules(a+"-import")}},styleTag:e,toElement:de(s,n),toHTML:pe(s,n)}},ye=function e(t,n){var r=void 0===t?Object.create(null):t,o=void 0===n?Object.create(null):n,i=function(e){var t=o[e];return void 0!==t?t:o[e]=[""]},a=function(){var e="";for(var t in o){var n=o[t][0];n&&(e+=ue(t)+n)}return e};return{clone:function(){var t=oe(r),n=Object.create(null);for(var i in o)n[i]=[o[i][0]];return e(t,n)},css:a,getIds:he(o),hasNameForId:ne(r),insertMarker:i,insertRules:function(e,t,n){i(e)[0]+=t.join(" "),ee(r,e,n)},removeRules:function(e){var t=o[e];void 0!==t&&(t[0]="",te(r,e))},styleTag:null,toElement:de(a,r),toHTML:pe(a,r)}},ge=function(){return ye()},be=function(e,t,n,r,o){if(Y&&!n){var i=le(e,t,r);return G?ve(i,o):fe(i,o)}return ge()},Se=function(e,t,n,r){var o=Q(function(){for(var r=0,o=n.length;r0&&void 0!==arguments[0]?arguments[0]:Y?document.head:null,r=arguments.length>1&&void 0!==arguments[1]&&arguments[1];v(this,e),this.getImportRuleTag=function(){var e=t.importRuleTag;if(void 0!==e)return e;var n=t.tags[0];return t.importRuleTag=be(t.target,n?n.styleTag:null,t.forceServer,!0)},Ie+=1,this.id=Ie,this.sealed=!1,this.forceServer=r,this.target=r?null:n,this.tagMap={},this.deferred={},this.rehydratedNames={},this.ignoreRehydratedNames={},this.tags=[],this.capacity=1,this.clones=[]}return e.prototype.rehydrate=function(){if(!Y||this.forceServer)return this;var e=[],t=[],n=!1,r=document.querySelectorAll("style["+H+"]"),o=r.length;if(0===o)return this;for(var i=0;i0&&void 0!==arguments[0]&&arguments[0];Oe=new e(void 0,t).rehydrate()},e.prototype.clone=function(){var t=new e(this.target,this.forceServer);return this.clones.push(t),t.tags=this.tags.map(function(e){for(var n=e.getIds(),r=e.clone(),o=0;o=je&&(console.warn("Over "+je+" classes were generated for component "+e+". \nConsider using the attrs method, together with a style object for frequently changed styles.\nExample:\n const Component = styled.div.attrs({\n style: ({ background }) => ({\n background,\n }),\n })`width: 100%;`\n\n "),n=!0,t={}))}},Ee=function(e,t,n){var r=n&&e.theme===n.theme;return e.theme&&!r?e.theme:t},Pe=/[[\].#*$><+~=|^:(),"'`-]+/g,Ae=/(^-|-$)/g;function Ne(e){return e.replace(Pe,"-").replace(Ae,"")}function _e(e){return e.displayName||e.name||"Component"}function Fe(e){return"string"==typeof e}function De(e){return Fe(e)?"styled."+e:"Styled("+_e(e)+")"}var Le=/^((?:s(?:uppressContentEditableWarn|croll|pac)|(?:shape|image|text)Render|(?:letter|word)Spac|vHang|hang)ing|(?:on(?:AnimationIteration|C(?:o(?:mposition(?:Update|Start|End)|ntextMenu|py)|anPlayThrough|anPlay|hange|lick|ut)|(?:Animation|Touch|Load|Drag)Start|(?:(?:Duration|Volume|Rate)Chang|(?:MouseLea|(?:Touch|Mouse)Mo|DragLea)v|Paus)e|Loaded(?:Metad|D)ata|(?:(?:T(?:ransition|ouch)|Animation)E|Suspe)nd|DoubleClick|(?:TouchCanc|Whe)el|Lo(?:stPointer|ad)|TimeUpdate|(?:Mouse(?:Ent|Ov)e|Drag(?:Ent|Ov)e|Erro)r|GotPointer|MouseDown|(?:E(?:n(?:crypt|d)|mpti)|S(?:tall|eek))ed|KeyPress|(?:MouseOu|DragExi|S(?:elec|ubmi)|Rese|Inpu)t|P(?:rogress|laying)|DragEnd|Key(?:Down|Up)|(?:MouseU|Dro)p|(?:Wait|Seek)ing|Scroll|Focus|Paste|Abort|Drag|Play|Blur)Captur|alignmentBaselin|(?:limitingConeAng|xlink(?:(?:Arcr|R)o|Tit)|s(?:urfaceSca|ty|ca)|unselectab|baseProfi|fontSty|(?:focus|dragg)ab|multip|profi|tit)l|d(?:ominantBaselin|efaultValu)|onPointerLeav|a(?:uto(?:Capitaliz|Revers|Sav)|dditiv)|(?:(?:formNoValid|xlinkActu|noValid|accumul|rot)a|autoComple|decelera)t|(?:(?:attribute|item)T|datat)yp|onPointerMov|(?:attribute|glyph)Nam|playsInlin|(?:writing|input|edge)Mod|(?:formE|e)ncTyp|(?:amplitu|mo)d|(?:xlinkTy|itemSco|keyTy|slo)p|(?:xmlSpa|non)c|fillRul|(?:dateTi|na)m|r(?:esourc|ol)|xmlBas|wmod)e|(?:glyphOrientationHorizont|loc)al|(?:externalResourcesRequir|select|revers|mut)ed|c(?:o(?:lorInterpolationFilter|ord)s|o(?:lor(?:Interpolation)?|nt(?:rols|ent))|(?:ontentS(?:cript|tyle)Typ|o(?:ntentEditab|lorProfi)l|l(?:assNam|ipRul)|a(?:lcMod|ptur)|it)e|olorRendering|l(?:ipPathUnits|assID)|(?:ontrolsLis|apHeigh)t|h(?:eckedLink|a(?:llenge|rSet)|ildren|ecked)|ell(?:Spac|Padd)ing|o(?:ntextMenu|ls)|(?:rossOrigi|olSpa)n|l(?:ip(?:Path)?|ass)|ursor|[xy])|glyphOrientationVertical|d(?:angerouslySetInnerHTML|efaultChecked|ownload|isabled|isplay|[xy])|(?:s(?:trikethroughThickn|eaml)es|(?:und|ov)erlineThicknes|r(?:equiredExtension|adiu)|(?:requiredFeatur|tableValu|stitchTil|numOctav|filterR)e|key(?:(?:Splin|Tim)e|Param)|auto[Ff]ocu|header|bia)s|(?:(?:st(?:rikethroughPosi|dDevia)|(?:und|ov)erlinePosi|(?:textDecor|elev)a|orienta)tio|(?:strokeLinejo|orig)i|on(?:PointerDow|FocusI)|formActio|zoomAndPa|directio|(?:vers|act)io|rowSpa|begi|ico)n|o(?:n(?:AnimationIteration|C(?:o(?:mposition(?:Update|Start|End)|ntextMenu|py)|anPlayThrough|anPlay|hange|lick|ut)|(?:(?:Duration|Volume|Rate)Chang|(?:MouseLea|(?:Touch|Mouse)Mo|DragLea)v|Paus)e|Loaded(?:Metad|D)ata|(?:Animation|Touch|Load|Drag)Start|(?:(?:T(?:ransition|ouch)|Animation)E|Suspe)nd|DoubleClick|(?:TouchCanc|Whe)el|(?:Mouse(?:Ent|Ov)e|Drag(?:Ent|Ov)e|Erro)r|TimeUpdate|(?:E(?:n(?:crypt|d)|mpti)|S(?:tall|eek))ed|MouseDown|P(?:rogress|laying)|(?:MouseOu|DragExi|S(?:elec|ubmi)|Rese|Inpu)t|KeyPress|DragEnd|Key(?:Down|Up)|(?:Wait|Seek)ing|(?:MouseU|Dro)p|Scroll|Paste|Focus|Abort|Drag|Play|Load|Blur)|rient)|p(?:reserveA(?:spectRatio|lpha)|ointsAt[X-Z]|anose1)|(?:patternContent|ma(?:sk(?:Content)?|rker)|primitive|gradient|pattern|filter)Units|(?:(?:allowTranspar|baseFrequ)enc|re(?:ferrerPolic|adOnl)|(?:(?:st(?:roke|op)O|floodO|fillO|o)pac|integr|secur)it|visibilit|fontFamil|accessKe|propert|summar)y|(?:gradientT|patternT|t)ransform|(?:[xy]ChannelSelect|lightingCol|textAnch|floodCol|stopCol|operat|htmlF)or|(?:strokeMiterlimi|(?:specularConsta|repeatCou|fontVaria)n|(?:(?:specularE|e)xpon|renderingInt|asc)en|d(?:iffuseConsta|esce)n|(?:fontSizeAdju|lengthAdju|manife)s|baselineShif|onPointerOu|vectorEffec|(?:(?:mar(?:ker|gin)|x)H|accentH|fontW)eigh|markerStar|a(?:utoCorrec|bou)|onFocusOu|intercep|restar|forma|inlis|heigh|lis)t|(?:(?:st(?:rokeDasho|artO)|o)ffs|acceptChars|formTarg|viewTarg|srcS)et|k(?:ernel(?:UnitLength|Matrix)|[1-4])|(?:(?:enableBackgrou|markerE)n|s(?:p(?:readMetho|ee)|ee)|formMetho|(?:markerM|onInval)i|preloa|metho|kin)d|strokeDasharray|(?:onPointerCanc|lab)el|(?:allowFullScre|hidd)en|systemLanguage|(?:(?:o(?:nPointer(?:Ent|Ov)|rd)|allowReord|placehold|frameBord|paintOrd|post)e|repeatDu|d(?:efe|u))r|v(?:Mathematical|ert(?:Origin[XY]|AdvY)|alues|ocab)|(?:pointerEve|keyPoi)nts|(?:strokeLineca|onPointerU|itemPro|useMa|wra|loo)p|h(?:oriz(?:Origin|Adv)X|ttpEquiv)|(?:vI|i)deographic|unicodeRange|mathematical|vAlphabetic|u(?:nicodeBidi|[12])|(?:fontStretc|hig)h|(?:(?:mar(?:ker|gin)W|strokeW)id|azimu)th|(?:xmlnsXl|valueL)ink|mediaGroup|spellCheck|(?:text|m(?:in|ax))Length|(?:unitsPerE|optimu|fro)m|r(?:adioGroup|e(?:sults|f[XY]|l)|ows|[xy])|a(?:rabicForm|l(?:phabetic|t)|sync)|pathLength|innerHTML|xlinkShow|(?:xlinkHr|glyphR)ef|(?:tabInde|(?:sand|b)bo|viewBo)x|(?:(?:href|xml|src)La|kerni)ng|autoPlay|o(?:verflow|pen)|f(?:o(?:ntSize|rm?)|il(?:ter|l))|r(?:e(?:quired|sult|f))?|divisor|p(?:attern|oints)|unicode|d(?:efault|ata|ir)?|i(?:temRef|n2|s)|t(?:arget[XY]|o)|srcDoc|s(?:coped|te(?:m[hv]|p)|pan)|(?:width|size)s|prefix|typeof|itemID|s(?:t(?:roke|art)|hape|cope|rc)|t(?:arget|ype)|(?:stri|la)ng|a(?:ccept|s)|m(?:edia|a(?:sk|x)|in)|x(?:mlns)?|width|value|size|href|k(?:ey)?|end|low|by|i[dn]|y[12]|g[12]|x[12]|f[xy]|[yz])$/,We=":A-Z_a-z\\u00C0-\\u00D6\\u00D8-\\u00F6\\u00F8-\\u02FF\\u0370-\\u037D\\u037F-\\u1FFF\\u200C-\\u200D\\u2070-\\u218F\\u2C00-\\u2FEF\\u3001-\\uD7FF\\uF900-\\uFDCF\\uFDF0-\\uFFFD",Ue=We+"\\-.0-9\\u00B7\\u0300-\\u036F\\u203F-\\u2040",qe=RegExp.prototype.test.bind(new RegExp("^(x|data|aria)-["+Ue+"]*$")),He=function(e){return Le.test(e)||qe(e.toLowerCase())};function Be(e,t){for(var n=e;n;)if((n=Object.getPrototypeOf(n))&&n===t)return!0;return!1}var ze,Ye,Ge=function(e){var t={},n=0,r=e;return{publish:function(e){for(var n in r=e,t){var o=t[n];void 0!==o&&o(r)}},subscribe:function(e){var o=n;return t[o]=e,n+=1,e(r),o},unsubscribe:function(e){t[e]=void 0}}},Ve="__styled-components__",Ke=Ve+"next__",Xe=l.default.shape({getTheme:l.default.func,subscribe:l.default.func,unsubscribe:l.default.func}),$e=((ze={})[Ve]=l.default.func,ze[Ke]=Xe,ze),Je=void 0;var Ze,Qe=function(e){return"function"==typeof e},et=function(e){function t(){v(this,t);var n=x(this,e.call(this));return n.unsubscribeToOuterId=-1,n.getTheme=n.getTheme.bind(n),n}return b(t,e),t.prototype.componentWillMount=function(){var e=this,t=this.context[Ke];void 0!==t&&(this.unsubscribeToOuterId=t.subscribe(function(t){e.outerTheme=t,void 0!==e.broadcast&&e.publish(e.props.theme)})),this.broadcast=Ge(this.getTheme())},t.prototype.getChildContext=function(){var e,t=this;return g({},this.context,((e={})[Ke]={getTheme:this.getTheme,subscribe:this.broadcast.subscribe,unsubscribe:this.broadcast.unsubscribe},e[Ve]=function(e){var n=t.broadcast.subscribe(e);return function(){return t.broadcast.unsubscribe(n)}},e))},t.prototype.componentWillReceiveProps=function(e){this.props.theme!==e.theme&&this.publish(e.theme)},t.prototype.componentWillUnmount=function(){-1!==this.unsubscribeToOuterId&&this.context[Ke].unsubscribe(this.unsubscribeToOuterId)},t.prototype.getTheme=function(e){var t=e||this.props.theme;if(Qe(t)){var n=t(this.outerTheme);return n}if(null===t||Array.isArray(t)||"object"!==(void 0===t?"undefined":m(t)))throw new X(8);return g({},this.outerTheme,t)},t.prototype.publish=function(e){this.broadcast.publish(this.getTheme(e))},t.prototype.render=function(){return this.props.children?u.default.Children.only(this.props.children):null},t}(s.Component);et.childContextTypes=$e,et.contextTypes=((Ye={})[Ke]=Xe,Ye);var tt={},nt=g({},$e,((Ze={})[z]=l.default.oneOfType([l.default.instanceOf(ke),l.default.instanceOf(Me)]),Ze)),rt={},ot=function(e,t,n){var r="string"!=typeof t?"sc":Ne(t),o=(rt[r]||0)+1;rt[r]=o;var i=r+"-"+e.generateName(r+o);return void 0!==n?n+"-"+i:i},it=function(e){function t(){var n,r;v(this,t);for(var o=arguments.length,i=Array(o),a=0;a=4;)t=1540483477*(65535&(t=255&e.charCodeAt(o)|(255&e.charCodeAt(++o))<<8|(255&e.charCodeAt(++o))<<16|(255&e.charCodeAt(++o))<<24))+((1540483477*(t>>>16)&65535)<<16),r=1540483477*(65535&r)+((1540483477*(r>>>16)&65535)<<16)^(t=1540483477*(65535&(t^=t>>>24))+((1540483477*(t>>>16)&65535)<<16)),n-=4,++o;switch(n){case 3:r^=(255&e.charCodeAt(o+2))<<16;case 2:r^=(255&e.charCodeAt(o+1))<<8;case 1:r=1540483477*(65535&(r^=255&e.charCodeAt(o)))+((1540483477*(r>>>16)&65535)<<16)}return r=1540483477*(65535&(r^=r>>>13))+((1540483477*(r>>>16)&65535)<<16),(r^=r>>>15)>>>0}var ut=Y,ct=function e(t,n){for(var r=0,o=t.length;r2&&void 0!==arguments[2]?arguments[2]:U;if(!(0,h.isValidElementType)(r))throw new X(1,String(r));var i=function(){return n(r,o,e.apply(void 0,arguments))};return i.withConfig=function(e){return t(n,r,g({},o,e))},i.attrs=function(e){return t(n,r,g({},o,{attrs:g({},o.attrs||U,e)}))},i}},gt=function(e){var t="function"==typeof e&&!(e.prototype&&"isReactComponent"in e.prototype),n=A(e)||t,r=function(t){function r(){var e,n;v(this,r);for(var o=arguments.length,i=Array(o),a=0;a'.",autoComplete:"off",onChange:function(t){return e.onChange(t)}}),n.default.createElement(r.Button,{innerRef:this.buttonRef},"Send"))}}]),i}();exports.default=i; +},{"react":"HdMw","./Styled":"R0fF"}],"M2pF":[function(require,module,exports) { +var define; +var t;!function(e,n){"object"==typeof exports&&"undefined"!=typeof module?module.exports=n():"function"==typeof t&&t.amd?t(n):e.dayjs=n()}(this,function(){"use strict";var t="millisecond",e="second",n="minute",r="hour",s="day",i="week",a="month",u="year",c=/^(\d{4})-?(\d{1,2})-?(\d{0,2})(.*?(\d{1,2}):(\d{1,2}):(\d{1,2}))?.?(\d{1,3})?$/,o=/\[.*?\]|Y{2,4}|M{1,4}|D{1,2}|d{1,4}|H{1,2}|h{1,2}|a|A|m{1,2}|s{1,2}|Z{1,2}|SSS/g,h={name:"en",weekdays:"Sunday_Monday_Tuesday_Wednesday_Thursday_Friday_Saturday".split("_"),months:"January_February_March_April_May_June_July_August_September_October_November_December".split("_")},d=function(t,e,n){var r=String(t);return!r||r.length>=e?t:""+Array(e+1-r.length).join(n)+t},$={padStart:d,padZoneStr:function(t){var e=Math.abs(t),n=Math.floor(e/60),r=e%60;return(t<=0?"+":"-")+d(n,2,"0")+":"+d(r,2,"0")},monthDiff:function(t,e){var n=12*(e.year()-t.year())+(e.month()-t.month()),r=t.clone().add(n,"months"),s=e-r<0,i=t.clone().add(n+(s?-1:1),"months");return Number(-(n+(e-r)/(s?r-i:i-r)))},absFloor:function(t){return t<0?Math.ceil(t)||0:Math.floor(t)},prettyUnit:function(c){return{M:a,y:u,w:i,d:s,h:r,m:n,s:e,ms:t}[c]||String(c||"").toLowerCase().replace(/s$/,"")},isUndefined:function(t){return void 0===t}},f="en",l={};l[f]=h;var m=function(t){return t instanceof D},y=function(t,e,n){var r;if(!t)return null;if("string"==typeof t)l[t]&&(r=t),e&&(l[t]=e,r=t);else{var s=t.name;l[s]=t,r=s}return n||(f=r),r},M=function(t,e){if(m(t))return t.clone();var n=e||{};return n.date=t,new D(n)},S=function(t,e){return M(t,{locale:e.$L})},p=$;p.parseLocale=y,p.isDayjs=m,p.wrapper=S;var D=function(){function h(t){this.parse(t)}var d=h.prototype;return d.parse=function(t){var e,n;this.$d=null===(e=t.date)?new Date(NaN):p.isUndefined(e)?new Date:e instanceof Date?e:"string"==typeof e&&/.*[^Z]$/i.test(e)&&(n=e.match(c))?new Date(n[1],n[2]-1,n[3]||1,n[5]||0,n[6]||0,n[7]||0,n[8]||0):new Date(e),this.init(t)},d.init=function(t){this.$y=this.$d.getFullYear(),this.$M=this.$d.getMonth(),this.$D=this.$d.getDate(),this.$W=this.$d.getDay(),this.$H=this.$d.getHours(),this.$m=this.$d.getMinutes(),this.$s=this.$d.getSeconds(),this.$ms=this.$d.getMilliseconds(),this.$L=this.$L||y(t.locale,null,!0)||f},d.$utils=function(){return p},d.isValid=function(){return!("Invalid Date"===this.$d.toString())},d.$compare=function(t){return this.valueOf()-M(t).valueOf()},d.isSame=function(t){return 0===this.$compare(t)},d.isBefore=function(t){return this.$compare(t)<0},d.isAfter=function(t){return this.$compare(t)>0},d.year=function(){return this.$y},d.month=function(){return this.$M},d.day=function(){return this.$W},d.date=function(){return this.$D},d.hour=function(){return this.$H},d.minute=function(){return this.$m},d.second=function(){return this.$s},d.millisecond=function(){return this.$ms},d.unix=function(){return Math.floor(this.valueOf()/1e3)},d.valueOf=function(){return this.$d.getTime()},d.startOf=function(t,c){var o=this,h=!!p.isUndefined(c)||c,d=function(t,e){var n=S(new Date(o.$y,e,t),o);return h?n:n.endOf(s)},$=function(t,e){return S(o.toDate()[t].apply(o.toDate(),h?[0,0,0,0].slice(e):[23,59,59,999].slice(e)),o)};switch(p.prettyUnit(t)){case u:return h?d(1,0):d(31,11);case a:return h?d(1,this.$M):d(0,this.$M+1);case i:return d(h?this.$D-this.$W:this.$D+(6-this.$W),this.$M);case s:case"date":return $("setHours",0);case r:return $("setMinutes",1);case n:return $("setSeconds",2);case e:return $("setMilliseconds",3);default:return this.clone()}},d.endOf=function(t){return this.startOf(t,!1)},d.$set=function(i,c){switch(p.prettyUnit(i)){case s:this.$d.setDate(this.$D+(c-this.$W));break;case"date":this.$d.setDate(c);break;case a:this.$d.setMonth(c);break;case u:this.$d.setFullYear(c);break;case r:this.$d.setHours(c);break;case n:this.$d.setMinutes(c);break;case e:this.$d.setSeconds(c);break;case t:this.$d.setMilliseconds(c)}return this.init(),this},d.set=function(t,e){return this.clone().$set(t,e)},d.add=function(t,c){var o=this;t=Number(t);var h,d=p.prettyUnit(c),$=function(e,n){var r=o.set("date",1).set(e,n+t);return r.set("date",Math.min(o.$D,r.daysInMonth()))};if(d===a)return $(a,this.$M);if(d===u)return $(u,this.$y);switch(d){case n:h=6e4;break;case r:h=36e5;break;case s:h=864e5;break;case i:h=6048e5;break;case e:h=1e3;break;default:h=1}var f=this.valueOf()+t*h;return S(f,this)},d.subtract=function(t,e){return this.add(-1*t,e)},d.format=function(t){var e=this,n=t||"YYYY-MM-DDTHH:mm:ssZ",r=p.padZoneStr(this.$d.getTimezoneOffset()),s=this.$locale(),i=s.weekdays,a=s.months,u=function(t,e,n,r){return t&&t[e]||n[e].substr(0,r)};return n.replace(o,function(t){if(t.indexOf("[")>-1)return t.replace(/\[|\]/g,"");switch(t){case"YY":return String(e.$y).slice(-2);case"YYYY":return String(e.$y);case"M":return String(e.$M+1);case"MM":return p.padStart(e.$M+1,2,"0");case"MMM":return u(s.monthsShort,e.$M,a,3);case"MMMM":return a[e.$M];case"D":return String(e.$D);case"DD":return p.padStart(e.$D,2,"0");case"d":return String(e.$W);case"dd":return u(s.weekdaysMin,e.$W,i,2);case"ddd":return u(s.weekdaysShort,e.$W,i,3);case"dddd":return i[e.$W];case"H":return String(e.$H);case"HH":return p.padStart(e.$H,2,"0");case"h":case"hh":return 0===e.$H?12:p.padStart(e.$H<13?e.$H:e.$H-12,"hh"===t?2:1,"0");case"a":return e.$H<12?"am":"pm";case"A":return e.$H<12?"AM":"PM";case"m":return String(e.$m);case"mm":return p.padStart(e.$m,2,"0");case"s":return String(e.$s);case"ss":return p.padStart(e.$s,2,"0");case"SSS":return p.padStart(e.$ms,3,"0");case"Z":return r;default:return r.replace(":","")}})},d.diff=function(t,c,o){var h=p.prettyUnit(c),d=M(t),$=this-d,f=p.monthDiff(this,d);switch(h){case u:f/=12;break;case a:break;case"quarter":f/=3;break;case i:f=$/6048e5;break;case s:f=$/864e5;break;case r:f=$/36e5;break;case n:f=$/6e4;break;case e:f=$/1e3;break;default:f=$}return o?f:p.absFloor(f)},d.daysInMonth=function(){return this.endOf(a).$D},d.$locale=function(){return l[this.$L]},d.locale=function(t,e){var n=this.clone();return n.$L=y(t,e,!0),n},d.clone=function(){return S(this.toDate(),this)},d.toDate=function(){return new Date(this.$d)},d.toArray=function(){return[this.$y,this.$M,this.$D,this.$H,this.$m,this.$s,this.$ms]},d.toJSON=function(){return this.toISOString()},d.toISOString=function(){return this.toDate().toISOString()},d.toObject=function(){return{years:this.$y,months:this.$M,date:this.$D,hours:this.$H,minutes:this.$m,seconds:this.$s,milliseconds:this.$ms}},d.toString=function(){return this.$d.toUTCString()},h}();return M.extend=function(t,e){return t(e,D,M),M},M.locale=y,M.isDayjs=m,M.en=l[f],M}); +},{}],"psKD":[function(require,module,exports) { +var define; +var r;!function(t,e){"object"==typeof exports&&"undefined"!=typeof module?module.exports=e():"function"==typeof r&&r.amd?r(e):t.dayjs_plugin_relativeTime=e()}(this,function(){"use strict";return function(r,t,e){var n=t.prototype;e.en.relativeTime={future:"in %s",past:"%s ago",s:"a few seconds",m:"a minute",mm:"%d minutes",h:"an hour",hh:"%d hours",d:"a day",dd:"%d days",M:"a month",MM:"%d months",y:"a year",yy:"%d years"};var o=function(r,t,n,o){for(var d,a,i=n.$locale().relativeTime,u=[{l:"s",r:44,d:"second"},{l:"m",r:89},{l:"mm",r:44,d:"minute"},{l:"h",r:89},{l:"hh",r:21,d:"hour"},{l:"d",r:35},{l:"dd",r:25,d:"day"},{l:"M",r:45},{l:"MM",r:10,d:"month"},{l:"y",r:17},{l:"yy",d:"year"}],s=u.length,f=0;f0?i.future:i.past).replace("%s",a)};n.to=function(r,t){return o(r,t,this,!0)},n.from=function(r,t){return o(r,t,this)},n.toNow=function(r){return this.to(e(),r)},n.fromNow=function(r){return this.from(e(),r)}}}); +},{}],"Ri+r":[function(require,module,exports) { +"use strict";Object.defineProperty(exports,"__esModule",{value:!0});var e=require("react"),t=d(e),a=require("dayjs"),l=d(a),r=require("dayjs/plugin/relativeTime"),u=d(r),n=require("./Styled");function d(e){return e&&e.__esModule?e:{default:e}}l.default.extend(u.default),exports.default=function(e){var a=e.message;return t.default.createElement(n.MessageWrapper,null,t.default.createElement(n.HorizontalLayout,null,t.default.createElement(n.HorizontalLayout,{justifyContent:"flex-start"},t.default.createElement(n.Author,null,a.author)," ",t.default.createElement(n.MessageText,null,a.message)),t.default.createElement(n.TimeAgo,null,(0,l.default)(a.date).fromNow())),t.default.createElement(n.ImageWrapper,null,a.image?t.default.createElement(n.Image,{src:a.image}):null))}; +},{"react":"HdMw","dayjs":"M2pF","dayjs/plugin/relativeTime":"psKD","./Styled":"R0fF"}],"RTd7":[function(require,module,exports) { +"use strict";Object.defineProperty(exports,"__esModule",{value:!0});var e=function(){function e(e,t){for(var r=0;r0&&void 0!==arguments[0]?arguments[0]:"",t=arguments.length>1&&void 0!==arguments[1]?arguments[1]:{};return fetch(e,{method:"POST",mode:"cors",headers:{"Content-Type":"application/json",Accept:"application/json"},redirect:"follow",referrer:"no-referrer",body:JSON.stringify(t)}).then(function(e){if(e.status>400)throw new Error("Failed status code: "+e.status);return e}).then(function(e){return e.json()})},n=exports.sendMessage=function(e,t,r){return o(s+"/message",{author:e,room:t,message:r})},a=exports.getMessages=function(){return fetch(s+"/message").then(function(e){return e.json()})}; +},{"dayjs":"M2pF"}],"Oy93":[function(require,module,exports) { +"use strict";Object.defineProperty(exports,"__esModule",{value:!0});var e=function(){function e(e,t){for(var r=0;r
\ No newline at end of file diff --git a/chapter-05/webapp/yarn.lock b/chapter-05/webapp/yarn.lock new file mode 100644 index 0000000..64d3cc1 --- /dev/null +++ b/chapter-05/webapp/yarn.lock @@ -0,0 +1,4368 @@ +# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. +# yarn lockfile v1 + + +"@mrmlnc/readdir-enhanced@^2.2.1": + version "2.2.1" + resolved "https://registry.yarnpkg.com/@mrmlnc/readdir-enhanced/-/readdir-enhanced-2.2.1.tgz#524af240d1a360527b730475ecfa1344aa540dde" + dependencies: + call-me-maybe "^1.0.1" + glob-to-regexp "^0.3.0" + +"@nodelib/fs.stat@^1.0.1": + version "1.1.0" + resolved "https://registry.yarnpkg.com/@nodelib/fs.stat/-/fs.stat-1.1.0.tgz#50c1e2260ac0ed9439a181de3725a0168d59c48a" + +abbrev@1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/abbrev/-/abbrev-1.1.1.tgz#f8f2c887ad10bf67f634f005b6987fed3179aac8" + +acorn@^5.0.0: + version "5.7.1" + resolved "https://registry.yarnpkg.com/acorn/-/acorn-5.7.1.tgz#f095829297706a7c9776958c0afc8930a9b9d9d8" + +alphanum-sort@^1.0.0, alphanum-sort@^1.0.1, alphanum-sort@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/alphanum-sort/-/alphanum-sort-1.0.2.tgz#97a1119649b211ad33691d9f9f486a8ec9fbe0a3" + +ansi-regex@^2.0.0: + version "2.1.1" + resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-2.1.1.tgz#c3b33ab5ee360d86e0e628f0468ae7ef27d654df" + +ansi-regex@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-3.0.0.tgz#ed0317c322064f79466c02966bddb605ab37d998" + +ansi-styles@^2.2.1: + version "2.2.1" + resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-2.2.1.tgz#b432dd3358b634cf75e1e4664368240533c1ddbe" + +ansi-styles@^3.2.1: + version "3.2.1" + resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-3.2.1.tgz#41fbb20243e50b12be0f04b8dedbf07520ce841d" + dependencies: + color-convert "^1.9.0" + +ansi-to-html@^0.6.4: + version "0.6.6" + resolved "https://registry.yarnpkg.com/ansi-to-html/-/ansi-to-html-0.6.6.tgz#58a8d04b87ec9a85e3ad273c12a5fbc7147b9c42" + dependencies: + entities "^1.1.1" + +anymatch@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/anymatch/-/anymatch-2.0.0.tgz#bcb24b4f37934d9aa7ac17b4adaf89e7c76ef2eb" + dependencies: + micromatch "^3.1.4" + normalize-path "^2.1.1" + +aproba@^1.0.3: + version "1.2.0" + resolved "https://registry.yarnpkg.com/aproba/-/aproba-1.2.0.tgz#6802e6264efd18c790a1b0d517f0f2627bf2c94a" + +are-we-there-yet@~1.1.2: + version "1.1.5" + resolved "https://registry.yarnpkg.com/are-we-there-yet/-/are-we-there-yet-1.1.5.tgz#4b35c2944f062a8bfcda66410760350fe9ddfc21" + dependencies: + delegates "^1.0.0" + readable-stream "^2.0.6" + +argparse@^1.0.7: + version "1.0.10" + resolved "https://registry.yarnpkg.com/argparse/-/argparse-1.0.10.tgz#bcd6791ea5ae09725e17e5ad988134cd40b3d911" + dependencies: + sprintf-js "~1.0.2" + +arr-diff@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/arr-diff/-/arr-diff-4.0.0.tgz#d6461074febfec71e7e15235761a329a5dc7c520" + +arr-flatten@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/arr-flatten/-/arr-flatten-1.1.0.tgz#36048bbff4e7b47e136644316c99669ea5ae91f1" + +arr-union@^3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/arr-union/-/arr-union-3.1.0.tgz#e39b09aea9def866a8f206e288af63919bae39c4" + +array-unique@^0.3.2: + version "0.3.2" + resolved "https://registry.yarnpkg.com/array-unique/-/array-unique-0.3.2.tgz#a894b75d4bc4f6cd679ef3244a9fd8f46ae2d428" + +asap@~2.0.3: + version "2.0.6" + resolved "https://registry.yarnpkg.com/asap/-/asap-2.0.6.tgz#e50347611d7e690943208bbdafebcbc2fb866d46" + +asn1.js@^4.0.0: + version "4.10.1" + resolved "https://registry.yarnpkg.com/asn1.js/-/asn1.js-4.10.1.tgz#b9c2bf5805f1e64aadeed6df3a2bfafb5a73f5a0" + dependencies: + bn.js "^4.0.0" + inherits "^2.0.1" + minimalistic-assert "^1.0.0" + +assert@^1.1.1: + version "1.4.1" + resolved "https://registry.yarnpkg.com/assert/-/assert-1.4.1.tgz#99912d591836b5a6f5b345c0f07eefc08fc65d91" + dependencies: + util "0.10.3" + +assign-symbols@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/assign-symbols/-/assign-symbols-1.0.0.tgz#59667f41fadd4f20ccbc2bb96b8d4f7f78ec0367" + +async-each@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/async-each/-/async-each-1.0.1.tgz#19d386a1d9edc6e7c1c85d388aedbcc56d33602d" + +async-limiter@~1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/async-limiter/-/async-limiter-1.0.0.tgz#78faed8c3d074ab81f22b4e985d79e8738f720f8" + +atob@^2.1.1: + version "2.1.1" + resolved "https://registry.yarnpkg.com/atob/-/atob-2.1.1.tgz#ae2d5a729477f289d60dd7f96a6314a22dd6c22a" + +autoprefixer@^6.3.1: + version "6.7.7" + resolved "https://registry.yarnpkg.com/autoprefixer/-/autoprefixer-6.7.7.tgz#1dbd1c835658e35ce3f9984099db00585c782014" + dependencies: + browserslist "^1.7.6" + caniuse-db "^1.0.30000634" + normalize-range "^0.1.2" + num2fraction "^1.2.2" + postcss "^5.2.16" + postcss-value-parser "^3.2.3" + +babel-code-frame@^6.26.0: + version "6.26.0" + resolved "https://registry.yarnpkg.com/babel-code-frame/-/babel-code-frame-6.26.0.tgz#63fd43f7dc1e3bb7ce35947db8fe369a3f58c74b" + dependencies: + chalk "^1.1.3" + esutils "^2.0.2" + js-tokens "^3.0.2" + +babel-core@^6.25.0, babel-core@^6.26.0: + version "6.26.3" + resolved "https://registry.yarnpkg.com/babel-core/-/babel-core-6.26.3.tgz#b2e2f09e342d0f0c88e2f02e067794125e75c207" + dependencies: + babel-code-frame "^6.26.0" + babel-generator "^6.26.0" + babel-helpers "^6.24.1" + babel-messages "^6.23.0" + babel-register "^6.26.0" + babel-runtime "^6.26.0" + babel-template "^6.26.0" + babel-traverse "^6.26.0" + babel-types "^6.26.0" + babylon "^6.18.0" + convert-source-map "^1.5.1" + debug "^2.6.9" + json5 "^0.5.1" + lodash "^4.17.4" + minimatch "^3.0.4" + path-is-absolute "^1.0.1" + private "^0.1.8" + slash "^1.0.0" + source-map "^0.5.7" + +babel-generator@^6.25.0, babel-generator@^6.26.0: + version "6.26.1" + resolved "https://registry.yarnpkg.com/babel-generator/-/babel-generator-6.26.1.tgz#1844408d3b8f0d35a404ea7ac180f087a601bd90" + dependencies: + babel-messages "^6.23.0" + babel-runtime "^6.26.0" + babel-types "^6.26.0" + detect-indent "^4.0.0" + jsesc "^1.3.0" + lodash "^4.17.4" + source-map "^0.5.7" + trim-right "^1.0.1" + +babel-helper-builder-binary-assignment-operator-visitor@^6.24.1: + version "6.24.1" + resolved "https://registry.yarnpkg.com/babel-helper-builder-binary-assignment-operator-visitor/-/babel-helper-builder-binary-assignment-operator-visitor-6.24.1.tgz#cce4517ada356f4220bcae8a02c2b346f9a56664" + dependencies: + babel-helper-explode-assignable-expression "^6.24.1" + babel-runtime "^6.22.0" + babel-types "^6.24.1" + +babel-helper-builder-react-jsx@^6.24.1: + version "6.26.0" + resolved "https://registry.yarnpkg.com/babel-helper-builder-react-jsx/-/babel-helper-builder-react-jsx-6.26.0.tgz#39ff8313b75c8b65dceff1f31d383e0ff2a408a0" + dependencies: + babel-runtime "^6.26.0" + babel-types "^6.26.0" + esutils "^2.0.2" + +babel-helper-call-delegate@^6.24.1: + version "6.24.1" + resolved "https://registry.yarnpkg.com/babel-helper-call-delegate/-/babel-helper-call-delegate-6.24.1.tgz#ece6aacddc76e41c3461f88bfc575bd0daa2df8d" + dependencies: + babel-helper-hoist-variables "^6.24.1" + babel-runtime "^6.22.0" + babel-traverse "^6.24.1" + babel-types "^6.24.1" + +babel-helper-define-map@^6.24.1: + version "6.26.0" + resolved "https://registry.yarnpkg.com/babel-helper-define-map/-/babel-helper-define-map-6.26.0.tgz#a5f56dab41a25f97ecb498c7ebaca9819f95be5f" + dependencies: + babel-helper-function-name "^6.24.1" + babel-runtime "^6.26.0" + babel-types "^6.26.0" + lodash "^4.17.4" + +babel-helper-explode-assignable-expression@^6.24.1: + version "6.24.1" + resolved "https://registry.yarnpkg.com/babel-helper-explode-assignable-expression/-/babel-helper-explode-assignable-expression-6.24.1.tgz#f25b82cf7dc10433c55f70592d5746400ac22caa" + dependencies: + babel-runtime "^6.22.0" + babel-traverse "^6.24.1" + babel-types "^6.24.1" + +babel-helper-function-name@^6.24.1: + version "6.24.1" + resolved "https://registry.yarnpkg.com/babel-helper-function-name/-/babel-helper-function-name-6.24.1.tgz#d3475b8c03ed98242a25b48351ab18399d3580a9" + dependencies: + babel-helper-get-function-arity "^6.24.1" + babel-runtime "^6.22.0" + babel-template "^6.24.1" + babel-traverse "^6.24.1" + babel-types "^6.24.1" + +babel-helper-get-function-arity@^6.24.1: + version "6.24.1" + resolved "https://registry.yarnpkg.com/babel-helper-get-function-arity/-/babel-helper-get-function-arity-6.24.1.tgz#8f7782aa93407c41d3aa50908f89b031b1b6853d" + dependencies: + babel-runtime "^6.22.0" + babel-types "^6.24.1" + +babel-helper-hoist-variables@^6.24.1: + version "6.24.1" + resolved "https://registry.yarnpkg.com/babel-helper-hoist-variables/-/babel-helper-hoist-variables-6.24.1.tgz#1ecb27689c9d25513eadbc9914a73f5408be7a76" + dependencies: + babel-runtime "^6.22.0" + babel-types "^6.24.1" + +babel-helper-optimise-call-expression@^6.24.1: + version "6.24.1" + resolved "https://registry.yarnpkg.com/babel-helper-optimise-call-expression/-/babel-helper-optimise-call-expression-6.24.1.tgz#f7a13427ba9f73f8f4fa993c54a97882d1244257" + dependencies: + babel-runtime "^6.22.0" + babel-types "^6.24.1" + +babel-helper-regex@^6.24.1: + version "6.26.0" + resolved "https://registry.yarnpkg.com/babel-helper-regex/-/babel-helper-regex-6.26.0.tgz#325c59f902f82f24b74faceed0363954f6495e72" + dependencies: + babel-runtime "^6.26.0" + babel-types "^6.26.0" + lodash "^4.17.4" + +babel-helper-remap-async-to-generator@^6.24.1: + version "6.24.1" + resolved "https://registry.yarnpkg.com/babel-helper-remap-async-to-generator/-/babel-helper-remap-async-to-generator-6.24.1.tgz#5ec581827ad723fecdd381f1c928390676e4551b" + dependencies: + babel-helper-function-name "^6.24.1" + babel-runtime "^6.22.0" + babel-template "^6.24.1" + babel-traverse "^6.24.1" + babel-types "^6.24.1" + +babel-helper-replace-supers@^6.24.1: + version "6.24.1" + resolved "https://registry.yarnpkg.com/babel-helper-replace-supers/-/babel-helper-replace-supers-6.24.1.tgz#bf6dbfe43938d17369a213ca8a8bf74b6a90ab1a" + dependencies: + babel-helper-optimise-call-expression "^6.24.1" + babel-messages "^6.23.0" + babel-runtime "^6.22.0" + babel-template "^6.24.1" + babel-traverse "^6.24.1" + babel-types "^6.24.1" + +babel-helpers@^6.24.1: + version "6.24.1" + resolved "https://registry.yarnpkg.com/babel-helpers/-/babel-helpers-6.24.1.tgz#3471de9caec388e5c850e597e58a26ddf37602b2" + dependencies: + babel-runtime "^6.22.0" + babel-template "^6.24.1" + +babel-messages@^6.23.0: + version "6.23.0" + resolved "https://registry.yarnpkg.com/babel-messages/-/babel-messages-6.23.0.tgz#f3cdf4703858035b2a2951c6ec5edf6c62f2630e" + dependencies: + babel-runtime "^6.22.0" + +babel-plugin-check-es2015-constants@^6.22.0: + version "6.22.0" + resolved "https://registry.yarnpkg.com/babel-plugin-check-es2015-constants/-/babel-plugin-check-es2015-constants-6.22.0.tgz#35157b101426fd2ffd3da3f75c7d1e91835bbf8a" + dependencies: + babel-runtime "^6.22.0" + +babel-plugin-syntax-async-functions@^6.8.0: + version "6.13.0" + resolved "https://registry.yarnpkg.com/babel-plugin-syntax-async-functions/-/babel-plugin-syntax-async-functions-6.13.0.tgz#cad9cad1191b5ad634bf30ae0872391e0647be95" + +babel-plugin-syntax-class-properties@^6.8.0: + version "6.13.0" + resolved "https://registry.yarnpkg.com/babel-plugin-syntax-class-properties/-/babel-plugin-syntax-class-properties-6.13.0.tgz#d7eb23b79a317f8543962c505b827c7d6cac27de" + +babel-plugin-syntax-exponentiation-operator@^6.8.0: + version "6.13.0" + resolved "https://registry.yarnpkg.com/babel-plugin-syntax-exponentiation-operator/-/babel-plugin-syntax-exponentiation-operator-6.13.0.tgz#9ee7e8337290da95288201a6a57f4170317830de" + +babel-plugin-syntax-flow@^6.18.0: + version "6.18.0" + resolved "https://registry.yarnpkg.com/babel-plugin-syntax-flow/-/babel-plugin-syntax-flow-6.18.0.tgz#4c3ab20a2af26aa20cd25995c398c4eb70310c8d" + +babel-plugin-syntax-jsx@^6.3.13, babel-plugin-syntax-jsx@^6.8.0: + version "6.18.0" + resolved "https://registry.yarnpkg.com/babel-plugin-syntax-jsx/-/babel-plugin-syntax-jsx-6.18.0.tgz#0af32a9a6e13ca7a3fd5069e62d7b0f58d0d8946" + +babel-plugin-syntax-trailing-function-commas@^6.22.0: + version "6.22.0" + resolved "https://registry.yarnpkg.com/babel-plugin-syntax-trailing-function-commas/-/babel-plugin-syntax-trailing-function-commas-6.22.0.tgz#ba0360937f8d06e40180a43fe0d5616fff532cf3" + +babel-plugin-transform-async-to-generator@^6.22.0: + version "6.24.1" + resolved "https://registry.yarnpkg.com/babel-plugin-transform-async-to-generator/-/babel-plugin-transform-async-to-generator-6.24.1.tgz#6536e378aff6cb1d5517ac0e40eb3e9fc8d08761" + dependencies: + babel-helper-remap-async-to-generator "^6.24.1" + babel-plugin-syntax-async-functions "^6.8.0" + babel-runtime "^6.22.0" + +babel-plugin-transform-class-properties@^6.24.1: + version "6.24.1" + resolved "https://registry.yarnpkg.com/babel-plugin-transform-class-properties/-/babel-plugin-transform-class-properties-6.24.1.tgz#6a79763ea61d33d36f37b611aa9def81a81b46ac" + dependencies: + babel-helper-function-name "^6.24.1" + babel-plugin-syntax-class-properties "^6.8.0" + babel-runtime "^6.22.0" + babel-template "^6.24.1" + +babel-plugin-transform-es2015-arrow-functions@^6.22.0: + version "6.22.0" + resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-arrow-functions/-/babel-plugin-transform-es2015-arrow-functions-6.22.0.tgz#452692cb711d5f79dc7f85e440ce41b9f244d221" + dependencies: + babel-runtime "^6.22.0" + +babel-plugin-transform-es2015-block-scoped-functions@^6.22.0: + version "6.22.0" + resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-block-scoped-functions/-/babel-plugin-transform-es2015-block-scoped-functions-6.22.0.tgz#bbc51b49f964d70cb8d8e0b94e820246ce3a6141" + dependencies: + babel-runtime "^6.22.0" + +babel-plugin-transform-es2015-block-scoping@^6.23.0: + version "6.26.0" + resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-block-scoping/-/babel-plugin-transform-es2015-block-scoping-6.26.0.tgz#d70f5299c1308d05c12f463813b0a09e73b1895f" + dependencies: + babel-runtime "^6.26.0" + babel-template "^6.26.0" + babel-traverse "^6.26.0" + babel-types "^6.26.0" + lodash "^4.17.4" + +babel-plugin-transform-es2015-classes@^6.23.0: + version "6.24.1" + resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-classes/-/babel-plugin-transform-es2015-classes-6.24.1.tgz#5a4c58a50c9c9461e564b4b2a3bfabc97a2584db" + dependencies: + babel-helper-define-map "^6.24.1" + babel-helper-function-name "^6.24.1" + babel-helper-optimise-call-expression "^6.24.1" + babel-helper-replace-supers "^6.24.1" + babel-messages "^6.23.0" + babel-runtime "^6.22.0" + babel-template "^6.24.1" + babel-traverse "^6.24.1" + babel-types "^6.24.1" + +babel-plugin-transform-es2015-computed-properties@^6.22.0: + version "6.24.1" + resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-computed-properties/-/babel-plugin-transform-es2015-computed-properties-6.24.1.tgz#6fe2a8d16895d5634f4cd999b6d3480a308159b3" + dependencies: + babel-runtime "^6.22.0" + babel-template "^6.24.1" + +babel-plugin-transform-es2015-destructuring@^6.23.0: + version "6.23.0" + resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-destructuring/-/babel-plugin-transform-es2015-destructuring-6.23.0.tgz#997bb1f1ab967f682d2b0876fe358d60e765c56d" + dependencies: + babel-runtime "^6.22.0" + +babel-plugin-transform-es2015-duplicate-keys@^6.22.0: + version "6.24.1" + resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-duplicate-keys/-/babel-plugin-transform-es2015-duplicate-keys-6.24.1.tgz#73eb3d310ca969e3ef9ec91c53741a6f1576423e" + dependencies: + babel-runtime "^6.22.0" + babel-types "^6.24.1" + +babel-plugin-transform-es2015-for-of@^6.23.0: + version "6.23.0" + resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-for-of/-/babel-plugin-transform-es2015-for-of-6.23.0.tgz#f47c95b2b613df1d3ecc2fdb7573623c75248691" + dependencies: + babel-runtime "^6.22.0" + +babel-plugin-transform-es2015-function-name@^6.22.0: + version "6.24.1" + resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-function-name/-/babel-plugin-transform-es2015-function-name-6.24.1.tgz#834c89853bc36b1af0f3a4c5dbaa94fd8eacaa8b" + dependencies: + babel-helper-function-name "^6.24.1" + babel-runtime "^6.22.0" + babel-types "^6.24.1" + +babel-plugin-transform-es2015-literals@^6.22.0: + version "6.22.0" + resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-literals/-/babel-plugin-transform-es2015-literals-6.22.0.tgz#4f54a02d6cd66cf915280019a31d31925377ca2e" + dependencies: + babel-runtime "^6.22.0" + +babel-plugin-transform-es2015-modules-amd@^6.22.0, babel-plugin-transform-es2015-modules-amd@^6.24.1: + version "6.24.1" + resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-modules-amd/-/babel-plugin-transform-es2015-modules-amd-6.24.1.tgz#3b3e54017239842d6d19c3011c4bd2f00a00d154" + dependencies: + babel-plugin-transform-es2015-modules-commonjs "^6.24.1" + babel-runtime "^6.22.0" + babel-template "^6.24.1" + +babel-plugin-transform-es2015-modules-commonjs@^6.23.0, babel-plugin-transform-es2015-modules-commonjs@^6.24.1, babel-plugin-transform-es2015-modules-commonjs@^6.26.0: + version "6.26.2" + resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-modules-commonjs/-/babel-plugin-transform-es2015-modules-commonjs-6.26.2.tgz#58a793863a9e7ca870bdc5a881117ffac27db6f3" + dependencies: + babel-plugin-transform-strict-mode "^6.24.1" + babel-runtime "^6.26.0" + babel-template "^6.26.0" + babel-types "^6.26.0" + +babel-plugin-transform-es2015-modules-systemjs@^6.23.0: + version "6.24.1" + resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-modules-systemjs/-/babel-plugin-transform-es2015-modules-systemjs-6.24.1.tgz#ff89a142b9119a906195f5f106ecf305d9407d23" + dependencies: + babel-helper-hoist-variables "^6.24.1" + babel-runtime "^6.22.0" + babel-template "^6.24.1" + +babel-plugin-transform-es2015-modules-umd@^6.23.0: + version "6.24.1" + resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-modules-umd/-/babel-plugin-transform-es2015-modules-umd-6.24.1.tgz#ac997e6285cd18ed6176adb607d602344ad38468" + dependencies: + babel-plugin-transform-es2015-modules-amd "^6.24.1" + babel-runtime "^6.22.0" + babel-template "^6.24.1" + +babel-plugin-transform-es2015-object-super@^6.22.0: + version "6.24.1" + resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-object-super/-/babel-plugin-transform-es2015-object-super-6.24.1.tgz#24cef69ae21cb83a7f8603dad021f572eb278f8d" + dependencies: + babel-helper-replace-supers "^6.24.1" + babel-runtime "^6.22.0" + +babel-plugin-transform-es2015-parameters@^6.23.0: + version "6.24.1" + resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-parameters/-/babel-plugin-transform-es2015-parameters-6.24.1.tgz#57ac351ab49caf14a97cd13b09f66fdf0a625f2b" + dependencies: + babel-helper-call-delegate "^6.24.1" + babel-helper-get-function-arity "^6.24.1" + babel-runtime "^6.22.0" + babel-template "^6.24.1" + babel-traverse "^6.24.1" + babel-types "^6.24.1" + +babel-plugin-transform-es2015-shorthand-properties@^6.22.0: + version "6.24.1" + resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-shorthand-properties/-/babel-plugin-transform-es2015-shorthand-properties-6.24.1.tgz#24f875d6721c87661bbd99a4622e51f14de38aa0" + dependencies: + babel-runtime "^6.22.0" + babel-types "^6.24.1" + +babel-plugin-transform-es2015-spread@^6.22.0: + version "6.22.0" + resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-spread/-/babel-plugin-transform-es2015-spread-6.22.0.tgz#d6d68a99f89aedc4536c81a542e8dd9f1746f8d1" + dependencies: + babel-runtime "^6.22.0" + +babel-plugin-transform-es2015-sticky-regex@^6.22.0: + version "6.24.1" + resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-sticky-regex/-/babel-plugin-transform-es2015-sticky-regex-6.24.1.tgz#00c1cdb1aca71112cdf0cf6126c2ed6b457ccdbc" + dependencies: + babel-helper-regex "^6.24.1" + babel-runtime "^6.22.0" + babel-types "^6.24.1" + +babel-plugin-transform-es2015-template-literals@^6.22.0: + version "6.22.0" + resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-template-literals/-/babel-plugin-transform-es2015-template-literals-6.22.0.tgz#a84b3450f7e9f8f1f6839d6d687da84bb1236d8d" + dependencies: + babel-runtime "^6.22.0" + +babel-plugin-transform-es2015-typeof-symbol@^6.23.0: + version "6.23.0" + resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-typeof-symbol/-/babel-plugin-transform-es2015-typeof-symbol-6.23.0.tgz#dec09f1cddff94b52ac73d505c84df59dcceb372" + dependencies: + babel-runtime "^6.22.0" + +babel-plugin-transform-es2015-unicode-regex@^6.22.0: + version "6.24.1" + resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-unicode-regex/-/babel-plugin-transform-es2015-unicode-regex-6.24.1.tgz#d38b12f42ea7323f729387f18a7c5ae1faeb35e9" + dependencies: + babel-helper-regex "^6.24.1" + babel-runtime "^6.22.0" + regexpu-core "^2.0.0" + +babel-plugin-transform-exponentiation-operator@^6.22.0: + version "6.24.1" + resolved "https://registry.yarnpkg.com/babel-plugin-transform-exponentiation-operator/-/babel-plugin-transform-exponentiation-operator-6.24.1.tgz#2ab0c9c7f3098fa48907772bb813fe41e8de3a0e" + dependencies: + babel-helper-builder-binary-assignment-operator-visitor "^6.24.1" + babel-plugin-syntax-exponentiation-operator "^6.8.0" + babel-runtime "^6.22.0" + +babel-plugin-transform-flow-strip-types@^6.22.0: + version "6.22.0" + resolved "https://registry.yarnpkg.com/babel-plugin-transform-flow-strip-types/-/babel-plugin-transform-flow-strip-types-6.22.0.tgz#84cb672935d43714fdc32bce84568d87441cf7cf" + dependencies: + babel-plugin-syntax-flow "^6.18.0" + babel-runtime "^6.22.0" + +babel-plugin-transform-react-display-name@^6.23.0: + version "6.25.0" + resolved "https://registry.yarnpkg.com/babel-plugin-transform-react-display-name/-/babel-plugin-transform-react-display-name-6.25.0.tgz#67e2bf1f1e9c93ab08db96792e05392bf2cc28d1" + dependencies: + babel-runtime "^6.22.0" + +babel-plugin-transform-react-jsx-self@^6.22.0: + version "6.22.0" + resolved "https://registry.yarnpkg.com/babel-plugin-transform-react-jsx-self/-/babel-plugin-transform-react-jsx-self-6.22.0.tgz#df6d80a9da2612a121e6ddd7558bcbecf06e636e" + dependencies: + babel-plugin-syntax-jsx "^6.8.0" + babel-runtime "^6.22.0" + +babel-plugin-transform-react-jsx-source@^6.22.0: + version "6.22.0" + resolved "https://registry.yarnpkg.com/babel-plugin-transform-react-jsx-source/-/babel-plugin-transform-react-jsx-source-6.22.0.tgz#66ac12153f5cd2d17b3c19268f4bf0197f44ecd6" + dependencies: + babel-plugin-syntax-jsx "^6.8.0" + babel-runtime "^6.22.0" + +babel-plugin-transform-react-jsx@^6.24.1: + version "6.24.1" + resolved "https://registry.yarnpkg.com/babel-plugin-transform-react-jsx/-/babel-plugin-transform-react-jsx-6.24.1.tgz#840a028e7df460dfc3a2d29f0c0d91f6376e66a3" + dependencies: + babel-helper-builder-react-jsx "^6.24.1" + babel-plugin-syntax-jsx "^6.8.0" + babel-runtime "^6.22.0" + +babel-plugin-transform-regenerator@^6.22.0: + version "6.26.0" + resolved "https://registry.yarnpkg.com/babel-plugin-transform-regenerator/-/babel-plugin-transform-regenerator-6.26.0.tgz#e0703696fbde27f0a3efcacf8b4dca2f7b3a8f2f" + dependencies: + regenerator-transform "^0.10.0" + +babel-plugin-transform-strict-mode@^6.24.1: + version "6.24.1" + resolved "https://registry.yarnpkg.com/babel-plugin-transform-strict-mode/-/babel-plugin-transform-strict-mode-6.24.1.tgz#d5faf7aa578a65bbe591cf5edae04a0c67020758" + dependencies: + babel-runtime "^6.22.0" + babel-types "^6.24.1" + +babel-preset-env@^1.7.0: + version "1.7.0" + resolved "https://registry.yarnpkg.com/babel-preset-env/-/babel-preset-env-1.7.0.tgz#dea79fa4ebeb883cd35dab07e260c1c9c04df77a" + dependencies: + babel-plugin-check-es2015-constants "^6.22.0" + babel-plugin-syntax-trailing-function-commas "^6.22.0" + babel-plugin-transform-async-to-generator "^6.22.0" + babel-plugin-transform-es2015-arrow-functions "^6.22.0" + babel-plugin-transform-es2015-block-scoped-functions "^6.22.0" + babel-plugin-transform-es2015-block-scoping "^6.23.0" + babel-plugin-transform-es2015-classes "^6.23.0" + babel-plugin-transform-es2015-computed-properties "^6.22.0" + babel-plugin-transform-es2015-destructuring "^6.23.0" + babel-plugin-transform-es2015-duplicate-keys "^6.22.0" + babel-plugin-transform-es2015-for-of "^6.23.0" + babel-plugin-transform-es2015-function-name "^6.22.0" + babel-plugin-transform-es2015-literals "^6.22.0" + babel-plugin-transform-es2015-modules-amd "^6.22.0" + babel-plugin-transform-es2015-modules-commonjs "^6.23.0" + babel-plugin-transform-es2015-modules-systemjs "^6.23.0" + babel-plugin-transform-es2015-modules-umd "^6.23.0" + babel-plugin-transform-es2015-object-super "^6.22.0" + babel-plugin-transform-es2015-parameters "^6.23.0" + babel-plugin-transform-es2015-shorthand-properties "^6.22.0" + babel-plugin-transform-es2015-spread "^6.22.0" + babel-plugin-transform-es2015-sticky-regex "^6.22.0" + babel-plugin-transform-es2015-template-literals "^6.22.0" + babel-plugin-transform-es2015-typeof-symbol "^6.23.0" + babel-plugin-transform-es2015-unicode-regex "^6.22.0" + babel-plugin-transform-exponentiation-operator "^6.22.0" + babel-plugin-transform-regenerator "^6.22.0" + browserslist "^3.2.6" + invariant "^2.2.2" + semver "^5.3.0" + +babel-preset-flow@^6.23.0: + version "6.23.0" + resolved "https://registry.yarnpkg.com/babel-preset-flow/-/babel-preset-flow-6.23.0.tgz#e71218887085ae9a24b5be4169affb599816c49d" + dependencies: + babel-plugin-transform-flow-strip-types "^6.22.0" + +babel-preset-react@^6.24.1: + version "6.24.1" + resolved "https://registry.yarnpkg.com/babel-preset-react/-/babel-preset-react-6.24.1.tgz#ba69dfaea45fc3ec639b6a4ecea6e17702c91380" + dependencies: + babel-plugin-syntax-jsx "^6.3.13" + babel-plugin-transform-react-display-name "^6.23.0" + babel-plugin-transform-react-jsx "^6.24.1" + babel-plugin-transform-react-jsx-self "^6.22.0" + babel-plugin-transform-react-jsx-source "^6.22.0" + babel-preset-flow "^6.23.0" + +babel-register@^6.26.0: + version "6.26.0" + resolved "https://registry.yarnpkg.com/babel-register/-/babel-register-6.26.0.tgz#6ed021173e2fcb486d7acb45c6009a856f647071" + dependencies: + babel-core "^6.26.0" + babel-runtime "^6.26.0" + core-js "^2.5.0" + home-or-tmp "^2.0.0" + lodash "^4.17.4" + mkdirp "^0.5.1" + source-map-support "^0.4.15" + +babel-runtime@^6.11.6, babel-runtime@^6.18.0, babel-runtime@^6.22.0, babel-runtime@^6.26.0: + version "6.26.0" + resolved "https://registry.yarnpkg.com/babel-runtime/-/babel-runtime-6.26.0.tgz#965c7058668e82b55d7bfe04ff2337bc8b5647fe" + dependencies: + core-js "^2.4.0" + regenerator-runtime "^0.11.0" + +babel-template@^6.24.1, babel-template@^6.26.0: + version "6.26.0" + resolved "https://registry.yarnpkg.com/babel-template/-/babel-template-6.26.0.tgz#de03e2d16396b069f46dd9fff8521fb1a0e35e02" + dependencies: + babel-runtime "^6.26.0" + babel-traverse "^6.26.0" + babel-types "^6.26.0" + babylon "^6.18.0" + lodash "^4.17.4" + +babel-traverse@^6.24.1, babel-traverse@^6.26.0: + version "6.26.0" + resolved "https://registry.yarnpkg.com/babel-traverse/-/babel-traverse-6.26.0.tgz#46a9cbd7edcc62c8e5c064e2d2d8d0f4035766ee" + dependencies: + babel-code-frame "^6.26.0" + babel-messages "^6.23.0" + babel-runtime "^6.26.0" + babel-types "^6.26.0" + babylon "^6.18.0" + debug "^2.6.8" + globals "^9.18.0" + invariant "^2.2.2" + lodash "^4.17.4" + +babel-types@^6.15.0, babel-types@^6.19.0, babel-types@^6.24.1, babel-types@^6.26.0: + version "6.26.0" + resolved "https://registry.yarnpkg.com/babel-types/-/babel-types-6.26.0.tgz#a3b073f94ab49eb6fa55cd65227a334380632497" + dependencies: + babel-runtime "^6.26.0" + esutils "^2.0.2" + lodash "^4.17.4" + to-fast-properties "^1.0.3" + +babylon-walk@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/babylon-walk/-/babylon-walk-1.0.2.tgz#3b15a5ddbb482a78b4ce9c01c8ba181702d9d6ce" + dependencies: + babel-runtime "^6.11.6" + babel-types "^6.15.0" + lodash.clone "^4.5.0" + +babylon@^6.17.4, babylon@^6.18.0: + version "6.18.0" + resolved "https://registry.yarnpkg.com/babylon/-/babylon-6.18.0.tgz#af2f3b88fa6f5c1e4c634d1a0f8eac4f55b395e3" + +balanced-match@^0.4.2: + version "0.4.2" + resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-0.4.2.tgz#cb3f3e3c732dc0f01ee70b403f302e61d7709838" + +balanced-match@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-1.0.0.tgz#89b4d199ab2bee49de164ea02b89ce462d71b767" + +base64-js@^1.0.2: + version "1.3.0" + resolved "https://registry.yarnpkg.com/base64-js/-/base64-js-1.3.0.tgz#cab1e6118f051095e58b5281aea8c1cd22bfc0e3" + +base@^0.11.1: + version "0.11.2" + resolved "https://registry.yarnpkg.com/base/-/base-0.11.2.tgz#7bde5ced145b6d551a90db87f83c558b4eb48a8f" + dependencies: + cache-base "^1.0.1" + class-utils "^0.3.5" + component-emitter "^1.2.1" + define-property "^1.0.0" + isobject "^3.0.1" + mixin-deep "^1.2.0" + pascalcase "^0.1.1" + +binary-extensions@^1.0.0: + version "1.11.0" + resolved "https://registry.yarnpkg.com/binary-extensions/-/binary-extensions-1.11.0.tgz#46aa1751fb6a2f93ee5e689bb1087d4b14c6c205" + +bindings@~1.2.1: + version "1.2.1" + resolved "https://registry.yarnpkg.com/bindings/-/bindings-1.2.1.tgz#14ad6113812d2d37d72e67b4cacb4bb726505f11" + +bluebird@^3.0.5: + version "3.5.1" + resolved "https://registry.yarnpkg.com/bluebird/-/bluebird-3.5.1.tgz#d9551f9de98f1fcda1e683d17ee91a0602ee2eb9" + +bn.js@^4.0.0, bn.js@^4.1.0, bn.js@^4.1.1, bn.js@^4.4.0: + version "4.11.8" + resolved "https://registry.yarnpkg.com/bn.js/-/bn.js-4.11.8.tgz#2cde09eb5ee341f484746bb0309b3253b1b1442f" + +boolbase@^1.0.0, boolbase@~1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/boolbase/-/boolbase-1.0.0.tgz#68dff5fbe60c51eb37725ea9e3ed310dcc1e776e" + +brace-expansion@^1.1.7: + version "1.1.11" + resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-1.1.11.tgz#3c7fcbf529d87226f3d2f52b966ff5271eb441dd" + dependencies: + balanced-match "^1.0.0" + concat-map "0.0.1" + +braces@^2.3.0, braces@^2.3.1: + version "2.3.2" + resolved "https://registry.yarnpkg.com/braces/-/braces-2.3.2.tgz#5979fd3f14cd531565e5fa2df1abfff1dfaee729" + dependencies: + arr-flatten "^1.1.0" + array-unique "^0.3.2" + extend-shallow "^2.0.1" + fill-range "^4.0.0" + isobject "^3.0.1" + repeat-element "^1.1.2" + snapdragon "^0.8.1" + snapdragon-node "^2.0.1" + split-string "^3.0.2" + to-regex "^3.0.1" + +brfs@^1.2.0: + version "1.6.1" + resolved "https://registry.yarnpkg.com/brfs/-/brfs-1.6.1.tgz#b78ce2336d818e25eea04a0947cba6d4fb8849c3" + dependencies: + quote-stream "^1.0.1" + resolve "^1.1.5" + static-module "^2.2.0" + through2 "^2.0.0" + +brorand@^1.0.1: + version "1.1.0" + resolved "https://registry.yarnpkg.com/brorand/-/brorand-1.1.0.tgz#12c25efe40a45e3c323eb8675a0a0ce57b22371f" + +browserify-aes@^1.0.0, browserify-aes@^1.0.4: + version "1.2.0" + resolved "https://registry.yarnpkg.com/browserify-aes/-/browserify-aes-1.2.0.tgz#326734642f403dabc3003209853bb70ad428ef48" + dependencies: + buffer-xor "^1.0.3" + cipher-base "^1.0.0" + create-hash "^1.1.0" + evp_bytestokey "^1.0.3" + inherits "^2.0.1" + safe-buffer "^5.0.1" + +browserify-cipher@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/browserify-cipher/-/browserify-cipher-1.0.1.tgz#8d6474c1b870bfdabcd3bcfcc1934a10e94f15f0" + dependencies: + browserify-aes "^1.0.4" + browserify-des "^1.0.0" + evp_bytestokey "^1.0.0" + +browserify-des@^1.0.0: + version "1.0.2" + resolved "https://registry.yarnpkg.com/browserify-des/-/browserify-des-1.0.2.tgz#3af4f1f59839403572f1c66204375f7a7f703e9c" + dependencies: + cipher-base "^1.0.1" + des.js "^1.0.0" + inherits "^2.0.1" + safe-buffer "^5.1.2" + +browserify-rsa@^4.0.0: + version "4.0.1" + resolved "https://registry.yarnpkg.com/browserify-rsa/-/browserify-rsa-4.0.1.tgz#21e0abfaf6f2029cf2fafb133567a701d4135524" + dependencies: + bn.js "^4.1.0" + randombytes "^2.0.1" + +browserify-sign@^4.0.0: + version "4.0.4" + resolved "https://registry.yarnpkg.com/browserify-sign/-/browserify-sign-4.0.4.tgz#aa4eb68e5d7b658baa6bf6a57e630cbd7a93d298" + dependencies: + bn.js "^4.1.1" + browserify-rsa "^4.0.0" + create-hash "^1.1.0" + create-hmac "^1.1.2" + elliptic "^6.0.0" + inherits "^2.0.1" + parse-asn1 "^5.0.0" + +browserify-zlib@^0.2.0: + version "0.2.0" + resolved "https://registry.yarnpkg.com/browserify-zlib/-/browserify-zlib-0.2.0.tgz#2869459d9aa3be245fe8fe2ca1f46e2e7f54d73f" + dependencies: + pako "~1.0.5" + +browserslist@^1.3.6, browserslist@^1.5.2, browserslist@^1.7.6: + version "1.7.7" + resolved "https://registry.yarnpkg.com/browserslist/-/browserslist-1.7.7.tgz#0bd76704258be829b2398bb50e4b62d1a166b0b9" + dependencies: + caniuse-db "^1.0.30000639" + electron-to-chromium "^1.2.7" + +browserslist@^3.2.6: + version "3.2.8" + resolved "https://registry.yarnpkg.com/browserslist/-/browserslist-3.2.8.tgz#b0005361d6471f0f5952797a76fc985f1f978fc6" + dependencies: + caniuse-lite "^1.0.30000844" + electron-to-chromium "^1.3.47" + +browserslist@^4.0.0: + version "4.0.2" + resolved "https://registry.yarnpkg.com/browserslist/-/browserslist-4.0.2.tgz#294388f5844bb3ab15ef7394ca17f49bf7a4e6f1" + dependencies: + caniuse-lite "^1.0.30000876" + electron-to-chromium "^1.3.57" + node-releases "^1.0.0-alpha.11" + +buffer-equal@0.0.1: + version "0.0.1" + resolved "https://registry.yarnpkg.com/buffer-equal/-/buffer-equal-0.0.1.tgz#91bc74b11ea405bc916bc6aa908faafa5b4aac4b" + +buffer-from@^1.0.0: + version "1.1.1" + resolved "https://registry.yarnpkg.com/buffer-from/-/buffer-from-1.1.1.tgz#32713bc028f75c02fdb710d7c7bcec1f2c6070ef" + +buffer-xor@^1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/buffer-xor/-/buffer-xor-1.0.3.tgz#26e61ed1422fb70dd42e6e36729ed51d855fe8d9" + +buffer@^4.3.0: + version "4.9.1" + resolved "https://registry.yarnpkg.com/buffer/-/buffer-4.9.1.tgz#6d1bb601b07a4efced97094132093027c95bc298" + dependencies: + base64-js "^1.0.2" + ieee754 "^1.1.4" + isarray "^1.0.0" + +buffer@^5.0.3: + version "5.2.0" + resolved "https://registry.yarnpkg.com/buffer/-/buffer-5.2.0.tgz#53cf98241100099e9eeae20ee6d51d21b16e541e" + dependencies: + base64-js "^1.0.2" + ieee754 "^1.1.4" + +builtin-status-codes@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/builtin-status-codes/-/builtin-status-codes-3.0.0.tgz#85982878e21b98e1c66425e03d0174788f569ee8" + +cache-base@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/cache-base/-/cache-base-1.0.1.tgz#0a7f46416831c8b662ee36fe4e7c59d76f666ab2" + dependencies: + collection-visit "^1.0.0" + component-emitter "^1.2.1" + get-value "^2.0.6" + has-value "^1.0.0" + isobject "^3.0.1" + set-value "^2.0.0" + to-object-path "^0.3.0" + union-value "^1.0.0" + unset-value "^1.0.0" + +call-me-maybe@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/call-me-maybe/-/call-me-maybe-1.0.1.tgz#26d208ea89e37b5cbde60250a15f031c16a4d66b" + +caniuse-api@^1.5.2: + version "1.6.1" + resolved "https://registry.yarnpkg.com/caniuse-api/-/caniuse-api-1.6.1.tgz#b534e7c734c4f81ec5fbe8aca2ad24354b962c6c" + dependencies: + browserslist "^1.3.6" + caniuse-db "^1.0.30000529" + lodash.memoize "^4.1.2" + lodash.uniq "^4.5.0" + +caniuse-api@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/caniuse-api/-/caniuse-api-3.0.0.tgz#5e4d90e2274961d46291997df599e3ed008ee4c0" + dependencies: + browserslist "^4.0.0" + caniuse-lite "^1.0.0" + lodash.memoize "^4.1.2" + lodash.uniq "^4.5.0" + +caniuse-db@^1.0.30000529, caniuse-db@^1.0.30000634, caniuse-db@^1.0.30000639: + version "1.0.30000877" + resolved "https://registry.yarnpkg.com/caniuse-db/-/caniuse-db-1.0.30000877.tgz#29ea435fdbe8a671cc5b027a75e28a816c17c340" + +caniuse-lite@^1.0.0, caniuse-lite@^1.0.30000844, caniuse-lite@^1.0.30000876: + version "1.0.30000877" + resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30000877.tgz#f189673b86ecc06436520e3e391de6a13ca923b4" + +chalk@^1.1.3: + version "1.1.3" + resolved "https://registry.yarnpkg.com/chalk/-/chalk-1.1.3.tgz#a8115c55e4a702fe4d150abd3872822a7e09fc98" + dependencies: + ansi-styles "^2.2.1" + escape-string-regexp "^1.0.2" + has-ansi "^2.0.0" + strip-ansi "^3.0.0" + supports-color "^2.0.0" + +chalk@^2.0.1, chalk@^2.1.0, chalk@^2.3.1, chalk@^2.4.1: + version "2.4.1" + resolved "https://registry.yarnpkg.com/chalk/-/chalk-2.4.1.tgz#18c49ab16a037b6eb0152cc83e3471338215b66e" + dependencies: + ansi-styles "^3.2.1" + escape-string-regexp "^1.0.5" + supports-color "^5.3.0" + +chokidar@^2.0.3: + version "2.0.4" + resolved "https://registry.yarnpkg.com/chokidar/-/chokidar-2.0.4.tgz#356ff4e2b0e8e43e322d18a372460bbcf3accd26" + dependencies: + anymatch "^2.0.0" + async-each "^1.0.0" + braces "^2.3.0" + glob-parent "^3.1.0" + inherits "^2.0.1" + is-binary-path "^1.0.0" + is-glob "^4.0.0" + lodash.debounce "^4.0.8" + normalize-path "^2.1.1" + path-is-absolute "^1.0.0" + readdirp "^2.0.0" + upath "^1.0.5" + optionalDependencies: + fsevents "^1.2.2" + +chownr@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/chownr/-/chownr-1.0.1.tgz#e2a75042a9551908bebd25b8523d5f9769d79181" + +cipher-base@^1.0.0, cipher-base@^1.0.1, cipher-base@^1.0.3: + version "1.0.4" + resolved "https://registry.yarnpkg.com/cipher-base/-/cipher-base-1.0.4.tgz#8760e4ecc272f4c363532f926d874aae2c1397de" + dependencies: + inherits "^2.0.1" + safe-buffer "^5.0.1" + +clap@^1.0.9: + version "1.2.3" + resolved "https://registry.yarnpkg.com/clap/-/clap-1.2.3.tgz#4f36745b32008492557f46412d66d50cb99bce51" + dependencies: + chalk "^1.1.3" + +class-utils@^0.3.5: + version "0.3.6" + resolved "https://registry.yarnpkg.com/class-utils/-/class-utils-0.3.6.tgz#f93369ae8b9a7ce02fd41faad0ca83033190c463" + dependencies: + arr-union "^3.1.0" + define-property "^0.2.5" + isobject "^3.0.0" + static-extend "^0.1.1" + +cli-cursor@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/cli-cursor/-/cli-cursor-2.1.0.tgz#b35dac376479facc3e94747d41d0d0f5238ffcb5" + dependencies: + restore-cursor "^2.0.0" + +cli-spinners@^1.1.0: + version "1.3.1" + resolved "https://registry.yarnpkg.com/cli-spinners/-/cli-spinners-1.3.1.tgz#002c1990912d0d59580c93bd36c056de99e4259a" + +clone@^1.0.2: + version "1.0.4" + resolved "https://registry.yarnpkg.com/clone/-/clone-1.0.4.tgz#da309cc263df15994c688ca902179ca3c7cd7c7e" + +clone@^2.1.1: + version "2.1.2" + resolved "https://registry.yarnpkg.com/clone/-/clone-2.1.2.tgz#1b7f4b9f591f1e8f83670401600345a02887435f" + +clones@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/clones/-/clones-1.1.0.tgz#87e904132d6140c5c0b72006c08c0d05bd7b63b3" + +coa@~1.0.1: + version "1.0.4" + resolved "https://registry.yarnpkg.com/coa/-/coa-1.0.4.tgz#a9ef153660d6a86a8bdec0289a5c684d217432fd" + dependencies: + q "^1.1.2" + +coa@~2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/coa/-/coa-2.0.1.tgz#f3f8b0b15073e35d70263fb1042cb2c023db38af" + dependencies: + q "^1.1.2" + +code-point-at@^1.0.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/code-point-at/-/code-point-at-1.1.0.tgz#0d070b4d043a5bea33a2f1a40e2edb3d9a4ccf77" + +collection-visit@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/collection-visit/-/collection-visit-1.0.0.tgz#4bc0373c164bc3291b4d368c829cf1a80a59dca0" + dependencies: + map-visit "^1.0.0" + object-visit "^1.0.0" + +color-convert@^1.3.0, color-convert@^1.9.0, color-convert@^1.9.1: + version "1.9.2" + resolved "https://registry.yarnpkg.com/color-convert/-/color-convert-1.9.2.tgz#49881b8fba67df12a96bdf3f56c0aab9e7913147" + dependencies: + color-name "1.1.1" + +color-name@1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.1.tgz#4b1415304cf50028ea81643643bd82ea05803689" + +color-name@^1.0.0: + version "1.1.3" + resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.3.tgz#a7d0558bd89c42f795dd42328f740831ca53bc25" + +color-string@^0.3.0: + version "0.3.0" + resolved "https://registry.yarnpkg.com/color-string/-/color-string-0.3.0.tgz#27d46fb67025c5c2fa25993bfbf579e47841b991" + dependencies: + color-name "^1.0.0" + +color-string@^1.5.2: + version "1.5.3" + resolved "https://registry.yarnpkg.com/color-string/-/color-string-1.5.3.tgz#c9bbc5f01b58b5492f3d6857459cb6590ce204cc" + dependencies: + color-name "^1.0.0" + simple-swizzle "^0.2.2" + +color@^0.11.0: + version "0.11.4" + resolved "https://registry.yarnpkg.com/color/-/color-0.11.4.tgz#6d7b5c74fb65e841cd48792ad1ed5e07b904d764" + dependencies: + clone "^1.0.2" + color-convert "^1.3.0" + color-string "^0.3.0" + +color@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/color/-/color-3.0.0.tgz#d920b4328d534a3ac8295d68f7bd4ba6c427be9a" + dependencies: + color-convert "^1.9.1" + color-string "^1.5.2" + +colormin@^1.0.5: + version "1.1.2" + resolved "https://registry.yarnpkg.com/colormin/-/colormin-1.1.2.tgz#ea2f7420a72b96881a38aae59ec124a6f7298133" + dependencies: + color "^0.11.0" + css-color-names "0.0.4" + has "^1.0.1" + +colors@~1.1.2: + version "1.1.2" + resolved "https://registry.yarnpkg.com/colors/-/colors-1.1.2.tgz#168a4701756b6a7f51a12ce0c97bfa28c084ed63" + +command-exists@^1.2.6: + version "1.2.7" + resolved "https://registry.yarnpkg.com/command-exists/-/command-exists-1.2.7.tgz#16828f0c3ff2b0c58805861ef211b64fc15692a8" + +commander@^2.11.0, commander@^2.9.0: + version "2.17.1" + resolved "https://registry.yarnpkg.com/commander/-/commander-2.17.1.tgz#bd77ab7de6de94205ceacc72f1716d29f20a77bf" + +commander@~2.16.0: + version "2.16.0" + resolved "https://registry.yarnpkg.com/commander/-/commander-2.16.0.tgz#f16390593996ceb4f3eeb020b31d78528f7f8a50" + +component-emitter@^1.2.1: + version "1.2.1" + resolved "https://registry.yarnpkg.com/component-emitter/-/component-emitter-1.2.1.tgz#137918d6d78283f7df7a6b7c5a63e140e69425e6" + +concat-map@0.0.1: + version "0.0.1" + resolved "https://registry.yarnpkg.com/concat-map/-/concat-map-0.0.1.tgz#d8a96bd77fd68df7793a73036a3ba0d5405d477b" + +concat-stream@~1.6.0: + version "1.6.2" + resolved "https://registry.yarnpkg.com/concat-stream/-/concat-stream-1.6.2.tgz#904bdf194cd3122fc675c77fc4ac3d4ff0fd1a34" + dependencies: + buffer-from "^1.0.0" + inherits "^2.0.3" + readable-stream "^2.2.2" + typedarray "^0.0.6" + +config-chain@~1.1.5: + version "1.1.11" + resolved "https://registry.yarnpkg.com/config-chain/-/config-chain-1.1.11.tgz#aba09747dfbe4c3e70e766a6e41586e1859fc6f2" + dependencies: + ini "^1.3.4" + proto-list "~1.2.1" + +console-browserify@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/console-browserify/-/console-browserify-1.1.0.tgz#f0241c45730a9fc6323b206dbf38edc741d0bb10" + dependencies: + date-now "^0.1.4" + +console-control-strings@^1.0.0, console-control-strings@~1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/console-control-strings/-/console-control-strings-1.1.0.tgz#3d7cf4464db6446ea644bf4b39507f9851008e8e" + +constants-browserify@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/constants-browserify/-/constants-browserify-1.0.0.tgz#c20b96d8c617748aaf1c16021760cd27fcb8cb75" + +convert-source-map@^1.5.1: + version "1.5.1" + resolved "https://registry.yarnpkg.com/convert-source-map/-/convert-source-map-1.5.1.tgz#b8278097b9bc229365de5c62cf5fcaed8b5599e5" + +copy-descriptor@^0.1.0: + version "0.1.1" + resolved "https://registry.yarnpkg.com/copy-descriptor/-/copy-descriptor-0.1.1.tgz#676f6eb3c39997c2ee1ac3a924fd6124748f578d" + +core-js@^1.0.0: + version "1.2.7" + resolved "https://registry.yarnpkg.com/core-js/-/core-js-1.2.7.tgz#652294c14651db28fa93bd2d5ff2983a4f08c636" + +core-js@^2.4.0, core-js@^2.5.0: + version "2.5.7" + resolved "https://registry.yarnpkg.com/core-js/-/core-js-2.5.7.tgz#f972608ff0cead68b841a16a932d0b183791814e" + +core-util-is@~1.0.0: + version "1.0.2" + resolved "https://registry.yarnpkg.com/core-util-is/-/core-util-is-1.0.2.tgz#b5fd54220aa2bc5ab57aab7140c940754503c1a7" + +cosmiconfig@^5.0.0: + version "5.0.6" + resolved "https://registry.yarnpkg.com/cosmiconfig/-/cosmiconfig-5.0.6.tgz#dca6cf680a0bd03589aff684700858c81abeeb39" + dependencies: + is-directory "^0.3.1" + js-yaml "^3.9.0" + parse-json "^4.0.0" + +create-ecdh@^4.0.0: + version "4.0.3" + resolved "https://registry.yarnpkg.com/create-ecdh/-/create-ecdh-4.0.3.tgz#c9111b6f33045c4697f144787f9254cdc77c45ff" + dependencies: + bn.js "^4.1.0" + elliptic "^6.0.0" + +create-hash@^1.1.0, create-hash@^1.1.2: + version "1.2.0" + resolved "https://registry.yarnpkg.com/create-hash/-/create-hash-1.2.0.tgz#889078af11a63756bcfb59bd221996be3a9ef196" + dependencies: + cipher-base "^1.0.1" + inherits "^2.0.1" + md5.js "^1.3.4" + ripemd160 "^2.0.1" + sha.js "^2.4.0" + +create-hmac@^1.1.0, create-hmac@^1.1.2, create-hmac@^1.1.4: + version "1.1.7" + resolved "https://registry.yarnpkg.com/create-hmac/-/create-hmac-1.1.7.tgz#69170c78b3ab957147b2b8b04572e47ead2243ff" + dependencies: + cipher-base "^1.0.3" + create-hash "^1.1.0" + inherits "^2.0.1" + ripemd160 "^2.0.0" + safe-buffer "^5.0.1" + sha.js "^2.4.8" + +create-react-context@^0.2.2: + version "0.2.2" + resolved "https://registry.yarnpkg.com/create-react-context/-/create-react-context-0.2.2.tgz#9836542f9aaa22868cd7d4a6f82667df38019dca" + dependencies: + fbjs "^0.8.0" + gud "^1.0.0" + +cross-spawn@^6.0.4: + version "6.0.5" + resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-6.0.5.tgz#4a5ec7c64dfae22c3a14124dbacdee846d80cbc4" + dependencies: + nice-try "^1.0.4" + path-key "^2.0.1" + semver "^5.5.0" + shebang-command "^1.2.0" + which "^1.2.9" + +crypto-browserify@^3.11.0: + version "3.12.0" + resolved "https://registry.yarnpkg.com/crypto-browserify/-/crypto-browserify-3.12.0.tgz#396cf9f3137f03e4b8e532c58f698254e00f80ec" + dependencies: + browserify-cipher "^1.0.0" + browserify-sign "^4.0.0" + create-ecdh "^4.0.0" + create-hash "^1.1.0" + create-hmac "^1.1.0" + diffie-hellman "^5.0.0" + inherits "^2.0.1" + pbkdf2 "^3.0.3" + public-encrypt "^4.0.0" + randombytes "^2.0.0" + randomfill "^1.0.3" + +css-color-keywords@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/css-color-keywords/-/css-color-keywords-1.0.0.tgz#fea2616dc676b2962686b3af8dbdbe180b244e05" + +css-color-names@0.0.4, css-color-names@^0.0.4: + version "0.0.4" + resolved "https://registry.yarnpkg.com/css-color-names/-/css-color-names-0.0.4.tgz#808adc2e79cf84738069b646cb20ec27beb629e0" + +css-declaration-sorter@^3.0.0: + version "3.0.1" + resolved "https://registry.yarnpkg.com/css-declaration-sorter/-/css-declaration-sorter-3.0.1.tgz#d0e3056b0fd88dc1ea9dceff435adbe9c702a7f8" + dependencies: + postcss "^6.0.0" + timsort "^0.3.0" + +css-select-base-adapter@~0.1.0: + version "0.1.0" + resolved "https://registry.yarnpkg.com/css-select-base-adapter/-/css-select-base-adapter-0.1.0.tgz#0102b3d14630df86c3eb9fa9f5456270106cf990" + +css-select@~1.3.0-rc0: + version "1.3.0-rc0" + resolved "https://registry.yarnpkg.com/css-select/-/css-select-1.3.0-rc0.tgz#6f93196aaae737666ea1036a8cb14a8fcb7a9231" + dependencies: + boolbase "^1.0.0" + css-what "2.1" + domutils "1.5.1" + nth-check "^1.0.1" + +css-to-react-native@^2.0.3: + version "2.2.1" + resolved "https://registry.yarnpkg.com/css-to-react-native/-/css-to-react-native-2.2.1.tgz#7f3f4c95de65501b8720c87bf0caf1f39073b88e" + dependencies: + css-color-keywords "^1.0.0" + fbjs "^0.8.5" + postcss-value-parser "^3.3.0" + +css-tree@1.0.0-alpha.29: + version "1.0.0-alpha.29" + resolved "https://registry.yarnpkg.com/css-tree/-/css-tree-1.0.0-alpha.29.tgz#3fa9d4ef3142cbd1c301e7664c1f352bd82f5a39" + dependencies: + mdn-data "~1.1.0" + source-map "^0.5.3" + +css-tree@1.0.0-alpha25: + version "1.0.0-alpha25" + resolved "https://registry.yarnpkg.com/css-tree/-/css-tree-1.0.0-alpha25.tgz#1bbfabfbf6eeef4f01d9108ff2edd0be2fe35597" + dependencies: + mdn-data "^1.0.0" + source-map "^0.5.3" + +css-unit-converter@^1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/css-unit-converter/-/css-unit-converter-1.1.1.tgz#d9b9281adcfd8ced935bdbaba83786897f64e996" + +css-url-regex@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/css-url-regex/-/css-url-regex-1.1.0.tgz#83834230cc9f74c457de59eebd1543feeb83b7ec" + +css-what@2.1: + version "2.1.0" + resolved "https://registry.yarnpkg.com/css-what/-/css-what-2.1.0.tgz#9467d032c38cfaefb9f2d79501253062f87fa1bd" + +cssnano-preset-default@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/cssnano-preset-default/-/cssnano-preset-default-4.0.0.tgz#c334287b4f7d49fb2d170a92f9214655788e3b6b" + dependencies: + css-declaration-sorter "^3.0.0" + cssnano-util-raw-cache "^4.0.0" + postcss "^6.0.0" + postcss-calc "^6.0.0" + postcss-colormin "^4.0.0" + postcss-convert-values "^4.0.0" + postcss-discard-comments "^4.0.0" + postcss-discard-duplicates "^4.0.0" + postcss-discard-empty "^4.0.0" + postcss-discard-overridden "^4.0.0" + postcss-merge-longhand "^4.0.0" + postcss-merge-rules "^4.0.0" + postcss-minify-font-values "^4.0.0" + postcss-minify-gradients "^4.0.0" + postcss-minify-params "^4.0.0" + postcss-minify-selectors "^4.0.0" + postcss-normalize-charset "^4.0.0" + postcss-normalize-display-values "^4.0.0" + postcss-normalize-positions "^4.0.0" + postcss-normalize-repeat-style "^4.0.0" + postcss-normalize-string "^4.0.0" + postcss-normalize-timing-functions "^4.0.0" + postcss-normalize-unicode "^4.0.0" + postcss-normalize-url "^4.0.0" + postcss-normalize-whitespace "^4.0.0" + postcss-ordered-values "^4.0.0" + postcss-reduce-initial "^4.0.0" + postcss-reduce-transforms "^4.0.0" + postcss-svgo "^4.0.0" + postcss-unique-selectors "^4.0.0" + +cssnano-util-get-arguments@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/cssnano-util-get-arguments/-/cssnano-util-get-arguments-4.0.0.tgz#ed3a08299f21d75741b20f3b81f194ed49cc150f" + +cssnano-util-get-match@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/cssnano-util-get-match/-/cssnano-util-get-match-4.0.0.tgz#c0e4ca07f5386bb17ec5e52250b4f5961365156d" + +cssnano-util-raw-cache@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/cssnano-util-raw-cache/-/cssnano-util-raw-cache-4.0.0.tgz#be0a2856e25f185f5f7a2bcc0624e28b7f179a9f" + dependencies: + postcss "^6.0.0" + +cssnano-util-same-parent@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/cssnano-util-same-parent/-/cssnano-util-same-parent-4.0.0.tgz#d2a3de1039aa98bc4ec25001fa050330c2a16dac" + +cssnano@^3.4.0: + version "3.10.0" + resolved "https://registry.yarnpkg.com/cssnano/-/cssnano-3.10.0.tgz#4f38f6cea2b9b17fa01490f23f1dc68ea65c1c38" + dependencies: + autoprefixer "^6.3.1" + decamelize "^1.1.2" + defined "^1.0.0" + has "^1.0.1" + object-assign "^4.0.1" + postcss "^5.0.14" + postcss-calc "^5.2.0" + postcss-colormin "^2.1.8" + postcss-convert-values "^2.3.4" + postcss-discard-comments "^2.0.4" + postcss-discard-duplicates "^2.0.1" + postcss-discard-empty "^2.0.1" + postcss-discard-overridden "^0.1.1" + postcss-discard-unused "^2.2.1" + postcss-filter-plugins "^2.0.0" + postcss-merge-idents "^2.1.5" + postcss-merge-longhand "^2.0.1" + postcss-merge-rules "^2.0.3" + postcss-minify-font-values "^1.0.2" + postcss-minify-gradients "^1.0.1" + postcss-minify-params "^1.0.4" + postcss-minify-selectors "^2.0.4" + postcss-normalize-charset "^1.1.0" + postcss-normalize-url "^3.0.7" + postcss-ordered-values "^2.1.0" + postcss-reduce-idents "^2.2.2" + postcss-reduce-initial "^1.0.0" + postcss-reduce-transforms "^1.0.3" + postcss-svgo "^2.1.1" + postcss-unique-selectors "^2.0.2" + postcss-value-parser "^3.2.3" + postcss-zindex "^2.0.1" + +cssnano@^4.0.0: + version "4.0.5" + resolved "https://registry.yarnpkg.com/cssnano/-/cssnano-4.0.5.tgz#8789b5fdbe7be05d8a0f7e45c4c789ebe712f5aa" + dependencies: + cosmiconfig "^5.0.0" + cssnano-preset-default "^4.0.0" + is-resolvable "^1.0.0" + postcss "^6.0.0" + +csso@^3.5.0: + version "3.5.1" + resolved "https://registry.yarnpkg.com/csso/-/csso-3.5.1.tgz#7b9eb8be61628973c1b261e169d2f024008e758b" + dependencies: + css-tree "1.0.0-alpha.29" + +csso@~2.3.1: + version "2.3.2" + resolved "https://registry.yarnpkg.com/csso/-/csso-2.3.2.tgz#ddd52c587033f49e94b71fc55569f252e8ff5f85" + dependencies: + clap "^1.0.9" + source-map "^0.5.3" + +date-now@^0.1.4: + version "0.1.4" + resolved "https://registry.yarnpkg.com/date-now/-/date-now-0.1.4.tgz#eaf439fd4d4848ad74e5cc7dbef200672b9e345b" + +dayjs@^1.7.5: + version "1.7.5" + resolved "https://registry.yarnpkg.com/dayjs/-/dayjs-1.7.5.tgz#14715cb565d1f8cb556a8531cb14bf1fc33067cc" + +deasync@^0.1.13: + version "0.1.13" + resolved "https://registry.yarnpkg.com/deasync/-/deasync-0.1.13.tgz#815c2b69bbd1117cae570152cd895661c09f20ea" + dependencies: + bindings "~1.2.1" + nan "^2.0.7" + +debug@2.6.9, debug@^2.1.2, debug@^2.2.0, debug@^2.3.3, debug@^2.6.8, debug@^2.6.9: + version "2.6.9" + resolved "https://registry.yarnpkg.com/debug/-/debug-2.6.9.tgz#5d128515df134ff327e90a4c93f4e077a536341f" + dependencies: + ms "2.0.0" + +decamelize@^1.1.2: + version "1.2.0" + resolved "https://registry.yarnpkg.com/decamelize/-/decamelize-1.2.0.tgz#f6534d15148269b20352e7bee26f501f9a191290" + +decode-uri-component@^0.2.0: + version "0.2.0" + resolved "https://registry.yarnpkg.com/decode-uri-component/-/decode-uri-component-0.2.0.tgz#eb3913333458775cb84cd1a1fae062106bb87545" + +deep-extend@^0.6.0: + version "0.6.0" + resolved "https://registry.yarnpkg.com/deep-extend/-/deep-extend-0.6.0.tgz#c4fa7c95404a17a9c3e8ca7e1537312b736330ac" + +deep-is@~0.1.3: + version "0.1.3" + resolved "https://registry.yarnpkg.com/deep-is/-/deep-is-0.1.3.tgz#b369d6fb5dbc13eecf524f91b070feedc357cf34" + +deepmerge@^2.1.1: + version "2.1.1" + resolved "https://registry.yarnpkg.com/deepmerge/-/deepmerge-2.1.1.tgz#e862b4e45ea0555072bf51e7fd0d9845170ae768" + +defaults@^1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/defaults/-/defaults-1.0.3.tgz#c656051e9817d9ff08ed881477f3fe4019f3ef7d" + dependencies: + clone "^1.0.2" + +define-properties@^1.1.2: + version "1.1.3" + resolved "https://registry.yarnpkg.com/define-properties/-/define-properties-1.1.3.tgz#cf88da6cbee26fe6db7094f61d870cbd84cee9f1" + dependencies: + object-keys "^1.0.12" + +define-property@^0.2.5: + version "0.2.5" + resolved "https://registry.yarnpkg.com/define-property/-/define-property-0.2.5.tgz#c35b1ef918ec3c990f9a5bc57be04aacec5c8116" + dependencies: + is-descriptor "^0.1.0" + +define-property@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/define-property/-/define-property-1.0.0.tgz#769ebaaf3f4a63aad3af9e8d304c9bbe79bfb0e6" + dependencies: + is-descriptor "^1.0.0" + +define-property@^2.0.2: + version "2.0.2" + resolved "https://registry.yarnpkg.com/define-property/-/define-property-2.0.2.tgz#d459689e8d654ba77e02a817f8710d702cb16e9d" + dependencies: + is-descriptor "^1.0.2" + isobject "^3.0.1" + +defined@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/defined/-/defined-1.0.0.tgz#c98d9bcef75674188e110969151199e39b1fa693" + +delegates@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/delegates/-/delegates-1.0.0.tgz#84c6e159b81904fdca59a0ef44cd870d31250f9a" + +depd@~1.1.2: + version "1.1.2" + resolved "https://registry.yarnpkg.com/depd/-/depd-1.1.2.tgz#9bcd52e14c097763e749b274c4346ed2e560b5a9" + +des.js@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/des.js/-/des.js-1.0.0.tgz#c074d2e2aa6a8a9a07dbd61f9a15c2cd83ec8ecc" + dependencies: + inherits "^2.0.1" + minimalistic-assert "^1.0.0" + +destroy@~1.0.4: + version "1.0.4" + resolved "https://registry.yarnpkg.com/destroy/-/destroy-1.0.4.tgz#978857442c44749e4206613e37946205826abd80" + +detect-indent@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/detect-indent/-/detect-indent-4.0.0.tgz#f76d064352cdf43a1cb6ce619c4ee3a9475de208" + dependencies: + repeating "^2.0.0" + +detect-libc@^1.0.2: + version "1.0.3" + resolved "https://registry.yarnpkg.com/detect-libc/-/detect-libc-1.0.3.tgz#fa137c4bd698edf55cd5cd02ac559f91a4c4ba9b" + +diffie-hellman@^5.0.0: + version "5.0.3" + resolved "https://registry.yarnpkg.com/diffie-hellman/-/diffie-hellman-5.0.3.tgz#40e8ee98f55a2149607146921c63e1ae5f3d2875" + dependencies: + bn.js "^4.1.0" + miller-rabin "^4.0.0" + randombytes "^2.0.0" + +dom-serializer@0: + version "0.1.0" + resolved "https://registry.yarnpkg.com/dom-serializer/-/dom-serializer-0.1.0.tgz#073c697546ce0780ce23be4a28e293e40bc30c82" + dependencies: + domelementtype "~1.1.1" + entities "~1.1.1" + +domain-browser@^1.1.1: + version "1.2.0" + resolved "https://registry.yarnpkg.com/domain-browser/-/domain-browser-1.2.0.tgz#3d31f50191a6749dd1375a7f522e823d42e54eda" + +domelementtype@1, domelementtype@^1.3.0: + version "1.3.0" + resolved "https://registry.yarnpkg.com/domelementtype/-/domelementtype-1.3.0.tgz#b17aed82e8ab59e52dd9c19b1756e0fc187204c2" + +domelementtype@~1.1.1: + version "1.1.3" + resolved "https://registry.yarnpkg.com/domelementtype/-/domelementtype-1.1.3.tgz#bd28773e2642881aec51544924299c5cd822185b" + +domhandler@^2.3.0: + version "2.4.2" + resolved "https://registry.yarnpkg.com/domhandler/-/domhandler-2.4.2.tgz#8805097e933d65e85546f726d60f5eb88b44f803" + dependencies: + domelementtype "1" + +domutils@1.5.1: + version "1.5.1" + resolved "https://registry.yarnpkg.com/domutils/-/domutils-1.5.1.tgz#dcd8488a26f563d61079e48c9f7b7e32373682cf" + dependencies: + dom-serializer "0" + domelementtype "1" + +domutils@^1.5.1: + version "1.7.0" + resolved "https://registry.yarnpkg.com/domutils/-/domutils-1.7.0.tgz#56ea341e834e06e6748af7a1cb25da67ea9f8c2a" + dependencies: + dom-serializer "0" + domelementtype "1" + +dot-prop@^4.1.1: + version "4.2.0" + resolved "https://registry.yarnpkg.com/dot-prop/-/dot-prop-4.2.0.tgz#1f19e0c2e1aa0e32797c49799f2837ac6af69c57" + dependencies: + is-obj "^1.0.0" + +dotenv@^5.0.0: + version "5.0.1" + resolved "https://registry.yarnpkg.com/dotenv/-/dotenv-5.0.1.tgz#a5317459bd3d79ab88cff6e44057a6a3fbb1fcef" + +duplexer2@~0.1.4: + version "0.1.4" + resolved "https://registry.yarnpkg.com/duplexer2/-/duplexer2-0.1.4.tgz#8b12dab878c0d69e3e7891051662a32fc6bddcc1" + dependencies: + readable-stream "^2.0.2" + +editorconfig@^0.13.2: + version "0.13.3" + resolved "https://registry.yarnpkg.com/editorconfig/-/editorconfig-0.13.3.tgz#e5219e587951d60958fd94ea9a9a008cdeff1b34" + dependencies: + bluebird "^3.0.5" + commander "^2.9.0" + lru-cache "^3.2.0" + semver "^5.1.0" + sigmund "^1.0.1" + +ee-first@1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/ee-first/-/ee-first-1.1.1.tgz#590c61156b0ae2f4f0255732a158b266bc56b21d" + +electron-to-chromium@^1.2.7, electron-to-chromium@^1.3.47, electron-to-chromium@^1.3.57: + version "1.3.58" + resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.3.58.tgz#8267a4000014e93986d9d18c65a8b4022ca75188" + +elliptic@^6.0.0: + version "6.4.1" + resolved "https://registry.yarnpkg.com/elliptic/-/elliptic-6.4.1.tgz#c2d0b7776911b86722c632c3c06c60f2f819939a" + dependencies: + bn.js "^4.4.0" + brorand "^1.0.1" + hash.js "^1.0.0" + hmac-drbg "^1.0.0" + inherits "^2.0.1" + minimalistic-assert "^1.0.0" + minimalistic-crypto-utils "^1.0.0" + +encodeurl@~1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/encodeurl/-/encodeurl-1.0.2.tgz#ad3ff4c86ec2d029322f5a02c3a9a606c95b3f59" + +encoding@^0.1.11: + version "0.1.12" + resolved "https://registry.yarnpkg.com/encoding/-/encoding-0.1.12.tgz#538b66f3ee62cd1ab51ec323829d1f9480c74beb" + dependencies: + iconv-lite "~0.4.13" + +entities@^1.1.1, entities@~1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/entities/-/entities-1.1.1.tgz#6e5c2d0a5621b5dadaecef80b90edfb5cd7772f0" + +error-ex@^1.3.1: + version "1.3.2" + resolved "https://registry.yarnpkg.com/error-ex/-/error-ex-1.3.2.tgz#b4ac40648107fdcdcfae242f428bea8a14d4f1bf" + dependencies: + is-arrayish "^0.2.1" + +es-abstract@^1.5.1, es-abstract@^1.6.1: + version "1.12.0" + resolved "https://registry.yarnpkg.com/es-abstract/-/es-abstract-1.12.0.tgz#9dbbdd27c6856f0001421ca18782d786bf8a6165" + dependencies: + es-to-primitive "^1.1.1" + function-bind "^1.1.1" + has "^1.0.1" + is-callable "^1.1.3" + is-regex "^1.0.4" + +es-to-primitive@^1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/es-to-primitive/-/es-to-primitive-1.1.1.tgz#45355248a88979034b6792e19bb81f2b7975dd0d" + dependencies: + is-callable "^1.1.1" + is-date-object "^1.0.1" + is-symbol "^1.0.1" + +escape-html@~1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/escape-html/-/escape-html-1.0.3.tgz#0258eae4d3d0c0974de1c169188ef0051d1d1988" + +escape-string-regexp@^1.0.2, escape-string-regexp@^1.0.5: + version "1.0.5" + resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz#1b61c0562190a8dff6ae3bb2cf0200ca130b86d4" + +escodegen@^1.8.1: + version "1.11.0" + resolved "https://registry.yarnpkg.com/escodegen/-/escodegen-1.11.0.tgz#b27a9389481d5bfd5bec76f7bb1eb3f8f4556589" + dependencies: + esprima "^3.1.3" + estraverse "^4.2.0" + esutils "^2.0.2" + optionator "^0.8.1" + optionalDependencies: + source-map "~0.6.1" + +escodegen@~1.9.0: + version "1.9.1" + resolved "https://registry.yarnpkg.com/escodegen/-/escodegen-1.9.1.tgz#dbae17ef96c8e4bedb1356f4504fa4cc2f7cb7e2" + dependencies: + esprima "^3.1.3" + estraverse "^4.2.0" + esutils "^2.0.2" + optionator "^0.8.1" + optionalDependencies: + source-map "~0.6.1" + +esprima@^2.6.0: + version "2.7.3" + resolved "https://registry.yarnpkg.com/esprima/-/esprima-2.7.3.tgz#96e3b70d5779f6ad49cd032673d1c312767ba581" + +esprima@^3.1.3: + version "3.1.3" + resolved "https://registry.yarnpkg.com/esprima/-/esprima-3.1.3.tgz#fdca51cee6133895e3c88d535ce49dbff62a4633" + +esprima@^4.0.0: + version "4.0.1" + resolved "https://registry.yarnpkg.com/esprima/-/esprima-4.0.1.tgz#13b04cdb3e6c5d19df91ab6987a8695619b0aa71" + +estraverse@^4.2.0: + version "4.2.0" + resolved "https://registry.yarnpkg.com/estraverse/-/estraverse-4.2.0.tgz#0dee3fed31fcd469618ce7342099fc1afa0bdb13" + +esutils@^2.0.2: + version "2.0.2" + resolved "https://registry.yarnpkg.com/esutils/-/esutils-2.0.2.tgz#0abf4f1caa5bcb1f7a9d8acc6dea4faaa04bac9b" + +etag@~1.8.1: + version "1.8.1" + resolved "https://registry.yarnpkg.com/etag/-/etag-1.8.1.tgz#41ae2eeb65efa62268aebfea83ac7d79299b0887" + +events@^1.0.0: + version "1.1.1" + resolved "https://registry.yarnpkg.com/events/-/events-1.1.1.tgz#9ebdb7635ad099c70dcc4c2a1f5004288e8bd924" + +evp_bytestokey@^1.0.0, evp_bytestokey@^1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/evp_bytestokey/-/evp_bytestokey-1.0.3.tgz#7fcbdb198dc71959432efe13842684e0525acb02" + dependencies: + md5.js "^1.3.4" + safe-buffer "^5.1.1" + +expand-brackets@^2.1.4: + version "2.1.4" + resolved "https://registry.yarnpkg.com/expand-brackets/-/expand-brackets-2.1.4.tgz#b77735e315ce30f6b6eff0f83b04151a22449622" + dependencies: + debug "^2.3.3" + define-property "^0.2.5" + extend-shallow "^2.0.1" + posix-character-classes "^0.1.0" + regex-not "^1.0.0" + snapdragon "^0.8.1" + to-regex "^3.0.1" + +extend-shallow@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/extend-shallow/-/extend-shallow-2.0.1.tgz#51af7d614ad9a9f610ea1bafbb989d6b1c56890f" + dependencies: + is-extendable "^0.1.0" + +extend-shallow@^3.0.0, extend-shallow@^3.0.2: + version "3.0.2" + resolved "https://registry.yarnpkg.com/extend-shallow/-/extend-shallow-3.0.2.tgz#26a71aaf073b39fb2127172746131c2704028db8" + dependencies: + assign-symbols "^1.0.0" + is-extendable "^1.0.1" + +extglob@^2.0.4: + version "2.0.4" + resolved "https://registry.yarnpkg.com/extglob/-/extglob-2.0.4.tgz#ad00fe4dc612a9232e8718711dc5cb5ab0285543" + dependencies: + array-unique "^0.3.2" + define-property "^1.0.0" + expand-brackets "^2.1.4" + extend-shallow "^2.0.1" + fragment-cache "^0.2.1" + regex-not "^1.0.0" + snapdragon "^0.8.1" + to-regex "^3.0.1" + +falafel@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/falafel/-/falafel-2.1.0.tgz#96bb17761daba94f46d001738b3cedf3a67fe06c" + dependencies: + acorn "^5.0.0" + foreach "^2.0.5" + isarray "0.0.1" + object-keys "^1.0.6" + +fast-glob@^2.2.2: + version "2.2.2" + resolved "https://registry.yarnpkg.com/fast-glob/-/fast-glob-2.2.2.tgz#71723338ac9b4e0e2fff1d6748a2a13d5ed352bf" + dependencies: + "@mrmlnc/readdir-enhanced" "^2.2.1" + "@nodelib/fs.stat" "^1.0.1" + glob-parent "^3.1.0" + is-glob "^4.0.0" + merge2 "^1.2.1" + micromatch "^3.1.10" + +fast-levenshtein@~2.0.4: + version "2.0.6" + resolved "https://registry.yarnpkg.com/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz#3d8a5c66883a16a30ca8643e851f19baa7797917" + +fbjs@^0.8.0, fbjs@^0.8.16, fbjs@^0.8.5: + version "0.8.17" + resolved "https://registry.yarnpkg.com/fbjs/-/fbjs-0.8.17.tgz#c4d598ead6949112653d6588b01a5cdcd9f90fdd" + dependencies: + core-js "^1.0.0" + isomorphic-fetch "^2.1.1" + loose-envify "^1.0.0" + object-assign "^4.1.0" + promise "^7.1.1" + setimmediate "^1.0.5" + ua-parser-js "^0.7.18" + +filesize@^3.6.0: + version "3.6.1" + resolved "https://registry.yarnpkg.com/filesize/-/filesize-3.6.1.tgz#090bb3ee01b6f801a8a8be99d31710b3422bb317" + +fill-range@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/fill-range/-/fill-range-4.0.0.tgz#d544811d428f98eb06a63dc402d2403c328c38f7" + dependencies: + extend-shallow "^2.0.1" + is-number "^3.0.0" + repeat-string "^1.6.1" + to-regex-range "^2.1.0" + +flatten@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/flatten/-/flatten-1.0.2.tgz#dae46a9d78fbe25292258cc1e780a41d95c03782" + +for-in@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/for-in/-/for-in-1.0.2.tgz#81068d295a8142ec0ac726c6e2200c30fb6d5e80" + +foreach@^2.0.5: + version "2.0.5" + resolved "https://registry.yarnpkg.com/foreach/-/foreach-2.0.5.tgz#0bee005018aeb260d0a3af3ae658dd0136ec1b99" + +formik@^1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/formik/-/formik-1.0.3.tgz#73972b8c91d0ee22cb30f00766cdb0019d40177e" + dependencies: + create-react-context "^0.2.2" + deepmerge "^2.1.1" + hoist-non-react-statics "^2.5.5" + lodash.clonedeep "^4.5.0" + lodash.topath "4.5.2" + prop-types "^15.6.1" + react-fast-compare "^1.0.0" + tslib "^1.9.3" + warning "^3.0.0" + +fragment-cache@^0.2.1: + version "0.2.1" + resolved "https://registry.yarnpkg.com/fragment-cache/-/fragment-cache-0.2.1.tgz#4290fad27f13e89be7f33799c6bc5a0abfff0d19" + dependencies: + map-cache "^0.2.2" + +fresh@0.5.2: + version "0.5.2" + resolved "https://registry.yarnpkg.com/fresh/-/fresh-0.5.2.tgz#3d8cadd90d976569fa835ab1f8e4b23a105605a7" + +fs-minipass@^1.2.5: + version "1.2.5" + resolved "https://registry.yarnpkg.com/fs-minipass/-/fs-minipass-1.2.5.tgz#06c277218454ec288df77ada54a03b8702aacb9d" + dependencies: + minipass "^2.2.1" + +fs.realpath@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/fs.realpath/-/fs.realpath-1.0.0.tgz#1504ad2523158caa40db4a2787cb01411994ea4f" + +fsevents@^1.2.2: + version "1.2.4" + resolved "https://registry.yarnpkg.com/fsevents/-/fsevents-1.2.4.tgz#f41dcb1af2582af3692da36fc55cbd8e1041c426" + dependencies: + nan "^2.9.2" + node-pre-gyp "^0.10.0" + +fswatcher-child@^1.0.3: + version "1.0.5" + resolved "https://registry.yarnpkg.com/fswatcher-child/-/fswatcher-child-1.0.5.tgz#134d012ffa74918975617e00e56e4139f36cb140" + dependencies: + chokidar "^2.0.3" + +function-bind@^1.1.0, function-bind@^1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/function-bind/-/function-bind-1.1.1.tgz#a56899d3ea3c9bab874bb9773b7c5ede92f4895d" + +gauge@~2.7.3: + version "2.7.4" + resolved "https://registry.yarnpkg.com/gauge/-/gauge-2.7.4.tgz#2c03405c7538c39d7eb37b317022e325fb018bf7" + dependencies: + aproba "^1.0.3" + console-control-strings "^1.0.0" + has-unicode "^2.0.0" + object-assign "^4.1.0" + signal-exit "^3.0.0" + string-width "^1.0.1" + strip-ansi "^3.0.1" + wide-align "^1.1.0" + +get-port@^3.2.0: + version "3.2.0" + resolved "https://registry.yarnpkg.com/get-port/-/get-port-3.2.0.tgz#dd7ce7de187c06c8bf353796ac71e099f0980ebc" + +get-value@^2.0.3, get-value@^2.0.6: + version "2.0.6" + resolved "https://registry.yarnpkg.com/get-value/-/get-value-2.0.6.tgz#dc15ca1c672387ca76bd37ac0a395ba2042a2c28" + +glob-parent@^3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/glob-parent/-/glob-parent-3.1.0.tgz#9e6af6299d8d3bd2bd40430832bd113df906c5ae" + dependencies: + is-glob "^3.1.0" + path-dirname "^1.0.0" + +glob-to-regexp@^0.3.0: + version "0.3.0" + resolved "https://registry.yarnpkg.com/glob-to-regexp/-/glob-to-regexp-0.3.0.tgz#8c5a1494d2066c570cc3bfe4496175acc4d502ab" + +glob@^7.0.5: + version "7.1.2" + resolved "https://registry.yarnpkg.com/glob/-/glob-7.1.2.tgz#c19c9df9a028702d678612384a6552404c636d15" + dependencies: + fs.realpath "^1.0.0" + inflight "^1.0.4" + inherits "2" + minimatch "^3.0.4" + once "^1.3.0" + path-is-absolute "^1.0.0" + +globals@^9.18.0: + version "9.18.0" + resolved "https://registry.yarnpkg.com/globals/-/globals-9.18.0.tgz#aa3896b3e69b487f17e31ed2143d69a8e30c2d8a" + +graceful-fs@^4.1.2: + version "4.1.11" + resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.1.11.tgz#0e8bdfe4d1ddb8854d64e04ea7c00e2a026e5658" + +grapheme-breaker@^0.3.2: + version "0.3.2" + resolved "https://registry.yarnpkg.com/grapheme-breaker/-/grapheme-breaker-0.3.2.tgz#5b9e6b78c3832452d2ba2bb1cb830f96276410ac" + dependencies: + brfs "^1.2.0" + unicode-trie "^0.3.1" + +gud@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/gud/-/gud-1.0.0.tgz#a489581b17e6a70beca9abe3ae57de7a499852c0" + +has-ansi@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/has-ansi/-/has-ansi-2.0.0.tgz#34f5049ce1ecdf2b0649af3ef24e45ed35416d91" + dependencies: + ansi-regex "^2.0.0" + +has-flag@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-1.0.0.tgz#9d9e793165ce017a00f00418c43f942a7b1d11fa" + +has-flag@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-3.0.0.tgz#b5d454dc2199ae225699f3467e5a07f3b955bafd" + +has-unicode@^2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/has-unicode/-/has-unicode-2.0.1.tgz#e0e6fe6a28cf51138855e086d1691e771de2a8b9" + +has-value@^0.3.1: + version "0.3.1" + resolved "https://registry.yarnpkg.com/has-value/-/has-value-0.3.1.tgz#7b1f58bada62ca827ec0a2078025654845995e1f" + dependencies: + get-value "^2.0.3" + has-values "^0.1.4" + isobject "^2.0.0" + +has-value@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/has-value/-/has-value-1.0.0.tgz#18b281da585b1c5c51def24c930ed29a0be6b177" + dependencies: + get-value "^2.0.6" + has-values "^1.0.0" + isobject "^3.0.0" + +has-values@^0.1.4: + version "0.1.4" + resolved "https://registry.yarnpkg.com/has-values/-/has-values-0.1.4.tgz#6d61de95d91dfca9b9a02089ad384bff8f62b771" + +has-values@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/has-values/-/has-values-1.0.0.tgz#95b0b63fec2146619a6fe57fe75628d5a39efe4f" + dependencies: + is-number "^3.0.0" + kind-of "^4.0.0" + +has@^1.0.0, has@^1.0.1: + version "1.0.3" + resolved "https://registry.yarnpkg.com/has/-/has-1.0.3.tgz#722d7cbfc1f6aa8241f16dd814e011e1f41e8796" + dependencies: + function-bind "^1.1.1" + +hash-base@^3.0.0: + version "3.0.4" + resolved "https://registry.yarnpkg.com/hash-base/-/hash-base-3.0.4.tgz#5fc8686847ecd73499403319a6b0a3f3f6ae4918" + dependencies: + inherits "^2.0.1" + safe-buffer "^5.0.1" + +hash.js@^1.0.0, hash.js@^1.0.3: + version "1.1.5" + resolved "https://registry.yarnpkg.com/hash.js/-/hash.js-1.1.5.tgz#e38ab4b85dfb1e0c40fe9265c0e9b54854c23812" + dependencies: + inherits "^2.0.3" + minimalistic-assert "^1.0.1" + +hex-color-regex@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/hex-color-regex/-/hex-color-regex-1.1.0.tgz#4c06fccb4602fe2602b3c93df82d7e7dbf1a8a8e" + +hmac-drbg@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/hmac-drbg/-/hmac-drbg-1.0.1.tgz#d2745701025a6c775a6c545793ed502fc0c649a1" + dependencies: + hash.js "^1.0.3" + minimalistic-assert "^1.0.0" + minimalistic-crypto-utils "^1.0.1" + +hoist-non-react-statics@^2.5.0, hoist-non-react-statics@^2.5.5: + version "2.5.5" + resolved "https://registry.yarnpkg.com/hoist-non-react-statics/-/hoist-non-react-statics-2.5.5.tgz#c5903cf409c0dfd908f388e619d86b9c1174cb47" + +home-or-tmp@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/home-or-tmp/-/home-or-tmp-2.0.0.tgz#e36c3f2d2cae7d746a857e38d18d5f32a7882db8" + dependencies: + os-homedir "^1.0.0" + os-tmpdir "^1.0.1" + +hsl-regex@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/hsl-regex/-/hsl-regex-1.0.0.tgz#d49330c789ed819e276a4c0d272dffa30b18fe6e" + +hsla-regex@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/hsla-regex/-/hsla-regex-1.0.0.tgz#c1ce7a3168c8c6614033a4b5f7877f3b225f9c38" + +html-comment-regex@^1.1.0: + version "1.1.1" + resolved "https://registry.yarnpkg.com/html-comment-regex/-/html-comment-regex-1.1.1.tgz#668b93776eaae55ebde8f3ad464b307a4963625e" + +htmlnano@^0.1.9: + version "0.1.10" + resolved "https://registry.yarnpkg.com/htmlnano/-/htmlnano-0.1.10.tgz#a0a548eb4c76ae2cf2423ec7a25c881734d3dea6" + dependencies: + cssnano "^3.4.0" + object-assign "^4.0.1" + posthtml "^0.11.3" + posthtml-render "^1.1.4" + svgo "^1.0.5" + terser "^3.8.1" + +htmlparser2@^3.9.2: + version "3.9.2" + resolved "https://registry.yarnpkg.com/htmlparser2/-/htmlparser2-3.9.2.tgz#1bdf87acca0f3f9e53fa4fcceb0f4b4cbb00b338" + dependencies: + domelementtype "^1.3.0" + domhandler "^2.3.0" + domutils "^1.5.1" + entities "^1.1.1" + inherits "^2.0.1" + readable-stream "^2.0.2" + +http-errors@~1.6.2: + version "1.6.3" + resolved "https://registry.yarnpkg.com/http-errors/-/http-errors-1.6.3.tgz#8b55680bb4be283a0b5bf4ea2e38580be1d9320d" + dependencies: + depd "~1.1.2" + inherits "2.0.3" + setprototypeof "1.1.0" + statuses ">= 1.4.0 < 2" + +https-browserify@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/https-browserify/-/https-browserify-1.0.0.tgz#ec06c10e0a34c0f2faf199f7fd7fc78fffd03c73" + +iconv-lite@^0.4.4, iconv-lite@~0.4.13: + version "0.4.23" + resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.4.23.tgz#297871f63be507adcfbfca715d0cd0eed84e9a63" + dependencies: + safer-buffer ">= 2.1.2 < 3" + +ieee754@^1.1.4: + version "1.1.12" + resolved "https://registry.yarnpkg.com/ieee754/-/ieee754-1.1.12.tgz#50bf24e5b9c8bb98af4964c941cdb0918da7b60b" + +ignore-walk@^3.0.1: + version "3.0.1" + resolved "https://registry.yarnpkg.com/ignore-walk/-/ignore-walk-3.0.1.tgz#a83e62e7d272ac0e3b551aaa82831a19b69f82f8" + dependencies: + minimatch "^3.0.4" + +indexes-of@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/indexes-of/-/indexes-of-1.0.1.tgz#f30f716c8e2bd346c7b67d3df3915566a7c05607" + +indexof@0.0.1: + version "0.0.1" + resolved "https://registry.yarnpkg.com/indexof/-/indexof-0.0.1.tgz#82dc336d232b9062179d05ab3293a66059fd435d" + +inflight@^1.0.4: + version "1.0.6" + resolved "https://registry.yarnpkg.com/inflight/-/inflight-1.0.6.tgz#49bd6331d7d02d0c09bc910a1075ba8165b56df9" + dependencies: + once "^1.3.0" + wrappy "1" + +inherits@2, inherits@2.0.3, inherits@^2.0.1, inherits@^2.0.3, inherits@~2.0.1, inherits@~2.0.3: + version "2.0.3" + resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.3.tgz#633c2c83e3da42a502f52466022480f4208261de" + +inherits@2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.1.tgz#b17d08d326b4423e568eff719f91b0b1cbdf69f1" + +ini@^1.3.4, ini@~1.3.0: + version "1.3.5" + resolved "https://registry.yarnpkg.com/ini/-/ini-1.3.5.tgz#eee25f56db1c9ec6085e0c22778083f596abf927" + +invariant@^2.2.2: + version "2.2.4" + resolved "https://registry.yarnpkg.com/invariant/-/invariant-2.2.4.tgz#610f3c92c9359ce1db616e538008d23ff35158e6" + dependencies: + loose-envify "^1.0.0" + +is-absolute-url@^2.0.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/is-absolute-url/-/is-absolute-url-2.1.0.tgz#50530dfb84fcc9aa7dbe7852e83a37b93b9f2aa6" + +is-accessor-descriptor@^0.1.6: + version "0.1.6" + resolved "https://registry.yarnpkg.com/is-accessor-descriptor/-/is-accessor-descriptor-0.1.6.tgz#a9e12cb3ae8d876727eeef3843f8a0897b5c98d6" + dependencies: + kind-of "^3.0.2" + +is-accessor-descriptor@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz#169c2f6d3df1f992618072365c9b0ea1f6878656" + dependencies: + kind-of "^6.0.0" + +is-arrayish@^0.2.1: + version "0.2.1" + resolved "https://registry.yarnpkg.com/is-arrayish/-/is-arrayish-0.2.1.tgz#77c99840527aa8ecb1a8ba697b80645a7a926a9d" + +is-arrayish@^0.3.1: + version "0.3.2" + resolved "https://registry.yarnpkg.com/is-arrayish/-/is-arrayish-0.3.2.tgz#4574a2ae56f7ab206896fb431eaeed066fdf8f03" + +is-binary-path@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/is-binary-path/-/is-binary-path-1.0.1.tgz#75f16642b480f187a711c814161fd3a4a7655898" + dependencies: + binary-extensions "^1.0.0" + +is-buffer@^1.1.5: + version "1.1.6" + resolved "https://registry.yarnpkg.com/is-buffer/-/is-buffer-1.1.6.tgz#efaa2ea9daa0d7ab2ea13a97b2b8ad51fefbe8be" + +is-callable@^1.1.1, is-callable@^1.1.3: + version "1.1.4" + resolved "https://registry.yarnpkg.com/is-callable/-/is-callable-1.1.4.tgz#1e1adf219e1eeb684d691f9d6a05ff0d30a24d75" + +is-color-stop@^1.0.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/is-color-stop/-/is-color-stop-1.1.0.tgz#cfff471aee4dd5c9e158598fbe12967b5cdad345" + dependencies: + css-color-names "^0.0.4" + hex-color-regex "^1.1.0" + hsl-regex "^1.0.0" + hsla-regex "^1.0.0" + rgb-regex "^1.0.1" + rgba-regex "^1.0.0" + +is-data-descriptor@^0.1.4: + version "0.1.4" + resolved "https://registry.yarnpkg.com/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz#0b5ee648388e2c860282e793f1856fec3f301b56" + dependencies: + kind-of "^3.0.2" + +is-data-descriptor@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz#d84876321d0e7add03990406abbbbd36ba9268c7" + dependencies: + kind-of "^6.0.0" + +is-date-object@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/is-date-object/-/is-date-object-1.0.1.tgz#9aa20eb6aeebbff77fbd33e74ca01b33581d3a16" + +is-descriptor@^0.1.0: + version "0.1.6" + resolved "https://registry.yarnpkg.com/is-descriptor/-/is-descriptor-0.1.6.tgz#366d8240dde487ca51823b1ab9f07a10a78251ca" + dependencies: + is-accessor-descriptor "^0.1.6" + is-data-descriptor "^0.1.4" + kind-of "^5.0.0" + +is-descriptor@^1.0.0, is-descriptor@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/is-descriptor/-/is-descriptor-1.0.2.tgz#3b159746a66604b04f8c81524ba365c5f14d86ec" + dependencies: + is-accessor-descriptor "^1.0.0" + is-data-descriptor "^1.0.0" + kind-of "^6.0.2" + +is-directory@^0.3.1: + version "0.3.1" + resolved "https://registry.yarnpkg.com/is-directory/-/is-directory-0.3.1.tgz#61339b6f2475fc772fd9c9d83f5c8575dc154ae1" + +is-extendable@^0.1.0, is-extendable@^0.1.1: + version "0.1.1" + resolved "https://registry.yarnpkg.com/is-extendable/-/is-extendable-0.1.1.tgz#62b110e289a471418e3ec36a617d472e301dfc89" + +is-extendable@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/is-extendable/-/is-extendable-1.0.1.tgz#a7470f9e426733d81bd81e1155264e3a3507cab4" + dependencies: + is-plain-object "^2.0.4" + +is-extglob@^2.1.0, is-extglob@^2.1.1: + version "2.1.1" + resolved "https://registry.yarnpkg.com/is-extglob/-/is-extglob-2.1.1.tgz#a88c02535791f02ed37c76a1b9ea9773c833f8c2" + +is-finite@^1.0.0: + version "1.0.2" + resolved "https://registry.yarnpkg.com/is-finite/-/is-finite-1.0.2.tgz#cc6677695602be550ef11e8b4aa6305342b6d0aa" + dependencies: + number-is-nan "^1.0.0" + +is-fullwidth-code-point@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz#ef9e31386f031a7f0d643af82fde50c457ef00cb" + dependencies: + number-is-nan "^1.0.0" + +is-fullwidth-code-point@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz#a3b30a5c4f199183167aaab93beefae3ddfb654f" + +is-glob@^3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/is-glob/-/is-glob-3.1.0.tgz#7ba5ae24217804ac70707b96922567486cc3e84a" + dependencies: + is-extglob "^2.1.0" + +is-glob@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/is-glob/-/is-glob-4.0.0.tgz#9521c76845cc2610a85203ddf080a958c2ffabc0" + dependencies: + is-extglob "^2.1.1" + +is-number@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/is-number/-/is-number-3.0.0.tgz#24fd6201a4782cf50561c810276afc7d12d71195" + dependencies: + kind-of "^3.0.2" + +is-obj@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/is-obj/-/is-obj-1.0.1.tgz#3e4729ac1f5fde025cd7d83a896dab9f4f67db0f" + +is-plain-obj@^1.0.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/is-plain-obj/-/is-plain-obj-1.1.0.tgz#71a50c8429dfca773c92a390a4a03b39fcd51d3e" + +is-plain-object@^2.0.1, is-plain-object@^2.0.3, is-plain-object@^2.0.4: + version "2.0.4" + resolved "https://registry.yarnpkg.com/is-plain-object/-/is-plain-object-2.0.4.tgz#2c163b3fafb1b606d9d17928f05c2a1c38e07677" + dependencies: + isobject "^3.0.1" + +is-regex@^1.0.4: + version "1.0.4" + resolved "https://registry.yarnpkg.com/is-regex/-/is-regex-1.0.4.tgz#5517489b547091b0930e095654ced25ee97e9491" + dependencies: + has "^1.0.1" + +is-resolvable@^1.0.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/is-resolvable/-/is-resolvable-1.1.0.tgz#fb18f87ce1feb925169c9a407c19318a3206ed88" + +is-stream@^1.0.1: + version "1.1.0" + resolved "https://registry.yarnpkg.com/is-stream/-/is-stream-1.1.0.tgz#12d4a3dd4e68e0b79ceb8dbc84173ae80d91ca44" + +is-svg@^2.0.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/is-svg/-/is-svg-2.1.0.tgz#cf61090da0d9efbcab8722deba6f032208dbb0e9" + dependencies: + html-comment-regex "^1.1.0" + +is-svg@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/is-svg/-/is-svg-3.0.0.tgz#9321dbd29c212e5ca99c4fa9794c714bcafa2f75" + dependencies: + html-comment-regex "^1.1.0" + +is-symbol@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/is-symbol/-/is-symbol-1.0.1.tgz#3cc59f00025194b6ab2e38dbae6689256b660572" + +is-url@^1.2.2: + version "1.2.4" + resolved "https://registry.yarnpkg.com/is-url/-/is-url-1.2.4.tgz#04a4df46d28c4cff3d73d01ff06abeb318a1aa52" + +is-windows@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/is-windows/-/is-windows-1.0.2.tgz#d1850eb9791ecd18e6182ce12a30f396634bb19d" + +is-wsl@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/is-wsl/-/is-wsl-1.1.0.tgz#1f16e4aa22b04d1336b66188a66af3c600c3a66d" + +isarray@0.0.1: + version "0.0.1" + resolved "https://registry.yarnpkg.com/isarray/-/isarray-0.0.1.tgz#8a18acfca9a8f4177e09abfc6038939b05d1eedf" + +isarray@1.0.0, isarray@^1.0.0, isarray@~1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/isarray/-/isarray-1.0.0.tgz#bb935d48582cba168c06834957a54a3e07124f11" + +isexe@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/isexe/-/isexe-2.0.0.tgz#e8fbf374dc556ff8947a10dcb0572d633f2cfa10" + +isobject@^2.0.0, isobject@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/isobject/-/isobject-2.1.0.tgz#f065561096a3f1da2ef46272f815c840d87e0c89" + dependencies: + isarray "1.0.0" + +isobject@^3.0.0, isobject@^3.0.1: + version "3.0.1" + resolved "https://registry.yarnpkg.com/isobject/-/isobject-3.0.1.tgz#4e431e92b11a9731636aa1f9c8d1ccbcfdab78df" + +isomorphic-fetch@^2.1.1: + version "2.2.1" + resolved "https://registry.yarnpkg.com/isomorphic-fetch/-/isomorphic-fetch-2.2.1.tgz#611ae1acf14f5e81f729507472819fe9733558a9" + dependencies: + node-fetch "^1.0.1" + whatwg-fetch ">=0.10.0" + +js-base64@^2.1.9: + version "2.4.8" + resolved "https://registry.yarnpkg.com/js-base64/-/js-base64-2.4.8.tgz#57a9b130888f956834aa40c5b165ba59c758f033" + +js-beautify@^1.7.5: + version "1.7.5" + resolved "https://registry.yarnpkg.com/js-beautify/-/js-beautify-1.7.5.tgz#69d9651ef60dbb649f65527b53674950138a7919" + dependencies: + config-chain "~1.1.5" + editorconfig "^0.13.2" + mkdirp "~0.5.0" + nopt "~3.0.1" + +"js-tokens@^3.0.0 || ^4.0.0": + version "4.0.0" + resolved "https://registry.yarnpkg.com/js-tokens/-/js-tokens-4.0.0.tgz#19203fb59991df98e3a287050d4647cdeaf32499" + +js-tokens@^3.0.2: + version "3.0.2" + resolved "https://registry.yarnpkg.com/js-tokens/-/js-tokens-3.0.2.tgz#9866df395102130e38f7f996bceb65443209c25b" + +js-yaml@^3.10.0, js-yaml@^3.9.0: + version "3.12.0" + resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-3.12.0.tgz#eaed656ec8344f10f527c6bfa1b6e2244de167d1" + dependencies: + argparse "^1.0.7" + esprima "^4.0.0" + +js-yaml@~3.10.0: + version "3.10.0" + resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-3.10.0.tgz#2e78441646bd4682e963f22b6e92823c309c62dc" + dependencies: + argparse "^1.0.7" + esprima "^4.0.0" + +js-yaml@~3.7.0: + version "3.7.0" + resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-3.7.0.tgz#5c967ddd837a9bfdca5f2de84253abe8a1c03b80" + dependencies: + argparse "^1.0.7" + esprima "^2.6.0" + +jsesc@^1.3.0: + version "1.3.0" + resolved "https://registry.yarnpkg.com/jsesc/-/jsesc-1.3.0.tgz#46c3fec8c1892b12b0833db9bc7622176dbab34b" + +jsesc@~0.5.0: + version "0.5.0" + resolved "https://registry.yarnpkg.com/jsesc/-/jsesc-0.5.0.tgz#e7dee66e35d6fc16f710fe91d5cf69f70f08911d" + +json-parse-better-errors@^1.0.1: + version "1.0.2" + resolved "https://registry.yarnpkg.com/json-parse-better-errors/-/json-parse-better-errors-1.0.2.tgz#bb867cfb3450e69107c131d1c514bab3dc8bcaa9" + +json5@^0.5.1: + version "0.5.1" + resolved "https://registry.yarnpkg.com/json5/-/json5-0.5.1.tgz#1eade7acc012034ad84e2396767ead9fa5495821" + +json5@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/json5/-/json5-1.0.1.tgz#779fb0018604fa854eacbf6252180d83543e3dbe" + dependencies: + minimist "^1.2.0" + +kind-of@^3.0.2, kind-of@^3.0.3, kind-of@^3.2.0: + version "3.2.2" + resolved "https://registry.yarnpkg.com/kind-of/-/kind-of-3.2.2.tgz#31ea21a734bab9bbb0f32466d893aea51e4a3c64" + dependencies: + is-buffer "^1.1.5" + +kind-of@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/kind-of/-/kind-of-4.0.0.tgz#20813df3d712928b207378691a45066fae72dd57" + dependencies: + is-buffer "^1.1.5" + +kind-of@^5.0.0: + version "5.1.0" + resolved "https://registry.yarnpkg.com/kind-of/-/kind-of-5.1.0.tgz#729c91e2d857b7a419a1f9aa65685c4c33f5845d" + +kind-of@^6.0.0, kind-of@^6.0.2: + version "6.0.2" + resolved "https://registry.yarnpkg.com/kind-of/-/kind-of-6.0.2.tgz#01146b36a6218e64e58f3a8d66de5d7fc6f6d051" + +levn@~0.3.0: + version "0.3.0" + resolved "https://registry.yarnpkg.com/levn/-/levn-0.3.0.tgz#3b09924edf9f083c0490fdd4c0bc4421e04764ee" + dependencies: + prelude-ls "~1.1.2" + type-check "~0.3.2" + +lodash.clone@^4.5.0: + version "4.5.0" + resolved "https://registry.yarnpkg.com/lodash.clone/-/lodash.clone-4.5.0.tgz#195870450f5a13192478df4bc3d23d2dea1907b6" + +lodash.clonedeep@^4.5.0: + version "4.5.0" + resolved "https://registry.yarnpkg.com/lodash.clonedeep/-/lodash.clonedeep-4.5.0.tgz#e23f3f9c4f8fbdde872529c1071857a086e5ccef" + +lodash.debounce@^4.0.8: + version "4.0.8" + resolved "https://registry.yarnpkg.com/lodash.debounce/-/lodash.debounce-4.0.8.tgz#82d79bff30a67c4005ffd5e2515300ad9ca4d7af" + +lodash.memoize@^4.1.2: + version "4.1.2" + resolved "https://registry.yarnpkg.com/lodash.memoize/-/lodash.memoize-4.1.2.tgz#bcc6c49a42a2840ed997f323eada5ecd182e0bfe" + +lodash.topath@4.5.2: + version "4.5.2" + resolved "https://registry.yarnpkg.com/lodash.topath/-/lodash.topath-4.5.2.tgz#3616351f3bba61994a0931989660bd03254fd009" + +lodash.uniq@^4.5.0: + version "4.5.0" + resolved "https://registry.yarnpkg.com/lodash.uniq/-/lodash.uniq-4.5.0.tgz#d0225373aeb652adc1bc82e4945339a842754773" + +lodash@^4.17.4: + version "4.17.10" + resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.10.tgz#1b7793cf7259ea38fb3661d4d38b3260af8ae4e7" + +log-symbols@^2.2.0: + version "2.2.0" + resolved "https://registry.yarnpkg.com/log-symbols/-/log-symbols-2.2.0.tgz#5740e1c5d6f0dfda4ad9323b5332107ef6b4c40a" + dependencies: + chalk "^2.0.1" + +loose-envify@^1.0.0, loose-envify@^1.1.0, loose-envify@^1.3.1: + version "1.4.0" + resolved "https://registry.yarnpkg.com/loose-envify/-/loose-envify-1.4.0.tgz#71ee51fa7be4caec1a63839f7e682d8132d30caf" + dependencies: + js-tokens "^3.0.0 || ^4.0.0" + +lru-cache@^3.2.0: + version "3.2.0" + resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-3.2.0.tgz#71789b3b7f5399bec8565dda38aa30d2a097efee" + dependencies: + pseudomap "^1.0.1" + +magic-string@^0.22.4: + version "0.22.5" + resolved "https://registry.yarnpkg.com/magic-string/-/magic-string-0.22.5.tgz#8e9cf5afddf44385c1da5bc2a6a0dbd10b03657e" + dependencies: + vlq "^0.2.2" + +map-cache@^0.2.2: + version "0.2.2" + resolved "https://registry.yarnpkg.com/map-cache/-/map-cache-0.2.2.tgz#c32abd0bd6525d9b051645bb4f26ac5dc98a0dbf" + +map-visit@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/map-visit/-/map-visit-1.0.0.tgz#ecdca8f13144e660f1b5bd41f12f3479d98dfb8f" + dependencies: + object-visit "^1.0.0" + +math-expression-evaluator@^1.2.14: + version "1.2.17" + resolved "https://registry.yarnpkg.com/math-expression-evaluator/-/math-expression-evaluator-1.2.17.tgz#de819fdbcd84dccd8fae59c6aeb79615b9d266ac" + +md5.js@^1.3.4: + version "1.3.4" + resolved "https://registry.yarnpkg.com/md5.js/-/md5.js-1.3.4.tgz#e9bdbde94a20a5ac18b04340fc5764d5b09d901d" + dependencies: + hash-base "^3.0.0" + inherits "^2.0.1" + +mdn-data@^1.0.0, mdn-data@~1.1.0: + version "1.1.4" + resolved "https://registry.yarnpkg.com/mdn-data/-/mdn-data-1.1.4.tgz#50b5d4ffc4575276573c4eedb8780812a8419f01" + +merge-source-map@1.0.4: + version "1.0.4" + resolved "https://registry.yarnpkg.com/merge-source-map/-/merge-source-map-1.0.4.tgz#a5de46538dae84d4114cc5ea02b4772a6346701f" + dependencies: + source-map "^0.5.6" + +merge2@^1.2.1: + version "1.2.2" + resolved "https://registry.yarnpkg.com/merge2/-/merge2-1.2.2.tgz#03212e3da8d86c4d8523cebd6318193414f94e34" + +micromatch@^3.0.4, micromatch@^3.1.10, micromatch@^3.1.4: + version "3.1.10" + resolved "https://registry.yarnpkg.com/micromatch/-/micromatch-3.1.10.tgz#70859bc95c9840952f359a068a3fc49f9ecfac23" + dependencies: + arr-diff "^4.0.0" + array-unique "^0.3.2" + braces "^2.3.1" + define-property "^2.0.2" + extend-shallow "^3.0.2" + extglob "^2.0.4" + fragment-cache "^0.2.1" + kind-of "^6.0.2" + nanomatch "^1.2.9" + object.pick "^1.3.0" + regex-not "^1.0.0" + snapdragon "^0.8.1" + to-regex "^3.0.2" + +miller-rabin@^4.0.0: + version "4.0.1" + resolved "https://registry.yarnpkg.com/miller-rabin/-/miller-rabin-4.0.1.tgz#f080351c865b0dc562a8462966daa53543c78a4d" + dependencies: + bn.js "^4.0.0" + brorand "^1.0.1" + +mime@1.4.1: + version "1.4.1" + resolved "https://registry.yarnpkg.com/mime/-/mime-1.4.1.tgz#121f9ebc49e3766f311a76e1fa1c8003c4b03aa6" + +mimic-fn@^1.0.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/mimic-fn/-/mimic-fn-1.2.0.tgz#820c86a39334640e99516928bd03fca88057d022" + +minimalistic-assert@^1.0.0, minimalistic-assert@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/minimalistic-assert/-/minimalistic-assert-1.0.1.tgz#2e194de044626d4a10e7f7fbc00ce73e83e4d5c7" + +minimalistic-crypto-utils@^1.0.0, minimalistic-crypto-utils@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/minimalistic-crypto-utils/-/minimalistic-crypto-utils-1.0.1.tgz#f6c00c1c0b082246e5c4d99dfb8c7c083b2b582a" + +minimatch@^3.0.2, minimatch@^3.0.4: + version "3.0.4" + resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.0.4.tgz#5166e286457f03306064be5497e8dbb0c3d32083" + dependencies: + brace-expansion "^1.1.7" + +minimist@0.0.8: + version "0.0.8" + resolved "https://registry.yarnpkg.com/minimist/-/minimist-0.0.8.tgz#857fcabfc3397d2625b8228262e86aa7a011b05d" + +minimist@^1.1.3, minimist@^1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.0.tgz#a35008b20f41383eec1fb914f4cd5df79a264284" + +minipass@^2.2.1, minipass@^2.3.3: + version "2.3.4" + resolved "https://registry.yarnpkg.com/minipass/-/minipass-2.3.4.tgz#4768d7605ed6194d6d576169b9e12ef71e9d9957" + dependencies: + safe-buffer "^5.1.2" + yallist "^3.0.0" + +minizlib@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/minizlib/-/minizlib-1.1.0.tgz#11e13658ce46bc3a70a267aac58359d1e0c29ceb" + dependencies: + minipass "^2.2.1" + +mixin-deep@^1.2.0: + version "1.3.1" + resolved "https://registry.yarnpkg.com/mixin-deep/-/mixin-deep-1.3.1.tgz#a49e7268dce1a0d9698e45326c5626df3543d0fe" + dependencies: + for-in "^1.0.2" + is-extendable "^1.0.1" + +mkdirp@^0.5.0, mkdirp@^0.5.1, mkdirp@~0.5.0, mkdirp@~0.5.1: + version "0.5.1" + resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-0.5.1.tgz#30057438eac6cf7f8c4767f38648d6697d75c903" + dependencies: + minimist "0.0.8" + +ms@2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/ms/-/ms-2.0.0.tgz#5608aeadfc00be6c2901df5f9861788de0d597c8" + +nan@^2.0.7, nan@^2.9.2: + version "2.10.0" + resolved "https://registry.yarnpkg.com/nan/-/nan-2.10.0.tgz#96d0cd610ebd58d4b4de9cc0c6828cda99c7548f" + +nanomatch@^1.2.9: + version "1.2.13" + resolved "https://registry.yarnpkg.com/nanomatch/-/nanomatch-1.2.13.tgz#b87a8aa4fc0de8fe6be88895b38983ff265bd119" + dependencies: + arr-diff "^4.0.0" + array-unique "^0.3.2" + define-property "^2.0.2" + extend-shallow "^3.0.2" + fragment-cache "^0.2.1" + is-windows "^1.0.2" + kind-of "^6.0.2" + object.pick "^1.3.0" + regex-not "^1.0.0" + snapdragon "^0.8.1" + to-regex "^3.0.1" + +needle@^2.2.1: + version "2.2.2" + resolved "https://registry.yarnpkg.com/needle/-/needle-2.2.2.tgz#1120ca4c41f2fcc6976fd28a8968afe239929418" + dependencies: + debug "^2.1.2" + iconv-lite "^0.4.4" + sax "^1.2.4" + +nice-try@^1.0.4: + version "1.0.4" + resolved "https://registry.yarnpkg.com/nice-try/-/nice-try-1.0.4.tgz#d93962f6c52f2c1558c0fbda6d512819f1efe1c4" + +node-fetch@^1.0.1: + version "1.7.3" + resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-1.7.3.tgz#980f6f72d85211a5347c6b2bc18c5b84c3eb47ef" + dependencies: + encoding "^0.1.11" + is-stream "^1.0.1" + +node-forge@^0.7.1: + version "0.7.6" + resolved "https://registry.yarnpkg.com/node-forge/-/node-forge-0.7.6.tgz#fdf3b418aee1f94f0ef642cd63486c77ca9724ac" + +node-libs-browser@^2.0.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/node-libs-browser/-/node-libs-browser-2.1.0.tgz#5f94263d404f6e44767d726901fff05478d600df" + dependencies: + assert "^1.1.1" + browserify-zlib "^0.2.0" + buffer "^4.3.0" + console-browserify "^1.1.0" + constants-browserify "^1.0.0" + crypto-browserify "^3.11.0" + domain-browser "^1.1.1" + events "^1.0.0" + https-browserify "^1.0.0" + os-browserify "^0.3.0" + path-browserify "0.0.0" + process "^0.11.10" + punycode "^1.2.4" + querystring-es3 "^0.2.0" + readable-stream "^2.3.3" + stream-browserify "^2.0.1" + stream-http "^2.7.2" + string_decoder "^1.0.0" + timers-browserify "^2.0.4" + tty-browserify "0.0.0" + url "^0.11.0" + util "^0.10.3" + vm-browserify "0.0.4" + +node-pre-gyp@^0.10.0: + version "0.10.3" + resolved "https://registry.yarnpkg.com/node-pre-gyp/-/node-pre-gyp-0.10.3.tgz#3070040716afdc778747b61b6887bf78880b80fc" + dependencies: + detect-libc "^1.0.2" + mkdirp "^0.5.1" + needle "^2.2.1" + nopt "^4.0.1" + npm-packlist "^1.1.6" + npmlog "^4.0.2" + rc "^1.2.7" + rimraf "^2.6.1" + semver "^5.3.0" + tar "^4" + +node-releases@^1.0.0-alpha.11: + version "1.0.0-alpha.11" + resolved "https://registry.yarnpkg.com/node-releases/-/node-releases-1.0.0-alpha.11.tgz#73c810acc2e5b741a17ddfbb39dfca9ab9359d8a" + dependencies: + semver "^5.3.0" + +nopt@^4.0.1: + version "4.0.1" + resolved "https://registry.yarnpkg.com/nopt/-/nopt-4.0.1.tgz#d0d4685afd5415193c8c7505602d0d17cd64474d" + dependencies: + abbrev "1" + osenv "^0.1.4" + +nopt@~3.0.1: + version "3.0.6" + resolved "https://registry.yarnpkg.com/nopt/-/nopt-3.0.6.tgz#c6465dbf08abcd4db359317f79ac68a646b28ff9" + dependencies: + abbrev "1" + +normalize-path@^2.1.1: + version "2.1.1" + resolved "https://registry.yarnpkg.com/normalize-path/-/normalize-path-2.1.1.tgz#1ab28b556e198363a8c1a6f7e6fa20137fe6aed9" + dependencies: + remove-trailing-separator "^1.0.1" + +normalize-range@^0.1.2: + version "0.1.2" + resolved "https://registry.yarnpkg.com/normalize-range/-/normalize-range-0.1.2.tgz#2d10c06bdfd312ea9777695a4d28439456b75942" + +normalize-url@^1.4.0: + version "1.9.1" + resolved "https://registry.yarnpkg.com/normalize-url/-/normalize-url-1.9.1.tgz#2cc0d66b31ea23036458436e3620d85954c66c3c" + dependencies: + object-assign "^4.0.1" + prepend-http "^1.0.0" + query-string "^4.1.0" + sort-keys "^1.0.0" + +normalize-url@^3.0.0: + version "3.2.0" + resolved "https://registry.yarnpkg.com/normalize-url/-/normalize-url-3.2.0.tgz#98d0948afc82829f374320f405fe9ca55a5f8567" + +npm-bundled@^1.0.1: + version "1.0.5" + resolved "https://registry.yarnpkg.com/npm-bundled/-/npm-bundled-1.0.5.tgz#3c1732b7ba936b3a10325aef616467c0ccbcc979" + +npm-packlist@^1.1.6: + version "1.1.11" + resolved "https://registry.yarnpkg.com/npm-packlist/-/npm-packlist-1.1.11.tgz#84e8c683cbe7867d34b1d357d893ce29e28a02de" + dependencies: + ignore-walk "^3.0.1" + npm-bundled "^1.0.1" + +npmlog@^4.0.2: + version "4.1.2" + resolved "https://registry.yarnpkg.com/npmlog/-/npmlog-4.1.2.tgz#08a7f2a8bf734604779a9efa4ad5cc717abb954b" + dependencies: + are-we-there-yet "~1.1.2" + console-control-strings "~1.1.0" + gauge "~2.7.3" + set-blocking "~2.0.0" + +nth-check@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/nth-check/-/nth-check-1.0.1.tgz#9929acdf628fc2c41098deab82ac580cf149aae4" + dependencies: + boolbase "~1.0.0" + +num2fraction@^1.2.2: + version "1.2.2" + resolved "https://registry.yarnpkg.com/num2fraction/-/num2fraction-1.2.2.tgz#6f682b6a027a4e9ddfa4564cd2589d1d4e669ede" + +number-is-nan@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/number-is-nan/-/number-is-nan-1.0.1.tgz#097b602b53422a522c1afb8790318336941a011d" + +object-assign@^4.0.1, object-assign@^4.1.0, object-assign@^4.1.1: + version "4.1.1" + resolved "https://registry.yarnpkg.com/object-assign/-/object-assign-4.1.1.tgz#2109adc7965887cfc05cbbd442cac8bfbb360863" + +object-copy@^0.1.0: + version "0.1.0" + resolved "https://registry.yarnpkg.com/object-copy/-/object-copy-0.1.0.tgz#7e7d858b781bd7c991a41ba975ed3812754e998c" + dependencies: + copy-descriptor "^0.1.0" + define-property "^0.2.5" + kind-of "^3.0.3" + +object-inspect@~1.4.0: + version "1.4.1" + resolved "https://registry.yarnpkg.com/object-inspect/-/object-inspect-1.4.1.tgz#37ffb10e71adaf3748d05f713b4c9452f402cbc4" + +object-keys@^1.0.12, object-keys@^1.0.6: + version "1.0.12" + resolved "https://registry.yarnpkg.com/object-keys/-/object-keys-1.0.12.tgz#09c53855377575310cca62f55bb334abff7b3ed2" + +object-visit@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/object-visit/-/object-visit-1.0.1.tgz#f79c4493af0c5377b59fe39d395e41042dd045bb" + dependencies: + isobject "^3.0.0" + +object.getownpropertydescriptors@^2.0.3: + version "2.0.3" + resolved "https://registry.yarnpkg.com/object.getownpropertydescriptors/-/object.getownpropertydescriptors-2.0.3.tgz#8758c846f5b407adab0f236e0986f14b051caa16" + dependencies: + define-properties "^1.1.2" + es-abstract "^1.5.1" + +object.pick@^1.3.0: + version "1.3.0" + resolved "https://registry.yarnpkg.com/object.pick/-/object.pick-1.3.0.tgz#87a10ac4c1694bd2e1cbf53591a66141fb5dd747" + dependencies: + isobject "^3.0.1" + +object.values@^1.0.4: + version "1.0.4" + resolved "https://registry.yarnpkg.com/object.values/-/object.values-1.0.4.tgz#e524da09b4f66ff05df457546ec72ac99f13069a" + dependencies: + define-properties "^1.1.2" + es-abstract "^1.6.1" + function-bind "^1.1.0" + has "^1.0.1" + +on-finished@~2.3.0: + version "2.3.0" + resolved "https://registry.yarnpkg.com/on-finished/-/on-finished-2.3.0.tgz#20f1336481b083cd75337992a16971aa2d906947" + dependencies: + ee-first "1.1.1" + +once@^1.3.0: + version "1.4.0" + resolved "https://registry.yarnpkg.com/once/-/once-1.4.0.tgz#583b1aa775961d4b113ac17d9c50baef9dd76bd1" + dependencies: + wrappy "1" + +onetime@^2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/onetime/-/onetime-2.0.1.tgz#067428230fd67443b2794b22bba528b6867962d4" + dependencies: + mimic-fn "^1.0.0" + +opn@^5.1.0: + version "5.3.0" + resolved "https://registry.yarnpkg.com/opn/-/opn-5.3.0.tgz#64871565c863875f052cfdf53d3e3cb5adb53b1c" + dependencies: + is-wsl "^1.1.0" + +optionator@^0.8.1: + version "0.8.2" + resolved "https://registry.yarnpkg.com/optionator/-/optionator-0.8.2.tgz#364c5e409d3f4d6301d6c0b4c05bba50180aeb64" + dependencies: + deep-is "~0.1.3" + fast-levenshtein "~2.0.4" + levn "~0.3.0" + prelude-ls "~1.1.2" + type-check "~0.3.2" + wordwrap "~1.0.0" + +ora@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/ora/-/ora-2.1.0.tgz#6caf2830eb924941861ec53a173799e008b51e5b" + dependencies: + chalk "^2.3.1" + cli-cursor "^2.1.0" + cli-spinners "^1.1.0" + log-symbols "^2.2.0" + strip-ansi "^4.0.0" + wcwidth "^1.0.1" + +os-browserify@^0.3.0: + version "0.3.0" + resolved "https://registry.yarnpkg.com/os-browserify/-/os-browserify-0.3.0.tgz#854373c7f5c2315914fc9bfc6bd8238fdda1ec27" + +os-homedir@^1.0.0: + version "1.0.2" + resolved "https://registry.yarnpkg.com/os-homedir/-/os-homedir-1.0.2.tgz#ffbc4988336e0e833de0c168c7ef152121aa7fb3" + +os-tmpdir@^1.0.0, os-tmpdir@^1.0.1: + version "1.0.2" + resolved "https://registry.yarnpkg.com/os-tmpdir/-/os-tmpdir-1.0.2.tgz#bbe67406c79aa85c5cfec766fe5734555dfa1274" + +osenv@^0.1.4: + version "0.1.5" + resolved "https://registry.yarnpkg.com/osenv/-/osenv-0.1.5.tgz#85cdfafaeb28e8677f416e287592b5f3f49ea410" + dependencies: + os-homedir "^1.0.0" + os-tmpdir "^1.0.0" + +pako@^0.2.5: + version "0.2.9" + resolved "https://registry.yarnpkg.com/pako/-/pako-0.2.9.tgz#f3f7522f4ef782348da8161bad9ecfd51bf83a75" + +pako@~1.0.5: + version "1.0.6" + resolved "https://registry.yarnpkg.com/pako/-/pako-1.0.6.tgz#0101211baa70c4bca4a0f63f2206e97b7dfaf258" + +parcel-bundler@^1.9.7: + version "1.9.7" + resolved "https://registry.yarnpkg.com/parcel-bundler/-/parcel-bundler-1.9.7.tgz#5cd00850dea0254d377005d55ab4bf60429ba2bc" + dependencies: + ansi-to-html "^0.6.4" + babel-code-frame "^6.26.0" + babel-core "^6.25.0" + babel-generator "^6.25.0" + babel-plugin-transform-es2015-modules-commonjs "^6.26.0" + babel-plugin-transform-react-jsx "^6.24.1" + babel-preset-env "^1.7.0" + babel-template "^6.26.0" + babel-traverse "^6.26.0" + babel-types "^6.26.0" + babylon "^6.17.4" + babylon-walk "^1.0.2" + browserslist "^3.2.6" + chalk "^2.1.0" + clone "^2.1.1" + command-exists "^1.2.6" + commander "^2.11.0" + cross-spawn "^6.0.4" + cssnano "^4.0.0" + deasync "^0.1.13" + dotenv "^5.0.0" + fast-glob "^2.2.2" + filesize "^3.6.0" + fswatcher-child "^1.0.3" + get-port "^3.2.0" + grapheme-breaker "^0.3.2" + htmlnano "^0.1.9" + is-glob "^4.0.0" + is-url "^1.2.2" + js-yaml "^3.10.0" + json5 "^1.0.1" + micromatch "^3.0.4" + mkdirp "^0.5.1" + node-forge "^0.7.1" + node-libs-browser "^2.0.0" + opn "^5.1.0" + ora "^2.1.0" + physical-cpu-count "^2.0.0" + postcss "^6.0.19" + postcss-value-parser "^3.3.0" + posthtml "^0.11.2" + posthtml-parser "^0.4.0" + posthtml-render "^1.1.3" + resolve "^1.4.0" + semver "^5.4.1" + serialize-to-js "^1.1.1" + serve-static "^1.12.4" + source-map "0.6.1" + strip-ansi "^4.0.0" + terser "^3.7.3" + toml "^2.3.3" + tomlify-j0.4 "^3.0.0" + v8-compile-cache "^2.0.0" + ws "^5.1.1" + +parse-asn1@^5.0.0: + version "5.1.1" + resolved "https://registry.yarnpkg.com/parse-asn1/-/parse-asn1-5.1.1.tgz#f6bf293818332bd0dab54efb16087724745e6ca8" + dependencies: + asn1.js "^4.0.0" + browserify-aes "^1.0.0" + create-hash "^1.1.0" + evp_bytestokey "^1.0.0" + pbkdf2 "^3.0.3" + +parse-json@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/parse-json/-/parse-json-4.0.0.tgz#be35f5425be1f7f6c747184f98a788cb99477ee0" + dependencies: + error-ex "^1.3.1" + json-parse-better-errors "^1.0.1" + +parseurl@~1.3.2: + version "1.3.2" + resolved "https://registry.yarnpkg.com/parseurl/-/parseurl-1.3.2.tgz#fc289d4ed8993119460c156253262cdc8de65bf3" + +pascalcase@^0.1.1: + version "0.1.1" + resolved "https://registry.yarnpkg.com/pascalcase/-/pascalcase-0.1.1.tgz#b363e55e8006ca6fe21784d2db22bd15d7917f14" + +path-browserify@0.0.0: + version "0.0.0" + resolved "https://registry.yarnpkg.com/path-browserify/-/path-browserify-0.0.0.tgz#a0b870729aae214005b7d5032ec2cbbb0fb4451a" + +path-dirname@^1.0.0: + version "1.0.2" + resolved "https://registry.yarnpkg.com/path-dirname/-/path-dirname-1.0.2.tgz#cc33d24d525e099a5388c0336c6e32b9160609e0" + +path-is-absolute@^1.0.0, path-is-absolute@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/path-is-absolute/-/path-is-absolute-1.0.1.tgz#174b9268735534ffbc7ace6bf53a5a9e1b5c5f5f" + +path-key@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/path-key/-/path-key-2.0.1.tgz#411cadb574c5a140d3a4b1910d40d80cc9f40b40" + +path-parse@^1.0.5: + version "1.0.6" + resolved "https://registry.yarnpkg.com/path-parse/-/path-parse-1.0.6.tgz#d62dbb5679405d72c4737ec58600e9ddcf06d24c" + +pbkdf2@^3.0.3: + version "3.0.16" + resolved "https://registry.yarnpkg.com/pbkdf2/-/pbkdf2-3.0.16.tgz#7404208ec6b01b62d85bf83853a8064f8d9c2a5c" + dependencies: + create-hash "^1.1.2" + create-hmac "^1.1.4" + ripemd160 "^2.0.1" + safe-buffer "^5.0.1" + sha.js "^2.4.8" + +physical-cpu-count@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/physical-cpu-count/-/physical-cpu-count-2.0.0.tgz#18de2f97e4bf7a9551ad7511942b5496f7aba660" + +posix-character-classes@^0.1.0: + version "0.1.1" + resolved "https://registry.yarnpkg.com/posix-character-classes/-/posix-character-classes-0.1.1.tgz#01eac0fe3b5af71a2a6c02feabb8c1fef7e00eab" + +postcss-calc@^5.2.0: + version "5.3.1" + resolved "https://registry.yarnpkg.com/postcss-calc/-/postcss-calc-5.3.1.tgz#77bae7ca928ad85716e2fda42f261bf7c1d65b5e" + dependencies: + postcss "^5.0.2" + postcss-message-helpers "^2.0.0" + reduce-css-calc "^1.2.6" + +postcss-calc@^6.0.0: + version "6.0.1" + resolved "https://registry.yarnpkg.com/postcss-calc/-/postcss-calc-6.0.1.tgz#3d24171bbf6e7629d422a436ebfe6dd9511f4330" + dependencies: + css-unit-converter "^1.1.1" + postcss "^6.0.0" + postcss-selector-parser "^2.2.2" + reduce-css-calc "^2.0.0" + +postcss-colormin@^2.1.8: + version "2.2.2" + resolved "https://registry.yarnpkg.com/postcss-colormin/-/postcss-colormin-2.2.2.tgz#6631417d5f0e909a3d7ec26b24c8a8d1e4f96e4b" + dependencies: + colormin "^1.0.5" + postcss "^5.0.13" + postcss-value-parser "^3.2.3" + +postcss-colormin@^4.0.0: + version "4.0.1" + resolved "https://registry.yarnpkg.com/postcss-colormin/-/postcss-colormin-4.0.1.tgz#6f1c18a0155bc69613f2ff13843e2e4ae8ff0bbe" + dependencies: + browserslist "^4.0.0" + color "^3.0.0" + has "^1.0.0" + postcss "^6.0.0" + postcss-value-parser "^3.0.0" + +postcss-convert-values@^2.3.4: + version "2.6.1" + resolved "https://registry.yarnpkg.com/postcss-convert-values/-/postcss-convert-values-2.6.1.tgz#bbd8593c5c1fd2e3d1c322bb925dcae8dae4d62d" + dependencies: + postcss "^5.0.11" + postcss-value-parser "^3.1.2" + +postcss-convert-values@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/postcss-convert-values/-/postcss-convert-values-4.0.0.tgz#77d77d9aed1dc4e6956e651cc349d53305876f62" + dependencies: + postcss "^6.0.0" + postcss-value-parser "^3.0.0" + +postcss-discard-comments@^2.0.4: + version "2.0.4" + resolved "https://registry.yarnpkg.com/postcss-discard-comments/-/postcss-discard-comments-2.0.4.tgz#befe89fafd5b3dace5ccce51b76b81514be00e3d" + dependencies: + postcss "^5.0.14" + +postcss-discard-comments@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/postcss-discard-comments/-/postcss-discard-comments-4.0.0.tgz#9684a299e76b3e93263ef8fd2adbf1a1c08fd88d" + dependencies: + postcss "^6.0.0" + +postcss-discard-duplicates@^2.0.1: + version "2.1.0" + resolved "https://registry.yarnpkg.com/postcss-discard-duplicates/-/postcss-discard-duplicates-2.1.0.tgz#b9abf27b88ac188158a5eb12abcae20263b91932" + dependencies: + postcss "^5.0.4" + +postcss-discard-duplicates@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/postcss-discard-duplicates/-/postcss-discard-duplicates-4.0.0.tgz#42f3c267f85fa909e042c35767ecfd65cb2bd72c" + dependencies: + postcss "^6.0.0" + +postcss-discard-empty@^2.0.1: + version "2.1.0" + resolved "https://registry.yarnpkg.com/postcss-discard-empty/-/postcss-discard-empty-2.1.0.tgz#d2b4bd9d5ced5ebd8dcade7640c7d7cd7f4f92b5" + dependencies: + postcss "^5.0.14" + +postcss-discard-empty@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/postcss-discard-empty/-/postcss-discard-empty-4.0.0.tgz#55e18a59c74128e38c7d2804bcfa4056611fb97f" + dependencies: + postcss "^6.0.0" + +postcss-discard-overridden@^0.1.1: + version "0.1.1" + resolved "https://registry.yarnpkg.com/postcss-discard-overridden/-/postcss-discard-overridden-0.1.1.tgz#8b1eaf554f686fb288cd874c55667b0aa3668d58" + dependencies: + postcss "^5.0.16" + +postcss-discard-overridden@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/postcss-discard-overridden/-/postcss-discard-overridden-4.0.0.tgz#4a0bf85978784cf1f81ed2c1c1fd9d964a1da1fa" + dependencies: + postcss "^6.0.0" + +postcss-discard-unused@^2.2.1: + version "2.2.3" + resolved "https://registry.yarnpkg.com/postcss-discard-unused/-/postcss-discard-unused-2.2.3.tgz#bce30b2cc591ffc634322b5fb3464b6d934f4433" + dependencies: + postcss "^5.0.14" + uniqs "^2.0.0" + +postcss-filter-plugins@^2.0.0: + version "2.0.3" + resolved "https://registry.yarnpkg.com/postcss-filter-plugins/-/postcss-filter-plugins-2.0.3.tgz#82245fdf82337041645e477114d8e593aa18b8ec" + dependencies: + postcss "^5.0.4" + +postcss-merge-idents@^2.1.5: + version "2.1.7" + resolved "https://registry.yarnpkg.com/postcss-merge-idents/-/postcss-merge-idents-2.1.7.tgz#4c5530313c08e1d5b3bbf3d2bbc747e278eea270" + dependencies: + has "^1.0.1" + postcss "^5.0.10" + postcss-value-parser "^3.1.1" + +postcss-merge-longhand@^2.0.1: + version "2.0.2" + resolved "https://registry.yarnpkg.com/postcss-merge-longhand/-/postcss-merge-longhand-2.0.2.tgz#23d90cd127b0a77994915332739034a1a4f3d658" + dependencies: + postcss "^5.0.4" + +postcss-merge-longhand@^4.0.0: + version "4.0.4" + resolved "https://registry.yarnpkg.com/postcss-merge-longhand/-/postcss-merge-longhand-4.0.4.tgz#bffc7c6ffa146591c993a0bb8373d65f9a06d4d0" + dependencies: + css-color-names "0.0.4" + postcss "^6.0.0" + postcss-value-parser "^3.0.0" + stylehacks "^4.0.0" + +postcss-merge-rules@^2.0.3: + version "2.1.2" + resolved "https://registry.yarnpkg.com/postcss-merge-rules/-/postcss-merge-rules-2.1.2.tgz#d1df5dfaa7b1acc3be553f0e9e10e87c61b5f721" + dependencies: + browserslist "^1.5.2" + caniuse-api "^1.5.2" + postcss "^5.0.4" + postcss-selector-parser "^2.2.2" + vendors "^1.0.0" + +postcss-merge-rules@^4.0.0: + version "4.0.1" + resolved "https://registry.yarnpkg.com/postcss-merge-rules/-/postcss-merge-rules-4.0.1.tgz#430fd59b3f2ed2e8afcd0b31278eda39854abb10" + dependencies: + browserslist "^4.0.0" + caniuse-api "^3.0.0" + cssnano-util-same-parent "^4.0.0" + postcss "^6.0.0" + postcss-selector-parser "^3.0.0" + vendors "^1.0.0" + +postcss-message-helpers@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/postcss-message-helpers/-/postcss-message-helpers-2.0.0.tgz#a4f2f4fab6e4fe002f0aed000478cdf52f9ba60e" + +postcss-minify-font-values@^1.0.2: + version "1.0.5" + resolved "https://registry.yarnpkg.com/postcss-minify-font-values/-/postcss-minify-font-values-1.0.5.tgz#4b58edb56641eba7c8474ab3526cafd7bbdecb69" + dependencies: + object-assign "^4.0.1" + postcss "^5.0.4" + postcss-value-parser "^3.0.2" + +postcss-minify-font-values@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/postcss-minify-font-values/-/postcss-minify-font-values-4.0.0.tgz#4cc33d283d6a81759036e757ef981d92cbd85bed" + dependencies: + postcss "^6.0.0" + postcss-value-parser "^3.0.0" + +postcss-minify-gradients@^1.0.1: + version "1.0.5" + resolved "https://registry.yarnpkg.com/postcss-minify-gradients/-/postcss-minify-gradients-1.0.5.tgz#5dbda11373703f83cfb4a3ea3881d8d75ff5e6e1" + dependencies: + postcss "^5.0.12" + postcss-value-parser "^3.3.0" + +postcss-minify-gradients@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/postcss-minify-gradients/-/postcss-minify-gradients-4.0.0.tgz#3fc3916439d27a9bb8066db7cdad801650eb090e" + dependencies: + cssnano-util-get-arguments "^4.0.0" + is-color-stop "^1.0.0" + postcss "^6.0.0" + postcss-value-parser "^3.0.0" + +postcss-minify-params@^1.0.4: + version "1.2.2" + resolved "https://registry.yarnpkg.com/postcss-minify-params/-/postcss-minify-params-1.2.2.tgz#ad2ce071373b943b3d930a3fa59a358c28d6f1f3" + dependencies: + alphanum-sort "^1.0.1" + postcss "^5.0.2" + postcss-value-parser "^3.0.2" + uniqs "^2.0.0" + +postcss-minify-params@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/postcss-minify-params/-/postcss-minify-params-4.0.0.tgz#05e9166ee48c05af651989ce84d39c1b4d790674" + dependencies: + alphanum-sort "^1.0.0" + cssnano-util-get-arguments "^4.0.0" + postcss "^6.0.0" + postcss-value-parser "^3.0.0" + uniqs "^2.0.0" + +postcss-minify-selectors@^2.0.4: + version "2.1.1" + resolved "https://registry.yarnpkg.com/postcss-minify-selectors/-/postcss-minify-selectors-2.1.1.tgz#b2c6a98c0072cf91b932d1a496508114311735bf" + dependencies: + alphanum-sort "^1.0.2" + has "^1.0.1" + postcss "^5.0.14" + postcss-selector-parser "^2.0.0" + +postcss-minify-selectors@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/postcss-minify-selectors/-/postcss-minify-selectors-4.0.0.tgz#b1e9f6c463416d3fcdcb26e7b785d95f61578aad" + dependencies: + alphanum-sort "^1.0.0" + has "^1.0.0" + postcss "^6.0.0" + postcss-selector-parser "^3.0.0" + +postcss-normalize-charset@^1.1.0: + version "1.1.1" + resolved "https://registry.yarnpkg.com/postcss-normalize-charset/-/postcss-normalize-charset-1.1.1.tgz#ef9ee71212d7fe759c78ed162f61ed62b5cb93f1" + dependencies: + postcss "^5.0.5" + +postcss-normalize-charset@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/postcss-normalize-charset/-/postcss-normalize-charset-4.0.0.tgz#24527292702d5e8129eafa3d1de49ed51a6ab730" + dependencies: + postcss "^6.0.0" + +postcss-normalize-display-values@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/postcss-normalize-display-values/-/postcss-normalize-display-values-4.0.0.tgz#950e0c7be3445770a160fffd6b6644c3c0cd8f89" + dependencies: + cssnano-util-get-match "^4.0.0" + postcss "^6.0.0" + postcss-value-parser "^3.0.0" + +postcss-normalize-positions@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/postcss-normalize-positions/-/postcss-normalize-positions-4.0.0.tgz#ee9343ab981b822c63ab72615ecccd08564445a3" + dependencies: + cssnano-util-get-arguments "^4.0.0" + has "^1.0.0" + postcss "^6.0.0" + postcss-value-parser "^3.0.0" + +postcss-normalize-repeat-style@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/postcss-normalize-repeat-style/-/postcss-normalize-repeat-style-4.0.0.tgz#b711c592cf16faf9ff575e42fa100b6799083eff" + dependencies: + cssnano-util-get-arguments "^4.0.0" + cssnano-util-get-match "^4.0.0" + postcss "^6.0.0" + postcss-value-parser "^3.0.0" + +postcss-normalize-string@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/postcss-normalize-string/-/postcss-normalize-string-4.0.0.tgz#718cb6d30a6fac6ac6a830e32c06c07dbc66fe5d" + dependencies: + has "^1.0.0" + postcss "^6.0.0" + postcss-value-parser "^3.0.0" + +postcss-normalize-timing-functions@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/postcss-normalize-timing-functions/-/postcss-normalize-timing-functions-4.0.0.tgz#0351f29886aa981d43d91b2c2bd1aea6d0af6d23" + dependencies: + cssnano-util-get-match "^4.0.0" + postcss "^6.0.0" + postcss-value-parser "^3.0.0" + +postcss-normalize-unicode@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/postcss-normalize-unicode/-/postcss-normalize-unicode-4.0.0.tgz#5acd5d47baea5d17674b2ccc4ae5166fa88cdf97" + dependencies: + postcss "^6.0.0" + postcss-value-parser "^3.0.0" + +postcss-normalize-url@^3.0.7: + version "3.0.8" + resolved "https://registry.yarnpkg.com/postcss-normalize-url/-/postcss-normalize-url-3.0.8.tgz#108f74b3f2fcdaf891a2ffa3ea4592279fc78222" + dependencies: + is-absolute-url "^2.0.0" + normalize-url "^1.4.0" + postcss "^5.0.14" + postcss-value-parser "^3.2.3" + +postcss-normalize-url@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/postcss-normalize-url/-/postcss-normalize-url-4.0.0.tgz#b7a9c8ad26cf26694c146eb2d68bd0cf49956f0d" + dependencies: + is-absolute-url "^2.0.0" + normalize-url "^3.0.0" + postcss "^6.0.0" + postcss-value-parser "^3.0.0" + +postcss-normalize-whitespace@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/postcss-normalize-whitespace/-/postcss-normalize-whitespace-4.0.0.tgz#1da7e76b10ae63c11827fa04fc3bb4a1efe99cc0" + dependencies: + postcss "^6.0.0" + postcss-value-parser "^3.0.0" + +postcss-ordered-values@^2.1.0: + version "2.2.3" + resolved "https://registry.yarnpkg.com/postcss-ordered-values/-/postcss-ordered-values-2.2.3.tgz#eec6c2a67b6c412a8db2042e77fe8da43f95c11d" + dependencies: + postcss "^5.0.4" + postcss-value-parser "^3.0.1" + +postcss-ordered-values@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/postcss-ordered-values/-/postcss-ordered-values-4.0.0.tgz#58b40c74f72e022eb34152c12e4b0f9354482fc2" + dependencies: + cssnano-util-get-arguments "^4.0.0" + postcss "^6.0.0" + postcss-value-parser "^3.0.0" + +postcss-reduce-idents@^2.2.2: + version "2.4.0" + resolved "https://registry.yarnpkg.com/postcss-reduce-idents/-/postcss-reduce-idents-2.4.0.tgz#c2c6d20cc958284f6abfbe63f7609bf409059ad3" + dependencies: + postcss "^5.0.4" + postcss-value-parser "^3.0.2" + +postcss-reduce-initial@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/postcss-reduce-initial/-/postcss-reduce-initial-1.0.1.tgz#68f80695f045d08263a879ad240df8dd64f644ea" + dependencies: + postcss "^5.0.4" + +postcss-reduce-initial@^4.0.0: + version "4.0.1" + resolved "https://registry.yarnpkg.com/postcss-reduce-initial/-/postcss-reduce-initial-4.0.1.tgz#f2d58f50cea2b0c5dc1278d6ea5ed0ff5829c293" + dependencies: + browserslist "^4.0.0" + caniuse-api "^3.0.0" + has "^1.0.0" + postcss "^6.0.0" + +postcss-reduce-transforms@^1.0.3: + version "1.0.4" + resolved "https://registry.yarnpkg.com/postcss-reduce-transforms/-/postcss-reduce-transforms-1.0.4.tgz#ff76f4d8212437b31c298a42d2e1444025771ae1" + dependencies: + has "^1.0.1" + postcss "^5.0.8" + postcss-value-parser "^3.0.1" + +postcss-reduce-transforms@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/postcss-reduce-transforms/-/postcss-reduce-transforms-4.0.0.tgz#f645fc7440c35274f40de8104e14ad7163edf188" + dependencies: + cssnano-util-get-match "^4.0.0" + has "^1.0.0" + postcss "^6.0.0" + postcss-value-parser "^3.0.0" + +postcss-selector-parser@^2.0.0, postcss-selector-parser@^2.2.2: + version "2.2.3" + resolved "https://registry.yarnpkg.com/postcss-selector-parser/-/postcss-selector-parser-2.2.3.tgz#f9437788606c3c9acee16ffe8d8b16297f27bb90" + dependencies: + flatten "^1.0.2" + indexes-of "^1.0.1" + uniq "^1.0.1" + +postcss-selector-parser@^3.0.0: + version "3.1.1" + resolved "https://registry.yarnpkg.com/postcss-selector-parser/-/postcss-selector-parser-3.1.1.tgz#4f875f4afb0c96573d5cf4d74011aee250a7e865" + dependencies: + dot-prop "^4.1.1" + indexes-of "^1.0.1" + uniq "^1.0.1" + +postcss-svgo@^2.1.1: + version "2.1.6" + resolved "https://registry.yarnpkg.com/postcss-svgo/-/postcss-svgo-2.1.6.tgz#b6df18aa613b666e133f08adb5219c2684ac108d" + dependencies: + is-svg "^2.0.0" + postcss "^5.0.14" + postcss-value-parser "^3.2.3" + svgo "^0.7.0" + +postcss-svgo@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/postcss-svgo/-/postcss-svgo-4.0.0.tgz#c0bbad02520fc636c9d78b0e8403e2e515c32285" + dependencies: + is-svg "^3.0.0" + postcss "^6.0.0" + postcss-value-parser "^3.0.0" + svgo "^1.0.0" + +postcss-unique-selectors@^2.0.2: + version "2.0.2" + resolved "https://registry.yarnpkg.com/postcss-unique-selectors/-/postcss-unique-selectors-2.0.2.tgz#981d57d29ddcb33e7b1dfe1fd43b8649f933ca1d" + dependencies: + alphanum-sort "^1.0.1" + postcss "^5.0.4" + uniqs "^2.0.0" + +postcss-unique-selectors@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/postcss-unique-selectors/-/postcss-unique-selectors-4.0.0.tgz#04c1e9764c75874261303402c41f0e9769fc5501" + dependencies: + alphanum-sort "^1.0.0" + postcss "^6.0.0" + uniqs "^2.0.0" + +postcss-value-parser@^3.0.0, postcss-value-parser@^3.0.1, postcss-value-parser@^3.0.2, postcss-value-parser@^3.1.1, postcss-value-parser@^3.1.2, postcss-value-parser@^3.2.3, postcss-value-parser@^3.3.0: + version "3.3.0" + resolved "https://registry.yarnpkg.com/postcss-value-parser/-/postcss-value-parser-3.3.0.tgz#87f38f9f18f774a4ab4c8a232f5c5ce8872a9d15" + +postcss-zindex@^2.0.1: + version "2.2.0" + resolved "https://registry.yarnpkg.com/postcss-zindex/-/postcss-zindex-2.2.0.tgz#d2109ddc055b91af67fc4cb3b025946639d2af22" + dependencies: + has "^1.0.1" + postcss "^5.0.4" + uniqs "^2.0.0" + +postcss@^5.0.10, postcss@^5.0.11, postcss@^5.0.12, postcss@^5.0.13, postcss@^5.0.14, postcss@^5.0.16, postcss@^5.0.2, postcss@^5.0.4, postcss@^5.0.5, postcss@^5.0.8, postcss@^5.2.16: + version "5.2.18" + resolved "https://registry.yarnpkg.com/postcss/-/postcss-5.2.18.tgz#badfa1497d46244f6390f58b319830d9107853c5" + dependencies: + chalk "^1.1.3" + js-base64 "^2.1.9" + source-map "^0.5.6" + supports-color "^3.2.3" + +postcss@^6.0.0, postcss@^6.0.19: + version "6.0.23" + resolved "https://registry.yarnpkg.com/postcss/-/postcss-6.0.23.tgz#61c82cc328ac60e677645f979054eb98bc0e3324" + dependencies: + chalk "^2.4.1" + source-map "^0.6.1" + supports-color "^5.4.0" + +posthtml-parser@^0.3.3: + version "0.3.3" + resolved "https://registry.yarnpkg.com/posthtml-parser/-/posthtml-parser-0.3.3.tgz#3fe986fca9f00c0f109d731ba590b192f26e776d" + dependencies: + htmlparser2 "^3.9.2" + isobject "^2.1.0" + object-assign "^4.1.1" + +posthtml-parser@^0.4.0: + version "0.4.1" + resolved "https://registry.yarnpkg.com/posthtml-parser/-/posthtml-parser-0.4.1.tgz#95b78fef766fbbe0a6f861b6e95582bc3d1ff933" + dependencies: + htmlparser2 "^3.9.2" + object-assign "^4.1.1" + +posthtml-render@^1.1.0, posthtml-render@^1.1.3, posthtml-render@^1.1.4: + version "1.1.4" + resolved "https://registry.yarnpkg.com/posthtml-render/-/posthtml-render-1.1.4.tgz#95dac09892f4f183fad5ac823f08f42c0256551e" + +posthtml@^0.11.2, posthtml@^0.11.3: + version "0.11.3" + resolved "https://registry.yarnpkg.com/posthtml/-/posthtml-0.11.3.tgz#17ea2921b0555b7455f33c977bd16d8b8cb74f27" + dependencies: + object-assign "^4.1.1" + posthtml-parser "^0.3.3" + posthtml-render "^1.1.0" + +prelude-ls@~1.1.2: + version "1.1.2" + resolved "https://registry.yarnpkg.com/prelude-ls/-/prelude-ls-1.1.2.tgz#21932a549f5e52ffd9a827f570e04be62a97da54" + +prepend-http@^1.0.0: + version "1.0.4" + resolved "https://registry.yarnpkg.com/prepend-http/-/prepend-http-1.0.4.tgz#d4f4562b0ce3696e41ac52d0e002e57a635dc6dc" + +private@^0.1.6, private@^0.1.8: + version "0.1.8" + resolved "https://registry.yarnpkg.com/private/-/private-0.1.8.tgz#2381edb3689f7a53d653190060fcf822d2f368ff" + +process-nextick-args@~2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/process-nextick-args/-/process-nextick-args-2.0.0.tgz#a37d732f4271b4ab1ad070d35508e8290788ffaa" + +process@^0.11.10: + version "0.11.10" + resolved "https://registry.yarnpkg.com/process/-/process-0.11.10.tgz#7332300e840161bda3e69a1d1d91a7d4bc16f182" + +promise@^7.1.1: + version "7.3.1" + resolved "https://registry.yarnpkg.com/promise/-/promise-7.3.1.tgz#064b72602b18f90f29192b8b1bc418ffd1ebd3bf" + dependencies: + asap "~2.0.3" + +prop-types@^15.5.4, prop-types@^15.6.0, prop-types@^15.6.1: + version "15.6.2" + resolved "https://registry.yarnpkg.com/prop-types/-/prop-types-15.6.2.tgz#05d5ca77b4453e985d60fc7ff8c859094a497102" + dependencies: + loose-envify "^1.3.1" + object-assign "^4.1.1" + +proto-list@~1.2.1: + version "1.2.4" + resolved "https://registry.yarnpkg.com/proto-list/-/proto-list-1.2.4.tgz#212d5bfe1318306a420f6402b8e26ff39647a849" + +pseudomap@^1.0.1: + version "1.0.2" + resolved "https://registry.yarnpkg.com/pseudomap/-/pseudomap-1.0.2.tgz#f052a28da70e618917ef0a8ac34c1ae5a68286b3" + +public-encrypt@^4.0.0: + version "4.0.2" + resolved "https://registry.yarnpkg.com/public-encrypt/-/public-encrypt-4.0.2.tgz#46eb9107206bf73489f8b85b69d91334c6610994" + dependencies: + bn.js "^4.1.0" + browserify-rsa "^4.0.0" + create-hash "^1.1.0" + parse-asn1 "^5.0.0" + randombytes "^2.0.1" + +punycode@1.3.2: + version "1.3.2" + resolved "https://registry.yarnpkg.com/punycode/-/punycode-1.3.2.tgz#9653a036fb7c1ee42342f2325cceefea3926c48d" + +punycode@^1.2.4: + version "1.4.1" + resolved "https://registry.yarnpkg.com/punycode/-/punycode-1.4.1.tgz#c0d5a63b2718800ad8e1eb0fa5269c84dd41845e" + +q@^1.1.2: + version "1.5.1" + resolved "https://registry.yarnpkg.com/q/-/q-1.5.1.tgz#7e32f75b41381291d04611f1bf14109ac00651d7" + +query-string@^4.1.0: + version "4.3.4" + resolved "https://registry.yarnpkg.com/query-string/-/query-string-4.3.4.tgz#bbb693b9ca915c232515b228b1a02b609043dbeb" + dependencies: + object-assign "^4.1.0" + strict-uri-encode "^1.0.0" + +querystring-es3@^0.2.0: + version "0.2.1" + resolved "https://registry.yarnpkg.com/querystring-es3/-/querystring-es3-0.2.1.tgz#9ec61f79049875707d69414596fd907a4d711e73" + +querystring@0.2.0: + version "0.2.0" + resolved "https://registry.yarnpkg.com/querystring/-/querystring-0.2.0.tgz#b209849203bb25df820da756e747005878521620" + +quote-stream@^1.0.1, quote-stream@~1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/quote-stream/-/quote-stream-1.0.2.tgz#84963f8c9c26b942e153feeb53aae74652b7e0b2" + dependencies: + buffer-equal "0.0.1" + minimist "^1.1.3" + through2 "^2.0.0" + +randombytes@^2.0.0, randombytes@^2.0.1, randombytes@^2.0.5: + version "2.0.6" + resolved "https://registry.yarnpkg.com/randombytes/-/randombytes-2.0.6.tgz#d302c522948588848a8d300c932b44c24231da80" + dependencies: + safe-buffer "^5.1.0" + +randomfill@^1.0.3: + version "1.0.4" + resolved "https://registry.yarnpkg.com/randomfill/-/randomfill-1.0.4.tgz#c92196fc86ab42be983f1bf31778224931d61458" + dependencies: + randombytes "^2.0.5" + safe-buffer "^5.1.0" + +range-parser@~1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/range-parser/-/range-parser-1.2.0.tgz#f49be6b487894ddc40dcc94a322f611092e00d5e" + +rc@^1.2.7: + version "1.2.8" + resolved "https://registry.yarnpkg.com/rc/-/rc-1.2.8.tgz#cd924bf5200a075b83c188cd6b9e211b7fc0d3ed" + dependencies: + deep-extend "^0.6.0" + ini "~1.3.0" + minimist "^1.2.0" + strip-json-comments "~2.0.1" + +react-dom@^16.4.2: + version "16.4.2" + resolved "https://registry.yarnpkg.com/react-dom/-/react-dom-16.4.2.tgz#4afed569689f2c561d2b8da0b819669c38a0bda4" + dependencies: + fbjs "^0.8.16" + loose-envify "^1.1.0" + object-assign "^4.1.1" + prop-types "^15.6.0" + +react-fast-compare@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/react-fast-compare/-/react-fast-compare-1.0.0.tgz#813a039155e49b43ceffe99528fe5e9d97a6c938" + +react-is@^16.3.1: + version "16.4.2" + resolved "https://registry.yarnpkg.com/react-is/-/react-is-16.4.2.tgz#84891b56c2b6d9efdee577cc83501dfc5ecead88" + +react@^16.4.2: + version "16.4.2" + resolved "https://registry.yarnpkg.com/react/-/react-16.4.2.tgz#2cd90154e3a9d9dd8da2991149fdca3c260e129f" + dependencies: + fbjs "^0.8.16" + loose-envify "^1.1.0" + object-assign "^4.1.1" + prop-types "^15.6.0" + +readable-stream@^2.0.2, readable-stream@^2.0.6, readable-stream@^2.1.5, readable-stream@^2.2.2, readable-stream@^2.3.3, readable-stream@^2.3.6, readable-stream@~2.3.3: + version "2.3.6" + resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-2.3.6.tgz#b11c27d88b8ff1fbe070643cf94b0c79ae1b0aaf" + dependencies: + core-util-is "~1.0.0" + inherits "~2.0.3" + isarray "~1.0.0" + process-nextick-args "~2.0.0" + safe-buffer "~5.1.1" + string_decoder "~1.1.1" + util-deprecate "~1.0.1" + +readdirp@^2.0.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/readdirp/-/readdirp-2.1.0.tgz#4ed0ad060df3073300c48440373f72d1cc642d78" + dependencies: + graceful-fs "^4.1.2" + minimatch "^3.0.2" + readable-stream "^2.0.2" + set-immediate-shim "^1.0.1" + +reduce-css-calc@^1.2.6: + version "1.3.0" + resolved "https://registry.yarnpkg.com/reduce-css-calc/-/reduce-css-calc-1.3.0.tgz#747c914e049614a4c9cfbba629871ad1d2927716" + dependencies: + balanced-match "^0.4.2" + math-expression-evaluator "^1.2.14" + reduce-function-call "^1.0.1" + +reduce-css-calc@^2.0.0: + version "2.1.4" + resolved "https://registry.yarnpkg.com/reduce-css-calc/-/reduce-css-calc-2.1.4.tgz#c20e9cda8445ad73d4ff4bea960c6f8353791708" + dependencies: + css-unit-converter "^1.1.1" + postcss-value-parser "^3.3.0" + +reduce-function-call@^1.0.1: + version "1.0.2" + resolved "https://registry.yarnpkg.com/reduce-function-call/-/reduce-function-call-1.0.2.tgz#5a200bf92e0e37751752fe45b0ab330fd4b6be99" + dependencies: + balanced-match "^0.4.2" + +regenerate@^1.2.1: + version "1.4.0" + resolved "https://registry.yarnpkg.com/regenerate/-/regenerate-1.4.0.tgz#4a856ec4b56e4077c557589cae85e7a4c8869a11" + +regenerator-runtime@^0.11.0: + version "0.11.1" + resolved "https://registry.yarnpkg.com/regenerator-runtime/-/regenerator-runtime-0.11.1.tgz#be05ad7f9bf7d22e056f9726cee5017fbf19e2e9" + +regenerator-transform@^0.10.0: + version "0.10.1" + resolved "https://registry.yarnpkg.com/regenerator-transform/-/regenerator-transform-0.10.1.tgz#1e4996837231da8b7f3cf4114d71b5691a0680dd" + dependencies: + babel-runtime "^6.18.0" + babel-types "^6.19.0" + private "^0.1.6" + +regex-not@^1.0.0, regex-not@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/regex-not/-/regex-not-1.0.2.tgz#1f4ece27e00b0b65e0247a6810e6a85d83a5752c" + dependencies: + extend-shallow "^3.0.2" + safe-regex "^1.1.0" + +regexpu-core@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/regexpu-core/-/regexpu-core-2.0.0.tgz#49d038837b8dcf8bfa5b9a42139938e6ea2ae240" + dependencies: + regenerate "^1.2.1" + regjsgen "^0.2.0" + regjsparser "^0.1.4" + +regjsgen@^0.2.0: + version "0.2.0" + resolved "https://registry.yarnpkg.com/regjsgen/-/regjsgen-0.2.0.tgz#6c016adeac554f75823fe37ac05b92d5a4edb1f7" + +regjsparser@^0.1.4: + version "0.1.5" + resolved "https://registry.yarnpkg.com/regjsparser/-/regjsparser-0.1.5.tgz#7ee8f84dc6fa792d3fd0ae228d24bd949ead205c" + dependencies: + jsesc "~0.5.0" + +remove-trailing-separator@^1.0.1: + version "1.1.0" + resolved "https://registry.yarnpkg.com/remove-trailing-separator/-/remove-trailing-separator-1.1.0.tgz#c24bce2a283adad5bc3f58e0d48249b92379d8ef" + +repeat-element@^1.1.2: + version "1.1.2" + resolved "https://registry.yarnpkg.com/repeat-element/-/repeat-element-1.1.2.tgz#ef089a178d1483baae4d93eb98b4f9e4e11d990a" + +repeat-string@^1.6.1: + version "1.6.1" + resolved "https://registry.yarnpkg.com/repeat-string/-/repeat-string-1.6.1.tgz#8dcae470e1c88abc2d600fff4a776286da75e637" + +repeating@^2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/repeating/-/repeating-2.0.1.tgz#5214c53a926d3552707527fbab415dbc08d06dda" + dependencies: + is-finite "^1.0.0" + +resolve-url@^0.2.1: + version "0.2.1" + resolved "https://registry.yarnpkg.com/resolve-url/-/resolve-url-0.2.1.tgz#2c637fe77c893afd2a663fe21aa9080068e2052a" + +resolve@^1.1.5, resolve@^1.4.0: + version "1.8.1" + resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.8.1.tgz#82f1ec19a423ac1fbd080b0bab06ba36e84a7a26" + dependencies: + path-parse "^1.0.5" + +restore-cursor@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/restore-cursor/-/restore-cursor-2.0.0.tgz#9f7ee287f82fd326d4fd162923d62129eee0dfaf" + dependencies: + onetime "^2.0.0" + signal-exit "^3.0.2" + +ret@~0.1.10: + version "0.1.15" + resolved "https://registry.yarnpkg.com/ret/-/ret-0.1.15.tgz#b8a4825d5bdb1fc3f6f53c2bc33f81388681c7bc" + +rgb-regex@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/rgb-regex/-/rgb-regex-1.0.1.tgz#c0e0d6882df0e23be254a475e8edd41915feaeb1" + +rgba-regex@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/rgba-regex/-/rgba-regex-1.0.0.tgz#43374e2e2ca0968b0ef1523460b7d730ff22eeb3" + +rimraf@^2.6.1: + version "2.6.2" + resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-2.6.2.tgz#2ed8150d24a16ea8651e6d6ef0f47c4158ce7a36" + dependencies: + glob "^7.0.5" + +ripemd160@^2.0.0, ripemd160@^2.0.1: + version "2.0.2" + resolved "https://registry.yarnpkg.com/ripemd160/-/ripemd160-2.0.2.tgz#a1c1a6f624751577ba5d07914cbc92850585890c" + dependencies: + hash-base "^3.0.0" + inherits "^2.0.1" + +safe-buffer@^5.0.1, safe-buffer@^5.1.0, safe-buffer@^5.1.1, safe-buffer@^5.1.2, safe-buffer@~5.1.0, safe-buffer@~5.1.1: + version "5.1.2" + resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.1.2.tgz#991ec69d296e0313747d59bdfd2b745c35f8828d" + +safe-regex@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/safe-regex/-/safe-regex-1.1.0.tgz#40a3669f3b077d1e943d44629e157dd48023bf2e" + dependencies: + ret "~0.1.10" + +"safer-buffer@>= 2.1.2 < 3": + version "2.1.2" + resolved "https://registry.yarnpkg.com/safer-buffer/-/safer-buffer-2.1.2.tgz#44fa161b0187b9549dd84bb91802f9bd8385cd6a" + +safer-eval@^1.2.3: + version "1.2.3" + resolved "https://registry.yarnpkg.com/safer-eval/-/safer-eval-1.2.3.tgz#73ba74a34bc8a07d6a44135c815fd18a8eebe7a0" + dependencies: + clones "^1.1.0" + +sax@^1.2.4, sax@~1.2.1, sax@~1.2.4: + version "1.2.4" + resolved "https://registry.yarnpkg.com/sax/-/sax-1.2.4.tgz#2816234e2378bddc4e5354fab5caa895df7100d9" + +semver@^5.1.0, semver@^5.3.0, semver@^5.4.1, semver@^5.5.0: + version "5.5.0" + resolved "https://registry.yarnpkg.com/semver/-/semver-5.5.0.tgz#dc4bbc7a6ca9d916dee5d43516f0092b58f7b8ab" + +send@0.16.2: + version "0.16.2" + resolved "https://registry.yarnpkg.com/send/-/send-0.16.2.tgz#6ecca1e0f8c156d141597559848df64730a6bbc1" + dependencies: + debug "2.6.9" + depd "~1.1.2" + destroy "~1.0.4" + encodeurl "~1.0.2" + escape-html "~1.0.3" + etag "~1.8.1" + fresh "0.5.2" + http-errors "~1.6.2" + mime "1.4.1" + ms "2.0.0" + on-finished "~2.3.0" + range-parser "~1.2.0" + statuses "~1.4.0" + +serialize-to-js@^1.1.1: + version "1.2.1" + resolved "https://registry.yarnpkg.com/serialize-to-js/-/serialize-to-js-1.2.1.tgz#2e87f61f938826d24c463a7cbd0dd2929ec38008" + dependencies: + js-beautify "^1.7.5" + safer-eval "^1.2.3" + +serve-static@^1.12.4: + version "1.13.2" + resolved "https://registry.yarnpkg.com/serve-static/-/serve-static-1.13.2.tgz#095e8472fd5b46237db50ce486a43f4b86c6cec1" + dependencies: + encodeurl "~1.0.2" + escape-html "~1.0.3" + parseurl "~1.3.2" + send "0.16.2" + +set-blocking@~2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/set-blocking/-/set-blocking-2.0.0.tgz#045f9782d011ae9a6803ddd382b24392b3d890f7" + +set-immediate-shim@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/set-immediate-shim/-/set-immediate-shim-1.0.1.tgz#4b2b1b27eb808a9f8dcc481a58e5e56f599f3f61" + +set-value@^0.4.3: + version "0.4.3" + resolved "https://registry.yarnpkg.com/set-value/-/set-value-0.4.3.tgz#7db08f9d3d22dc7f78e53af3c3bf4666ecdfccf1" + dependencies: + extend-shallow "^2.0.1" + is-extendable "^0.1.1" + is-plain-object "^2.0.1" + to-object-path "^0.3.0" + +set-value@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/set-value/-/set-value-2.0.0.tgz#71ae4a88f0feefbbf52d1ea604f3fb315ebb6274" + dependencies: + extend-shallow "^2.0.1" + is-extendable "^0.1.1" + is-plain-object "^2.0.3" + split-string "^3.0.1" + +setimmediate@^1.0.4, setimmediate@^1.0.5: + version "1.0.5" + resolved "https://registry.yarnpkg.com/setimmediate/-/setimmediate-1.0.5.tgz#290cbb232e306942d7d7ea9b83732ab7856f8285" + +setprototypeof@1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/setprototypeof/-/setprototypeof-1.1.0.tgz#d0bd85536887b6fe7c0d818cb962d9d91c54e656" + +sha.js@^2.4.0, sha.js@^2.4.8: + version "2.4.11" + resolved "https://registry.yarnpkg.com/sha.js/-/sha.js-2.4.11.tgz#37a5cf0b81ecbc6943de109ba2960d1b26584ae7" + dependencies: + inherits "^2.0.1" + safe-buffer "^5.0.1" + +shallow-copy@~0.0.1: + version "0.0.1" + resolved "https://registry.yarnpkg.com/shallow-copy/-/shallow-copy-0.0.1.tgz#415f42702d73d810330292cc5ee86eae1a11a170" + +shebang-command@^1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/shebang-command/-/shebang-command-1.2.0.tgz#44aac65b695b03398968c39f363fee5deafdf1ea" + dependencies: + shebang-regex "^1.0.0" + +shebang-regex@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/shebang-regex/-/shebang-regex-1.0.0.tgz#da42f49740c0b42db2ca9728571cb190c98efea3" + +sigmund@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/sigmund/-/sigmund-1.0.1.tgz#3ff21f198cad2175f9f3b781853fd94d0d19b590" + +signal-exit@^3.0.0, signal-exit@^3.0.2: + version "3.0.2" + resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-3.0.2.tgz#b5fdc08f1287ea1178628e415e25132b73646c6d" + +simple-swizzle@^0.2.2: + version "0.2.2" + resolved "https://registry.yarnpkg.com/simple-swizzle/-/simple-swizzle-0.2.2.tgz#a4da6b635ffcccca33f70d17cb92592de95e557a" + dependencies: + is-arrayish "^0.3.1" + +slash@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/slash/-/slash-1.0.0.tgz#c41f2f6c39fc16d1cd17ad4b5d896114ae470d55" + +snapdragon-node@^2.0.1: + version "2.1.1" + resolved "https://registry.yarnpkg.com/snapdragon-node/-/snapdragon-node-2.1.1.tgz#6c175f86ff14bdb0724563e8f3c1b021a286853b" + dependencies: + define-property "^1.0.0" + isobject "^3.0.0" + snapdragon-util "^3.0.1" + +snapdragon-util@^3.0.1: + version "3.0.1" + resolved "https://registry.yarnpkg.com/snapdragon-util/-/snapdragon-util-3.0.1.tgz#f956479486f2acd79700693f6f7b805e45ab56e2" + dependencies: + kind-of "^3.2.0" + +snapdragon@^0.8.1: + version "0.8.2" + resolved "https://registry.yarnpkg.com/snapdragon/-/snapdragon-0.8.2.tgz#64922e7c565b0e14204ba1aa7d6964278d25182d" + dependencies: + base "^0.11.1" + debug "^2.2.0" + define-property "^0.2.5" + extend-shallow "^2.0.1" + map-cache "^0.2.2" + source-map "^0.5.6" + source-map-resolve "^0.5.0" + use "^3.1.0" + +sort-keys@^1.0.0: + version "1.1.2" + resolved "https://registry.yarnpkg.com/sort-keys/-/sort-keys-1.1.2.tgz#441b6d4d346798f1b4e49e8920adfba0e543f9ad" + dependencies: + is-plain-obj "^1.0.0" + +source-map-resolve@^0.5.0: + version "0.5.2" + resolved "https://registry.yarnpkg.com/source-map-resolve/-/source-map-resolve-0.5.2.tgz#72e2cc34095543e43b2c62b2c4c10d4a9054f259" + dependencies: + atob "^2.1.1" + decode-uri-component "^0.2.0" + resolve-url "^0.2.1" + source-map-url "^0.4.0" + urix "^0.1.0" + +source-map-support@^0.4.15: + version "0.4.18" + resolved "https://registry.yarnpkg.com/source-map-support/-/source-map-support-0.4.18.tgz#0286a6de8be42641338594e97ccea75f0a2c585f" + dependencies: + source-map "^0.5.6" + +source-map-support@~0.5.6: + version "0.5.8" + resolved "https://registry.yarnpkg.com/source-map-support/-/source-map-support-0.5.8.tgz#04f5581713a8a65612d0175fbf3a01f80a162613" + dependencies: + buffer-from "^1.0.0" + source-map "^0.6.0" + +source-map-url@^0.4.0: + version "0.4.0" + resolved "https://registry.yarnpkg.com/source-map-url/-/source-map-url-0.4.0.tgz#3e935d7ddd73631b97659956d55128e87b5084a3" + +source-map@0.6.1, source-map@^0.6.0, source-map@^0.6.1, source-map@~0.6.1: + version "0.6.1" + resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.6.1.tgz#74722af32e9614e9c287a8d0bbde48b5e2f1a263" + +source-map@^0.5.3, source-map@^0.5.6, source-map@^0.5.7: + version "0.5.7" + resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.5.7.tgz#8a039d2d1021d22d1ea14c80d8ea468ba2ef3fcc" + +split-string@^3.0.1, split-string@^3.0.2: + version "3.1.0" + resolved "https://registry.yarnpkg.com/split-string/-/split-string-3.1.0.tgz#7cb09dda3a86585705c64b39a6466038682e8fe2" + dependencies: + extend-shallow "^3.0.0" + +sprintf-js@~1.0.2: + version "1.0.3" + resolved "https://registry.yarnpkg.com/sprintf-js/-/sprintf-js-1.0.3.tgz#04e6926f662895354f3dd015203633b857297e2c" + +stable@~0.1.6: + version "0.1.8" + resolved "https://registry.yarnpkg.com/stable/-/stable-0.1.8.tgz#836eb3c8382fe2936feaf544631017ce7d47a3cf" + +static-eval@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/static-eval/-/static-eval-2.0.0.tgz#0e821f8926847def7b4b50cda5d55c04a9b13864" + dependencies: + escodegen "^1.8.1" + +static-extend@^0.1.1: + version "0.1.2" + resolved "https://registry.yarnpkg.com/static-extend/-/static-extend-0.1.2.tgz#60809c39cbff55337226fd5e0b520f341f1fb5c6" + dependencies: + define-property "^0.2.5" + object-copy "^0.1.0" + +static-module@^2.2.0: + version "2.2.5" + resolved "https://registry.yarnpkg.com/static-module/-/static-module-2.2.5.tgz#bd40abceae33da6b7afb84a0e4329ff8852bfbbf" + dependencies: + concat-stream "~1.6.0" + convert-source-map "^1.5.1" + duplexer2 "~0.1.4" + escodegen "~1.9.0" + falafel "^2.1.0" + has "^1.0.1" + magic-string "^0.22.4" + merge-source-map "1.0.4" + object-inspect "~1.4.0" + quote-stream "~1.0.2" + readable-stream "~2.3.3" + shallow-copy "~0.0.1" + static-eval "^2.0.0" + through2 "~2.0.3" + +"statuses@>= 1.4.0 < 2": + version "1.5.0" + resolved "https://registry.yarnpkg.com/statuses/-/statuses-1.5.0.tgz#161c7dac177659fd9811f43771fa99381478628c" + +statuses@~1.4.0: + version "1.4.0" + resolved "https://registry.yarnpkg.com/statuses/-/statuses-1.4.0.tgz#bb73d446da2796106efcc1b601a253d6c46bd087" + +stream-browserify@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/stream-browserify/-/stream-browserify-2.0.1.tgz#66266ee5f9bdb9940a4e4514cafb43bb71e5c9db" + dependencies: + inherits "~2.0.1" + readable-stream "^2.0.2" + +stream-http@^2.7.2: + version "2.8.3" + resolved "https://registry.yarnpkg.com/stream-http/-/stream-http-2.8.3.tgz#b2d242469288a5a27ec4fe8933acf623de6514fc" + dependencies: + builtin-status-codes "^3.0.0" + inherits "^2.0.1" + readable-stream "^2.3.6" + to-arraybuffer "^1.0.0" + xtend "^4.0.0" + +strict-uri-encode@^1.0.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/strict-uri-encode/-/strict-uri-encode-1.1.0.tgz#279b225df1d582b1f54e65addd4352e18faa0713" + +string-width@^1.0.1: + version "1.0.2" + resolved "https://registry.yarnpkg.com/string-width/-/string-width-1.0.2.tgz#118bdf5b8cdc51a2a7e70d211e07e2b0b9b107d3" + dependencies: + code-point-at "^1.0.0" + is-fullwidth-code-point "^1.0.0" + strip-ansi "^3.0.0" + +"string-width@^1.0.2 || 2": + version "2.1.1" + resolved "https://registry.yarnpkg.com/string-width/-/string-width-2.1.1.tgz#ab93f27a8dc13d28cac815c462143a6d9012ae9e" + dependencies: + is-fullwidth-code-point "^2.0.0" + strip-ansi "^4.0.0" + +string_decoder@^1.0.0, string_decoder@~1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-1.1.1.tgz#9cf1611ba62685d7030ae9e4ba34149c3af03fc8" + dependencies: + safe-buffer "~5.1.0" + +strip-ansi@^3.0.0, strip-ansi@^3.0.1: + version "3.0.1" + resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-3.0.1.tgz#6a385fb8853d952d5ff05d0e8aaf94278dc63dcf" + dependencies: + ansi-regex "^2.0.0" + +strip-ansi@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-4.0.0.tgz#a8479022eb1ac368a871389b635262c505ee368f" + dependencies: + ansi-regex "^3.0.0" + +strip-json-comments@~2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-2.0.1.tgz#3c531942e908c2697c0ec344858c286c7ca0a60a" + +styled-components@^3.4.2: + version "3.4.2" + resolved "https://registry.yarnpkg.com/styled-components/-/styled-components-3.4.2.tgz#8f518419932327e47fe9144824e3184b3e2da95d" + dependencies: + buffer "^5.0.3" + css-to-react-native "^2.0.3" + fbjs "^0.8.16" + hoist-non-react-statics "^2.5.0" + prop-types "^15.5.4" + react-is "^16.3.1" + stylis "^3.5.0" + stylis-rule-sheet "^0.0.10" + supports-color "^3.2.3" + +stylehacks@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/stylehacks/-/stylehacks-4.0.0.tgz#64b323951c4a24e5fc7b2ec06c137bf32d155e8a" + dependencies: + browserslist "^4.0.0" + postcss "^6.0.0" + postcss-selector-parser "^3.0.0" + +stylis-rule-sheet@^0.0.10: + version "0.0.10" + resolved "https://registry.yarnpkg.com/stylis-rule-sheet/-/stylis-rule-sheet-0.0.10.tgz#44e64a2b076643f4b52e5ff71efc04d8c3c4a430" + +stylis@^3.5.0: + version "3.5.3" + resolved "https://registry.yarnpkg.com/stylis/-/stylis-3.5.3.tgz#99fdc46afba6af4deff570825994181a5e6ce546" + +supports-color@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-2.0.0.tgz#535d045ce6b6363fa40117084629995e9df324c7" + +supports-color@^3.2.3: + version "3.2.3" + resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-3.2.3.tgz#65ac0504b3954171d8a64946b2ae3cbb8a5f54f6" + dependencies: + has-flag "^1.0.0" + +supports-color@^5.3.0, supports-color@^5.4.0: + version "5.4.0" + resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-5.4.0.tgz#1c6b337402c2137605efe19f10fec390f6faab54" + dependencies: + has-flag "^3.0.0" + +svgo@^0.7.0: + version "0.7.2" + resolved "https://registry.yarnpkg.com/svgo/-/svgo-0.7.2.tgz#9f5772413952135c6fefbf40afe6a4faa88b4bb5" + dependencies: + coa "~1.0.1" + colors "~1.1.2" + csso "~2.3.1" + js-yaml "~3.7.0" + mkdirp "~0.5.1" + sax "~1.2.1" + whet.extend "~0.9.9" + +svgo@^1.0.0, svgo@^1.0.5: + version "1.0.5" + resolved "https://registry.yarnpkg.com/svgo/-/svgo-1.0.5.tgz#7040364c062a0538abacff4401cea6a26a7a389a" + dependencies: + coa "~2.0.1" + colors "~1.1.2" + css-select "~1.3.0-rc0" + css-select-base-adapter "~0.1.0" + css-tree "1.0.0-alpha25" + css-url-regex "^1.1.0" + csso "^3.5.0" + js-yaml "~3.10.0" + mkdirp "~0.5.1" + object.values "^1.0.4" + sax "~1.2.4" + stable "~0.1.6" + unquote "~1.1.1" + util.promisify "~1.0.0" + +tar@^4: + version "4.4.6" + resolved "https://registry.yarnpkg.com/tar/-/tar-4.4.6.tgz#63110f09c00b4e60ac8bcfe1bf3c8660235fbc9b" + dependencies: + chownr "^1.0.1" + fs-minipass "^1.2.5" + minipass "^2.3.3" + minizlib "^1.1.0" + mkdirp "^0.5.0" + safe-buffer "^5.1.2" + yallist "^3.0.2" + +terser@^3.7.3, terser@^3.8.1: + version "3.8.1" + resolved "https://registry.yarnpkg.com/terser/-/terser-3.8.1.tgz#cb70070ac9e0a71add169dfb63c0a64fca2738ac" + dependencies: + commander "~2.16.0" + source-map "~0.6.1" + source-map-support "~0.5.6" + +through2@^2.0.0, through2@~2.0.3: + version "2.0.3" + resolved "https://registry.yarnpkg.com/through2/-/through2-2.0.3.tgz#0004569b37c7c74ba39c43f3ced78d1ad94140be" + dependencies: + readable-stream "^2.1.5" + xtend "~4.0.1" + +timers-browserify@^2.0.4: + version "2.0.10" + resolved "https://registry.yarnpkg.com/timers-browserify/-/timers-browserify-2.0.10.tgz#1d28e3d2aadf1d5a5996c4e9f95601cd053480ae" + dependencies: + setimmediate "^1.0.4" + +timsort@^0.3.0: + version "0.3.0" + resolved "https://registry.yarnpkg.com/timsort/-/timsort-0.3.0.tgz#405411a8e7e6339fe64db9a234de11dc31e02bd4" + +tiny-inflate@^1.0.0: + version "1.0.2" + resolved "https://registry.yarnpkg.com/tiny-inflate/-/tiny-inflate-1.0.2.tgz#93d9decffc8805bd57eae4310f0b745e9b6fb3a7" + +to-arraybuffer@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/to-arraybuffer/-/to-arraybuffer-1.0.1.tgz#7d229b1fcc637e466ca081180836a7aabff83f43" + +to-fast-properties@^1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/to-fast-properties/-/to-fast-properties-1.0.3.tgz#b83571fa4d8c25b82e231b06e3a3055de4ca1a47" + +to-object-path@^0.3.0: + version "0.3.0" + resolved "https://registry.yarnpkg.com/to-object-path/-/to-object-path-0.3.0.tgz#297588b7b0e7e0ac08e04e672f85c1f4999e17af" + dependencies: + kind-of "^3.0.2" + +to-regex-range@^2.1.0: + version "2.1.1" + resolved "https://registry.yarnpkg.com/to-regex-range/-/to-regex-range-2.1.1.tgz#7c80c17b9dfebe599e27367e0d4dd5590141db38" + dependencies: + is-number "^3.0.0" + repeat-string "^1.6.1" + +to-regex@^3.0.1, to-regex@^3.0.2: + version "3.0.2" + resolved "https://registry.yarnpkg.com/to-regex/-/to-regex-3.0.2.tgz#13cfdd9b336552f30b51f33a8ae1b42a7a7599ce" + dependencies: + define-property "^2.0.2" + extend-shallow "^3.0.2" + regex-not "^1.0.2" + safe-regex "^1.1.0" + +toml@^2.3.3: + version "2.3.3" + resolved "https://registry.yarnpkg.com/toml/-/toml-2.3.3.tgz#8d683d729577cb286231dfc7a8affe58d31728fb" + +tomlify-j0.4@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/tomlify-j0.4/-/tomlify-j0.4-3.0.0.tgz#99414d45268c3a3b8bf38be82145b7bba34b7473" + +trim-right@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/trim-right/-/trim-right-1.0.1.tgz#cb2e1203067e0c8de1f614094b9fe45704ea6003" + +tslib@^1.9.3: + version "1.9.3" + resolved "https://registry.yarnpkg.com/tslib/-/tslib-1.9.3.tgz#d7e4dd79245d85428c4d7e4822a79917954ca286" + +tty-browserify@0.0.0: + version "0.0.0" + resolved "https://registry.yarnpkg.com/tty-browserify/-/tty-browserify-0.0.0.tgz#a157ba402da24e9bf957f9aa69d524eed42901a6" + +type-check@~0.3.2: + version "0.3.2" + resolved "https://registry.yarnpkg.com/type-check/-/type-check-0.3.2.tgz#5884cab512cf1d355e3fb784f30804b2b520db72" + dependencies: + prelude-ls "~1.1.2" + +typedarray@^0.0.6: + version "0.0.6" + resolved "https://registry.yarnpkg.com/typedarray/-/typedarray-0.0.6.tgz#867ac74e3864187b1d3d47d996a78ec5c8830777" + +ua-parser-js@^0.7.18: + version "0.7.18" + resolved "https://registry.yarnpkg.com/ua-parser-js/-/ua-parser-js-0.7.18.tgz#a7bfd92f56edfb117083b69e31d2aa8882d4b1ed" + +unicode-trie@^0.3.1: + version "0.3.1" + resolved "https://registry.yarnpkg.com/unicode-trie/-/unicode-trie-0.3.1.tgz#d671dddd89101a08bac37b6a5161010602052085" + dependencies: + pako "^0.2.5" + tiny-inflate "^1.0.0" + +union-value@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/union-value/-/union-value-1.0.0.tgz#5c71c34cb5bad5dcebe3ea0cd08207ba5aa1aea4" + dependencies: + arr-union "^3.1.0" + get-value "^2.0.6" + is-extendable "^0.1.1" + set-value "^0.4.3" + +uniq@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/uniq/-/uniq-1.0.1.tgz#b31c5ae8254844a3a8281541ce2b04b865a734ff" + +uniqs@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/uniqs/-/uniqs-2.0.0.tgz#ffede4b36b25290696e6e165d4a59edb998e6b02" + +unquote@~1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/unquote/-/unquote-1.1.1.tgz#8fded7324ec6e88a0ff8b905e7c098cdc086d544" + +unset-value@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/unset-value/-/unset-value-1.0.0.tgz#8376873f7d2335179ffb1e6fc3a8ed0dfc8ab559" + dependencies: + has-value "^0.3.1" + isobject "^3.0.0" + +upath@^1.0.5: + version "1.1.0" + resolved "https://registry.yarnpkg.com/upath/-/upath-1.1.0.tgz#35256597e46a581db4793d0ce47fa9aebfc9fabd" + +urix@^0.1.0: + version "0.1.0" + resolved "https://registry.yarnpkg.com/urix/-/urix-0.1.0.tgz#da937f7a62e21fec1fd18d49b35c2935067a6c72" + +url@^0.11.0: + version "0.11.0" + resolved "https://registry.yarnpkg.com/url/-/url-0.11.0.tgz#3838e97cfc60521eb73c525a8e55bfdd9e2e28f1" + dependencies: + punycode "1.3.2" + querystring "0.2.0" + +use@^3.1.0: + version "3.1.1" + resolved "https://registry.yarnpkg.com/use/-/use-3.1.1.tgz#d50c8cac79a19fbc20f2911f56eb973f4e10070f" + +util-deprecate@~1.0.1: + version "1.0.2" + resolved "https://registry.yarnpkg.com/util-deprecate/-/util-deprecate-1.0.2.tgz#450d4dc9fa70de732762fbd2d4a28981419a0ccf" + +util.promisify@~1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/util.promisify/-/util.promisify-1.0.0.tgz#440f7165a459c9a16dc145eb8e72f35687097030" + dependencies: + define-properties "^1.1.2" + object.getownpropertydescriptors "^2.0.3" + +util@0.10.3: + version "0.10.3" + resolved "https://registry.yarnpkg.com/util/-/util-0.10.3.tgz#7afb1afe50805246489e3db7fe0ed379336ac0f9" + dependencies: + inherits "2.0.1" + +util@^0.10.3: + version "0.10.4" + resolved "https://registry.yarnpkg.com/util/-/util-0.10.4.tgz#3aa0125bfe668a4672de58857d3ace27ecb76901" + dependencies: + inherits "2.0.3" + +v8-compile-cache@^2.0.0: + version "2.0.2" + resolved "https://registry.yarnpkg.com/v8-compile-cache/-/v8-compile-cache-2.0.2.tgz#a428b28bb26790734c4fc8bc9fa106fccebf6a6c" + +vendors@^1.0.0: + version "1.0.2" + resolved "https://registry.yarnpkg.com/vendors/-/vendors-1.0.2.tgz#7fcb5eef9f5623b156bcea89ec37d63676f21801" + +vlq@^0.2.2: + version "0.2.3" + resolved "https://registry.yarnpkg.com/vlq/-/vlq-0.2.3.tgz#8f3e4328cf63b1540c0d67e1b2778386f8975b26" + +vm-browserify@0.0.4: + version "0.0.4" + resolved "https://registry.yarnpkg.com/vm-browserify/-/vm-browserify-0.0.4.tgz#5d7ea45bbef9e4a6ff65f95438e0a87c357d5a73" + dependencies: + indexof "0.0.1" + +warning@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/warning/-/warning-3.0.0.tgz#32e5377cb572de4ab04753bdf8821c01ed605b7c" + dependencies: + loose-envify "^1.0.0" + +wcwidth@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/wcwidth/-/wcwidth-1.0.1.tgz#f0b0dcf915bc5ff1528afadb2c0e17b532da2fe8" + dependencies: + defaults "^1.0.3" + +whatwg-fetch@>=0.10.0: + version "2.0.4" + resolved "https://registry.yarnpkg.com/whatwg-fetch/-/whatwg-fetch-2.0.4.tgz#dde6a5df315f9d39991aa17621853d720b85566f" + +whet.extend@~0.9.9: + version "0.9.9" + resolved "https://registry.yarnpkg.com/whet.extend/-/whet.extend-0.9.9.tgz#f877d5bf648c97e5aa542fadc16d6a259b9c11a1" + +which@^1.2.9: + version "1.3.1" + resolved "https://registry.yarnpkg.com/which/-/which-1.3.1.tgz#a45043d54f5805316da8d62f9f50918d3da70b0a" + dependencies: + isexe "^2.0.0" + +wide-align@^1.1.0: + version "1.1.3" + resolved "https://registry.yarnpkg.com/wide-align/-/wide-align-1.1.3.tgz#ae074e6bdc0c14a431e804e624549c633b000457" + dependencies: + string-width "^1.0.2 || 2" + +wordwrap@~1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/wordwrap/-/wordwrap-1.0.0.tgz#27584810891456a4171c8d0226441ade90cbcaeb" + +wrappy@1: + version "1.0.2" + resolved "https://registry.yarnpkg.com/wrappy/-/wrappy-1.0.2.tgz#b5243d8f3ec1aa35f1364605bc0d1036e30ab69f" + +ws@^5.1.1: + version "5.2.2" + resolved "https://registry.yarnpkg.com/ws/-/ws-5.2.2.tgz#dffef14866b8e8dc9133582514d1befaf96e980f" + dependencies: + async-limiter "~1.0.0" + +xtend@^4.0.0, xtend@~4.0.1: + version "4.0.1" + resolved "https://registry.yarnpkg.com/xtend/-/xtend-4.0.1.tgz#a5c6d532be656e23db820efb943a1f04998d63af" + +yallist@^3.0.0, yallist@^3.0.2: + version "3.0.2" + resolved "https://registry.yarnpkg.com/yallist/-/yallist-3.0.2.tgz#8452b4bb7e83c7c188d8041c1a837c773d6d8bb9" diff --git a/chapter-07/.gitignore b/chapter-07/.gitignore new file mode 100644 index 0000000..d11829c --- /dev/null +++ b/chapter-07/.gitignore @@ -0,0 +1,4 @@ +.classpath +.project +.settings/ +target/ diff --git a/chapter-07/.mvn/wrapper/.gitignore b/chapter-07/.mvn/wrapper/.gitignore new file mode 100644 index 0000000..e72f5e8 --- /dev/null +++ b/chapter-07/.mvn/wrapper/.gitignore @@ -0,0 +1 @@ +maven-wrapper.jar diff --git a/chapter-07/.mvn/wrapper/MavenWrapperDownloader.java b/chapter-07/.mvn/wrapper/MavenWrapperDownloader.java new file mode 100755 index 0000000..fa4f7b4 --- /dev/null +++ b/chapter-07/.mvn/wrapper/MavenWrapperDownloader.java @@ -0,0 +1,110 @@ +/* +Licensed to the Apache Software Foundation (ASF) under one +or more contributor license agreements. See the NOTICE file +distributed with this work for additional information +regarding copyright ownership. The ASF licenses this file +to you under the Apache License, Version 2.0 (the +"License"); you may not use this file except in compliance +with the License. You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, +software distributed under the License is distributed on an +"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +KIND, either express or implied. See the License for the +specific language governing permissions and limitations +under the License. +*/ + +import java.net.*; +import java.io.*; +import java.nio.channels.*; +import java.util.Properties; + +public class MavenWrapperDownloader { + + /** + * Default URL to download the maven-wrapper.jar from, if no 'downloadUrl' is provided. + */ + private static final String DEFAULT_DOWNLOAD_URL = + "https://repo.maven.apache.org/maven2/io/takari/maven-wrapper/0.4.2/maven-wrapper-0.4.2.jar"; + + /** + * Path to the maven-wrapper.properties file, which might contain a downloadUrl property to + * use instead of the default one. + */ + private static final String MAVEN_WRAPPER_PROPERTIES_PATH = + ".mvn/wrapper/maven-wrapper.properties"; + + /** + * Path where the maven-wrapper.jar will be saved to. + */ + private static final String MAVEN_WRAPPER_JAR_PATH = + ".mvn/wrapper/maven-wrapper.jar"; + + /** + * Name of the property which should be used to override the default download url for the wrapper. + */ + private static final String PROPERTY_NAME_WRAPPER_URL = "wrapperUrl"; + + public static void main(String args[]) { + System.out.println("- Downloader started"); + File baseDirectory = new File(args[0]); + System.out.println("- Using base directory: " + baseDirectory.getAbsolutePath()); + + // If the maven-wrapper.properties exists, read it and check if it contains a custom + // wrapperUrl parameter. + File mavenWrapperPropertyFile = new File(baseDirectory, MAVEN_WRAPPER_PROPERTIES_PATH); + String url = DEFAULT_DOWNLOAD_URL; + if(mavenWrapperPropertyFile.exists()) { + FileInputStream mavenWrapperPropertyFileInputStream = null; + try { + mavenWrapperPropertyFileInputStream = new FileInputStream(mavenWrapperPropertyFile); + Properties mavenWrapperProperties = new Properties(); + mavenWrapperProperties.load(mavenWrapperPropertyFileInputStream); + url = mavenWrapperProperties.getProperty(PROPERTY_NAME_WRAPPER_URL, url); + } catch (IOException e) { + System.out.println("- ERROR loading '" + MAVEN_WRAPPER_PROPERTIES_PATH + "'"); + } finally { + try { + if(mavenWrapperPropertyFileInputStream != null) { + mavenWrapperPropertyFileInputStream.close(); + } + } catch (IOException e) { + // Ignore ... + } + } + } + System.out.println("- Downloading from: : " + url); + + File outputFile = new File(baseDirectory.getAbsolutePath(), MAVEN_WRAPPER_JAR_PATH); + if(!outputFile.getParentFile().exists()) { + if(!outputFile.getParentFile().mkdirs()) { + System.out.println( + "- ERROR creating output direcrory '" + outputFile.getParentFile().getAbsolutePath() + "'"); + } + } + System.out.println("- Downloading to: " + outputFile.getAbsolutePath()); + try { + downloadFileFromURL(url, outputFile); + System.out.println("Done"); + System.exit(0); + } catch (Throwable e) { + System.out.println("- Error downloading"); + e.printStackTrace(); + System.exit(1); + } + } + + private static void downloadFileFromURL(String urlString, File destination) throws Exception { + URL website = new URL(urlString); + ReadableByteChannel rbc; + rbc = Channels.newChannel(website.openStream()); + FileOutputStream fos = new FileOutputStream(destination); + fos.getChannel().transferFrom(rbc, 0, Long.MAX_VALUE); + fos.close(); + rbc.close(); + } + +} diff --git a/chapter-07/.mvn/wrapper/maven-wrapper.properties b/chapter-07/.mvn/wrapper/maven-wrapper.properties new file mode 100755 index 0000000..00d32aa --- /dev/null +++ b/chapter-07/.mvn/wrapper/maven-wrapper.properties @@ -0,0 +1 @@ +distributionUrl=https://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.5.4/apache-maven-3.5.4-bin.zip \ No newline at end of file diff --git a/chapter-07/Dockerfile b/chapter-07/Dockerfile new file mode 100644 index 0000000..b1e9443 --- /dev/null +++ b/chapter-07/Dockerfile @@ -0,0 +1,27 @@ +FROM openjdk:alpine + +ENV APP_HOME /app/ + +COPY exercise1/hello/target/hello-1-0.0.1-SNAPSHOT.jar $APP_HOME/hello.jar +COPY exercise1/formatter/target/formatter-1-0.0.1-SNAPSHOT.jar $APP_HOME/formatter.jar + +WORKDIR $APP_HOME + +EXPOSE 8080 + +# Env variables must be set when starting the container: +# Always: +# app_name = hello | formatter +# Hello: +# formatter_host = localhost +# formatter_port = 8080 +# Formatter: +# professor = false | true +CMD java \ + -DJAEGER_SERVICE_NAME=${app_name} \ + -DJAEGER_PROPAGATION=b3 \ + -DJAEGER_ENDPOINT=http://jaeger-collector.istio-system:14268/api/traces \ + -Dformatter.host=${formatter_host:-formatter} \ + -Dformatter.port=${formatter_port:-8080} \ + -Dprofessor=${professor:-false} \ + -jar ${app_name:?'app_name must be set'}.jar diff --git a/chapter-07/Makefile b/chapter-07/Makefile new file mode 100644 index 0000000..2f5b4c5 --- /dev/null +++ b/chapter-07/Makefile @@ -0,0 +1,41 @@ +build-app: + mvn install + docker build -t hello-app:latest . + @echo '*** make sure the right docker repository is used' + @echo '*** on minikube run this first: eval $$(minikube docker-env)' + +deploy-app: + istioctl kube-inject -f app.yml | kubectl apply -f - + kubectl apply -f gateway.yml + istioctl create -f routing.yml + +delete-app: + istioctl delete -f routing.yml + kubectl delete -f app.yml + +hostport: + @echo export GATEWAY_URL=$$(minikube ip):$$(kubectl \ + get service -n istio-system istio-ingressgateway \ + -o jsonpath='{.spec.ports[?(@.name=="http2")].nodePort}') + +jaeger: + kubectl port-forward -n istio-system $$(kubectl \ + get pod -n istio-system -l app=jaeger \ + -o jsonpath='{.items[0].metadata.name}') 16686:16686 + +service-graph: + kubectl -n istio-system port-forward $$(kubectl \ + get pod -n istio-system -l app=servicegraph \ + -o jsonpath='{.items[0].metadata.name}') 8088:8088 + +logs-hello: + kubectl logs $$(kubectl get pod -l app=hello-svc \ + -o jsonpath='{.items[0].metadata.name}') hello-svc + +logs-formatter-v1: + kubectl logs $$(kubectl get pod -l app=formatter-svc -l version=v1 \ + -o jsonpath='{.items[0].metadata.name}') formatter-svc + +logs-formatter-v2: + kubectl logs $$(kubectl get pod -l app=formatter-svc -l version=v2 \ + -o jsonpath='{.items[0].metadata.name}') formatter-svc diff --git a/chapter-07/app.yml b/chapter-07/app.yml new file mode 100644 index 0000000..40ea471 --- /dev/null +++ b/chapter-07/app.yml @@ -0,0 +1,104 @@ +################################################################################################## +# Hello service +################################################################################################## +apiVersion: v1 +kind: Service +metadata: + name: hello-svc + labels: + app: hello-svc +spec: + # type: LoadBalancer + selector: + app: hello-svc + ports: + - name: http + port: 8080 +--- +apiVersion: extensions/v1beta1 +kind: Deployment +metadata: + name: hello-svc +spec: + replicas: 1 + template: + metadata: + labels: + app: hello-svc + spec: + containers: + - name: hello-svc + image: hello-app:latest + imagePullPolicy: Never + ports: + - name: http + containerPort: 8080 + env: + - name: app_name + value: "hello" + - name: formatter_host + value: "formatter-svc" +--- +################################################################################################## +# Formatter service v1/v2 +################################################################################################## +apiVersion: v1 +kind: Service +metadata: + name: formatter-svc + labels: + app: formatter-svc +spec: + selector: + app: formatter-svc + ports: + - name: http + port: 8080 +--- +apiVersion: extensions/v1beta1 +kind: Deployment +metadata: + name: formatter-svc-v1 +spec: + replicas: 1 + template: + metadata: + labels: + app: formatter-svc + version: v1 + spec: + containers: + - name: formatter-svc + image: hello-app:latest + imagePullPolicy: Never + ports: + - name: http + containerPort: 8080 + env: + - name: app_name + value: "formatter" +--- +apiVersion: extensions/v1beta1 +kind: Deployment +metadata: + name: formatter-svc-v2 +spec: + replicas: 1 + template: + metadata: + labels: + app: formatter-svc + version: v2 + spec: + containers: + - name: formatter-svc + image: hello-app:latest + imagePullPolicy: Never + ports: + - name: http + containerPort: 8080 + env: + - name: app_name + value: "formatter" + - name: professor + value: "true" diff --git a/chapter-07/exercise1/formatter/pom.xml b/chapter-07/exercise1/formatter/pom.xml new file mode 100644 index 0000000..74bdf43 --- /dev/null +++ b/chapter-07/exercise1/formatter/pom.xml @@ -0,0 +1,55 @@ + + + 4.0.0 + + com.packt.distributed-tracing-chapter-07 + formatter-1 + jar + + + com.packt.distributed-tracing-chapter-07 + exercise1 + 0.0.1-SNAPSHOT + ../ + + + + UTF-8 + UTF-8 + 1.8 + ${project.basedir}/.. + + + + + org.springframework.boot + spring-boot-starter-web + + + io.opentracing.contrib + opentracing-spring-cloud-starter + + + io.opentracing.contrib + opentracing-spring-tracer-configuration-starter + + + io.jaegertracing + jaeger-client + + + io.jaegertracing + jaeger-zipkin + + + + + + + org.springframework.boot + spring-boot-maven-plugin + + + + diff --git a/chapter-07/exercise1/formatter/src/main/java/formatter/FApp.java b/chapter-07/exercise1/formatter/src/main/java/formatter/FApp.java new file mode 100644 index 0000000..4a59e24 --- /dev/null +++ b/chapter-07/exercise1/formatter/src/main/java/formatter/FApp.java @@ -0,0 +1,12 @@ +package formatter; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; + +@SpringBootApplication +public class FApp { + + public static void main(String[] args) { + SpringApplication.run(FApp.class, args); + } +} diff --git a/chapter-07/exercise1/formatter/src/main/java/formatter/FController.java b/chapter-07/exercise1/formatter/src/main/java/formatter/FController.java new file mode 100644 index 0000000..8e66833 --- /dev/null +++ b/chapter-07/exercise1/formatter/src/main/java/formatter/FController.java @@ -0,0 +1,29 @@ +package formatter; + +import org.springframework.http.HttpHeaders; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestHeader; +import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.bind.annotation.RestController; + +@RestController +public class FController { + + private final String template; + + public FController() { + if (Boolean.getBoolean("professor")) { + template = "Good news, %s! If anyone needs me I'll be in the Angry Dome!"; + } else { + template = "Hello, puny human %s! Morbo asks: how do you like running on Kubernetes?"; + } + System.out.println("Using template: " + template); + } + + @GetMapping("/formatGreeting") + public String formatGreeting(@RequestParam String name, @RequestHeader HttpHeaders headers) { + System.out.println("Headers: " + headers); + + return String.format(template, name); + } +} diff --git a/chapter-07/exercise1/hello/pom.xml b/chapter-07/exercise1/hello/pom.xml new file mode 100644 index 0000000..f9e5786 --- /dev/null +++ b/chapter-07/exercise1/hello/pom.xml @@ -0,0 +1,55 @@ + + + 4.0.0 + + com.packt.distributed-tracing-chapter-07 + hello-1 + jar + + + com.packt.distributed-tracing-chapter-07 + exercise1 + 0.0.1-SNAPSHOT + ../ + + + + UTF-8 + UTF-8 + 1.8 + ${project.basedir}/.. + + + + + org.springframework.boot + spring-boot-starter-web + + + io.opentracing.contrib + opentracing-spring-cloud-starter + + + io.opentracing.contrib + opentracing-spring-tracer-configuration-starter + + + io.jaegertracing + jaeger-client + + + io.jaegertracing + jaeger-zipkin + + + + + + + org.springframework.boot + spring-boot-maven-plugin + + + + diff --git a/chapter-07/exercise1/hello/src/main/java/hello/HelloApp.java b/chapter-07/exercise1/hello/src/main/java/hello/HelloApp.java new file mode 100644 index 0000000..7041356 --- /dev/null +++ b/chapter-07/exercise1/hello/src/main/java/hello/HelloApp.java @@ -0,0 +1,20 @@ +package hello; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.boot.web.client.RestTemplateBuilder; +import org.springframework.context.annotation.Bean; +import org.springframework.web.client.RestTemplate; + +@SpringBootApplication +public class HelloApp { + + @Bean + public RestTemplate restTemplate(RestTemplateBuilder restTemplateBuilder) { + return restTemplateBuilder.build(); + } + + public static void main(String[] args) { + SpringApplication.run(HelloApp.class, args); + } +} diff --git a/chapter-07/exercise1/hello/src/main/java/hello/HelloController.java b/chapter-07/exercise1/hello/src/main/java/hello/HelloController.java new file mode 100644 index 0000000..f3bee9f --- /dev/null +++ b/chapter-07/exercise1/hello/src/main/java/hello/HelloController.java @@ -0,0 +1,56 @@ +package hello; + +import java.net.URI; +import java.util.Collections; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.http.HttpHeaders; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.RequestHeader; +import org.springframework.web.bind.annotation.RestController; +import org.springframework.web.client.RestTemplate; +import org.springframework.web.util.UriComponentsBuilder; + +import io.opentracing.Span; + +@RestController +public class HelloController { + + private final String formatterUrl; + + public HelloController() { + String host = System.getProperty("formatter.host", "localhost"); + String port = System.getProperty("formatter.port", "8080"); + formatterUrl = "http://" + host + ":" + port + "/formatGreeting"; + } + + @Autowired + private RestTemplate restTemplate; + + @Autowired + private io.opentracing.Tracer tracer; + + @GetMapping("/sayHello/{name}") + public String sayHello(@PathVariable String name, @RequestHeader HttpHeaders headers) { + System.out.println("Headers: " + headers); + + Span span = tracer.activeSpan(); + if (span != null) { + span.setBaggageItem("user-agent", headers.getFirst(HttpHeaders.USER_AGENT)); + } + + String response = formatGreeting(name); + return response; + } + + private String formatGreeting(String name) { + URI uri = UriComponentsBuilder // + .fromHttpUrl(formatterUrl) // + .queryParam("name", name) // + .build(Collections.emptyMap()); + ResponseEntity response = restTemplate.getForEntity(uri, String.class); + return response.getBody(); + } +} diff --git a/chapter-07/exercise1/hello/src/main/java/hello/HelloController2.java b/chapter-07/exercise1/hello/src/main/java/hello/HelloController2.java new file mode 100644 index 0000000..5113240 --- /dev/null +++ b/chapter-07/exercise1/hello/src/main/java/hello/HelloController2.java @@ -0,0 +1,72 @@ +package hello; + +import java.net.URI; +import java.util.Collections; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.http.HttpEntity; +import org.springframework.http.HttpHeaders; +import org.springframework.http.HttpMethod; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.RequestHeader; +import org.springframework.web.bind.annotation.RestController; +import org.springframework.web.client.RestTemplate; +import org.springframework.web.util.UriComponentsBuilder; + +@RestController +public class HelloController2 { + + private final String formatterUrl; + + public HelloController2() { + String host = System.getProperty("formatter.host", "localhost"); + String port = System.getProperty("formatter.port", "8080"); + formatterUrl = "http://" + host + ":" + port + "/formatGreeting"; + } + + @Autowired + private RestTemplate restTemplate; + + @GetMapping("/sayHello2/{name}") + public String sayHello(@PathVariable String name, @RequestHeader HttpHeaders headers) { + System.out.println("Headers: " + headers); + + String response = formatGreeting(name, copyHeaders(headers)); + return response; + } + + private String formatGreeting(String name, HttpHeaders tracingHeaders) { + URI uri = UriComponentsBuilder // + .fromHttpUrl(formatterUrl) // + .queryParam("name", name) // + .build(Collections.emptyMap()); + + ResponseEntity response = restTemplate.exchange( // + uri, HttpMethod.GET, new HttpEntity<>(tracingHeaders), // + String.class); + return response.getBody(); + } + + private final static String[] tracingHeaderKeys = { // + "x-request-id", // + "x-b3-traceid", // + "x-b3-spanid", // + "x-b3-parentspanid", // + "x-b3-sampled", // + "x-b3-flags", // + "x-ot-span-context" // + }; + + private HttpHeaders copyHeaders(HttpHeaders headers) { + HttpHeaders tracingHeaders = new HttpHeaders(); + for (String key : tracingHeaderKeys) { + String value = headers.getFirst(key); + if (value != null) { + tracingHeaders.add(key, value); + } + } + return tracingHeaders; + } +} diff --git a/chapter-07/exercise1/pom.xml b/chapter-07/exercise1/pom.xml new file mode 100644 index 0000000..9fd5b28 --- /dev/null +++ b/chapter-07/exercise1/pom.xml @@ -0,0 +1,23 @@ + + + 4.0.0 + + com.packt.distributed-tracing-chapter-07 + exercise1 + pom + + + hello + formatter + + + + com.packt.distributed-tracing-chapter-07 + parent + 0.0.1-SNAPSHOT + ../ + + + + diff --git a/chapter-07/gateway.yml b/chapter-07/gateway.yml new file mode 100644 index 0000000..c24ce39 --- /dev/null +++ b/chapter-07/gateway.yml @@ -0,0 +1,35 @@ +apiVersion: networking.istio.io/v1alpha3 +kind: Gateway +metadata: + name: hello-app-gateway +spec: + selector: + istio: ingressgateway # use istio default controller + servers: + - port: + number: 8080 + name: http + protocol: HTTP + hosts: + - "*" +--- +apiVersion: networking.istio.io/v1alpha3 +kind: VirtualService +metadata: + name: hello-app +spec: + hosts: + - "*" + gateways: + - hello-app-gateway + http: + - match: + - uri: + prefix: /sayHello + - uri: + prefix: /sayHello2 + route: + - destination: + host: hello-svc + port: + number: 8080 diff --git a/chapter-07/mvnw b/chapter-07/mvnw new file mode 100755 index 0000000..5551fde --- /dev/null +++ b/chapter-07/mvnw @@ -0,0 +1,286 @@ +#!/bin/sh +# ---------------------------------------------------------------------------- +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +# ---------------------------------------------------------------------------- + +# ---------------------------------------------------------------------------- +# Maven2 Start Up Batch script +# +# Required ENV vars: +# ------------------ +# JAVA_HOME - location of a JDK home dir +# +# Optional ENV vars +# ----------------- +# M2_HOME - location of maven2's installed home dir +# MAVEN_OPTS - parameters passed to the Java VM when running Maven +# e.g. to debug Maven itself, use +# set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000 +# MAVEN_SKIP_RC - flag to disable loading of mavenrc files +# ---------------------------------------------------------------------------- + +if [ -z "$MAVEN_SKIP_RC" ] ; then + + if [ -f /etc/mavenrc ] ; then + . /etc/mavenrc + fi + + if [ -f "$HOME/.mavenrc" ] ; then + . "$HOME/.mavenrc" + fi + +fi + +# OS specific support. $var _must_ be set to either true or false. +cygwin=false; +darwin=false; +mingw=false +case "`uname`" in + CYGWIN*) cygwin=true ;; + MINGW*) mingw=true;; + Darwin*) darwin=true + # Use /usr/libexec/java_home if available, otherwise fall back to /Library/Java/Home + # See https://developer.apple.com/library/mac/qa/qa1170/_index.html + if [ -z "$JAVA_HOME" ]; then + if [ -x "/usr/libexec/java_home" ]; then + export JAVA_HOME="`/usr/libexec/java_home`" + else + export JAVA_HOME="/Library/Java/Home" + fi + fi + ;; +esac + +if [ -z "$JAVA_HOME" ] ; then + if [ -r /etc/gentoo-release ] ; then + JAVA_HOME=`java-config --jre-home` + fi +fi + +if [ -z "$M2_HOME" ] ; then + ## resolve links - $0 may be a link to maven's home + PRG="$0" + + # need this for relative symlinks + while [ -h "$PRG" ] ; do + ls=`ls -ld "$PRG"` + link=`expr "$ls" : '.*-> \(.*\)$'` + if expr "$link" : '/.*' > /dev/null; then + PRG="$link" + else + PRG="`dirname "$PRG"`/$link" + fi + done + + saveddir=`pwd` + + M2_HOME=`dirname "$PRG"`/.. + + # make it fully qualified + M2_HOME=`cd "$M2_HOME" && pwd` + + cd "$saveddir" + # echo Using m2 at $M2_HOME +fi + +# For Cygwin, ensure paths are in UNIX format before anything is touched +if $cygwin ; then + [ -n "$M2_HOME" ] && + M2_HOME=`cygpath --unix "$M2_HOME"` + [ -n "$JAVA_HOME" ] && + JAVA_HOME=`cygpath --unix "$JAVA_HOME"` + [ -n "$CLASSPATH" ] && + CLASSPATH=`cygpath --path --unix "$CLASSPATH"` +fi + +# For Mingw, ensure paths are in UNIX format before anything is touched +if $mingw ; then + [ -n "$M2_HOME" ] && + M2_HOME="`(cd "$M2_HOME"; pwd)`" + [ -n "$JAVA_HOME" ] && + JAVA_HOME="`(cd "$JAVA_HOME"; pwd)`" + # TODO classpath? +fi + +if [ -z "$JAVA_HOME" ]; then + javaExecutable="`which javac`" + if [ -n "$javaExecutable" ] && ! [ "`expr \"$javaExecutable\" : '\([^ ]*\)'`" = "no" ]; then + # readlink(1) is not available as standard on Solaris 10. + readLink=`which readlink` + if [ ! `expr "$readLink" : '\([^ ]*\)'` = "no" ]; then + if $darwin ; then + javaHome="`dirname \"$javaExecutable\"`" + javaExecutable="`cd \"$javaHome\" && pwd -P`/javac" + else + javaExecutable="`readlink -f \"$javaExecutable\"`" + fi + javaHome="`dirname \"$javaExecutable\"`" + javaHome=`expr "$javaHome" : '\(.*\)/bin'` + JAVA_HOME="$javaHome" + export JAVA_HOME + fi + fi +fi + +if [ -z "$JAVACMD" ] ; then + if [ -n "$JAVA_HOME" ] ; then + if [ -x "$JAVA_HOME/jre/sh/java" ] ; then + # IBM's JDK on AIX uses strange locations for the executables + JAVACMD="$JAVA_HOME/jre/sh/java" + else + JAVACMD="$JAVA_HOME/bin/java" + fi + else + JAVACMD="`which java`" + fi +fi + +if [ ! -x "$JAVACMD" ] ; then + echo "Error: JAVA_HOME is not defined correctly." >&2 + echo " We cannot execute $JAVACMD" >&2 + exit 1 +fi + +if [ -z "$JAVA_HOME" ] ; then + echo "Warning: JAVA_HOME environment variable is not set." +fi + +CLASSWORLDS_LAUNCHER=org.codehaus.plexus.classworlds.launcher.Launcher + +# traverses directory structure from process work directory to filesystem root +# first directory with .mvn subdirectory is considered project base directory +find_maven_basedir() { + + if [ -z "$1" ] + then + echo "Path not specified to find_maven_basedir" + return 1 + fi + + basedir="$1" + wdir="$1" + while [ "$wdir" != '/' ] ; do + if [ -d "$wdir"/.mvn ] ; then + basedir=$wdir + break + fi + # workaround for JBEAP-8937 (on Solaris 10/Sparc) + if [ -d "${wdir}" ]; then + wdir=`cd "$wdir/.."; pwd` + fi + # end of workaround + done + echo "${basedir}" +} + +# concatenates all lines of a file +concat_lines() { + if [ -f "$1" ]; then + echo "$(tr -s '\n' ' ' < "$1")" + fi +} + +BASE_DIR=`find_maven_basedir "$(pwd)"` +if [ -z "$BASE_DIR" ]; then + exit 1; +fi + +########################################################################################## +# Extension to allow automatically downloading the maven-wrapper.jar from Maven-central +# This allows using the maven wrapper in projects that prohibit checking in binary data. +########################################################################################## +if [ -r "$BASE_DIR/.mvn/wrapper/maven-wrapper.jar" ]; then + if [ "$MVNW_VERBOSE" = true ]; then + echo "Found .mvn/wrapper/maven-wrapper.jar" + fi +else + if [ "$MVNW_VERBOSE" = true ]; then + echo "Couldn't find .mvn/wrapper/maven-wrapper.jar, downloading it ..." + fi + jarUrl="https://repo.maven.apache.org/maven2/io/takari/maven-wrapper/0.4.2/maven-wrapper-0.4.2.jar" + while IFS="=" read key value; do + case "$key" in (wrapperUrl) jarUrl="$value"; break ;; + esac + done < "$BASE_DIR/.mvn/wrapper/maven-wrapper.properties" + if [ "$MVNW_VERBOSE" = true ]; then + echo "Downloading from: $jarUrl" + fi + wrapperJarPath="$BASE_DIR/.mvn/wrapper/maven-wrapper.jar" + + if command -v wget > /dev/null; then + if [ "$MVNW_VERBOSE" = true ]; then + echo "Found wget ... using wget" + fi + wget "$jarUrl" -O "$wrapperJarPath" + elif command -v curl > /dev/null; then + if [ "$MVNW_VERBOSE" = true ]; then + echo "Found curl ... using curl" + fi + curl -o "$wrapperJarPath" "$jarUrl" + else + if [ "$MVNW_VERBOSE" = true ]; then + echo "Falling back to using Java to download" + fi + javaClass="$BASE_DIR/.mvn/wrapper/MavenWrapperDownloader.java" + if [ -e "$javaClass" ]; then + if [ ! -e "$BASE_DIR/.mvn/wrapper/MavenWrapperDownloader.class" ]; then + if [ "$MVNW_VERBOSE" = true ]; then + echo " - Compiling MavenWrapperDownloader.java ..." + fi + # Compiling the Java class + ("$JAVA_HOME/bin/javac" "$javaClass") + fi + if [ -e "$BASE_DIR/.mvn/wrapper/MavenWrapperDownloader.class" ]; then + # Running the downloader + if [ "$MVNW_VERBOSE" = true ]; then + echo " - Running MavenWrapperDownloader.java ..." + fi + ("$JAVA_HOME/bin/java" -cp .mvn/wrapper MavenWrapperDownloader "$MAVEN_PROJECTBASEDIR") + fi + fi + fi +fi +########################################################################################## +# End of extension +########################################################################################## + +export MAVEN_PROJECTBASEDIR=${MAVEN_BASEDIR:-"$BASE_DIR"} +if [ "$MVNW_VERBOSE" = true ]; then + echo $MAVEN_PROJECTBASEDIR +fi +MAVEN_OPTS="$(concat_lines "$MAVEN_PROJECTBASEDIR/.mvn/jvm.config") $MAVEN_OPTS" + +# For Cygwin, switch paths to Windows format before running java +if $cygwin; then + [ -n "$M2_HOME" ] && + M2_HOME=`cygpath --path --windows "$M2_HOME"` + [ -n "$JAVA_HOME" ] && + JAVA_HOME=`cygpath --path --windows "$JAVA_HOME"` + [ -n "$CLASSPATH" ] && + CLASSPATH=`cygpath --path --windows "$CLASSPATH"` + [ -n "$MAVEN_PROJECTBASEDIR" ] && + MAVEN_PROJECTBASEDIR=`cygpath --path --windows "$MAVEN_PROJECTBASEDIR"` +fi + +WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain + +exec "$JAVACMD" \ + $MAVEN_OPTS \ + -classpath "$MAVEN_PROJECTBASEDIR/.mvn/wrapper/maven-wrapper.jar" \ + "-Dmaven.home=${M2_HOME}" "-Dmaven.multiModuleProjectDirectory=${MAVEN_PROJECTBASEDIR}" \ + ${WRAPPER_LAUNCHER} $MAVEN_CONFIG "$@" diff --git a/chapter-07/mvnw.cmd b/chapter-07/mvnw.cmd new file mode 100755 index 0000000..48363fa --- /dev/null +++ b/chapter-07/mvnw.cmd @@ -0,0 +1,161 @@ +@REM ---------------------------------------------------------------------------- +@REM Licensed to the Apache Software Foundation (ASF) under one +@REM or more contributor license agreements. See the NOTICE file +@REM distributed with this work for additional information +@REM regarding copyright ownership. The ASF licenses this file +@REM to you under the Apache License, Version 2.0 (the +@REM "License"); you may not use this file except in compliance +@REM with the License. You may obtain a copy of the License at +@REM +@REM http://www.apache.org/licenses/LICENSE-2.0 +@REM +@REM Unless required by applicable law or agreed to in writing, +@REM software distributed under the License is distributed on an +@REM "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +@REM KIND, either express or implied. See the License for the +@REM specific language governing permissions and limitations +@REM under the License. +@REM ---------------------------------------------------------------------------- + +@REM ---------------------------------------------------------------------------- +@REM Maven2 Start Up Batch script +@REM +@REM Required ENV vars: +@REM JAVA_HOME - location of a JDK home dir +@REM +@REM Optional ENV vars +@REM M2_HOME - location of maven2's installed home dir +@REM MAVEN_BATCH_ECHO - set to 'on' to enable the echoing of the batch commands +@REM MAVEN_BATCH_PAUSE - set to 'on' to wait for a key stroke before ending +@REM MAVEN_OPTS - parameters passed to the Java VM when running Maven +@REM e.g. to debug Maven itself, use +@REM set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000 +@REM MAVEN_SKIP_RC - flag to disable loading of mavenrc files +@REM ---------------------------------------------------------------------------- + +@REM Begin all REM lines with '@' in case MAVEN_BATCH_ECHO is 'on' +@echo off +@REM set title of command window +title %0 +@REM enable echoing my setting MAVEN_BATCH_ECHO to 'on' +@if "%MAVEN_BATCH_ECHO%" == "on" echo %MAVEN_BATCH_ECHO% + +@REM set %HOME% to equivalent of $HOME +if "%HOME%" == "" (set "HOME=%HOMEDRIVE%%HOMEPATH%") + +@REM Execute a user defined script before this one +if not "%MAVEN_SKIP_RC%" == "" goto skipRcPre +@REM check for pre script, once with legacy .bat ending and once with .cmd ending +if exist "%HOME%\mavenrc_pre.bat" call "%HOME%\mavenrc_pre.bat" +if exist "%HOME%\mavenrc_pre.cmd" call "%HOME%\mavenrc_pre.cmd" +:skipRcPre + +@setlocal + +set ERROR_CODE=0 + +@REM To isolate internal variables from possible post scripts, we use another setlocal +@setlocal + +@REM ==== START VALIDATION ==== +if not "%JAVA_HOME%" == "" goto OkJHome + +echo. +echo Error: JAVA_HOME not found in your environment. >&2 +echo Please set the JAVA_HOME variable in your environment to match the >&2 +echo location of your Java installation. >&2 +echo. +goto error + +:OkJHome +if exist "%JAVA_HOME%\bin\java.exe" goto init + +echo. +echo Error: JAVA_HOME is set to an invalid directory. >&2 +echo JAVA_HOME = "%JAVA_HOME%" >&2 +echo Please set the JAVA_HOME variable in your environment to match the >&2 +echo location of your Java installation. >&2 +echo. +goto error + +@REM ==== END VALIDATION ==== + +:init + +@REM Find the project base dir, i.e. the directory that contains the folder ".mvn". +@REM Fallback to current working directory if not found. + +set MAVEN_PROJECTBASEDIR=%MAVEN_BASEDIR% +IF NOT "%MAVEN_PROJECTBASEDIR%"=="" goto endDetectBaseDir + +set EXEC_DIR=%CD% +set WDIR=%EXEC_DIR% +:findBaseDir +IF EXIST "%WDIR%"\.mvn goto baseDirFound +cd .. +IF "%WDIR%"=="%CD%" goto baseDirNotFound +set WDIR=%CD% +goto findBaseDir + +:baseDirFound +set MAVEN_PROJECTBASEDIR=%WDIR% +cd "%EXEC_DIR%" +goto endDetectBaseDir + +:baseDirNotFound +set MAVEN_PROJECTBASEDIR=%EXEC_DIR% +cd "%EXEC_DIR%" + +:endDetectBaseDir + +IF NOT EXIST "%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config" goto endReadAdditionalConfig + +@setlocal EnableExtensions EnableDelayedExpansion +for /F "usebackq delims=" %%a in ("%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config") do set JVM_CONFIG_MAVEN_PROPS=!JVM_CONFIG_MAVEN_PROPS! %%a +@endlocal & set JVM_CONFIG_MAVEN_PROPS=%JVM_CONFIG_MAVEN_PROPS% + +:endReadAdditionalConfig + +SET MAVEN_JAVA_EXE="%JAVA_HOME%\bin\java.exe" +set WRAPPER_JAR="%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.jar" +set WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain + +set DOWNLOAD_URL="https://repo.maven.apache.org/maven2/io/takari/maven-wrapper/0.4.2/maven-wrapper-0.4.2.jar" +FOR /F "tokens=1,2 delims==" %%A IN (%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.properties) DO ( + IF "%%A"=="wrapperUrl" SET DOWNLOAD_URL=%%B +) + +@REM Extension to allow automatically downloading the maven-wrapper.jar from Maven-central +@REM This allows using the maven wrapper in projects that prohibit checking in binary data. +if exist %WRAPPER_JAR% ( + echo Found %WRAPPER_JAR% +) else ( + echo Couldn't find %WRAPPER_JAR%, downloading it ... + echo Downloading from: %DOWNLOAD_URL% + powershell -Command "(New-Object Net.WebClient).DownloadFile('%DOWNLOAD_URL%', '%WRAPPER_JAR%')" + echo Finished downloading %WRAPPER_JAR% +) +@REM End of extension + +%MAVEN_JAVA_EXE% %JVM_CONFIG_MAVEN_PROPS% %MAVEN_OPTS% %MAVEN_DEBUG_OPTS% -classpath %WRAPPER_JAR% "-Dmaven.multiModuleProjectDirectory=%MAVEN_PROJECTBASEDIR%" %WRAPPER_LAUNCHER% %MAVEN_CONFIG% %* +if ERRORLEVEL 1 goto error +goto end + +:error +set ERROR_CODE=1 + +:end +@endlocal & set ERROR_CODE=%ERROR_CODE% + +if not "%MAVEN_SKIP_RC%" == "" goto skipRcPost +@REM check for post script, once with legacy .bat ending and once with .cmd ending +if exist "%HOME%\mavenrc_post.bat" call "%HOME%\mavenrc_post.bat" +if exist "%HOME%\mavenrc_post.cmd" call "%HOME%\mavenrc_post.cmd" +:skipRcPost + +@REM pause the script if MAVEN_BATCH_PAUSE is set to 'on' +if "%MAVEN_BATCH_PAUSE%" == "on" pause + +if "%MAVEN_TERMINATE_CMD%" == "on" exit %ERROR_CODE% + +exit /B %ERROR_CODE% diff --git a/chapter-07/pom.xml b/chapter-07/pom.xml new file mode 100644 index 0000000..fdacff6 --- /dev/null +++ b/chapter-07/pom.xml @@ -0,0 +1,78 @@ + + + 4.0.0 + + com.packt.distributed-tracing-chapter-07 + parent + 0.0.1-SNAPSHOT + pom + + Tracing with Service Mesh + Chapter 7 - Tracing with Service Mesh + + + exercise1 + + + + org.springframework.boot + spring-boot-starter-parent + 2.0.4.RELEASE + + + + + + UTF-8 + UTF-8 + 1.8 + + + + + + org.springframework.boot + spring-boot-starter-web + 2.0.4.RELEASE + + + io.opentracing + opentracing-api + 0.31.0 + + + io.opentracing.contrib + opentracing-spring-cloud-starter + 0.1.13 + + + io.opentracing.contrib + opentracing-spring-tracer-configuration-starter + 0.1.0 + + + io.jaegertracing + jaeger-client + 0.31.0 + + + io.jaegertracing + jaeger-zipkin + 0.31.0 + + + + + + + + + org.springframework.boot + spring-boot-maven-plugin + + + + + + diff --git a/chapter-07/routing.yml b/chapter-07/routing.yml new file mode 100644 index 0000000..53b5238 --- /dev/null +++ b/chapter-07/routing.yml @@ -0,0 +1,34 @@ +apiVersion: networking.istio.io/v1alpha3 +kind: VirtualService +metadata: + name: formatter-virtual-svc +spec: + hosts: + - formatter-svc + http: + - match: + - headers: + baggage-user-agent: + regex: .*Netscape.* + route: + - destination: + host: formatter-svc + subset: v2 + - route: + - destination: + host: formatter-svc + subset: v1 +--- +apiVersion: networking.istio.io/v1alpha3 +kind: DestinationRule +metadata: + name: formatter-svc-destination +spec: + host: formatter-svc + subsets: + - name: v1 + labels: + version: v1 + - name: v2 + labels: + version: v2