├── .gitignore ├── test ├── workflow │ ├── go.mod │ ├── README.md │ ├── go.sum │ ├── cache │ │ └── services.go │ └── nosqldb │ │ └── services.go ├── wiring │ ├── README.md │ ├── go.sum │ ├── go.mod │ ├── simplenosqldb_test.go │ └── simplecache_test.go └── README.md ├── examples ├── sockshop │ ├── tests │ │ ├── docs.go │ │ ├── go.mod │ │ └── paymentservice_test.go │ ├── workflow │ │ ├── README.md │ │ ├── go.mod │ │ ├── queuemaster │ │ │ ├── README.md │ │ │ └── queuemaster_test.go │ │ └── payment │ │ │ ├── paymentservice.go │ │ │ └── README.md │ ├── workload │ │ ├── README.md │ │ ├── go.mod │ │ └── workloadgen │ │ │ ├── README.md │ │ │ └── workload.go │ └── wiring │ │ ├── README.md │ │ └── main.go ├── train_ticket │ ├── workflow │ │ ├── news │ │ │ ├── data.go │ │ │ ├── newsService.go │ │ │ └── README.md │ │ ├── station │ │ │ └── data.go │ │ ├── config │ │ │ └── data.go │ │ ├── delivery │ │ │ ├── data.go │ │ │ └── deliveryService.go │ │ ├── go.mod │ │ ├── price │ │ │ └── data.go │ │ ├── trainfood │ │ │ └── data.go │ │ ├── user │ │ │ └── data.go │ │ ├── consignprice │ │ │ └── data.go │ │ ├── payment │ │ │ └── data.go │ │ ├── train │ │ │ └── data.go │ │ ├── food │ │ │ ├── data.go │ │ │ └── README.md │ │ ├── contacts │ │ │ └── data.go │ │ ├── route │ │ │ └── data.go │ │ ├── stationfood │ │ │ └── data.go │ │ └── assurance │ │ │ └── data.go │ ├── tests │ │ ├── doc.go │ │ ├── foodservice_test.go │ │ └── go.mod │ └── wiring │ │ ├── README.md │ │ ├── specs │ │ └── README.md │ │ ├── main.go │ │ └── go.mod ├── dsb_hotel │ ├── workflow │ │ ├── README.md │ │ ├── go.mod │ │ └── hotelreservation │ │ │ ├── SearchService.go │ │ │ └── DataModels.go │ ├── tests │ │ ├── doc.go │ │ ├── go.mod │ │ ├── geoservice_test.go │ │ ├── searchservice_test.go │ │ ├── rateservice_test.go │ │ ├── userservice_test.go │ │ ├── recommendationservice_test.go │ │ ├── profileservice_test.go │ │ └── reservationservice_test.go │ ├── cmplx_workload │ │ ├── README.md │ │ └── go.mod │ ├── wiring │ │ ├── README.md │ │ ├── specs │ │ │ ├── original.env │ │ │ └── README.md │ │ └── main.go │ └── workload │ │ ├── go.mod │ │ └── workloadgen │ │ └── workload.go ├── dsb_sn │ ├── tests │ │ ├── doc.go │ │ ├── uniqueidservice_test.go │ │ ├── mediaservice_test.go │ │ ├── go.mod │ │ ├── urlshortenservice_test.go │ │ └── useridservice_test.go │ ├── wiring │ │ ├── README.md │ │ ├── main.go │ │ ├── specs │ │ │ └── README.md │ │ └── go.mod │ └── workflow │ │ ├── go.mod │ │ └── socialnetwork │ │ ├── MediaService.go │ │ ├── DataModels.go │ │ ├── UserIdService.go │ │ ├── UniqueIdService.go │ │ └── common.go ├── leaf │ ├── workflow │ │ ├── README.md │ │ ├── go.mod │ │ └── leaf │ │ │ └── nonleafservice.go │ ├── README.md │ └── wiring │ │ ├── README.md │ │ ├── main.go │ │ └── specs │ │ ├── thrift.go │ │ ├── govector.go │ │ └── http.go └── README.md ├── scripts ├── README.md ├── cleanup_autodocs.sh ├── gen_docs.sh ├── gen_docs.py └── gen_plugins.py ├── blueprint ├── pkg │ ├── blueprint │ │ ├── docs.go │ │ ├── errors.go │ │ ├── README.md │ │ ├── stringutil │ │ │ ├── name.go │ │ │ └── indent.go │ │ └── ioutil │ │ │ ├── README.md │ │ │ └── ioutil.go │ ├── coreplugins │ │ ├── service │ │ │ ├── service.go │ │ │ ├── ir.go │ │ │ └── README.md │ │ └── backend │ │ │ ├── ir.go │ │ │ └── README.md │ ├── ir │ │ └── buildcontext.go │ └── wiring │ │ └── reflect.go ├── go.mod ├── README.md ├── go.sum └── LICENSE ├── runtime ├── core │ ├── workload │ │ └── stats.go │ ├── registry │ │ └── registry_test.go │ └── backend │ │ ├── docs.go │ │ ├── queue.go │ │ ├── cache.go │ │ ├── metric.go │ │ ├── trace.go │ │ ├── reflect.go │ │ └── reldb.go ├── plugins │ ├── govector │ │ └── iface.go │ ├── opentelemetry │ │ ├── trace.go │ │ └── metric.go │ ├── zipkin │ │ ├── trace.go │ │ └── README.md │ ├── jaeger │ │ ├── trace.go │ │ └── README.md │ ├── xtrace │ │ └── xtrace_iface.go │ ├── slogger │ │ └── log.go │ ├── simplequeue │ │ └── queue.go │ ├── simplecache │ │ └── cache_test.go │ ├── sqlitereldb │ │ └── reldb.go │ ├── rabbitmq │ │ └── README.md │ └── mysql │ │ └── reldb.go ├── doc.go ├── README.md └── LICENSE ├── docs ├── manual │ ├── running.md │ ├── glossary.md │ ├── gettingstarted.md │ ├── README.md │ └── requirements.md └── dev │ ├── api.md │ └── autodoc.md ├── plugins ├── opentelemetry │ └── ir_collector_client.go ├── golang │ └── gogen │ │ └── doc.go ├── README.md ├── dockercompose │ ├── ir.go │ ├── defaults.go │ └── dockergen │ │ └── template.go ├── linux │ └── util.go ├── goproc │ ├── goprocgen │ │ └── README.md │ ├── linuxgen │ │ ├── dockerfile_buildcommands.go │ │ ├── goproc_runfunc.go │ │ └── README.md │ ├── ir_goproc.go │ ├── defaults.go │ └── log.go ├── linuxcontainer │ ├── ir.go │ ├── defaults.go │ ├── dockergen │ │ └── README.md │ └── linuxgen │ │ ├── template.go │ │ └── runfunc.go ├── http │ └── httpcodegen │ │ └── README.md ├── LICENSE ├── govector │ └── example_logs │ │ └── README.md ├── workload │ └── ir.go ├── circuitbreaker │ └── wiring.go ├── timeouts │ └── wiring.go └── latency │ └── codegen.go ├── go.work └── LICENSE /.gitignore: -------------------------------------------------------------------------------- 1 | .vs 2 | build -------------------------------------------------------------------------------- /test/workflow/go.mod: -------------------------------------------------------------------------------- 1 | module github.com/blueprint-uservices/blueprint/test/workflow 2 | 3 | go 1.20 -------------------------------------------------------------------------------- /examples/sockshop/tests/docs.go: -------------------------------------------------------------------------------- 1 | // Package tests implements SockShop black-box unit tests. 2 | package tests -------------------------------------------------------------------------------- /scripts/README.md: -------------------------------------------------------------------------------- 1 | # Scripts 2 | 3 | Scripts used for project maintenance; not intended for use by users. -------------------------------------------------------------------------------- /test/wiring/README.md: -------------------------------------------------------------------------------- 1 | Wiring spec tests for different Blueprint plugins. Each plugin's tests reside in a file called [plugin]_test.go -------------------------------------------------------------------------------- /blueprint/pkg/blueprint/docs.go: -------------------------------------------------------------------------------- 1 | // Package blueprint provides some common utility methods for logging, io, errors and string manipulation. 2 | package blueprint 3 | -------------------------------------------------------------------------------- /examples/train_ticket/workflow/news/data.go: -------------------------------------------------------------------------------- 1 | package news 2 | 3 | type News struct { 4 | Title string `bson:"Title"` 5 | Content string `bson:"Content"` 6 | } 7 | -------------------------------------------------------------------------------- /examples/train_ticket/workflow/station/data.go: -------------------------------------------------------------------------------- 1 | package station 2 | 3 | type Station struct { 4 | ID string 5 | Name string 6 | StayTime int64 7 | } 8 | -------------------------------------------------------------------------------- /examples/train_ticket/workflow/config/data.go: -------------------------------------------------------------------------------- 1 | package config 2 | 3 | type Config struct { 4 | Name string 5 | Value string 6 | Description string 7 | } 8 | -------------------------------------------------------------------------------- /runtime/core/workload/stats.go: -------------------------------------------------------------------------------- 1 | package workload 2 | 3 | // Stat captures basic statistics about a request 4 | type Stat struct { 5 | Start int64 6 | Duration int64 7 | IsError bool 8 | } 9 | -------------------------------------------------------------------------------- /scripts/cleanup_autodocs.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | HOME_DIR=$PWD 3 | 4 | # Remove all the old docs 5 | mkdir -p $HOME_DIR/docs/api 6 | cd $HOME_DIR/docs/api/ 7 | rm *.md 8 | cd $HOME_DIR 9 | 10 | -------------------------------------------------------------------------------- /test/workflow/README.md: -------------------------------------------------------------------------------- 1 | Workflow specs used for testing different plugins. Each plugin may want to define its own workflow specs, which should live in different subfolders; or they may reuse common ones. -------------------------------------------------------------------------------- /blueprint/go.mod: -------------------------------------------------------------------------------- 1 | module github.com/blueprint-uservices/blueprint/blueprint 2 | 3 | go 1.20 4 | 5 | require ( 6 | golang.org/x/exp v0.0.0-20240416160154-fe59bbe5cc7f 7 | golang.org/x/mod v0.17.0 8 | ) 9 | -------------------------------------------------------------------------------- /examples/sockshop/workflow/README.md: -------------------------------------------------------------------------------- 1 | # SockShop Workflow Spec 2 | 3 | This contains the workflow spec implementation for the SockShop microservices demo. 4 | 5 | Each service is implemented in its own package. -------------------------------------------------------------------------------- /examples/dsb_hotel/workflow/README.md: -------------------------------------------------------------------------------- 1 | # DeathStarBench HotelReservation - Workflow Spec 2 | 3 | This contains the workflow spec for the HotelReservation application from the DeathStarBench microservices suite. 4 | -------------------------------------------------------------------------------- /examples/sockshop/workload/README.md: -------------------------------------------------------------------------------- 1 | # SockShop Workload Generator 2 | 3 | This contains a workload generator implementation for the SockShop microservices demo. 4 | 5 | It is used in conjunction with the workload plugin. -------------------------------------------------------------------------------- /examples/train_ticket/tests/doc.go: -------------------------------------------------------------------------------- 1 | // Package tests implements a number of tests for the Train Ticket services. 2 | // 3 | // These tests can be run standalone and are also compiled into the application. 4 | package tests 5 | -------------------------------------------------------------------------------- /test/wiring/go.sum: -------------------------------------------------------------------------------- 1 | golang.org/x/exp v0.0.0-20230728194245-b0cb94b80691 h1:/yRP+0AN7mf5DkD3BAI6TOFnd51gEoDEb8o35jIFtgw= 2 | golang.org/x/exp v0.0.0-20230728194245-b0cb94b80691/go.mod h1:FXUEEKJgO7OQYeo8N01OfiKP8RXMtf6e8aTskBGqWdc= 3 | -------------------------------------------------------------------------------- /examples/dsb_sn/tests/doc.go: -------------------------------------------------------------------------------- 1 | // Package tests implements a number of tests for the DeathStarBench SocialNetwork services. 2 | // 3 | // These tests can be run standalone and are also compiled into the application. 4 | package tests 5 | -------------------------------------------------------------------------------- /examples/train_ticket/workflow/delivery/data.go: -------------------------------------------------------------------------------- 1 | package delivery 2 | 3 | type Delivery struct { 4 | ID string 5 | OrderID string 6 | FoodName string 7 | StoreName string 8 | StationName string 9 | } 10 | -------------------------------------------------------------------------------- /test/workflow/go.sum: -------------------------------------------------------------------------------- 1 | golang.org/x/exp v0.0.0-20230728194245-b0cb94b80691 h1:/yRP+0AN7mf5DkD3BAI6TOFnd51gEoDEb8o35jIFtgw= 2 | golang.org/x/exp v0.0.0-20230728194245-b0cb94b80691/go.mod h1:FXUEEKJgO7OQYeo8N01OfiKP8RXMtf6e8aTskBGqWdc= 3 | -------------------------------------------------------------------------------- /examples/dsb_hotel/tests/doc.go: -------------------------------------------------------------------------------- 1 | // Package tests implements a number of tests for the DeathStarBench HotelReservation services. 2 | // 3 | // These tests can be run standalone and are also compiled into the application. 4 | package tests 5 | -------------------------------------------------------------------------------- /examples/leaf/workflow/README.md: -------------------------------------------------------------------------------- 1 | # Leaf Application - Workflow Spec 2 | 3 | This contains the workflow spec for the simple "leaf" application. 4 | 5 | The workflow spec defines two services, called Leaf and NonLeaf. NonLeaf makes RPC calls to Leaf. -------------------------------------------------------------------------------- /blueprint/README.md: -------------------------------------------------------------------------------- 1 | # Blueprint 2 | 3 | This go module implements Blueprint's compiler as well as the [WiringSpec API](pkg/wiring) used by Blueprint applications. 4 | 5 | For documentation on usage, consult the [Blueprint User Guide](../docs/manual). 6 | -------------------------------------------------------------------------------- /test/wiring/go.mod: -------------------------------------------------------------------------------- 1 | module github.com/blueprint-uservices/blueprint/test/wiring 2 | 3 | go 1.20 4 | 5 | require github.com/blueprint-uservices/blueprint/test/workflow v0.0.0 6 | 7 | replace github.com/blueprint-uservices/blueprint/test/workflow => ../workflow -------------------------------------------------------------------------------- /examples/train_ticket/workflow/go.mod: -------------------------------------------------------------------------------- 1 | module github.com/blueprint-uservices/blueprint/examples/train_ticket/workflow 2 | 3 | go 1.21 4 | 5 | toolchain go1.22.1 6 | 7 | require github.com/blueprint-uservices/blueprint/runtime v0.0.0-20240619221802-d064c5861c1e 8 | -------------------------------------------------------------------------------- /examples/train_ticket/workflow/price/data.go: -------------------------------------------------------------------------------- 1 | package price 2 | 3 | type PriceConfig struct { 4 | ID string 5 | TrainType string 6 | RouteID string 7 | BasicPriceRate float64 8 | FirstClassPriceRate float64 9 | } 10 | -------------------------------------------------------------------------------- /examples/train_ticket/workflow/trainfood/data.go: -------------------------------------------------------------------------------- 1 | package trainfood 2 | 3 | import "github.com/blueprint-uservices/blueprint/examples/train_ticket/workflow/food" 4 | 5 | type TrainFood struct { 6 | ID string 7 | TripID string 8 | Foods []food.Food 9 | } 10 | -------------------------------------------------------------------------------- /runtime/core/registry/registry_test.go: -------------------------------------------------------------------------------- 1 | package registry 2 | 3 | import ( 4 | "testing" 5 | ) 6 | 7 | type mystruct struct{} 8 | 9 | var reg = NewServiceRegistry[mystruct]("test") 10 | 11 | func TestNothing(t *testing.T) { 12 | reg.SetDefault("hi") 13 | } 14 | -------------------------------------------------------------------------------- /examples/train_ticket/workflow/user/data.go: -------------------------------------------------------------------------------- 1 | package user 2 | 3 | type User struct { 4 | UserID string 5 | Username string 6 | Password string 7 | Gender int64 8 | DocumentType int64 9 | DocumentNum string 10 | Email string 11 | } 12 | -------------------------------------------------------------------------------- /examples/train_ticket/workflow/consignprice/data.go: -------------------------------------------------------------------------------- 1 | package consignprice 2 | 3 | type ConsignPrice struct { 4 | ID string 5 | Index int64 6 | InitialWeight float64 7 | InitialPrice float64 8 | WithinPrice float64 9 | BeyondPrice float64 10 | } 11 | -------------------------------------------------------------------------------- /examples/train_ticket/workflow/payment/data.go: -------------------------------------------------------------------------------- 1 | package payment 2 | 3 | type Payment struct { 4 | ID string 5 | OrderID string 6 | UserID string 7 | Price string 8 | } 9 | 10 | type Money struct { 11 | ID string 12 | UserID string 13 | Price string 14 | } 15 | -------------------------------------------------------------------------------- /examples/dsb_hotel/cmplx_workload/README.md: -------------------------------------------------------------------------------- 1 | # Realistic Workload 2 | 3 | This example shows how to use the [workload generator](https://github.com/Blueprint-uServices/blueprint/tree/main/runtime/core/workload) for generating and executing a workload against the generated HotelReservation example. -------------------------------------------------------------------------------- /docs/manual/running.md: -------------------------------------------------------------------------------- 1 | # Running a Blueprint Application 2 | 3 | TODO 4 | 5 | This doc describes the process after compiling an application, explaining how to run it. 6 | 7 | Different plugins will need to be run differently; we can go over the core ones here. 8 | 9 | Major thing to cover is environment variables. -------------------------------------------------------------------------------- /examples/train_ticket/workflow/train/data.go: -------------------------------------------------------------------------------- 1 | // package train implements ts-train-service from the original TrainTicket application 2 | package train 3 | 4 | type TrainType struct { 5 | ID string 6 | Name string 7 | EconomyClass int64 8 | ComfortClass int64 9 | AvgSpeed int64 10 | } 11 | -------------------------------------------------------------------------------- /examples/train_ticket/workflow/food/data.go: -------------------------------------------------------------------------------- 1 | package food 2 | 3 | type Food struct { 4 | Name string 5 | Price float64 6 | } 7 | 8 | type FoodOrder struct { 9 | ID string 10 | OrderID string 11 | FoodType int64 12 | StationName string 13 | StoreName string 14 | FoodName string 15 | Price float64 16 | } 17 | -------------------------------------------------------------------------------- /docs/manual/glossary.md: -------------------------------------------------------------------------------- 1 | # Glossary of Terms 2 | 3 | 4 | 📓Workflow Spec\ 5 | 📝Wiring Spec\ 6 | ✏️Plugin\ 7 | 🔧Compiling\ 8 | 🏁Compiled Application 9 | 10 | 11 | workflow 12 | wiring 13 | service 14 | modifier 15 | backend 16 | backend interface 17 | 18 | 19 | 20 | container-level 21 | process-level 22 | application-level 23 | 24 | 25 | 26 | namespace -------------------------------------------------------------------------------- /examples/train_ticket/workflow/contacts/data.go: -------------------------------------------------------------------------------- 1 | package contacts 2 | 3 | // DocumentType enum 4 | const ( 5 | NULL int64 = iota 6 | ID_CARD 7 | PASSPORT 8 | OTHER 9 | ) 10 | 11 | type Contact struct { 12 | ID string 13 | AccountID string 14 | Name string 15 | DocumentType int 16 | DocumentNumber string 17 | PhoneNumber string 18 | } 19 | -------------------------------------------------------------------------------- /test/README.md: -------------------------------------------------------------------------------- 1 | # Test 2 | 3 | Contains wiring specs to test plugins produce the correct IR 4 | 5 | Tests generally contain a workflow spec and a wiring spec, which is why they reside here instead of with plugin logic (for now) 6 | 7 | Tests for wiring specs live in the 'wiring' folder. Workflow specs used by tests live in workflow. 8 | 9 | ``` 10 | cd wiring 11 | go test 12 | ``` -------------------------------------------------------------------------------- /examples/train_ticket/workflow/route/data.go: -------------------------------------------------------------------------------- 1 | package route 2 | 3 | type Route struct { 4 | ID string 5 | Stations []string 6 | Distances []int64 7 | StartStation string 8 | EndStation string 9 | } 10 | 11 | type RouteInfo struct { 12 | ID string 13 | StartStation string 14 | EndStation string 15 | StationList string 16 | DistanceList string 17 | } 18 | -------------------------------------------------------------------------------- /examples/leaf/README.md: -------------------------------------------------------------------------------- 1 | # leaf application 2 | 3 | Leaf is a simple application that is not representative of a real application. Its intended use is to provide examples of Blueprint API usage; it is frequently referenced by plugin documentation. 4 | 5 | For a more realistic application to run, look at the [sockshop](../sockshop) or [dsb_sn](../dsb_sn), or other applications listed in the [examples](..) directory. -------------------------------------------------------------------------------- /examples/train_ticket/workflow/stationfood/data.go: -------------------------------------------------------------------------------- 1 | package stationfood 2 | 3 | import ( 4 | "github.com/blueprint-uservices/blueprint/examples/train_ticket/workflow/food" 5 | ) 6 | 7 | type StationFoodStore struct { 8 | ID string 9 | StationName string 10 | StoreName string 11 | Telephone string 12 | BusinessTime string 13 | DeliveryFee float64 14 | Foods []food.Food 15 | } 16 | -------------------------------------------------------------------------------- /plugins/opentelemetry/ir_collector_client.go: -------------------------------------------------------------------------------- 1 | package opentelemetry 2 | 3 | import ( 4 | "github.com/blueprint-uservices/blueprint/plugins/golang" 5 | ) 6 | 7 | // Interface that indicates if an IRNode implements the OTCollector interface 8 | // All custom collector clients **must** implement this interface 9 | type OpenTelemetryCollectorInterface interface { 10 | golang.Node 11 | golang.Instantiable 12 | ImplementsOTCollectorClient() 13 | } 14 | -------------------------------------------------------------------------------- /blueprint/pkg/coreplugins/service/service.go: -------------------------------------------------------------------------------- 1 | package service 2 | 3 | // General representation of a service 4 | 5 | type ( 6 | ServiceInterface interface { 7 | GetName() string 8 | GetMethods() []Method 9 | } 10 | 11 | Method interface { 12 | GetName() string 13 | GetArguments() []Variable 14 | GetReturns() []Variable 15 | } 16 | 17 | Variable interface { 18 | GetName() string 19 | GetType() string // a "well-known" type 20 | } 21 | ) 22 | -------------------------------------------------------------------------------- /scripts/gen_docs.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # Depends on godoc2markdown being installed. 4 | # Installation instructions: https://git.sr.ht/~humaid/godoc2markdown 5 | 6 | HOME_DIR=$PWD 7 | dir=$1 8 | 9 | cd $dir 10 | title=$(echo "$dir" | tr '/' '_') 11 | outfile=$HOME_DIR/docs/api/$title.md 12 | echo "---" > $outfile 13 | echo "title: $dir" >> $outfile 14 | echo "---" >> $outfile 15 | echo "# $dir" >> $outfile 16 | go doc -all | godoc2markdown >> $outfile 17 | cd $HOME_DIR 18 | -------------------------------------------------------------------------------- /examples/train_ticket/wiring/README.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | # wiring 4 | 5 | ```go 6 | import "github.com/blueprint-uservices/blueprint/examples/train_ticket/wiring" 7 | ``` 8 | 9 | Package main provides an application for compiling different wiring specs for TrainTicket application. 10 | 11 | To display options and usage, invoke: 12 | 13 | ``` 14 | go run main.go -h 15 | ``` 16 | 17 | ## Index 18 | 19 | 20 | 21 | Generated by [gomarkdoc]() 22 | -------------------------------------------------------------------------------- /examples/dsb_sn/wiring/README.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | # wiring 4 | 5 | ```go 6 | import "github.com/blueprint-uservices/blueprint/examples/dsb_sn/wiring" 7 | ``` 8 | 9 | Package main provides an application for compiling different wiring specs for DeathStarBench SocialNetwork application. 10 | 11 | To display options and usage, invoke: 12 | 13 | ``` 14 | go run main.go -h 15 | ``` 16 | 17 | ## Index 18 | 19 | 20 | 21 | Generated by [gomarkdoc]() 22 | -------------------------------------------------------------------------------- /examples/leaf/workflow/go.mod: -------------------------------------------------------------------------------- 1 | module github.com/blueprint-uservices/blueprint/examples/leaf/workflow 2 | 3 | go 1.21 4 | 5 | toolchain go1.22.1 6 | 7 | require ( 8 | github.com/blueprint-uservices/blueprint/runtime v0.0.0-20240619221802-d064c5861c1e 9 | go.mongodb.org/mongo-driver v1.15.0 10 | go.opentelemetry.io/otel/metric v1.26.0 11 | ) 12 | 13 | require ( 14 | go.opentelemetry.io/otel v1.26.0 // indirect 15 | go.opentelemetry.io/otel/trace v1.26.0 // indirect 16 | golang.org/x/exp v0.0.0-20240416160154-fe59bbe5cc7f // indirect 17 | ) 18 | -------------------------------------------------------------------------------- /blueprint/pkg/coreplugins/service/ir.go: -------------------------------------------------------------------------------- 1 | package service 2 | 3 | import "github.com/blueprint-uservices/blueprint/blueprint/pkg/ir" 4 | 5 | /* 6 | Interface for IRNodes that are Call-Response Services 7 | 8 | At build time, services need to be able to provide information about the interface that they implement 9 | */ 10 | 11 | // Any IR node that represents a callable service should implement this interface. 12 | type ServiceNode interface { 13 | 14 | // Returns the interface of this service 15 | GetInterface(ctx ir.BuildContext) (ServiceInterface, error) 16 | } 17 | -------------------------------------------------------------------------------- /examples/dsb_hotel/wiring/README.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | # wiring 4 | 5 | ```go 6 | import "github.com/blueprint-uservices/blueprint/examples/dsb_hotel/wiring" 7 | ``` 8 | 9 | Package main provides an application for compiling a number of different wiring specs for the Hotel Reservation application from the DeathStarBench suite. 10 | 11 | To display options and usage, invoke: 12 | 13 | ``` 14 | go run main.go -h 15 | ``` 16 | 17 | ## Index 18 | 19 | 20 | 21 | Generated by [gomarkdoc]() 22 | -------------------------------------------------------------------------------- /examples/sockshop/wiring/README.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | # wiring 4 | 5 | ```go 6 | import "github.com/blueprint-uservices/blueprint/examples/sockshop/wiring" 7 | ``` 8 | 9 | An application for compiling the SockShop application. Provides a number of different wiring specs for compiling the application in different configurations. 10 | 11 | To display options and usage, invoke: 12 | 13 | ``` 14 | go run main.go -h 15 | ``` 16 | 17 | ## Index 18 | 19 | 20 | 21 | Generated by [gomarkdoc]() 22 | -------------------------------------------------------------------------------- /runtime/plugins/govector/iface.go: -------------------------------------------------------------------------------- 1 | package govector 2 | 3 | import ( 4 | "context" 5 | ) 6 | 7 | // Represents the GoVector logger interface exposed to applications and used by the GoVector plugin 8 | type GoVector interface { 9 | // Gets the govector context (the vector clock) as a bytes array that will be sent from one process to another 10 | GetSendCtx(ctx context.Context, msg string) ([]byte, error) 11 | // Unpacks the received context `bytes` and merges the context into the process' current vector clock 12 | UnpackReceiveCtx(ctx context.Context, msg string, bytes []byte) error 13 | } 14 | -------------------------------------------------------------------------------- /examples/dsb_sn/workflow/go.mod: -------------------------------------------------------------------------------- 1 | module github.com/blueprint-uservices/blueprint/examples/dsb_sn/workflow 2 | 3 | go 1.21 4 | 5 | toolchain go1.22.1 6 | 7 | require ( 8 | github.com/blueprint-uservices/blueprint/runtime v0.0.0-20240619221802-d064c5861c1e 9 | github.com/dgrijalva/jwt-go v3.2.0+incompatible 10 | go.mongodb.org/mongo-driver v1.15.0 11 | ) 12 | 13 | require ( 14 | go.opentelemetry.io/otel v1.26.0 // indirect 15 | go.opentelemetry.io/otel/metric v1.26.0 // indirect 16 | go.opentelemetry.io/otel/trace v1.26.0 // indirect 17 | golang.org/x/exp v0.0.0-20240416160154-fe59bbe5cc7f // indirect 18 | ) 19 | -------------------------------------------------------------------------------- /runtime/core/backend/docs.go: -------------------------------------------------------------------------------- 1 | // Package backend provides the interfaces for common backends like caches, queues, databases, etc. that are often 2 | // used by application workflow specs. 3 | // 4 | // Workflow services can, and should, make use of the interfaces defined in this package. 5 | // 6 | // To use a backend, an application's workflow should require this module and import the interfaces from this package. 7 | // Service constructors should receive the backend interface as an argument, e.g. 8 | // 9 | // func NewMyService(ctx context.Context, db backend.NoSQLDB) (MyService, error) {...} 10 | package backend 11 | -------------------------------------------------------------------------------- /blueprint/pkg/coreplugins/backend/ir.go: -------------------------------------------------------------------------------- 1 | // Package backend provides IR node interfaces for common backend components. 2 | package backend 3 | 4 | import ( 5 | "github.com/blueprint-uservices/blueprint/blueprint/pkg/coreplugins/service" 6 | "github.com/blueprint-uservices/blueprint/blueprint/pkg/ir" 7 | ) 8 | 9 | type ( 10 | Cache interface { 11 | ir.IRNode 12 | service.ServiceNode 13 | } 14 | 15 | NoSQLDB interface { 16 | ir.IRNode 17 | service.ServiceNode 18 | } 19 | 20 | Queue interface { 21 | ir.IRNode 22 | service.ServiceNode 23 | } 24 | 25 | RelDB interface { 26 | ir.IRNode 27 | service.ServiceNode 28 | } 29 | ) 30 | -------------------------------------------------------------------------------- /examples/dsb_hotel/wiring/specs/original.env: -------------------------------------------------------------------------------- 1 | FRONTEND_SERVICE_HTTP_BIND_ADDR=9000 2 | GEO_DB_BIND_ADDR=27018 3 | GEO_SERVICE_GRPC_BIND_ADDR=9001 4 | JAEGER_BIND_ADDR=14268 5 | PROFILE_CACHE_BIND_ADDR=11212 6 | PROFILE_DB_BIND_ADDR=27019 7 | PROFILE_SERVICE_GRPC_BIND_ADDR=9002 8 | RATE_CACHE_BIND_ADDR=11213 9 | RATE_DB_BIND_ADDR=27020 10 | RATE_SERVICE_GRPC_BIND_ADDR=9003 11 | RECOMD_DB_BIND_ADDR=27021 12 | RECOMD_SERVICE_GRPC_BIND_ADDR=9004 13 | RESERV_DB_BIND_ADDR=27022 14 | RESERV_CACHE_BIND_ADDR=11214 15 | RESERV_SERVICE_GRPC_BIND_ADDR=9005 16 | SEARCH_SERVICE_GRPC_BIND_ADDR=9006 17 | USER_DB_BIND_ADDR=27023 18 | USER_SERVICE_GRPC_BIND_ADDR=9007 19 | -------------------------------------------------------------------------------- /examples/dsb_hotel/workflow/go.mod: -------------------------------------------------------------------------------- 1 | module github.com/blueprint-uservices/blueprint/examples/dsb_hotel/workflow 2 | 3 | go 1.21 4 | 5 | toolchain go1.22.1 6 | 7 | require github.com/hailocab/go-geoindex v0.0.0-20160127134810-64631bfe9711 8 | 9 | require ( 10 | github.com/blueprint-uservices/blueprint/runtime v0.0.0-20240619221802-d064c5861c1e 11 | go.mongodb.org/mongo-driver v1.15.0 12 | ) 13 | 14 | require ( 15 | go.opentelemetry.io/otel v1.26.0 // indirect 16 | go.opentelemetry.io/otel/metric v1.26.0 // indirect 17 | go.opentelemetry.io/otel/trace v1.26.0 // indirect 18 | golang.org/x/exp v0.0.0-20240416160154-fe59bbe5cc7f // indirect 19 | ) 20 | -------------------------------------------------------------------------------- /go.work: -------------------------------------------------------------------------------- 1 | go 1.22.0 2 | 3 | toolchain go1.22.1 4 | 5 | use ( 6 | ./blueprint 7 | ./examples/dsb_hotel/tests 8 | ./examples/dsb_hotel/wiring 9 | ./examples/dsb_hotel/workflow 10 | ./examples/dsb_hotel/workload 11 | ./examples/dsb_sn/tests 12 | ./examples/dsb_sn/wiring 13 | ./examples/dsb_sn/workflow 14 | ./examples/leaf/wiring 15 | ./examples/leaf/workflow 16 | ./examples/sockshop/tests 17 | ./examples/sockshop/wiring 18 | ./examples/sockshop/workflow 19 | ./examples/sockshop/workload 20 | ./examples/train_ticket/tests 21 | ./examples/train_ticket/wiring 22 | ./examples/train_ticket/workflow 23 | ./plugins 24 | ./runtime 25 | ./test/wiring 26 | ./test/workflow 27 | ) 28 | -------------------------------------------------------------------------------- /examples/train_ticket/tests/foodservice_test.go: -------------------------------------------------------------------------------- 1 | package tests 2 | 3 | import ( 4 | "fmt" 5 | "testing" 6 | 7 | "github.com/blueprint-uservices/blueprint/examples/train_ticket/workflow/food" 8 | "github.com/stretchr/testify/require" 9 | ) 10 | 11 | func genTestFoodData() []food.Food { 12 | res := []food.Food{} 13 | for i := 0; i < 10; i++ { 14 | f := food.Food{ 15 | Name: fmt.Sprintf("Food%d", i), 16 | Price: float64(100*i + 100), 17 | } 18 | res = append(res, f) 19 | } 20 | return res 21 | } 22 | 23 | func requireFood(t *testing.T, expected food.Food, actual food.Food) { 24 | require.Equal(t, expected.Name, actual.Name) 25 | require.Equal(t, expected.Price, actual.Price) 26 | } 27 | -------------------------------------------------------------------------------- /plugins/golang/gogen/doc.go: -------------------------------------------------------------------------------- 1 | // Package gogen provides implementations of the builder interfaces defined by the [golang] plugin 2 | // 3 | // The builders are intended for use by plugins that define golang namespace nodes. For example, the 4 | // [goproc] plugin defines a process node that collects together golang instance nodes. The [goproc] 5 | // plugin then uses the builders defined here to accumulate the code declarations of those golang instances, 6 | // and to generate the main file for the process. 7 | // 8 | // [golang]: https://github.com/Blueprint-uServices/blueprint/tree/main/plugins/golang 9 | // [goproc]: https://github.com/Blueprint-uServices/blueprint/tree/main/plugins/goproc 10 | package gogen 11 | -------------------------------------------------------------------------------- /blueprint/go.sum: -------------------------------------------------------------------------------- 1 | golang.org/x/exp v0.0.0-20230728194245-b0cb94b80691 h1:/yRP+0AN7mf5DkD3BAI6TOFnd51gEoDEb8o35jIFtgw= 2 | golang.org/x/exp v0.0.0-20230728194245-b0cb94b80691/go.mod h1:FXUEEKJgO7OQYeo8N01OfiKP8RXMtf6e8aTskBGqWdc= 3 | golang.org/x/exp v0.0.0-20240416160154-fe59bbe5cc7f h1:99ci1mjWVBWwJiEKYY6jWa4d2nTQVIEhZIptnrVb1XY= 4 | golang.org/x/exp v0.0.0-20240416160154-fe59bbe5cc7f/go.mod h1:/lliqkxwWAhPjf5oSOIJup2XcqJaw8RGS6k3TGEc7GI= 5 | golang.org/x/mod v0.11.0 h1:bUO06HqtnRcc/7l71XBe4WcqTZ+3AH1J59zWDDwLKgU= 6 | golang.org/x/mod v0.11.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= 7 | golang.org/x/mod v0.17.0 h1:zY54UmvipHiNd+pm+m0x9KhZ9hl1/7QNMyxXbc6ICqA= 8 | golang.org/x/mod v0.17.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= 9 | -------------------------------------------------------------------------------- /examples/README.md: -------------------------------------------------------------------------------- 1 | # Examples 2 | 3 | This directory contains some example Blueprint applications, primarily for use in getting started with Blueprint. 4 | 5 | - [sockshop](sockshop) re-implements the SockShop microservices benchmark 6 | - [dsb_hotel](dsb_hotel) re-implements the hotel-reservation application from the DeathStarBench microservices benchmark 7 | - [dsb_sn](dsb_sn) re-implements the social-network application from the DeathStarBench microservices benchmark 8 | - [train_ticket](train_ticket) re-implements the TrainTicket microservices benchmark 9 | 10 | - [leaf](leaf) is a two-service application where one service calls the other; this is intended to be used to demo Blueprint features and not as a meaningful microservice application. -------------------------------------------------------------------------------- /examples/leaf/wiring/README.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | # wiring 4 | 5 | ```go 6 | import "github.com/blueprint-uservices/blueprint/examples/leaf/wiring" 7 | ``` 8 | 9 | Package main provides the LeafApp application, a simple application designed for demonstrating Blueprint usage and not as a realistic executable application. 10 | 11 | The wiring specs in the \[specs\] directory illustrate the usage of various Blueprint plugins. 12 | 13 | Leaf is also used by Blueprint developers while developing plugins. 14 | 15 | ### Usage 16 | 17 | To display usage, run 18 | 19 | ``` 20 | go run . -h 21 | ``` 22 | 23 | ## Index 24 | 25 | 26 | 27 | Generated by [gomarkdoc]() 28 | -------------------------------------------------------------------------------- /examples/train_ticket/workflow/assurance/data.go: -------------------------------------------------------------------------------- 1 | package assurance 2 | 3 | import ( 4 | "context" 5 | "errors" 6 | "fmt" 7 | ) 8 | 9 | type AssuranceType struct { 10 | Index int64 11 | Name string 12 | Price float64 13 | } 14 | 15 | var TRAFFIC_ACCIDENT = AssuranceType{1, "Traffic Accident Assurance", 3.0} 16 | var ALL_ASSURANCES = []AssuranceType{TRAFFIC_ACCIDENT} 17 | 18 | func getAssuranceType(ctx context.Context, index int64) (AssuranceType, error) { 19 | if index == TRAFFIC_ACCIDENT.Index { 20 | return TRAFFIC_ACCIDENT, nil 21 | } 22 | return AssuranceType{}, errors.New(fmt.Sprintf("Assurance with index %d does not exist", index)) 23 | } 24 | 25 | type Assurance struct { 26 | ID string 27 | OrderID string 28 | AT AssuranceType 29 | } 30 | -------------------------------------------------------------------------------- /docs/manual/gettingstarted.md: -------------------------------------------------------------------------------- 1 | # Getting Started with Blueprint 2 | 3 | ## Prerequisites 4 | 5 | To run the example applications, you will need to install the recommended [prerequisites](requirements.md). 6 | 7 | ## Applications 8 | 9 | Blueprint has a number of out-of-the-box applications, listed in the [examples](../../examples) directory. 10 | 11 | To get started, we will compile and run the SockShop application. Navigate to the [SockShop](../../examples/sockshop) directory and follow the directions there. 12 | 13 | ## Modify SockShop 14 | 15 | The SockShop documentation describes how to compile the `docker` spec. However, other specs exist for SockShop (in the [specs](../../examples/sockshop/wiring/specs/)) directory. These demonstrate different ways of modifying and instantiating services. -------------------------------------------------------------------------------- /examples/sockshop/workflow/go.mod: -------------------------------------------------------------------------------- 1 | module github.com/blueprint-uservices/blueprint/examples/sockshop/workflow 2 | 3 | go 1.21 4 | 5 | toolchain go1.22.1 6 | 7 | require ( 8 | github.com/blueprint-uservices/blueprint/runtime v0.0.0-20240619221802-d064c5861c1e 9 | github.com/google/uuid v1.6.0 10 | github.com/pkg/errors v0.9.1 11 | github.com/stretchr/testify v1.9.0 12 | go.mongodb.org/mongo-driver v1.15.0 13 | golang.org/x/exp v0.0.0-20240416160154-fe59bbe5cc7f 14 | ) 15 | 16 | require ( 17 | github.com/davecgh/go-spew v1.1.1 // indirect 18 | github.com/pmezard/go-difflib v1.0.0 // indirect 19 | go.opentelemetry.io/otel v1.26.0 // indirect 20 | go.opentelemetry.io/otel/metric v1.26.0 // indirect 21 | go.opentelemetry.io/otel/trace v1.26.0 // indirect 22 | gopkg.in/yaml.v3 v3.0.1 // indirect 23 | ) 24 | -------------------------------------------------------------------------------- /examples/dsb_hotel/workload/go.mod: -------------------------------------------------------------------------------- 1 | module github.com/blueprint-uservices/blueprint/examples/dsb_hotel/workload 2 | 3 | go 1.21 4 | 5 | replace github.com/blueprint-uservices/blueprint/examples/dsb_hotel/workflow => ../workflow 6 | 7 | require github.com/blueprint-uservices/blueprint/examples/dsb_hotel/workflow v0.0.0 8 | 9 | require github.com/blueprint-uservices/blueprint/runtime v0.0.0-20240619221802-d064c5861c1e // indirect 10 | 11 | require ( 12 | github.com/hailocab/go-geoindex v0.0.0-20160127134810-64631bfe9711 // indirect 13 | go.mongodb.org/mongo-driver v1.15.0 // indirect 14 | go.opentelemetry.io/otel v1.26.0 // indirect 15 | go.opentelemetry.io/otel/metric v1.26.0 // indirect 16 | go.opentelemetry.io/otel/trace v1.26.0 // indirect 17 | golang.org/x/exp v0.0.0-20240416160154-fe59bbe5cc7f // indirect 18 | ) 19 | -------------------------------------------------------------------------------- /plugins/README.md: -------------------------------------------------------------------------------- 1 | # Plugins 2 | 3 | Much of Blueprint's features and functionality are implemented by plugins contained in this module. 4 | 5 | Each plugin resides in its own subdirectory and provides its own documentation. 6 | 7 | The Blueprint [User Manual](../docs/manual/plugins.md) provides a high-level overview of some of the more prominent plugins. 8 | 9 | ## WiringSpec 10 | 11 | Most plugins make use of a blueprint WiringSpec from the [blueprint/pkg/wiring](../blueprint/pkg/wiring) package. See the documentation for that package or from the Blueprint [User Manual](../docs/manual/) for details on initializing a wiring spec. 12 | 13 | ## Developing Plugins 14 | 15 | If you want to develop your own plugin, it is not mandatory for the plugin to live in this directory. It can reside in a different repository and module. -------------------------------------------------------------------------------- /examples/dsb_hotel/wiring/main.go: -------------------------------------------------------------------------------- 1 | // Package main provides an application for compiling a number of different 2 | // wiring specs for the Hotel Reservation application from the DeathStarBench suite. 3 | // 4 | // To display options and usage, invoke: 5 | // 6 | // go run main.go -h 7 | package main 8 | 9 | import ( 10 | _ "github.com/blueprint-uservices/blueprint/examples/dsb_hotel/tests" 11 | "github.com/blueprint-uservices/blueprint/examples/dsb_hotel/wiring/specs" 12 | "github.com/blueprint-uservices/blueprint/plugins/cmdbuilder" 13 | "github.com/blueprint-uservices/blueprint/plugins/workflow/workflowspec" 14 | ) 15 | 16 | func main() { 17 | workflowspec.AddModule("github.com/blueprint-uservices/blueprint/examples/dsb_hotel/tests") 18 | 19 | name := "Hotel" 20 | cmdbuilder.MakeAndExecute( 21 | name, 22 | specs.Original, 23 | ) 24 | } 25 | -------------------------------------------------------------------------------- /runtime/plugins/opentelemetry/trace.go: -------------------------------------------------------------------------------- 1 | package opentelemetry 2 | 3 | import ( 4 | "context" 5 | 6 | "go.opentelemetry.io/otel/exporters/stdout/stdouttrace" 7 | tracesdk "go.opentelemetry.io/otel/sdk/trace" 8 | "go.opentelemetry.io/otel/trace" 9 | ) 10 | 11 | type StdoutTracer struct { 12 | tp *tracesdk.TracerProvider 13 | } 14 | 15 | func NewStdoutTracer(ctx context.Context, addr string) (*StdoutTracer, error) { 16 | exp, err := stdouttrace.New(stdouttrace.WithPrettyPrint()) 17 | if err != nil { 18 | return nil, err 19 | } 20 | 21 | bsp := tracesdk.NewBatchSpanProcessor(exp) 22 | tp := tracesdk.NewTracerProvider( 23 | tracesdk.WithSpanProcessor(bsp), 24 | ) 25 | return &StdoutTracer{tp}, nil 26 | } 27 | 28 | func (t *StdoutTracer) GetTracerProvider(ctx context.Context) (trace.TracerProvider, error) { 29 | return t.tp, nil 30 | } 31 | -------------------------------------------------------------------------------- /examples/train_ticket/wiring/specs/README.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | # specs 4 | 5 | ```go 6 | import "github.com/blueprint-uservices/blueprint/examples/train_ticket/wiring/specs" 7 | ``` 8 | 9 | ## Index 10 | 11 | - [Variables](<#variables>) 12 | 13 | 14 | ## Variables 15 | 16 | A wiring spec that deploys each service into its own Docker container and uses http to communicate between services. The user service uses MongoDB instance to store their data. 17 | 18 | ```go 19 | var Docker = cmdbuilder.SpecOption{ 20 | Name: "docker", 21 | Description: "Deploys each service in a separate container with http, and uses mongodb as NoSQL database backends", 22 | Build: makeDockerSpec, 23 | } 24 | ``` 25 | 26 | Generated by [gomarkdoc]() 27 | -------------------------------------------------------------------------------- /plugins/dockercompose/ir.go: -------------------------------------------------------------------------------- 1 | package dockercompose 2 | 3 | import ( 4 | "github.com/blueprint-uservices/blueprint/blueprint/pkg/ir" 5 | ) 6 | 7 | // An IRNode representing a docker-compose deployment, which is simply a collection of 8 | // container instances. 9 | type Deployment struct { 10 | /* The implemented build targets for dockercompose.DockerCompose nodes */ 11 | dockerComposeDeployer /* Can be deployed as a docker-compose file; implemented in deploydockercompose.go */ 12 | 13 | DeploymentName string 14 | Nodes []ir.IRNode 15 | Edges []ir.IRNode 16 | } 17 | 18 | // Implements IRNode 19 | func (node *Deployment) Name() string { 20 | return node.DeploymentName 21 | } 22 | 23 | // Implements IRNode 24 | func (node *Deployment) String() string { 25 | return ir.PrettyPrintNamespace(node.DeploymentName, "DockerApp", node.Edges, node.Nodes) 26 | } 27 | -------------------------------------------------------------------------------- /scripts/gen_docs.py: -------------------------------------------------------------------------------- 1 | import os 2 | import subprocess 3 | import fnmatch 4 | 5 | PKGS=["blueprint", "plugins", "runtime"] 6 | 7 | def main(): 8 | ''' 9 | Script assumes that you are running it from the root directory. 10 | Expected Usage: python3 scripts/gen_docs.py 11 | ''' 12 | print("Removing old auto-generated documentation") 13 | rc = subprocess.call("./scripts/cleanup_autodocs.sh") 14 | 15 | for pkg in PKGS: 16 | for root, dirs, files in os.walk(pkg): 17 | filtered_files = fnmatch.filter(files, "*.go") 18 | # Current directory has some go files so we can auto-generate documentation 19 | if len(filtered_files) != 0: 20 | print("Generating documentation for ", root) 21 | subprocess.run(["./scripts/gen_docs.sh", root]) 22 | 23 | if __name__ == '__main__': 24 | main() 25 | -------------------------------------------------------------------------------- /examples/sockshop/workload/go.mod: -------------------------------------------------------------------------------- 1 | module github.com/blueprint-uservices/blueprint/examples/sockshop/workload 2 | 3 | go 1.21 4 | 5 | toolchain go1.22.1 6 | 7 | require github.com/blueprint-uservices/blueprint/runtime v0.0.0-20240619221802-d064c5861c1e // indirect 8 | 9 | replace github.com/blueprint-uservices/blueprint/examples/sockshop/workflow => ../workflow 10 | 11 | require github.com/blueprint-uservices/blueprint/examples/sockshop/workflow v0.0.0-20240405152959-f078915d2306 12 | 13 | require ( 14 | github.com/google/uuid v1.6.0 // indirect 15 | github.com/pkg/errors v0.9.1 // indirect 16 | go.mongodb.org/mongo-driver v1.15.0 // indirect 17 | go.opentelemetry.io/otel v1.26.0 // indirect 18 | go.opentelemetry.io/otel/metric v1.26.0 // indirect 19 | go.opentelemetry.io/otel/trace v1.26.0 // indirect 20 | golang.org/x/exp v0.0.0-20240416160154-fe59bbe5cc7f // indirect 21 | ) 22 | -------------------------------------------------------------------------------- /examples/train_ticket/wiring/main.go: -------------------------------------------------------------------------------- 1 | // Package main provides an application for compiling different wiring specs for TrainTicket application. 2 | // 3 | // To display options and usage, invoke: 4 | // 5 | // go run main.go -h 6 | package main 7 | 8 | import ( 9 | _ "github.com/blueprint-uservices/blueprint/examples/train_ticket/tests" 10 | "github.com/blueprint-uservices/blueprint/examples/train_ticket/wiring/specs" 11 | "github.com/blueprint-uservices/blueprint/plugins/cmdbuilder" 12 | "github.com/blueprint-uservices/blueprint/plugins/workflow/workflowspec" 13 | ) 14 | 15 | func main() { 16 | // Configure the location of our tests 17 | workflowspec.AddModule("github.com/blueprint-uservices/blueprint/examples/train_ticket/tests") 18 | 19 | // Build a supported wiring spec 20 | name := "TrainTicket" 21 | cmdbuilder.MakeAndExecute( 22 | name, 23 | specs.Docker, 24 | ) 25 | } 26 | -------------------------------------------------------------------------------- /examples/dsb_sn/wiring/main.go: -------------------------------------------------------------------------------- 1 | // Package main provides an application for compiling different 2 | // wiring specs for DeathStarBench SocialNetwork application. 3 | // 4 | // To display options and usage, invoke: 5 | // 6 | // go run main.go -h 7 | package main 8 | 9 | import ( 10 | _ "github.com/blueprint-uservices/blueprint/examples/dsb_sn/tests" 11 | "github.com/blueprint-uservices/blueprint/examples/dsb_sn/wiring/specs" 12 | "github.com/blueprint-uservices/blueprint/plugins/cmdbuilder" 13 | "github.com/blueprint-uservices/blueprint/plugins/workflow/workflowspec" 14 | ) 15 | 16 | func main() { 17 | // Configure the location of our tests 18 | workflowspec.AddModule("github.com/blueprint-uservices/blueprint/examples/dsb_sn/tests") 19 | 20 | // Build a supported wiring spec 21 | name := "SocialNetwork" 22 | cmdbuilder.MakeAndExecute( 23 | name, 24 | specs.Docker, 25 | ) 26 | } 27 | -------------------------------------------------------------------------------- /examples/dsb_hotel/cmplx_workload/go.mod: -------------------------------------------------------------------------------- 1 | module github.com/blueprint-uservices/blueprint/examples/dsb_hotel/cmplx_workload 2 | 3 | go 1.22.1 4 | 5 | replace github.com/blueprint-uservices/blueprint/examples/dsb_hotel/workflow => ../workflow 6 | 7 | require github.com/blueprint-uservices/blueprint/examples/dsb_hotel/workflow v0.0.0 8 | 9 | require github.com/blueprint-uservices/blueprint/runtime v0.0.0 10 | 11 | require ( 12 | github.com/hailocab/go-geoindex v0.0.0-20160127134810-64631bfe9711 // indirect 13 | go.mongodb.org/mongo-driver v1.15.0 // indirect 14 | go.opentelemetry.io/otel v1.26.0 // indirect 15 | go.opentelemetry.io/otel/metric v1.26.0 // indirect 16 | go.opentelemetry.io/otel/trace v1.26.0 // indirect 17 | golang.org/x/exp v0.0.0-20240416160154-fe59bbe5cc7f // indirect 18 | gonum.org/v1/gonum v0.15.1 // indirect 19 | ) 20 | 21 | replace github.com/blueprint-uservices/blueprint/runtime => ../../../runtime -------------------------------------------------------------------------------- /plugins/linux/util.go: -------------------------------------------------------------------------------- 1 | package linux 2 | 3 | import ( 4 | "strings" 5 | 6 | "github.com/blueprint-uservices/blueprint/blueprint/pkg/ir" 7 | ) 8 | 9 | // A utility function to deterministically convert a string into a 10 | // a valid linux environment variable name. This is done by converting 11 | // all punctuation characters to underscores, and converting alphabetic 12 | // characters to uppercase (for convention), e.g. 13 | // 14 | // a.grpc_addr becomes A_GRPC_ADDR. 15 | // 16 | // Punctuation is converted to underscores, and alpha are made uppercase. 17 | func EnvVar(name string) string { 18 | return strings.ToUpper(ir.CleanName(name)) 19 | } 20 | 21 | // A utility function for use when using commands. 22 | // Converts a string to a compatible command name. 23 | // Punctuation is converted to underscores, and alpha are made uppercase. 24 | func FuncName(name string) string { 25 | return strings.ToLower(ir.CleanName(name)) 26 | } 27 | -------------------------------------------------------------------------------- /examples/train_ticket/workflow/news/newsService.go: -------------------------------------------------------------------------------- 1 | // package news implements the ts-news-service from the TrainTicket application 2 | package news 3 | 4 | import "context" 5 | 6 | // News Service provides the latest news about the application 7 | type NewsService interface { 8 | Hello(ctx context.Context, val string) (string, error) 9 | } 10 | 11 | // News Service Implementation 12 | type NewsServiceImpl struct{} 13 | 14 | func NewNewsServiceImpl(ctx context.Context) (*NewsServiceImpl, error) { 15 | return &NewsServiceImpl{}, nil 16 | } 17 | 18 | func (n *NewsServiceImpl) Hello(ctx context.Context, val string) (string, error) { 19 | var str = []byte(`[ 20 | {"Title": "News Service Complete", "Content": "Congratulations:Your News Service Complete"}, 21 | {"Title": "Total Ticket System Complete", "Content": "Just a total test"} 22 | ]`) 23 | return string(str), nil 24 | } 25 | -------------------------------------------------------------------------------- /docs/dev/api.md: -------------------------------------------------------------------------------- 1 | # Blueprint API Documentation 2 | 3 | Deprecated: do not use this any more. 4 | 5 | Blueprint API documentation for the core plugins, external plugins, and blueprint runtime packages is available in [api](api/). 6 | 7 | ## Updating Documentation 8 | 9 | ### Dependencies 10 | 11 | The documentations is generated using ```go doc``` and converted to markdown using godoc2markdown. 12 | Installation instructions for installing godoc2markdown can be found [here](https://git.sr.ht/~humaid/godoc2markdown). 13 | 14 | ### Re-generating documentation 15 | 16 | To regenerate the documentation, execute the following script from the root blueprint directory: 17 | 18 | ```bash 19 | python scripts/gen_docs.py 20 | ``` 21 | 22 | ### Updating Website docs 23 | 24 | To update the documentation on the website, please follow the instructions [here](https://github.com/blueprint-uservices/Blueprint-uServices.github.io/tree/main#updating-documentation). -------------------------------------------------------------------------------- /examples/dsb_sn/tests/uniqueidservice_test.go: -------------------------------------------------------------------------------- 1 | package tests 2 | 3 | import ( 4 | "context" 5 | "testing" 6 | 7 | "github.com/blueprint-uservices/blueprint/examples/dsb_sn/workflow/socialnetwork" 8 | "github.com/blueprint-uservices/blueprint/runtime/core/registry" 9 | "github.com/stretchr/testify/require" 10 | ) 11 | 12 | var uniqueIdServiceRegistry = registry.NewServiceRegistry[socialnetwork.UniqueIdService]("uniqueId_service") 13 | 14 | func init() { 15 | 16 | uniqueIdServiceRegistry.Register("local", func(ctx context.Context) (socialnetwork.UniqueIdService, error) { 17 | return socialnetwork.NewUniqueIdServiceImpl(ctx) 18 | }) 19 | } 20 | 21 | func TestComposeUniqueId(t *testing.T) { 22 | ctx := context.Background() 23 | service, err := uniqueIdServiceRegistry.Get(ctx) 24 | require.NoError(t, err) 25 | 26 | id, err := service.ComposeUniqueId(ctx, 1000, socialnetwork.POST) 27 | require.NoError(t, err) 28 | require.Positive(t, id) 29 | } 30 | -------------------------------------------------------------------------------- /examples/train_ticket/tests/go.mod: -------------------------------------------------------------------------------- 1 | module github.com/blueprint-uservices/blueprint/examples/train_ticket/tests 2 | 3 | go 1.21 4 | 5 | toolchain go1.22.1 6 | 7 | require ( 8 | github.com/blueprint-uservices/blueprint/examples/train_ticket/workflow v0.0.0 9 | github.com/blueprint-uservices/blueprint/runtime v0.0.0-20240619221802-d064c5861c1e 10 | github.com/stretchr/testify v1.9.0 11 | go.mongodb.org/mongo-driver v1.15.0 12 | ) 13 | 14 | require ( 15 | github.com/davecgh/go-spew v1.1.1 // indirect 16 | github.com/google/uuid v1.6.0 // indirect 17 | github.com/pmezard/go-difflib v1.0.0 // indirect 18 | go.opentelemetry.io/otel v1.26.0 // indirect 19 | go.opentelemetry.io/otel/metric v1.26.0 // indirect 20 | go.opentelemetry.io/otel/trace v1.26.0 // indirect 21 | golang.org/x/exp v0.0.0-20240416160154-fe59bbe5cc7f // indirect 22 | gopkg.in/yaml.v3 v3.0.1 // indirect 23 | ) 24 | 25 | replace github.com/blueprint-uservices/blueprint/examples/train_ticket/workflow => ../workflow 26 | -------------------------------------------------------------------------------- /docs/manual/README.md: -------------------------------------------------------------------------------- 1 | # 📘Blueprint User Manual 2 | 3 | This User Manual is a work in progress. 4 | 5 | 1. First Steps 6 | 1. [Requirements](requirements.md) 7 | 1. [Getting Started](gettingstarted.md) 8 | 1. [Blueprint Overview](overview.md) 9 | 1. Applications: the Workflow Spec 10 | 1. [📓Workflow Spec](workflow.md) 11 | 1. [📓Testing an Application](workflow_tests.md) 12 | 1. [📓Example Applications](../../examples/) 13 | 1. Configuring Applications: the Wiring Spec 14 | 1. [📝Wiring Spec](wiring.md) 15 | 1. [📝Supported Plugins](plugins.md) 16 | 1. Compiling and Running 17 | 1. [Compiling an Application](compiling.md) 18 | 1. [Running an Application](running.md) 19 | 1. Power Users 20 | 1. [Blueprint Internals]() 21 | 1. [Project Layout]() 22 | 1. [Developing a Plugin](manual/plugin_development.md) 23 | 1. [Glossary of Terms](glossary.md) 24 | 25 | If any aspects of the user manual are incomplete or require clarification, please reach out to us on Slack. -------------------------------------------------------------------------------- /docs/manual/requirements.md: -------------------------------------------------------------------------------- 1 | # Requirements 2 | 3 | In order to compile a Blueprint application, the build machine must satisfy the pre-requisites listed here. Note that these requirements apply to the machine on which you are compiling the application; they do not typically also apply to the machine(s) on which you run the application. 4 | 5 | ## Compiler Requirements 6 | 7 | Blueprint requires golang 1.20 or higher. 8 | 9 | We **highly recommend** also installing the following in order to run the Blueprint examples. Follow the instructions under the **Prerequisites** heading for the following plugins: 10 | * [gRPC](../../plugins/grpc) 11 | * [Docker](../../plugins/docker) 12 | * [Kubernetes](../../plugins/kubernetes) 13 | 14 | The above dependencies are sufficient for compiling most of the Blueprint example applications. However, in addition to the above, some plugins might have further dependencies that you will need to install before you can use that plugin. The plugin will document these dependencies. -------------------------------------------------------------------------------- /examples/sockshop/wiring/main.go: -------------------------------------------------------------------------------- 1 | // An application for compiling the SockShop application. 2 | // Provides a number of different wiring specs for compiling 3 | // the application in different configurations. 4 | // 5 | // To display options and usage, invoke: 6 | // 7 | // go run main.go -h 8 | package main 9 | 10 | import ( 11 | _ "github.com/blueprint-uservices/blueprint/examples/sockshop/tests" 12 | "github.com/blueprint-uservices/blueprint/examples/sockshop/wiring/specs" 13 | "github.com/blueprint-uservices/blueprint/plugins/cmdbuilder" 14 | "github.com/blueprint-uservices/blueprint/plugins/workflow/workflowspec" 15 | ) 16 | 17 | func main() { 18 | // Make sure tests and workflow can be found 19 | workflowspec.AddModule("github.com/blueprint-uservices/blueprint/examples/sockshop/tests") 20 | 21 | // Build a supported wiring spec 22 | name := "SockShop" 23 | cmdbuilder.MakeAndExecute( 24 | name, 25 | specs.Basic, 26 | specs.GRPC, 27 | specs.Docker, 28 | specs.DockerRabbit, 29 | ) 30 | } 31 | -------------------------------------------------------------------------------- /examples/dsb_hotel/wiring/specs/README.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | # specs 4 | 5 | ```go 6 | import "github.com/blueprint-uservices/blueprint/examples/dsb_hotel/wiring/specs" 7 | ``` 8 | 9 | ## Index 10 | 11 | - [Variables](<#variables>) 12 | 13 | 14 | ## Variables 15 | 16 | Wiring spec that represents the original configuration of the HotelReservation application. Each service is deployed in a separate container with all inter\-service communication happening via GRPC. FrontEnd service provides a http frontend for making requests. All services are instrumented with opentelemetry tracing with spans being exported to a central Jaeger collector. 17 | 18 | ```go 19 | var Original = cmdbuilder.SpecOption{ 20 | Name: "original", 21 | Description: "Deploys the original configuration of the DeathStarBench application.", 22 | Build: makeOriginalSpec, 23 | } 24 | ``` 25 | 26 | Generated by [gomarkdoc]() 27 | -------------------------------------------------------------------------------- /blueprint/pkg/blueprint/errors.go: -------------------------------------------------------------------------------- 1 | package blueprint 2 | 3 | import ( 4 | "fmt" 5 | "runtime" 6 | ) 7 | 8 | const ( 9 | MAX_ERR_SIZE = 2048 10 | ) 11 | 12 | // An error used by Blueprint's compiler that captures the calling 13 | // stack so that we can tie errors back to the plugins that caused 14 | // the error 15 | type blueprintError struct { 16 | Stack []byte 17 | Err error 18 | } 19 | 20 | func (e *blueprintError) Error() string { 21 | return fmt.Sprintf("%v\n%v", e.Err, string(e.Stack)) 22 | } 23 | 24 | // Generates an error in the same way as fmt.Errorf but also includes 25 | // the call stack. 26 | // 27 | // Plugins should generally use this method, because it enables us 28 | // to more easily tie errors back to the plugins and wiring specs 29 | // that caused the error. 30 | func Errorf(format string, a ...any) error { 31 | bytes := make([]byte, MAX_ERR_SIZE) 32 | runtime.Stack(bytes, false) 33 | err := fmt.Errorf(format, a...) 34 | return &blueprintError{ 35 | Stack: bytes, 36 | Err: err, 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /examples/dsb_hotel/tests/go.mod: -------------------------------------------------------------------------------- 1 | module github.com/blueprint-uservices/blueprint/examples/dsb_hotel/tests 2 | 3 | go 1.21 4 | 5 | toolchain go1.22.1 6 | 7 | require github.com/blueprint-uservices/blueprint/runtime v0.0.0-20240619221802-d064c5861c1e 8 | 9 | require ( 10 | github.com/blueprint-uservices/blueprint/examples/dsb_hotel/workflow v0.0.0 11 | github.com/stretchr/testify v1.9.0 12 | ) 13 | 14 | require ( 15 | github.com/davecgh/go-spew v1.1.1 // indirect 16 | github.com/hailocab/go-geoindex v0.0.0-20160127134810-64631bfe9711 // indirect 17 | github.com/pmezard/go-difflib v1.0.0 // indirect 18 | go.mongodb.org/mongo-driver v1.15.0 // indirect 19 | go.opentelemetry.io/otel v1.26.0 // indirect 20 | go.opentelemetry.io/otel/metric v1.26.0 // indirect 21 | go.opentelemetry.io/otel/trace v1.26.0 // indirect 22 | golang.org/x/exp v0.0.0-20240416160154-fe59bbe5cc7f // indirect 23 | gopkg.in/yaml.v3 v3.0.1 // indirect 24 | ) 25 | 26 | replace github.com/blueprint-uservices/blueprint/examples/dsb_hotel/workflow => ../workflow 27 | -------------------------------------------------------------------------------- /plugins/goproc/goprocgen/README.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | # goprocgen 4 | 5 | ```go 6 | import "github.com/blueprint-uservices/blueprint/plugins/goproc/goprocgen" 7 | ``` 8 | 9 | Package goprocgen implements code generation for the main.go file 10 | 11 | ## Index 12 | 13 | - [func GenerateMain\(name string, argNodes \[\]ir.IRNode, nodesToInstantiate \[\]ir.IRNode, module golang.ModuleBuilder, namespaceConstructor string\) error](<#GenerateMain>) 14 | 15 | 16 | 17 | ## func [GenerateMain]() 18 | 19 | ```go 20 | func GenerateMain(name string, argNodes []ir.IRNode, nodesToInstantiate []ir.IRNode, module golang.ModuleBuilder, namespaceConstructor string) error 21 | ``` 22 | 23 | Generates a main.go file in the provided module. The main method will call the namespaceConstructor provided to create and instantiate nodes. 24 | 25 | Generated by [gomarkdoc]() 26 | -------------------------------------------------------------------------------- /runtime/core/backend/queue.go: -------------------------------------------------------------------------------- 1 | package backend 2 | 3 | import ( 4 | "context" 5 | ) 6 | 7 | // A Queue backend is used for pushing and popping elements. 8 | type Queue interface { 9 | 10 | // Pushes an item to the tail of the queue. 11 | // 12 | // This call will block until the item is successfully pushed, or until the context 13 | // is cancelled. 14 | // 15 | // Reports whether the item was pushed to the queue, or if an error was encountered. 16 | // A context cancellation/timeout is not considered an error. 17 | Push(ctx context.Context, item interface{}) (bool, error) 18 | 19 | // Pops an item from the front of the queue. 20 | // 21 | // This call will block until an item is successfully popped, or until the context 22 | // is cancelled. 23 | // 24 | // dst must be a pointer type that can receive the item popped from the queue. 25 | // 26 | // Reports whether the item was pushed to the queue, or if an error was encountered. 27 | // A context cancellation/timeout is not considered an error. 28 | Pop(ctx context.Context, dst interface{}) (bool, error) 29 | } 30 | -------------------------------------------------------------------------------- /plugins/goproc/linuxgen/dockerfile_buildcommands.go: -------------------------------------------------------------------------------- 1 | package linuxgen 2 | 3 | import "github.com/blueprint-uservices/blueprint/plugins/golang/gogen" 4 | 5 | /* 6 | If the goproc is being deployed to Docker, we can provide some custom 7 | build commands to add to the Dockerfile 8 | */ 9 | func GenerateDockerfileBuildCommands(goProcName string) (string, error) { 10 | args := dockerfileBuildTemplateArgs{ 11 | ProcName: goProcName, 12 | } 13 | return gogen.ExecuteTemplate("dockerfile_buildgoproc", dockerfileBuildTemplate, args) 14 | } 15 | 16 | type dockerfileBuildTemplateArgs struct { 17 | ProcName string 18 | } 19 | 20 | var dockerfileBuildTemplate = ` 21 | ####### BEGIN 22 | # custom docker build commands provided by goproc.Process {{.ProcName}} 23 | # 24 | 25 | FROM golang:1.23-bookworm AS {{.ProcName}} 26 | 27 | COPY ./{{.ProcName}} /src 28 | 29 | WORKDIR /src 30 | RUN go mod download 31 | 32 | RUN mkdir /{{.ProcName}} 33 | RUN go build -o /{{.ProcName}} ./{{.ProcName}} 34 | 35 | # 36 | # custom docker build commands provided by goproc.Process {{.ProcName}} 37 | ######## END 38 | ` 39 | -------------------------------------------------------------------------------- /examples/train_ticket/workflow/food/README.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | # food 4 | 5 | ```go 6 | import "github.com/blueprint-uservices/blueprint/examples/train_ticket/workflow/food" 7 | ``` 8 | 9 | ## Index 10 | 11 | - [type Food](<#Food>) 12 | - [type FoodOrder](<#FoodOrder>) 13 | 14 | 15 | 16 | ## type [Food]() 17 | 18 | 19 | 20 | ```go 21 | type Food struct { 22 | Name string 23 | Price float64 24 | } 25 | ``` 26 | 27 | 28 | ## type [FoodOrder]() 29 | 30 | 31 | 32 | ```go 33 | type FoodOrder struct { 34 | ID string 35 | OrderID string 36 | FoodType int64 37 | StationName string 38 | StoreName string 39 | FoodName string 40 | Price float64 41 | } 42 | ``` 43 | 44 | Generated by [gomarkdoc]() 45 | -------------------------------------------------------------------------------- /blueprint/pkg/blueprint/README.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | # blueprint 4 | 5 | ```go 6 | import "github.com/blueprint-uservices/blueprint/blueprint/pkg/blueprint" 7 | ``` 8 | 9 | Package blueprint provides some common utility methods for logging, io, errors and string manipulation. 10 | 11 | ## Index 12 | 13 | - [Constants](<#constants>) 14 | - [func Errorf\(format string, a ...any\) error](<#Errorf>) 15 | 16 | 17 | ## Constants 18 | 19 | 20 | 21 | ```go 22 | const ( 23 | MAX_ERR_SIZE = 2048 24 | ) 25 | ``` 26 | 27 | 28 | ## func [Errorf]() 29 | 30 | ```go 31 | func Errorf(format string, a ...any) error 32 | ``` 33 | 34 | Generates an error in the same way as fmt.Errorf but also includes the call stack. 35 | 36 | Plugins should generally use this method, because it enables us to more easily tie errors back to the plugins and wiring specs that caused the error. 37 | 38 | Generated by [gomarkdoc]() 39 | -------------------------------------------------------------------------------- /examples/dsb_hotel/tests/geoservice_test.go: -------------------------------------------------------------------------------- 1 | package tests 2 | 3 | import ( 4 | "context" 5 | "testing" 6 | 7 | "github.com/blueprint-uservices/blueprint/examples/dsb_hotel/workflow/hotelreservation" 8 | "github.com/blueprint-uservices/blueprint/runtime/core/registry" 9 | "github.com/blueprint-uservices/blueprint/runtime/plugins/simplenosqldb" 10 | "github.com/stretchr/testify/assert" 11 | ) 12 | 13 | var geoServiceRegistry = registry.NewServiceRegistry[hotelreservation.GeoService]("geo_service") 14 | 15 | func init() { 16 | 17 | geoServiceRegistry.Register("local", func(ctx context.Context) (hotelreservation.GeoService, error) { 18 | db, err := simplenosqldb.NewSimpleNoSQLDB(ctx) 19 | if err != nil { 20 | return nil, err 21 | } 22 | 23 | return hotelreservation.NewGeoServiceImpl(ctx, db) 24 | }) 25 | } 26 | 27 | func TestNearby(t *testing.T) { 28 | ctx := context.Background() 29 | service, err := geoServiceRegistry.Get(ctx) 30 | assert.NoError(t, err) 31 | 32 | hotels, err := service.Nearby(ctx, 37.7835, -122.41) 33 | assert.NoError(t, err) 34 | assert.Len(t, hotels, 5) 35 | t.Log(hotels) 36 | } 37 | -------------------------------------------------------------------------------- /examples/dsb_sn/tests/mediaservice_test.go: -------------------------------------------------------------------------------- 1 | package tests 2 | 3 | import ( 4 | "context" 5 | "testing" 6 | 7 | "github.com/blueprint-uservices/blueprint/examples/dsb_sn/workflow/socialnetwork" 8 | "github.com/blueprint-uservices/blueprint/runtime/core/registry" 9 | "github.com/stretchr/testify/require" 10 | ) 11 | 12 | var mediaServiceRegistry = registry.NewServiceRegistry[socialnetwork.MediaService]("media_service") 13 | 14 | func init() { 15 | mediaServiceRegistry.Register("local", func(ctx context.Context) (socialnetwork.MediaService, error) { 16 | return socialnetwork.NewMediaServiceImpl(ctx) 17 | }) 18 | } 19 | 20 | func TestComposeMedia(t *testing.T) { 21 | ctx := context.Background() 22 | service, err := mediaServiceRegistry.Get(ctx) 23 | require.NoError(t, err) 24 | 25 | // Equal length of media types 26 | media, err := service.ComposeMedia(ctx, 1000, []string{"video", "pic", "audio"}, []int64{0, 1, 2}) 27 | require.NoError(t, err) 28 | require.Len(t, media, 3) 29 | 30 | // Non-equal length 31 | media, err = service.ComposeMedia(ctx, 1000, []string{}, []int64{0, 1, 2}) 32 | require.Error(t, err) 33 | } 34 | -------------------------------------------------------------------------------- /examples/sockshop/tests/go.mod: -------------------------------------------------------------------------------- 1 | module github.com/blueprint-uservices/blueprint/examples/sockshop/tests 2 | 3 | go 1.21 4 | 5 | toolchain go1.22.1 6 | 7 | require github.com/blueprint-uservices/blueprint/runtime v0.0.0-20240619221802-d064c5861c1e 8 | 9 | require ( 10 | github.com/blueprint-uservices/blueprint/examples/sockshop/workflow v0.0.0 11 | github.com/stretchr/testify v1.9.0 12 | golang.org/x/exp v0.0.0-20240416160154-fe59bbe5cc7f 13 | ) 14 | 15 | require ( 16 | github.com/davecgh/go-spew v1.1.1 // indirect 17 | github.com/google/uuid v1.6.0 // indirect 18 | github.com/jmoiron/sqlx v1.4.0 // indirect 19 | github.com/mattn/go-sqlite3 v1.14.22 // indirect 20 | github.com/pkg/errors v0.9.1 // indirect 21 | github.com/pmezard/go-difflib v1.0.0 // indirect 22 | go.mongodb.org/mongo-driver v1.15.0 // indirect 23 | go.opentelemetry.io/otel v1.26.0 // indirect 24 | go.opentelemetry.io/otel/metric v1.26.0 // indirect 25 | go.opentelemetry.io/otel/trace v1.26.0 // indirect 26 | gopkg.in/yaml.v3 v3.0.1 // indirect 27 | ) 28 | 29 | replace github.com/blueprint-uservices/blueprint/examples/sockshop/workflow => ../workflow 30 | -------------------------------------------------------------------------------- /examples/dsb_sn/wiring/specs/README.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | # specs 4 | 5 | ```go 6 | import "github.com/blueprint-uservices/blueprint/examples/dsb_sn/wiring/specs" 7 | ``` 8 | 9 | ## Index 10 | 11 | - [Variables](<#variables>) 12 | 13 | 14 | ## Variables 15 | 16 | A wiring spec that deploys each service into its own Docker container and using thrift to communicate between services. All services except the Wrk2API service use thrift for communication; WRK2API service provides the http frontend. The user, socialgraph, urlshorten, and usertimeline services use MongoDB instances to store their data. The user, socialgraph, urlshorten, usertimeine, and hometimeline services use memcached instances as the cache data for faster responses. 17 | 18 | ```go 19 | var Docker = cmdbuilder.SpecOption{ 20 | Name: "docker", 21 | Description: "Deploys each service in a separate container with thrift, and uses mongodb as NoSQL database backends.", 22 | Build: makeDockerSpec, 23 | } 24 | ``` 25 | 26 | Generated by [gomarkdoc]() 27 | -------------------------------------------------------------------------------- /runtime/plugins/opentelemetry/metric.go: -------------------------------------------------------------------------------- 1 | package opentelemetry 2 | 3 | import ( 4 | "context" 5 | "time" 6 | 7 | "github.com/blueprint-uservices/blueprint/runtime/core/backend" 8 | "go.opentelemetry.io/otel" 9 | "go.opentelemetry.io/otel/exporters/stdout/stdoutmetric" 10 | "go.opentelemetry.io/otel/metric" 11 | metricsdk "go.opentelemetry.io/otel/sdk/metric" 12 | ) 13 | 14 | type StdoutMetricCollector struct { 15 | mp *metricsdk.MeterProvider 16 | } 17 | 18 | func (s *StdoutMetricCollector) GetMetricProvider(ctx context.Context) (metric.MeterProvider, error) { 19 | return s.mp, nil 20 | } 21 | 22 | func NewStdoutMetricCollector(ctx context.Context) (*StdoutMetricCollector, error) { 23 | exp, err := stdoutmetric.New() 24 | if err != nil { 25 | return nil, err 26 | } 27 | 28 | mp := metricsdk.NewMeterProvider( 29 | metricsdk.WithReader(metricsdk.NewPeriodicReader(exp, 30 | // Default is 1m. Set to 3s for demonstrative purposes. 31 | metricsdk.WithInterval(3*time.Second))), 32 | ) 33 | 34 | otel.SetMeterProvider(mp) 35 | mc := &StdoutMetricCollector{mp} 36 | backend.SetDefaultMetricCollector(mc) 37 | return mc, nil 38 | } 39 | -------------------------------------------------------------------------------- /runtime/doc.go: -------------------------------------------------------------------------------- 1 | // Package runtime contains interfaces and implementations that are used by compiled Blueprint applications at runtime 2 | // 3 | // During compilation, Blueprint's compiler will take code from this module and include it into the compiled output. 4 | // This is primarily implemented by the [golang/goparser] and [workflowspec] plugins 5 | // 6 | // [runtime/core/backend] defines the backend interfaces used by workflow specs. A workflow spec implementation might want to import that package and use the interfaces defined there. 7 | // 8 | // [plugins] defines implementations that are used by plugins and automatically compiled into the application. These should not need to be directly reference by workflow specs. 9 | // 10 | // [golang/goparser]: https://github.com/Blueprint-uServices/blueprint/tree/main/plugins/golang/goparser 11 | // [workflowspec]: https://github.com/Blueprint-uServices/blueprint/tree/main/plugins/workflowspec 12 | // [runtime/core/backend]: https://github.com/Blueprint-uServices/blueprint/tree/main/runtime/core/backend 13 | // [plugins]: https://github.com/Blueprint-uServices/blueprint/tree/main/runtime/plugins 14 | package runtime 15 | -------------------------------------------------------------------------------- /examples/leaf/wiring/main.go: -------------------------------------------------------------------------------- 1 | // Package main provides the LeafApp application, a simple application designed for demonstrating Blueprint 2 | // usage and not as a realistic executable application. 3 | // 4 | // The wiring specs in the [specs] directory illustrate the usage of various Blueprint plugins. 5 | // 6 | // Leaf is also used by Blueprint developers while developing plugins. 7 | // 8 | // # Usage 9 | // 10 | // To display usage, run 11 | // 12 | // go run . -h 13 | package main 14 | 15 | import ( 16 | "github.com/blueprint-uservices/blueprint/examples/leaf/wiring/specs" 17 | "github.com/blueprint-uservices/blueprint/plugins/cmdbuilder" 18 | "github.com/blueprint-uservices/blueprint/plugins/workflow/workflowspec" 19 | ) 20 | 21 | func main() { 22 | // Configure the location of our workflow spec 23 | workflowspec.AddModule("github.com/blueprint-uservices/blueprint/examples/leaf/workflow") 24 | 25 | // Build a supported wiring spec 26 | name := "LeafApp" 27 | cmdbuilder.MakeAndExecute( 28 | name, 29 | specs.Docker, 30 | specs.Thrift, 31 | specs.HTTP, 32 | specs.TimeoutDemo, 33 | specs.TimeoutRetriesDemo, 34 | specs.Xtrace_Logger, 35 | specs.OT_Logger, 36 | specs.Govector, 37 | ) 38 | } 39 | -------------------------------------------------------------------------------- /examples/dsb_hotel/tests/searchservice_test.go: -------------------------------------------------------------------------------- 1 | package tests 2 | 3 | import ( 4 | "context" 5 | "testing" 6 | 7 | "github.com/blueprint-uservices/blueprint/examples/dsb_hotel/workflow/hotelreservation" 8 | "github.com/blueprint-uservices/blueprint/runtime/core/registry" 9 | "github.com/stretchr/testify/assert" 10 | ) 11 | 12 | var searchServiceRegistry = registry.NewServiceRegistry[hotelreservation.SearchService]("search_service") 13 | 14 | func init() { 15 | 16 | searchServiceRegistry.Register("local", func(ctx context.Context) (hotelreservation.SearchService, error) { 17 | geoService, err := geoServiceRegistry.Get(ctx) 18 | if err != nil { 19 | return nil, err 20 | } 21 | 22 | rateService, err := rateServiceRegistry.Get(ctx) 23 | if err != nil { 24 | return nil, err 25 | } 26 | 27 | return hotelreservation.NewSearchServiceImpl(ctx, geoService, rateService) 28 | }) 29 | } 30 | 31 | func TestSearchNearby(t *testing.T) { 32 | ctx := context.Background() 33 | service, err := searchServiceRegistry.Get(ctx) 34 | assert.NoError(t, err) 35 | 36 | hotels, err := service.Nearby(ctx, 37.7835, -122.41, "2015-04-09", "2015-04-10") 37 | assert.NoError(t, err) 38 | assert.True(t, len(hotels) > 0) 39 | } 40 | -------------------------------------------------------------------------------- /examples/dsb_hotel/workload/workloadgen/workload.go: -------------------------------------------------------------------------------- 1 | package workloadgen 2 | 3 | import ( 4 | "context" 5 | "fmt" 6 | "time" 7 | 8 | "github.com/blueprint-uservices/blueprint/examples/dsb_hotel/workflow/hotelreservation" 9 | ) 10 | 11 | type SimpleWorkload interface { 12 | ImplementsSimpleWorkload(ctx context.Context) error 13 | } 14 | 15 | type workloadGen struct { 16 | SimpleWorkload 17 | 18 | frontend hotelreservation.FrontEndService 19 | } 20 | 21 | func NewSimpleWorkload(ctx context.Context, frontend hotelreservation.FrontEndService) (SimpleWorkload, error) { 22 | return &workloadGen{frontend: frontend}, nil 23 | } 24 | 25 | func (s *workloadGen) Run(ctx context.Context) error { 26 | ticker := time.NewTicker(1 * time.Second) 27 | for { 28 | select { 29 | case <-ctx.Done(): 30 | return nil 31 | case t := <-ticker.C: 32 | fmt.Println("Tick at", t) 33 | hotels, err := s.frontend.SearchHandler(ctx, "Vaastav", "2015-04-09", "2015-04-10", 37.7835, -122.41, "en") 34 | if err != nil { 35 | return err 36 | } 37 | fmt.Println("Query found", len(hotels), "hotels!") 38 | } 39 | } 40 | } 41 | 42 | func (s *workloadGen) ImplementsSimpleWorkload(context.Context) error { 43 | return nil 44 | } 45 | -------------------------------------------------------------------------------- /runtime/plugins/zipkin/trace.go: -------------------------------------------------------------------------------- 1 | // Package zipkin implements a tracer [backend.Tracer] client interface for the zipkin tracer. 2 | package zipkin 3 | 4 | import ( 5 | "context" 6 | 7 | "go.opentelemetry.io/otel/exporters/zipkin" 8 | tracesdk "go.opentelemetry.io/otel/sdk/trace" 9 | "go.opentelemetry.io/otel/trace" 10 | ) 11 | 12 | // ZipkinTracer implements the runtime backend instance that implements the backend/trace.Tracer interface. 13 | // REQUIRED: A functional backend running the zipkin collector. 14 | type ZipkinTracer struct { 15 | tp *tracesdk.TracerProvider 16 | } 17 | 18 | // Returns a new instance of ZipkinTracer. 19 | // Configures opentelemetry to export zipkin traces to the zipkin collector hosted at address `addr`. 20 | func NewZipkinTracer(ctx context.Context, addr string) (*ZipkinTracer, error) { 21 | exp, err := zipkin.New("http://" + addr + "/api/v2/spans") 22 | if err != nil { 23 | return nil, err 24 | } 25 | 26 | tp := tracesdk.NewTracerProvider( 27 | tracesdk.WithBatcher(exp), 28 | ) 29 | return &ZipkinTracer{tp}, nil 30 | } 31 | 32 | // Implements the backend/trace interface. 33 | func (t *ZipkinTracer) GetTracerProvider(ctx context.Context) (trace.TracerProvider, error) { 34 | return t.tp, nil 35 | } 36 | -------------------------------------------------------------------------------- /examples/sockshop/workload/workloadgen/README.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | # workloadgen 4 | 5 | ```go 6 | import "github.com/blueprint-uservices/blueprint/examples/sockshop/workload/workloadgen" 7 | ``` 8 | 9 | ## Index 10 | 11 | - [type SimpleWorkload](<#SimpleWorkload>) 12 | - [func NewSimpleWorkload\(ctx context.Context, frontend frontend.Frontend\) \(SimpleWorkload, error\)](<#NewSimpleWorkload>) 13 | 14 | 15 | 16 | ## type [SimpleWorkload]() 17 | 18 | The WorkloadGen interface, which the Blueprint compiler will treat as a Workflow service 19 | 20 | ```go 21 | type SimpleWorkload interface { 22 | ImplementsSimpleWorkload(context.Context) error 23 | } 24 | ``` 25 | 26 | 27 | ### func [NewSimpleWorkload]() 28 | 29 | ```go 30 | func NewSimpleWorkload(ctx context.Context, frontend frontend.Frontend) (SimpleWorkload, error) 31 | ``` 32 | 33 | 34 | 35 | Generated by [gomarkdoc]() 36 | -------------------------------------------------------------------------------- /blueprint/pkg/ir/buildcontext.go: -------------------------------------------------------------------------------- 1 | package ir 2 | 3 | type ( 4 | // A Blueprint application can potentially have multiple IR node instances spread across the application 5 | // that generate the same code. 6 | // 7 | // Visit tracker is a utility method used during artifact generation to prevent nodes from unnecessarily 8 | // generating the same artifact repeatedly, when once will suffice. 9 | VisitTracker interface { 10 | // Returns false on the first invocation of name; true on subsequent invocations 11 | Visited(name string) bool 12 | } 13 | 14 | // All artifact generation occurs in the context of some BuildContext. 15 | // 16 | // Plugins that control the artifact generation process should implement this interface. 17 | BuildContext interface { 18 | VisitTracker 19 | ImplementsBuildContext() 20 | } 21 | ) 22 | 23 | // Basic implementation of the [VisitTracker] interface 24 | type VisitTrackerImpl struct { 25 | visited map[string]any 26 | } 27 | 28 | func (tracker *VisitTrackerImpl) Visited(name string) bool { 29 | if tracker.visited == nil { 30 | tracker.visited = make(map[string]any) 31 | } 32 | _, has_visited := tracker.visited[name] 33 | if !has_visited { 34 | tracker.visited[name] = nil 35 | } 36 | return has_visited 37 | } 38 | -------------------------------------------------------------------------------- /test/wiring/simplenosqldb_test.go: -------------------------------------------------------------------------------- 1 | package wiring 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/blueprint-uservices/blueprint/plugins/simple" 7 | "github.com/blueprint-uservices/blueprint/plugins/workflow" 8 | "github.com/blueprint-uservices/blueprint/test/workflow/nosqldb" 9 | wf "github.com/blueprint-uservices/blueprint/test/workflow/workflow" 10 | ) 11 | 12 | func TestSimpleNoSQLDB(t *testing.T) { 13 | spec := newWiringSpec("TestSimpleNoSQLDB") 14 | 15 | leaf_cache := simple.Cache(spec, "leaf_cache") 16 | leaf_db := simple.NoSQLDB(spec, "leaf_db") 17 | leaf := workflow.Service[*nosqldb.TestLeafServiceImplWithDB](spec, "leaf", leaf_cache, leaf_db) 18 | nonleaf := workflow.Service[wf.TestNonLeafService](spec, "nonleaf", leaf) 19 | 20 | app := assertBuildSuccess(t, spec, leaf, leaf_db, nonleaf) 21 | 22 | assertIR(t, app, 23 | `TestSimpleNoSQLDB = BlueprintApplication() { 24 | leaf = TestLeafService(leaf_cache, leaf_db) 25 | leaf.client = leaf 26 | leaf.handler.visibility 27 | leaf_cache = SimpleCache() 28 | leaf_cache.backend.visibility 29 | leaf_db = SimpleNoSQLDB() 30 | leaf_db.backend.visibility 31 | nonleaf = TestNonLeafService(leaf.client) 32 | nonleaf.client = nonleaf 33 | nonleaf.handler.visibility 34 | }`) 35 | } 36 | -------------------------------------------------------------------------------- /runtime/README.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | # runtime 4 | 5 | ```go 6 | import "github.com/blueprint-uservices/blueprint/runtime" 7 | ``` 8 | 9 | Package runtime contains interfaces and implementations that are used by compiled Blueprint applications at runtime 10 | 11 | During compilation, Blueprint's compiler will take code from this module and include it into the compiled output. This is primarily implemented by the [golang/goparser]() and [workflowspec]() plugins 12 | 13 | [runtime/core/backend]() defines the backend interfaces used by workflow specs. A workflow spec implementation might want to import that package and use the interfaces defined there. 14 | 15 | [plugins]() defines implementations that are used by plugins and automatically compiled into the application. These should not need to be directly reference by workflow specs. 16 | 17 | ## Index 18 | 19 | 20 | 21 | Generated by [gomarkdoc]() 22 | -------------------------------------------------------------------------------- /examples/dsb_sn/workflow/socialnetwork/MediaService.go: -------------------------------------------------------------------------------- 1 | package socialnetwork 2 | 3 | import ( 4 | "context" 5 | "errors" 6 | ) 7 | 8 | // The MediaService interface 9 | type MediaService interface { 10 | // Creates and returns a list of Media objects from the provided `mediaTypes` and `mediaIds` arguments. 11 | // Returns an error if the length of mediaTypes and mediaIds do not match. 12 | ComposeMedia(ctx context.Context, reqID int64, mediaTypes []string, mediaIds []int64) ([]Media, error) 13 | } 14 | 15 | // Implementation of [MediaService] 16 | type MediaServiceImpl struct{} 17 | 18 | // Creates a [MediaService] instance that creates media objects 19 | func NewMediaServiceImpl(ctx context.Context) (MediaService, error) { 20 | return &MediaServiceImpl{}, nil 21 | } 22 | 23 | // Implements MediaService interface 24 | func (m *MediaServiceImpl) ComposeMedia(ctx context.Context, reqID int64, mediaTypes []string, mediaIds []int64) ([]Media, error) { 25 | var medias []Media 26 | 27 | if len(mediaTypes) != len(mediaIds) { 28 | return medias, errors.New("The lengths of media_id list and media_type list are not equal") 29 | } 30 | 31 | for idx, mediaId := range mediaIds { 32 | media := Media{MediaID: mediaId, MediaType: mediaTypes[idx]} 33 | medias = append(medias, media) 34 | } 35 | 36 | return medias, nil 37 | } 38 | -------------------------------------------------------------------------------- /plugins/goproc/ir_goproc.go: -------------------------------------------------------------------------------- 1 | package goproc 2 | 3 | import ( 4 | "github.com/blueprint-uservices/blueprint/blueprint/pkg/ir" 5 | ) 6 | 7 | var generatedModulePrefix = "blueprint/goproc" 8 | 9 | // An IRNode representing a golang process, which is a collection of application-level golang instances. 10 | type Process struct { 11 | /* The implemented build targets for golang.Process nodes */ 12 | filesystemDeployer /* Can be deployed as a basic go process; implemented in deploy.go */ 13 | linuxDeployer /* Can be deployed to linux; implemented in deploylinux.go */ 14 | 15 | InstanceName string 16 | ProcName string 17 | ModuleName string 18 | Nodes []ir.IRNode 19 | Edges []ir.IRNode 20 | metricProvider ir.IRNode 21 | logger ir.IRNode 22 | } 23 | 24 | func newGolangProcessNode(name string) *Process { 25 | proc := Process{ 26 | InstanceName: name, 27 | ProcName: ir.CleanName(name), 28 | } 29 | proc.ModuleName = generatedModulePrefix + "/" + proc.ProcName 30 | return &proc 31 | } 32 | 33 | // Implements ir.IRNode 34 | func (proc *Process) Name() string { 35 | return proc.InstanceName 36 | } 37 | 38 | // Implements ir.IRNode 39 | func (proc *Process) String() string { 40 | return ir.PrettyPrintNamespace(proc.InstanceName, "GolangProcessNode", proc.Edges, proc.Nodes) 41 | } 42 | -------------------------------------------------------------------------------- /examples/dsb_hotel/tests/rateservice_test.go: -------------------------------------------------------------------------------- 1 | package tests 2 | 3 | import ( 4 | "context" 5 | "testing" 6 | 7 | "github.com/blueprint-uservices/blueprint/examples/dsb_hotel/workflow/hotelreservation" 8 | "github.com/blueprint-uservices/blueprint/runtime/core/registry" 9 | "github.com/blueprint-uservices/blueprint/runtime/plugins/simplecache" 10 | "github.com/blueprint-uservices/blueprint/runtime/plugins/simplenosqldb" 11 | "github.com/stretchr/testify/assert" 12 | ) 13 | 14 | var rateServiceRegistry = registry.NewServiceRegistry[hotelreservation.RateService]("rate_service") 15 | 16 | func init() { 17 | rateServiceRegistry.Register("local", func(ctx context.Context) (hotelreservation.RateService, error) { 18 | db, err := simplenosqldb.NewSimpleNoSQLDB(ctx) 19 | if err != nil { 20 | return nil, err 21 | } 22 | cache, err := simplecache.NewSimpleCache(ctx) 23 | if err != nil { 24 | return nil, err 25 | } 26 | return hotelreservation.NewRateServiceImpl(ctx, cache, db) 27 | }) 28 | } 29 | 30 | func TestGetRates(t *testing.T) { 31 | ctx := context.Background() 32 | service, err := rateServiceRegistry.Get(ctx) 33 | assert.NoError(t, err) 34 | 35 | plans, err := service.GetRates(ctx, []string{"1", "2", "3", "12", "9"}, "2015-04-09", "2015-04-10") 36 | assert.NoError(t, err) 37 | assert.Len(t, plans, 5) 38 | } 39 | -------------------------------------------------------------------------------- /examples/dsb_sn/workflow/socialnetwork/DataModels.go: -------------------------------------------------------------------------------- 1 | package socialnetwork 2 | 3 | // The format of a user stored in the user database 4 | type User struct { 5 | UserID int64 6 | FirstName string 7 | LastName string 8 | Username string 9 | PwdHashed string 10 | Salt string 11 | } 12 | 13 | // The format of a media stored as part of a post. 14 | type Media struct { 15 | MediaID int64 16 | MediaType string 17 | } 18 | 19 | // The format of a url stored in the url-shorten database 20 | type URL struct { 21 | ShortenedUrl string 22 | ExpandedUrl string 23 | } 24 | 25 | // The format of a usermention stored as part of a post 26 | type UserMention struct { 27 | UserID int64 28 | Username string 29 | } 30 | 31 | // The format of a creator stored as part of a post 32 | type Creator struct { 33 | UserID int64 34 | Username string 35 | } 36 | 37 | // The type of the post. 38 | type PostType int64 39 | 40 | // Enums aren't supported atm. So just use integers instead. 41 | const ( 42 | POST int64 = iota 43 | REPOST 44 | REPLY 45 | DM 46 | ) 47 | 48 | type Post struct { 49 | PostID int64 50 | Creator Creator 51 | ReqID int64 52 | Text string 53 | UserMentions []UserMention 54 | Medias []Media 55 | Urls []URL 56 | Timestamp int64 57 | PostType int64 58 | } 59 | -------------------------------------------------------------------------------- /plugins/linuxcontainer/ir.go: -------------------------------------------------------------------------------- 1 | package linuxcontainer 2 | 3 | import ( 4 | "github.com/blueprint-uservices/blueprint/blueprint/pkg/ir" 5 | ) 6 | 7 | /* 8 | linuxcontainer.Container is a node that represents a collection of runnable linux processes. 9 | It can contain any number of other process.Node IRNodes. When it's compiled, the goproc.Process 10 | will generate a run script that instantiates all contained processes. 11 | */ 12 | 13 | type Container struct { 14 | ir.IRNode 15 | 16 | /* The implemented build targets for linuxcontainer.Container nodes */ 17 | filesystemDeployer /* Can be deployed as a basic collection of processes; implemented in deploy.go */ 18 | dockerDeployer /* Can be deployed as a docker container; implemented in deploydocker.go */ 19 | 20 | InstanceName string 21 | ImageName string 22 | Edges []ir.IRNode 23 | Nodes []ir.IRNode 24 | } 25 | 26 | func newLinuxContainerNode(name string) *Container { 27 | node := Container{ 28 | InstanceName: name, 29 | ImageName: ir.CleanName(name), 30 | } 31 | return &node 32 | } 33 | 34 | // Implements ir.IRNode 35 | func (ctr *Container) Name() string { 36 | return ctr.InstanceName 37 | } 38 | 39 | // Implements ir.IRNode 40 | func (ctr *Container) String() string { 41 | return ir.PrettyPrintNamespace(ctr.InstanceName, "LinuxContainer", ctr.Edges, ctr.Nodes) 42 | } 43 | -------------------------------------------------------------------------------- /runtime/plugins/jaeger/trace.go: -------------------------------------------------------------------------------- 1 | // Package jaeger implements a tracer [backend.Tracer] client interface for the jaeger tracer. 2 | package jaeger 3 | 4 | import ( 5 | "context" 6 | 7 | jaeger_exporter "go.opentelemetry.io/otel/exporters/jaeger" 8 | tracesdk "go.opentelemetry.io/otel/sdk/trace" 9 | "go.opentelemetry.io/otel/trace" 10 | ) 11 | 12 | // JaegerTracer implements the runtime backend instance that implements the backend/trace.Tracer interface. 13 | // REQUIRED: A functional backend running the jaeger collector. 14 | type JaegerTracer struct { 15 | tp *tracesdk.TracerProvider 16 | } 17 | 18 | // Returns a new instance of JaegerTracer. 19 | // Configures opentelemetry to export jaeger traces to the jaeger collector hosted at address `addr`. 20 | func NewJaegerTracer(ctx context.Context, addr string) (*JaegerTracer, error) { 21 | exp, err := jaeger_exporter.New(jaeger_exporter.WithCollectorEndpoint(jaeger_exporter.WithEndpoint("http://" + addr + "/api/traces"))) 22 | if err != nil { 23 | return nil, err 24 | } 25 | tp := tracesdk.NewTracerProvider( 26 | // Always be sure to batch in production. 27 | tracesdk.WithBatcher(exp), 28 | ) 29 | return &JaegerTracer{tp}, nil 30 | } 31 | 32 | // Implements the backend/trace interface. 33 | func (t *JaegerTracer) GetTracerProvider(ctx context.Context) (trace.TracerProvider, error) { 34 | return t.tp, nil 35 | } 36 | -------------------------------------------------------------------------------- /examples/dsb_hotel/tests/userservice_test.go: -------------------------------------------------------------------------------- 1 | package tests 2 | 3 | import ( 4 | "context" 5 | "testing" 6 | 7 | "github.com/blueprint-uservices/blueprint/examples/dsb_hotel/workflow/hotelreservation" 8 | "github.com/blueprint-uservices/blueprint/runtime/core/registry" 9 | "github.com/blueprint-uservices/blueprint/runtime/plugins/simplenosqldb" 10 | "github.com/stretchr/testify/assert" 11 | ) 12 | 13 | // Tests acquire a UserService instance using a service registry. 14 | // This enables us to run local unit tests, while also enabling 15 | // the Blueprint test plugin to auto-generate tests 16 | // for different deployments when compiling an application. 17 | var userServiceRegistry = registry.NewServiceRegistry[hotelreservation.UserService]("user_service") 18 | 19 | func init() { 20 | 21 | userServiceRegistry.Register("local", func(ctx context.Context) (hotelreservation.UserService, error) { 22 | db, err := simplenosqldb.NewSimpleNoSQLDB(ctx) 23 | if err != nil { 24 | return nil, err 25 | } 26 | 27 | return hotelreservation.NewUserServiceImpl(ctx, db) 28 | }) 29 | } 30 | 31 | func TestCheckUser(t *testing.T) { 32 | ctx := context.Background() 33 | service, err := userServiceRegistry.Get(ctx) 34 | assert.NoError(t, err) 35 | 36 | valid, err := service.CheckUser(ctx, "Cornell_1", "1111111111") 37 | assert.NoError(t, err) 38 | assert.True(t, valid) 39 | } 40 | -------------------------------------------------------------------------------- /runtime/plugins/xtrace/xtrace_iface.go: -------------------------------------------------------------------------------- 1 | package xtrace 2 | 3 | import ( 4 | "context" 5 | 6 | "github.com/tracingplane/tracingplane-go/tracingplane" 7 | ) 8 | 9 | // Represents the XTrace tracer interface exposed to applications and used by the xtrace plugin. 10 | type XTracer interface { 11 | // Creates an event in the current trace with `msg` as content. 12 | Log(ctx context.Context, msg string) (context.Context, error) 13 | // Creates an event in the current trace with `msg` as content along with `tags` as the keywords for the text. 14 | LogWithTags(ctx context.Context, msg string, tags ...string) (context.Context, error) 15 | // Starts a new trace with given `tags` 16 | StartTask(ctx context.Context, tags ...string) (context.Context, error) 17 | // Stops the current running trace. 18 | StopTask(ctx context.Context) (context.Context, error) 19 | // Merges the incoming `other` baggage into the current trace's existing baggage. 20 | Merge(ctx context.Context, other tracingplane.BaggageContext) (context.Context, error) 21 | // Sets the current trace's `baggage` to the one provided. 22 | Set(ctx context.Context, baggage tracingplane.BaggageContext) (context.Context, error) 23 | // Returns the current trace's baggage. 24 | Get(ctx context.Context) (tracingplane.BaggageContext, error) 25 | // Returns true if there is an ongoing trace. 26 | IsTracing(ctx context.Context) (bool, error) 27 | } 28 | -------------------------------------------------------------------------------- /plugins/http/httpcodegen/README.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | # httpcodegen 4 | 5 | ```go 6 | import "github.com/blueprint-uservices/blueprint/plugins/http/httpcodegen" 7 | ``` 8 | 9 | ## Index 10 | 11 | - [func GenerateClient\(builder golang.ModuleBuilder, service \*gocode.ServiceInterface, outputPackage string\) error](<#GenerateClient>) 12 | - [func GenerateServerHandler\(builder golang.ModuleBuilder, service \*gocode.ServiceInterface, outputPackage string\) error](<#GenerateServerHandler>) 13 | 14 | 15 | 16 | ## func [GenerateClient]() 17 | 18 | ```go 19 | func GenerateClient(builder golang.ModuleBuilder, service *gocode.ServiceInterface, outputPackage string) error 20 | ``` 21 | 22 | This function is used by the HTTP plugin to generate the client\-side HTTP service 23 | 24 | 25 | ## func [GenerateServerHandler]() 26 | 27 | ```go 28 | func GenerateServerHandler(builder golang.ModuleBuilder, service *gocode.ServiceInterface, outputPackage string) error 29 | ``` 30 | 31 | This function is used by the HTTP plugin to generate the server\-side HTTP service. 32 | 33 | Generated by [gomarkdoc]() 34 | -------------------------------------------------------------------------------- /runtime/core/backend/cache.go: -------------------------------------------------------------------------------- 1 | package backend 2 | 3 | import "context" 4 | 5 | // Represents a key-value cache. 6 | type Cache interface { 7 | // Store a key-value pair in the cache 8 | Put(ctx context.Context, key string, value interface{}) error 9 | 10 | // Retrieves a value from the cache. 11 | // val should be a pointer in which the value will be stored, e.g. 12 | // 13 | // var value interface{} 14 | // cache.Get(ctx, "key", &value) 15 | // 16 | // Reports whether the key existed in the cache 17 | Get(ctx context.Context, key string, val interface{}) (bool, error) 18 | 19 | // Store multiple key-value pairs in the cache. 20 | // keys and values must have the same length or an error will be returned 21 | Mset(ctx context.Context, keys []string, values []interface{}) error 22 | 23 | // Retrieve the values for multiple keys from the cache. 24 | // keys and values must have the same length or an error will be returned 25 | // values is an array of pointers to which the values will be stored, e.g. 26 | // 27 | // var a string 28 | // var b int64 29 | // cache.Mget(ctx, []string{"a", "b"}, []any{&a, &b}) 30 | Mget(ctx context.Context, keys []string, values []interface{}) error 31 | 32 | // Delete from the cache 33 | Delete(ctx context.Context, key string) error 34 | 35 | // Treats the value mapped to key as an integer, and increments it 36 | Incr(ctx context.Context, key string) (int64, error) 37 | } 38 | -------------------------------------------------------------------------------- /blueprint/pkg/blueprint/stringutil/name.go: -------------------------------------------------------------------------------- 1 | package stringutil 2 | 3 | import ( 4 | "regexp" 5 | "strconv" 6 | "strings" 7 | "unicode" 8 | ) 9 | 10 | // Returns s with the first letter converted to uppercase. 11 | func Capitalize(s string) string { 12 | r := []rune(s) 13 | return string(append([]rune{unicode.ToUpper(r[0])}, r[1:]...)) 14 | } 15 | 16 | var r = regexp.MustCompile(`[^a-zA-Z0-9 ]+`) 17 | 18 | // Returns name with only alphanumeric characters and all other 19 | // symbols converted to underscores. 20 | // 21 | // CleanName is primarily used by plugins to convert user-defined 22 | // service names into names that are valid as e.g. environment variables, 23 | // command line arguments, etc. 24 | func CleanName(name string) string { 25 | cleanName := r.ReplaceAllString(name, "_") 26 | for len(cleanName) > 0 { 27 | if _, err := strconv.Atoi(cleanName[0:1]); err != nil { 28 | return cleanName 29 | } else { 30 | cleanName = cleanName[1:] 31 | } 32 | } 33 | return cleanName 34 | } 35 | 36 | // If s ends with the provided suffix, removes the suffix from s and 37 | // replaces it with replacement. If s does not end with the provided 38 | // suffix, then simply returns s . replacement 39 | func ReplaceSuffix(s string, suffix string, replacement string) string { 40 | if before, hasSuffix := strings.CutSuffix(s, suffix); hasSuffix { 41 | return before + replacement 42 | } 43 | return s + "." + replacement 44 | } 45 | -------------------------------------------------------------------------------- /examples/dsb_hotel/workflow/hotelreservation/SearchService.go: -------------------------------------------------------------------------------- 1 | package hotelreservation 2 | 3 | import ( 4 | "context" 5 | ) 6 | 7 | // SearchService implements the Search service from hotel reservation 8 | type SearchService interface { 9 | // Returns the list of available hotels based on a given location for the desired date range 10 | Nearby(ctx context.Context, lat float64, lon float64, inDate string, outDate string) ([]string, error) 11 | } 12 | 13 | // Implementation of the Search Service 14 | type SearchServiceImpl struct { 15 | geoService GeoService 16 | rateService RateService 17 | } 18 | 19 | // Creates and Returns a new SearchService object 20 | func NewSearchServiceImpl(ctx context.Context, geoService GeoService, rateService RateService) (SearchService, error) { 21 | return &SearchServiceImpl{geoService: geoService, rateService: rateService}, nil 22 | } 23 | 24 | func (s *SearchServiceImpl) Nearby(ctx context.Context, lat float64, lon float64, inDate string, outDate string) ([]string, error) { 25 | var nearby_hotels []string 26 | nearby_hotel_ids, err := s.geoService.Nearby(ctx, lat, lon) 27 | if err != nil { 28 | return nearby_hotels, err 29 | } 30 | 31 | rates, err := s.rateService.GetRates(ctx, nearby_hotel_ids, inDate, outDate) 32 | if err != nil { 33 | return nearby_hotels, err 34 | } 35 | for _, rate := range rates { 36 | nearby_hotels = append(nearby_hotels, rate.HotelID) 37 | } 38 | return nearby_hotels, nil 39 | } 40 | -------------------------------------------------------------------------------- /plugins/linuxcontainer/defaults.go: -------------------------------------------------------------------------------- 1 | package linuxcontainer 2 | 3 | import ( 4 | "github.com/blueprint-uservices/blueprint/blueprint/pkg/blueprint/ioutil" 5 | "github.com/blueprint-uservices/blueprint/blueprint/pkg/ir" 6 | "github.com/blueprint-uservices/blueprint/plugins/linux" 7 | ) 8 | 9 | // RegisterAsDefaultBuilder should be invoked by a wiring spec if it wishes to use linuxcontainer as the default 10 | // way of combining process instances. 11 | // 12 | // If you are using the [cmdbuilder], then linuxcontainer is automatically set as the default builder and you 13 | // do not need to call this function. 14 | // 15 | // Default builders are responsible for building any process instances that exist in a wiring spec but aren't 16 | // explicitly added to a container within that wiring spec. The Blueprint compiler groups these 17 | // "floating" process instances into a default linux container with the name "linux". 18 | // 19 | // [cmdbuilder]: https://github.com/Blueprint-uServices/blueprint/tree/main/plugins/cmdbuilder 20 | func RegisterAsDefaultBuilder() { 21 | ir.RegisterDefaultNamespace[linux.Process]("linux", buildDefaultLinuxWorkspace) 22 | } 23 | 24 | func buildDefaultLinuxWorkspace(outputDir string, nodes []ir.IRNode) error { 25 | ctr := newLinuxContainerNode("linux") 26 | ctr.Nodes = nodes 27 | ctrDir, err := ioutil.CreateNodeDir(outputDir, "linux") 28 | if err != nil { 29 | return err 30 | } 31 | return ctr.GenerateArtifacts(ctrDir) 32 | } 33 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright 2024 The Blueprint Authors 2 | 3 | Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 4 | 5 | 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 6 | 7 | 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. 8 | 9 | 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. 10 | 11 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS “AS IS” AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -------------------------------------------------------------------------------- /blueprint/LICENSE: -------------------------------------------------------------------------------- 1 | Copyright 2024 Max Planck Institute for Software Systems 2 | 3 | Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 4 | 5 | 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 6 | 7 | 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. 8 | 9 | 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. 10 | 11 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS “AS IS” AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -------------------------------------------------------------------------------- /examples/dsb_sn/tests/go.mod: -------------------------------------------------------------------------------- 1 | module github.com/blueprint-uservices/blueprint/examples/dsb_sn/tests 2 | 3 | go 1.22 4 | 5 | toolchain go1.22.1 6 | 7 | require github.com/blueprint-uservices/blueprint/runtime v0.0.0-20240619221802-d064c5861c1e 8 | 9 | require ( 10 | github.com/blueprint-uservices/blueprint/examples/dsb_sn/workflow v0.0.0 11 | github.com/stretchr/testify v1.9.0 12 | go.mongodb.org/mongo-driver v1.15.0 13 | ) 14 | 15 | require ( 16 | github.com/davecgh/go-spew v1.1.1 // indirect 17 | github.com/dgrijalva/jwt-go v3.2.0+incompatible // indirect 18 | github.com/golang/snappy v0.0.4 // indirect 19 | github.com/klauspost/compress v1.17.8 // indirect 20 | github.com/montanaflynn/stats v0.7.1 // indirect 21 | github.com/pmezard/go-difflib v1.0.0 // indirect 22 | github.com/xdg-go/pbkdf2 v1.0.0 // indirect 23 | github.com/xdg-go/scram v1.1.2 // indirect 24 | github.com/xdg-go/stringprep v1.0.4 // indirect 25 | github.com/youmark/pkcs8 v0.0.0-20240424034433-3c2c7870ae76 // indirect 26 | go.opentelemetry.io/otel v1.26.0 // indirect 27 | go.opentelemetry.io/otel/metric v1.26.0 // indirect 28 | go.opentelemetry.io/otel/trace v1.26.0 // indirect 29 | golang.org/x/crypto v0.22.0 // indirect 30 | golang.org/x/exp v0.0.0-20240416160154-fe59bbe5cc7f // indirect 31 | golang.org/x/sync v0.7.0 // indirect 32 | golang.org/x/text v0.14.0 // indirect 33 | gopkg.in/yaml.v3 v3.0.1 // indirect 34 | ) 35 | 36 | replace github.com/blueprint-uservices/blueprint/examples/dsb_sn/workflow => ../workflow 37 | -------------------------------------------------------------------------------- /plugins/LICENSE: -------------------------------------------------------------------------------- 1 | Copyright 2024 Max Planck Institute for Software Systems 2 | 3 | Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 4 | 5 | 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 6 | 7 | 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. 8 | 9 | 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. 10 | 11 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS “AS IS” AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -------------------------------------------------------------------------------- /runtime/LICENSE: -------------------------------------------------------------------------------- 1 | Copyright 2024 Max Planck Institute for Software Systems 2 | 3 | Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 4 | 5 | 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 6 | 7 | 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. 8 | 9 | 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. 10 | 11 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS “AS IS” AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -------------------------------------------------------------------------------- /plugins/dockercompose/defaults.go: -------------------------------------------------------------------------------- 1 | package dockercompose 2 | 3 | import ( 4 | "github.com/blueprint-uservices/blueprint/blueprint/pkg/blueprint/ioutil" 5 | "github.com/blueprint-uservices/blueprint/blueprint/pkg/ir" 6 | "github.com/blueprint-uservices/blueprint/plugins/docker" 7 | ) 8 | 9 | // RegisterAsDefaultBuilder should be invoked by a wiring spec if it wishes to use docker-compose as the default 10 | // way of combining container instances. 11 | // 12 | // If you are using the [cmdbuilder], then docker-compose is automatically set as the default builder and you 13 | // do not need to call this function again. 14 | // 15 | // Default builders are responsible for building any container instances that exist in a wiring spec but aren't 16 | // explicitly added to a container deployment within that wiring spec. The Blueprint compiler groups these 17 | // "floating" container instances into a default dockercompose deployment with the name "docker". 18 | // 19 | // [cmdbuilder]: https://github.com/Blueprint-uServices/blueprint/tree/main/plugins/cmdbuilder 20 | func RegisterAsDefaultBuilder() { 21 | ir.RegisterDefaultNamespace[docker.Container]("containerdeployment", buildDefaultContainerWorkspace) 22 | } 23 | 24 | func buildDefaultContainerWorkspace(outputDir string, nodes []ir.IRNode) error { 25 | ctr := &Deployment{DeploymentName: "docker", Nodes: nodes} 26 | subdir, err := ioutil.CreateNodeDir(outputDir, "docker") 27 | if err != nil { 28 | return err 29 | } 30 | return ctr.GenerateArtifacts(subdir) 31 | } 32 | -------------------------------------------------------------------------------- /examples/sockshop/workload/workloadgen/workload.go: -------------------------------------------------------------------------------- 1 | package workloadgen 2 | 3 | import ( 4 | "context" 5 | "fmt" 6 | "time" 7 | 8 | "flag" 9 | 10 | "github.com/blueprint-uservices/blueprint/examples/sockshop/workflow/frontend" 11 | ) 12 | 13 | // The WorkloadGen interface, which the Blueprint compiler will treat as a 14 | // Workflow service 15 | type SimpleWorkload interface { 16 | ImplementsSimpleWorkload(context.Context) error 17 | } 18 | 19 | // workloadGen implementation 20 | type workloadGen struct { 21 | SimpleWorkload 22 | 23 | frontend frontend.Frontend 24 | } 25 | 26 | var myarg = flag.Int("myarg", 12345, "help message for myarg") 27 | 28 | func NewSimpleWorkload(ctx context.Context, frontend frontend.Frontend) (SimpleWorkload, error) { 29 | return &workloadGen{frontend: frontend}, nil 30 | } 31 | 32 | func (s *workloadGen) Run(ctx context.Context) error { 33 | _, err := s.frontend.LoadCatalogue(ctx) 34 | if err != nil { 35 | fmt.Println("Failed to load catalogue") 36 | return err 37 | } 38 | fmt.Printf("myarg is %v\n", *myarg) 39 | ticker := time.NewTicker(1 * time.Second) 40 | for { 41 | select { 42 | case <-ctx.Done(): 43 | return nil 44 | case t := <-ticker.C: 45 | fmt.Println("Tick at", t) 46 | items, err := s.frontend.ListItems(ctx, []string{}, "", 1, 100) 47 | if err != nil { 48 | return err 49 | } 50 | fmt.Println("Got", len(items), "items!") 51 | } 52 | } 53 | } 54 | 55 | func (s *workloadGen) ImplementsSimpleWorkload(context.Context) error { 56 | return nil 57 | } 58 | -------------------------------------------------------------------------------- /plugins/govector/example_logs/README.md: -------------------------------------------------------------------------------- 1 | # Sample GoVector Logs 2 | 3 | This directory contains sample GoVector logs generated from the [leaf](https://github.com/Blueprint-uServices/blueprint/tree/main/examples/leaf) application variant generated from the `govector` wiring specification. 4 | 5 | ## Individual Log Files 6 | 7 | + `leaf_process.goveclogger-Log.txt`: Log file generated from the process containing the leaf service instance. 8 | + `nonleaf_process.goveclogger-Log.txt`: Log file generated from the process containing the nonleaf service instance. 9 | + `shiviz_all_services.log`: [ShiViz](https://bestchai.bitbucket.io/shiviz/)-compatible log file generated using the [GoVector](https://github.com/DistributedClocks/GoVector) command line tool. 10 | 11 | ## Log Entry Format 12 | 13 | Each log entry follows the following format: 14 | 15 | ``` 16 | $process.id {$process.vector_clock} 17 | $log.message 18 | ``` 19 | 20 | Here is a detailed explanation for each field in the log entry: 21 | 22 | + `$process.id`: corresponds to the unique ID of the process. The `id` for each process is generated as `process_name.goveclogger`. For example, the `id` for a process with name `p1` would be `p1.goveclogger`. 23 | + `$process.vector_clock`: string representation of the vector clock timestamp at the time the log entry was generated. The vector clock is represented as a tuple of key-value pairs with each process's id as the key and its locally available logical time as the value. 24 | + `$log.message`: the message accompanying the log entry. 25 | -------------------------------------------------------------------------------- /examples/sockshop/tests/paymentservice_test.go: -------------------------------------------------------------------------------- 1 | package tests 2 | 3 | import ( 4 | "context" 5 | "testing" 6 | 7 | "github.com/blueprint-uservices/blueprint/examples/sockshop/workflow/payment" 8 | "github.com/blueprint-uservices/blueprint/runtime/core/registry" 9 | "github.com/stretchr/testify/assert" 10 | ) 11 | 12 | // Tests acquire a PaymentService instance using a service registry. 13 | // This enables us to run local unit tests, while also enabling 14 | // the Blueprint test plugin to auto-generate tests 15 | // for different deployments when compiling an application. 16 | var paymentServiceRegistry = registry.NewServiceRegistry[payment.PaymentService]("payment_service") 17 | 18 | func init() { 19 | // If the tests are run locally, we fall back to this PaymentService implementation 20 | paymentServiceRegistry.Register("local", func(ctx context.Context) (payment.PaymentService, error) { 21 | return payment.NewPaymentService(ctx, "500") 22 | }) 23 | } 24 | 25 | // We write the service test as a single test because we don't want to tear down and 26 | // spin up the Mongo backends between tests, so state will persist in the database 27 | // between tests. 28 | func TestPaymentService(t *testing.T) { 29 | ctx := context.Background() 30 | service, err := paymentServiceRegistry.Get(ctx) 31 | assert.NoError(t, err) 32 | 33 | rsp, err := service.Authorise(ctx, 1000) 34 | assert.NoError(t, err) 35 | assert.False(t, rsp.Authorised) 36 | 37 | rsp, err = service.Authorise(ctx, 100) 38 | assert.NoError(t, err) 39 | assert.True(t, rsp.Authorised) 40 | } 41 | -------------------------------------------------------------------------------- /test/workflow/cache/services.go: -------------------------------------------------------------------------------- 1 | package cache 2 | 3 | import ( 4 | ctxx "context" 5 | 6 | "github.com/blueprint-uservices/blueprint/runtime/core/backend" 7 | "github.com/blueprint-uservices/blueprint/test/workflow/workflow" 8 | ) 9 | 10 | /* 11 | Implements the services from ../workflow using a cache 12 | */ 13 | 14 | /* 15 | Service implementation structs 16 | */ 17 | type ( 18 | TestLeafServiceImplWithCache struct { 19 | workflow.TestLeafService 20 | Cache backend.Cache 21 | } 22 | ) 23 | 24 | /* 25 | Constructors 26 | */ 27 | 28 | func NewTestLeafServiceImplWithCache(ctx ctxx.Context, cache backend.Cache) (*TestLeafServiceImplWithCache, error) { 29 | return &TestLeafServiceImplWithCache{Cache: cache}, nil 30 | } 31 | 32 | /* 33 | Interface method bodies 34 | */ 35 | 36 | func (l *TestLeafServiceImplWithCache) HelloNothing(ctx ctxx.Context) error { 37 | return nil 38 | } 39 | 40 | func (l *TestLeafServiceImplWithCache) HelloInt(ctx ctxx.Context, a int16) (int32, error) { 41 | err := l.Cache.Put(ctx, "myint", int32(a)) 42 | if err != nil { 43 | return 0, err 44 | } 45 | var myint int32 46 | _, err = l.Cache.Get(ctx, "myint", &myint) 47 | return myint, err 48 | } 49 | 50 | func (l *TestLeafServiceImplWithCache) HelloObject(ctx ctxx.Context, obj workflow.TestLeafObject) (*workflow.TestLeafObject, error) { 51 | var count int64 52 | _, err := l.Cache.Get(ctx, "objectcount", &count) 53 | if err != nil { 54 | return nil, err 55 | } 56 | count += 10 57 | obj.Count = int(count) 58 | return &obj, l.Cache.Put(ctx, "objectcount", count) 59 | } 60 | -------------------------------------------------------------------------------- /test/workflow/nosqldb/services.go: -------------------------------------------------------------------------------- 1 | package nosqldb 2 | 3 | import ( 4 | ctxx "context" 5 | 6 | "github.com/blueprint-uservices/blueprint/runtime/core/backend" 7 | "github.com/blueprint-uservices/blueprint/test/workflow/workflow" 8 | ) 9 | 10 | /* 11 | Implements the services from ../workflow using a cache 12 | */ 13 | 14 | /* 15 | Service implementation structs 16 | */ 17 | type ( 18 | TestLeafServiceImplWithDB struct { 19 | workflow.TestLeafService 20 | Cache backend.Cache 21 | Database backend.NoSQLDatabase 22 | } 23 | ) 24 | 25 | /* 26 | Constructors 27 | */ 28 | 29 | func NewTestLeafServiceImplWithDB(ctx ctxx.Context, cache backend.Cache, db backend.NoSQLDatabase) (*TestLeafServiceImplWithDB, error) { 30 | return &TestLeafServiceImplWithDB{Cache: cache, Database: db}, nil 31 | } 32 | 33 | /* 34 | Interface method bodies 35 | */ 36 | 37 | func (l *TestLeafServiceImplWithDB) HelloNothing(ctx ctxx.Context) error { 38 | return nil 39 | } 40 | 41 | func (l *TestLeafServiceImplWithDB) HelloInt(ctx ctxx.Context, a int16) (int32, error) { 42 | err := l.Cache.Put(ctx, "myint", int32(a)) 43 | if err != nil { 44 | return 0, err 45 | } 46 | var myint int32 47 | _, err = l.Cache.Get(ctx, "myint", &myint) 48 | return myint, err 49 | } 50 | 51 | func (l *TestLeafServiceImplWithDB) HelloObject(ctx ctxx.Context, obj workflow.TestLeafObject) (*workflow.TestLeafObject, error) { 52 | var count int64 53 | _, err := l.Cache.Get(ctx, "objectcount", &count) 54 | if err != nil { 55 | return nil, err 56 | } 57 | count += 10 58 | obj.Count = int(count) 59 | return &obj, l.Cache.Put(ctx, "objectcount", count) 60 | } 61 | -------------------------------------------------------------------------------- /examples/dsb_hotel/tests/recommendationservice_test.go: -------------------------------------------------------------------------------- 1 | package tests 2 | 3 | import ( 4 | "context" 5 | "testing" 6 | 7 | "github.com/blueprint-uservices/blueprint/examples/dsb_hotel/workflow/hotelreservation" 8 | "github.com/blueprint-uservices/blueprint/runtime/core/registry" 9 | "github.com/blueprint-uservices/blueprint/runtime/plugins/simplenosqldb" 10 | "github.com/stretchr/testify/assert" 11 | ) 12 | 13 | var recommendationServiceRegistry = registry.NewServiceRegistry[hotelreservation.RecommendationService]("recommendation_service") 14 | 15 | func init() { 16 | 17 | recommendationServiceRegistry.Register("local", func(ctx context.Context) (hotelreservation.RecommendationService, error) { 18 | db, err := simplenosqldb.NewSimpleNoSQLDB(ctx) 19 | if err != nil { 20 | return nil, err 21 | } 22 | 23 | return hotelreservation.NewRecommendationServiceImpl(ctx, db) 24 | }) 25 | } 26 | 27 | func TestGetRecommendations(t *testing.T) { 28 | ctx := context.Background() 29 | service, err := recommendationServiceRegistry.Get(ctx) 30 | assert.NoError(t, err) 31 | 32 | // Check require = "dis" 33 | dis_hotels, err := service.GetRecommendations(ctx, "dis", 37.7835, -122.41) 34 | assert.NoError(t, err) 35 | assert.True(t, len(dis_hotels) > 0) 36 | // Check require = "rate" 37 | rate_hotels, err := service.GetRecommendations(ctx, "rate", 37.7835, -122.41) 38 | assert.NoError(t, err) 39 | assert.True(t, len(rate_hotels) > 0) 40 | // Check require = "price" 41 | price_hotels, err := service.GetRecommendations(ctx, "price", 37.7835, -122.41) 42 | assert.NoError(t, err) 43 | assert.True(t, len(price_hotels) > 0) 44 | } 45 | -------------------------------------------------------------------------------- /docs/dev/autodoc.md: -------------------------------------------------------------------------------- 1 | # Generating code documentation in READMEs 2 | 3 | Code documentation is generated using `gomarkdoc`. After making substantial changes to the structs or funcs in a package, you may want to regenerate the documentation for the package. 4 | 5 | Install `gomarkdoc`: 6 | 7 | ``` 8 | go install github.com/princjef/gomarkdoc/cmd/gomarkdoc@latest 9 | ``` 10 | 11 | Run `gomarkdoc` for a particular package; in this case we use the `blueprint` package as an example 12 | ``` 13 | cd blueprint 14 | gomarkdoc --output '{{.Dir}}/README.md' --repository.default-branch main --repository.url https://github.com/blueprint-uservices/blueprint ./... 15 | ``` 16 | 17 | Currently we use `gomarkdoc` to auto-generate READMEs for the following: 18 | ``` 19 | blueprint 20 | plugins 21 | runtime 22 | examples/*/workflow 23 | examples/*/wiring 24 | examples/*/workload 25 | ``` 26 | 27 | The following command, when run from the root of the blueprint repository, refreshes all documentation: 28 | 29 | ``` 30 | gomarkdoc --output '{{.Dir}}/README.md' --repository.default-branch main --repository.url https://github.com/blueprint-uservices/blueprint ./blueprint/... ./plugins/... ./runtime/... ./examples/sockshop/wiring/... ./examples/sockshop/workflow/... ./examples/sockshop/workload/... ./examples/dsb_hotel/wiring/... ./examples/dsb_hotel/workflow/... ./examples/dsb_sn/wiring/... ./examples/dsb_sn/workflow/... ./examples/leaf/wiring/... ./examples/leaf/workflow/... ./examples/train_ticket/wiring/... ./examples/train_ticket/workflow/... 31 | ``` 32 | 33 | For further information see the [gomarkdoc](https://github.com/princjef/gomarkdoc/tree/master) github repo. -------------------------------------------------------------------------------- /plugins/goproc/defaults.go: -------------------------------------------------------------------------------- 1 | package goproc 2 | 3 | import ( 4 | "github.com/blueprint-uservices/blueprint/blueprint/pkg/blueprint/ioutil" 5 | "github.com/blueprint-uservices/blueprint/blueprint/pkg/ir" 6 | "github.com/blueprint-uservices/blueprint/plugins/golang" 7 | ) 8 | 9 | // RegisterAsDefaultBuilder should be invoked by a wiring spec if it wishes to use goproc as the default 10 | // way of combining golang instances. 11 | // 12 | // If you are using the [cmdbuilder], then goproc is automatically set as the default builder and you 13 | // do not need to call this function. 14 | // 15 | // Default builders are responsible for building any golang instances that exist in a wiring spec but aren't 16 | // explicitly added to a goproc within that wiring spec. The Blueprint compiler groups these 17 | // "floating" golang instances into a default golang process with the name "goproc". 18 | // 19 | // [cmdbuilder]: https://github.com/Blueprint-uServices/blueprint/tree/main/plugins/cmdbuilder 20 | func RegisterAsDefaultBuilder() { 21 | /* any unattached golang nodes will be instantiated in a "default" golang workspace */ 22 | ir.RegisterDefaultNamespace[golang.Node]("goproc", buildDefaultGolangWorkspace) 23 | } 24 | 25 | /* 26 | If the Blueprint application contains any floating golang nodes, they get 27 | built by this function. 28 | */ 29 | func buildDefaultGolangWorkspace(outputDir string, nodes []ir.IRNode) error { 30 | proc := newGolangProcessNode("golang") 31 | proc.Nodes = nodes 32 | procDir, err := ioutil.CreateNodeDir(outputDir, "golang") 33 | if err != nil { 34 | return err 35 | } 36 | return proc.GenerateArtifacts(procDir) 37 | } 38 | -------------------------------------------------------------------------------- /blueprint/pkg/coreplugins/backend/README.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | # backend 4 | 5 | ```go 6 | import "github.com/blueprint-uservices/blueprint/blueprint/pkg/coreplugins/backend" 7 | ``` 8 | 9 | Package backend provides IR node interfaces for common backend components. 10 | 11 | ## Index 12 | 13 | - [type Cache](<#Cache>) 14 | - [type NoSQLDB](<#NoSQLDB>) 15 | - [type Queue](<#Queue>) 16 | - [type RelDB](<#RelDB>) 17 | 18 | 19 | 20 | ## type [Cache]() 21 | 22 | 23 | 24 | ```go 25 | type Cache interface { 26 | ir.IRNode 27 | service.ServiceNode 28 | } 29 | ``` 30 | 31 | 32 | ## type [NoSQLDB]() 33 | 34 | 35 | 36 | ```go 37 | type NoSQLDB interface { 38 | ir.IRNode 39 | service.ServiceNode 40 | } 41 | ``` 42 | 43 | 44 | ## type [Queue]() 45 | 46 | 47 | 48 | ```go 49 | type Queue interface { 50 | ir.IRNode 51 | service.ServiceNode 52 | } 53 | ``` 54 | 55 | 56 | ## type [RelDB]() 57 | 58 | 59 | 60 | ```go 61 | type RelDB interface { 62 | ir.IRNode 63 | service.ServiceNode 64 | } 65 | ``` 66 | 67 | Generated by [gomarkdoc]() 68 | -------------------------------------------------------------------------------- /plugins/goproc/linuxgen/goproc_runfunc.go: -------------------------------------------------------------------------------- 1 | // Package linuxgen implements code generation for the goproc plugin. 2 | package linuxgen 3 | 4 | import ( 5 | "github.com/blueprint-uservices/blueprint/blueprint/pkg/ir" 6 | "github.com/blueprint-uservices/blueprint/plugins/linuxcontainer/linuxgen" 7 | ) 8 | 9 | /* 10 | Generates command-line function to run a goproc 11 | */ 12 | func GenerateRunFunc(procName string, args ...ir.IRNode) (string, error) { 13 | templateArgs := runFuncTemplateArgs{ 14 | Name: procName, 15 | Args: args, 16 | } 17 | return linuxgen.ExecuteTemplate("goproc_runfunc", runFuncTemplate, templateArgs) 18 | } 19 | 20 | /* 21 | Generates command-line function to run a goproc that has been built to a binary 22 | using `go build` 23 | */ 24 | func GenerateBinaryRunFunc(procName string, args ...ir.IRNode) (string, error) { 25 | templateArgs := runFuncTemplateArgs{ 26 | Name: procName, 27 | Args: args, 28 | } 29 | return linuxgen.ExecuteTemplate("goproc_binaryrunfunc", binaryRunFuncTemplate, templateArgs) 30 | } 31 | 32 | type runFuncTemplateArgs struct { 33 | Name string 34 | Args []ir.IRNode 35 | } 36 | 37 | var binaryRunFuncTemplate = ` 38 | run_{{RunFuncName .Name}} { 39 | cd {{.Name}} 40 | ./{{.Name}} 41 | {{- range $i, $arg := .Args}} --{{$arg.Name}}=${{EnvVarName $arg.Name}}{{end}} & 42 | {{EnvVarName .Name}}=$! 43 | return $? 44 | }` 45 | 46 | var runFuncTemplate = ` 47 | run_{{RunFuncName .Name}} { 48 | export CGO_ENABLED=1 49 | cd {{.Name}}/{{.Name}} 50 | go run . 51 | {{- range $i, $arg := .Args}} --{{$arg.Name}}=${{EnvVarName $arg.Name}}{{end}} & 52 | {{EnvVarName .Name}}=$! 53 | return $? 54 | }` 55 | -------------------------------------------------------------------------------- /examples/dsb_hotel/tests/profileservice_test.go: -------------------------------------------------------------------------------- 1 | package tests 2 | 3 | import ( 4 | "context" 5 | "testing" 6 | 7 | "github.com/blueprint-uservices/blueprint/examples/dsb_hotel/workflow/hotelreservation" 8 | "github.com/blueprint-uservices/blueprint/runtime/core/registry" 9 | "github.com/blueprint-uservices/blueprint/runtime/plugins/simplecache" 10 | "github.com/blueprint-uservices/blueprint/runtime/plugins/simplenosqldb" 11 | "github.com/stretchr/testify/assert" 12 | ) 13 | 14 | var profileServiceRegistry = registry.NewServiceRegistry[hotelreservation.ProfileService]("profile_service") 15 | 16 | func init() { 17 | profileServiceRegistry.Register("local", func(ctx context.Context) (hotelreservation.ProfileService, error) { 18 | db, err := simplenosqldb.NewSimpleNoSQLDB(ctx) 19 | if err != nil { 20 | return nil, err 21 | } 22 | cache, err := simplecache.NewSimpleCache(ctx) 23 | if err != nil { 24 | return nil, err 25 | } 26 | 27 | return hotelreservation.NewProfileServiceImpl(ctx, cache, db) 28 | }) 29 | } 30 | 31 | func TestGetProfiles(t *testing.T) { 32 | ctx := context.Background() 33 | service, err := profileServiceRegistry.Get(ctx) 34 | assert.NoError(t, err) 35 | 36 | profiles, err := service.GetProfiles(ctx, []string{"1", "2", "3", "4", "5"}, "en") 37 | assert.NoError(t, err) 38 | // Check number of hotels 39 | assert.Len(t, profiles, 5) 40 | 41 | // Check names of hotels 42 | assert.Equal(t, "Clift Hotel", profiles[0].Name) 43 | assert.Equal(t, "W San Francisco", profiles[1].Name) 44 | assert.Equal(t, "Hotel Zetta", profiles[2].Name) 45 | assert.Equal(t, "Hotel Vitale", profiles[3].Name) 46 | assert.Equal(t, "Phoenix Hotel", profiles[4].Name) 47 | } 48 | -------------------------------------------------------------------------------- /runtime/plugins/slogger/log.go: -------------------------------------------------------------------------------- 1 | package slogger 2 | 3 | import ( 4 | "context" 5 | "fmt" 6 | 7 | "github.com/blueprint-uservices/blueprint/runtime/core/backend" 8 | "golang.org/x/exp/slog" 9 | ) 10 | 11 | // SLogger implements the backend.Logger interface by wrapping calls to golang's slog package. 12 | type SLogger struct{} 13 | 14 | // Implements backend.Logger 15 | func (l *SLogger) Debug(ctx context.Context, format string, args ...any) (context.Context, error) { 16 | slog.DebugContext(ctx, fmt.Sprintf(format, args...)) 17 | return ctx, nil 18 | } 19 | 20 | // Implements backend.Logger 21 | func (l *SLogger) Info(ctx context.Context, format string, args ...any) (context.Context, error) { 22 | slog.InfoContext(ctx, fmt.Sprintf(format, args...)) 23 | return ctx, nil 24 | } 25 | 26 | // Implements backend.Logger 27 | func (l *SLogger) Warn(ctx context.Context, format string, args ...any) (context.Context, error) { 28 | slog.WarnContext(ctx, fmt.Sprintf(format, args...)) 29 | return ctx, nil 30 | } 31 | 32 | // Implements backend.Logger 33 | func (l *SLogger) Error(ctx context.Context, format string, args ...any) (context.Context, error) { 34 | slog.ErrorContext(ctx, fmt.Sprintf(format, args...)) 35 | return ctx, nil 36 | } 37 | 38 | // Implements backend.Logger 39 | func (l *SLogger) Logf(ctx context.Context, opts backend.LogOptions, format string, args ...any) (context.Context, error) { 40 | msg := fmt.Sprintf(format, args...) 41 | slog.Log(ctx, slog.Level(opts.Level), msg) 42 | return ctx, nil 43 | } 44 | 45 | // Returns a new logger object 46 | func NewSLogger(ctx context.Context) (*SLogger, error) { 47 | l := &SLogger{} 48 | backend.SetDefaultLogger(l) 49 | return l, nil 50 | } 51 | -------------------------------------------------------------------------------- /blueprint/pkg/blueprint/stringutil/indent.go: -------------------------------------------------------------------------------- 1 | // Package stringutil implements utility methods for common string manipulations used 2 | // by Blueprint plugins. 3 | package stringutil 4 | 5 | import ( 6 | "strings" 7 | ) 8 | 9 | // Indents str by the specified amount by prepending whitespace (space characters) 10 | // at the beginning of every line. str can be a multi-line string; whitespace will 11 | // be prepended to every line. 12 | func Indent(str string, amount int) string { 13 | lines := strings.Split(str, "\n") 14 | for i, line := range lines { 15 | lines[i] = strings.Repeat(" ", amount) + line 16 | } 17 | return strings.Join(lines, "\n") 18 | } 19 | 20 | // Indents str by the specified amount by adding or removing whitespace (space characters) 21 | // at the beginning of every line. If str is already indented by some amount, then this 22 | // method will add or remove whitespace so that the result is indented by amount total whitespace. 23 | func Reindent(str string, amount int) string { 24 | splits := strings.Split(str, "\n") 25 | for i := range splits { 26 | if strings.TrimSpace(splits[i]) == "" { 27 | splits[i] = "" 28 | } else { 29 | splits[i] = strings.Replace(splits[i], "\t", " ", -1) 30 | } 31 | } 32 | 33 | currentIndent := 10000 34 | for i := range splits { 35 | if len(splits[i]) > 0 { 36 | indent := len(splits[i]) - len(strings.TrimLeft(splits[i], " ")) 37 | if indent < currentIndent { 38 | currentIndent = indent 39 | } 40 | } 41 | } 42 | 43 | prefix := strings.Repeat(" ", amount) 44 | for i := range splits { 45 | if len(splits[i]) > 0 { 46 | splits[i] = prefix + splits[i][currentIndent:] 47 | } 48 | } 49 | 50 | return strings.Join(splits, "\n") 51 | } 52 | -------------------------------------------------------------------------------- /examples/train_ticket/workflow/delivery/deliveryService.go: -------------------------------------------------------------------------------- 1 | //Package delivery implements ts-delivery service from the original train ticket application 2 | package delivery 3 | 4 | import ( 5 | "context" 6 | "fmt" 7 | 8 | "github.com/blueprint-uservices/blueprint/runtime/core/backend" 9 | "golang.org/x/exp/slog" 10 | ) 11 | 12 | // DeliveryService implements the Delivery microservice. 13 | // 14 | // It is not a service that can be called; instead it pulls deliveries from 15 | // the delivery queue 16 | type DeliveryService interface { 17 | Run(ctx context.Context) error 18 | } 19 | 20 | type DeliveryServiceImpl struct { 21 | db backend.NoSQLDatabase 22 | delQ backend.Queue 23 | } 24 | 25 | func NewDeliveryServiceImpl(ctx context.Context, queue backend.Queue, db backend.NoSQLDatabase) (*DeliveryServiceImpl, error) { 26 | return &DeliveryServiceImpl{db: db, delQ: queue}, nil 27 | } 28 | 29 | func (d *DeliveryServiceImpl) Run(ctx context.Context) error { 30 | for { 31 | select { 32 | case <-ctx.Done(): 33 | return nil 34 | default: 35 | var delivery Delivery 36 | didpop, err := d.delQ.Pop(ctx, &delivery) 37 | if err != nil { 38 | slog.Error(fmt.Sprintf("DeliveryService unable to pull delivery info from deliver queue due to %v", err)) 39 | } 40 | if didpop { 41 | coll, err := d.db.GetCollection(ctx, "delivery", "delivery") 42 | if err != nil { 43 | slog.Error(fmt.Sprintf("DeliveryService unable to obtain a collection to delivery database due to %v", err)) 44 | } 45 | err = coll.InsertOne(ctx, delivery) 46 | if err != nil { 47 | slog.Error(fmt.Sprintf("DeliveryService unable to add a delivery to the database due to %v", err)) 48 | } 49 | } 50 | } 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /test/wiring/simplecache_test.go: -------------------------------------------------------------------------------- 1 | package wiring 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/blueprint-uservices/blueprint/plugins/simple" 7 | "github.com/blueprint-uservices/blueprint/plugins/workflow" 8 | "github.com/blueprint-uservices/blueprint/test/workflow/cache" 9 | wf "github.com/blueprint-uservices/blueprint/test/workflow/workflow" 10 | ) 11 | 12 | func TestSimpleCache(t *testing.T) { 13 | spec := newWiringSpec("TestSimpleCache") 14 | 15 | leaf_cache := simple.Cache(spec, "leaf_cache") 16 | leaf := workflow.Service[*cache.TestLeafServiceImplWithCache](spec, "leaf", leaf_cache) 17 | 18 | app := assertBuildSuccess(t, spec, leaf, leaf_cache) 19 | 20 | assertIR(t, app, 21 | `TestSimpleCache = BlueprintApplication() { 22 | leaf = TestLeafService(leaf_cache) 23 | leaf.client = leaf 24 | leaf.handler.visibility 25 | leaf_cache = SimpleCache() 26 | leaf_cache.backend.visibility 27 | }`) 28 | } 29 | func TestSimpleCacheAndServices(t *testing.T) { 30 | spec := newWiringSpec("TestSimpleCacheAndServices") 31 | 32 | leaf_cache := simple.Cache(spec, "leaf_cache") 33 | leaf := workflow.Service[*cache.TestLeafServiceImplWithCache](spec, "leaf", leaf_cache) 34 | nonleaf := workflow.Service[wf.TestNonLeafService](spec, "nonleaf", leaf) 35 | 36 | app := assertBuildSuccess(t, spec, leaf, leaf_cache, nonleaf) 37 | 38 | assertIR(t, app, 39 | `TestSimpleCacheAndServices = BlueprintApplication() { 40 | leaf = TestLeafService(leaf_cache) 41 | leaf.client = leaf 42 | leaf.handler.visibility 43 | leaf_cache = SimpleCache() 44 | leaf_cache.backend.visibility 45 | nonleaf = TestNonLeafService(leaf.client) 46 | nonleaf.client = nonleaf 47 | nonleaf.handler.visibility 48 | }`) 49 | } 50 | -------------------------------------------------------------------------------- /plugins/dockercompose/dockergen/template.go: -------------------------------------------------------------------------------- 1 | package dockergen 2 | 3 | import ( 4 | "bytes" 5 | "os" 6 | "strings" 7 | "text/template" 8 | 9 | "github.com/blueprint-uservices/blueprint/plugins/linux" 10 | ) 11 | 12 | /* 13 | Any template that is executed using ExecuteTemplate will be able to use 14 | the helper functions defined in this file within the template. 15 | */ 16 | 17 | func ExecuteTemplate(name string, body string, args any) (string, error) { 18 | return newTemplateExecutor(args).exec(name, body, args) 19 | } 20 | 21 | func ExecuteTemplateToFile(name string, body string, args any, filename string) error { 22 | f, err := os.OpenFile(filename, os.O_CREATE|os.O_RDWR, 0755) 23 | if err != nil { 24 | return err 25 | } 26 | defer f.Close() 27 | 28 | code, err := ExecuteTemplate(name, body, args) 29 | if err != nil { 30 | return err 31 | } 32 | _, err = f.WriteString(code) 33 | return err 34 | } 35 | 36 | type templateExecutor struct { 37 | Funcs template.FuncMap 38 | } 39 | 40 | func newTemplateExecutor(args any) *templateExecutor { 41 | e := &templateExecutor{ 42 | Funcs: template.FuncMap{}, 43 | } 44 | 45 | e.Funcs["EnvVarName"] = e.EnvVarName 46 | e.Funcs["Title"] = e.TitleCase 47 | 48 | return e 49 | } 50 | 51 | func (e *templateExecutor) exec(name string, body string, args any) (string, error) { 52 | t, err := template.New(name).Funcs(e.Funcs).Parse(body) 53 | if err != nil { 54 | return "", err 55 | } 56 | buf := &bytes.Buffer{} 57 | err = t.Execute(buf, args) 58 | return buf.String(), err 59 | } 60 | 61 | func (e *templateExecutor) EnvVarName(name string) (string, error) { 62 | return linux.EnvVar(name), nil 63 | } 64 | 65 | func (e *templateExecutor) TitleCase(arg string) (string, error) { 66 | return strings.Title(arg), nil 67 | } 68 | -------------------------------------------------------------------------------- /examples/sockshop/workflow/queuemaster/README.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | # queuemaster 4 | 5 | ```go 6 | import "github.com/blueprint-uservices/blueprint/examples/sockshop/workflow/queuemaster" 7 | ``` 8 | 9 | Package queuemaster implements the queue\-master SockShop service, responsible for pulling and "processing" shipments from the shipment queue. 10 | 11 | ## Index 12 | 13 | - [type QueueMaster](<#QueueMaster>) 14 | - [func NewQueueMaster\(ctx context.Context, queue backend.Queue, shipping shipping.ShippingService\) \(QueueMaster, error\)](<#NewQueueMaster>) 15 | 16 | 17 | 18 | ## type [QueueMaster]() 19 | 20 | QueueMaster implements the SockShop queue\-master microservice. 21 | 22 | It is not a service that can be called; instead it pulls shipments from the shipments queue 23 | 24 | ```go 25 | type QueueMaster interface { 26 | // Runs the background goroutine that continually pulls elements from 27 | // the queue. Does not return until ctx is cancelled or an error is 28 | // encountered 29 | Run(ctx context.Context) error 30 | } 31 | ``` 32 | 33 | 34 | ### func [NewQueueMaster]() 35 | 36 | ```go 37 | func NewQueueMaster(ctx context.Context, queue backend.Queue, shipping shipping.ShippingService) (QueueMaster, error) 38 | ``` 39 | 40 | Creates a new QueueMaster service. 41 | 42 | New: once an order is shipped, it will update the order status in the orderservice. 43 | 44 | Generated by [gomarkdoc]() 45 | -------------------------------------------------------------------------------- /examples/sockshop/workflow/queuemaster/queuemaster_test.go: -------------------------------------------------------------------------------- 1 | package queuemaster 2 | 3 | import ( 4 | "context" 5 | "sync/atomic" 6 | "testing" 7 | "time" 8 | 9 | "github.com/blueprint-uservices/blueprint/examples/sockshop/workflow/shipping" 10 | "github.com/blueprint-uservices/blueprint/runtime/plugins/simplenosqldb" 11 | "github.com/blueprint-uservices/blueprint/runtime/plugins/simplequeue" 12 | "github.com/stretchr/testify/require" 13 | ) 14 | 15 | // Unit tests that don't use gotests plugin 16 | 17 | func TestQueueMaster(t *testing.T) { 18 | ctx, cancel := context.WithCancel(context.Background()) 19 | 20 | q, err := simplequeue.NewSimpleQueue(ctx) 21 | require.NoError(t, err) 22 | 23 | db, err := simplenosqldb.NewSimpleNoSQLDB(ctx) 24 | require.NoError(t, err) 25 | 26 | shipService, err := shipping.NewShippingService(ctx, q, db) 27 | require.NoError(t, err) 28 | 29 | qMaster := newQueueMasterImpl(q, shipService, true) 30 | require.Equal(t, int32(0), qMaster.processed) 31 | 32 | exitCount := int32(0) 33 | go func() { 34 | err = qMaster.Run(ctx) 35 | require.NoError(t, err) 36 | atomic.AddInt32(&exitCount, 1) 37 | }() 38 | 39 | shipment := shipping.Shipment{ 40 | ID: "first", 41 | Name: "my first shipment", 42 | Status: "unshipped", 43 | } 44 | _, err = shipService.PostShipping(ctx, shipment) 45 | require.NoError(t, err) 46 | 47 | time.Sleep(10 * time.Millisecond) 48 | 49 | require.Equal(t, int32(1), atomic.LoadInt32(&qMaster.processed)) 50 | 51 | shipment2, err := shipService.GetShipment(ctx, shipment.ID) 52 | require.NoError(t, err) 53 | require.NotEqual(t, shipment, shipment2) 54 | require.Equal(t, "shipped", shipment2.Status) 55 | 56 | cancel() 57 | 58 | time.Sleep(10 * time.Millisecond) 59 | require.Equal(t, int32(1), atomic.LoadInt32(&exitCount)) 60 | } 61 | -------------------------------------------------------------------------------- /examples/dsb_hotel/tests/reservationservice_test.go: -------------------------------------------------------------------------------- 1 | package tests 2 | 3 | import ( 4 | "context" 5 | "testing" 6 | 7 | "github.com/blueprint-uservices/blueprint/examples/dsb_hotel/workflow/hotelreservation" 8 | "github.com/blueprint-uservices/blueprint/runtime/core/registry" 9 | "github.com/blueprint-uservices/blueprint/runtime/plugins/simplecache" 10 | "github.com/blueprint-uservices/blueprint/runtime/plugins/simplenosqldb" 11 | "github.com/stretchr/testify/assert" 12 | ) 13 | 14 | var reservationServiceRegistry = registry.NewServiceRegistry[hotelreservation.ReservationService]("reservation_service") 15 | 16 | func init() { 17 | reservationServiceRegistry.Register("local", func(ctx context.Context) (hotelreservation.ReservationService, error) { 18 | db, err := simplenosqldb.NewSimpleNoSQLDB(ctx) 19 | if err != nil { 20 | return nil, err 21 | } 22 | cache, err := simplecache.NewSimpleCache(ctx) 23 | if err != nil { 24 | return nil, err 25 | } 26 | 27 | return hotelreservation.NewReservationServiceImpl(ctx, cache, db) 28 | }) 29 | } 30 | 31 | func TestCheckAvailability(t *testing.T) { 32 | ctx := context.Background() 33 | service, err := reservationServiceRegistry.Get(ctx) 34 | assert.NoError(t, err) 35 | 36 | hotels, err := service.CheckAvailability(ctx, "Vaastav", []string{"1"}, "2015-04-09", "2015-04-10", 1) 37 | assert.NoError(t, err) 38 | assert.Len(t, hotels, 1) 39 | assert.Equal(t, hotels[0], "1") 40 | } 41 | 42 | func TestMakeReservation(t *testing.T) { 43 | ctx := context.Background() 44 | service, err := reservationServiceRegistry.Get(ctx) 45 | assert.NoError(t, err) 46 | 47 | hotels, err := service.MakeReservation(ctx, "Vaastav", []string{"1"}, "2015-04-09", "2015-04-10", 1) 48 | assert.NoError(t, err) 49 | assert.Len(t, hotels, 1) 50 | assert.Equal(t, hotels[0], "1") 51 | } 52 | -------------------------------------------------------------------------------- /examples/leaf/wiring/specs/thrift.go: -------------------------------------------------------------------------------- 1 | package specs 2 | 3 | import ( 4 | "github.com/blueprint-uservices/blueprint/blueprint/pkg/wiring" 5 | "github.com/blueprint-uservices/blueprint/examples/leaf/workflow/leaf" 6 | "github.com/blueprint-uservices/blueprint/plugins/clientpool" 7 | "github.com/blueprint-uservices/blueprint/plugins/cmdbuilder" 8 | "github.com/blueprint-uservices/blueprint/plugins/goproc" 9 | "github.com/blueprint-uservices/blueprint/plugins/mongodb" 10 | "github.com/blueprint-uservices/blueprint/plugins/simple" 11 | "github.com/blueprint-uservices/blueprint/plugins/thrift" 12 | "github.com/blueprint-uservices/blueprint/plugins/workflow" 13 | ) 14 | 15 | // [Thrift] demonstrates how to deploy a service as over RPC using the [thrift] plugin. 16 | // 17 | // [thrift]: https://github.com/Blueprint-uServices/blueprint/tree/main/plugins/thrift 18 | var Thrift = cmdbuilder.SpecOption{ 19 | Name: "thrift", 20 | Description: "Deploys each service in a separate process, communicating using Thrift.", 21 | Build: makeThriftSpec, 22 | } 23 | 24 | func makeThriftSpec(spec wiring.WiringSpec) ([]string, error) { 25 | 26 | applyThriftDefaults := func(spec wiring.WiringSpec, serviceName string) string { 27 | clientpool.Create(spec, serviceName, 5) 28 | thrift.Deploy(spec, serviceName) 29 | return goproc.Deploy(spec, serviceName) 30 | } 31 | 32 | leaf_db := mongodb.Container(spec, "leaf_db") 33 | leaf_cache := simple.Cache(spec, "leaf_cache") 34 | leaf_service := workflow.Service[*leaf.LeafServiceImpl](spec, "leaf_service", leaf_cache, leaf_db) 35 | leaf_proc := applyThriftDefaults(spec, leaf_service) 36 | 37 | nonleaf_service := workflow.Service[leaf.NonLeafService](spec, "nonleaf_service", leaf_service) 38 | nonleaf_proc := applyThriftDefaults(spec, nonleaf_service) 39 | 40 | return []string{leaf_proc, nonleaf_proc}, nil 41 | } 42 | -------------------------------------------------------------------------------- /examples/leaf/workflow/leaf/nonleafservice.go: -------------------------------------------------------------------------------- 1 | package leaf 2 | 3 | import ( 4 | "context" 5 | "fmt" 6 | 7 | "github.com/blueprint-uservices/blueprint/runtime/core/backend" 8 | ) 9 | 10 | type NonLeafService interface { 11 | Hello(ctx context.Context, a int64) (int64, error) 12 | } 13 | 14 | type NonLeafServiceImpl struct { 15 | NonLeafService 16 | leafService LeafService 17 | logger backend.Logger 18 | } 19 | 20 | func NewNonLeafServiceImpl(ctx context.Context, leafService LeafService) (NonLeafService, error) { 21 | nonleaf := &NonLeafServiceImpl{} 22 | nonleaf.leafService = leafService 23 | nonleaf.logger = backend.GetLogger() 24 | return nonleaf, nil 25 | } 26 | 27 | func (nl *NonLeafServiceImpl) Hello(ctx context.Context, a int64) (int64, error) { 28 | a, err := nl.leafService.HelloInt(ctx, a) 29 | if err != nil { 30 | return a, err 31 | } 32 | 33 | err = nl.leafService.HelloNothing(ctx) 34 | if err != nil { 35 | return 0, err 36 | } 37 | 38 | var b int32 39 | b = int32(a * 2) 40 | 41 | c := fmt.Sprintf("hello %v", a) 42 | 43 | d := make(map[string]LeafObject) 44 | dc := LeafObject{ 45 | ID: a, 46 | Name: c, 47 | Props: make(map[string]NestedLeafObject), 48 | } 49 | d[c] = dc 50 | d[c].Props["hello"] = NestedLeafObject{ 51 | Key: "greetings", 52 | Value: "mate", 53 | Props: []string{"cool", "beans"}, 54 | } 55 | // string, []string, int32, int, map[string]LeafObject, error) 56 | ra, rb, rc, rd, re, err := nl.leafService.HelloMate(ctx, int(a), b, c, d, []string{"hi", "bye"}, nil) 57 | if err != nil { 58 | return a, err 59 | } 60 | 61 | ctx, _ = nl.logger.Info(ctx, "ra: %s", ra) 62 | ctx, _ = nl.logger.Info(ctx, "rb: %v", rb) 63 | ctx, _ = nl.logger.Info(ctx, "rc: %d", rc) 64 | ctx, _ = nl.logger.Info(ctx, "rd: %d", rd) 65 | ctx, _ = nl.logger.Info(ctx, "re: %v", re) 66 | 67 | return a, nil 68 | } 69 | -------------------------------------------------------------------------------- /runtime/plugins/simplequeue/queue.go: -------------------------------------------------------------------------------- 1 | // Package simplequeue implements an simple in-memory [backend.Queue] that internally 2 | // uses a golang channel of capacity 10 for passing items from producer to consumer. 3 | // 4 | // Calls to [backend.Queue.Push] will block once the queue capacity reaches 10. 5 | package simplequeue 6 | 7 | import ( 8 | "context" 9 | 10 | "github.com/blueprint-uservices/blueprint/runtime/core/backend" 11 | ) 12 | 13 | // A simple chan-based queue that implements the [backend.Queue] interface 14 | type SimpleQueue struct { 15 | backend.Queue 16 | q chan any 17 | } 18 | 19 | // Instantiates a [backend.Queue] that internally uses a golang channel of capacity 10. 20 | // 21 | // Calls to [q.Push] will block once the queue capacity reaches 10. 22 | func NewSimpleQueue(ctx context.Context) (q *SimpleQueue, err error) { 23 | return newSimpleQueueWithCapacity(10), nil 24 | } 25 | 26 | // Instantiates a [simpleQueue] with the specified capacity. 27 | func newSimpleQueueWithCapacity(capacity int) *SimpleQueue { 28 | return &SimpleQueue{ 29 | q: make(chan any, capacity), 30 | } 31 | } 32 | 33 | // Pop implements backend.Queue. 34 | func (q *SimpleQueue) Pop(ctx context.Context, dst interface{}) (bool, error) { 35 | select { 36 | case v := <-q.q: 37 | return true, backend.CopyResult(v, dst) 38 | default: 39 | { 40 | select { 41 | case v := <-q.q: 42 | return true, backend.CopyResult(v, dst) 43 | case <-ctx.Done(): 44 | return false, nil 45 | } 46 | } 47 | } 48 | } 49 | 50 | // Push implements backend.Queue. 51 | func (q *SimpleQueue) Push(ctx context.Context, item interface{}) (bool, error) { 52 | select { 53 | case q.q <- item: 54 | return true, nil 55 | default: 56 | { 57 | select { 58 | case q.q <- item: 59 | return true, nil 60 | case <-ctx.Done(): 61 | return false, nil 62 | } 63 | } 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /plugins/goproc/linuxgen/README.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | # linuxgen 4 | 5 | ```go 6 | import "github.com/blueprint-uservices/blueprint/plugins/goproc/linuxgen" 7 | ``` 8 | 9 | Package linuxgen implements code generation for the goproc plugin. 10 | 11 | ## Index 12 | 13 | - [func GenerateBinaryRunFunc\(procName string, args ...ir.IRNode\) \(string, error\)](<#GenerateBinaryRunFunc>) 14 | - [func GenerateDockerfileBuildCommands\(goProcName string\) \(string, error\)](<#GenerateDockerfileBuildCommands>) 15 | - [func GenerateRunFunc\(procName string, args ...ir.IRNode\) \(string, error\)](<#GenerateRunFunc>) 16 | 17 | 18 | 19 | ## func [GenerateBinaryRunFunc]() 20 | 21 | ```go 22 | func GenerateBinaryRunFunc(procName string, args ...ir.IRNode) (string, error) 23 | ``` 24 | 25 | Generates command\-line function to run a goproc that has been built to a binary using \`go build\` 26 | 27 | 28 | ## func [GenerateDockerfileBuildCommands]() 29 | 30 | ```go 31 | func GenerateDockerfileBuildCommands(goProcName string) (string, error) 32 | ``` 33 | 34 | If the goproc is being deployed to Docker, we can provide some custom build commands to add to the Dockerfile 35 | 36 | 37 | ## func [GenerateRunFunc]() 38 | 39 | ```go 40 | func GenerateRunFunc(procName string, args ...ir.IRNode) (string, error) 41 | ``` 42 | 43 | Generates command\-line function to run a goproc 44 | 45 | Generated by [gomarkdoc]() 46 | -------------------------------------------------------------------------------- /blueprint/pkg/coreplugins/service/README.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | # service 4 | 5 | ```go 6 | import "github.com/blueprint-uservices/blueprint/blueprint/pkg/coreplugins/service" 7 | ``` 8 | 9 | ## Index 10 | 11 | - [type Method](<#Method>) 12 | - [type ServiceInterface](<#ServiceInterface>) 13 | - [type ServiceNode](<#ServiceNode>) 14 | - [type Variable](<#Variable>) 15 | 16 | 17 | 18 | ## type [Method]() 19 | 20 | 21 | 22 | ```go 23 | type Method interface { 24 | GetName() string 25 | GetArguments() []Variable 26 | GetReturns() []Variable 27 | } 28 | ``` 29 | 30 | 31 | ## type [ServiceInterface]() 32 | 33 | 34 | 35 | ```go 36 | type ServiceInterface interface { 37 | GetName() string 38 | GetMethods() []Method 39 | } 40 | ``` 41 | 42 | 43 | ## type [ServiceNode]() 44 | 45 | Any IR node that represents a callable service should implement this interface. 46 | 47 | ```go 48 | type ServiceNode interface { 49 | 50 | // Returns the interface of this service 51 | GetInterface(ctx ir.BuildContext) (ServiceInterface, error) 52 | } 53 | ``` 54 | 55 | 56 | ## type [Variable]() 57 | 58 | 59 | 60 | ```go 61 | type Variable interface { 62 | GetName() string 63 | GetType() string // a "well-known" type 64 | } 65 | ``` 66 | 67 | Generated by [gomarkdoc]() 68 | -------------------------------------------------------------------------------- /runtime/plugins/simplecache/cache_test.go: -------------------------------------------------------------------------------- 1 | package simplecache 2 | 3 | import ( 4 | "context" 5 | "testing" 6 | 7 | "github.com/stretchr/testify/assert" 8 | ) 9 | 10 | func TestGet(t *testing.T) { 11 | ctx := context.Background() 12 | cache, _ := NewSimpleCache(ctx) 13 | 14 | err := cache.Put(ctx, "hello", "world") 15 | assert.NoError(t, err) 16 | 17 | var v string 18 | exists, err := cache.Get(ctx, "hello", &v) 19 | assert.True(t, exists) 20 | assert.NoError(t, err) 21 | 22 | assert.Equal(t, v, "world") 23 | 24 | var j int 25 | exists, err = cache.Get(ctx, "nonexistent", &j) 26 | assert.NoError(t, err) 27 | assert.False(t, exists) 28 | 29 | // Can't cast string to int 30 | var i int 31 | exists, err = cache.Get(ctx, "hello", &i) 32 | assert.True(t, exists) 33 | assert.Error(t, err) 34 | } 35 | 36 | func TestIncr(t *testing.T) { 37 | ctx := context.Background() 38 | cache, _ := NewSimpleCache(ctx) 39 | 40 | for j := 0; j < 10; j++ { 41 | jv, err := cache.Incr(ctx, "nothing") 42 | assert.NoError(t, err) 43 | assert.Equal(t, jv, int64(j+1)) 44 | } 45 | } 46 | 47 | func TestMget(t *testing.T) { 48 | ctx := context.Background() 49 | cache, _ := NewSimpleCache(ctx) 50 | 51 | keys := []string{"a", "b"} 52 | values := []interface{}{int64(5), "hello"} 53 | err := cache.Mset(ctx, keys, values) 54 | assert.NoError(t, err) 55 | 56 | var va int64 57 | var vb string 58 | getvalues := []interface{}{&va, &vb} 59 | err = cache.Mget(ctx, keys, getvalues) 60 | assert.NoError(t, err) 61 | assert.Equal(t, va, int64(5)) 62 | assert.Equal(t, vb, "hello") 63 | 64 | err = cache.Mset(ctx, keys, []interface{}{}) 65 | assert.Error(t, err) 66 | 67 | err = cache.Mset(ctx, []string{}, values) 68 | assert.Error(t, err) 69 | 70 | err = cache.Mget(ctx, keys, []interface{}{}) 71 | assert.Error(t, err) 72 | 73 | err = cache.Mget(ctx, []string{}, getvalues) 74 | assert.Error(t, err) 75 | } 76 | -------------------------------------------------------------------------------- /blueprint/pkg/blueprint/ioutil/README.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | # ioutil 4 | 5 | ```go 6 | import "github.com/blueprint-uservices/blueprint/blueprint/pkg/blueprint/ioutil" 7 | ``` 8 | 9 | Package ioutil implements filesystem related utility methods primarily for use by plugins that produce artifacts onto the local filesystem. 10 | 11 | ## Index 12 | 13 | - [func CheckDir\(path string, createIfAbsent bool\) error](<#CheckDir>) 14 | - [func CreateNodeDir\(workspaceDir string, name string\) \(string, error\)](<#CreateNodeDir>) 15 | - [func IsDir\(path string\) bool](<#IsDir>) 16 | 17 | 18 | 19 | ## func [CheckDir]() 20 | 21 | ```go 22 | func CheckDir(path string, createIfAbsent bool) error 23 | ``` 24 | 25 | Returns nil if the specified path exists and is a directory; if not returns an error. If the specified path does not exist, then createIfAbsent dictates whether the path is either created, or an error is returned. This method can also return an error if it was unable to create a directory at the given path. 26 | 27 | 28 | ## func [CreateNodeDir]() 29 | 30 | ```go 31 | func CreateNodeDir(workspaceDir string, name string) (string, error) 32 | ``` 33 | 34 | Creates a subdirectory in the provided workspaceDir. The provided name is first sanitized using \[stringutil.CleanName\] 35 | 36 | 37 | ## func [IsDir]() 38 | 39 | ```go 40 | func IsDir(path string) bool 41 | ``` 42 | 43 | Returns true if the specified path exists and is a directory; false otherwise 44 | 45 | Generated by [gomarkdoc]() 46 | -------------------------------------------------------------------------------- /runtime/plugins/jaeger/README.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | # jaeger 4 | 5 | ```go 6 | import "github.com/blueprint-uservices/blueprint/runtime/plugins/jaeger" 7 | ``` 8 | 9 | Package jaeger implements a tracer \[backend.Tracer\] client interface for the jaeger tracer. 10 | 11 | ## Index 12 | 13 | - [type JaegerTracer](<#JaegerTracer>) 14 | - [func NewJaegerTracer\(ctx context.Context, addr string\) \(\*JaegerTracer, error\)](<#NewJaegerTracer>) 15 | - [func \(t \*JaegerTracer\) GetTracerProvider\(ctx context.Context\) \(trace.TracerProvider, error\)](<#JaegerTracer.GetTracerProvider>) 16 | 17 | 18 | 19 | ## type [JaegerTracer]() 20 | 21 | JaegerTracer implements the runtime backend instance that implements the backend/trace.Tracer interface. REQUIRED: A functional backend running the jaeger collector. 22 | 23 | ```go 24 | type JaegerTracer struct { 25 | // contains filtered or unexported fields 26 | } 27 | ``` 28 | 29 | 30 | ### func [NewJaegerTracer]() 31 | 32 | ```go 33 | func NewJaegerTracer(ctx context.Context, addr string) (*JaegerTracer, error) 34 | ``` 35 | 36 | Returns a new instance of JaegerTracer. Configures opentelemetry to export jaeger traces to the jaeger collector hosted at address \`addr\`. 37 | 38 | 39 | ### func \(\*JaegerTracer\) [GetTracerProvider]() 40 | 41 | ```go 42 | func (t *JaegerTracer) GetTracerProvider(ctx context.Context) (trace.TracerProvider, error) 43 | ``` 44 | 45 | Implements the backend/trace interface. 46 | 47 | Generated by [gomarkdoc]() 48 | -------------------------------------------------------------------------------- /runtime/plugins/zipkin/README.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | # zipkin 4 | 5 | ```go 6 | import "github.com/blueprint-uservices/blueprint/runtime/plugins/zipkin" 7 | ``` 8 | 9 | Package zipkin implements a tracer \[backend.Tracer\] client interface for the zipkin tracer. 10 | 11 | ## Index 12 | 13 | - [type ZipkinTracer](<#ZipkinTracer>) 14 | - [func NewZipkinTracer\(ctx context.Context, addr string\) \(\*ZipkinTracer, error\)](<#NewZipkinTracer>) 15 | - [func \(t \*ZipkinTracer\) GetTracerProvider\(ctx context.Context\) \(trace.TracerProvider, error\)](<#ZipkinTracer.GetTracerProvider>) 16 | 17 | 18 | 19 | ## type [ZipkinTracer]() 20 | 21 | ZipkinTracer implements the runtime backend instance that implements the backend/trace.Tracer interface. REQUIRED: A functional backend running the zipkin collector. 22 | 23 | ```go 24 | type ZipkinTracer struct { 25 | // contains filtered or unexported fields 26 | } 27 | ``` 28 | 29 | 30 | ### func [NewZipkinTracer]() 31 | 32 | ```go 33 | func NewZipkinTracer(ctx context.Context, addr string) (*ZipkinTracer, error) 34 | ``` 35 | 36 | Returns a new instance of ZipkinTracer. Configures opentelemetry to export zipkin traces to the zipkin collector hosted at address \`addr\`. 37 | 38 | 39 | ### func \(\*ZipkinTracer\) [GetTracerProvider]() 40 | 41 | ```go 42 | func (t *ZipkinTracer) GetTracerProvider(ctx context.Context) (trace.TracerProvider, error) 43 | ``` 44 | 45 | Implements the backend/trace interface. 46 | 47 | Generated by [gomarkdoc]() 48 | -------------------------------------------------------------------------------- /examples/dsb_sn/tests/urlshortenservice_test.go: -------------------------------------------------------------------------------- 1 | package tests 2 | 3 | import ( 4 | "context" 5 | "strings" 6 | "testing" 7 | 8 | "github.com/blueprint-uservices/blueprint/examples/dsb_sn/workflow/socialnetwork" 9 | "github.com/blueprint-uservices/blueprint/runtime/core/registry" 10 | "github.com/blueprint-uservices/blueprint/runtime/plugins/simplenosqldb" 11 | "github.com/stretchr/testify/require" 12 | ) 13 | 14 | var urlShortenServiceRegistry = registry.NewServiceRegistry[socialnetwork.UrlShortenService]("urlShorten_service") 15 | 16 | func init() { 17 | urlShortenServiceRegistry.Register("local", func(ctx context.Context) (socialnetwork.UrlShortenService, error) { 18 | db, err := simplenosqldb.NewSimpleNoSQLDB(ctx) 19 | if err != nil { 20 | return nil, err 21 | } 22 | 23 | return socialnetwork.NewUrlShortenServiceImpl(ctx, db) 24 | }) 25 | } 26 | 27 | func TestComposeUrls(t *testing.T) { 28 | ctx := context.Background() 29 | service, err := urlShortenServiceRegistry.Get(ctx) 30 | require.NoError(t, err) 31 | 32 | urls, err := service.ComposeUrls(ctx, 1000, []string{"http://localhost:9000/hello", "http://localhost:9000/world"}) 33 | require.NoError(t, err) 34 | require.Len(t, urls, 2) 35 | 36 | require.True(t, strings.HasPrefix(urls[0].ShortenedUrl, "http://short-url/")) 37 | require.True(t, strings.HasPrefix(urls[1].ShortenedUrl, "http://short-url/")) 38 | require.Equal(t, "http://localhost:9000/hello", urls[0].ExpandedUrl) 39 | require.Equal(t, "http://localhost:9000/world", urls[1].ExpandedUrl) 40 | } 41 | 42 | func TestGetExtendedUrls(t *testing.T) { 43 | ctx := context.Background() 44 | service, err := urlShortenServiceRegistry.Get(ctx) 45 | require.NoError(t, err) 46 | 47 | // API is not currently implemented, so we should get blank values and no error 48 | extended_urls, err := service.GetExtendedUrls(ctx, 1000, []string{"http://short-url/blah"}) 49 | require.NoError(t, err) 50 | require.Len(t, extended_urls, 0) 51 | } 52 | -------------------------------------------------------------------------------- /examples/dsb_sn/workflow/socialnetwork/UserIdService.go: -------------------------------------------------------------------------------- 1 | package socialnetwork 2 | 3 | import ( 4 | "context" 5 | "errors" 6 | 7 | "github.com/blueprint-uservices/blueprint/runtime/core/backend" 8 | "go.mongodb.org/mongo-driver/bson" 9 | ) 10 | 11 | // The UserIDService interface 12 | type UserIDService interface { 13 | // Returns the userID of the user associated with the `username`. 14 | // Returns an error if no user exists with the given `username`. 15 | GetUserId(ctx context.Context, reqID int64, username string) (int64, error) 16 | } 17 | 18 | // Implementation of [UserIDService] 19 | type UserIDServiceImpl struct { 20 | userCache backend.Cache 21 | userDB backend.NoSQLDatabase 22 | } 23 | 24 | // Creates a [UserIDService] instance for looking up users with usernames. 25 | func NewUserIDServiceImpl(ctx context.Context, userCache backend.Cache, userDB backend.NoSQLDatabase) (UserIDService, error) { 26 | return &UserIDServiceImpl{userCache: userCache, userDB: userDB}, nil 27 | } 28 | 29 | // Implements UserIDService interface 30 | func (u *UserIDServiceImpl) GetUserId(ctx context.Context, reqID int64, username string) (int64, error) { 31 | user_id := int64(-1) 32 | exists, err := u.userCache.Get(ctx, username+":UserID", &user_id) 33 | if err != nil { 34 | return user_id, err 35 | } 36 | if !exists { 37 | var user User 38 | collection, err := u.userDB.GetCollection(ctx, "user", "user") 39 | if err != nil { 40 | return -1, err 41 | } 42 | query := bson.D{{"username", username}} 43 | res, err := collection.FindOne(ctx, query) 44 | if err != nil { 45 | return -1, err 46 | } 47 | result, err := res.One(ctx, &user) 48 | if err != nil { 49 | return -1, err 50 | } 51 | if !result { 52 | return -1, errors.New("Username " + username + " not found") 53 | } 54 | user_id = user.UserID 55 | 56 | err = u.userCache.Put(ctx, username+":UserID", user_id) 57 | if err != nil { 58 | return -1, err 59 | } 60 | } 61 | return user_id, nil 62 | } 63 | -------------------------------------------------------------------------------- /scripts/gen_plugins.py: -------------------------------------------------------------------------------- 1 | ''' 2 | This script generates index files for all the available plugins for Blueprint's website. 3 | The generated index files are placed at https://github.com/Blueprint-uServices/Blueprint-uServices.github.io/tree/main/_documents 4 | which then get listed on the Blueprint's website at https://blueprint-uservices.github.io/plugins/. 5 | ''' 6 | import os 7 | import sys 8 | from datetime import date 9 | import re 10 | 11 | BASE_URL="https://github.com/Blueprint-uServices/blueprint/tree/main/plugins" 12 | 13 | def main(): 14 | if len(sys.argv) != 2: 15 | print("Usage: python3 scripts/gen_plugins.py 2 | 3 | # dockergen 4 | 5 | ```go 6 | import "github.com/blueprint-uservices/blueprint/plugins/linuxcontainer/dockergen" 7 | ``` 8 | 9 | ## Index 10 | 11 | - [type Dockerfile](<#Dockerfile>) 12 | - [func NewDockerfile\(workspaceName, workspaceDir string\) \*Dockerfile](<#NewDockerfile>) 13 | - [func \(d \*Dockerfile\) AddCustomCommands\(procName string, commands string\)](<#Dockerfile.AddCustomCommands>) 14 | - [func \(d \*Dockerfile\) Generate\(procDirs map\[string\]string\) error](<#Dockerfile.Generate>) 15 | 16 | 17 | 18 | ## type [Dockerfile]() 19 | 20 | 21 | 22 | ```go 23 | type Dockerfile struct { 24 | WorkspaceName string 25 | WorkspaceDir string 26 | FilePath string 27 | CustomProcs map[string]string 28 | DefaultProcs map[string]string 29 | } 30 | ``` 31 | 32 | 33 | ### func [NewDockerfile]() 34 | 35 | ```go 36 | func NewDockerfile(workspaceName, workspaceDir string) *Dockerfile 37 | ``` 38 | 39 | 40 | 41 | 42 | ### func \(\*Dockerfile\) [AddCustomCommands]() 43 | 44 | ```go 45 | func (d *Dockerfile) AddCustomCommands(procName string, commands string) 46 | ``` 47 | 48 | 49 | 50 | 51 | ### func \(\*Dockerfile\) [Generate]() 52 | 53 | ```go 54 | func (d *Dockerfile) Generate(procDirs map[string]string) error 55 | ``` 56 | 57 | 58 | 59 | Generated by [gomarkdoc]() 60 | -------------------------------------------------------------------------------- /examples/sockshop/workflow/payment/paymentservice.go: -------------------------------------------------------------------------------- 1 | // Package payment implements the SockShop payment microservice. 2 | // 3 | // The service fakes payments, implementing simple logic whereby payments 4 | // are authorized when they're below a predefined threshold, and rejected 5 | // when they are above that threshold. 6 | package payment 7 | 8 | import ( 9 | "context" 10 | "errors" 11 | "fmt" 12 | "strconv" 13 | 14 | errors_ "github.com/pkg/errors" 15 | ) 16 | 17 | // PaymentService provides payment services 18 | type PaymentService interface { 19 | Authorise(ctx context.Context, amount float32) (Authorisation, error) 20 | } 21 | 22 | type Authorisation struct { 23 | Authorised bool `json:"authorised"` 24 | Message string `json:"message"` 25 | } 26 | 27 | // Returns a payment service where any transaction above the preconfigured 28 | // threshold will return an invalid payment amount 29 | func NewPaymentService(ctx context.Context, declineOverAmount string) (PaymentService, error) { 30 | amount, err := strconv.ParseFloat(declineOverAmount, 32) 31 | if err != nil { 32 | return nil, errors_.Errorf("invalid declineOverAmount %v; expected a float32", declineOverAmount) 33 | } 34 | return &paymentImpl{ 35 | declineOverAmount: float32(amount), 36 | }, nil 37 | } 38 | 39 | type paymentImpl struct { 40 | declineOverAmount float32 41 | } 42 | 43 | var ErrInvalidPaymentAmount = errors.New("invalid payment amount") 44 | 45 | func (s *paymentImpl) Authorise(ctx context.Context, amount float32) (Authorisation, error) { 46 | if amount == 0 { 47 | return Authorisation{}, ErrInvalidPaymentAmount 48 | } 49 | if amount < 0 { 50 | return Authorisation{}, ErrInvalidPaymentAmount 51 | } 52 | if amount <= s.declineOverAmount { 53 | return Authorisation{ 54 | Authorised: true, 55 | Message: "Payment authorised", 56 | }, nil 57 | } 58 | return Authorisation{ 59 | Authorised: false, 60 | Message: fmt.Sprintf("Payment declined: amount exceeds %.2f", s.declineOverAmount), 61 | }, nil 62 | } 63 | -------------------------------------------------------------------------------- /runtime/core/backend/trace.go: -------------------------------------------------------------------------------- 1 | package backend 2 | 3 | import ( 4 | "context" 5 | "encoding/hex" 6 | "encoding/json" 7 | 8 | "go.opentelemetry.io/otel/trace" 9 | ) 10 | 11 | // Represents a tracer that can be used by the tracer/opentelemetry plugin 12 | type Tracer interface { 13 | // Returns a go.opentelemetry.io/otel/trace.TracerProvider 14 | // TracerProvider provides Tracers that are used by instrumentation code to trace computational workflows. 15 | GetTracerProvider(ctx context.Context) (trace.TracerProvider, error) 16 | } 17 | 18 | // traceCtx mimics the internal trace context object from OpenTelemetry. 19 | // Included here to be able to implement and provide the `GetSpanContext` function. 20 | type traceCtx struct { 21 | // ID of the current trace 22 | TraceID string 23 | // ID of the current span 24 | SpanID string 25 | // Additional flags for the trace 26 | TraceFlags string 27 | // Additional state for the trace 28 | TraceState string 29 | // If span is a remote span 30 | Remote bool 31 | } 32 | 33 | // Utility function to convert an encoded string into a Span Context 34 | func GetSpanContext(encoded_string string) (trace.SpanContextConfig, error) { 35 | var tCtx traceCtx 36 | err := json.Unmarshal([]byte(encoded_string), &tCtx) 37 | if err != nil { 38 | return trace.SpanContextConfig{}, err 39 | } 40 | tid, err := trace.TraceIDFromHex(tCtx.TraceID) 41 | if err != nil { 42 | return trace.SpanContextConfig{}, err 43 | } 44 | sid, err := trace.SpanIDFromHex(tCtx.SpanID) 45 | if err != nil { 46 | return trace.SpanContextConfig{}, err 47 | } 48 | flag_bytes, err := hex.DecodeString(tCtx.TraceFlags) 49 | if err != nil { 50 | return trace.SpanContextConfig{}, err 51 | } 52 | tFlags := trace.TraceFlags(flag_bytes[0]) 53 | tState, err := trace.ParseTraceState(tCtx.TraceState) 54 | if err != nil { 55 | return trace.SpanContextConfig{}, err 56 | } 57 | return trace.SpanContextConfig{TraceID: tid, SpanID: sid, TraceFlags: tFlags, TraceState: tState, Remote: tCtx.Remote}, nil 58 | } 59 | -------------------------------------------------------------------------------- /examples/dsb_hotel/workflow/hotelreservation/DataModels.go: -------------------------------------------------------------------------------- 1 | // Package hotelreservation implements the workflow specification of the Hotel Reservation application 2 | package hotelreservation 3 | 4 | type Point struct { 5 | Pid string 6 | Plat float64 7 | Plon float64 8 | } 9 | 10 | func (p Point) remote() {} 11 | 12 | func (p Point) Id() string { return p.Pid } 13 | 14 | func (p Point) Lat() float64 { return p.Plat } 15 | 16 | func (p Point) Lon() float64 { return p.Plon } 17 | 18 | type User struct { 19 | Username string 20 | Password string 21 | } 22 | 23 | func (u User) remote() {} 24 | 25 | type RoomType struct { 26 | BookableRate float64 27 | Code string 28 | RoomDescription string 29 | TotalRate float64 30 | TotalRateInclusive float64 31 | } 32 | 33 | func (rt RoomType) remote() {} 34 | 35 | type RatePlan struct { 36 | HotelID string 37 | Code string 38 | InDate string 39 | OutDate string 40 | RType RoomType 41 | } 42 | 43 | func (rp RatePlan) remote() {} 44 | 45 | type Reservation struct { 46 | HotelId string 47 | CustomerName string 48 | InDate string 49 | OutDate string 50 | Number int64 51 | } 52 | 53 | func (r Reservation) remote() {} 54 | 55 | type HotelNumber struct { 56 | HotelId string 57 | Number int64 58 | } 59 | 60 | func (h HotelNumber) remote() {} 61 | 62 | type Hotel struct { 63 | HId string 64 | HLat float64 65 | HLon float64 66 | HRate float64 67 | HPrice float64 68 | } 69 | 70 | func (h Hotel) remote() {} 71 | 72 | type Address struct { 73 | StreetNumber string 74 | StreetName string 75 | City string 76 | State string 77 | Country string 78 | PostalCode string 79 | Lat float64 80 | Lon float64 81 | } 82 | 83 | func (a Address) remote() {} 84 | 85 | type HotelProfile struct { 86 | ID string 87 | Name string 88 | PhoneNumber string 89 | Description string 90 | Address Address 91 | } 92 | 93 | func (hp HotelProfile) remote() {} 94 | -------------------------------------------------------------------------------- /runtime/core/backend/reflect.go: -------------------------------------------------------------------------------- 1 | package backend 2 | 3 | import ( 4 | "reflect" 5 | 6 | "github.com/pkg/errors" 7 | ) 8 | 9 | func GetPointerValue(val any) (any, error) { 10 | val_ptr := reflect.ValueOf(val) 11 | if val_ptr.Kind() != reflect.Pointer { 12 | return nil, errors.Errorf("cannot indirect non-pointer type %v", val) 13 | } 14 | return reflect.Indirect(val_ptr).Interface(), nil 15 | } 16 | 17 | /* 18 | Lots of APIs want to copy results into interfaces. This is a helper method to do so. 19 | 20 | src can be anything; dst must be a pointer to the same type as src 21 | */ 22 | func CopyResult(src any, dst any) error { 23 | dst_ptr := reflect.ValueOf(dst) 24 | if dst_ptr.Kind() != reflect.Pointer || dst_ptr.IsNil() { 25 | return errors.Errorf("unable to copy result to type %v", reflect.TypeOf(dst)) 26 | } 27 | dst_val := reflect.Indirect(dst_ptr) 28 | src_val := reflect.ValueOf(src) 29 | 30 | if dst_val.Kind() == reflect.Slice && src_val.Kind() == reflect.Slice { 31 | // Special handling for slices: we want to support copying []any to []mytype 32 | new_dst := reflect.MakeSlice(dst_val.Type(), src_val.Len(), src_val.Len()) 33 | for i := 0; i < src_val.Len(); i++ { 34 | src_elem := src_val.Index(i).Interface() 35 | dst_elem := new_dst.Index(i).Addr().Interface() 36 | err := CopyResult(src_elem, dst_elem) 37 | if err != nil { 38 | return err 39 | } 40 | } 41 | dst_val.Set(new_dst) 42 | return nil 43 | } else { 44 | if !src_val.Type().AssignableTo(dst_val.Type()) { 45 | return errors.Errorf("unable to copy incompatible types %v and %v", src_val.Type(), dst_val.Type()) 46 | } 47 | dst_val.Set(src_val) 48 | return nil 49 | } 50 | } 51 | 52 | /* 53 | Sets the zero value of a pointer 54 | */ 55 | func SetZero(dst any) error { 56 | receiver_ptr := reflect.ValueOf(dst) 57 | if receiver_ptr.Kind() != reflect.Pointer || receiver_ptr.IsNil() { 58 | return errors.Errorf("unable to copy result to type %v", reflect.TypeOf(dst)) 59 | } 60 | reflect.Indirect(receiver_ptr).SetZero() 61 | return nil 62 | } 63 | -------------------------------------------------------------------------------- /runtime/plugins/sqlitereldb/reldb.go: -------------------------------------------------------------------------------- 1 | // Package sqlitereldb implements a [backend.RelationalDB] using the in-memory Golang 2 | // SQLite package [github.com/mattn/go-sqlite3]. 3 | // 4 | // If you are directly running go code (e.g. not from a docker container), the go-sqlite3 5 | // package requires CGO_ENABLED=1 and you must have gcc installed. See [https://github.com/mattn/go-sqlite3] 6 | // for more details about installation instructions. 7 | package sqlitereldb 8 | 9 | import ( 10 | "context" 11 | "database/sql" 12 | 13 | "github.com/jmoiron/sqlx" 14 | 15 | _ "github.com/mattn/go-sqlite3" 16 | ) 17 | 18 | // An in-memory relational DB that uses the go-sqlite3 package 19 | type SqliteRelDB struct { 20 | db *sqlx.DB 21 | } 22 | 23 | // Instantiates a new [SqliteRelDB] instance that stores query data in-memory 24 | func NewSqliteRelDB(ctx context.Context) (*SqliteRelDB, error) { 25 | db, err := sqlx.Open("sqlite3", "file:foobar?mode=memory&cache=shared") 26 | if err != nil { 27 | return nil, err 28 | } 29 | return &SqliteRelDB{db: db}, nil 30 | } 31 | 32 | // Exec implements backend.RelationalDB. 33 | func (s *SqliteRelDB) Exec(ctx context.Context, query string, args ...any) (sql.Result, error) { 34 | return s.db.ExecContext(ctx, query, args...) 35 | } 36 | 37 | // Query implements backend.RelationalDB. 38 | func (s *SqliteRelDB) Query(ctx context.Context, query string, args ...any) (*sql.Rows, error) { 39 | return s.db.QueryContext(ctx, query, args...) 40 | } 41 | 42 | // Get implements backend.RelationalDB. 43 | func (s *SqliteRelDB) Get(ctx context.Context, dst interface{}, query string, args ...any) error { 44 | return s.db.GetContext(ctx, dst, query, args...) 45 | } 46 | 47 | // Prepare implements backend.RelationalDB. 48 | func (s *SqliteRelDB) Prepare(ctx context.Context, query string) (*sql.Stmt, error) { 49 | return s.db.Prepare(query) 50 | } 51 | 52 | // Select implements backend.RelationalDB. 53 | func (s *SqliteRelDB) Select(ctx context.Context, dst interface{}, query string, args ...any) error { 54 | return s.db.SelectContext(ctx, dst, query, args...) 55 | } 56 | -------------------------------------------------------------------------------- /plugins/workload/ir.go: -------------------------------------------------------------------------------- 1 | package workload 2 | 3 | import ( 4 | "fmt" 5 | "os/exec" 6 | "path/filepath" 7 | "strings" 8 | 9 | "golang.org/x/exp/slog" 10 | 11 | "github.com/blueprint-uservices/blueprint/blueprint/pkg/blueprint/ioutil" 12 | "github.com/blueprint-uservices/blueprint/blueprint/pkg/ir" 13 | "github.com/blueprint-uservices/blueprint/plugins/goproc" 14 | ) 15 | 16 | // Wraps a goproc.Process in order to control its artifact generation 17 | type workloadGenerator struct { 18 | ir.ArtifactGenerator 19 | 20 | WorkloadName string // The name of the service impl for the workload generator 21 | ProcName string // The name of the proc of the workload generator 22 | ProcNode *goproc.Process 23 | } 24 | 25 | func newWorkloadGenerator(name string, procName string) *workloadGenerator { 26 | wlgen := &workloadGenerator{ 27 | WorkloadName: name, 28 | ProcName: procName, 29 | } 30 | return wlgen 31 | } 32 | 33 | // Implements [ir.IRNode] 34 | func (w *workloadGenerator) Name() string { 35 | return w.WorkloadName 36 | } 37 | 38 | // Implements [ir.IRNode] 39 | func (w *workloadGenerator) String() string { 40 | return ir.PrettyPrintNamespace(w.WorkloadName, "WorkloadGenerator", w.ProcNode.Edges, w.ProcNode.Nodes) 41 | } 42 | 43 | // Implements [ir.ArtifactGenerator] 44 | func (w *workloadGenerator) GenerateArtifacts(workspaceDir string) error { 45 | // Create a subdir for the actual process artifacts 46 | procDir, err := ioutil.CreateNodeDir(workspaceDir, w.ProcName) 47 | if err != nil { 48 | return err 49 | } 50 | 51 | // Generate process artifacts into the subdir 52 | err = w.ProcNode.GenerateArtifacts(procDir) 53 | if err != nil { 54 | return err 55 | } 56 | 57 | // Build the process 58 | mainPath := filepath.Join(procDir, w.ProcNode.ProcName) 59 | cmd := exec.Command("go", "build", "-C", mainPath) 60 | var out strings.Builder 61 | cmd.Stdout = &out 62 | cmd.Stderr = &out 63 | slog.Info(fmt.Sprintf("go build -C %s\n", mainPath)) 64 | if err := cmd.Run(); err != nil { 65 | slog.Error(out.String()) 66 | return err 67 | } 68 | return nil 69 | } 70 | -------------------------------------------------------------------------------- /blueprint/pkg/wiring/reflect.go: -------------------------------------------------------------------------------- 1 | package wiring 2 | 3 | import ( 4 | "reflect" 5 | 6 | "github.com/blueprint-uservices/blueprint/blueprint/pkg/blueprint" 7 | ) 8 | 9 | func getPointerValue(val any) (any, error) { 10 | val_ptr := reflect.ValueOf(val) 11 | if val_ptr.Kind() != reflect.Pointer { 12 | return nil, blueprint.Errorf("cannot indirect non-pointer type %v", val) 13 | } 14 | return reflect.Indirect(val_ptr).Interface(), nil 15 | } 16 | 17 | /* 18 | Lots of APIs want to copy results into interfaces. This is a helper method to do so. 19 | 20 | src can be anything; dst must be a pointer to the same type as src 21 | */ 22 | func copyResult(src any, dst any) error { 23 | dst_ptr := reflect.ValueOf(dst) 24 | if dst_ptr.Kind() != reflect.Pointer || dst_ptr.IsNil() { 25 | return blueprint.Errorf("unable to copy result to type %v", reflect.TypeOf(dst)) 26 | } 27 | dst_val := reflect.Indirect(dst_ptr) 28 | src_val := reflect.ValueOf(src) 29 | 30 | if dst_val.Kind() == reflect.Slice && src_val.Kind() == reflect.Slice { 31 | // Special handling for slices: we want to support copying []any to []mytype 32 | new_dst := reflect.MakeSlice(dst_val.Type(), src_val.Len(), src_val.Len()) 33 | for i := 0; i < src_val.Len(); i++ { 34 | src_elem := src_val.Index(i).Interface() 35 | dst_elem := new_dst.Index(i).Addr().Interface() 36 | err := copyResult(src_elem, dst_elem) 37 | if err != nil { 38 | return err 39 | } 40 | } 41 | dst_val.Set(new_dst) 42 | return nil 43 | } else { 44 | if !src_val.Type().AssignableTo(dst_val.Type()) { 45 | return blueprint.Errorf("unable to copy incompatible types %v and %v", src_val.Type(), dst_val.Type()) 46 | } 47 | dst_val.Set(src_val) 48 | return nil 49 | } 50 | } 51 | 52 | /* 53 | Sets the zero value of a pointer 54 | */ 55 | func setZero(dst any) error { 56 | receiver_ptr := reflect.ValueOf(dst) 57 | if receiver_ptr.Kind() != reflect.Pointer || receiver_ptr.IsNil() { 58 | return blueprint.Errorf("unable to copy result to type %v", reflect.TypeOf(dst)) 59 | } 60 | reflect.Indirect(receiver_ptr).SetZero() 61 | return nil 62 | } 63 | -------------------------------------------------------------------------------- /runtime/plugins/rabbitmq/README.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | # rabbitmq 4 | 5 | ```go 6 | import "github.com/blueprint-uservices/blueprint/runtime/plugins/rabbitmq" 7 | ``` 8 | 9 | Package rabbitmq provides a client\-wrapper implementation of the \[backend.Queue\] interface for a rabbitmq server. 10 | 11 | ## Index 12 | 13 | - [type RabbitMQ](<#RabbitMQ>) 14 | - [func NewRabbitMQ\(ctx context.Context, addr string, queue\_name string\) \(\*RabbitMQ, error\)](<#NewRabbitMQ>) 15 | - [func \(q \*RabbitMQ\) Pop\(ctx context.Context, dst interface\{\}\) \(bool, error\)](<#RabbitMQ.Pop>) 16 | - [func \(q \*RabbitMQ\) Push\(ctx context.Context, item interface\{\}\) \(bool, error\)](<#RabbitMQ.Push>) 17 | 18 | 19 | 20 | ## type [RabbitMQ]() 21 | 22 | Implements a Queue that uses the rabbitmq package 23 | 24 | ```go 25 | type RabbitMQ struct { 26 | // contains filtered or unexported fields 27 | } 28 | ``` 29 | 30 | 31 | ### func [NewRabbitMQ]() 32 | 33 | ```go 34 | func NewRabbitMQ(ctx context.Context, addr string, queue_name string) (*RabbitMQ, error) 35 | ``` 36 | 37 | Instantiates a new \[Queue\] instances that provides a queue interface via a RabbitMQ instance 38 | 39 | 40 | ### func \(\*RabbitMQ\) [Pop]() 41 | 42 | ```go 43 | func (q *RabbitMQ) Pop(ctx context.Context, dst interface{}) (bool, error) 44 | ``` 45 | 46 | Pop implements backend.Queue 47 | 48 | 49 | ### func \(\*RabbitMQ\) [Push]() 50 | 51 | ```go 52 | func (q *RabbitMQ) Push(ctx context.Context, item interface{}) (bool, error) 53 | ``` 54 | 55 | Push implements backend.Queue 56 | 57 | Generated by [gomarkdoc]() 58 | -------------------------------------------------------------------------------- /examples/leaf/wiring/specs/govector.go: -------------------------------------------------------------------------------- 1 | package specs 2 | 3 | import ( 4 | "strings" 5 | 6 | "github.com/blueprint-uservices/blueprint/blueprint/pkg/wiring" 7 | "github.com/blueprint-uservices/blueprint/examples/leaf/workflow/leaf" 8 | "github.com/blueprint-uservices/blueprint/plugins/cmdbuilder" 9 | "github.com/blueprint-uservices/blueprint/plugins/goproc" 10 | "github.com/blueprint-uservices/blueprint/plugins/govector" 11 | "github.com/blueprint-uservices/blueprint/plugins/http" 12 | "github.com/blueprint-uservices/blueprint/plugins/simple" 13 | "github.com/blueprint-uservices/blueprint/plugins/workflow" 14 | ) 15 | 16 | // [Govector] demonstrates how to instrument an application using GoVector to propagate vector clocks and to create logs with vector clocks. 17 | // The wiring spec uses govector logger as the custom logger for processes. 18 | // Each service is deployed in a separate process with services communicating using HTTP. 19 | var Govector = cmdbuilder.SpecOption{ 20 | Name: "govector", 21 | Description: "Deploys each service in a separate process, communicating using HTTP. Wraps each service in GoVector vector clocks and sets the GoVectorLogger for each process.", 22 | Build: makeGoVectorLoggerSpec, 23 | } 24 | 25 | func makeGoVectorLoggerSpec(spec wiring.WiringSpec) ([]string, error) { 26 | applyLoggerDefaults := func(service_name string) string { 27 | 28 | procName := strings.ReplaceAll(service_name, "service", "process") 29 | govector.Instrument(spec, service_name) 30 | 31 | http.Deploy(spec, service_name) 32 | proc := goproc.CreateProcess(spec, procName, service_name) 33 | govector.Logger(spec, procName) 34 | return proc 35 | } 36 | leaf_db := simple.NoSQLDB(spec, "leaf_db") 37 | leaf_cache := simple.Cache(spec, "leaf_cache") 38 | leaf_service := workflow.Service[*leaf.LeafService](spec, "leaf_service", leaf_cache, leaf_db) 39 | leaf_proc := applyLoggerDefaults(leaf_service) 40 | 41 | nonleaf_service := workflow.Service[leaf.NonLeafService](spec, "nonleaf_service", leaf_service) 42 | nonleaf_proc := applyLoggerDefaults(nonleaf_service) 43 | 44 | return []string{leaf_proc, nonleaf_proc}, nil 45 | } 46 | -------------------------------------------------------------------------------- /examples/sockshop/workflow/payment/README.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | # payment 4 | 5 | ```go 6 | import "github.com/blueprint-uservices/blueprint/examples/sockshop/workflow/payment" 7 | ``` 8 | 9 | Package payment implements the SockShop payment microservice. 10 | 11 | The service fakes payments, implementing simple logic whereby payments are authorized when they're below a predefined threshold, and rejected when they are above that threshold. 12 | 13 | ## Index 14 | 15 | - [Variables](<#variables>) 16 | - [type Authorisation](<#Authorisation>) 17 | - [type PaymentService](<#PaymentService>) 18 | - [func NewPaymentService\(ctx context.Context, declineOverAmount string\) \(PaymentService, error\)](<#NewPaymentService>) 19 | 20 | 21 | ## Variables 22 | 23 | 24 | 25 | ```go 26 | var ErrInvalidPaymentAmount = errors.New("invalid payment amount") 27 | ``` 28 | 29 | 30 | ## type [Authorisation]() 31 | 32 | 33 | 34 | ```go 35 | type Authorisation struct { 36 | Authorised bool `json:"authorised"` 37 | Message string `json:"message"` 38 | } 39 | ``` 40 | 41 | 42 | ## type [PaymentService]() 43 | 44 | PaymentService provides payment services 45 | 46 | ```go 47 | type PaymentService interface { 48 | Authorise(ctx context.Context, amount float32) (Authorisation, error) 49 | } 50 | ``` 51 | 52 | 53 | ### func [NewPaymentService]() 54 | 55 | ```go 56 | func NewPaymentService(ctx context.Context, declineOverAmount string) (PaymentService, error) 57 | ``` 58 | 59 | Returns a payment service where any transaction above the preconfigured threshold will return an invalid payment amount 60 | 61 | Generated by [gomarkdoc]() 62 | -------------------------------------------------------------------------------- /runtime/plugins/mysql/reldb.go: -------------------------------------------------------------------------------- 1 | // Package mysql provides a client-wrapper implementation of the [backend.RelationalDB] interface for a mysql server. 2 | package mysql 3 | 4 | import ( 5 | "context" 6 | "database/sql" 7 | 8 | "github.com/jmoiron/sqlx" 9 | 10 | _ "github.com/go-sql-driver/mysql" 11 | ) 12 | 13 | // Implements a RelationalDB that uses the mysql package 14 | type MySqlDB struct { 15 | name string 16 | db *sqlx.DB 17 | } 18 | 19 | // Instantiates a new [MySqlDB] instance that stores query data in a MySqlDB instance 20 | func NewMySqlDB(ctx context.Context, addr string, name string, username string, password string) (*MySqlDB, error) { 21 | db, err := sqlx.Open("mysql", username+":"+password+"@tcp("+addr+")/") 22 | if err != nil { 23 | return nil, err 24 | } 25 | 26 | if err != nil { 27 | return nil, err 28 | } 29 | 30 | _, err = db.Exec("CREATE DATABASE IF NOT EXISTS " + name) 31 | if err != nil { 32 | return nil, err 33 | } 34 | 35 | db.Close() 36 | 37 | db, err = sqlx.Open("mysql", username+":"+password+"@tcp("+addr+")/"+name) 38 | if err != nil { 39 | return nil, err 40 | } 41 | 42 | return &MySqlDB{name: name, db: db}, nil 43 | } 44 | 45 | // Exec implements backend.RelationalDB 46 | func (s *MySqlDB) Exec(ctx context.Context, query string, args ...any) (sql.Result, error) { 47 | return s.db.ExecContext(ctx, query, args...) 48 | } 49 | 50 | // Query implements backend.RelationalDB 51 | func (s *MySqlDB) Query(ctx context.Context, query string, args ...any) (*sql.Rows, error) { 52 | return s.db.QueryContext(ctx, query, args...) 53 | } 54 | 55 | // Prepare implements backend.RelationalDB 56 | func (s *MySqlDB) Prepare(ctx context.Context, query string) (*sql.Stmt, error) { 57 | return s.db.PrepareContext(ctx, query) 58 | } 59 | 60 | // Select implements backend.RelationalDB 61 | func (s *MySqlDB) Select(ctx context.Context, dst interface{}, query string, args ...any) error { 62 | return s.db.SelectContext(ctx, dst, query, args...) 63 | } 64 | 65 | // Get implements backend.RelationalDB 66 | func (s *MySqlDB) Get(ctx context.Context, dst interface{}, query string, args ...any) error { 67 | return s.db.GetContext(ctx, dst, query, args...) 68 | } 69 | -------------------------------------------------------------------------------- /examples/dsb_sn/workflow/socialnetwork/UniqueIdService.go: -------------------------------------------------------------------------------- 1 | package socialnetwork 2 | 3 | import ( 4 | "context" 5 | "log" 6 | "strconv" 7 | "strings" 8 | "time" 9 | ) 10 | 11 | // The UniqueIdService interface 12 | type UniqueIdService interface { 13 | // Returns a newly generated unique id to be used as a post's unique identifier. 14 | ComposeUniqueId(ctx context.Context, reqID int64, postType int64) (int64, error) 15 | } 16 | 17 | // Implementation of [UserTimelineService] 18 | type UniqueIdServiceImpl struct { 19 | counter int64 20 | current_timestamp int64 21 | machine_id string 22 | } 23 | 24 | // Implements UniqueIdService interface 25 | func NewUniqueIdServiceImpl(ctx context.Context) (UniqueIdService, error) { 26 | return &UniqueIdServiceImpl{counter: 0, current_timestamp: -1, machine_id: GetMachineID()}, nil 27 | } 28 | 29 | func (u *UniqueIdServiceImpl) getCounter(timestamp int64) int64 { 30 | if u.current_timestamp == timestamp { 31 | retVal := u.counter 32 | u.counter += 1 33 | return retVal 34 | } else { 35 | u.current_timestamp = timestamp 36 | u.counter = 1 37 | return 0 38 | } 39 | } 40 | 41 | // Implements UniqueIdService interface 42 | func (u *UniqueIdServiceImpl) ComposeUniqueId(ctx context.Context, reqID int64, postType int64) (int64, error) { 43 | timestamp := time.Now().UnixNano() / int64(time.Millisecond) 44 | idx := u.getCounter(timestamp) 45 | timestamp_hex := strconv.FormatInt(timestamp, 16) 46 | if len(timestamp_hex) > 10 { 47 | timestamp_hex = timestamp_hex[:10] 48 | } else if len(timestamp_hex) < 10 { 49 | timestamp_hex = strings.Repeat("0", 10-len(timestamp_hex)) + timestamp_hex 50 | } 51 | counter_hex := strconv.FormatInt(idx, 16) 52 | if len(counter_hex) > 1 { 53 | counter_hex = counter_hex[:1] 54 | } else if len(counter_hex) < 1 { 55 | counter_hex = strings.Repeat("0", 1-len(counter_hex)) + counter_hex 56 | } 57 | log.Println(u.machine_id, timestamp_hex, counter_hex) 58 | post_id_str := u.machine_id + timestamp_hex + counter_hex 59 | post_id, err := strconv.ParseInt(post_id_str, 16, 64) 60 | if err != nil { 61 | return 0, err 62 | } 63 | post_id = post_id & 0x7FFFFFFFFFFFFFFF 64 | return post_id, nil 65 | } 66 | -------------------------------------------------------------------------------- /plugins/circuitbreaker/wiring.go: -------------------------------------------------------------------------------- 1 | // Package circuitbreaker provides a Blueprint modifier for the client side of service calls. 2 | // 3 | // The plugin wraps clients with a circuitbreaker that blocks any new requests from being sent out over a connection if the failure rate exceeds a provided number in a fixed duration. The block is removed after the completion of the fixed duration interval. 4 | package circuitbreaker 5 | 6 | import ( 7 | "github.com/blueprint-uservices/blueprint/blueprint/pkg/blueprint" 8 | "github.com/blueprint-uservices/blueprint/blueprint/pkg/coreplugins/pointer" 9 | "github.com/blueprint-uservices/blueprint/blueprint/pkg/ir" 10 | "github.com/blueprint-uservices/blueprint/blueprint/pkg/wiring" 11 | "github.com/blueprint-uservices/blueprint/plugins/golang" 12 | "golang.org/x/exp/slog" 13 | ) 14 | 15 | // Adds circuit breaker functionality to all clients of the specified service. 16 | // Uses a [blueprint.WiringSpec]. 17 | // Circuit breaker trips when `failure_rate` percentage of requests fail. Minimum number of requests for the circuit to break is specified using `min_reqs`. 18 | // The circuit breaker counters are reset after `interval` duration. 19 | // Usage: 20 | // 21 | // AddCircuitBreaker(spec, "serviceA", 1000, 0.1, "1s") 22 | func AddCircuitBreaker(spec wiring.WiringSpec, serviceName string, min_reqs int64, failure_rate float64, interval string) { 23 | clientWrapper := serviceName + ".client.cb" 24 | 25 | ptr := pointer.GetPointer(spec, serviceName) 26 | if ptr == nil { 27 | slog.Error("Unable to add a circuit breaker to " + serviceName + " as it is not a pointer") 28 | return 29 | } 30 | 31 | clientNext := ptr.AddSrcModifier(spec, clientWrapper) 32 | 33 | spec.Define(clientWrapper, &CircuitBreakerClient{Min_Reqs: min_reqs, FailureRate: failure_rate}, func(ns wiring.Namespace) (ir.IRNode, error) { 34 | var wrapped golang.Service 35 | 36 | if err := ns.Get(clientNext, &wrapped); err != nil { 37 | return nil, blueprint.Errorf("CircuitBreaker %s expected %s to be a golang.Service, but encountered %s", clientWrapper, clientNext, err) 38 | } 39 | 40 | return newCircuitBreakerClient(clientWrapper, wrapped, min_reqs, failure_rate, interval) 41 | }) 42 | } 43 | -------------------------------------------------------------------------------- /examples/train_ticket/wiring/go.mod: -------------------------------------------------------------------------------- 1 | module github.com/blueprint-uservices/blueprint/examples/train_ticket/wiring 2 | 3 | go 1.22 4 | 5 | toolchain go1.22.1 6 | 7 | require ( 8 | github.com/blueprint-uservices/blueprint/blueprint v0.0.0-20240619221802-d064c5861c1e 9 | github.com/blueprint-uservices/blueprint/examples/train_ticket/tests v0.0.0-20240619221802-d064c5861c1e 10 | github.com/blueprint-uservices/blueprint/examples/train_ticket/workflow v0.0.0 11 | github.com/blueprint-uservices/blueprint/plugins v0.0.0-20240619221802-d064c5861c1e 12 | ) 13 | 14 | require ( 15 | github.com/blueprint-uservices/blueprint/runtime v0.0.0-20240619221802-d064c5861c1e // indirect 16 | github.com/go-logr/logr v1.4.1 // indirect 17 | github.com/go-logr/stdr v1.2.2 // indirect 18 | github.com/golang/snappy v0.0.4 // indirect 19 | github.com/google/uuid v1.6.0 // indirect 20 | github.com/klauspost/compress v1.17.8 // indirect 21 | github.com/montanaflynn/stats v0.7.1 // indirect 22 | github.com/otiai10/copy v1.14.0 // indirect 23 | github.com/rabbitmq/amqp091-go v1.9.0 // indirect 24 | github.com/xdg-go/pbkdf2 v1.0.0 // indirect 25 | github.com/xdg-go/scram v1.1.2 // indirect 26 | github.com/xdg-go/stringprep v1.0.4 // indirect 27 | github.com/youmark/pkcs8 v0.0.0-20240424034433-3c2c7870ae76 // indirect 28 | go.mongodb.org/mongo-driver v1.15.0 // indirect 29 | go.opentelemetry.io/otel v1.26.0 // indirect 30 | go.opentelemetry.io/otel/exporters/stdout/stdoutmetric v1.26.0 // indirect 31 | go.opentelemetry.io/otel/exporters/stdout/stdouttrace v1.26.0 // indirect 32 | go.opentelemetry.io/otel/metric v1.26.0 // indirect 33 | go.opentelemetry.io/otel/sdk v1.26.0 // indirect 34 | go.opentelemetry.io/otel/sdk/metric v1.26.0 // indirect 35 | go.opentelemetry.io/otel/trace v1.26.0 // indirect 36 | golang.org/x/crypto v0.22.0 // indirect 37 | golang.org/x/exp v0.0.0-20240416160154-fe59bbe5cc7f // indirect 38 | golang.org/x/mod v0.17.0 // indirect 39 | golang.org/x/sync v0.7.0 // indirect 40 | golang.org/x/sys v0.19.0 // indirect 41 | golang.org/x/text v0.14.0 // indirect 42 | golang.org/x/tools v0.20.0 // indirect 43 | ) 44 | 45 | replace github.com/blueprint-uservices/blueprint/examples/train_ticket/workflow => ../workflow 46 | -------------------------------------------------------------------------------- /plugins/goproc/log.go: -------------------------------------------------------------------------------- 1 | package goproc 2 | 3 | import ( 4 | "fmt" 5 | 6 | "github.com/blueprint-uservices/blueprint/blueprint/pkg/coreplugins/service" 7 | "github.com/blueprint-uservices/blueprint/blueprint/pkg/ir" 8 | "github.com/blueprint-uservices/blueprint/plugins/golang" 9 | "github.com/blueprint-uservices/blueprint/plugins/workflow/workflowspec" 10 | "github.com/blueprint-uservices/blueprint/runtime/plugins/slogger" 11 | "golang.org/x/exp/slog" 12 | ) 13 | 14 | type stdoutLogger struct { 15 | golang.Node 16 | service.ServiceNode 17 | golang.Instantiable 18 | 19 | LoggerName string 20 | Spec *workflowspec.Service 21 | } 22 | 23 | func newStdoutLogger(name string) (*stdoutLogger, error) { 24 | spec, err := workflowspec.GetService[slogger.SLogger]() 25 | node := &stdoutLogger{ 26 | LoggerName: name, 27 | Spec: spec, 28 | } 29 | return node, err 30 | } 31 | 32 | // Implements ir.IRNode 33 | func (node *stdoutLogger) Name() string { 34 | return node.LoggerName 35 | } 36 | 37 | // Implements ir.IRNode 38 | func (node *stdoutLogger) String() string { 39 | return node.Name() + " = SLogger()" 40 | } 41 | 42 | // Implements golang.ProvidesModule 43 | func (node *stdoutLogger) AddToWorkspace(builder golang.WorkspaceBuilder) error { 44 | return node.Spec.AddToWorkspace(builder) 45 | } 46 | 47 | // Implements golang.ProvidesInterface 48 | func (node *stdoutLogger) AddInterfaces(builder golang.ModuleBuilder) error { 49 | return node.Spec.AddToModule(builder) 50 | } 51 | 52 | // Implements service.ServiceNode 53 | func (node *stdoutLogger) GetInterface(ctx ir.BuildContext) (service.ServiceInterface, error) { 54 | return node.Spec.Iface.ServiceInterface(ctx), nil 55 | } 56 | 57 | // Implements golang.Instantiable 58 | func (node *stdoutLogger) AddInstantiation(builder golang.NamespaceBuilder) error { 59 | if builder.Visited(node.LoggerName) { 60 | return nil 61 | } 62 | 63 | slog.Info(fmt.Sprintf("Instantiating SLogger %v in %v/%v", node.LoggerName, builder.Info().Package.PackageName, builder.Info().FileName)) 64 | 65 | return builder.DeclareConstructor(node.LoggerName, node.Spec.Constructor.AsConstructor(), []ir.IRNode{}) 66 | } 67 | 68 | func (node *stdoutLogger) ImplementsGolangNode() {} 69 | -------------------------------------------------------------------------------- /plugins/linuxcontainer/linuxgen/template.go: -------------------------------------------------------------------------------- 1 | package linuxgen 2 | 3 | import ( 4 | "bytes" 5 | "os" 6 | "strings" 7 | "text/template" 8 | 9 | "github.com/blueprint-uservices/blueprint/plugins/linux" 10 | ) 11 | 12 | /* 13 | Any template that is executed using ExecuteTemplate will be able to use 14 | the helper functions defined in this file within the template. 15 | */ 16 | 17 | func ExecuteTemplate(name string, body string, args any) (string, error) { 18 | return newTemplateExecutor(args).exec(name, body, args) 19 | } 20 | 21 | func ExecuteTemplateToFile(name string, body string, args any, filename string) error { 22 | f, err := os.OpenFile(filename, os.O_CREATE|os.O_RDWR, 0755) 23 | if err != nil { 24 | return err 25 | } 26 | defer f.Close() 27 | 28 | code, err := ExecuteTemplate(name, body, args) 29 | if err != nil { 30 | return err 31 | } 32 | _, err = f.WriteString(code) 33 | return err 34 | } 35 | 36 | type templateExecutor struct { 37 | Funcs template.FuncMap 38 | } 39 | 40 | func newTemplateExecutor(args any) *templateExecutor { 41 | e := &templateExecutor{ 42 | Funcs: template.FuncMap{}, 43 | } 44 | 45 | e.Funcs["Get"] = e.Get 46 | e.Funcs["EnvVarName"] = e.EnvVarName 47 | e.Funcs["RunFuncName"] = e.RunFuncName 48 | e.Funcs["Title"] = e.TitleCase 49 | 50 | return e 51 | } 52 | 53 | func (e *templateExecutor) exec(name string, body string, args any) (string, error) { 54 | t, err := template.New(name).Funcs(e.Funcs).Parse(body) 55 | if err != nil { 56 | return "", err 57 | } 58 | buf := &bytes.Buffer{} 59 | err = t.Execute(buf, args) 60 | return buf.String(), err 61 | } 62 | 63 | func (e *templateExecutor) Get(name string) (string, error) { 64 | tmpl := `if [ -z "${ {{- EnvVarName .}}+x}" ]; then 65 | if ! {{RunFuncName .}}; then 66 | return $? 67 | fi 68 | fi` 69 | return e.exec("Get", tmpl, name) 70 | } 71 | 72 | func (e *templateExecutor) EnvVarName(name string) (string, error) { 73 | return linux.EnvVar(name), nil 74 | } 75 | 76 | func (e *templateExecutor) RunFuncName(name string) (string, error) { 77 | return linux.FuncName(name), nil 78 | } 79 | 80 | func (e *templateExecutor) TitleCase(arg string) (string, error) { 81 | return strings.Title(arg), nil 82 | } 83 | -------------------------------------------------------------------------------- /examples/leaf/wiring/specs/http.go: -------------------------------------------------------------------------------- 1 | package specs 2 | 3 | import ( 4 | "github.com/blueprint-uservices/blueprint/blueprint/pkg/wiring" 5 | "github.com/blueprint-uservices/blueprint/examples/leaf/workflow/leaf" 6 | "github.com/blueprint-uservices/blueprint/plugins/cmdbuilder" 7 | "github.com/blueprint-uservices/blueprint/plugins/goproc" 8 | "github.com/blueprint-uservices/blueprint/plugins/http" 9 | "github.com/blueprint-uservices/blueprint/plugins/mongodb" 10 | "github.com/blueprint-uservices/blueprint/plugins/opentelemetry" 11 | "github.com/blueprint-uservices/blueprint/plugins/simple" 12 | "github.com/blueprint-uservices/blueprint/plugins/workflow" 13 | "github.com/blueprint-uservices/blueprint/plugins/zipkin" 14 | ) 15 | 16 | // [HTTP] demonstrates how to deploy a service as an HTTP webserver using the [http] plugin. 17 | // The wiring spec also instruments services with distributed tracing using the [opentelemetry] plugin. 18 | // 19 | // [http]: https://github.com/Blueprint-uServices/blueprint/tree/main/plugins/http 20 | // [opentelemetry]: https://github.com/Blueprint-uServices/blueprint/tree/main/plugins/opentelemetry 21 | var HTTP = cmdbuilder.SpecOption{ 22 | Name: "http", 23 | Description: "Deploys each service in a separate process, communicating using HTTP. Wraps each service in Zipkin tracing.", 24 | Build: makeHTTPSpec, 25 | } 26 | 27 | func makeHTTPSpec(spec wiring.WiringSpec) ([]string, error) { 28 | trace_collector := zipkin.Collector(spec, "zipkin") 29 | 30 | applyHTTPDefaults := func(spec wiring.WiringSpec, serviceName string) string { 31 | opentelemetry.Instrument(spec, serviceName, trace_collector) 32 | http.Deploy(spec, serviceName) 33 | return goproc.Deploy(spec, serviceName) 34 | } 35 | 36 | leaf_db := mongodb.Container(spec, "leaf_db") 37 | leaf_cache := simple.Cache(spec, "leaf_cache") 38 | leaf_service := workflow.Service[*leaf.LeafServiceImpl](spec, "leaf_service", leaf_cache, leaf_db) 39 | leaf_proc := applyHTTPDefaults(spec, leaf_service) 40 | 41 | nonleaf_service := workflow.Service[leaf.NonLeafService](spec, "nonleaf_service", leaf_service) 42 | nonleaf_proc := applyHTTPDefaults(spec, nonleaf_service) 43 | 44 | return []string{leaf_proc, nonleaf_proc}, nil 45 | } 46 | -------------------------------------------------------------------------------- /blueprint/pkg/blueprint/ioutil/ioutil.go: -------------------------------------------------------------------------------- 1 | // Package ioutil implements filesystem related utility methods primarily for use by 2 | // plugins that produce artifacts onto the local filesystem. 3 | package ioutil 4 | 5 | import ( 6 | "errors" 7 | "os" 8 | "path/filepath" 9 | 10 | "github.com/blueprint-uservices/blueprint/blueprint/pkg/blueprint" 11 | "github.com/blueprint-uservices/blueprint/blueprint/pkg/blueprint/stringutil" 12 | ) 13 | 14 | // Returns true if the specified path exists and is a directory; false otherwise 15 | func IsDir(path string) bool { 16 | if info, err := os.Stat(path); err == nil && info.IsDir() { 17 | return true 18 | } 19 | return false 20 | } 21 | 22 | // Returns nil if the specified path exists and is a directory; if not returns an error. 23 | // If the specified path does not exist, then createIfAbsent dictates whether the 24 | // path is either created, or an error is returned. 25 | // This method can also return an error if it was unable to create a directory at 26 | // the given path. 27 | func CheckDir(path string, createIfAbsent bool) error { 28 | if info, err := os.Stat(path); err == nil { 29 | if info.IsDir() { 30 | return nil 31 | } else { 32 | return blueprint.Errorf("expected %s to be a directory but it is not", path) 33 | } 34 | } else if errors.Is(err, os.ErrNotExist) { 35 | if !createIfAbsent { 36 | return blueprint.Errorf("expected directory %s but it does not exist", path) 37 | } 38 | err = os.MkdirAll(path, 0755) 39 | if err != nil { 40 | return blueprint.Errorf("unable to create directory %s due to %s", path, err.Error()) 41 | } 42 | return nil 43 | } else { 44 | return blueprint.Errorf("unexpected error for directory %s due to %s", path, err.Error()) 45 | } 46 | } 47 | 48 | // Creates a subdirectory in the provided workspaceDir. 49 | // The provided name is first sanitized using [stringutil.CleanName] 50 | func CreateNodeDir(workspaceDir string, name string) (string, error) { 51 | nodeDir := filepath.Join(workspaceDir, stringutil.CleanName(name)) 52 | if err := CheckDir(nodeDir, true); err != nil { 53 | return "", blueprint.Errorf("unable to create output dir for %v at %v due to %v", name, nodeDir, err.Error()) 54 | } 55 | return nodeDir, nil 56 | } 57 | -------------------------------------------------------------------------------- /plugins/timeouts/wiring.go: -------------------------------------------------------------------------------- 1 | // Package timeouts provides a Blueprint modifier for the client side of service calls. 2 | // 3 | // The plugin configures clients with a timeout mechanism using contexts. 4 | // The plugin will generate a wrapper client class that will wait for a fixed amount of time (the specified timeout value) before canceling the context. Once the context is cancelled, the execution returns to the caller. 5 | // 6 | // Example Usage to add a "1s" timeout to each request: 7 | // timeouts.Add(spec, "my_service", "1s") 8 | package timeouts 9 | 10 | import ( 11 | "github.com/blueprint-uservices/blueprint/blueprint/pkg/blueprint" 12 | "github.com/blueprint-uservices/blueprint/blueprint/pkg/coreplugins/pointer" 13 | "github.com/blueprint-uservices/blueprint/blueprint/pkg/ir" 14 | "github.com/blueprint-uservices/blueprint/blueprint/pkg/wiring" 15 | "github.com/blueprint-uservices/blueprint/plugins/golang" 16 | "golang.org/x/exp/slog" 17 | ) 18 | 19 | // Adds timeouts to client calls for the specified service. 20 | // Uses a [blueprint.WiringSpec]. 21 | // Modifies the given service such that all clients to that service have a user-specified `timeout`. 22 | // 23 | // The `timeout` string must be a sequence of decimal numbers, each with optional fraction and a unit suffix, such as "300ms", "1.5h" or "2h45m". Valid time units are "ns", "us" (or "µs"), "ms", "s", "m", "h". 24 | // 25 | // Usage: 26 | // Add(spec, "my_service", "1s") 27 | func Add(spec wiring.WiringSpec, serviceName string, timeout string) { 28 | clientWrapper := serviceName + ".client.timeout" 29 | 30 | ptr := pointer.GetPointer(spec, serviceName) 31 | if ptr == nil { 32 | slog.Error("Unable to add timeouts to " + serviceName + " as it is not a pointer") 33 | } 34 | 35 | clientNext := ptr.AddSrcModifier(spec, clientWrapper) 36 | 37 | spec.Define(clientWrapper, &TimeoutClient{}, func(ns wiring.Namespace) (ir.IRNode, error) { 38 | var wrapped golang.Service 39 | 40 | if err := ns.Get(clientNext, &wrapped); err != nil { 41 | return nil, blueprint.Errorf("Timeouts %s expected %s to be a golang.Service, but encountered %s", clientWrapper, clientNext, err) 42 | } 43 | 44 | return newTimeoutClient(clientWrapper, wrapped, timeout) 45 | }) 46 | } 47 | -------------------------------------------------------------------------------- /examples/dsb_sn/workflow/socialnetwork/common.go: -------------------------------------------------------------------------------- 1 | package socialnetwork 2 | 3 | import ( 4 | "bytes" 5 | "encoding/json" 6 | "net" 7 | "strconv" 8 | "strings" 9 | 10 | "go.mongodb.org/mongo-driver/bson" 11 | ) 12 | 13 | // From: https://gist.github.com/tsilvers/085c5f39430ced605d970094edf167ba 14 | func GetMachineID() string { 15 | interfaces, err := net.Interfaces() 16 | if err != nil { 17 | return "0" 18 | } 19 | 20 | for _, i := range interfaces { 21 | if i.Flags&net.FlagUp != 0 && bytes.Compare(i.HardwareAddr, nil) != 0 { 22 | 23 | // Skip locally administered addresses 24 | if i.HardwareAddr[0]&2 == 2 { 25 | continue 26 | } 27 | 28 | var mac uint64 29 | for j, b := range i.HardwareAddr { 30 | if j >= 8 { 31 | break 32 | } 33 | mac <<= 8 34 | mac += uint64(b) 35 | } 36 | 37 | // Convert from uint64 to uint16 38 | mac_ui16 := uint16(mac) 39 | return strconv.FormatUint(uint64(mac_ui16), 16) 40 | } 41 | } 42 | 43 | return "0" 44 | } 45 | 46 | var letterRunes = []rune("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ") 47 | 48 | // Converts a json-encoded string to a bson.D document to be used as arguments for one of the other NoSQLDatabase functions 49 | func parseNoSQLDBQuery(query string) (bson.D, error) { 50 | return handleFormats(query) 51 | } 52 | 53 | func lower(f interface{}) interface{} { 54 | switch f := f.(type) { 55 | case []interface{}: 56 | for i := range f { 57 | f[i] = lower(f[i]) 58 | } 59 | return f 60 | case map[string]interface{}: 61 | lf := make(map[string]interface{}, len(f)) 62 | for k, v := range f { 63 | if k == "$elemMatch" { 64 | lf[k] = lower(v) 65 | } else { 66 | lf[strings.ToLower(k)] = lower(v) 67 | } 68 | } 69 | return lf 70 | default: 71 | return f 72 | } 73 | } 74 | 75 | func handleFormats(jsonQuery string) (bdoc bson.D, err error) { 76 | 77 | if jsonQuery == "" { 78 | bdoc = bson.D{} 79 | return 80 | } 81 | 82 | var f interface{} 83 | err = json.Unmarshal([]byte(jsonQuery), &f) 84 | if err != nil { 85 | return 86 | } 87 | 88 | f = lower(f) 89 | 90 | lowerQuery, err := json.Marshal(f) 91 | if err != nil { 92 | return 93 | } 94 | err = bson.UnmarshalExtJSON(lowerQuery, true, &bdoc) 95 | return 96 | } 97 | -------------------------------------------------------------------------------- /runtime/core/backend/reldb.go: -------------------------------------------------------------------------------- 1 | package backend 2 | 3 | import ( 4 | "context" 5 | "database/sql" 6 | ) 7 | 8 | // A Relational database backend is used for storing and querying structured data using SQL queries. 9 | // 10 | // SQL is relatively standardized in golang under the database/sql interfaces. Blueprint's [RelationalDB] 11 | // interface exposes the github.com/jmoiron/sqlx interfaces, which are more convenient for casual usage 12 | // and help in marshalling structs into rows and back. 13 | type RelationalDB interface { 14 | // Exec executes a query without returning any rows. The args are for any placeholder parameters in the query. 15 | // 16 | // Returns a [sql.Result] object from the [database/sql] package. 17 | Exec(ctx context.Context, query string, args ...any) (sql.Result, error) 18 | 19 | // Query executes a query that returns rows, typically a SELECT. The args are for any placeholder parameters in the query. 20 | // 21 | // Returns a [sql.Rows] object from the [database/sql] package, that can be used to access query results. 22 | // Rows' cursor starts before the first row of the result set. Use Next to advance from row to row. 23 | Query(ctx context.Context, query string, args ...any) (*sql.Rows, error) 24 | 25 | // Prepare creates a prepared statement for later queries or executions. Multiple queries or executions may 26 | // be run concurrently from the returned statement. The caller must call the statement's Close method 27 | // when the statement is no longer needed. 28 | // 29 | // Returns a [sql.Stmt] object from the [database/sql] package, that can be used to execute the prepared 30 | // statement. 31 | Prepare(ctx context.Context, query string) (*sql.Stmt, error) 32 | 33 | // Select using this DB. Any placeholder parameters are replaced with supplied args. 34 | // 35 | // Uses [github.com/jmoiron/sqlx] to marshal query results into dst. 36 | Select(ctx context.Context, dst interface{}, query string, args ...any) error 37 | 38 | // Get using this DB. Any placeholder parameters are replaced with supplied args. An error is returned if the result set is empty. 39 | // 40 | // Uses [github.com/jmoiron/sqlx] to marshal query results into dst. 41 | Get(ctx context.Context, dst interface{}, query string, args ...any) error 42 | } 43 | -------------------------------------------------------------------------------- /plugins/latency/codegen.go: -------------------------------------------------------------------------------- 1 | package latency 2 | 3 | import ( 4 | "fmt" 5 | "path/filepath" 6 | 7 | "github.com/blueprint-uservices/blueprint/plugins/golang" 8 | "github.com/blueprint-uservices/blueprint/plugins/golang/gocode" 9 | "github.com/blueprint-uservices/blueprint/plugins/golang/gogen" 10 | "golang.org/x/exp/slog" 11 | ) 12 | 13 | // code generation function called from the ir.go file. 14 | func generateServerWrapper(builder golang.ModuleBuilder, wrapped *gocode.ServiceInterface, outputPackage string) error { 15 | pkg, err := builder.CreatePackage(outputPackage) 16 | if err != nil { 17 | return err 18 | } 19 | 20 | server := serverArgs{ 21 | Package: pkg, 22 | Service: wrapped, 23 | Name: wrapped.BaseName + "_LatencyInjector", 24 | Imports: gogen.NewImports(pkg.Name), 25 | } 26 | 27 | server.Imports.AddPackages("context", "time") 28 | slog.Info(fmt.Sprintf("Generating %v/%v", server.Package.PackageName, wrapped.BaseName+"_LatencyInjector")) 29 | outputFile := filepath.Join(server.Package.Path, wrapped.BaseName+"_LatencyInjector.go") 30 | 31 | return gogen.ExecuteTemplateToFile("LatencyInjector", serverTemplate, server, outputFile) 32 | } 33 | 34 | type serverArgs struct { 35 | Package golang.PackageInfo 36 | Service *gocode.ServiceInterface 37 | Name string 38 | Imports *gogen.Imports 39 | } 40 | 41 | var serverTemplate = `// Blueprint: Auto-generated by LatencyInjector Plugin 42 | package {{.Package.ShortName}} 43 | 44 | {{.Imports}} 45 | 46 | type {{.Name}} struct { 47 | Server {{.Imports.NameOf .Service.UserType}} 48 | Latency time.Duration 49 | } 50 | 51 | func New_{{.Name}} (ctx context.Context, server {{.Imports.NameOf .Service.UserType}}, latency_str string) (*{{.Name}}, error) { 52 | handler := &{{.Name}}{} 53 | handler.Server = server 54 | latency, err := time.ParseDuration(latency_str) 55 | if err != nil { 56 | return nil, err 57 | } 58 | handler.Latency = latency 59 | return handler, nil 60 | } 61 | 62 | {{$service := .Service.Name -}} 63 | {{$receiver := .Name -}} 64 | {{ range $_, $f := .Service.Methods }} 65 | func (server *{{$receiver}}) {{$f.Name -}} ({{ArgVarsAndTypes $f "ctx context.Context"}}) ({{RetVarsAndTypes $f "err error"}}) { 66 | time.Sleep(server.Latency) 67 | return server.Server.{{$f.Name}}({{ArgVars $f "ctx"}}) 68 | } 69 | {{end}} 70 | ` 71 | -------------------------------------------------------------------------------- /plugins/linuxcontainer/linuxgen/runfunc.go: -------------------------------------------------------------------------------- 1 | package linuxgen 2 | 3 | import ( 4 | "strings" 5 | 6 | "github.com/blueprint-uservices/blueprint/blueprint/pkg/blueprint" 7 | "github.com/blueprint-uservices/blueprint/blueprint/pkg/blueprint/stringutil" 8 | "github.com/blueprint-uservices/blueprint/blueprint/pkg/ir" 9 | ) 10 | 11 | /* 12 | Used to generate bash run funcs that get gathered together into a single run.sh 13 | 14 | Process nodes provide the bash command needed to execute the process; but that's 15 | not enough. We need to make sure dependencies are correctly instantiated when needed. 16 | 17 | This code provides a wrapper function implementation around the commands provided 18 | by the process nodes 19 | */ 20 | 21 | func GenerateRunFunc(name string, runfunc string, deps ...ir.IRNode) (string, error) { 22 | runfunc = getFuncBody(runfunc) 23 | if runfunc == "" { 24 | return "", blueprint.Errorf("invalid runfunc for process %v %v", name, runfunc) 25 | } 26 | 27 | templateArgs := runFuncTemplateArgs{ 28 | Name: name, 29 | Dependencies: deps, 30 | RunFuncBody: stringutil.Reindent(runfunc, 8), 31 | } 32 | 33 | return ExecuteTemplate("runfunc", runFuncTemplate, templateArgs) 34 | } 35 | 36 | func getFuncBody(runcmd string) string { 37 | from := strings.Index(runcmd, "{") + 1 38 | to := strings.LastIndex(runcmd, "}") 39 | if from < to { 40 | return runcmd[from:to] 41 | } else { 42 | return "" 43 | } 44 | } 45 | 46 | type runFuncTemplateArgs struct { 47 | Name string 48 | Dependencies []ir.IRNode 49 | RunFuncBody string 50 | } 51 | 52 | var runFuncTemplate = `{{RunFuncName .Name}}() { 53 | cd $WORKSPACE_DIR 54 | 55 | {{ range $i, $dep := .Dependencies -}} 56 | {{Get $dep.Name}} 57 | 58 | {{end -}} 59 | 60 | run_{{RunFuncName .Name}}() { 61 | {{.RunFuncBody}} 62 | } 63 | 64 | if run_{{RunFuncName .Name}}; then 65 | if [ -z "${ {{- EnvVarName .Name}}+x}" ]; then 66 | echo "${WORKSPACE_NAME} error starting {{.Name}}: function {{RunFuncName .Name}} did not set {{EnvVarName .Name}}" 67 | return 1 68 | else 69 | echo "${WORKSPACE_NAME} started {{.Name}}" 70 | return 0 71 | fi 72 | else 73 | exitcode=$? 74 | echo "${WORKSPACE_NAME} aborting {{.Name}} due to exitcode ${exitcode} from {{RunFuncName .Name}}" 75 | return $exitcode 76 | fi 77 | }` 78 | -------------------------------------------------------------------------------- /examples/dsb_sn/wiring/go.mod: -------------------------------------------------------------------------------- 1 | module github.com/blueprint-uservices/blueprint/examples/dsb_sn/wiring 2 | 3 | go 1.22 4 | 5 | toolchain go1.22.1 6 | 7 | require github.com/blueprint-uservices/blueprint/blueprint v0.0.0-20240619221802-d064c5861c1e 8 | 9 | require github.com/blueprint-uservices/blueprint/plugins v0.0.0-20240619221802-d064c5861c1e 10 | 11 | require github.com/blueprint-uservices/blueprint/examples/dsb_sn/workflow v0.0.0 12 | 13 | require github.com/blueprint-uservices/blueprint/examples/dsb_sn/tests v0.0.0 14 | 15 | require ( 16 | github.com/blueprint-uservices/blueprint/runtime v0.0.0-20240619221802-d064c5861c1e // indirect 17 | github.com/bradfitz/gomemcache v0.0.0-20230905024940-24af94b03874 // indirect 18 | github.com/dgrijalva/jwt-go v3.2.0+incompatible // indirect 19 | github.com/go-logr/logr v1.4.1 // indirect 20 | github.com/go-logr/stdr v1.2.2 // indirect 21 | github.com/golang/snappy v0.0.4 // indirect 22 | github.com/klauspost/compress v1.17.8 // indirect 23 | github.com/montanaflynn/stats v0.7.1 // indirect 24 | github.com/otiai10/copy v1.14.0 // indirect 25 | github.com/xdg-go/pbkdf2 v1.0.0 // indirect 26 | github.com/xdg-go/scram v1.1.2 // indirect 27 | github.com/xdg-go/stringprep v1.0.4 // indirect 28 | github.com/youmark/pkcs8 v0.0.0-20240424034433-3c2c7870ae76 // indirect 29 | go.mongodb.org/mongo-driver v1.15.0 // indirect 30 | go.opentelemetry.io/otel v1.26.0 // indirect 31 | go.opentelemetry.io/otel/exporters/stdout/stdoutmetric v1.26.0 // indirect 32 | go.opentelemetry.io/otel/exporters/stdout/stdouttrace v1.26.0 // indirect 33 | go.opentelemetry.io/otel/metric v1.26.0 // indirect 34 | go.opentelemetry.io/otel/sdk v1.26.0 // indirect 35 | go.opentelemetry.io/otel/sdk/metric v1.26.0 // indirect 36 | go.opentelemetry.io/otel/trace v1.26.0 // indirect 37 | golang.org/x/crypto v0.22.0 // indirect 38 | golang.org/x/exp v0.0.0-20240416160154-fe59bbe5cc7f // indirect 39 | golang.org/x/mod v0.17.0 // indirect 40 | golang.org/x/sync v0.7.0 // indirect 41 | golang.org/x/sys v0.19.0 // indirect 42 | golang.org/x/text v0.14.0 // indirect 43 | golang.org/x/tools v0.20.0 // indirect 44 | ) 45 | 46 | replace github.com/blueprint-uservices/blueprint/examples/dsb_sn/workflow => ../workflow 47 | 48 | replace github.com/blueprint-uservices/blueprint/examples/dsb_sn/tests => ../tests 49 | -------------------------------------------------------------------------------- /examples/train_ticket/workflow/news/README.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | # news 4 | 5 | ```go 6 | import "github.com/blueprint-uservices/blueprint/examples/train_ticket/workflow/news" 7 | ``` 8 | 9 | package news implements the ts\-news\-service from the TrainTicket application 10 | 11 | ## Index 12 | 13 | - [type News](<#News>) 14 | - [type NewsService](<#NewsService>) 15 | - [type NewsServiceImpl](<#NewsServiceImpl>) 16 | - [func NewNewsServiceImpl\(ctx context.Context\) \(\*NewsServiceImpl, error\)](<#NewNewsServiceImpl>) 17 | - [func \(n \*NewsServiceImpl\) Hello\(ctx context.Context, val string\) \(string, error\)](<#NewsServiceImpl.Hello>) 18 | 19 | 20 | 21 | ## type [News]() 22 | 23 | 24 | 25 | ```go 26 | type News struct { 27 | Title string `bson:"Title"` 28 | Content string `bson:"Content"` 29 | } 30 | ``` 31 | 32 | 33 | ## type [NewsService]() 34 | 35 | News Service provides the latest news about the application 36 | 37 | ```go 38 | type NewsService interface { 39 | Hello(ctx context.Context, val string) (string, error) 40 | } 41 | ``` 42 | 43 | 44 | ## type [NewsServiceImpl]() 45 | 46 | News Service Implementation 47 | 48 | ```go 49 | type NewsServiceImpl struct{} 50 | ``` 51 | 52 | 53 | ### func [NewNewsServiceImpl]() 54 | 55 | ```go 56 | func NewNewsServiceImpl(ctx context.Context) (*NewsServiceImpl, error) 57 | ``` 58 | 59 | 60 | 61 | 62 | ### func \(\*NewsServiceImpl\) [Hello]() 63 | 64 | ```go 65 | func (n *NewsServiceImpl) Hello(ctx context.Context, val string) (string, error) 66 | ``` 67 | 68 | 69 | 70 | Generated by [gomarkdoc]() 71 | --------------------------------------------------------------------------------