/** * Copyright 2017 Hortonworks. * <p> * 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 * <p> * http://www.apache.org/licenses/LICENSE-2.0 * <p> * 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. **/ package com.hortonworks.registries.model.service; import com.codahale.metrics.annotation.Timed; import com.hortonworks.registries.common.exception.service.exception.request.BadRequestException; import com.hortonworks.registries.common.util.WSUtils; import com.hortonworks.registries.model.data.MLModel; import org.glassfish.jersey.media.multipart.FormDataContentDisposition; import org.glassfish.jersey.media.multipart.FormDataParam; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.xml.sax.SAXException; import javax.ws.rs.Consumes; import javax.ws.rs.DELETE; import javax.ws.rs.GET; import javax.ws.rs.POST; import javax.ws.rs.PUT; import javax.ws.rs.Path; import javax.ws.rs.PathParam; import javax.ws.rs.Produces; import javax.ws.rs.core.MediaType; import javax.ws.rs.core.Response; import javax.xml.bind.JAXBException; import java.io.IOException; import java.io.InputStream; import java.util.List; import static javax.ws.rs.core.Response.Status.CREATED; import static javax.ws.rs.core.Response.Status.OK; @Path("/v1/catalog") @Produces(MediaType.APPLICATION_JSON) public final class MLModelRegistryResource { private static final Logger LOG = LoggerFactory.getLogger(MLModelRegistryResource.class); private final MLModelRegistryService modelRegistryService; public MLModelRegistryResource(MLModelRegistryService modelRegistryService) { this.modelRegistryService = modelRegistryService; } @GET @Path("ml/models") @Produces(MediaType.APPLICATION_JSON) @Timed public Response listModelInfos() { return WSUtils.respondEntities(modelRegistryService.listModelInfos(), OK); } /** * Add a model info * <p> * modelInfo.json * { * "name": "ModelName" * } * curl -sS -X POST -F modelInfo="@modelInfo.json;type=text/json" -F pmmlFile=@modelFile http://localhost:8080/api/v1/catalog/ml/models * </p> * <pre> *{ * "responseCode":1000, * "responseMessage":"Success", * "entities":{ * "id": 1, * "modelName": "model", * "pmmlFileName":"modelFile" * "timestamp":... * } *} * </pre> */ @POST @Path("ml/models") @Consumes(MediaType.MULTIPART_FORM_DATA) @Timed public Response addModelInfo(@FormDataParam("modelInfo") final MLModel modelInfo, @FormDataParam("pmmlFile") final InputStream pmmlInputStream, @FormDataParam("pmmlFile") final FormDataContentDisposition fileDisposition) throws IOException, SAXException, JAXBException { if (pmmlInputStream == null) { throw BadRequestException.missingParameter("pmmlFile"); } try { MLModel createdModelInfo = modelRegistryService.addModelInfo(modelInfo, pmmlInputStream, fileDisposition.getFileName()); return WSUtils.respondEntity(createdModelInfo, CREATED); } catch (Exception exception) { LOG.debug("Error occured while adding the pmml model", exception); throw exception; } finally { try { if (pmmlInputStream != null) { pmmlInputStream.close(); } } catch (IOException exception) { LOG.debug("Error while closing the pmml file stream", exception); throw exception; } } } /** * Add a model info * modelInfo.json * { * "name": "ChangedModelName" * } * * <p> * curl -sS -X PUT -F modelInfo="@modelInfo.json;type=text/json" http://localhost:8080/api/v1/catalog/ml/models/1 * </p> * <pre> *{ * "responseCode":1000, * "responseMessage":"Success", * "entities":{ * "id": 1, * "modelName": "model", * "pmmlFileName":"modelFile" * "timestamp":... * } *} * </pre> */ @PUT @Path("/ml/models/{id}") @Consumes(MediaType.MULTIPART_FORM_DATA) @Timed public Response addOrUpdateModelInfo( @PathParam("id") final Long modelId, @FormDataParam("modelInfo") final MLModel modelInfo, @FormDataParam("pmmlFile") final InputStream pmmlInputStream, @FormDataParam("pmmlFile") final FormDataContentDisposition fileDisposition) throws IOException, SAXException, JAXBException { if (pmmlInputStream == null) { throw BadRequestException.missingParameter("pmmlFile"); } try { MLModel createdModelInfo = modelRegistryService.addOrUpdateModelInfo( modelId, modelInfo, pmmlInputStream, fileDisposition.getFileName()); return WSUtils.respondEntity(createdModelInfo, CREATED); } catch (Exception exception) { LOG.debug("Error occured while adding the pmml model", exception); throw exception; } finally { try { if (pmmlInputStream != null) { pmmlInputStream.close(); } } catch (IOException exception) { LOG.debug("Error while closing the pmml file stream", exception); throw exception; } } } /** * Add a model info * <p> * curl -sS -X DELETE http://localhost:8080/api/v1/catalog/ml/models/1 * </p> * <pre> *{ * "responseCode":1000, * "responseMessage":"Success", * "entities":{ * "id": 1, * "modelName": "model", * "pmmlFileName":"modelFile" * "timestamp":... * } *} * </pre> */ @DELETE @Path("ml/models/{id}") @Timed public Response removeModelInfo(@PathParam("id") final Long modelId) { MLModel removedModelInfo = modelRegistryService.removeModelInfo(modelId); return WSUtils.respondEntity(removedModelInfo, OK); } /** * Get a model info by name * <p> * curl -sS -X GET http://localhost:8080/api/v1/catalog/ml/models/names/testModel * </p> * <pre> *{ * "responseCode":1000, * "responseMessage":"Success", * "entities":{ * "id": 1, * "modelName": "model", * "pmmlFileName":"modelFile" * "timestamp":... * } *} * </pre> */ @GET @Path("ml/models/names/{name}") @Produces(MediaType.APPLICATION_JSON) @Timed public Response getModelInfoByName(@PathParam("name") final String modelName) { MLModel modelInfo = modelRegistryService.getModelInfo(modelName); return WSUtils.respondEntity(modelInfo, OK); } /** * Get a model info by id * <p> * curl -sS -X GET http://localhost:8080/api/v1/catalog/models/1 * </p> * <pre> *{ * "responseCode":1000, * "responseMessage":"Success", * "entities":{ * "id": 1, * "modelName": "model", * "pmmlFileName":"modelFile" * "timestamp":... * } *} * </pre> */ @GET @Path("ml/models/{id}") @Produces(MediaType.APPLICATION_JSON) @Timed public Response getModelInfoById(@PathParam("id") final Long modelId) { MLModel modelInfo = modelRegistryService.getModelInfo(modelId); return WSUtils.respondEntity(modelInfo, OK); } /** * Get model fields by id * <p> * curl -sS -X GET http://localhost:8080/api/v1/catalog/ml/models/1/fields/output * </p> * <pre> *{ * "responseCode":1000, * "responseMessage":"Success", * "entities":[ * {"name": "fieldName", "type": "fieldType"} * ... * ] *} * </pre> */ @GET @Path("ml/models/{id}/fields/output") @Produces(MediaType.APPLICATION_JSON) @Timed public Response getModelOutputFieldsById(@PathParam("id") final Long modelId) throws Exception { MLModel modelInfo = modelRegistryService.getModelInfo(modelId); final List<MLModelField> fieldNames = modelRegistryService.getModelOutputFields(modelInfo); return WSUtils.respondEntity(fieldNames, OK); } /** * Get model fields by id * <p> * curl -sS -X GET http://localhost:8080/api/v1/catalog/ml/models/1/fields/input * </p> * <pre> *{ * "responseCode":1000, * "responseMessage":"Success", * "entities":[ * {"name": "fieldName", "type": "fieldType"} * ... * ... * ] *} * </pre> */ @GET @Path("ml/models/{id}/fields/input") @Produces(MediaType.APPLICATION_JSON) @Timed public Response getModelInputFieldsById(@PathParam("id") final Long modelId) throws Exception { MLModel modelInfo = modelRegistryService.getModelInfo(modelId); final List<MLModelField> fieldNames = modelRegistryService.getModelInputFields(modelInfo); return WSUtils.respondEntity(fieldNames, OK); } /* * API endpoint to get the model file contents associated with the id. * <p> * curl -sS -X GET http://localhost:8080/api/v1/catalog/ml/models/pmml/{name} * </p> */ @GET @Produces(MediaType.APPLICATION_JSON) @Path("ml/models/pmml/{name}") @Timed public Response getMLModelContents(@PathParam("name") String modelName) { MLModel modelInfo = modelRegistryService.getModelInfo(modelName); return WSUtils.respondEntity(modelInfo.getPmml(), OK); } }