From bcb266824e04e0068a0c33790bfd9e2cf42d0407 Mon Sep 17 00:00:00 2001 From: jih147 Date: Mon, 12 Jun 2023 16:29:29 -0700 Subject: [PATCH 1/2] add media score endpoint --- common/swagger/v2/swagger.yaml | 46 +++++++++++++++++++ .../rest/Converter/MultipartConverter.java | 13 ++++++ .../Converter/ScoreMediaRequestConverter.java | 13 ++++++ .../deploy/local/rest/config/WebConfig.java | 16 +++++++ .../rest/controller/ModelsApiController.java | 8 ++++ .../controller/ModelsApiControllerTest.java | 18 ++++++++ 6 files changed, 114 insertions(+) create mode 100644 local-rest-scorer/src/main/java/ai/h2o/mojos/deploy/local/rest/Converter/MultipartConverter.java create mode 100644 local-rest-scorer/src/main/java/ai/h2o/mojos/deploy/local/rest/Converter/ScoreMediaRequestConverter.java create mode 100644 local-rest-scorer/src/main/java/ai/h2o/mojos/deploy/local/rest/config/WebConfig.java diff --git a/common/swagger/v2/swagger.yaml b/common/swagger/v2/swagger.yaml index a6f62cba..47495e86 100644 --- a/common/swagger/v2/swagger.yaml +++ b/common/swagger/v2/swagger.yaml @@ -160,6 +160,40 @@ paths: description: Implementation not supported content: {} x-codegen-request-body-name: payload + /model/media-score: + post: + tags: + - scoring + summary: Score model with provided media files + description: Computes score of provided data making use of provided media files. + operationId: getMediaScore + requestBody: + content: + multipart/form-data: + schema: + type: object + properties: + mediaScoreRequest: + $ref: '#/components/schemas/MediaScoreRequest' + files: + type: array + items: + type: string + format: binary + required: + - mediaScoreRequest + - files + responses: + '200': + description: Successful scoring operation + content: + application/json: + schema: + $ref: '#/components/schemas/ScoreResponse' + '501': + description: Implementation not supported + '400': + description: Invalid payload components: schemas: Model: @@ -365,6 +399,18 @@ components: type: array items: $ref: '#/components/schemas/Row' + MediaScoreRequest: + allOf: + - $ref: '#/components/schemas/ScoreRequest' + - properties: + mediaFields: + description: > + An array holding the names of all fields which are expected to contain media files. + Contents of these fields will be replaced by corresponding uploaded files where the + expected values in the column must be the file names of the uploaded files. + type: array + items: + type: string securitySchemes: api_key: type: apiKey diff --git a/local-rest-scorer/src/main/java/ai/h2o/mojos/deploy/local/rest/Converter/MultipartConverter.java b/local-rest-scorer/src/main/java/ai/h2o/mojos/deploy/local/rest/Converter/MultipartConverter.java new file mode 100644 index 00000000..6d616fad --- /dev/null +++ b/local-rest-scorer/src/main/java/ai/h2o/mojos/deploy/local/rest/Converter/MultipartConverter.java @@ -0,0 +1,13 @@ +package ai.h2o.mojos.deploy.local.rest.Converter; + +import org.springframework.core.convert.converter.Converter; +import org.springframework.core.io.Resource; +import org.springframework.web.multipart.MultipartFile; + +public class MultipartConverter implements Converter { + + @Override + public Resource convert(MultipartFile source) { + return source.getResource(); + } +} diff --git a/local-rest-scorer/src/main/java/ai/h2o/mojos/deploy/local/rest/Converter/ScoreMediaRequestConverter.java b/local-rest-scorer/src/main/java/ai/h2o/mojos/deploy/local/rest/Converter/ScoreMediaRequestConverter.java new file mode 100644 index 00000000..97b78b20 --- /dev/null +++ b/local-rest-scorer/src/main/java/ai/h2o/mojos/deploy/local/rest/Converter/ScoreMediaRequestConverter.java @@ -0,0 +1,13 @@ +package ai.h2o.mojos.deploy.local.rest.Converter; + +import ai.h2o.mojos.deploy.common.rest.v2.model.ScoreMediaRequest; +import com.google.gson.Gson; +import org.springframework.core.convert.converter.Converter; + +public class ScoreMediaRequestConverter implements Converter { + + @Override + public ScoreMediaRequest convert(String input) { + return new Gson().fromJson(input, ScoreMediaRequest.class); + } +} diff --git a/local-rest-scorer/src/main/java/ai/h2o/mojos/deploy/local/rest/config/WebConfig.java b/local-rest-scorer/src/main/java/ai/h2o/mojos/deploy/local/rest/config/WebConfig.java new file mode 100644 index 00000000..3461bfce --- /dev/null +++ b/local-rest-scorer/src/main/java/ai/h2o/mojos/deploy/local/rest/config/WebConfig.java @@ -0,0 +1,16 @@ +package ai.h2o.mojos.deploy.local.rest.config; + +import ai.h2o.mojos.deploy.local.rest.converter.MultipartConverter; +import ai.h2o.mojos.deploy.local.rest.converter.ScoreMediaRequestConverter; +import org.springframework.context.annotation.Configuration; +import org.springframework.format.FormatterRegistry; +import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; + +public class WebConfig implements WebMvcConfigurer { + + @Override + public void addFormatters(FormatterRegistry registry) { + registry.addConverter(new MultipartConverter()); + registry.addConverter(new ScoreMediaRequestConverter()); + } +} diff --git a/local-rest-scorer/src/main/java/ai/h2o/mojos/deploy/local/rest/controller/ModelsApiController.java b/local-rest-scorer/src/main/java/ai/h2o/mojos/deploy/local/rest/controller/ModelsApiController.java index f6fd49fb..e2834cfb 100644 --- a/local-rest-scorer/src/main/java/ai/h2o/mojos/deploy/local/rest/controller/ModelsApiController.java +++ b/local-rest-scorer/src/main/java/ai/h2o/mojos/deploy/local/rest/controller/ModelsApiController.java @@ -20,6 +20,7 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.core.io.Resource; import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; import org.springframework.stereotype.Controller; @@ -156,6 +157,13 @@ public ResponseEntity getSampleRequest() { return ResponseEntity.ok(sampleRequestBuilder.build(scorer.getPipeline().getInputMeta())); } + @Override + public ResponseEntity getScoreMedia( + ScoreMediaRequest scoreMediaRequest, List files) { + log.info("Received score media request"); + return ResponseEntity.status(HttpStatus.NOT_IMPLEMENTED).build(); + } + private String getScorerModelId() { try { String res = System.getenv(MODEL_ID); diff --git a/local-rest-scorer/src/test/java/ai/h2o/mojos/deploy/local/rest/controller/ModelsApiControllerTest.java b/local-rest-scorer/src/test/java/ai/h2o/mojos/deploy/local/rest/controller/ModelsApiControllerTest.java index bae20b18..41e28b7c 100644 --- a/local-rest-scorer/src/test/java/ai/h2o/mojos/deploy/local/rest/controller/ModelsApiControllerTest.java +++ b/local-rest-scorer/src/test/java/ai/h2o/mojos/deploy/local/rest/controller/ModelsApiControllerTest.java @@ -20,6 +20,7 @@ import java.io.File; import java.io.IOException; +import java.util.ArrayList; import java.util.Arrays; import java.util.List; @@ -32,6 +33,7 @@ import org.mockito.MockedStatic; import org.mockito.Mockito; import org.mockito.junit.jupiter.MockitoExtension; +import org.springframework.core.io.Resource; import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; import org.springframework.web.server.ResponseStatusException; @@ -187,4 +189,20 @@ void verifyScore_Fails_ReturnsException() { assertEquals(HttpStatus.SERVICE_UNAVAILABLE, ((ResponseStatusException) ex).getStatus()); } } + + @Test + void verifyScoreMedia_ReturnsUnimplemented() { + // Given + MojoScorer scorer = mock(MojoScorer.class); + ScoreMediaRequest request = mock(ScoreMediaRequest.class); + List files = new ArrayList<>(); + ModelsApiControllerV1Exp controller = new ModelsApiController(scorer, sampleRequestBuilder); + + // When + ResponseEntity response = + controller.getScoreMedia(request, files); + + // Then + assertEquals(response.getStatusCode(), HttpStatus.NOT_IMPLEMENTED); + } } From c0c691c2646efa31c5d0a6ec8ed61f25fab2cbd7 Mon Sep 17 00:00:00 2001 From: jih147 Date: Tue, 13 Jun 2023 10:39:04 -0700 Subject: [PATCH 2/2] add media score endpoint with exp controller --- .../Converter/ScoreMediaRequestConverter.java | 13 ---------- .../deploy/local/rest/config/WebConfig.java | 11 ++++---- .../rest/controller/ModelsApiController.java | 8 ------ .../controller/ModelsMediaController.java | 26 +++++++++++++++++++ .../MultipartConverter.java | 10 +++---- .../converter/ScoreMediaRequestConverter.java | 13 ++++++++++ .../controller/ModelsApiControllerTest.java | 18 +++++++------ 7 files changed, 60 insertions(+), 39 deletions(-) delete mode 100644 local-rest-scorer/src/main/java/ai/h2o/mojos/deploy/local/rest/Converter/ScoreMediaRequestConverter.java create mode 100644 local-rest-scorer/src/main/java/ai/h2o/mojos/deploy/local/rest/controller/ModelsMediaController.java rename local-rest-scorer/src/main/java/ai/h2o/mojos/deploy/local/rest/{Converter => converter}/MultipartConverter.java (60%) create mode 100644 local-rest-scorer/src/main/java/ai/h2o/mojos/deploy/local/rest/converter/ScoreMediaRequestConverter.java diff --git a/local-rest-scorer/src/main/java/ai/h2o/mojos/deploy/local/rest/Converter/ScoreMediaRequestConverter.java b/local-rest-scorer/src/main/java/ai/h2o/mojos/deploy/local/rest/Converter/ScoreMediaRequestConverter.java deleted file mode 100644 index 97b78b20..00000000 --- a/local-rest-scorer/src/main/java/ai/h2o/mojos/deploy/local/rest/Converter/ScoreMediaRequestConverter.java +++ /dev/null @@ -1,13 +0,0 @@ -package ai.h2o.mojos.deploy.local.rest.Converter; - -import ai.h2o.mojos.deploy.common.rest.v2.model.ScoreMediaRequest; -import com.google.gson.Gson; -import org.springframework.core.convert.converter.Converter; - -public class ScoreMediaRequestConverter implements Converter { - - @Override - public ScoreMediaRequest convert(String input) { - return new Gson().fromJson(input, ScoreMediaRequest.class); - } -} diff --git a/local-rest-scorer/src/main/java/ai/h2o/mojos/deploy/local/rest/config/WebConfig.java b/local-rest-scorer/src/main/java/ai/h2o/mojos/deploy/local/rest/config/WebConfig.java index 3461bfce..4f959c99 100644 --- a/local-rest-scorer/src/main/java/ai/h2o/mojos/deploy/local/rest/config/WebConfig.java +++ b/local-rest-scorer/src/main/java/ai/h2o/mojos/deploy/local/rest/config/WebConfig.java @@ -6,11 +6,12 @@ import org.springframework.format.FormatterRegistry; import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; +@Configuration public class WebConfig implements WebMvcConfigurer { - @Override - public void addFormatters(FormatterRegistry registry) { - registry.addConverter(new MultipartConverter()); - registry.addConverter(new ScoreMediaRequestConverter()); - } + @Override + public void addFormatters(FormatterRegistry registry) { + registry.addConverter(new MultipartConverter()); + registry.addConverter(new ScoreMediaRequestConverter()); + } } diff --git a/local-rest-scorer/src/main/java/ai/h2o/mojos/deploy/local/rest/controller/ModelsApiController.java b/local-rest-scorer/src/main/java/ai/h2o/mojos/deploy/local/rest/controller/ModelsApiController.java index e2834cfb..f6fd49fb 100644 --- a/local-rest-scorer/src/main/java/ai/h2o/mojos/deploy/local/rest/controller/ModelsApiController.java +++ b/local-rest-scorer/src/main/java/ai/h2o/mojos/deploy/local/rest/controller/ModelsApiController.java @@ -20,7 +20,6 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.core.io.Resource; import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; import org.springframework.stereotype.Controller; @@ -157,13 +156,6 @@ public ResponseEntity getSampleRequest() { return ResponseEntity.ok(sampleRequestBuilder.build(scorer.getPipeline().getInputMeta())); } - @Override - public ResponseEntity getScoreMedia( - ScoreMediaRequest scoreMediaRequest, List files) { - log.info("Received score media request"); - return ResponseEntity.status(HttpStatus.NOT_IMPLEMENTED).build(); - } - private String getScorerModelId() { try { String res = System.getenv(MODEL_ID); diff --git a/local-rest-scorer/src/main/java/ai/h2o/mojos/deploy/local/rest/controller/ModelsMediaController.java b/local-rest-scorer/src/main/java/ai/h2o/mojos/deploy/local/rest/controller/ModelsMediaController.java new file mode 100644 index 00000000..0cc7aa75 --- /dev/null +++ b/local-rest-scorer/src/main/java/ai/h2o/mojos/deploy/local/rest/controller/ModelsMediaController.java @@ -0,0 +1,26 @@ +package ai.h2o.mojos.deploy.local.rest.controller; + +import ai.h2o.mojos.deploy.common.rest.v1exp.api.ModelApi; +import ai.h2o.mojos.deploy.common.rest.v1exp.model.ScoreMediaRequest; +import ai.h2o.mojos.deploy.common.rest.v1exp.model.ScoreResponse; +import java.util.List; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.core.io.Resource; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.stereotype.Controller; +import org.springframework.web.server.ResponseStatusException; + +@Controller +public class ModelsMediaController implements ModelApi { + private static final Logger log = LoggerFactory.getLogger(ModelsMediaController.class); + + @Override + public ResponseEntity getMediaScore( + ScoreMediaRequest request, List files) { + log.info("Got score media request"); + throw new ResponseStatusException(HttpStatus.NOT_IMPLEMENTED, + "score media files is not implemented"); + } +} diff --git a/local-rest-scorer/src/main/java/ai/h2o/mojos/deploy/local/rest/Converter/MultipartConverter.java b/local-rest-scorer/src/main/java/ai/h2o/mojos/deploy/local/rest/converter/MultipartConverter.java similarity index 60% rename from local-rest-scorer/src/main/java/ai/h2o/mojos/deploy/local/rest/Converter/MultipartConverter.java rename to local-rest-scorer/src/main/java/ai/h2o/mojos/deploy/local/rest/converter/MultipartConverter.java index 6d616fad..b385dc7e 100644 --- a/local-rest-scorer/src/main/java/ai/h2o/mojos/deploy/local/rest/Converter/MultipartConverter.java +++ b/local-rest-scorer/src/main/java/ai/h2o/mojos/deploy/local/rest/converter/MultipartConverter.java @@ -1,4 +1,4 @@ -package ai.h2o.mojos.deploy.local.rest.Converter; +package ai.h2o.mojos.deploy.local.rest.converter; import org.springframework.core.convert.converter.Converter; import org.springframework.core.io.Resource; @@ -6,8 +6,8 @@ public class MultipartConverter implements Converter { - @Override - public Resource convert(MultipartFile source) { - return source.getResource(); - } + @Override + public Resource convert(MultipartFile source) { + return source.getResource(); + } } diff --git a/local-rest-scorer/src/main/java/ai/h2o/mojos/deploy/local/rest/converter/ScoreMediaRequestConverter.java b/local-rest-scorer/src/main/java/ai/h2o/mojos/deploy/local/rest/converter/ScoreMediaRequestConverter.java new file mode 100644 index 00000000..b30f1181 --- /dev/null +++ b/local-rest-scorer/src/main/java/ai/h2o/mojos/deploy/local/rest/converter/ScoreMediaRequestConverter.java @@ -0,0 +1,13 @@ +package ai.h2o.mojos.deploy.local.rest.converter; + +import ai.h2o.mojos.deploy.common.rest.v1exp.model.ScoreMediaRequest; +import com.google.gson.Gson; +import org.springframework.core.convert.converter.Converter; + +public class ScoreMediaRequestConverter implements Converter { + + @Override + public ScoreMediaRequest convert(String input) { + return new Gson().fromJson(input, ScoreMediaRequest.class); + } +} diff --git a/local-rest-scorer/src/test/java/ai/h2o/mojos/deploy/local/rest/controller/ModelsApiControllerTest.java b/local-rest-scorer/src/test/java/ai/h2o/mojos/deploy/local/rest/controller/ModelsApiControllerTest.java index 41e28b7c..64f5a716 100644 --- a/local-rest-scorer/src/test/java/ai/h2o/mojos/deploy/local/rest/controller/ModelsApiControllerTest.java +++ b/local-rest-scorer/src/test/java/ai/h2o/mojos/deploy/local/rest/controller/ModelsApiControllerTest.java @@ -11,6 +11,7 @@ import ai.h2o.mojos.deploy.common.rest.model.Model; import ai.h2o.mojos.deploy.common.rest.model.ScoreRequest; import ai.h2o.mojos.deploy.common.rest.model.ScoreResponse; +import ai.h2o.mojos.deploy.common.rest.v1exp.model.ScoreMediaRequest; import ai.h2o.mojos.deploy.common.transform.MojoScorer; import ai.h2o.mojos.deploy.common.transform.SampleRequestBuilder; import ai.h2o.mojos.deploy.common.transform.ShapleyLoadOption; @@ -193,16 +194,17 @@ void verifyScore_Fails_ReturnsException() { @Test void verifyScoreMedia_ReturnsUnimplemented() { // Given - MojoScorer scorer = mock(MojoScorer.class); ScoreMediaRequest request = mock(ScoreMediaRequest.class); List files = new ArrayList<>(); - ModelsApiControllerV1Exp controller = new ModelsApiController(scorer, sampleRequestBuilder); - - // When - ResponseEntity response = - controller.getScoreMedia(request, files); + ModelsMediaController controller = new ModelsMediaController(); - // Then - assertEquals(response.getStatusCode(), HttpStatus.NOT_IMPLEMENTED); + // When & Then + try { + controller.getMediaScore(request, files); + fail("exception is expected, but fail to raise"); + } catch (Exception ex) { + assertTrue(ex instanceof ResponseStatusException); + assertEquals(HttpStatus.NOT_IMPLEMENTED, ((ResponseStatusException) ex).getStatus()); + } } }