diff --git a/docker-compose.yml b/docker-compose.yml index 6429fab0d8..a0100e95ac 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -208,7 +208,7 @@ services: context: ./ dockerfile: ./src/recommendationservice/Dockerfile ports: - - "${RECOMMENDATION_SERVICE_PORT}" + - "${RECOMMENDATION_SERVICE_PORT}:${RECOMMENDATION_SERVICE_PORT}" depends_on: - productcatalogservice - otelcol diff --git a/src/recommendationservice/README.md b/src/recommendationservice/README.md index 1c678e6964..bbb628c38e 100644 --- a/src/recommendationservice/README.md +++ b/src/recommendationservice/README.md @@ -1,30 +1 @@ # Read Me - -1. Expose the service's port to the localhost by modifying `/compose.yml` - - ```diff - - - "${RECOMMENDATION_SERVICE_PORT}" - + - "${RECOMMENDATION_SERVICE_PORT}:${RECOMMENDATION_SERVICE_PORT}" - ``` - -1. To run the `./client.py` you must compile `/pb/demo.proto` into python code - - ```shell - python -m grpc_tools.protoc -I./pb/ --python_out=./src/recommendationservice/ --grpc_python_out=./src/recommendationservice/ ./pb/demo.proto - python ./src/recommendationservice/client.py - ``` - -1. You should see output similar to the following - - ```json - { - "asctime": "2022-06-02 13:42:44,793", - "levelname": "INFO", - "name": "recommendationservice-server", - "filename": "client.py", - "lineno": 35, - "otelTraceID": "00000000000000000000000000000000", - "otelSpanID": "0000000000000000", - "message": "product_ids: \"6E92ZMYYFZ\"\nproduct_ids: \"OLJCESPC7Z\"\nproduct_ids: \"LS4PSXUNUM\"\nproduct_ids: \"2ZYFJ3GM2N\"\nproduct_ids: \"1YMWWN1N4O\"\n" - } - ``` diff --git a/src/recommendationservice/client.py b/src/recommendationservice/client.py deleted file mode 100644 index 4555a53149..0000000000 --- a/src/recommendationservice/client.py +++ /dev/null @@ -1,44 +0,0 @@ -#!/usr/bin/python -# -# Copyright 2018 Google LLC -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -# Python -import os - -# Pip -import dotenv -import grpc - -# Local -import demo_pb2 -import demo_pb2_grpc -from logger import getJSONLogger - -logger = getJSONLogger('recommendationservice-server') - -if __name__ == "__main__": - dotenv.load_dotenv() - - # set up server stub - port = os.getenv('RECOMMENDATION_SERVICE_PORT') - channel = grpc.insecure_channel(f'localhost:{port}') - stub = demo_pb2_grpc.RecommendationServiceStub(channel) - - # form request - request = demo_pb2.ListRecommendationsRequest(user_id="test", product_ids=["test"]) - - # make call to server - response = stub.ListRecommendations(request) - logger.info(response) diff --git a/src/recommendationservice/recommendation_server.py b/src/recommendationservice/recommendation_server.py index 51758fb9bd..efef9b3ae9 100644 --- a/src/recommendationservice/recommendation_server.py +++ b/src/recommendationservice/recommendation_server.py @@ -14,30 +14,25 @@ # See the License for the specific language governing permissions and # limitations under the License. +# Python import os import random import time from concurrent import futures +# Pip import grpc - -import demo_pb2 -import demo_pb2_grpc -from grpc_health.v1 import health_pb2 -from grpc_health.v1 import health_pb2_grpc - from opentelemetry import trace from opentelemetry.sdk.trace import TracerProvider from opentelemetry.sdk.trace.export import (BatchSpanProcessor) from opentelemetry.exporter.otlp.proto.grpc.trace_exporter import OTLPSpanExporter +# Local +import demo_pb2 +import demo_pb2_grpc +from grpc_health.v1 import health_pb2 +from grpc_health.v1 import health_pb2_grpc from logger import getJSONLogger -logger = getJSONLogger('recommendationservice-server') - -tracer_provider = TracerProvider() -trace.set_tracer_provider(tracer_provider) -tracer_provider.add_span_processor(BatchSpanProcessor(OTLPSpanExporter())) -tracer = trace.get_tracer("recommendationservice") class RecommendationService(demo_pb2_grpc.RecommendationServiceServicer): @@ -86,19 +81,21 @@ def must_map_env(key: str): raise Exception(f'{key} environment variable must be set') return value - if __name__ == "__main__": - logger.info("initializing recommendationservice") + logger = getJSONLogger('recommendationservice-server') + tracer_provider = TracerProvider() + trace.set_tracer_provider(tracer_provider) + tracer_provider.add_span_processor(BatchSpanProcessor(OTLPSpanExporter())) + tracer = trace.get_tracer("recommendationservice") port = must_map_env('RECOMMENDATION_SERVICE_PORT') catalog_addr = must_map_env('PRODUCT_CATALOG_SERVICE_ADDR') - logger.info("product catalog address: " + catalog_addr) channel = grpc.insecure_channel(catalog_addr) product_catalog_stub = demo_pb2_grpc.ProductCatalogServiceStub(channel) # create gRPC server - server = grpc.server(futures.ThreadPoolExecutor(max_workers=10),) + server = grpc.server(futures.ThreadPoolExecutor(max_workers=10)) # add class to gRPC server service = RecommendationService() @@ -106,13 +103,7 @@ def must_map_env(key: str): health_pb2_grpc.add_HealthServicer_to_server(service, server) # start server - logger.info("listening on port: " + port) - server.add_insecure_port('[::]:'+port) + logger.info("RecommendationService listening on port: " + port) + server.add_insecure_port('[::]:' + port) server.start() - - # keep alive - try: - while True: - time.sleep(10000) - except KeyboardInterrupt: - server.stop(0) + server.wait_for_termination() diff --git a/test/data.json b/test/data.json index 4ee3fbabe9..c38ae4331d 100644 --- a/test/data.json +++ b/test/data.json @@ -11,5 +11,9 @@ "creditCardExpirationYear": 2039, "creditCardExpirationMonth": 1 } + }, + "recommend": { + "userId": "1234", + "productIds": [ "OLJCESPC7Z", "66VCHSJNUP", "1YMWWN1N4O", "L9ECAV7KIM", "2ZYFJ3GM2N" ] } } diff --git a/test/test.js b/test/test.js index 29e608c476..399f7f6dc2 100644 --- a/test/test.js +++ b/test/test.js @@ -13,8 +13,11 @@ const data = require('./data.json') // Functions const deepCopy = obj => JSON.parse(JSON.stringify(obj)) +const arrayIntersection = (a, b) => a.filter(x => b.indexOf(x) !== -1) + // Main let charge = null +let recommend = null test.before(() => { dotenv.config({ path: '../.env' }) @@ -23,6 +26,9 @@ test.before(() => { const paymentClient = new hipstershop.PaymentService(`0.0.0.0:${process.env.PAYMENT_SERVICE_PORT}`, grpc.credentials.createInsecure()) charge = promisify(paymentClient.charge).bind(paymentClient) + + const recommendationClient = new hipstershop.RecommendationService(`0.0.0.0:${process.env.RECOMMENDATION_SERVICE_PORT}`, grpc.credentials.createInsecure()) + recommend = promisify(recommendationClient.listRecommendations).bind(recommendationClient) }) // --------------- Payment Service --------------- @@ -61,3 +67,14 @@ test('payment: expired credit card', t => { t.is(err.details, 'The credit card (ending 0454) expired on 1/2021.') }) }) + +// --------------- Recommendation Service --------------- + +test('recommendation: list products', t => { + const request = deepCopy(data.recommend) + + return recommend(request).then(res => { + t.is(res.productIds.length, 4) + t.is(arrayIntersection(res.productIds, request.productIds).length, 0) + }) +})