diff --git a/go.mod b/go.mod index f752d29..3fcc777 100644 --- a/go.mod +++ b/go.mod @@ -29,7 +29,7 @@ require ( github.com/m3o/goduckgo v0.0.0-20210630141545-c760fe67b945 github.com/mattheath/base62 v0.0.0-20150408093626-b80cdc656a7a // indirect github.com/mattheath/kala v0.0.0-20171219141654-d6276794bf0e - github.com/micro/micro/v3 v3.7.1-0.20211111170433-1ebb8328e280 + github.com/micro/micro/v3 v3.8.0 github.com/miekg/dns v1.1.31 // indirect github.com/oschwald/geoip2-golang v1.5.0 github.com/patrickmn/go-cache v2.1.0+incompatible diff --git a/go.sum b/go.sum index 266cdaf..859d75a 100644 --- a/go.sum +++ b/go.sum @@ -166,6 +166,7 @@ github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7 github.com/evanphx/json-patch/v5 v5.0.0 h1:dKTrUeykyQwKb/kx7Z+4ukDs6l+4L41HqG1XHnhX7WE= github.com/evanphx/json-patch/v5 v5.0.0/go.mod h1:G79N1coSVB93tBe7j6PhzjmR3/2VvlbKOFpnXhI9Bw4= github.com/exoscale/egoscale v0.18.1/go.mod h1:Z7OOdzzTOz1Q1PjQXumlz9Wn/CddH0zSYdCF3rnBKXE= +github.com/fatih/camelcase v1.0.0/go.mod h1:yN2Sb0lFhZJUdVvtELVWefmrXpuZESvPmqwoZc+/fpc= github.com/fatih/color v1.7.0 h1:DkWD4oS2D8LGGgTQ6IvwJJXSL5Vp2ffcQg58nFV38Ys= github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4= github.com/fatih/structs v1.1.0/go.mod h1:9NiDSp5zOcgEDl+j00MP/WkGVPOlPRLejGD8Ga6PJ7M= @@ -484,6 +485,8 @@ github.com/mattn/go-tty v0.0.0-20180219170247-931426f7535a/go.mod h1:XPvLUNfbS4f github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= github.com/micro/micro/v3 v3.7.1-0.20211111170433-1ebb8328e280 h1:SOthdbPABgdxgsi9d7lXBDE8WzlTvj+2owHR51vW2AA= github.com/micro/micro/v3 v3.7.1-0.20211111170433-1ebb8328e280/go.mod h1:NqYnFOGrnc0Apk912w49oX9qIk1YDJcCaO+y+CLaAXA= +github.com/micro/micro/v3 v3.8.0 h1:RTH2835RJ4/aqLZGMjGCIf7HroCmYlJh2KRHHuSL/AE= +github.com/micro/micro/v3 v3.8.0/go.mod h1:gjFa8T2ouD6BvorPTVPXLWtrRJwSKT5KxUNuu23Kkts= github.com/miekg/dns v1.1.15/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg= github.com/miekg/dns v1.1.27/go.mod h1:KNUDUusw/aVsxyTYZM1oqvCicbwhgbNgztCETuNZ7xM= github.com/miekg/dns v1.1.31 h1:sJFOl9BgwbYAWOGEwr61FU28pqsBNdpRBnhGXtO06Oo= diff --git a/movie/.gitignore b/movie/.gitignore new file mode 100644 index 0000000..e106cc9 --- /dev/null +++ b/movie/.gitignore @@ -0,0 +1,2 @@ + +movie diff --git a/movie/Dockerfile b/movie/Dockerfile new file mode 100644 index 0000000..62321bc --- /dev/null +++ b/movie/Dockerfile @@ -0,0 +1,3 @@ +FROM alpine +ADD movie /movie +ENTRYPOINT [ "/movie" ] diff --git a/movie/Makefile b/movie/Makefile new file mode 100644 index 0000000..5653c95 --- /dev/null +++ b/movie/Makefile @@ -0,0 +1,28 @@ + +GOPATH:=$(shell go env GOPATH) +.PHONY: init +init: + go get -u github.com/golang/protobuf/proto + go get -u github.com/golang/protobuf/protoc-gen-go + go get github.com/micro/micro/v3/cmd/protoc-gen-micro + go get github.com/micro/micro/v3/cmd/protoc-gen-openapi + +.PHONY: api +api: + protoc --openapi_out=. --proto_path=. proto/movie.proto + +.PHONY: proto +proto: + protoc --proto_path=. --micro_out=. --go_out=:. proto/movie.proto + +.PHONY: build +build: + go build -o movie *.go + +.PHONY: test +test: + go test -v ./... -cover + +.PHONY: docker +docker: + docker build . -t movie:latest diff --git a/movie/README.md b/movie/README.md new file mode 100644 index 0000000..85fc8f7 --- /dev/null +++ b/movie/README.md @@ -0,0 +1,7 @@ +# Movie Service + +Search movies by IMDb ID or title + +Powered by [https://www.themoviedb.org/](https://www.themoviedb.org/). + +Generate API Key: [https://www.themoviedb.org/settings/api](https://www.themoviedb.org/settings/api) \ No newline at end of file diff --git a/movie/examples.json b/movie/examples.json new file mode 100644 index 0000000..10b95ce --- /dev/null +++ b/movie/examples.json @@ -0,0 +1,65 @@ +{ + "search": [ + { + "title": "Search for movies", + "run_check": false, + "description": "Search for movies", + "request": { + "language": "en-US", + "query": "inception", + "page": 1, + "region": "US", + "year": 2010, + "primary_release_year": 2010 + }, + "response": { + "total_results": 2, + "total_pages": 1, + "page": 1, + "results": [ + { + "poster_path": "/9gk7adHYeDvHkCSEqAvQNLV5Uge.jpg", + "adult": false, + "overview": "Cobb, a skilled thief who commits corporate espionage by infiltrating the subconscious of his targets is offered a chance to regain his old life as payment for a task considered to be impossible: \"inception\", the implantation of another person's idea into a target's subconscious.", + "release_date": "2010-07-16", + "genre_ids": [ + 28, + 878, + 12 + ], + "id": 27205, + "original_title": "Inception", + "original_language": "en", + "title": "Inception", + "backdrop_path": "/9gk7adHYeDvHkCSEqAvQNLV5Uge.jpg", + "popularity": 130.507, + "vote_count": 30372, + "video": false, + "vote_average": 8.4 + }, + { + "poster_path": "/sNxqwtyHMNQwKWoFYDqcYTui5Ok.jpg", + "adult": false, + "overview": "The Cobol Job is a fourteen-minute animated prequel to Christopher Nolan’s award-winning movie: Inception, detailing the heist on Mr. Kaneda's mind by Nash, Cobb, Arthur, and several Cobol Engineering thugs.", + "release_date": "2010-12-07", + "genre_ids": [ + 16, + 28, + 53, + 878 + ], + "id": 64956, + "original_title": "Inception: The Cobol Job", + "original_language": "en", + "title": "Inception: The Cobol Job", + "backdrop_path": "/sNxqwtyHMNQwKWoFYDqcYTui5Ok.jpg", + "popularity": 9.748, + "vote_count": 269, + "video": false, + "vote_average": 7.3 + } + ] + } + } + ] +} diff --git a/movie/generate.go b/movie/generate.go new file mode 100644 index 0000000..96f431a --- /dev/null +++ b/movie/generate.go @@ -0,0 +1,2 @@ +package main +//go:generate make proto diff --git a/movie/handler/movie.go b/movie/handler/movie.go new file mode 100644 index 0000000..fd816b8 --- /dev/null +++ b/movie/handler/movie.go @@ -0,0 +1,138 @@ +package handler + +import ( + "context" + "encoding/json" + "fmt" + "io/ioutil" + "net/http" + "net/url" + + "github.com/micro/micro/v3/service/config" + "github.com/micro/micro/v3/service/errors" + "github.com/micro/micro/v3/service/logger" + + pb "github.com/micro/services/movie/proto" +) + +type Movie struct { + Api string + Key string +} + +func New() *Movie { + v, err := config.Get("movie.api") + if err != nil { + logger.Fatal("movie.api config not found: %v", err) + } + + api := v.String("") + if len(api) == 0 { + logger.Fatal("movie.api config not found") + } + + v, err = config.Get("movie.key") + if err != nil { + logger.Fatal("movie.key config not found: %v", err) + } + + key := v.String("") + if len(key) == 0 { + logger.Fatal("movie.key config not found") + } + + return &Movie{ + Api: api, + Key: key, + } +} + +func (m *Movie) Search(_ context.Context, req *pb.SearchRequest, rsp *pb.SearchResponse) error { + if req.Page == 0 { + req.Page = 1 + } + + vals := url.Values{} + vals.Set("api_key", m.Key) + vals.Set("query", req.Query) + vals.Set("language", req.Language) + vals.Set("page", fmt.Sprintf("%d", req.Page)) + vals.Set("include_adult", "false") + vals.Set("region", req.Region) + if req.Year > 0 { + vals.Set("year", fmt.Sprintf("%d", req.Year)) + } + if req.PrimaryReleaseYear > 0 { + vals.Set("primary_release_year", fmt.Sprintf("%d", req.PrimaryReleaseYear)) + } + + api := fmt.Sprintf("%s/search/movie?%s", m.Api, vals.Encode()) + + resp, err := http.Get(api) + if err != nil { + logger.Errorf("Failed to get movie search results: %v\n", err) + return errors.InternalServerError("movie.search", "failed to get movie search results") + } + + if resp.StatusCode != http.StatusOK { + logger.Errorf("Movie search api status code is not OK! status=%d\n", resp.StatusCode) + return errors.InternalServerError("movie.search", fmt.Sprintf("movie search status is not 200, it's %d", resp.StatusCode)) + } + + defer func() { + if err := resp.Body.Close(); err != nil { + logger.Errorf("Movie search close response body error: %v\n", err) + } + }() + + b, _ := ioutil.ReadAll(resp.Body) + + var respBody map[string]interface{} + + if err := json.Unmarshal(b, &respBody); err != nil { + logger.Errorf("Failed to unmarshal movie search results: %v\n", err) + return errors.InternalServerError("movie.search", "failed to unmarshal movie search results") + } + + rsp.Page = int32(respBody["page"].(float64)) + rsp.TotalPages = int32(respBody["total_pages"].(float64)) + rsp.TotalResults = int32(respBody["total_results"].(float64)) + + results := respBody["results"].([]interface{}) + + for _, v := range results { + info := v.(map[string]interface{}) + + genreIds := make([]int32, 0) + if ids, ok := info["genre_ids"].([]interface{}); ok { + for _, id := range ids { + genreIds = append(genreIds, int32(id.(float64))) + } + } + + mi := &pb.MovieInfo{} + + id, _ := info["id"].(float64) + mi.Id = int32(id) + + voteCount, _ := info["vote_count"].(float64) + mi.VoteCount = int32(voteCount) + + mi.PosterPath, _ = info["poster_path"].(string) + mi.Adult, _ = info["adult"].(bool) + mi.Overview, _ = info["overview"].(string) + mi.ReleaseDate, _ = info["release_date"].(string) + mi.GenreIds = genreIds + mi.OriginalTitle, _ = info["original_title"].(string) + mi.OriginalLanguage, _ = info["original_language"].(string) + mi.Title, _ = info["title"].(string) + mi.BackdropPath, _ = info["poster_path"].(string) + mi.Popularity, _ = info["popularity"].(float64) + mi.Video, _ = info["video"].(bool) + mi.VoteAverage, _ = info["vote_average"].(float64) + + rsp.Results = append(rsp.Results, mi) + } + + return nil +} diff --git a/movie/main.go b/movie/main.go new file mode 100644 index 0000000..d2fdcb6 --- /dev/null +++ b/movie/main.go @@ -0,0 +1,25 @@ +package main + +import ( + "github.com/micro/services/movie/handler" + pb "github.com/micro/services/movie/proto" + + "github.com/micro/micro/v3/service" + "github.com/micro/micro/v3/service/logger" +) + +func main() { + // Create service + srv := service.New( + service.Name("movie"), + service.Version("latest"), + ) + + // Register handler + pb.RegisterMovieHandler(srv.Server(), handler.New()) + + // Run service + if err := srv.Run(); err != nil { + logger.Fatal(err) + } +} diff --git a/movie/micro.mu b/movie/micro.mu new file mode 100644 index 0000000..c25a4ee --- /dev/null +++ b/movie/micro.mu @@ -0,0 +1 @@ +service movie diff --git a/movie/proto/movie.pb.go b/movie/proto/movie.pb.go new file mode 100644 index 0000000..1cd2107 --- /dev/null +++ b/movie/proto/movie.pb.go @@ -0,0 +1,487 @@ +// Code generated by protoc-gen-go. DO NOT EDIT. +// versions: +// protoc-gen-go v1.27.1 +// protoc v3.19.1 +// source: proto/movie.proto + +package movie + +import ( + protoreflect "google.golang.org/protobuf/reflect/protoreflect" + protoimpl "google.golang.org/protobuf/runtime/protoimpl" + reflect "reflect" + sync "sync" +) + +const ( + // Verify that this generated code is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion) + // Verify that runtime/protoimpl is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) +) + +type MovieInfo struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + PosterPath string `protobuf:"bytes,1,opt,name=poster_path,json=posterPath,proto3" json:"poster_path,omitempty"` + Adult bool `protobuf:"varint,2,opt,name=adult,proto3" json:"adult,omitempty"` + Overview string `protobuf:"bytes,3,opt,name=overview,proto3" json:"overview,omitempty"` + ReleaseDate string `protobuf:"bytes,4,opt,name=release_date,json=releaseDate,proto3" json:"release_date,omitempty"` + GenreIds []int32 `protobuf:"varint,5,rep,packed,name=genre_ids,json=genreIds,proto3" json:"genre_ids,omitempty"` + Id int32 `protobuf:"varint,6,opt,name=id,proto3" json:"id,omitempty"` + OriginalTitle string `protobuf:"bytes,7,opt,name=original_title,json=originalTitle,proto3" json:"original_title,omitempty"` + OriginalLanguage string `protobuf:"bytes,8,opt,name=original_language,json=originalLanguage,proto3" json:"original_language,omitempty"` + Title string `protobuf:"bytes,9,opt,name=title,proto3" json:"title,omitempty"` + BackdropPath string `protobuf:"bytes,10,opt,name=backdrop_path,json=backdropPath,proto3" json:"backdrop_path,omitempty"` + Popularity float64 `protobuf:"fixed64,11,opt,name=popularity,proto3" json:"popularity,omitempty"` + VoteCount int32 `protobuf:"varint,12,opt,name=vote_count,json=voteCount,proto3" json:"vote_count,omitempty"` + Video bool `protobuf:"varint,13,opt,name=video,proto3" json:"video,omitempty"` + VoteAverage float64 `protobuf:"fixed64,14,opt,name=vote_average,json=voteAverage,proto3" json:"vote_average,omitempty"` +} + +func (x *MovieInfo) Reset() { + *x = MovieInfo{} + if protoimpl.UnsafeEnabled { + mi := &file_proto_movie_proto_msgTypes[0] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *MovieInfo) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*MovieInfo) ProtoMessage() {} + +func (x *MovieInfo) ProtoReflect() protoreflect.Message { + mi := &file_proto_movie_proto_msgTypes[0] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use MovieInfo.ProtoReflect.Descriptor instead. +func (*MovieInfo) Descriptor() ([]byte, []int) { + return file_proto_movie_proto_rawDescGZIP(), []int{0} +} + +func (x *MovieInfo) GetPosterPath() string { + if x != nil { + return x.PosterPath + } + return "" +} + +func (x *MovieInfo) GetAdult() bool { + if x != nil { + return x.Adult + } + return false +} + +func (x *MovieInfo) GetOverview() string { + if x != nil { + return x.Overview + } + return "" +} + +func (x *MovieInfo) GetReleaseDate() string { + if x != nil { + return x.ReleaseDate + } + return "" +} + +func (x *MovieInfo) GetGenreIds() []int32 { + if x != nil { + return x.GenreIds + } + return nil +} + +func (x *MovieInfo) GetId() int32 { + if x != nil { + return x.Id + } + return 0 +} + +func (x *MovieInfo) GetOriginalTitle() string { + if x != nil { + return x.OriginalTitle + } + return "" +} + +func (x *MovieInfo) GetOriginalLanguage() string { + if x != nil { + return x.OriginalLanguage + } + return "" +} + +func (x *MovieInfo) GetTitle() string { + if x != nil { + return x.Title + } + return "" +} + +func (x *MovieInfo) GetBackdropPath() string { + if x != nil { + return x.BackdropPath + } + return "" +} + +func (x *MovieInfo) GetPopularity() float64 { + if x != nil { + return x.Popularity + } + return 0 +} + +func (x *MovieInfo) GetVoteCount() int32 { + if x != nil { + return x.VoteCount + } + return 0 +} + +func (x *MovieInfo) GetVideo() bool { + if x != nil { + return x.Video + } + return false +} + +func (x *MovieInfo) GetVoteAverage() float64 { + if x != nil { + return x.VoteAverage + } + return 0 +} + +type SearchRequest struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + // a ISO 639-1 value to display translated data + Language string `protobuf:"bytes,1,opt,name=language,proto3" json:"language,omitempty"` + // a text query to search + Query string `protobuf:"bytes,2,opt,name=query,proto3" json:"query,omitempty"` + // page to query + Page int32 `protobuf:"varint,3,opt,name=page,proto3" json:"page,omitempty"` + // a ISO 3166-1 code to filter release dates. + Region string `protobuf:"bytes,4,opt,name=region,proto3" json:"region,omitempty"` + Year int32 `protobuf:"varint,5,opt,name=year,proto3" json:"year,omitempty"` + PrimaryReleaseYear int32 `protobuf:"varint,6,opt,name=primary_release_year,json=primaryReleaseYear,proto3" json:"primary_release_year,omitempty"` +} + +func (x *SearchRequest) Reset() { + *x = SearchRequest{} + if protoimpl.UnsafeEnabled { + mi := &file_proto_movie_proto_msgTypes[1] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *SearchRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*SearchRequest) ProtoMessage() {} + +func (x *SearchRequest) ProtoReflect() protoreflect.Message { + mi := &file_proto_movie_proto_msgTypes[1] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use SearchRequest.ProtoReflect.Descriptor instead. +func (*SearchRequest) Descriptor() ([]byte, []int) { + return file_proto_movie_proto_rawDescGZIP(), []int{1} +} + +func (x *SearchRequest) GetLanguage() string { + if x != nil { + return x.Language + } + return "" +} + +func (x *SearchRequest) GetQuery() string { + if x != nil { + return x.Query + } + return "" +} + +func (x *SearchRequest) GetPage() int32 { + if x != nil { + return x.Page + } + return 0 +} + +func (x *SearchRequest) GetRegion() string { + if x != nil { + return x.Region + } + return "" +} + +func (x *SearchRequest) GetYear() int32 { + if x != nil { + return x.Year + } + return 0 +} + +func (x *SearchRequest) GetPrimaryReleaseYear() int32 { + if x != nil { + return x.PrimaryReleaseYear + } + return 0 +} + +type SearchResponse struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + TotalResults int32 `protobuf:"varint,1,opt,name=total_results,json=totalResults,proto3" json:"total_results,omitempty"` + TotalPages int32 `protobuf:"varint,2,opt,name=total_pages,json=totalPages,proto3" json:"total_pages,omitempty"` + Page int32 `protobuf:"varint,3,opt,name=page,proto3" json:"page,omitempty"` + Results []*MovieInfo `protobuf:"bytes,4,rep,name=results,proto3" json:"results,omitempty"` +} + +func (x *SearchResponse) Reset() { + *x = SearchResponse{} + if protoimpl.UnsafeEnabled { + mi := &file_proto_movie_proto_msgTypes[2] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *SearchResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*SearchResponse) ProtoMessage() {} + +func (x *SearchResponse) ProtoReflect() protoreflect.Message { + mi := &file_proto_movie_proto_msgTypes[2] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use SearchResponse.ProtoReflect.Descriptor instead. +func (*SearchResponse) Descriptor() ([]byte, []int) { + return file_proto_movie_proto_rawDescGZIP(), []int{2} +} + +func (x *SearchResponse) GetTotalResults() int32 { + if x != nil { + return x.TotalResults + } + return 0 +} + +func (x *SearchResponse) GetTotalPages() int32 { + if x != nil { + return x.TotalPages + } + return 0 +} + +func (x *SearchResponse) GetPage() int32 { + if x != nil { + return x.Page + } + return 0 +} + +func (x *SearchResponse) GetResults() []*MovieInfo { + if x != nil { + return x.Results + } + return nil +} + +var File_proto_movie_proto protoreflect.FileDescriptor + +var file_proto_movie_proto_rawDesc = []byte{ + 0x0a, 0x11, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2f, 0x6d, 0x6f, 0x76, 0x69, 0x65, 0x2e, 0x70, 0x72, + 0x6f, 0x74, 0x6f, 0x12, 0x05, 0x6d, 0x6f, 0x76, 0x69, 0x65, 0x22, 0xb5, 0x03, 0x0a, 0x09, 0x4d, + 0x6f, 0x76, 0x69, 0x65, 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x1f, 0x0a, 0x0b, 0x70, 0x6f, 0x73, 0x74, + 0x65, 0x72, 0x5f, 0x70, 0x61, 0x74, 0x68, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x70, + 0x6f, 0x73, 0x74, 0x65, 0x72, 0x50, 0x61, 0x74, 0x68, 0x12, 0x14, 0x0a, 0x05, 0x61, 0x64, 0x75, + 0x6c, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x08, 0x52, 0x05, 0x61, 0x64, 0x75, 0x6c, 0x74, 0x12, + 0x1a, 0x0a, 0x08, 0x6f, 0x76, 0x65, 0x72, 0x76, 0x69, 0x65, 0x77, 0x18, 0x03, 0x20, 0x01, 0x28, + 0x09, 0x52, 0x08, 0x6f, 0x76, 0x65, 0x72, 0x76, 0x69, 0x65, 0x77, 0x12, 0x21, 0x0a, 0x0c, 0x72, + 0x65, 0x6c, 0x65, 0x61, 0x73, 0x65, 0x5f, 0x64, 0x61, 0x74, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, + 0x09, 0x52, 0x0b, 0x72, 0x65, 0x6c, 0x65, 0x61, 0x73, 0x65, 0x44, 0x61, 0x74, 0x65, 0x12, 0x1b, + 0x0a, 0x09, 0x67, 0x65, 0x6e, 0x72, 0x65, 0x5f, 0x69, 0x64, 0x73, 0x18, 0x05, 0x20, 0x03, 0x28, + 0x05, 0x52, 0x08, 0x67, 0x65, 0x6e, 0x72, 0x65, 0x49, 0x64, 0x73, 0x12, 0x0e, 0x0a, 0x02, 0x69, + 0x64, 0x18, 0x06, 0x20, 0x01, 0x28, 0x05, 0x52, 0x02, 0x69, 0x64, 0x12, 0x25, 0x0a, 0x0e, 0x6f, + 0x72, 0x69, 0x67, 0x69, 0x6e, 0x61, 0x6c, 0x5f, 0x74, 0x69, 0x74, 0x6c, 0x65, 0x18, 0x07, 0x20, + 0x01, 0x28, 0x09, 0x52, 0x0d, 0x6f, 0x72, 0x69, 0x67, 0x69, 0x6e, 0x61, 0x6c, 0x54, 0x69, 0x74, + 0x6c, 0x65, 0x12, 0x2b, 0x0a, 0x11, 0x6f, 0x72, 0x69, 0x67, 0x69, 0x6e, 0x61, 0x6c, 0x5f, 0x6c, + 0x61, 0x6e, 0x67, 0x75, 0x61, 0x67, 0x65, 0x18, 0x08, 0x20, 0x01, 0x28, 0x09, 0x52, 0x10, 0x6f, + 0x72, 0x69, 0x67, 0x69, 0x6e, 0x61, 0x6c, 0x4c, 0x61, 0x6e, 0x67, 0x75, 0x61, 0x67, 0x65, 0x12, + 0x14, 0x0a, 0x05, 0x74, 0x69, 0x74, 0x6c, 0x65, 0x18, 0x09, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, + 0x74, 0x69, 0x74, 0x6c, 0x65, 0x12, 0x23, 0x0a, 0x0d, 0x62, 0x61, 0x63, 0x6b, 0x64, 0x72, 0x6f, + 0x70, 0x5f, 0x70, 0x61, 0x74, 0x68, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0c, 0x62, 0x61, + 0x63, 0x6b, 0x64, 0x72, 0x6f, 0x70, 0x50, 0x61, 0x74, 0x68, 0x12, 0x1e, 0x0a, 0x0a, 0x70, 0x6f, + 0x70, 0x75, 0x6c, 0x61, 0x72, 0x69, 0x74, 0x79, 0x18, 0x0b, 0x20, 0x01, 0x28, 0x01, 0x52, 0x0a, + 0x70, 0x6f, 0x70, 0x75, 0x6c, 0x61, 0x72, 0x69, 0x74, 0x79, 0x12, 0x1d, 0x0a, 0x0a, 0x76, 0x6f, + 0x74, 0x65, 0x5f, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x18, 0x0c, 0x20, 0x01, 0x28, 0x05, 0x52, 0x09, + 0x76, 0x6f, 0x74, 0x65, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x69, 0x64, + 0x65, 0x6f, 0x18, 0x0d, 0x20, 0x01, 0x28, 0x08, 0x52, 0x05, 0x76, 0x69, 0x64, 0x65, 0x6f, 0x12, + 0x21, 0x0a, 0x0c, 0x76, 0x6f, 0x74, 0x65, 0x5f, 0x61, 0x76, 0x65, 0x72, 0x61, 0x67, 0x65, 0x18, + 0x0e, 0x20, 0x01, 0x28, 0x01, 0x52, 0x0b, 0x76, 0x6f, 0x74, 0x65, 0x41, 0x76, 0x65, 0x72, 0x61, + 0x67, 0x65, 0x22, 0xb3, 0x01, 0x0a, 0x0d, 0x53, 0x65, 0x61, 0x72, 0x63, 0x68, 0x52, 0x65, 0x71, + 0x75, 0x65, 0x73, 0x74, 0x12, 0x1a, 0x0a, 0x08, 0x6c, 0x61, 0x6e, 0x67, 0x75, 0x61, 0x67, 0x65, + 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x6c, 0x61, 0x6e, 0x67, 0x75, 0x61, 0x67, 0x65, + 0x12, 0x14, 0x0a, 0x05, 0x71, 0x75, 0x65, 0x72, 0x79, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, + 0x05, 0x71, 0x75, 0x65, 0x72, 0x79, 0x12, 0x12, 0x0a, 0x04, 0x70, 0x61, 0x67, 0x65, 0x18, 0x03, + 0x20, 0x01, 0x28, 0x05, 0x52, 0x04, 0x70, 0x61, 0x67, 0x65, 0x12, 0x16, 0x0a, 0x06, 0x72, 0x65, + 0x67, 0x69, 0x6f, 0x6e, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x72, 0x65, 0x67, 0x69, + 0x6f, 0x6e, 0x12, 0x12, 0x0a, 0x04, 0x79, 0x65, 0x61, 0x72, 0x18, 0x05, 0x20, 0x01, 0x28, 0x05, + 0x52, 0x04, 0x79, 0x65, 0x61, 0x72, 0x12, 0x30, 0x0a, 0x14, 0x70, 0x72, 0x69, 0x6d, 0x61, 0x72, + 0x79, 0x5f, 0x72, 0x65, 0x6c, 0x65, 0x61, 0x73, 0x65, 0x5f, 0x79, 0x65, 0x61, 0x72, 0x18, 0x06, + 0x20, 0x01, 0x28, 0x05, 0x52, 0x12, 0x70, 0x72, 0x69, 0x6d, 0x61, 0x72, 0x79, 0x52, 0x65, 0x6c, + 0x65, 0x61, 0x73, 0x65, 0x59, 0x65, 0x61, 0x72, 0x22, 0x96, 0x01, 0x0a, 0x0e, 0x53, 0x65, 0x61, + 0x72, 0x63, 0x68, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x23, 0x0a, 0x0d, 0x74, + 0x6f, 0x74, 0x61, 0x6c, 0x5f, 0x72, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x73, 0x18, 0x01, 0x20, 0x01, + 0x28, 0x05, 0x52, 0x0c, 0x74, 0x6f, 0x74, 0x61, 0x6c, 0x52, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x73, + 0x12, 0x1f, 0x0a, 0x0b, 0x74, 0x6f, 0x74, 0x61, 0x6c, 0x5f, 0x70, 0x61, 0x67, 0x65, 0x73, 0x18, + 0x02, 0x20, 0x01, 0x28, 0x05, 0x52, 0x0a, 0x74, 0x6f, 0x74, 0x61, 0x6c, 0x50, 0x61, 0x67, 0x65, + 0x73, 0x12, 0x12, 0x0a, 0x04, 0x70, 0x61, 0x67, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x05, 0x52, + 0x04, 0x70, 0x61, 0x67, 0x65, 0x12, 0x2a, 0x0a, 0x07, 0x72, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x73, + 0x18, 0x04, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x10, 0x2e, 0x6d, 0x6f, 0x76, 0x69, 0x65, 0x2e, 0x4d, + 0x6f, 0x76, 0x69, 0x65, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x07, 0x72, 0x65, 0x73, 0x75, 0x6c, 0x74, + 0x73, 0x32, 0x40, 0x0a, 0x05, 0x4d, 0x6f, 0x76, 0x69, 0x65, 0x12, 0x37, 0x0a, 0x06, 0x53, 0x65, + 0x61, 0x72, 0x63, 0x68, 0x12, 0x14, 0x2e, 0x6d, 0x6f, 0x76, 0x69, 0x65, 0x2e, 0x53, 0x65, 0x61, + 0x72, 0x63, 0x68, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x15, 0x2e, 0x6d, 0x6f, 0x76, + 0x69, 0x65, 0x2e, 0x53, 0x65, 0x61, 0x72, 0x63, 0x68, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, + 0x65, 0x22, 0x00, 0x42, 0x0f, 0x5a, 0x0d, 0x2e, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x3b, 0x6d, + 0x6f, 0x76, 0x69, 0x65, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, +} + +var ( + file_proto_movie_proto_rawDescOnce sync.Once + file_proto_movie_proto_rawDescData = file_proto_movie_proto_rawDesc +) + +func file_proto_movie_proto_rawDescGZIP() []byte { + file_proto_movie_proto_rawDescOnce.Do(func() { + file_proto_movie_proto_rawDescData = protoimpl.X.CompressGZIP(file_proto_movie_proto_rawDescData) + }) + return file_proto_movie_proto_rawDescData +} + +var file_proto_movie_proto_msgTypes = make([]protoimpl.MessageInfo, 3) +var file_proto_movie_proto_goTypes = []interface{}{ + (*MovieInfo)(nil), // 0: movie.MovieInfo + (*SearchRequest)(nil), // 1: movie.SearchRequest + (*SearchResponse)(nil), // 2: movie.SearchResponse +} +var file_proto_movie_proto_depIdxs = []int32{ + 0, // 0: movie.SearchResponse.results:type_name -> movie.MovieInfo + 1, // 1: movie.Movie.Search:input_type -> movie.SearchRequest + 2, // 2: movie.Movie.Search:output_type -> movie.SearchResponse + 2, // [2:3] is the sub-list for method output_type + 1, // [1:2] is the sub-list for method input_type + 1, // [1:1] is the sub-list for extension type_name + 1, // [1:1] is the sub-list for extension extendee + 0, // [0:1] is the sub-list for field type_name +} + +func init() { file_proto_movie_proto_init() } +func file_proto_movie_proto_init() { + if File_proto_movie_proto != nil { + return + } + if !protoimpl.UnsafeEnabled { + file_proto_movie_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*MovieInfo); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_proto_movie_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*SearchRequest); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_proto_movie_proto_msgTypes[2].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*SearchResponse); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + } + type x struct{} + out := protoimpl.TypeBuilder{ + File: protoimpl.DescBuilder{ + GoPackagePath: reflect.TypeOf(x{}).PkgPath(), + RawDescriptor: file_proto_movie_proto_rawDesc, + NumEnums: 0, + NumMessages: 3, + NumExtensions: 0, + NumServices: 1, + }, + GoTypes: file_proto_movie_proto_goTypes, + DependencyIndexes: file_proto_movie_proto_depIdxs, + MessageInfos: file_proto_movie_proto_msgTypes, + }.Build() + File_proto_movie_proto = out.File + file_proto_movie_proto_rawDesc = nil + file_proto_movie_proto_goTypes = nil + file_proto_movie_proto_depIdxs = nil +} diff --git a/movie/proto/movie.pb.micro.go b/movie/proto/movie.pb.micro.go new file mode 100644 index 0000000..a0059ab --- /dev/null +++ b/movie/proto/movie.pb.micro.go @@ -0,0 +1,93 @@ +// Code generated by protoc-gen-micro. DO NOT EDIT. +// source: proto/movie.proto + +package movie + +import ( + fmt "fmt" + proto "github.com/golang/protobuf/proto" + math "math" +) + +import ( + context "context" + api "github.com/micro/micro/v3/service/api" + client "github.com/micro/micro/v3/service/client" + server "github.com/micro/micro/v3/service/server" +) + +// Reference imports to suppress errors if they are not otherwise used. +var _ = proto.Marshal +var _ = fmt.Errorf +var _ = math.Inf + +// This is a compile-time assertion to ensure that this generated file +// is compatible with the proto package it is being compiled against. +// A compilation error at this line likely means your copy of the +// proto package needs to be updated. +const _ = proto.ProtoPackageIsVersion3 // please upgrade the proto package + +// Reference imports to suppress errors if they are not otherwise used. +var _ api.Endpoint +var _ context.Context +var _ client.Option +var _ server.Option + +// Api Endpoints for Movie service + +func NewMovieEndpoints() []*api.Endpoint { + return []*api.Endpoint{} +} + +// Client API for Movie service + +type MovieService interface { + Search(ctx context.Context, in *SearchRequest, opts ...client.CallOption) (*SearchResponse, error) +} + +type movieService struct { + c client.Client + name string +} + +func NewMovieService(name string, c client.Client) MovieService { + return &movieService{ + c: c, + name: name, + } +} + +func (c *movieService) Search(ctx context.Context, in *SearchRequest, opts ...client.CallOption) (*SearchResponse, error) { + req := c.c.NewRequest(c.name, "Movie.Search", in) + out := new(SearchResponse) + err := c.c.Call(ctx, req, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +// Server API for Movie service + +type MovieHandler interface { + Search(context.Context, *SearchRequest, *SearchResponse) error +} + +func RegisterMovieHandler(s server.Server, hdlr MovieHandler, opts ...server.HandlerOption) error { + type movie interface { + Search(ctx context.Context, in *SearchRequest, out *SearchResponse) error + } + type Movie struct { + movie + } + h := &movieHandler{hdlr} + return s.Handle(s.NewHandler(&Movie{h}, opts...)) +} + +type movieHandler struct { + MovieHandler +} + +func (h *movieHandler) Search(ctx context.Context, in *SearchRequest, out *SearchResponse) error { + return h.MovieHandler.Search(ctx, in, out) +} diff --git a/movie/proto/movie.proto b/movie/proto/movie.proto new file mode 100644 index 0000000..10acd7a --- /dev/null +++ b/movie/proto/movie.proto @@ -0,0 +1,50 @@ +syntax = "proto3"; + +package movie; + +option go_package = "./proto;movie"; + +service Movie { + rpc Search(SearchRequest) returns (SearchResponse) {} +} + +message MovieInfo { + string poster_path = 1; + bool adult = 2; + string overview = 3; + string release_date = 4; + repeated int32 genre_ids = 5; + int32 id = 6; + string original_title = 7; + string original_language = 8; + string title = 9; + string backdrop_path = 10; + double popularity = 11; + int32 vote_count = 12; + bool video = 13; + double vote_average = 14; +} + +message SearchRequest { + // a ISO 639-1 value to display translated data + string language = 1; + // a text query to search + string query = 2; + // page to query + int32 page = 3; + // a ISO 3166-1 code to filter release dates. + string region = 4; + + int32 year = 5; + int32 primary_release_year = 6; +} + + +message SearchResponse { + int32 total_results = 1; + int32 total_pages = 2; + int32 page = 3; + repeated MovieInfo results = 4; +} + + diff --git a/movie/publicapi.json b/movie/publicapi.json new file mode 100644 index 0000000..5a03480 --- /dev/null +++ b/movie/publicapi.json @@ -0,0 +1,6 @@ +{ + "name": "movie", + "icon": "🎬", + "category": "search", + "display_name": "Movies" +}