diff options
Diffstat (limited to 'music-rest/src/main/java')
25 files changed, 5258 insertions, 0 deletions
diff --git a/music-rest/src/main/java/LICENSE.txt b/music-rest/src/main/java/LICENSE.txt new file mode 100644 index 00000000..cc6cdea5 --- /dev/null +++ b/music-rest/src/main/java/LICENSE.txt @@ -0,0 +1,24 @@ + +The following license applies to all files in this and sub-directories. Licenses +are included in individual source files where appropriate, and if it differs +from this text, it supersedes this. Any file that does not have license text +defaults to being covered by this text; not all files support the addition of +licenses. +# +# ------------------------------------------------------------------------- +# Copyright (c) 2017 AT&T Intellectual Property +# +# 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. +# +# ------------------------------------------------------------------------- +#
\ No newline at end of file diff --git a/music-rest/src/main/java/org/onap/music/JerseyConfig.java b/music-rest/src/main/java/org/onap/music/JerseyConfig.java new file mode 100755 index 00000000..b64e7044 --- /dev/null +++ b/music-rest/src/main/java/org/onap/music/JerseyConfig.java @@ -0,0 +1,87 @@ +/* + * ============LICENSE_START========================================== + * org.onap.music + * =================================================================== + * Copyright (c) 2019 AT&T Intellectual Property + * =================================================================== + * 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. + * + * ============LICENSE_END============================================= + * ==================================================================== + */ + +package org.onap.music; + +import io.swagger.jaxrs.config.BeanConfig; +import io.swagger.jaxrs.listing.ApiListingResource; +import io.swagger.jaxrs.listing.SwaggerSerializers; + +import javax.annotation.PostConstruct; + +import org.glassfish.jersey.server.ResourceConfig; +import org.onap.music.conductor.conditionals.RestMusicConditionalAPI; +import org.onap.music.exceptions.MusicExceptionMapper; +import org.onap.music.rest.RestMusicDataAPI; +import org.onap.music.rest.RestMusicHealthCheckAPI; +import org.onap.music.rest.RestMusicLocksAPI; +import org.onap.music.rest.RestMusicQAPI; +import org.onap.music.rest.RestMusicTestAPI; +import org.onap.music.rest.RestMusicVersionAPI; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.stereotype.Component; + +@Component +public class JerseyConfig extends ResourceConfig { + + @Value("${spring.jersey.application-path:/}") + private String apiPath; + + public JerseyConfig() { + this.registerEndpoints(); + register(MusicExceptionMapper.class); + } + + @PostConstruct + public void init() { + this.configureSwagger(); + } + + private void registerEndpoints() { + register(RestMusicDataAPI.class); + register(RestMusicLocksAPI.class); + register(RestMusicConditionalAPI.class); + register(RestMusicQAPI.class); + register(RestMusicTestAPI.class); + register(RestMusicVersionAPI.class); + register(RestMusicHealthCheckAPI.class); + + } + + private void configureSwagger() { + // Available at localhost:port/swagger.json + this.register(ApiListingResource.class); + this.register(SwaggerSerializers.class); + + BeanConfig config = new BeanConfig(); + config.setConfigId("MUSIC"); + config.setTitle("MUSIC"); + config.setVersion("v2"); + config.setContact("Thomas Nelson"); + config.setSchemes(new String[] {"http", "https"}); + config.setBasePath("/MUSIC/rest"); + config.setResourcePackage("org.onap.music"); + config.setPrettyPrint(true); + config.setScan(true); + } + +} diff --git a/music-rest/src/main/java/org/onap/music/MusicApplication.java b/music-rest/src/main/java/org/onap/music/MusicApplication.java new file mode 100755 index 00000000..22c9e7bf --- /dev/null +++ b/music-rest/src/main/java/org/onap/music/MusicApplication.java @@ -0,0 +1,203 @@ +/* + * ============LICENSE_START========================================== + * org.onap.music + * =================================================================== + * Copyright (c) 2017 AT&T Intellectual Property + * =================================================================== + * 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. + * + * ============LICENSE_END============================================= + * ==================================================================== + */ + +package org.onap.music; + +import javax.servlet.Filter; +import javax.servlet.FilterChain; +import javax.servlet.ServletException; +import javax.servlet.ServletRequest; +import javax.servlet.ServletResponse; + +import org.onap.aaf.cadi.PropAccess; +import org.onap.music.authentication.CadiAuthFilter; +import org.onap.music.authentication.MusicAuthorizationFilter; +import org.onap.music.eelf.logging.EELFLoggerDelegate; +import org.onap.music.eelf.logging.MusicLoggingServletFilter; +import org.onap.music.main.MusicUtil; +import org.onap.music.main.PropertiesLoader; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.autoconfigure.EnableAutoConfiguration; +import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; +import org.springframework.boot.autoconfigure.data.cassandra.CassandraDataAutoConfiguration; +import org.springframework.boot.builder.SpringApplicationBuilder; +import org.springframework.boot.web.servlet.FilterRegistrationBean; +import org.springframework.boot.web.servlet.support.SpringBootServletInitializer; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.ComponentScan; +import org.springframework.context.annotation.DependsOn; +import org.springframework.scheduling.annotation.EnableScheduling; +import org.springframework.web.context.request.RequestContextListener; + +@SpringBootApplication(scanBasePackages = { "org.onap.music.rest"}) +@EnableAutoConfiguration(exclude = { CassandraDataAutoConfiguration.class }) +@ComponentScan(value = { "org.onap.music" }) +@EnableScheduling +public class MusicApplication extends SpringBootServletInitializer { + + private static final String KEYSPACE_PATTERN = "/v2/keyspaces/*"; + private static final String LOCKS_PATTERN = "/v2/locks/*"; + private static final String Q_PATTERN = "/v2/priorityq/*"; + + @Autowired + private PropertiesLoader propertyLoader; + private EELFLoggerDelegate logger = EELFLoggerDelegate.getLogger(MusicApplication.class); + + + public static void main(String[] args) { + new MusicApplication().configure(new SpringApplicationBuilder(MusicApplication.class)).run(args); + } + + @Override + protected SpringApplicationBuilder configure(SpringApplicationBuilder application) { + + return application.sources(MusicApplication.class); + } + + @Bean("loadProperties") + public void loadProperties() { + propertyLoader.loadProperties(); + } + + + @Bean + @DependsOn("loadProperties") + public PropAccess propAccess() { + if (MusicUtil.getIsCadi()) { + return new PropAccess(new String[] { + "cadi_prop_files=/opt/app/music/etc/cadi.properties" }); + } else { + return null; + } + } + + @Bean(name = "cadiFilter") + @DependsOn("loadProperties") + public Filter cadiFilter() throws ServletException { + propertyLoader.loadProperties(); + if (MusicUtil.getIsCadi()) { + PropAccess propAccess = propAccess(); + return new CadiAuthFilter(propAccess); + } else { + return (ServletRequest request, ServletResponse response, FilterChain chain) -> { + // do nothing for now. + }; + } + } + + /** + * Added for capturing custom header values from client. + * + * order is set to 1 for this filter + * + * sp931a + * + * @return + * @throws ServletException + */ + @Bean(name="logFilter") + @DependsOn("loadProperties") + public FilterRegistrationBean<Filter> loggingFilterRegistration() throws ServletException { + logger.info("loggingFilterRegistration called for log filter.."); + propertyLoader.loadProperties(); + FilterRegistrationBean<Filter> frb = new FilterRegistrationBean<>(); + frb.setFilter(new MusicLoggingServletFilter()); + frb.addUrlPatterns( + KEYSPACE_PATTERN, + LOCKS_PATTERN, + Q_PATTERN + ); + frb.setName("logFilter"); + frb.setOrder(1); + return frb; + } + + @Bean + @DependsOn("loadProperties") + public FilterRegistrationBean<Filter> cadiFilterRegistration() throws ServletException { + logger.info("cadiFilterRegistration called for cadi filter.."); + FilterRegistrationBean<Filter> frb = new FilterRegistrationBean<>(); + frb.setFilter(cadiFilter()); + if (MusicUtil.getIsCadi()) { + frb.addUrlPatterns( + KEYSPACE_PATTERN, + LOCKS_PATTERN, + Q_PATTERN + ); + } else { + frb.addUrlPatterns("/v0/test"); + } + frb.setName("cadiFilter"); + frb.setOrder(2); + return frb; + } + + + /** + * Added for Authorization using CADI + * + * sp931a + * + * @return + * @throws ServletException + */ + @Bean + @DependsOn("loadProperties") + public FilterRegistrationBean<Filter> cadiFilterRegistrationForAuth() throws ServletException { + logger.info("cadiFilterRegistrationForAuth called for cadi auth filter.."); + FilterRegistrationBean<Filter> frb = new FilterRegistrationBean<>(); + frb.setFilter(cadiMusicAuthFilter()); + + if (MusicUtil.getIsCadi()) { + frb.addUrlPatterns( + KEYSPACE_PATTERN, + LOCKS_PATTERN, + Q_PATTERN + ); + } else { + frb.addUrlPatterns("/v0/test"); + } + frb.setName("cadiMusicAuthFilter"); + frb.setOrder(3); + return frb; + } + + @Bean(name = "cadiMusicAuthFilter") + @DependsOn("loadProperties") + public Filter cadiMusicAuthFilter() throws ServletException { + propertyLoader.loadProperties(); + if (MusicUtil.getIsCadi()) { + return new MusicAuthorizationFilter(); + } else { + return (ServletRequest request, ServletResponse response, FilterChain chain) -> { + // do nothing for now. + }; + } + } + + @Bean + @ConditionalOnMissingBean(RequestContextListener.class) + public RequestContextListener requestContextListener() { + return new RequestContextListener(); + } +} diff --git a/music-rest/src/main/java/org/onap/music/authentication/AuthUtil.java b/music-rest/src/main/java/org/onap/music/authentication/AuthUtil.java new file mode 100644 index 00000000..ee3b77a4 --- /dev/null +++ b/music-rest/src/main/java/org/onap/music/authentication/AuthUtil.java @@ -0,0 +1,276 @@ +/* + * ============LICENSE_START========================================== + * org.onap.music + * =================================================================== + * Copyright (c) 2017 AT&T Intellectual Property + * + * Modifications Copyright (C) 2019 IBM. + * =================================================================== + * 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. + * + * ============LICENSE_END============================================= + * ==================================================================== + */ + +package org.onap.music.authentication; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Iterator; +import java.util.List; +import java.util.regex.Pattern; + +import javax.servlet.ServletRequest; +import javax.servlet.http.HttpServletRequest; + +import org.apache.commons.codec.DecoderException; +import org.apache.commons.codec.binary.Hex; +import org.onap.aaf.cadi.CadiWrap; +import org.onap.aaf.cadi.Permission; +import org.onap.aaf.cadi.aaf.AAFPermission; +import org.onap.music.eelf.logging.EELFLoggerDelegate; +import org.onap.music.exceptions.MusicAuthenticationException; + +public class AuthUtil { + + private static EELFLoggerDelegate logger = EELFLoggerDelegate.getLogger(AuthUtil.class); + + private AuthUtil() { + throw new IllegalStateException("Utility class"); + } + + /** + * Get the list of permissions from the Request object. + * + * + * @param request servlet request object + * @return returns list of AAFPermission of the requested MechId for all the + * namespaces + */ + public static List<AAFPermission> getAAFPermissions(ServletRequest request) { + CadiWrap wrapReq = (CadiWrap) request; + + List<Permission> perms = wrapReq.getPermissions(wrapReq.getUserPrincipal()); + List<AAFPermission> aafPermsList = new ArrayList<>(); + for (Permission perm : perms) { + AAFPermission aafPerm = (AAFPermission) perm; + aafPermsList.add(aafPerm); + } + return aafPermsList; + } + + /** + * Here is a sample of a permission object in AAI. The key attribute will have + * Type|Instance|Action. + * AAFPermission: + * NS: null + * Type: org.onap.music.cadi.keyspace ( Permission Type ) + * Instance: tomtest ( Cassandra Keyspace ) + * Action: *|GET|ALL ( Access Level [*|ALL] for full access and [GET] for Read only) + * Key: org.onap.music.cadi.keyspace|tomtest|* + * + * This method will filter all permissions whose key starts with the requested namespace. + * The nsamespace here is the music namespace which is defined in music.property file. + * i;e is the type contains in key is org.onap.music.cadi.keyspace and the namespace + * value is org.onap.music.cadi.keyspace, it will add to list + * otherwise reject. + * + * @param nameSpace + * @param allPermissionsList + * @return + */ + private static List<AAFPermission> filterNameSpacesAAFPermissions(String nameSpace, + List<AAFPermission> allPermissionsList) { + List<AAFPermission> list = new ArrayList<>(); + for (Iterator<AAFPermission> iterator = allPermissionsList.iterator(); iterator.hasNext();) { + AAFPermission aafPermission = iterator.next(); + if(aafPermission.getType().indexOf(nameSpace) == 0) { + list.add(aafPermission); + } + } + return list; + } + + /** + * Decode certian characters from url encoded to normal. + * + * @param str - String being decoded. + * @return returns the decoded string. + * @throws Exception throws excpetion + */ + public static String decodeFunctionCode(String str) throws MusicAuthenticationException { + final String DECODEVALUE_FORWARDSLASH = "2f"; + final String DECODEVALUE_HYPHEN = "2d"; + final String DECODEVALUE_ASTERISK = "2a"; + String decodedString = str; + List<Pattern> decodingList = new ArrayList<>(); + decodingList.add(Pattern.compile(DECODEVALUE_FORWARDSLASH)); + decodingList.add(Pattern.compile(DECODEVALUE_HYPHEN)); + decodingList.add(Pattern.compile(DECODEVALUE_ASTERISK)); + for (Pattern xssInputPattern : decodingList) { + try { + decodedString = decodedString.replaceAll("%" + xssInputPattern, + new String(Hex.decodeHex(xssInputPattern.toString().toCharArray()))); + } catch (DecoderException e) { + logger.error(EELFLoggerDelegate.securityLogger, + "AuthUtil Decode Failed! for instance: " + str); + throw new MusicAuthenticationException("Decode failed", e); + } + } + + return decodedString; + } + + /** + * + * + * @param request servlet request object + * @param nameSpace application namespace + * @return boolean value if the access is allowed + * @throws Exception throws exception + */ + public static boolean isAccessAllowed(ServletRequest request, String nameSpace) throws MusicAuthenticationException { + + if (request==null) { + throw new MusicAuthenticationException("Request cannot be null"); + } + + if (nameSpace==null || nameSpace.isEmpty()) { + throw new MusicAuthenticationException("NameSpace not Declared!"); + } + + boolean isauthorized = false; + List<AAFPermission> aafPermsList = getAAFPermissions(request); + logger.info(EELFLoggerDelegate.securityLogger, + "AAFPermission of the requested MechId for all the namespaces: " + aafPermsList); + logger.debug(EELFLoggerDelegate.securityLogger, "Requested nameSpace: " + nameSpace); + + List<AAFPermission> aafPermsFinalList = filterNameSpacesAAFPermissions(nameSpace, aafPermsList); + + logger.debug(EELFLoggerDelegate.securityLogger, + "AuthUtil list of AAFPermission for the specific namespace :::" + + aafPermsFinalList); + + HttpServletRequest httpRequest = (HttpServletRequest) request; + String requestUri = httpRequest.getRequestURI().substring(httpRequest.getContextPath().length() + 1); + + logger.debug(EELFLoggerDelegate.securityLogger, + "AuthUtil requestUri :::" + requestUri); + + for (Iterator<AAFPermission> iterator = aafPermsFinalList.iterator(); iterator.hasNext();) { + AAFPermission aafPermission = iterator.next(); + if(!isauthorized) { + isauthorized = isMatchPattern(aafPermission, requestUri, httpRequest.getMethod()); + } + } + + logger.debug(EELFLoggerDelegate.securityLogger, + "isAccessAllowed for the request uri: " + requestUri + "is :" + isauthorized); + return isauthorized; + } + + /** + * + * This method will check, if the requested URI matches any of the instance + * found with the AAF permission list. + * i;e if the request URI is; /v2/keyspaces/tomtest/tables/emp15 and in the + * AAF permission table, we have an instance + * defined as "tomtest" mapped the logged in user, it will allow else error. + * + * User trying to create or aquire a lock + * Here is the requested URI /v2/locks/create/tomtest.MyTable.Field1 + * Here the keyspace name i;e tomtest will be test throught out the URL if it + * matches, it will allow the user to create a lock. + * "tomtest" here is the key, which is mapped as an instance in permission object. + * Instance can be delimited with ":" i;e ":music-cassandra-1908-dev:admin". In this case, + * each delimited + * token will be matched with that of request URI. + * + * Example Permission: + * org.onap.music.api.user.access|tomtest|* or ALL + * org.onap.music.api.user.access|tomtest|GET + * In case of the action field is ALL and *, user will be allowed else it will + * be matched with the requested http method type. + * + * + * + * @param aafPermission - AAfpermission obtained by cadi. + * @param requestUri - Rest URL client is calling. + * @param method - REST Method being used (GET,POST,PUT,DELETE) + * @return returns a boolean + * @throws Exception - throws an exception + */ + private static boolean isMatchPattern( + AAFPermission aafPermission, + String requestUri, + String method) throws MusicAuthenticationException { + if (null == aafPermission || null == requestUri || null == method) { + return false; + } + + String permKey = aafPermission.getKey(); + + logger.debug(EELFLoggerDelegate.securityLogger, "isMatchPattern permKey: " + + permKey + ", requestUri " + requestUri + " ," + method); + + String[] keyArray = permKey.split("\\|"); + String[] subPath = null; + String instance = keyArray[1]; + String action = keyArray[2]; + + //if the instance & action both are * , then allow + if ("*".equalsIgnoreCase(instance) && "*".equalsIgnoreCase(action)) { + return true; + } + //Decode string like %2f, %2d and %2a + if (!"*".equals(instance)) { + instance = decodeFunctionCode(instance); + } + if (!"*".equals(action)) { + action = decodeFunctionCode(action); + } + //Instance: :music-cassandra-1908-dev:admin + List<String> instanceList = Arrays.asList(instance.split(":")); + + String[] path = requestUri.split("/"); + + for (int i = 0; i < path.length; i++) { + // Sometimes the value will begin with "$", so we need to remove it + if (path[i].startsWith("$")) { + path[i] = path[i].replace("$",""); + } + // Each path element can again delemited by ".";i;e + // tomtest.tables.emp. We have scenarios like lock aquire URL + subPath = path[i].split("\\."); + for (int j = 0; j < subPath.length; j++) { + if (instanceList.contains(subPath[j])) { + return checkAction(method,action); + } else { + continue; + } + } + } + return false; + } + + private static boolean checkAction(String method, String action) { + if ("*".equals(action) || "ALL".equalsIgnoreCase(action)) { + return true; + } else { + return (method.equalsIgnoreCase(action)); + } + } + + + +}
\ No newline at end of file diff --git a/music-rest/src/main/java/org/onap/music/authentication/AuthorizationError.java b/music-rest/src/main/java/org/onap/music/authentication/AuthorizationError.java new file mode 100644 index 00000000..7015b550 --- /dev/null +++ b/music-rest/src/main/java/org/onap/music/authentication/AuthorizationError.java @@ -0,0 +1,55 @@ +/* + * ============LICENSE_START========================================== + * org.onap.music + * =================================================================== + * Copyright (c) 2017 AT&T Intellectual Property + * =================================================================== + * 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. + * + * ============LICENSE_END============================================= + * ==================================================================== + */ + +package org.onap.music.authentication; + + +/** + * Authorization error class used while setting error code and description back to client. + * + * + * @author sp931a + * + */ +public class AuthorizationError { + + private int responseCode; + + private String responseMessage; + + public int getResponseCode() { + return responseCode; + } + + public void setResponseCode(int responseCode) { + this.responseCode = responseCode; + } + + public String getResponseMessage() { + return responseMessage; + } + + public void setResponseMessage(String responseMessage) { + this.responseMessage = responseMessage; + } + +}
\ No newline at end of file diff --git a/music-rest/src/main/java/org/onap/music/authentication/CadiAuthFilter.java b/music-rest/src/main/java/org/onap/music/authentication/CadiAuthFilter.java new file mode 100644 index 00000000..d043e6d6 --- /dev/null +++ b/music-rest/src/main/java/org/onap/music/authentication/CadiAuthFilter.java @@ -0,0 +1,65 @@ +/* + * ============LICENSE_START========================================== + * org.onap.music + * =================================================================== + * Copyright (c) 2017 AT&T Intellectual Property + * =================================================================== + * 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. + * + * ============LICENSE_END============================================= + * ==================================================================== + */ + +package org.onap.music.authentication; + + +import java.io.IOException; + +import javax.servlet.FilterChain; +import javax.servlet.FilterConfig; +import javax.servlet.ServletException; +import javax.servlet.ServletRequest; +import javax.servlet.ServletResponse; +import javax.servlet.annotation.WebFilter; + +import org.onap.aaf.cadi.PropAccess; +import org.onap.aaf.cadi.filter.CadiFilter; +import org.onap.music.eelf.logging.EELFLoggerDelegate; + +@WebFilter(urlPatterns = { "/*" }) +public class CadiAuthFilter extends CadiFilter { + + private static final EELFLoggerDelegate logger = EELFLoggerDelegate.getLogger(CadiAuthFilter.class); + + public CadiAuthFilter(PropAccess access) throws ServletException { + super(true, access); + } + + public CadiAuthFilter() throws ServletException { + super(); + } + + @Override + public void init(FilterConfig filterConfig) throws ServletException { + super.init(filterConfig); + } + + @Override + public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) + throws IOException, ServletException { + logger.info(EELFLoggerDelegate.securityLogger, "Request is entering cadifilter"); + long startTime = System.currentTimeMillis(); + request.setAttribute("startTime", startTime); + super.doFilter(request, response, chain); + } +}
\ No newline at end of file diff --git a/music-rest/src/main/java/org/onap/music/authentication/MusicAuthorizationFilter.java b/music-rest/src/main/java/org/onap/music/authentication/MusicAuthorizationFilter.java new file mode 100644 index 00000000..bde3e205 --- /dev/null +++ b/music-rest/src/main/java/org/onap/music/authentication/MusicAuthorizationFilter.java @@ -0,0 +1,122 @@ +/* + * ============LICENSE_START========================================== + * org.onap.music + * =================================================================== + * Copyright (c) 2017 AT&T Intellectual Property + * =================================================================== + * Modifications Copyright (c) 2019 Samsung + * =================================================================== + * 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. + * + * ============LICENSE_END============================================= + * ==================================================================== + */ + +package org.onap.music.authentication; + +import java.io.IOException; + +import javax.servlet.Filter; +import javax.servlet.FilterChain; +import javax.servlet.FilterConfig; +import javax.servlet.ServletException; +import javax.servlet.ServletRequest; +import javax.servlet.ServletResponse; +import javax.servlet.http.HttpServletResponse; + +import org.onap.music.eelf.logging.EELFLoggerDelegate; +import org.onap.music.exceptions.MusicAuthenticationException; +import org.onap.music.main.MusicUtil; +import com.fasterxml.jackson.databind.ObjectMapper; + +/** + * This filter class does authorization from AAF + * + * @author sp931a + * + */ +//@PropertySource(value = {"file:/opt/app/music/etc/music.properties"}) +public class MusicAuthorizationFilter implements Filter { + + private String musicNS = MusicUtil.getMusicAafNs(); + + private EELFLoggerDelegate logger = EELFLoggerDelegate.getLogger(MusicAuthorizationFilter.class); + + public MusicAuthorizationFilter() throws ServletException { + super(); + } + + @Override + public void init(FilterConfig filterConfig) throws ServletException { + // Do Nothing + } + + @Override + public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) + throws IOException, ServletException { + HttpServletResponse httpResponse = null; + + boolean isAuthAllowed = false; + + if (null != servletRequest && null != servletResponse) { + httpResponse = (HttpServletResponse) servletResponse; + long startTime = 0; + if( null != servletRequest.getAttribute("startTime")) { + startTime = ((Long)servletRequest.getAttribute("startTime")).longValue(); + } else { + startTime = System.currentTimeMillis(); // this will set only incase the request attribute not found + } + + try { + isAuthAllowed = AuthUtil.isAccessAllowed(servletRequest, musicNS); + } catch (MusicAuthenticationException e) { + logger.error(EELFLoggerDelegate.securityLogger, + "Error while checking authorization Music Namespace: " + musicNS + " : " + e.getMessage(),e); + } catch ( Exception e) { + logger.error(EELFLoggerDelegate.securityLogger, + "Error while checking authorization Music Namespace: " + musicNS + " : " + e.getMessage(),e); + } + + long endTime = System.currentTimeMillis(); + + //startTime set in <code>CadiAuthFilter</code> doFilter + logger.debug(EELFLoggerDelegate.securityLogger, + "Time took for authentication & authorization : " + + (endTime - startTime) + " milliseconds"); + + if (!isAuthAllowed) { + logger.info(EELFLoggerDelegate.securityLogger, + "Unauthorized Access"); + AuthorizationError authError = new AuthorizationError(); + authError.setResponseCode(HttpServletResponse.SC_UNAUTHORIZED); + authError.setResponseMessage("Unauthorized Access - Please make sure you are " + + "onboarded and have proper access to MUSIC. "); + + byte[] responseToSend = restResponseBytes(authError); + httpResponse.setHeader("Content-Type", "application/json"); + + httpResponse.setStatus(HttpServletResponse.SC_UNAUTHORIZED); + servletResponse.getOutputStream().write(responseToSend); + return; + } else { + filterChain.doFilter(servletRequest, servletResponse); + } + } + } + + private byte[] restResponseBytes(AuthorizationError eErrorResponse) throws IOException { + String serialized = new ObjectMapper().writeValueAsString(eErrorResponse); + return serialized.getBytes(); + } +} + diff --git a/music-rest/src/main/java/org/onap/music/conductor/conditionals/JsonConditional.java b/music-rest/src/main/java/org/onap/music/conductor/conditionals/JsonConditional.java new file mode 100644 index 00000000..4efcabea --- /dev/null +++ b/music-rest/src/main/java/org/onap/music/conductor/conditionals/JsonConditional.java @@ -0,0 +1,90 @@ +/* + * ============LICENSE_START========================================== + * org.onap.music + * =================================================================== + * Copyright (c) 2017 AT&T Intellectual Property + * =================================================================== + * 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. + * + * ============LICENSE_END============================================= + * ==================================================================== + */ + +package org.onap.music.conductor.conditionals; + +import java.io.Serializable; +import java.util.Map; + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; + +import io.swagger.annotations.ApiModel; + +@ApiModel(value = "JsonConditional", description = "Json model for insert or update into table based on some conditions") +@JsonIgnoreProperties(ignoreUnknown = true) +public class JsonConditional implements Serializable { + + private String primaryKey; + private String primaryKeyValue; + private String casscadeColumnName; + private Map<String,Object> tableValues; + private Map<String,Object> casscadeColumnData; + private Map<String,Map<String,String>> conditions; + + public Map<String, Object> getTableValues() { + return tableValues; + } + public void setTableValues(Map<String, Object> tableValues) { + this.tableValues = tableValues; + } + + public String getPrimaryKey() { + return primaryKey; + } + public String getPrimaryKeyValue() { + return primaryKeyValue; + } + public String getCasscadeColumnName() { + return casscadeColumnName; + } + + public Map<String, Object> getCasscadeColumnData() { + return casscadeColumnData; + } + + + + public void setPrimaryKey(String primaryKey) { + this.primaryKey = primaryKey; + } + public void setPrimaryKeyValue(String primaryKeyValue) { + this.primaryKeyValue = primaryKeyValue; + } + public Map<String, Map<String, String>> getConditions() { + return conditions; + } + public void setConditions(Map<String, Map<String, String>> conditions) { + this.conditions = conditions; + } + public void setCasscadeColumnName(String casscadeColumnName) { + this.casscadeColumnName = casscadeColumnName; + } + + public void setCasscadeColumnData(Map<String, Object> casscadeColumnData) { + this.casscadeColumnData = casscadeColumnData; + } + + + + + +}
\ No newline at end of file diff --git a/music-rest/src/main/java/org/onap/music/conductor/conditionals/MusicConditional.java b/music-rest/src/main/java/org/onap/music/conductor/conditionals/MusicConditional.java new file mode 100644 index 00000000..18fa8a18 --- /dev/null +++ b/music-rest/src/main/java/org/onap/music/conductor/conditionals/MusicConditional.java @@ -0,0 +1,379 @@ +/* + * ============LICENSE_START========================================== + * org.onap.music + * =================================================================== + * Copyright (c) 2017 AT&T Intellectual Property + * =================================================================== + * Modifications Copyright (C) 2019 IBM. + * Modifications Copyright (c) 2019 Samsung + * =================================================================== + * 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. + * + * ============LICENSE_END============================================= + * ==================================================================== + */ + +package org.onap.music.conductor.conditionals; + +import java.io.PrintWriter; +import java.io.StringWriter; +import java.util.HashMap; +import java.util.Map; + +import org.codehaus.jettison.json.JSONObject; +import org.onap.music.datastore.MusicDataStoreHandle; +import org.onap.music.datastore.PreparedQueryObject; +import org.onap.music.eelf.logging.EELFLoggerDelegate; +import org.onap.music.eelf.logging.format.AppMessages; +import org.onap.music.eelf.logging.format.ErrorSeverity; +import org.onap.music.eelf.logging.format.ErrorTypes; +import org.onap.music.exceptions.MusicLockingException; +import org.onap.music.exceptions.MusicQueryException; +import org.onap.music.exceptions.MusicServiceException; +import org.onap.music.main.MusicCore; +import org.onap.music.main.MusicUtil; +import org.onap.music.main.ResultType; +import org.onap.music.main.ReturnType; +import org.onap.music.rest.RestMusicDataAPI; + +import com.datastax.driver.core.ColumnDefinitions; +import com.datastax.driver.core.DataType; +import com.datastax.driver.core.ResultSet; +import com.datastax.driver.core.Row; +import com.datastax.driver.core.TableMetadata; + +public class MusicConditional { + private static EELFLoggerDelegate logger = EELFLoggerDelegate.getLogger(RestMusicDataAPI.class); + + public static ReturnType conditionalInsert(String keyspace, String tablename, String casscadeColumnName, + Map<String, Object> casscadeColumnData, String primaryKey, Map<String, Object> valuesMap, + Map<String, String> status) throws Exception { + + Map<String, PreparedQueryObject> queryBank = new HashMap<>(); + TableMetadata tableInfo = null; + tableInfo = MusicDataStoreHandle.returnColumnMetadata(keyspace, tablename); + DataType primaryIdType = tableInfo.getPrimaryKey().get(0).getType(); + String primaryId = tableInfo.getPrimaryKey().get(0).getName(); + DataType casscadeColumnType = tableInfo.getColumn(casscadeColumnName).getType(); + String vector = String.valueOf(Thread.currentThread().getId() + System.currentTimeMillis()); + + PreparedQueryObject select = new PreparedQueryObject(); + select.appendQueryString("SELECT * FROM " + keyspace + "." + tablename + " where " + primaryId + " = ?"); + select.addValue(MusicUtil.convertToActualDataType(primaryIdType, primaryKey)); + queryBank.put(MusicUtil.SELECT, select); + + PreparedQueryObject update = new PreparedQueryObject(); + //casscade column values + Map<String, String> updateColumnvalues = getValues(true, casscadeColumnData, status); + Object formatedValues = MusicUtil.convertToActualDataType(casscadeColumnType, updateColumnvalues); + update.appendQueryString("UPDATE " + keyspace + "." + tablename + " SET " + casscadeColumnName + " =" + + casscadeColumnName + " + ? , vector_ts = ?" + " WHERE " + primaryId + " = ? "); + update.addValue(formatedValues); + update.addValue(MusicUtil.convertToActualDataType(DataType.text(), vector)); + update.addValue(MusicUtil.convertToActualDataType(primaryIdType, primaryKey)); + queryBank.put(MusicUtil.UPDATE, update); + + + //casscade column values + Map<String, String> insertColumnvalues = getValues(false, casscadeColumnData, status); + formatedValues = MusicUtil.convertToActualDataType(casscadeColumnType, insertColumnvalues); + PreparedQueryObject insert = extractQuery(valuesMap, tableInfo, tablename, keyspace, primaryId, primaryKey,casscadeColumnName,formatedValues); + queryBank.put(MusicUtil.INSERT, insert); + + + String key = keyspace + "." + tablename + "." + primaryKey; + String lockId; + try { + lockId = MusicCore.createLockReference(key); + } catch (MusicLockingException e) { + return new ReturnType(ResultType.FAILURE, e.getMessage()); + } + long leasePeriod = MusicUtil.getDefaultLockLeasePeriod(); + ReturnType lockAcqResult = MusicCore.acquireLockWithLease(key, lockId, leasePeriod); + + try { + if (lockAcqResult.getResult().equals(ResultType.SUCCESS)) { + ReturnType criticalPutResult = conditionalInsertAtomic(lockId, keyspace, tablename, primaryKey, + queryBank); + MusicCore.destroyLockRef(lockId); + if (criticalPutResult.getMessage().contains("insert")) + criticalPutResult + .setMessage("Insert values: "); + else if (criticalPutResult.getMessage().contains("update")) + criticalPutResult + .setMessage("Update values: " + updateColumnvalues); + return criticalPutResult; + + } else { + MusicCore.destroyLockRef(lockId); + return lockAcqResult; + } + } catch (Exception e) { + logger.error(EELFLoggerDelegate.applicationLogger, e); + MusicCore.destroyLockRef(lockId); + return new ReturnType(ResultType.FAILURE, e.getMessage()); + } + + } + + public static ReturnType conditionalInsertAtomic(String lockId, String keyspace, String tableName, + String primaryKey, Map<String, PreparedQueryObject> queryBank) { + + ResultSet results = null; + + try { + String fullyQualifiedKey = keyspace + "." + tableName + "." + primaryKey; + ReturnType lockAcqResult = MusicCore.acquireLock(fullyQualifiedKey, lockId); + if (lockAcqResult.getResult().equals(ResultType.SUCCESS)) { + try { + results = MusicDataStoreHandle.getDSHandle().executeQuorumConsistencyGet(queryBank.get(MusicUtil.SELECT)); + } catch (Exception e) { + logger.error(EELFLoggerDelegate.applicationLogger, e); + return new ReturnType(ResultType.FAILURE, e.getMessage()); + } + if (results.all().isEmpty()) { + MusicDataStoreHandle.getDSHandle().executePut(queryBank.get(MusicUtil.INSERT), "critical"); + return new ReturnType(ResultType.SUCCESS, "insert"); + } else { + MusicDataStoreHandle.getDSHandle().executePut(queryBank.get(MusicUtil.UPDATE), "critical"); + return new ReturnType(ResultType.SUCCESS, "update"); + } + } else { + return new ReturnType(ResultType.FAILURE, + "Cannot perform operation since you are the not the lock holder"); + } + + } catch (Exception e) { + StringWriter sw = new StringWriter(); + e.printStackTrace(new PrintWriter(sw)); + String exceptionAsString = sw.toString(); + logger.error(EELFLoggerDelegate.applicationLogger, e); + return new ReturnType(ResultType.FAILURE, + "Exception thrown while doing the critical put, check sanctity of the row/conditions:\n" + + exceptionAsString); + } + + } + + public static ReturnType update(UpdateDataObject dataObj) + throws MusicLockingException, MusicQueryException, MusicServiceException { + + String key = dataObj.getKeyspace() + "." + dataObj.getTableName() + "." + dataObj.getPrimaryKeyValue(); + String lockId = MusicCore.createLockReference(key); + long leasePeriod = MusicUtil.getDefaultLockLeasePeriod(); + ReturnType lockAcqResult = MusicCore.acquireLockWithLease(key, lockId, leasePeriod); + + try { + + if (lockAcqResult.getResult().equals(ResultType.SUCCESS)) { + ReturnType criticalPutResult = updateAtomic(new UpdateDataObject().setLockId(lockId) + .setKeyspace(dataObj.getKeyspace()) + .setTableName( dataObj.getTableName()) + .setPrimaryKey(dataObj.getPrimaryKey()) + .setPrimaryKeyValue(dataObj.getPrimaryKeyValue()) + .setQueryBank(dataObj.getQueryBank()) + .setPlanId(dataObj.getPlanId()) + .setCascadeColumnValues(dataObj.getCascadeColumnValues()) + .setCascadeColumnName(dataObj.getCascadeColumnName())); + + MusicCore.destroyLockRef(lockId); + return criticalPutResult; + } else { + MusicCore.destroyLockRef(lockId); + return lockAcqResult; + } + + } catch (Exception e) { + MusicCore.destroyLockRef(lockId); + logger.error(EELFLoggerDelegate.applicationLogger, e); + return new ReturnType(ResultType.FAILURE, e.getMessage()); + + } + } + + public static ReturnType updateAtomic(UpdateDataObject dataObj) { + try { + String fullyQualifiedKey = dataObj.getKeyspace() + "." + dataObj.getTableName() + "." + dataObj.getPrimaryKeyValue(); + ReturnType lockAcqResult = MusicCore.acquireLock(fullyQualifiedKey, dataObj.getLockId()); + + if (lockAcqResult.getResult().equals(ResultType.SUCCESS)) { + Row row = MusicDataStoreHandle.getDSHandle().executeQuorumConsistencyGet(dataObj.getQueryBank().get(MusicUtil.SELECT)).one(); + + if(row != null) { + Map<String, String> updatedValues = cascadeColumnUpdateSpecific(row, dataObj.getCascadeColumnValues(), dataObj.getCascadeColumnName(), dataObj.getPlanId()); + JSONObject json = new JSONObject(updatedValues); + PreparedQueryObject update = new PreparedQueryObject(); + String vector_ts = String.valueOf(Thread.currentThread().getId() + System.currentTimeMillis()); + update.appendQueryString("UPDATE " + dataObj.getKeyspace() + "." + dataObj.getTableName() + " SET " + dataObj.getCascadeColumnName() + "['" + dataObj.getPlanId() + + "'] = ?, vector_ts = ? WHERE " + dataObj.getPrimaryKey() + " = ?"); + update.addValue(MusicUtil.convertToActualDataType(DataType.text(), json.toString())); + update.addValue(MusicUtil.convertToActualDataType(DataType.text(), vector_ts)); + update.addValue(MusicUtil.convertToActualDataType(DataType.text(), dataObj.getPrimaryKeyValue())); + try { + MusicDataStoreHandle.getDSHandle().executePut(update, "critical"); + } catch (Exception ex) { + logger.error(EELFLoggerDelegate.applicationLogger, ex); + return new ReturnType(ResultType.FAILURE, ex.getMessage()); + } + }else { + return new ReturnType(ResultType.FAILURE,"Cannot find data related to key: "+dataObj.getPrimaryKey()); + } + MusicDataStoreHandle.getDSHandle().executePut(dataObj.getQueryBank().get(MusicUtil.UPSERT), "critical"); + return new ReturnType(ResultType.SUCCESS, "update success"); + + } else { + return new ReturnType(ResultType.FAILURE, + "Cannot perform operation since you are the not the lock holder"); + } + + } catch (Exception e) { + StringWriter sw = new StringWriter(); + e.printStackTrace(new PrintWriter(sw)); + String exceptionAsString = sw.toString(); + logger.error(EELFLoggerDelegate.applicationLogger, e); + return new ReturnType(ResultType.FAILURE, + "Exception thrown while doing the critical put, check sanctity of the row/conditions:\n" + + exceptionAsString); + } + + } + + @SuppressWarnings("unchecked") + public static Map<String, String> getValues(boolean isExists, Map<String, Object> casscadeColumnData, + Map<String, String> status) { + + Map<String, String> returnMap = new HashMap<>(); + Object key = casscadeColumnData.get("key"); + String setStatus = ""; + Map<String, String> value = (Map<String, String>) casscadeColumnData.get("value"); + + if (isExists) + setStatus = status.get("exists"); + else + setStatus = status.get("nonexists"); + + value.put("status", setStatus); + JSONObject valueJson = new JSONObject(value); + returnMap.put(key.toString(), valueJson.toString()); + return returnMap; + + } + + public static PreparedQueryObject extractQuery(Map<String, Object> valuesMap, TableMetadata tableInfo, String tableName, + String keySpaceName,String primaryKeyName,String primaryKey,String casscadeColumn,Object casscadeColumnValues) throws Exception { + + PreparedQueryObject queryObject = new PreparedQueryObject(); + StringBuilder fieldsString = new StringBuilder("(vector_ts"+","); + StringBuilder valueString = new StringBuilder("(" + "?" + ","); + String vector = String.valueOf(Thread.currentThread().getId() + System.currentTimeMillis()); + String localPrimaryKey; + queryObject.addValue(vector); + if(casscadeColumn!=null && casscadeColumnValues!=null) { + fieldsString.append(casscadeColumn).append(" ,"); + valueString.append("?,"); + queryObject.addValue(casscadeColumnValues); + } + + int counter = 0; + for (Map.Entry<String, Object> entry : valuesMap.entrySet()) { + + fieldsString.append(entry.getKey()); + Object valueObj = entry.getValue(); + if (primaryKeyName.equals(entry.getKey())) { + localPrimaryKey = entry.getValue() + ""; + localPrimaryKey = localPrimaryKey.replace("'", "''"); + } + DataType colType = null; + try { + colType = tableInfo.getColumn(entry.getKey()).getType(); + } catch(NullPointerException ex) { + logger.error(EELFLoggerDelegate.errorLogger,ex.getMessage() +" Invalid column name : "+entry.getKey(), + AppMessages.INCORRECTDATA ,ErrorSeverity.CRITICAL, ErrorTypes.DATAERROR, ex); + } + + Object formattedValue = null; + try { + formattedValue = MusicUtil.convertToActualDataType(colType, valueObj); + } catch (Exception e) { + logger.error(EELFLoggerDelegate.errorLogger,e.getMessage(), e); + } + + valueString.append("?"); + queryObject.addValue(formattedValue); + + + if (counter == valuesMap.size() - 1) { + fieldsString.append(")"); + valueString.append(")"); + } else { + fieldsString.append(","); + valueString.append(","); + } + counter = counter + 1; + } + queryObject.appendQueryString("INSERT INTO " + keySpaceName + "." + tableName + " " + + fieldsString + " VALUES " + valueString); + return queryObject; + } + + public static Object getColValue(Row row, String colName, DataType colType) { + switch (colType.getName()) { + case VARCHAR: + return row.getString(colName); + case UUID: + return row.getUUID(colName); + case VARINT: + return row.getVarint(colName); + case BIGINT: + return row.getLong(colName); + case INT: + return row.getInt(colName); + case FLOAT: + return row.getFloat(colName); + case DOUBLE: + return row.getDouble(colName); + case BOOLEAN: + return row.getBool(colName); + case MAP: + return row.getMap(colName, String.class, String.class); + default: + return null; + } + } + + @SuppressWarnings("unchecked") + public static Map<String, String> cascadeColumnUpdateSpecific(Row row, Map<String, String> changeOfStatus, + String cascadeColumnName, String planId) { + + ColumnDefinitions colInfo = row.getColumnDefinitions(); + DataType colType = colInfo.getType(cascadeColumnName); + Object columnValue = getColValue(row, cascadeColumnName, colType); + + Map<String, String> finalValues = new HashMap<>(); + Map<String, String> values = (Map<String, String>) columnValue; + if (values != null && values.keySet().contains(planId)) { + String valueString = values.get(planId); + String tempValueString = valueString.replaceAll("\\{", "").replaceAll("\"", "").replaceAll("\\}", ""); + String[] elements = tempValueString.split(","); + for (String str : elements) { + String[] keyValue = str.split(":"); + if ((changeOfStatus.keySet().contains(keyValue[0].replaceAll("\\s", "")))) + keyValue[1] = changeOfStatus.get(keyValue[0].replaceAll("\\s", "")); + finalValues.put(keyValue[0], keyValue[1]); + } + } + return finalValues; + + } + +} diff --git a/music-rest/src/main/java/org/onap/music/conductor/conditionals/RestMusicConditionalAPI.java b/music-rest/src/main/java/org/onap/music/conductor/conditionals/RestMusicConditionalAPI.java new file mode 100644 index 00000000..584a9e47 --- /dev/null +++ b/music-rest/src/main/java/org/onap/music/conductor/conditionals/RestMusicConditionalAPI.java @@ -0,0 +1,205 @@ +/* + * ============LICENSE_START========================================== + * org.onap.music + * =================================================================== + * Copyright (c) 2017 AT&T Intellectual Property + * =================================================================== + * Modifications Copyright (c) 2018 IBM + * =================================================================== + * 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. + * + * ============LICENSE_END============================================= + * ==================================================================== + */ + +package org.onap.music.conductor.conditionals; + +import java.util.HashMap; +import java.util.LinkedHashMap; +import java.util.Map; + +import javax.ws.rs.Consumes; +import javax.ws.rs.HeaderParam; +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.ws.rs.core.Response.ResponseBuilder; +import javax.ws.rs.core.Response.Status; + +import org.onap.music.datastore.MusicDataStoreHandle; +import org.onap.music.datastore.PreparedQueryObject; +import org.onap.music.eelf.logging.EELFLoggerDelegate; +import org.onap.music.eelf.logging.format.AppMessages; +import org.onap.music.eelf.logging.format.ErrorSeverity; +import org.onap.music.eelf.logging.format.ErrorTypes; +import org.onap.music.main.MusicUtil; +import org.onap.music.main.ResultType; +import org.onap.music.main.ReturnType; +import org.onap.music.response.jsonobjects.JsonResponse; +import org.onap.music.conductor.*; + +import com.datastax.driver.core.DataType; +import com.datastax.driver.core.TableMetadata; + +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiParam; + +@Path("/v2/conditional") +@Api(value = "Conditional Api", hidden = true) +public class RestMusicConditionalAPI { + private static EELFLoggerDelegate logger = EELFLoggerDelegate.getLogger(RestMusicConditionalAPI.class); + private static final String XMINORVERSION = "X-minorVersion"; + private static final String XPATCHVERSION = "X-patchVersion"; + private static final String NS = "ns"; + private static final String VERSION = "v2"; + + @POST + @Path("/insert/keyspaces/{keyspace}/tables/{tablename}") + @Consumes(MediaType.APPLICATION_JSON) + @Produces(MediaType.APPLICATION_JSON) + public Response insertConditional( + @ApiParam(value = "Major Version", required = true) + @PathParam("version") String version, + @ApiParam(value = "Minor Version", required = false) + @HeaderParam(XMINORVERSION) String minorVersion, + @ApiParam(value = "Patch Version", required = false) + @HeaderParam(XPATCHVERSION) String patchVersion, + @ApiParam(value = "AID", required = true) + @HeaderParam("aid") String aid, + @ApiParam(value = "Application namespace", required = true) + @HeaderParam(NS) String ns, + @ApiParam(value = "Authorization", required = true) + @HeaderParam("Authorization") String authorization, + @ApiParam(value = "Keyspace Name", required = true) + @PathParam("keyspace") String keyspace, + @ApiParam(value = "Table Name", required = true) + @PathParam("tablename") String tablename, + JsonConditional jsonObj) throws Exception { + ResponseBuilder response = + MusicUtil.buildVersionResponse(VERSION, minorVersion, patchVersion); + String primaryKey = jsonObj.getPrimaryKey(); + String primaryKeyValue = jsonObj.getPrimaryKeyValue(); + String casscadeColumnName = jsonObj.getCasscadeColumnName(); + Map<String, Object> tableValues = jsonObj.getTableValues(); + Map<String, Object> casscadeColumnData = jsonObj.getCasscadeColumnData(); + Map<String, Map<String, String>> conditions = jsonObj.getConditions(); + + if (primaryKey == null || primaryKeyValue == null || casscadeColumnName == null + || tableValues.isEmpty() || casscadeColumnData.isEmpty() || conditions.isEmpty()) { + logger.error(EELFLoggerDelegate.errorLogger, "", AppMessages.MISSINGINFO, ErrorSeverity.CRITICAL, + ErrorTypes.AUTHENTICATIONERROR); + return response.status(Status.UNAUTHORIZED).entity(new JsonResponse(ResultType.FAILURE) + .setError(String.valueOf("One or more input values missing")).toMap()).build(); + } + Map<String, Object> authMap = null; + Map<String, Object> valuesMap = new LinkedHashMap<>(); + for (Map.Entry<String, Object> entry : tableValues.entrySet()) { + valuesMap.put(entry.getKey(), entry.getValue()); + } + + Map<String, String> status = new HashMap<>(); + status.put("exists", conditions.get("exists").get("status")); + status.put("nonexists", conditions.get("nonexists").get("status")); + ReturnType out = null; + + out = MusicConditional.conditionalInsert(keyspace, tablename, + casscadeColumnName, casscadeColumnData,primaryKeyValue, valuesMap, status); + return response.status(Status.OK).entity(new JsonResponse( + out.getResult()).setMessage(out.getMessage()).toMap()) + .build(); + + } + + @SuppressWarnings("unchecked") + @PUT + @Path("/update/keyspaces/{keyspace}/tables/{tablename}") + @Consumes(MediaType.APPLICATION_JSON) + @Produces(MediaType.APPLICATION_JSON) + public Response updateConditional( + @ApiParam(value = "Major Version", required = true) + @PathParam("version") String version, + @ApiParam(value = "Minor Version", required = false) + @HeaderParam(XMINORVERSION) String minorVersion, + @ApiParam(value = "Patch Version", required = false) + @HeaderParam(XPATCHVERSION) String patchVersion, + @ApiParam(value = "AID", required = true) + @HeaderParam("aid") String aid, + @ApiParam(value = "Application namespace", required = true) + @HeaderParam(NS) String ns, + @ApiParam(value = "Authorization", required = true) + @HeaderParam("Authorization") String authorization, + @ApiParam(value = "Major Version", required = true) + @PathParam("keyspace") String keyspace, + @ApiParam(value = "Major Version", required = true) + @PathParam("tablename") String tablename, + JsonConditional upObj) throws Exception { + ResponseBuilder response = MusicUtil.buildVersionResponse(VERSION, minorVersion, patchVersion); + + String primaryKey = upObj.getPrimaryKey(); + String primaryKeyValue = upObj.getPrimaryKeyValue(); + String casscadeColumnName = upObj.getCasscadeColumnName(); + Map<String, Object> casscadeColumnData = upObj.getCasscadeColumnData(); + Map<String, Object> tableValues = upObj.getTableValues(); + + if (primaryKey == null || primaryKeyValue == null || casscadeColumnName == null + || casscadeColumnData.isEmpty()) { + logger.error(EELFLoggerDelegate.errorLogger, "", AppMessages.MISSINGINFO, ErrorSeverity.CRITICAL, + ErrorTypes.AUTHENTICATIONERROR); + return response.status(Status.UNAUTHORIZED).entity(new JsonResponse(ResultType.FAILURE) + .setError(String.valueOf("One or more input values missing")).toMap()).build(); + + } + + Map<String,String> casscadeColumnValueMap = + (Map<String, String>) casscadeColumnData.get("value"); + TableMetadata tableInfo = null; + tableInfo = MusicDataStoreHandle.returnColumnMetadata(keyspace, tablename); + DataType primaryIdType = tableInfo.getPrimaryKey().get(0).getType(); + String primaryId = tableInfo.getPrimaryKey().get(0).getName(); + + PreparedQueryObject select = new PreparedQueryObject(); + select.appendQueryString("SELECT * FROM " + keyspace + "." + + tablename + " where " + primaryId + " = ?"); + select.addValue(MusicUtil.convertToActualDataType(primaryIdType, primaryKeyValue)); + + PreparedQueryObject upsert = + MusicConditional.extractQuery(tableValues, tableInfo, tablename, + keyspace, primaryKey, primaryKeyValue, null, null); + Map<String,PreparedQueryObject> queryBank = new HashMap<>(); + queryBank.put(MusicUtil.SELECT, select); + queryBank.put(MusicUtil.UPSERT, upsert); + String planId = casscadeColumnData.get("key").toString(); + ReturnType result = MusicConditional.update(new UpdateDataObject().setQueryBank(queryBank) + .setKeyspace(keyspace) + .setTableName(tablename) + .setPrimaryKey(primaryKey) + .setPrimaryKeyValue(primaryKeyValue) + .setPlanId(planId) + .setCascadeColumnName(casscadeColumnName) + .setCascadeColumnValues(casscadeColumnValueMap)); + if (result.getResult() == ResultType.SUCCESS) { + return response.status(Status.OK) + .entity(new JsonResponse(result.getResult()) + .setMessage(result.getMessage()).toMap()).build(); + } + return response.status(Status.BAD_REQUEST) + .entity(new JsonResponse(result.getResult()) + .setMessage(result.getMessage()).toMap()).build(); + + } + +}
\ No newline at end of file diff --git a/music-rest/src/main/java/org/onap/music/conductor/conditionals/UpdateDataObject.java b/music-rest/src/main/java/org/onap/music/conductor/conditionals/UpdateDataObject.java new file mode 100644 index 00000000..1ea8994e --- /dev/null +++ b/music-rest/src/main/java/org/onap/music/conductor/conditionals/UpdateDataObject.java @@ -0,0 +1,119 @@ +/*- + * ============LICENSE_START======================================================= + * Copyright (C) 2019 Samsung Electronics Co., Ltd. All rights reserved. + * ================================================================================ + * 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. + * + * SPDX-License-Identifier: Apache-2.0 + * ============LICENSE_END========================================================= + */ +package org.onap.music.conductor.conditionals; + +import java.util.Map; +import org.onap.music.datastore.PreparedQueryObject; + +public class UpdateDataObject { + + Map<String, PreparedQueryObject> queryBank; + String keyspace; + String tableName; + String primaryKey; + String primaryKeyValue; + String planId; + String cascadeColumnName; + Map<String, String> cascadeColumnValues; + String lockId; + + public Map<String, PreparedQueryObject> getQueryBank() { + return queryBank; + } + + public UpdateDataObject setQueryBank(Map<String, PreparedQueryObject> queryBank) { + this.queryBank = queryBank; + return this; + } + + public String getKeyspace() { + return keyspace; + } + + public UpdateDataObject setKeyspace(String keyspace) { + this.keyspace = keyspace; + return this; + } + + public String getTableName() { + return tableName; + } + + public UpdateDataObject setTableName(String tableName) { + this.tableName = tableName; + return this; + } + + public String getPrimaryKey() { + return primaryKey; + } + + public UpdateDataObject setPrimaryKey(String primaryKey) { + this.primaryKey = primaryKey; + return this; + } + + public String getPrimaryKeyValue() { + return primaryKeyValue; + } + + public UpdateDataObject setPrimaryKeyValue(String primaryKeyValue) { + this.primaryKeyValue = primaryKeyValue; + return this; + } + + public String getPlanId() { + return planId; + } + + public UpdateDataObject setPlanId(String planId) { + this.planId = planId; + return this; + } + + public String getCascadeColumnName() { + return cascadeColumnName; + } + + public UpdateDataObject setCascadeColumnName(String cascadeColumnName) { + this.cascadeColumnName = cascadeColumnName; + return this; + } + + public Map<String, String> getCascadeColumnValues() { + return cascadeColumnValues; + } + + public UpdateDataObject setLockId(String lockId) { + this.lockId=lockId; + return this; + } + + public String getLockId() { + return lockId; + } + + public UpdateDataObject setCascadeColumnValues(Map<String, String> cascadeColumnValues) { + this.cascadeColumnValues = cascadeColumnValues; + return this; + } + + +} diff --git a/music-rest/src/main/java/org/onap/music/eelf/healthcheck/MusicHealthCheck.java b/music-rest/src/main/java/org/onap/music/eelf/healthcheck/MusicHealthCheck.java new file mode 100644 index 00000000..fbfc0de6 --- /dev/null +++ b/music-rest/src/main/java/org/onap/music/eelf/healthcheck/MusicHealthCheck.java @@ -0,0 +1,130 @@ +/* + * ============LICENSE_START========================================== + * org.onap.music + * =================================================================== + * Copyright (c) 2017 AT&T Intellectual Property + * + * Modifications Copyright (C) 2019 IBM. + * =================================================================== + * 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. + * + * ============LICENSE_END============================================= + * ==================================================================== + */ + +package org.onap.music.eelf.healthcheck; + +import java.util.UUID; + +import org.onap.music.datastore.MusicDataStoreHandle; +import org.onap.music.datastore.PreparedQueryObject; +import org.onap.music.eelf.logging.EELFLoggerDelegate; +import org.onap.music.eelf.logging.format.AppMessages; +import org.onap.music.eelf.logging.format.ErrorSeverity; +import org.onap.music.eelf.logging.format.ErrorTypes; +import org.onap.music.exceptions.MusicQueryException; +import org.onap.music.exceptions.MusicServiceException; +import org.onap.music.main.MusicUtil; +import org.onap.music.main.ResultType; +import org.onap.music.main.MusicCore; + +import com.datastax.driver.core.ConsistencyLevel; + +/** + * @author inam + * + */ +public class MusicHealthCheck { + + private static EELFLoggerDelegate logger = EELFLoggerDelegate.getLogger(MusicUtil.class); + + private String cassandrHost; + + public String getCassandraStatus(String consistency) { + logger.info(EELFLoggerDelegate.applicationLogger, "Getting Status for Cassandra"); + + boolean result = false; + UUID randomUUID = UUID.randomUUID(); + try { + result = getAdminKeySpace(consistency, randomUUID); + } catch( Exception e) { + if(e.getMessage().toLowerCase().contains("unconfigured table healthcheck")) { + logger.error("Error", e); + logger.debug("Creating table...."); + try { + boolean ksresult = createKeyspace(); + if(ksresult) { + result = getAdminKeySpace(consistency, randomUUID); + } + } catch (MusicServiceException e1) { + logger.error(EELFLoggerDelegate.errorLogger, e1.getMessage(), AppMessages.UNKNOWNERROR, ErrorSeverity.ERROR, ErrorTypes.UNKNOWN, e1); + } catch (MusicQueryException e1) { + logger.error(EELFLoggerDelegate.errorLogger, e1.getMessage(), AppMessages.UNKNOWNERROR, ErrorSeverity.ERROR, ErrorTypes.UNKNOWN,e1); + } + } else { + logger.error("Error", e); + return "One or more nodes are down or not responding."; + } + } + try { + cleanHealthCheckId(randomUUID); + } catch (MusicServiceException | MusicQueryException e) { + logger.error("Error while cleaning healthcheck record id...", e); + } + if (result) { + return "ACTIVE"; + } else { + logger.info(EELFLoggerDelegate.applicationLogger, "Cassandra Service is not responding"); + return "INACTIVE"; + } + } + + private Boolean getAdminKeySpace(String consistency, UUID randomUUID) throws MusicServiceException,MusicQueryException { + PreparedQueryObject pQuery = new PreparedQueryObject(); + pQuery.appendQueryString("insert into admin.healthcheck (id) values (?)"); + pQuery.addValue(randomUUID); + ResultType rs = null; + rs = MusicCore.nonKeyRelatedPut(pQuery, consistency); + logger.info(rs.toString()); + return null != rs; + + } + + private void cleanHealthCheckId(UUID randomUUID) throws MusicServiceException, MusicQueryException { + String cleanQuery = "delete from admin.healthcheck where id = ?"; + PreparedQueryObject deleteQueryObject = new PreparedQueryObject(); + deleteQueryObject.appendQueryString(cleanQuery); + deleteQueryObject.addValue(randomUUID); + MusicDataStoreHandle.getDSHandle().executePut(deleteQueryObject, "eventual"); + logger.info(EELFLoggerDelegate.applicationLogger, "Cassandra healthcheck responded and cleaned up."); + } + + + + private boolean createKeyspace() throws MusicServiceException,MusicQueryException { + PreparedQueryObject pQuery = new PreparedQueryObject(); + pQuery.appendQueryString("CREATE TABLE admin.healthcheck (id uuid PRIMARY KEY)"); + ResultType rs = null ; + rs = MusicCore.nonKeyRelatedPut(pQuery, ConsistencyLevel.ONE.toString()); + return rs != null && rs.getResult().toLowerCase().contains("success"); + } + + public String getCassandrHost() { + return cassandrHost; + } + + public void setCassandrHost(String cassandrHost) { + this.cassandrHost = cassandrHost; + } + +}
\ No newline at end of file diff --git a/music-rest/src/main/java/org/onap/music/eelf/logging/MusicContainerFilter.java b/music-rest/src/main/java/org/onap/music/eelf/logging/MusicContainerFilter.java new file mode 100644 index 00000000..bac02afa --- /dev/null +++ b/music-rest/src/main/java/org/onap/music/eelf/logging/MusicContainerFilter.java @@ -0,0 +1,68 @@ +/* + * ============LICENSE_START========================================== + * org.onap.music + * =================================================================== + * Copyright (c) 2017 AT&T Intellectual Property + * =================================================================== + * 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. + * + * ============LICENSE_END============================================= + * ==================================================================== + */ +package org.onap.music.eelf.logging; + +import java.io.IOException; + +import javax.ws.rs.container.ContainerRequestContext; +import javax.ws.rs.container.ContainerResponseContext; +import javax.ws.rs.container.ContainerResponseFilter; + +import org.springframework.stereotype.Component; + + +/** + * This filter filter/modifies outbound http responses just before sending back to client. + * + * @author sp931a + * + */ +@Component +public class MusicContainerFilter implements ContainerResponseFilter { + + private static EELFLoggerDelegate logger = EELFLoggerDelegate.getLogger(MusicContainerFilter.class); + + public MusicContainerFilter() { + + } + + @Override + public void filter(ContainerRequestContext requestContext, ContainerResponseContext responseContext) + throws IOException { + if (null != EELFLoggerDelegate.mdcGet("transactionId")) { + EELFLoggerDelegate.mdcRemove("transactionId"); + } + + if (null != EELFLoggerDelegate.mdcGet("conversationId")) { + EELFLoggerDelegate.mdcRemove("conversationId"); + } + + if (null != EELFLoggerDelegate.mdcGet("clientId")) { + EELFLoggerDelegate.mdcRemove("clientId"); + } + + if (null != EELFLoggerDelegate.mdcGet("messageId")) { + EELFLoggerDelegate.mdcRemove("messageId"); + } + } + +} diff --git a/music-rest/src/main/java/org/onap/music/eelf/logging/MusicLoggingServletFilter.java b/music-rest/src/main/java/org/onap/music/eelf/logging/MusicLoggingServletFilter.java new file mode 100644 index 00000000..c8c6ba65 --- /dev/null +++ b/music-rest/src/main/java/org/onap/music/eelf/logging/MusicLoggingServletFilter.java @@ -0,0 +1,207 @@ +/* + * ============LICENSE_START========================================== + * org.onap.music + * =================================================================== + * Copyright (c) 2017 AT&T Intellectual Property + * =================================================================== + * Modifications Copyright (C) 2019 IBM + * =================================================================== + * 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. + * + * ============LICENSE_END============================================= + * ==================================================================== + */ +package org.onap.music.eelf.logging; + +import java.io.IOException; +import java.util.Enumeration; +import java.util.HashMap; +import java.util.Map; +import java.util.stream.Collectors; + +import javax.servlet.Filter; +import javax.servlet.FilterChain; +import javax.servlet.FilterConfig; +import javax.servlet.ServletException; +import javax.servlet.ServletRequest; +import javax.servlet.ServletResponse; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +import org.onap.music.authentication.AuthorizationError; +import org.onap.music.main.MusicUtil; + +import com.fasterxml.jackson.databind.ObjectMapper; + +/** + * + * This is the first filter in the chain to be executed before cadi + * authentication. The priority has been set in <code>MusicApplication</code> + * through filter registration bean + * + * The responsibility of this filter is to validate header values as per + * contract and write it to MDC and http response header back. + * + * + * @author sp931a + * + */ + +public class MusicLoggingServletFilter implements Filter { + + private EELFLoggerDelegate logger = EELFLoggerDelegate.getLogger(MusicLoggingServletFilter.class); + // client transaction id, specific to client system, set in properties + public static final String CONVERSATION_ID = MusicUtil.getConversationIdPrefix() + "ConversationId"; + + // can be used as correlation-id in case of callback, also this can be passed to + // other services for tracking. + public static final String MESSAGE_ID = MusicUtil.getMessageIdPrefix() + "MessageId"; + + // client id would be the unique client source-system-id, i;e VALET or CONDUCTOR + // etc + public static final String CLIENT_ID = MusicUtil.getClientIdPrefix() + "ClientId"; + + // unique transaction of the source system + private static final String TRANSACTION_ID = MusicUtil.getTransIdPrefix() + "Transaction-Id"; + + public MusicLoggingServletFilter() throws ServletException { + super(); + } + + @Override + public void init(FilterConfig filterConfig) throws ServletException { + + } + + @Override + public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) + throws IOException, ServletException { + + logger.info(EELFLoggerDelegate.securityLogger, + "In MusicLogginServletFilter doFilter start() :: [\"+MusicUtil.getTransIdRequired()+\",\"+MusicUtil.getConversationIdRequired()+\",\"+MusicUtil.getClientIdRequired()+\",\"+MusicUtil.getMessageIdRequired()"); + + HttpServletRequest httpRequest = null; + HttpServletResponse httpResponse = null; + Map<String, String> headerMap = null; + Map<String, String> upperCaseHeaderMap = null; + + if (null != request && null != response) { + httpRequest = (HttpServletRequest) request; + httpResponse = (HttpServletResponse) response; + + headerMap = getHeadersInfo(httpRequest); + + // The custom header values automatically converted into lower case, not sure + // why ? So i had to covert all keys to upper case + // The response header back to client will have all custom header values as + // upper case. + upperCaseHeaderMap = headerMap.entrySet().stream() + .collect(Collectors.toMap(entry -> entry.getKey().toUpperCase(), entry -> entry.getValue())); + // Enable/disable keys are present in /opt/app/music/etc/music.properties + + if (MusicUtil.getTransIdRequired() + && !upperCaseHeaderMap.containsKey(TRANSACTION_ID.toUpperCase())) { + populateError(httpResponse, "Transaction id '" + TRANSACTION_ID + + "' required on http header"); + return; + } else { + populateMDCAndResponseHeader(upperCaseHeaderMap, TRANSACTION_ID, "transactionId", + MusicUtil.getTransIdRequired(), httpResponse); + } + + if (MusicUtil.getConversationIdRequired() + && !upperCaseHeaderMap.containsKey(CONVERSATION_ID.toUpperCase())) { + populateError(httpResponse, "Conversation Id '" + CONVERSATION_ID + + "' required on http header"); + return; + } else { + populateMDCAndResponseHeader(upperCaseHeaderMap, CONVERSATION_ID, "conversationId", + MusicUtil.getConversationIdRequired(), httpResponse); + } + + if (MusicUtil.getMessageIdRequired() + && !upperCaseHeaderMap.containsKey(MESSAGE_ID.toUpperCase())) { + populateError(httpResponse, "Message Id '" + MESSAGE_ID + + "' required on http header"); + return; + } else { + populateMDCAndResponseHeader(upperCaseHeaderMap, MESSAGE_ID, "messageId", + MusicUtil.getMessageIdRequired(), httpResponse); + } + + if (MusicUtil.getClientIdRequired() + && !upperCaseHeaderMap.containsKey(CLIENT_ID.toUpperCase())) { + populateError(httpResponse, "Client Id '" + CLIENT_ID + + "' required on http header"); + return; + } else { + populateMDCAndResponseHeader(upperCaseHeaderMap, CLIENT_ID, "clientId", + MusicUtil.getClientIdRequired(), httpResponse); + } + + } + + logger.info(EELFLoggerDelegate.securityLogger, + "In MusicLogginServletFilter doFilter. Header values validated sucessfully"); + + chain.doFilter(request, response); + } + + private void populateError(HttpServletResponse httpResponse, String errMsg) throws IOException { + AuthorizationError authError = new AuthorizationError(); + authError.setResponseCode(HttpServletResponse.SC_BAD_REQUEST); + authError.setResponseMessage(errMsg); + + byte[] responseToSend = restResponseBytes(authError); + httpResponse.setHeader("Content-Type", "application/json"); + + // ideally the http response code should be 200, as this is a biz validation + // failure. For now, keeping it consistent with other places. + httpResponse.setStatus(HttpServletResponse.SC_BAD_REQUEST); + httpResponse.getOutputStream().write(responseToSend); + } + + private void populateMDCAndResponseHeader(Map<String, String> headerMap, String idKey, String mdcKey, + boolean isRequired, HttpServletResponse httpResponse) { + + idKey = idKey.trim().toUpperCase(); + + // 1. setting the keys & value in MDC for future use 2.setting the values in + // http response header back to client. + if (isRequired && (headerMap.containsKey(idKey))) { + EELFLoggerDelegate.mdcPut(mdcKey, headerMap.get(idKey)); + httpResponse.addHeader(idKey, headerMap.get(idKey)); + } else { + // do nothing + } + } + + private Map<String, String> getHeadersInfo(HttpServletRequest request) { + + Map<String, String> map = new HashMap<String, String>(); + + Enumeration<String> headerNames = request.getHeaderNames(); + while (headerNames.hasMoreElements()) { + String key = (String) headerNames.nextElement(); + String value = request.getHeader(key); + map.put(key, value); + } + + return map; + } + + private byte[] restResponseBytes(AuthorizationError eErrorResponse) throws IOException { + String serialized = new ObjectMapper().writeValueAsString(eErrorResponse); + return serialized.getBytes(); + } +} diff --git a/music-rest/src/main/java/org/onap/music/exceptions/MusicAuthenticationException.java b/music-rest/src/main/java/org/onap/music/exceptions/MusicAuthenticationException.java new file mode 100644 index 00000000..ab44fd6e --- /dev/null +++ b/music-rest/src/main/java/org/onap/music/exceptions/MusicAuthenticationException.java @@ -0,0 +1,75 @@ +/* + * ============LICENSE_START========================================== + * org.onap.music + * =================================================================== + * Copyright (c) 2019 AT&T Intellectual Property + * =================================================================== + * 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. + * + * ============LICENSE_END============================================= + * ==================================================================== + */ + +package org.onap.music.exceptions; + +/** + * @author inam + * + */ +public class MusicAuthenticationException extends Exception { + + /** + * + */ + public MusicAuthenticationException() { + + } + + /** + * @param message + */ + public MusicAuthenticationException(String message) { + super(message); + + } + + /** + * @param cause + */ + public MusicAuthenticationException(Throwable cause) { + super(cause); + + } + + /** + * @param message + * @param cause + */ + public MusicAuthenticationException(String message, Throwable cause) { + super(message, cause); + + } + + /** + * @param message + * @param cause + * @param enableSuppression + * @param writableStackTrace + */ + public MusicAuthenticationException(String message, Throwable cause, boolean enableSuppression, + boolean writableStackTrace) { + super(message, cause, enableSuppression, writableStackTrace); + + } + +} diff --git a/music-rest/src/main/java/org/onap/music/exceptions/MusicExceptionMapper.java b/music-rest/src/main/java/org/onap/music/exceptions/MusicExceptionMapper.java new file mode 100644 index 00000000..c31fcf73 --- /dev/null +++ b/music-rest/src/main/java/org/onap/music/exceptions/MusicExceptionMapper.java @@ -0,0 +1,56 @@ +/* + * ============LICENSE_START========================================== + * org.onap.music + * =================================================================== + * Copyright (c) 2017 AT&T Intellectual Property + * =================================================================== + * 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. + * + * ============LICENSE_END============================================= + * ==================================================================== + */ + +package org.onap.music.exceptions; + +import java.io.EOFException; +import javax.ws.rs.core.Response; +import javax.ws.rs.ext.ExceptionMapper; +import javax.ws.rs.ext.Provider; + +import org.codehaus.jackson.map.exc.UnrecognizedPropertyException; +import org.onap.music.main.ResultType; +import org.onap.music.response.jsonobjects.JsonResponse; + +@Provider +public class MusicExceptionMapper implements ExceptionMapper<Exception> { + @Override + public Response toResponse(Exception exception) { + if(exception instanceof UnrecognizedPropertyException) { + return Response.status(Response.Status.BAD_REQUEST). + entity(new JsonResponse(ResultType.FAILURE).setError("Unknown field :"+((UnrecognizedPropertyException) exception).getUnrecognizedPropertyName()).toMap()). + build(); + } else if(exception instanceof EOFException) { + return Response.status(Response.Status.BAD_REQUEST). + entity(new JsonResponse(ResultType.FAILURE).setError("Request body cannot be empty").toMap()). + build(); + } else if(exception instanceof NullPointerException) { + return Response.status(Response.Status.BAD_REQUEST). + entity(new JsonResponse(ResultType.FAILURE).setError("NullPointerException - Please check to make sure all inputs are valid.").toMap()). + build(); + } else { + return Response.status(Response.Status.BAD_REQUEST). + entity(new JsonResponse(ResultType.FAILURE).setError(exception.getMessage()).toMap()). + build(); + } + } +} diff --git a/music-rest/src/main/java/org/onap/music/main/PropertiesLoader.java b/music-rest/src/main/java/org/onap/music/main/PropertiesLoader.java new file mode 100644 index 00000000..8aac2672 --- /dev/null +++ b/music-rest/src/main/java/org/onap/music/main/PropertiesLoader.java @@ -0,0 +1,295 @@ +/* + * ============LICENSE_START========================================== + * org.onap.music + * =================================================================== + * Copyright (c) 2017 AT&T Intellectual Property + * =================================================================== + * 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. + * + * ============LICENSE_END============================================= + * ==================================================================== + */ + +package org.onap.music.main; + +import java.util.Properties; + +import org.onap.music.eelf.logging.EELFLoggerDelegate; +import org.springframework.beans.factory.InitializingBean; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.PropertySource; +import org.springframework.context.support.PropertySourcesPlaceholderConfigurer; +import org.springframework.stereotype.Component; + +@PropertySource(value = {"file:/opt/app/music/etc/music.properties", "classpath:/project.properties","file:/opt/app/music/etc/key.properties"}) +@Component +public class PropertiesLoader implements InitializingBean { + + @Value("${cassandra.host}") + public String cassandraHost; +/* + @Value("${music.ip}") + public String musicIp; +*/ + @Value("${debug}") + public String debug; + + @Value("${version}") + public String version; + + @Value("${build}") + public String build; + + @Value("${music.properties}") + public String musicProperties; + + @Value("${lock.lease.period}") + public String lockLeasePeriod; + + @Value("${cassandra.user}") + public String cassandraUser; + + @Value("${cassandra.password}") + public String cassandraPassword; + + @Value("${cassandra.port}") + public String cassandraPort; + + @Value("${cadi}") + public String isCadi; + + @Value("${keyspace.active}") + public String isKeyspaceActive; + + @Value("${retry.count}") + public String rertryCount; + + @Value("${transId.header.prefix}") + private String transIdPrefix; + + @Value("${conversation.header.prefix}") + private String conversationIdPrefix; + + @Value("${clientId.header.prefix}") + private String clientIdPrefix; + + @Value("${messageId.header.prefix}") + private String messageIdPrefix; + + @Value("${transId.header.required}") + private Boolean transIdRequired; + + @Value("${conversation.header.required}") + private Boolean conversationIdRequired; + + @Value("${clientId.header.required}") + private Boolean clientIdRequired; + + @Value("${messageId.header.required}") + private Boolean messageIdRequired; + + @Value("${music.aaf.ns}") + private String musicAafNs; + + @Value("${cipher.enc.key}") + private String cipherEncKey; + + private static EELFLoggerDelegate logger = EELFLoggerDelegate.getLogger(PropertiesLoader.class); + + @Bean + public static PropertySourcesPlaceholderConfigurer propertyConfigInDev() { + //return new PropertySourcesPlaceholderConfigurer(); + PropertySourcesPlaceholderConfigurer pspc = new PropertySourcesPlaceholderConfigurer(); + pspc.setIgnoreResourceNotFound(true); + pspc.setIgnoreUnresolvablePlaceholders(true); + return pspc; + } + + /** + * . + */ + public void loadProperties() { + if(cipherEncKey != null) { + MusicUtil.setCipherEncKey(cipherEncKey); + } + if (musicAafNs != null) { + MusicUtil.setMusicAafNs(musicAafNs); + } + if (cassandraPort != null && !cassandraPort.equals("${cassandra.port}")) { + MusicUtil.setCassandraPort(Integer.parseInt(cassandraPort)); + } + if (cassandraUser != null && !cassandraUser.equals("${cassandra.user}")) { + MusicUtil.setCassName(cassandraUser); + } + if (cassandraPassword != null && !cassandraPassword.equals("${cassandra.password}")) { + MusicUtil.setCassPwd(cassandraPassword); + } + if (debug != null && !debug.equals("${debug}")) { + MusicUtil.setDebug(Boolean.parseBoolean(debug)); + } + if (lockLeasePeriod != null && !lockLeasePeriod.equals("${lock.lease.period}")) { + MusicUtil.setDefaultLockLeasePeriod(Long.parseLong(lockLeasePeriod)); + } + if (musicProperties != null && !musicProperties.equals("${music.properties}")) { + MusicUtil.setMusicPropertiesFilePath(musicProperties); + } + if (cassandraHost != null && !cassandraHost.equals("${cassandra.host}")) { + MusicUtil.setMyCassaHost(cassandraHost); + } + if (version != null && !version.equals("${version}")) { + MusicUtil.setVersion(version); + } + if (build != null && !version.equals("${build}")) { + MusicUtil.setBuild(build); + } + if (isCadi != null && !isCadi.equals("${cadi}")) { + MusicUtil.setIsCadi(Boolean.parseBoolean(isCadi)); + } + if (rertryCount != null && !rertryCount.equals("${retry.count}")) { + MusicUtil.setRetryCount(Integer.parseInt(rertryCount)); + } + if (isKeyspaceActive != null && !isKeyspaceActive.equals("${keyspace.active}")) { + MusicUtil.setKeyspaceActive(Boolean.parseBoolean(isKeyspaceActive)); + } + if(transIdPrefix!=null) { + MusicUtil.setTransIdPrefix(transIdPrefix); + } + + if(conversationIdPrefix!=null) { + MusicUtil.setConversationIdPrefix(conversationIdPrefix); + } + + if(clientIdPrefix!=null) { + MusicUtil.setClientIdPrefix(clientIdPrefix); + } + + if(messageIdPrefix!=null) { + MusicUtil.setMessageIdPrefix(messageIdPrefix); + } + + if(transIdRequired!=null) { + MusicUtil.setTransIdRequired(transIdRequired); + } + + if(conversationIdRequired!=null) { + MusicUtil.setConversationIdRequired(conversationIdRequired); + } + + if(clientIdRequired!=null) { + MusicUtil.setClientIdRequired(clientIdRequired); + } + + if(messageIdRequired!=null) { + MusicUtil.setMessageIdRequired(messageIdRequired); + } + } + + public static void loadProperties(Properties properties) { + if (properties.getProperty("cassandra.host")!=null) { + MusicUtil.setMyCassaHost(properties.getProperty("cassandra.host")); + } + + if (properties.getProperty("cassandra.port")!=null) { + MusicUtil.setCassandraPort(Integer.parseInt(properties.getProperty("cassandra.port"))); + } + + if (properties.getProperty("cassandra.user")!=null) { + MusicUtil.setCassName(properties.getProperty("cassandra.user")); + } + + if (properties.getProperty("cassandra.password")!=null) { + MusicUtil.setCassPwd(properties.getProperty("cassandra.password")); + } + + if (properties.getProperty("music.properties")!=null) { + MusicUtil.setMusicPropertiesFilePath(properties.getProperty("music.properties")); + } + + if (properties.getProperty("debug")!=null) { + MusicUtil.setDebug(Boolean.parseBoolean(properties.getProperty("debug"))); + } + + if (properties.getProperty("version")!=null) { + MusicUtil.setVersion(properties.getProperty("version")); + } + + if (properties.getProperty("build")!=null) { + MusicUtil.setBuild(properties.getProperty("build")); + } + + if (properties.getProperty("lock.lease.period")!=null) { + MusicUtil.setDefaultLockLeasePeriod(Long.parseLong(properties.getProperty("lock.lease.period"))); + } + + if (properties.getProperty("cadi")!=null) { + MusicUtil.setIsCadi(Boolean.parseBoolean(properties.getProperty("cadi"))); + } + + if (properties.getProperty("keyspace.active")!=null) { + MusicUtil.setKeyspaceActive(Boolean.parseBoolean(properties.getProperty("keyspace.active"))); + } + + if (properties.getProperty("retry.count")!=null) { + MusicUtil.setRetryCount(Integer.parseInt(properties.getProperty("retry.count"))); + } + + if (properties.getProperty("transId.header.prefix")!=null) { + MusicUtil.setTransIdPrefix(properties.getProperty("transId.header.prefix")); + } + + if (properties.getProperty("conversation.header.prefix")!=null) { + MusicUtil.setConversationIdPrefix(properties.getProperty("conversation.header.prefix")); + } + + if (properties.getProperty("clientId.header.prefix")!=null) { + MusicUtil.setClientIdPrefix(properties.getProperty("clientId.header.prefix")); + } + + if (properties.getProperty("messageId.header.prefix")!=null) { + MusicUtil.setMessageIdPrefix(properties.getProperty("messageId.header.prefix")); + } + + if (properties.getProperty("transId.header.required")!=null) { + MusicUtil.setTransIdRequired(Boolean.parseBoolean(properties.getProperty("transId.header.required"))); + } + + if (properties.getProperty("conversation.header.required")!=null) { + MusicUtil.setConversationIdRequired(Boolean.parseBoolean(properties.getProperty("conversation.header.required"))); + } + + if (properties.getProperty("clientId.header.required")!=null) { + MusicUtil.setClientIdRequired(Boolean.parseBoolean(properties.getProperty("clientId.header.required"))); + } + + if (properties.getProperty("messageId.header.required")!=null) { + MusicUtil.setMessageIdRequired(Boolean.parseBoolean(properties.getProperty("messageId.header.required"))); + } + + if (properties.getProperty("music.aaf.ns")!=null) { + MusicUtil.setMusicAafNs(properties.getProperty("music.aaf.ns")); + } + + if (properties.getProperty("cipher.enc.key")!=null) { + MusicUtil.setCipherEncKey(properties.getProperty("cipher.enc.key")); + } + + } + + @Override + public void afterPropertiesSet() throws Exception { + // TODO Auto-generated method stub + + } + +} diff --git a/music-rest/src/main/java/org/onap/music/response/jsonobjects/JsonResponse.java b/music-rest/src/main/java/org/onap/music/response/jsonobjects/JsonResponse.java new file mode 100644 index 00000000..5ae49f5d --- /dev/null +++ b/music-rest/src/main/java/org/onap/music/response/jsonobjects/JsonResponse.java @@ -0,0 +1,322 @@ +/* + * ============LICENSE_START========================================== + * org.onap.music + * =================================================================== + * Copyright (c) 2017 AT&T Intellectual Property + * + * Modifications Copyright (C) 2019 IBM. + * =================================================================== + * 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. + * + * ============LICENSE_END============================================= + * ==================================================================== + */ + +package org.onap.music.response.jsonobjects; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import org.onap.music.lockingservice.cassandra.MusicLockState.LockStatus; +import org.onap.music.main.ResultType; + + +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; + +@ApiModel(value = "JsonResponse", description = "General Response JSON") +public class JsonResponse { + + /* Status is required */ + private ResultType status; + + /* Standard informational fields */ + private String error; + private String message; + + /* versioning */ + private String musicVersion; + private String musicBuild; + + /* Data Fields */ + private Map<String, HashMap<String, Object>> dataResult; + + /* Locking fields */ + private String lock; + private LockStatus lockStatus; + private List<String> lockHolders; + private String lockLease; + private boolean isLockHolders=false; + + /** + * Create a JSONLock Response + * Use setters to provide more information as in + * JsonLockResponse(ResultType.SUCCESS).setMessage("We did it").setLock(mylockname) + * @param status + */ + public JsonResponse(ResultType status) { + this.status = status; + } + + public boolean isLockHolders() { + return isLockHolders; + } + + public JsonResponse setisLockHolders(boolean isLockHolders) { + this.isLockHolders = isLockHolders; + return this; + } + + /** + * + * @return + */ + @ApiModelProperty(value = "Overall status of the response.", + allowableValues = "Success,Failure") + public ResultType getStatus() { + return status; + } + + /** + * + * @param status + */ + public JsonResponse setStatus(ResultType status) { + this.status = status; + return this; + } + + /** + * + * @return the error + */ + @ApiModelProperty(value = "Error value") + public String getError() { + return error; + } + + /** + * + * @param error + */ + public JsonResponse setError(String error) { + this.error = error; + return this; + } + + /** + * + * @return the message + */ + @ApiModelProperty(value = "Message value") + public String getMessage() { + return message; + } + + /** + * + * @param message + */ + public JsonResponse setMessage(String message) { + this.message = message; + return this; + } + + + /** + * . + * @return the music version + */ + public String getMusicVersion() { + return this.musicVersion; + } + + /** + * . + * @param version of music + * @return + */ + public JsonResponse setMusicVersion(String version) { + this.musicVersion = version; + return this; + } + + /** + * . + * @return the music version + */ + public String getMusicBuild() { + return this.musicBuild; + } + + /** + * . + * @param build of music + * @return + */ + public JsonResponse setMusicBuild(String build) { + this.musicBuild = build; + return this; + } + + + public Map<String, HashMap<String, Object>> getDataResult() { + return this.dataResult; + } + + public JsonResponse setDataResult(Map<String, HashMap<String, Object>> map) { + this.dataResult = map; + return this; + } + + /** + * + * @return + */ + public String getLock() { + return lock; + } + + /** + * + * @param lock + */ + public JsonResponse setLock(String lock) { + this.lock = lock; + return this; + } + + /** + * + * @return the lockStatus + */ + @ApiModelProperty(value = "Status of the lock") + public LockStatus getLockStatus() { + return lockStatus; + } + + /** + * + * @param lockStatus + */ + public JsonResponse setLockStatus(LockStatus lockStatus) { + this.lockStatus = lockStatus; + return this; + } + + /** + * + * + * @return the lockHolder + */ + @ApiModelProperty(value = "Holder of the Lock") + public List<String> getLockHolder() { + return lockHolders; + } + + /** + * + * @param lockHolder + */ + public JsonResponse setLockHolder(String lockHolder) { + this.lockHolders = new ArrayList<String>(); + this.lockHolders.add(lockHolder); + return this; + } + + public JsonResponse setLockHolder(List<String> lockHolders) { + this.lockHolders = lockHolders; + return this; + } + + + /** + * @return the lockLease + */ + public String getLockLease() { + return lockLease; + } + + /** + * @param lockLease the lockLease to set + */ + public JsonResponse setLockLease(String lockLease) { + this.lockLease = lockLease; + return this; + } + + /** + * Convert to Map + * + * @return + */ + public Map<String, Object> toMap() { + Map<String, Object> fullMap = new HashMap<>(); + fullMap.put("status", status); + if (error != null && !"".equals(error)) { + fullMap.put("error", error); + } + if (message != null) { + fullMap.put("message", message); + } + + if (musicVersion != null) { + fullMap.put("version", musicVersion); + } + + if (musicBuild != null) { + fullMap.put("build", musicBuild); + } + + if (dataResult != null) { + fullMap.put("result", dataResult); + } + + if (lock != null) { + Map<String, Object> lockMap = new HashMap<>(); + if (lock != null) { + lockMap.put("lock", lock); + } + if (lockStatus != null) { + lockMap.put("lock-status", lockStatus); + } + if (lockHolders != null && !lockHolders.isEmpty()) { + if (lockHolders.size()==1 && !isLockHolders) { + //for backwards compatability + lockMap.put("lock-holder", lockHolders.get(0)); + } else { + lockMap.put("lock-holder", lockHolders); + } + } + if (lockLease != null) { + lockMap.put("lock-lease", lockLease); + } + fullMap.put("lock", lockMap); + } + + return fullMap; + } + + /** + * Convert to String + */ + @Override + public String toString() { + return "JsonLockResponse [status=" + status + ", error=" + error + ", message=" + message + + ", lock=" + lock + ", lockStatus=" + lockStatus + ", lockHolder=" + + lockHolders + "]"; + } + +} diff --git a/music-rest/src/main/java/org/onap/music/rest/Application.java b/music-rest/src/main/java/org/onap/music/rest/Application.java new file mode 100644 index 00000000..5375155b --- /dev/null +++ b/music-rest/src/main/java/org/onap/music/rest/Application.java @@ -0,0 +1,79 @@ +/* + * ============LICENSE_START========================================== + * org.onap.music + * =================================================================== + * Copyright (c) 2017 AT&T Intellectual Property + * =================================================================== + * 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. + * + * ============LICENSE_END============================================= + * ==================================================================== + */ + +package org.onap.music.rest; + +public class Application { + + private String application_name; + private String username; + private String password; + private String keyspace_name; + private boolean is_aaf; + private String uuid; + private boolean is_api; + + public String getApplication_name() { + return application_name; + } + public void setApplication_name(String application_name) { + this.application_name = application_name; + } + public String getUsername() { + return username; + } + public void setUsername(String username) { + this.username = username; + } + public String getPassword() { + return password; + } + public void setPassword(String password) { + this.password = password; + } + public String getKeyspace_name() { + return keyspace_name; + } + public void setKeyspace_name(String keyspace_name) { + this.keyspace_name = keyspace_name; + } + public boolean isIs_aaf() { + return is_aaf; + } + public void setIs_aaf(boolean is_aaf) { + this.is_aaf = is_aaf; + } + public String getUuid() { + return uuid; + } + public void setUuid(String uuid) { + this.uuid = uuid; + } + public boolean getIs_api() { + return is_api; + } + public void setIs_api(boolean is_api) { + this.is_api = is_api; + } + + +} diff --git a/music-rest/src/main/java/org/onap/music/rest/RestMusicDataAPI.java b/music-rest/src/main/java/org/onap/music/rest/RestMusicDataAPI.java new file mode 100755 index 00000000..756856d0 --- /dev/null +++ b/music-rest/src/main/java/org/onap/music/rest/RestMusicDataAPI.java @@ -0,0 +1,1052 @@ +/* + * ============LICENSE_START========================================== + * org.onap.music + * =================================================================== + * Copyright (c) 2017 AT&T Intellectual Property + * =================================================================== + * Modifications Copyright (c) 2019 Samsung + * =================================================================== + * Modifications Copyright (C) 2019 IBM + * =================================================================== + * 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. + * + * ============LICENSE_END============================================= + * ==================================================================== + */ + +package org.onap.music.rest; + +import java.util.List; +import java.util.Map; +import java.util.UUID; + +import javax.ws.rs.Consumes; +import javax.ws.rs.DELETE; +import javax.ws.rs.GET; +import javax.ws.rs.HeaderParam; +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.Context; +import javax.ws.rs.core.MediaType; +import javax.ws.rs.core.MultivaluedMap; +import javax.ws.rs.core.Response; +import javax.ws.rs.core.Response.ResponseBuilder; +import javax.ws.rs.core.Response.Status; +import javax.ws.rs.core.UriInfo; + +import org.onap.music.datastore.MusicDataStoreHandle; +import org.onap.music.datastore.PreparedQueryObject; +import org.onap.music.datastore.jsonobjects.JsonDelete; +import org.onap.music.datastore.jsonobjects.JsonIndex; +import org.onap.music.datastore.jsonobjects.JsonInsert; +import org.onap.music.datastore.jsonobjects.JsonKeySpace; +import org.onap.music.datastore.jsonobjects.JsonSelect; +import org.onap.music.datastore.jsonobjects.JsonTable; +import org.onap.music.datastore.jsonobjects.JsonUpdate; +import org.onap.music.eelf.logging.EELFLoggerDelegate; +import org.onap.music.eelf.logging.format.AppMessages; +import org.onap.music.eelf.logging.format.ErrorSeverity; +import org.onap.music.eelf.logging.format.ErrorTypes; +import org.onap.music.exceptions.MusicLockingException; +import org.onap.music.exceptions.MusicQueryException; +import org.onap.music.exceptions.MusicServiceException; +import org.onap.music.main.MusicCore; +import org.onap.music.main.MusicUtil; +import org.onap.music.main.ResultType; +import org.onap.music.main.ReturnType; +import org.onap.music.response.jsonobjects.JsonResponse; + +import com.datastax.driver.core.DataType; +import com.datastax.driver.core.ResultSet; +import com.datastax.driver.core.TableMetadata; + +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiOperation; +import io.swagger.annotations.ApiParam; +import io.swagger.annotations.ApiResponse; +import io.swagger.annotations.ApiResponses; +import io.swagger.annotations.Example; +import io.swagger.annotations.ExampleProperty; + +/* Version 2 Class */ +//@Path("/v{version: [0-9]+}/keyspaces") +@Path("/v2/keyspaces") +@Api(value = "Data Api") +public class RestMusicDataAPI { + /* + * Header values for Versioning X-minorVersion *** - Used to request or communicate a MINOR + * version back from the client to the server, and from the server back to the client - This + * will be the MINOR version requested by the client, or the MINOR version of the last MAJOR + * version (if not specified by the client on the request) - Contains a single position value + * (e.g. if the full version is 1.24.5, X-minorVersion = "24") - Is optional for the client on + * request; however, this header should be provided if the client needs to take advantage of + * MINOR incremented version functionality - Is mandatory for the server on response + * + *** X-patchVersion *** - Used only to communicate a PATCH version in a response for + * troubleshooting purposes only, and will not be provided by the client on request - This will + * be the latest PATCH version of the MINOR requested by the client, or the latest PATCH version + * of the MAJOR (if not specified by the client on the request) - Contains a single position + * value (e.g. if the full version is 1.24.5, X-patchVersion = "5") - Is mandatory for the + * server on response (CURRENTLY NOT USED) + * + *** X-latestVersion *** - Used only to communicate an API's latest version - Is mandatory for the + * server on response, and shall include the entire version of the API (e.g. if the full version + * is 1.24.5, X-latestVersion = "1.24.5") - Used in the response to inform clients that they are + * not using the latest version of the API (CURRENTLY NOT USED) + * + */ + + private EELFLoggerDelegate logger = EELFLoggerDelegate.getLogger(RestMusicDataAPI.class); + private static final String XMINORVERSION = "X-minorVersion"; + private static final String XPATCHVERSION = "X-patchVersion"; + private static final String NS = "ns"; + private static final String VERSION = "v2"; + private static final String PARAMETER_ERROR = "Missing Row Identifier. Please provide the parameter of key=value for the row being selected."; + + + private class RowIdentifier { + public String primarKeyValue; + public StringBuilder rowIdString; + @SuppressWarnings("unused") + public PreparedQueryObject queryObject; // the string with all the row + // identifiers separated by AND + + public RowIdentifier(String primaryKeyValue, StringBuilder rowIdString, + PreparedQueryObject queryObject) { + this.primarKeyValue = primaryKeyValue; + this.rowIdString = rowIdString; + this.queryObject = queryObject; + } + } + + + /** + * Create Keyspace REST + * + * @param kspObject + * @param keyspaceName + * @return + * @throws Exception + */ + @POST + @Path("/{name}") + @ApiOperation(value = "Create Keyspace", response = String.class, + notes = "This API will not work if MUSIC properties has keyspace.active=false ") + @Consumes(MediaType.APPLICATION_JSON) + @Produces(MediaType.APPLICATION_JSON) + @ApiResponses(value={ + @ApiResponse(code=200, message = "Success",examples = @Example( value = { + @ExampleProperty(mediaType="application/json",value = + "{\"message\" : \"Keysapce <keyspace> Created\"," + + "\"status\" : \"SUCCESS\"}") + })), + @ApiResponse(code=400, message = "Failure",examples = @Example( value = { + @ExampleProperty(mediaType="application/json",value = + "{\"error\" : \"<errorMessage>\"," + + "\"status\" : \"FAILURE\"}") + })) + }) + public Response createKeySpace( + @ApiParam(value = "Major Version",required = true) @PathParam("version") String version, + @ApiParam(value = "Minor Version",required = false) @HeaderParam(XMINORVERSION) String minorVersion, + @ApiParam(value = "Patch Version",required = false) @HeaderParam(XPATCHVERSION) String patchVersion, + @ApiParam(value = "AID", required = false, hidden = true) @HeaderParam("aid") String aid, + @ApiParam(value = "Authorization", required = true) @HeaderParam(MusicUtil.AUTHORIZATION) String authorization, + @ApiParam(value = "Application namespace",required = false, hidden = true) @HeaderParam(NS) String ns, + JsonKeySpace kspObject, + @ApiParam(value = "Keyspace Name",required = true) @PathParam("name") String keyspaceName) { + try { + ResponseBuilder response = MusicUtil.buildVersionResponse(VERSION, minorVersion, patchVersion); + EELFLoggerDelegate.mdcPut("keyspace", "( "+keyspaceName+" ) "); + logger.info(EELFLoggerDelegate.applicationLogger,"In Create Keyspace " + keyspaceName); + if (MusicUtil.isKeyspaceActive() ) { + logger.info(EELFLoggerDelegate.applicationLogger,"Creating Keyspace " + keyspaceName); + + if(kspObject == null || kspObject.getReplicationInfo() == null) { + response.status(Status.BAD_REQUEST); + return response.entity(new JsonResponse(ResultType.FAILURE).setError(ResultType.BODYMISSING.getResult()).toMap()).build(); + } + ResultType result = ResultType.FAILURE; + try { + kspObject.setKeyspaceName(keyspaceName); + result = MusicCore.createKeyspace(kspObject, MusicUtil.EVENTUAL); + logger.info(EELFLoggerDelegate.applicationLogger, "result = " + result); + } catch ( MusicQueryException ex) { + logger.error(EELFLoggerDelegate.errorLogger,ex.getMessage(), AppMessages.QUERYERROR + ,ErrorSeverity.WARN, ErrorTypes.QUERYERROR); + return response.status(Status.BAD_REQUEST).entity(new JsonResponse(ResultType.FAILURE).setError(ex.getMessage()).toMap()).build(); + } catch ( MusicServiceException ex) { + logger.error(EELFLoggerDelegate.errorLogger,ex.getMessage(), AppMessages.UNKNOWNERROR ,ErrorSeverity + .WARN, ErrorTypes.MUSICSERVICEERROR, ex); + return response.status(Status.BAD_REQUEST).entity(new JsonResponse(ResultType.FAILURE).setError(ex.getMessage()).toMap()).build(); + } + + return response.status(Status.OK).entity(new JsonResponse(ResultType.SUCCESS).setMessage("Keyspace " + keyspaceName + " Created").toMap()).build(); + } else { + String vError = "Keyspace Creation has been turned off. Contact DBA to create the keyspace or set keyspace.active to true."; + logger.info(EELFLoggerDelegate.applicationLogger,vError); + logger.error(EELFLoggerDelegate.errorLogger,vError, AppMessages.UNKNOWNERROR,ErrorSeverity.WARN, ErrorTypes.MUSICSERVICEERROR); + return response.status(Response.Status.BAD_REQUEST).entity(new JsonResponse(ResultType.FAILURE).setError(vError).toMap()).build(); + } + } finally { + EELFLoggerDelegate.mdcRemove("keyspace"); + } + + } + + /** + * + * @param kspObject + * @param keyspaceName + * @return + * @throws Exception + */ + @DELETE + @Path("/{name}") + @ApiOperation(value = "Delete Keyspace", response = String.class, + notes = "This API will not work if MUSIC properties has keyspace.active=false ") + @Produces(MediaType.APPLICATION_JSON) + @ApiResponses(value={ + @ApiResponse(code=200, message = "Success",examples = @Example( value = { + @ExampleProperty(mediaType="application/json",value = + "{\"message\" : \"Keysapce <keyspace> Deleted\"," + + "\"status\" : \"SUCCESS\"}") + })), + @ApiResponse(code=400, message = "Failure",examples = @Example( value = { + @ExampleProperty(mediaType="application/json",value = + "{\"error\" : \"<errorMessage>\"," + + "\"status\" : \"FAILURE\"}") + })) + }) + public Response dropKeySpace( + @ApiParam(value = "Major Version",required = true) @PathParam("version") String version, + @ApiParam(value = "Minor Version",required = false) @HeaderParam(XMINORVERSION) String minorVersion, + @ApiParam(value = "Patch Version",required = false) @HeaderParam(XPATCHVERSION) String patchVersion, + @ApiParam(value = "AID", required = false, hidden = true) @HeaderParam("aid") String aid, + @ApiParam(value = "Authorization", required = true) @HeaderParam(MusicUtil.AUTHORIZATION) String authorization, + @ApiParam(value = "Application namespace",required = false, hidden = true) @HeaderParam(NS) String ns, + @ApiParam(value = "Keyspace Name",required = true) @PathParam("name") String keyspaceName) throws Exception { + try { + ResponseBuilder response = MusicUtil.buildVersionResponse(VERSION, minorVersion, patchVersion); + EELFLoggerDelegate.mdcPut("keyspace", "( " + keyspaceName + " ) "); + logger.info(EELFLoggerDelegate.applicationLogger,"In Drop Keyspace " + keyspaceName); + if (MusicUtil.isKeyspaceActive()) { + String consistency = MusicUtil.EVENTUAL;// for now this needs only + String droperror = "Error Deleteing Keyspace " + keyspaceName; + JsonKeySpace kspObject = new JsonKeySpace(); + kspObject.setKeyspaceName(keyspaceName); + try{ + ResultType result = MusicCore.dropKeyspace(kspObject, consistency); + if ( result.equals(ResultType.FAILURE) ) { + return response.status(Status.BAD_REQUEST).entity(new JsonResponse(result).setError(droperror).toMap()).build(); + } + } catch ( MusicQueryException ex) { + logger.error(EELFLoggerDelegate.errorLogger,ex.getMessage(), AppMessages.QUERYERROR + ,ErrorSeverity.WARN, ErrorTypes.QUERYERROR); + return response.status(Status.BAD_REQUEST).entity(new JsonResponse(ResultType.FAILURE).setError(droperror + " " + ex.getMessage()).toMap()).build(); + } catch ( MusicServiceException ex) { + logger.error(EELFLoggerDelegate.errorLogger,ex.getMessage(), AppMessages.UNKNOWNERROR + ,ErrorSeverity.WARN, ErrorTypes.MUSICSERVICEERROR, ex); + return response.status(Status.BAD_REQUEST).entity(new JsonResponse(ResultType.FAILURE).setError(droperror + " " + ex.getMessage()).toMap()).build(); + } + return response.status(Status.OK).entity(new JsonResponse(ResultType.SUCCESS).setMessage("Keyspace " + keyspaceName + " Deleted").toMap()).build(); + } else { + String vError = "Keyspace deletion has been turned off. Contact DBA to delete the keyspace or set keyspace.active to true."; + logger.info(EELFLoggerDelegate.applicationLogger,vError); + logger.error(EELFLoggerDelegate.errorLogger,vError, AppMessages.UNKNOWNERROR,ErrorSeverity.WARN, ErrorTypes.MUSICSERVICEERROR); + return response.status(Response.Status.BAD_REQUEST).entity(new JsonResponse(ResultType.FAILURE).setError(vError).toMap()).build(); + } + } finally { + EELFLoggerDelegate.mdcRemove("keyspace"); + } + } + + /** + * + * @param tableObj + * @param version + * @param keyspace + * @param tablename + * @param headers + * @return + * @throws Exception - + */ + @POST + @Path("/{keyspace: .*}/tables/{tablename: .*}") + @ApiOperation(value = "Create Table", response = String.class, + notes = "Create a table with the required json in the body.") + @Consumes(MediaType.APPLICATION_JSON) + @Produces(MediaType.APPLICATION_JSON) + @ApiResponses(value={ + @ApiResponse(code=200, message = "Success",examples = @Example( value = { + @ExampleProperty(mediaType="application/json",value = + "{\"message\" : \"Tablename <tablename> Created under keyspace <keyspace>\"," + + "\"status\" : \"SUCCESS\"}") + })), + @ApiResponse(code=400, message = "Failure",examples = @Example( value = { + @ExampleProperty(mediaType="application/json",value = + "{\"error\" : \"<errorMessage>\"," + + "\"status\" : \"FAILURE\"}") + })) + }) + public Response createTable( + @ApiParam(value = "Major Version",required = true) @PathParam("version") String version, + @ApiParam(value = "Minor Version",required = false) @HeaderParam(XMINORVERSION) String minorVersion, + @ApiParam(value = "Patch Version",required = false) @HeaderParam(XPATCHVERSION) String patchVersion, + @ApiParam(value = "AID", required = false, hidden = true) @HeaderParam("aid") String aid, + @ApiParam(value = "Application namespace",required = false, hidden = true) @HeaderParam(NS) String ns, + @ApiParam(value = "Authorization", required = true) @HeaderParam(MusicUtil.AUTHORIZATION) String authorization, + JsonTable tableObj, + @ApiParam(value = "Keyspace Name",required = true) @PathParam("keyspace") String keyspace, + @ApiParam(value = "Table Name",required = true) @PathParam("tablename") String tablename) throws Exception { + try { + ResponseBuilder response = MusicUtil.buildVersionResponse(VERSION, minorVersion, patchVersion); + if ( null == tableObj ) { + return response.status(Status.BAD_REQUEST).entity(new JsonResponse(ResultType.FAILURE) + .setError(ResultType.BODYMISSING.getResult()).toMap()).build(); + } + if(keyspace == null || keyspace.isEmpty() || tablename == null || tablename.isEmpty()){ + return response.status(Status.BAD_REQUEST).entity(new JsonResponse(ResultType.FAILURE) + .setError("One or more path parameters are not set, please check and try again." + + "Parameter values: keyspace='" + keyspace + "' tablename='" + tablename + "'") + .toMap()).build(); + } + EELFLoggerDelegate.mdcPut("keyspace", "( "+keyspace+" ) "); + String consistency = MusicUtil.EVENTUAL; + // for now this needs only eventual consistency + ResultType result = ResultType.FAILURE; + try { + tableObj.setKeyspaceName(keyspace); + tableObj.setTableName(tablename); + result = MusicCore.createTable(tableObj, consistency); + } catch (MusicQueryException ex) { + logger.error(EELFLoggerDelegate.errorLogger,ex.getMessage(), ex.getMessage() ,ErrorSeverity + .WARN, ErrorTypes.MUSICSERVICEERROR, ex); + return response.status(Status.BAD_REQUEST).entity(new JsonResponse(ResultType.FAILURE).setError(ex.getMessage()).toMap()).build(); + } catch (MusicServiceException ex) { + logger.error(EELFLoggerDelegate.errorLogger,ex.getMessage(), AppMessages.UNKNOWNERROR ,ErrorSeverity.CRITICAL, ErrorTypes.MUSICSERVICEERROR); + response.status(Status.BAD_REQUEST); + return response.status(Status.BAD_REQUEST).entity(new JsonResponse(ResultType.FAILURE).setError(ex.getMessage()).toMap()).build(); + } + if ( result.equals(ResultType.FAILURE) ) { + return response.status(Status.BAD_REQUEST).entity(new JsonResponse(result).setError("Error Creating Table " + tablename).toMap()).build(); + } + return response.status(Status.OK).entity(new JsonResponse(ResultType.SUCCESS).setMessage("TableName " + tablename.trim() + " Created under keyspace " + keyspace.trim()).toMap()).build(); + } finally { + EELFLoggerDelegate.mdcRemove("keyspace"); + } + } + + /** + * + * @param keyspace + * @param tablename + * @param fieldName + * @param info + * @throws Exception + */ + @POST + @Path("/{keyspace: .*}/tables/{tablename: .*}/index/{field: .*}") + @ApiOperation(value = "Create Index", response = String.class, + notes = "An index provides a means to access data using attributes " + + "other than the partition key. The benefit is fast, efficient lookup " + + "of data matching a given condition.") + @Produces(MediaType.APPLICATION_JSON) + @ApiResponses(value={ + @ApiResponse(code=200, message = "Success",examples = @Example( value = { + @ExampleProperty(mediaType="application/json",value = + "{\"message\" : \"Index Created on <keyspace>.<table>.<field>\"," + + "\"status\" : \"SUCCESS\"}") + })), + @ApiResponse(code=400, message = "Failure",examples = @Example( value = { + @ExampleProperty(mediaType="application/json",value = + "{\"error\" : \"<errorMessage>\"," + + "\"status\" : \"FAILURE\"}") + })), + @ApiResponse(code=400, message = "Failure",examples = @Example( value = { + @ExampleProperty(mediaType="application/json",value = + "{\"error\" : \"Unknown Error in create index.\"," + + "\"status\" : \"FAILURE\"}") + })) + }) + public Response createIndex( + @ApiParam(value = "Major Version",required = true) @PathParam("version") String version, + @ApiParam(value = "Minor Version",required = false) @HeaderParam(XMINORVERSION) String minorVersion, + @ApiParam(value = "Patch Version",required = false) @HeaderParam(XPATCHVERSION) String patchVersion, + @ApiParam(value = "AID", required = false, hidden = true) @HeaderParam("aid") String aid, + @ApiParam(value = "Application namespace",required = false, hidden = true) @HeaderParam(NS) String ns, + @ApiParam(value = "Authorization", required = true) @HeaderParam(MusicUtil.AUTHORIZATION) String authorization, + @ApiParam(value = "Keyspace Name",required = true) @PathParam("keyspace") String keyspace, + @ApiParam(value = "Table Name",required = true) @PathParam("tablename") String tablename, + @ApiParam(value = "Field Name",required = true) @PathParam("field") String fieldName, + @Context UriInfo info) throws Exception { + try { + ResponseBuilder response = MusicUtil.buildVersionResponse(VERSION, minorVersion, patchVersion); + if ((keyspace == null || keyspace.isEmpty()) || (tablename == null || tablename.isEmpty()) || (fieldName == null || fieldName.isEmpty())){ + return response.status(Status.BAD_REQUEST).entity(new JsonResponse(ResultType.FAILURE) + .setError("one or more path parameters are not set, please check and try again") + .toMap()).build(); + } + EELFLoggerDelegate.mdcPut("keyspace", "( "+keyspace+" ) "); + MultivaluedMap<String, String> rowParams = info.getQueryParameters(); + String indexName = ""; + if (rowParams.getFirst("index_name") != null) + indexName = rowParams.getFirst("index_name"); + + JsonIndex jsonIndexObject = new JsonIndex(indexName, keyspace, tablename, fieldName); + + ResultType result = ResultType.FAILURE; + try { + result = MusicCore.createIndex(jsonIndexObject, MusicUtil.EVENTUAL); + } catch (MusicServiceException ex) { + logger.error(EELFLoggerDelegate.errorLogger,ex.getMessage(), AppMessages.UNKNOWNERROR ,ErrorSeverity + .CRITICAL, ErrorTypes.GENERALSERVICEERROR, ex); + response.status(Status.BAD_REQUEST); + return response.entity(new JsonResponse(ResultType.FAILURE).setError(ex.getMessage()).toMap()).build(); + } + if ( result.equals(ResultType.SUCCESS) ) { + return response.status(Status.OK).entity(new JsonResponse(result).setMessage("Index Created on " + keyspace+"."+tablename+"."+fieldName).toMap()).build(); + } else { + return response.status(Status.BAD_REQUEST).entity(new JsonResponse(result).setError("Unknown Error in create index.").toMap()).build(); + } + } finally { + EELFLoggerDelegate.mdcRemove("keyspace"); + } + } + + /** + * + * @param insObj + * @param keyspace + * @param tablename + * @return + * @throws Exception + */ + @POST + @Path("/{keyspace: .*}/tables/{tablename: .*}/rows") + @ApiOperation(value = "Insert Into Table", response = String.class, + notes = "Insert into table with data in json body.") + @Consumes(MediaType.APPLICATION_JSON) + @Produces(MediaType.APPLICATION_JSON) + @ApiResponses(value={ + @ApiResponse(code=200, message = "Success",examples = @Example( value = { + @ExampleProperty(mediaType="application/json",value = + "{\"message\" : \"Insert Successful\"," + + "\"status\" : \"SUCCESS\"}") + })), + @ApiResponse(code=400, message = "Failure - Generic",examples = @Example( value = { + @ExampleProperty(mediaType="application/json",value = + "{\"error\" : \"<errorMessage>\"," + + "\"status\" : \"FAILURE\"}") + })) + }) + public Response insertIntoTable( + @ApiParam(value = "Major Version",required = true) @PathParam("version") String version, + @ApiParam(value = "Minor Version",required = false) @HeaderParam(XMINORVERSION) String minorVersion, + @ApiParam(value = "Patch Version",required = false) @HeaderParam(XPATCHVERSION) String patchVersion, + @ApiParam(value = "AID", required = false, hidden = true) @HeaderParam("aid") String aid, + @ApiParam(value = "Application namespace",required = false, hidden = true) @HeaderParam(NS) String ns, + @ApiParam(value = "Authorization", required = true) @HeaderParam(MusicUtil.AUTHORIZATION) String authorization, + JsonInsert insObj, + @ApiParam(value = "Keyspace Name", + required = true) @PathParam("keyspace") String keyspace, + @ApiParam(value = "Table Name", + required = true) @PathParam("tablename") String tablename) { + try { + ResponseBuilder response = MusicUtil.buildVersionResponse(VERSION, minorVersion, patchVersion); + if ( null == insObj ) { + return response.status(Status.BAD_REQUEST).entity(new JsonResponse(ResultType.FAILURE) + .setError(ResultType.BODYMISSING.getResult()).toMap()).build(); + } + if((keyspace == null || keyspace.isEmpty()) || (tablename == null || tablename.isEmpty())){ + return response.status(Status.BAD_REQUEST).entity(new JsonResponse(ResultType.FAILURE) + .setError("one or more path parameters are not set, please check and try again") + .toMap()).build(); + } + EELFLoggerDelegate.mdcPut("keyspace","(" + keyspace + ")"); + ReturnType result = null; + String consistency = insObj.getConsistencyInfo().get("type"); + try { + insObj.setKeyspaceName(keyspace); + insObj.setTableName(tablename); + if (consistency.equalsIgnoreCase(MusicUtil.CRITICAL)) { + String lockId = insObj.getConsistencyInfo().get("lockId"); + if(lockId == null) { + logger.error(EELFLoggerDelegate.errorLogger,"LockId cannot be null. Create lock reference or" + + " use ATOMIC instead of CRITICAL", ErrorSeverity.FATAL, ErrorTypes.MUSICSERVICEERROR); + return response.status(Status.BAD_REQUEST).entity(new JsonResponse(ResultType.FAILURE).setError("LockId cannot be null. Create lock " + + "and acquire lock or use ATOMIC instead of CRITICAL").toMap()).build(); + } + } + result = MusicCore.insertIntoTable(insObj); + }catch (MusicQueryException ex) { + logger.error(EELFLoggerDelegate.errorLogger,ex.getMessage(), ex.getMessage() ,ErrorSeverity + .WARN, ErrorTypes.MUSICSERVICEERROR, ex); + return response.status(Status.BAD_REQUEST).entity(new JsonResponse(ResultType.FAILURE).setError(ex.getMessage()).toMap()).build(); + }catch (Exception ex) { + logger.error(EELFLoggerDelegate.errorLogger,ex.getMessage(), AppMessages.UNKNOWNERROR ,ErrorSeverity + .WARN, ErrorTypes.MUSICSERVICEERROR, ex); + return response.status(Status.BAD_REQUEST).entity(new JsonResponse(ResultType.FAILURE).setError(ex.getMessage()).toMap()).build(); + } + if (result==null) { + logger.error(EELFLoggerDelegate.errorLogger,"Null result - Please Contact admin", AppMessages.UNKNOWNERROR ,ErrorSeverity.WARN, ErrorTypes.MUSICSERVICEERROR); + return response.status(Status.BAD_REQUEST).entity(new JsonResponse(ResultType.FAILURE).setError("Null result - Please Contact admin").toMap()).build(); + }else if(result.getResult() == ResultType.FAILURE) { + return response.status(Status.BAD_REQUEST).entity(new JsonResponse(result.getResult()).setError(result.getMessage()).toMap()).build(); + } + return response.status(Status.OK).entity(new JsonResponse(result.getResult()).setMessage("Insert Successful").toMap()).build(); + } finally { + EELFLoggerDelegate.mdcRemove("keyspace"); + } + } + + /** + * + * @param insObj + * @param keyspace + * @param tablename + * @param info + * @return + * @throws MusicServiceException + * @throws MusicQueryException + * @throws Exception + */ + @PUT + @Path("/{keyspace: .*}/tables/{tablename: .*}/rows") + @ApiOperation(value = "Update Table", response = String.class, + notes = "Update the table with the data in the JSON body.") + @Consumes(MediaType.APPLICATION_JSON) + @Produces(MediaType.APPLICATION_JSON) + public Response updateTable( + @ApiParam(value = "Major Version", + required = true) @PathParam("version") String version, + @ApiParam(value = "Minor Version", + required = false) @HeaderParam(XMINORVERSION) String minorVersion, + @ApiParam(value = "Patch Version", + required = false) @HeaderParam(XPATCHVERSION) String patchVersion, + @ApiParam(value = "AID", required = false, hidden = true) @HeaderParam("aid") String aid, + @ApiParam(value = "Application namespace", + required = false, hidden = true) @HeaderParam(NS) String ns, + @ApiParam(value = "Authorization", required = true) @HeaderParam(MusicUtil.AUTHORIZATION) String authorization, + JsonUpdate updateObj, + @ApiParam(value = "Keyspace Name", + required = true) @PathParam("keyspace") String keyspace, + @ApiParam(value = "Table Name", + required = true) @PathParam("tablename") String tablename, + @Context UriInfo info) throws MusicQueryException, MusicServiceException { + try { + ResponseBuilder response = MusicUtil.buildVersionResponse(VERSION, minorVersion, patchVersion); + if ( null == updateObj ) { + return response.status(Status.BAD_REQUEST).entity(new JsonResponse(ResultType.FAILURE) + .setError(ResultType.BODYMISSING.getResult()).toMap()).build(); + } + if((keyspace == null || keyspace.isEmpty()) || (tablename == null || tablename.isEmpty())){ + return response.status(Status.BAD_REQUEST).entity(new JsonResponse(ResultType.FAILURE) + .setError("one or more path parameters are not set, please check and try again") + .toMap()).build(); + } + EELFLoggerDelegate.mdcPut("keyspace", "( "+keyspace+" ) "); + long startTime = System.currentTimeMillis(); + String operationId = UUID.randomUUID().toString(); // just for infoging + // purposes. + String consistency = updateObj.getConsistencyInfo().get("type"); + ReturnType operationResult = null; + logger.info(EELFLoggerDelegate.applicationLogger, "--------------Music " + consistency + + " update-" + operationId + "-------------------------"); + + updateObj.setKeyspaceName(keyspace); + updateObj.setTableName(tablename); + + try { + if (consistency.equalsIgnoreCase(MusicUtil.CRITICAL)) { + String lockId = updateObj.getConsistencyInfo().get("lockId"); + if(lockId == null) { + logger.error(EELFLoggerDelegate.errorLogger,"LockId cannot be null. Create lock reference or" + + " use ATOMIC instead of CRITICAL", ErrorSeverity.FATAL, ErrorTypes.MUSICSERVICEERROR); + return response.status(Status.BAD_REQUEST).entity(new JsonResponse(ResultType.FAILURE).setError("LockId cannot be null. Create lock " + + "and acquire lock or use ATOMIC instead of CRITICAL").toMap()).build(); + } + } + operationResult = MusicCore.updateTable(updateObj,info.getQueryParameters()); + }catch (MusicLockingException e) { + logger.error(EELFLoggerDelegate.errorLogger,e, AppMessages.UNKNOWNERROR ,ErrorSeverity.WARN, + ErrorTypes.GENERALSERVICEERROR, e); + return response.status(Status.BAD_REQUEST).entity(new JsonResponse(ResultType.FAILURE).setError(e.getMessage()).toMap()).build(); + }catch (MusicQueryException ex) { + logger.error(EELFLoggerDelegate.errorLogger,ex.getMessage(), ex.getMessage() ,ErrorSeverity + .WARN, ErrorTypes.MUSICSERVICEERROR, ex); + return response.status(Status.BAD_REQUEST).entity(new JsonResponse(ResultType.FAILURE).setError(ex.getMessage()).toMap()).build(); + }catch (Exception ex) { + logger.error(EELFLoggerDelegate.errorLogger,ex.getMessage(), AppMessages.UNKNOWNERROR ,ErrorSeverity + .WARN, ErrorTypes.MUSICSERVICEERROR, ex); + return response.status(Status.BAD_REQUEST).entity(new JsonResponse(ResultType.FAILURE).setError(ex.getMessage()).toMap()).build(); + } + long actualUpdateCompletionTime = System.currentTimeMillis(); + + long endTime = System.currentTimeMillis(); + long jsonParseCompletionTime = System.currentTimeMillis(); + String timingString = "Time taken in ms for Music " + consistency + " update-" + operationId + + ":" + "|total operation time:" + (endTime - startTime) + + "|json parsing time:" + (jsonParseCompletionTime - startTime) + + "|update time:" + (actualUpdateCompletionTime - jsonParseCompletionTime) + + "|"; + + if (operationResult != null && operationResult.getTimingInfo() != null) { + String lockManagementTime = operationResult.getTimingInfo(); + timingString = timingString + lockManagementTime; + } + logger.info(EELFLoggerDelegate.applicationLogger, timingString); + + if (operationResult==null) { + logger.error(EELFLoggerDelegate.errorLogger,"Null result - Please Contact admin", AppMessages.UNKNOWNERROR ,ErrorSeverity.WARN, ErrorTypes.GENERALSERVICEERROR); + return response.status(Status.BAD_REQUEST).entity(new JsonResponse(ResultType.FAILURE).setError("Null result - Please Contact admin").toMap()).build(); + } + if ( operationResult.getResult() == ResultType.SUCCESS ) { + return response.status(Status.OK).entity(new JsonResponse(operationResult.getResult()).setMessage(operationResult.getMessage()).toMap()).build(); + } else { + logger.error(EELFLoggerDelegate.errorLogger,operationResult.getMessage(), AppMessages.UNKNOWNERROR ,ErrorSeverity.WARN, ErrorTypes.GENERALSERVICEERROR); + return response.status(Status.BAD_REQUEST).entity(new JsonResponse(operationResult.getResult()).setError(operationResult.getMessage()).toMap()).build(); + } + } finally { + EELFLoggerDelegate.mdcRemove("keyspace"); + } + } + + /** + * + * @param delObj + * @param keyspace + * @param tablename + * @param info + * @return + * @throws MusicServiceException + * @throws MusicQueryException + * @throws Exception + */ + @DELETE + @Path("/{keyspace: .*}/tables/{tablename: .*}/rows") + @ApiOperation(value = "Delete From table", response = String.class, + notes = "Delete from a table, the row or parts of a row. Based on JSON body.") + @Consumes(MediaType.APPLICATION_JSON) + @Produces(MediaType.APPLICATION_JSON) + public Response deleteFromTable( + @ApiParam(value = "Major Version", + required = true) @PathParam("version") String version, + @ApiParam(value = "Minor Version", + required = false) @HeaderParam(XMINORVERSION) String minorVersion, + @ApiParam(value = "Patch Version", + required = false) @HeaderParam(XPATCHVERSION) String patchVersion, + @ApiParam(value = "AID", required = false, hidden = true) @HeaderParam("aid") String aid, + @ApiParam(value = "Application namespace", + required = false, hidden = true) @HeaderParam(NS) String ns, + @ApiParam(value = "Authorization", required = true) @HeaderParam(MusicUtil.AUTHORIZATION) String authorization, + JsonDelete delObj, + @ApiParam(value = "Keyspace Name", + required = true) @PathParam("keyspace") String keyspace, + @ApiParam(value = "Table Name", + required = true) @PathParam("tablename") String tablename, + @Context UriInfo info) throws MusicQueryException, MusicServiceException { + try { + ResponseBuilder response = MusicUtil.buildVersionResponse(VERSION, minorVersion, patchVersion); + if((keyspace == null || keyspace.isEmpty()) || (tablename == null || tablename.isEmpty())){ + return response.status(Status.BAD_REQUEST).entity(new JsonResponse(ResultType.FAILURE) + .setError("one or more path parameters are not set, please check and try again") + .toMap()).build(); + } + EELFLoggerDelegate.mdcPut("keyspace", "( "+keyspace+" ) "); + if(delObj == null) { + logger.error(EELFLoggerDelegate.errorLogger,ResultType.BODYMISSING.getResult(), AppMessages.MISSINGDATA ,ErrorSeverity.WARN, ErrorTypes.DATAERROR); + return response.status(Status.BAD_REQUEST).entity(new JsonResponse(ResultType.FAILURE).setError(ResultType.BODYMISSING.getResult()).toMap()).build(); + } + ReturnType operationResult = null; + String consistency = delObj.getConsistencyInfo().get("type"); + delObj.setKeyspaceName(keyspace); + delObj.setTableName(tablename); + try { + if (consistency.equalsIgnoreCase(MusicUtil.CRITICAL)) { + String lockId = delObj.getConsistencyInfo().get("lockId"); + if(lockId == null) { + logger.error(EELFLoggerDelegate.errorLogger,"LockId cannot be null. Create lock reference or" + + " use ATOMIC instead of CRITICAL", ErrorSeverity.FATAL, ErrorTypes.MUSICSERVICEERROR); + return response.status(Status.BAD_REQUEST).entity(new JsonResponse(ResultType.FAILURE).setError("LockId cannot be null. Create lock " + + "and acquire lock or use ATOMIC instead of CRITICAL").toMap()).build(); + } + } + + operationResult = MusicCore.deleteFromTable(delObj,info.getQueryParameters()); + } catch (MusicQueryException ex) { + logger.error(EELFLoggerDelegate.errorLogger,ex.getMessage(), ex.getMessage() ,ErrorSeverity + .WARN, ErrorTypes.MUSICSERVICEERROR, ex); + return response.status(Status.BAD_REQUEST).entity(new JsonResponse(ResultType.FAILURE).setError(ex.getMessage()).toMap()).build(); + + } catch (MusicLockingException e) { + logger.error(EELFLoggerDelegate.errorLogger,e, AppMessages.UNKNOWNERROR ,ErrorSeverity.WARN, ErrorTypes.GENERALSERVICEERROR, e); + return response.status(Status.BAD_REQUEST).entity(new JsonResponse(ResultType.FAILURE) + .setError("Unable to perform Delete operation. Exception from music").toMap()).build(); + } + if (operationResult==null) { + logger.error(EELFLoggerDelegate.errorLogger,"Null result - Please Contact admin", AppMessages.UNKNOWNERROR ,ErrorSeverity.WARN, ErrorTypes.GENERALSERVICEERROR); + return response.status(Status.BAD_REQUEST).entity(new JsonResponse(ResultType.FAILURE).setError("Null result - Please Contact admin").toMap()).build(); + } + if (operationResult.getResult().equals(ResultType.SUCCESS)) { + return response.status(Status.OK).entity(new JsonResponse(operationResult.getResult()).setMessage(operationResult.getMessage()).toMap()).build(); + } else { + logger.error(EELFLoggerDelegate.errorLogger,operationResult.getMessage(), AppMessages.UNKNOWNERROR ,ErrorSeverity.WARN, ErrorTypes.GENERALSERVICEERROR); + return response.status(Status.BAD_REQUEST).entity(new JsonResponse(ResultType.FAILURE).setError(operationResult.getMessage()).toMap()).build(); + } + } finally { + EELFLoggerDelegate.mdcRemove("keyspace"); + } + } + + /** + * + * @param tabObj + * @param keyspace + * @param tablename + * @throws Exception + */ + @DELETE + @Path("/{keyspace: .*}/tables/{tablename: .*}") + @ApiOperation(value = "Drop Table", response = String.class) + @Produces(MediaType.APPLICATION_JSON) + @ApiResponses(value={ + @ApiResponse(code=200, message = "Success",examples = @Example( value = { + @ExampleProperty(mediaType="application/json",value = + "{\"status\" : \"SUCCESS\"}") + })), + @ApiResponse(code=400, message = "Failure",examples = @Example( value = { + @ExampleProperty(mediaType="application/json",value = + "{\"error\" : \"<errorMessage>\"," + + "\"status\" : \"FAILURE\"}") + })) + }) + public Response dropTable( + @ApiParam(value = "Major Version", + required = true) @PathParam("version") String version, + @ApiParam(value = "Minor Version", + required = false) @HeaderParam(XMINORVERSION) String minorVersion, + @ApiParam(value = "Patch Version", + required = false) @HeaderParam(XPATCHVERSION) String patchVersion, + @ApiParam(value = "AID", required = false, hidden = true) @HeaderParam("aid") String aid, + @ApiParam(value = "Application namespace", + required = false, hidden = true) @HeaderParam(NS) String ns, + @ApiParam(value = "Authorization", required = true) @HeaderParam(MusicUtil.AUTHORIZATION) String authorization, + @ApiParam(value = "Keyspace Name", + required = true) @PathParam("keyspace") String keyspace, + @ApiParam(value = "Table Name", + required = true) @PathParam("tablename") String tablename) throws Exception { + try { + ResponseBuilder response = MusicUtil.buildVersionResponse(VERSION, minorVersion, patchVersion); + if((keyspace == null || keyspace.isEmpty()) || (tablename == null || tablename.isEmpty())){ + return response.status(Status.BAD_REQUEST).entity(new JsonResponse(ResultType.FAILURE) + .setError("one or more path parameters are not set, please check and try again") + .toMap()).build(); + } + EELFLoggerDelegate.mdcPut("keyspace", "( "+keyspace+" ) "); + JsonTable jsonTable = new JsonTable(); + jsonTable.setKeyspaceName(keyspace); + jsonTable.setTableName(tablename); + try { + return response.status(Status.OK).entity(new JsonResponse(MusicCore.dropTable(jsonTable, MusicUtil.EVENTUAL)).toMap()).build(); + } catch (MusicQueryException ex) { + logger.error(EELFLoggerDelegate.errorLogger, ex.getMessage(), AppMessages.QUERYERROR,ErrorSeverity.WARN + , ErrorTypes.QUERYERROR); + return response.status(Status.BAD_REQUEST).entity(new JsonResponse(ResultType.FAILURE).setError(ex.getMessage()).toMap()).build(); + } catch (MusicServiceException ex) { + logger.error(EELFLoggerDelegate.errorLogger, ex.getMessage(), AppMessages.MISSINGINFO ,ErrorSeverity.WARN + , ErrorTypes.GENERALSERVICEERROR,ex); + return response.status(Status.BAD_REQUEST).entity(new JsonResponse(ResultType.FAILURE).setError(ex.getMessage()).toMap()).build(); + } + } finally { + EELFLoggerDelegate.mdcRemove("keyspace"); + } + } + + /** + * + * @param selObj + * @param keyspace + * @param tablename + * @param info + * @return + */ + @PUT + @Path("/{keyspace: .*}/tables/{tablename: .*}/rows/criticalget") + @ApiOperation(value = "** Depreciated ** - Select Critical", response = Map.class, + notes = "This API is depreciated in favor of the regular select api.\n" + + "Avaliable to use with the select api by providing a minorVersion of 1 " + + "and patchVersion of 0.\n" + + "Critical Get requires parameter rowId=value and consistency in order to work.\n" + + "It will fail if either are missing.") + @Consumes(MediaType.APPLICATION_JSON) + @Produces(MediaType.APPLICATION_JSON) + @ApiResponses(value={ + @ApiResponse(code=200, message = "Success",examples = @Example( value = { + @ExampleProperty(mediaType="application/json",value = + "{\"result\":{\"row 0\":{\"address\":" + + "{\"city\":\"Someplace\",\"street\":\"1 Some way\"}," + + "\"emp_salary\":50,\"emp_name\":\"tom\",\"emp_id\":" + + "\"cfd66ccc-d857-4e90-b1e5-df98a3d40cd6\"}},\"status\":\"SUCCESS\"}") + })), + @ApiResponse(code=400, message = "Failure",examples = @Example( value = { + @ExampleProperty(mediaType="application/json",value = + "{\"error\" : \"<errorMessage>\"," + + "\"status\" : \"FAILURE\"}") + })) + }) + public Response selectCritical( + @ApiParam(value = "Major Version", + required = true) @PathParam("version") String version, + @ApiParam(value = "Minor Version",example = "0", + required = false) @HeaderParam(XMINORVERSION) String minorVersion, + @ApiParam(value = "Patch Version",example = "0", + required = false) @HeaderParam(XPATCHVERSION) String patchVersion, + @ApiParam(value = "AID", required = false, hidden = true) @HeaderParam("aid") String aid, + @ApiParam(value = "Application namespace", + required = false, hidden = true) @HeaderParam(NS) String ns, + @ApiParam(value = "Authorization", required = true) @HeaderParam(MusicUtil.AUTHORIZATION) String authorization, + JsonInsert selObj, + @ApiParam(value = "Keyspace Name", + required = true) @PathParam("keyspace") String keyspace, + @ApiParam(value = "Table Name", + required = true) @PathParam("tablename") String tablename, + @Context UriInfo info) throws Exception { + try { + ResponseBuilder response = MusicUtil.buildVersionResponse(VERSION, minorVersion, patchVersion); + if((keyspace == null || keyspace.isEmpty()) || (tablename == null || tablename.isEmpty())) { + return response.status(Status.BAD_REQUEST).entity(new JsonResponse(ResultType.FAILURE) + .setError("one or more path parameters are not set, please check and try again") + .toMap()).build(); + } + EELFLoggerDelegate.mdcPut("keyspace", "( " + keyspace + " )"); + if (info.getQueryParameters().isEmpty()) { + logger.error(EELFLoggerDelegate.errorLogger,RestMusicDataAPI.PARAMETER_ERROR, AppMessages.UNKNOWNERROR ,ErrorSeverity.WARN, ErrorTypes + .GENERALSERVICEERROR); + return response.status(Status.BAD_REQUEST).entity(new JsonResponse(ResultType.FAILURE).setError(RestMusicDataAPI.PARAMETER_ERROR).toMap()).build(); + } + if (selObj == null || selObj.getConsistencyInfo().isEmpty()) { + String error = " Missing Body or Consistency type."; + logger.error(EELFLoggerDelegate.errorLogger,ResultType.BODYMISSING.getResult() + error, AppMessages.MISSINGDATA ,ErrorSeverity.WARN, ErrorTypes.DATAERROR); + return response.status(Status.BAD_REQUEST).entity(new JsonResponse(ResultType.FAILURE).setError(ResultType.BODYMISSING.getResult() + error).toMap()).build(); + } + ResultSet results = null; + String consistency = selObj.getConsistencyInfo().get("type"); + String lockId = selObj.getConsistencyInfo().get("lockId"); + selObj.setKeyspaceName(keyspace); + selObj.setTableName(tablename); + try { + if (consistency.equalsIgnoreCase(MusicUtil.CRITICAL)) { + if(lockId == null) { + logger.error(EELFLoggerDelegate.errorLogger,"LockId cannot be null. Create lock reference or" + + " use ATOMIC instead of CRITICAL", ErrorSeverity.FATAL, ErrorTypes.MUSICSERVICEERROR); + return response.status(Status.BAD_REQUEST).entity(new JsonResponse(ResultType.FAILURE).setError("LockId cannot be null. Create lock " + + "and acquire lock or use ATOMIC instead of CRITICAL").toMap()).build(); + } + } + results = MusicCore.selectCritical(selObj, info.getQueryParameters()); + }catch (MusicQueryException ex) { + logger.error(EELFLoggerDelegate.errorLogger,ex.getMessage(), ex.getMessage() ,ErrorSeverity + .WARN, ErrorTypes.MUSICSERVICEERROR, ex); + return response.status(Status.BAD_REQUEST).entity(new JsonResponse(ResultType.FAILURE).setError(ex.getMessage()).toMap()).build(); + + }catch(Exception ex) { + return response.status(Status.BAD_REQUEST).entity(new JsonResponse(ResultType.FAILURE).setError(ex.getMessage()).toMap()).build(); + } + + if(results!=null && results.getAvailableWithoutFetching() >0) { + return response.status(Status.OK).entity(new JsonResponse(ResultType.SUCCESS).setDataResult(MusicDataStoreHandle.marshallResults(results)).toMap()).build(); + } + return response.status(Status.OK).entity(new JsonResponse(ResultType.SUCCESS).setError("No data found").toMap()).build(); + } finally { + EELFLoggerDelegate.mdcRemove("keyspace"); + } + } + + /** + * This API will replace the original select and provide a single API fro select and critical. + * The idea is to depreciate the older api of criticalGet and use a single API. + * + * @param selObj + * @param keyspace + * @param tablename + * @param info + * @return + */ + @GET + @Path("/{keyspace: .*}/tables/{tablename: .*}/rows") + @ApiOperation(value = "Select", response = Map.class, + notes = "This has 2 versions: if minorVersion and patchVersion is null or 0, this will be a Eventual Select only.\n" + + "If minorVersion is 1 and patchVersion is 0, this will act like the Critical Select \n" + + "Critical Get requires parameter rowId=value and consistency in order to work.\n" + + "If parameters are missing or consistency information is missing. An eventual select will be preformed.") + @Consumes(MediaType.APPLICATION_JSON) + @Produces(MediaType.APPLICATION_JSON) + @ApiResponses(value={ + @ApiResponse(code=200, message = "Success",examples = @Example( value = { + @ExampleProperty(mediaType="application/json",value = + "{\"result\":{\"row 0\":{\"address\":" + + "{\"city\":\"Someplace\",\"street\":\"1 Some way\"}," + + "\"emp_salary\":50,\"emp_name\":\"tom\",\"emp_id\":" + + "\"cfd66ccc-d857-4e90-b1e5-df98a3d40cd6\"}},\"status\":\"SUCCESS\"}") + })), + @ApiResponse(code=400, message = "Failure",examples = @Example( value = { + @ExampleProperty(mediaType="application/json",value = + "{\"error\" : \"<errorMessage>\"," + + "\"status\" : \"FAILURE\"}") + })) + }) + public Response selectWithCritical( + @ApiParam(value = "Major Version",example = "v2", + required = true) @PathParam("version") String version, + @ApiParam(value = "Minor Version",example = "1", + required = false) @HeaderParam(XMINORVERSION) String minorVersion, + @ApiParam(value = "Patch Version",example = "0", + required = false) @HeaderParam(XPATCHVERSION) String patchVersion, + @ApiParam(value = "AID", required = false, hidden = true) @HeaderParam("aid") String aid, + @ApiParam(value = "Application namespace", + required = false,hidden = true) @HeaderParam(NS) String ns, + @ApiParam(value = "Authorization", required = true) @HeaderParam(MusicUtil.AUTHORIZATION) String authorization, + JsonInsert selObj, + @ApiParam(value = "Keyspace Name", required = true) @PathParam("keyspace") String keyspace, + @ApiParam(value = "Table Name", required = true) @PathParam("tablename") String tablename, + @Context UriInfo info) throws Exception { + if ((minorVersion != null && patchVersion != null) && + (Integer.parseInt(minorVersion) == 1 && Integer.parseInt(patchVersion) == 0) && + (!(null == selObj) && !selObj.getConsistencyInfo().isEmpty())) { + return selectCritical(version, minorVersion, patchVersion, aid, ns, authorization, selObj, keyspace, tablename, info); + } else { + return select(version, minorVersion, patchVersion, aid, ns, authorization, keyspace, tablename, info); + } + } + + /** + * + * @param keyspace + * @param tablename + * @param info + * @return + * @throws Exception + */ + private Response select( + String version,String minorVersion,String patchVersion, + String aid,String ns,String authorization,String keyspace, + String tablename,UriInfo info) throws Exception { + try { + ResponseBuilder response = MusicUtil.buildVersionResponse(VERSION, minorVersion, patchVersion); + if((keyspace == null || keyspace.isEmpty()) || (tablename == null || tablename.isEmpty())){ + return response.status(Status.BAD_REQUEST).entity(new JsonResponse(ResultType.FAILURE) + .setError("one or more path parameters are not set, please check and try again") + .toMap()).build(); + } + EELFLoggerDelegate.mdcPut("keyspace", "( " + keyspace + " ) "); + try { + JsonSelect jsonSelect = new JsonSelect(); + jsonSelect.setKeyspaceName(keyspace); + jsonSelect.setTableName(tablename); + ResultSet results = MusicCore.select(jsonSelect, info.getQueryParameters()); + if(results.getAvailableWithoutFetching() >0) { + return response.status(Status.OK).entity(new JsonResponse(ResultType.SUCCESS).setDataResult(MusicDataStoreHandle.marshallResults(results)).toMap()).build(); + } + return response.status(Status.OK).entity(new JsonResponse(ResultType.SUCCESS).setDataResult(MusicDataStoreHandle.marshallResults(results)).setError("No data found").toMap()).build(); + } catch (MusicQueryException ex) { + logger.error(EELFLoggerDelegate.errorLogger,ex.getMessage(), ex.getMessage() ,ErrorSeverity + .WARN, ErrorTypes.MUSICSERVICEERROR, ex); + return response.status(Status.BAD_REQUEST).entity(new JsonResponse(ResultType.FAILURE).setError(ex.getMessage()).toMap()).build(); + + } catch (MusicServiceException ex) { + logger.error(EELFLoggerDelegate.errorLogger, ex, AppMessages.UNKNOWNERROR ,ErrorSeverity.ERROR, + ErrorTypes.MUSICSERVICEERROR, ex); + return response.status(Status.BAD_REQUEST).entity(new JsonResponse(ResultType.FAILURE).setError(ex.getMessage()).toMap()).build(); + } + } finally { + EELFLoggerDelegate.mdcRemove("keyspace"); + } + } + + /** + * + * @param keyspace + * @param tablename + * @param info + * @param limit + * @return + * @throws MusicServiceException + */ + public PreparedQueryObject selectSpecificQuery(String keyspace, + String tablename, UriInfo info, int limit) + throws MusicServiceException { + PreparedQueryObject queryObject = new PreparedQueryObject(); + StringBuilder rowIdString = getRowIdentifier(keyspace, + tablename,info.getQueryParameters(),queryObject).rowIdString; + queryObject.appendQueryString( + "SELECT * FROM " + keyspace + "." + tablename + " WHERE " + rowIdString); + if (limit != -1) { + queryObject.appendQueryString(" LIMIT " + limit); + } + queryObject.appendQueryString(";"); + return queryObject; + } + + /** + * + * @param keyspace + * @param tablename + * @param rowParams + * @param queryObject + * @return + * @throws MusicServiceException + */ + private RowIdentifier getRowIdentifier(String keyspace, String tablename, + MultivaluedMap<String, String> rowParams, PreparedQueryObject queryObject) + throws MusicServiceException { + StringBuilder rowSpec = new StringBuilder(); + int counter = 0; + TableMetadata tableInfo = MusicDataStoreHandle.returnColumnMetadata(keyspace, tablename); + if (tableInfo == null) { + logger.error(EELFLoggerDelegate.errorLogger, + "Table information not found. Please check input for table name= " + + keyspace + "." + tablename); + throw new MusicServiceException( + "Table information not found. Please check input for table name= " + + keyspace + "." + tablename); + } + StringBuilder primaryKey = new StringBuilder(); + for (MultivaluedMap.Entry<String, List<String>> entry : rowParams.entrySet()) { + String keyName = entry.getKey(); + List<String> valueList = entry.getValue(); + String indValue = valueList.get(0); + DataType colType = null; + Object formattedValue = null; + try { + colType = tableInfo.getColumn(entry.getKey()).getType(); + formattedValue = MusicUtil.convertToActualDataType(colType, indValue); + } catch (Exception e) { + logger.error(EELFLoggerDelegate.errorLogger,e); + } + if(tableInfo.getPrimaryKey().get(0).getName().equals(entry.getKey())) { + primaryKey.append(indValue); + } + rowSpec.append(keyName + "= ?"); + queryObject.addValue(formattedValue); + if (counter != rowParams.size() - 1) { + rowSpec.append(" AND "); + } + counter = counter + 1; + } + return new RowIdentifier(primaryKey.toString(), rowSpec, queryObject); + } +} diff --git a/music-rest/src/main/java/org/onap/music/rest/RestMusicHealthCheckAPI.java b/music-rest/src/main/java/org/onap/music/rest/RestMusicHealthCheckAPI.java new file mode 100644 index 00000000..eef3aa3a --- /dev/null +++ b/music-rest/src/main/java/org/onap/music/rest/RestMusicHealthCheckAPI.java @@ -0,0 +1,124 @@ +/* + * ============LICENSE_START========================================== + * org.onap.music + * =================================================================== + * Copyright (c) 2017 AT&T Intellectual Property + * + * Modifications Copyright (C) 2018 IBM. + * =================================================================== + * 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. + * + * ============LICENSE_END============================================= + * ==================================================================== + */ + +package org.onap.music.rest; + +import java.util.HashMap; +/** + * @author inam + * + */ +import java.util.Map; + +import javax.servlet.http.HttpServletResponse; +import javax.ws.rs.GET; +import javax.ws.rs.Path; +import javax.ws.rs.PathParam; +import javax.ws.rs.Produces; +import javax.ws.rs.core.Context; +import javax.ws.rs.core.MediaType; +import javax.ws.rs.core.Response; +import javax.ws.rs.core.Response.Status; + + +import org.onap.music.eelf.healthcheck.MusicHealthCheck; +import org.onap.music.eelf.logging.EELFLoggerDelegate; +import org.onap.music.main.MusicUtil; + +import com.datastax.driver.core.ConsistencyLevel; + +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiOperation; +import io.swagger.annotations.ApiParam; + + + + +@Path("/v2/service") +@Api(value="Healthcheck Api") +public class RestMusicHealthCheckAPI { + + + private static EELFLoggerDelegate logger = EELFLoggerDelegate.getLogger(MusicUtil.class); + private static final String ACTIVE_STATUS = "ACTIVE"; + private static final String INVALID_STATUS = "INVALID"; + private static final String INACTIVE_STATUS = "INACTIVE"; + private static final String INVALID_MESSAGE = "Consistency level is invalid..."; + private static final String INACTIVE_MESSAGE = "One or more nodes in the Cluster is/are down or not responding."; + private static final String ACTIVE_MESSAGE = "Cassandra Running and Listening to requests"; + private static final String STATUS_KEY = "status"; + private static final String MESSAGE_KEY = "message"; + + @GET + @Path("/pingCassandra/{consistency}") + @ApiOperation(value = "Get Health Status", response = Map.class) + @Produces(MediaType.APPLICATION_JSON) + public Response cassandraStatus( + @Context HttpServletResponse response, + @ApiParam(value = "Consistency level",required = true) + @PathParam("consistency") String consistency) { + logger.info(EELFLoggerDelegate.applicationLogger,"Replying to request for MUSIC Health Check status for Cassandra"); + + Map<String, Object> resultMap = new HashMap<>(); + if(ConsistencyLevel.valueOf(consistency) == null) { + resultMap.put(STATUS_KEY,INVALID_STATUS); + resultMap.put(MESSAGE_KEY, INVALID_MESSAGE); + resultMap.put(INVALID_STATUS, INVALID_STATUS); + return Response.status(Status.BAD_REQUEST).entity(resultMap).build(); + } + MusicHealthCheck cassHealthCheck = new MusicHealthCheck(); + String status = cassHealthCheck.getCassandraStatus(consistency); + if(status.equals(ACTIVE_STATUS)) { + resultMap.put(STATUS_KEY,ACTIVE_STATUS); + resultMap.put(MESSAGE_KEY, ACTIVE_MESSAGE); + resultMap.put(ACTIVE_STATUS, ACTIVE_MESSAGE); + return Response.status(Status.OK).entity(resultMap).build(); + } else { + resultMap.put(STATUS_KEY,INACTIVE_STATUS); + resultMap.put(MESSAGE_KEY, INACTIVE_MESSAGE); + resultMap.put(INACTIVE_STATUS, INACTIVE_MESSAGE); + return Response.status(Status.BAD_REQUEST).entity(resultMap).build(); + } + } + + @GET + @Path("/musicHealthCheck") + @ApiOperation(value = "Get Health Status", response = Map.class) + @Produces(MediaType.APPLICATION_JSON) + public Response musicHealthCheck() { + logger.info(EELFLoggerDelegate.applicationLogger,"Replying to request for Health Check status for MUSIC"); + Map<String, Object> resultMap = new HashMap<>(); + MusicHealthCheck healthCheck = new MusicHealthCheck(); + + String status = healthCheck.getCassandraStatus(ConsistencyLevel.ANY.toString()); + if(status.equals(ACTIVE_STATUS)) { + resultMap.put("Cassandra", "Active"); + } else { + resultMap.put("Cassandra", "Inactive"); + } + resultMap.put("MUSIC", "Active"); + return Response.status(Status.OK).entity(resultMap).build(); + } + +} diff --git a/music-rest/src/main/java/org/onap/music/rest/RestMusicLocksAPI.java b/music-rest/src/main/java/org/onap/music/rest/RestMusicLocksAPI.java new file mode 100644 index 00000000..321e2561 --- /dev/null +++ b/music-rest/src/main/java/org/onap/music/rest/RestMusicLocksAPI.java @@ -0,0 +1,632 @@ +/* + * ============LICENSE_START========================================== + * org.onap.music + * =================================================================== + * Copyright (c) 2017 AT&T Intellectual Property + * =================================================================== + * * Modifications Copyright (c) 2019 Samsung + * =================================================================== + * + * 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. + * + * ============LICENSE_END============================================= + * ==================================================================== + */ + +package org.onap.music.rest; + +import java.util.List; +import java.util.Map; + +import javax.ws.rs.Consumes; +import javax.ws.rs.DELETE; +import javax.ws.rs.GET; +import javax.ws.rs.HeaderParam; +import javax.ws.rs.POST; +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.ws.rs.core.Response.ResponseBuilder; +import javax.ws.rs.core.Response.Status; + +import org.onap.music.datastore.jsonobjects.JsonLeasedLock; +import org.onap.music.datastore.jsonobjects.JsonLock; +import org.onap.music.eelf.logging.EELFLoggerDelegate; +import org.onap.music.eelf.logging.format.AppMessages; +import org.onap.music.eelf.logging.format.ErrorSeverity; +import org.onap.music.eelf.logging.format.ErrorTypes; +import org.onap.music.exceptions.MusicLockingException; +import org.onap.music.lockingservice.cassandra.LockType; +import org.onap.music.lockingservice.cassandra.MusicLockState; +import org.onap.music.main.MusicCore; +import org.onap.music.main.MusicUtil; +import org.onap.music.main.ResultType; +import org.onap.music.main.ReturnType; +import org.onap.music.response.jsonobjects.JsonResponse; + +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiOperation; +import io.swagger.annotations.ApiParam; +import io.swagger.annotations.ApiResponse; +import io.swagger.annotations.ApiResponses; +import io.swagger.annotations.Example; +import io.swagger.annotations.ExampleProperty; + + +@Path("/v2/locks/") +@Api(value="Locking Api") +public class RestMusicLocksAPI { + + private EELFLoggerDelegate logger =EELFLoggerDelegate.getLogger(RestMusicLocksAPI.class); + private static final String XMINORVERSION = "X-minorVersion"; + private static final String XPATCHVERSION = "X-patchVersion"; + private static final String VERSION = "v2"; + + /** + * Puts the requesting process in the q for this lock. The corresponding + * node will be created if it did not already exist + * + * @param lockName + * @return + * @throws Exception + */ + @POST + @Path("/create/{lockname}") + @ApiOperation(value = "Create and Acquire a Lock Id for a single row.", + notes = "Creates and Acquires a Lock Id for a specific Row in a table based on the key of that row.\n" + + " The corresponding lock will be created if it did not already exist." + + " Lock Name also the Lock is in the form of keyspaceName.tableName.rowId.\n" + + " The Response will be in the form of \"$kesypaceName.tableName.rowId$lockRef\" " + + " where the lockRef is a integer representing the Lock Name buffered by \"$\" " + + " followed by the lock number. This term for " + + " this response is a lockId and it will be used in other /locks API calls where a " + + " lockId is required. If just a lock is required then the form that would be " + + " the original lockname(without the buffered \"$\").", + response = Map.class) + @Consumes(MediaType.APPLICATION_JSON) + @Produces(MediaType.APPLICATION_JSON) + @ApiResponses(value={ + @ApiResponse(code=200, message = "Success",examples = @Example( value = { + @ExampleProperty(mediaType="application/json",value = + "{\"lock\" : {\"lock\" : \"$keyspace.table.rowId$<integer>\"}," + + "\"status\" : \"SUCCESS\"}") + })), + @ApiResponse(code=400, message = "Failure",examples = @Example( value = { + @ExampleProperty(mediaType="application/json",value = + "{\"error\" : \"Unable to aquire lock\"," + + "\"status\" : \"FAILURE\"}") + })) + }) + public Response createLockReference( + @ApiParam(value="Lock Name",required=true) @PathParam("lockname") String lockName, + @ApiParam(value = "Minor Version",required = false) @HeaderParam(XMINORVERSION) String minorVersion, + @ApiParam(value = "Patch Version",required = false) @HeaderParam(XPATCHVERSION) String patchVersion, + @ApiParam(value = "Authorization", required = true) @HeaderParam(MusicUtil.AUTHORIZATION) String authorization, + @ApiParam(value = "AID", required = false, hidden = true) @HeaderParam("aid") String aid, + JsonLock lockObject, + @ApiParam(value = "Lock Owner", required = false) @HeaderParam("owner") String owner, + @ApiParam(value = "Application namespace", + required = false, hidden = true) @HeaderParam("ns") String ns) throws Exception{ + try { + ResponseBuilder response = MusicUtil.buildVersionResponse(VERSION, minorVersion, patchVersion); + Map<String, Object> resultMap = MusicCore.validateLock(lockName); + if (resultMap.containsKey("Error")) { + logger.error(EELFLoggerDelegate.errorLogger,"", AppMessages.MISSINGINFO ,ErrorSeverity.CRITICAL, ErrorTypes.GENERALSERVICEERROR); + response.status(Status.BAD_REQUEST); + return response.entity(new JsonResponse(ResultType.FAILURE).setError(String.valueOf(resultMap.get("Error"))).toMap()).build(); + } + String keyspaceName = (String) resultMap.get("keyspace"); + EELFLoggerDelegate.mdcPut("keyspace", "( " + keyspaceName + " ) "); + + //default lock type is write, as this is always semantically safe + LockType locktype = LockType.WRITE; + if (lockObject!=null && lockObject.getLocktype()!=null) { + locktype = lockObject.getLocktype(); + } + String lockId; + try { + lockId= MusicCore.createLockReference(lockName, locktype, owner); + } catch (MusicLockingException e) { + return response.status(Status.BAD_REQUEST).entity(new JsonResponse(ResultType.FAILURE).setError(e.getMessage()).toMap()).build(); + } + + if (lockId == null) { + logger.error(EELFLoggerDelegate.errorLogger,"", AppMessages.LOCKINGERROR ,ErrorSeverity.CRITICAL, ErrorTypes.LOCKINGERROR); + return response.status(Status.BAD_REQUEST).entity(new JsonResponse(ResultType.FAILURE).setError("Lock Id is null").toMap()).build(); + } + return response.status(Status.OK).entity(new JsonResponse(ResultType.SUCCESS).setLock(lockId).toMap()).build(); + } finally { + EELFLoggerDelegate.mdcRemove("keyspace"); + } + } + + /** + * + * Checks if the node is in the top of the queue and hence acquires the lock + * + * @param lockId + * @return + * @throws Exception + */ + @GET + @Path("/acquire/{lockId}") + @ApiOperation(value = "Aquire Lock Id ", + notes = "Checks if the node is in the top of the queue and hence acquires the lock", + response = Map.class) + @Produces(MediaType.APPLICATION_JSON) + @ApiResponses(value={ + @ApiResponse(code=200, message = "Success",examples = @Example( value = { + @ExampleProperty(mediaType="application/json",value = + "{\"lock\" : {\"lock\" : \"$keyspace.table.rowId$<integer>\"}," + + "\"message\" : \"<integer> is the lock holder for the key\"," + + "\"status\" : \"SUCCESS\"}") + })), + @ApiResponse(code=400, message = "Failure",examples = @Example( value = { + @ExampleProperty(mediaType="application/json",value = + "{\"error\" : \"Unable to aquire lock\"," + + "\"status\" : \"FAILURE\"}") + })) + }) + public Response accquireLock( + @ApiParam(value="Lock Id",required=true) @PathParam("lockId") String lockId, + @ApiParam(value = "Minor Version",required = false) @HeaderParam(XMINORVERSION) String minorVersion, + @ApiParam(value = "Patch Version",required = false) @HeaderParam(XPATCHVERSION) String patchVersion, + @ApiParam(value = "Authorization", required = true) @HeaderParam(MusicUtil.AUTHORIZATION) String authorization, + @ApiParam(value = "AID", required = false, hidden = true) @HeaderParam("aid") String aid, + @ApiParam(value = "Application namespace", + required = false, hidden = true) @HeaderParam("ns") String ns) throws Exception{ + try { + ResponseBuilder response = MusicUtil.buildVersionResponse(VERSION, minorVersion, patchVersion); + Map<String, Object> resultMap = MusicCore.validateLock(lockId); + if (resultMap.containsKey("Error")) { + logger.error(EELFLoggerDelegate.errorLogger,"", AppMessages.INCORRECTDATA ,ErrorSeverity.CRITICAL, ErrorTypes.GENERALSERVICEERROR); + response.status(Status.BAD_REQUEST); + return response.entity(new JsonResponse(ResultType.FAILURE).setError(String.valueOf(resultMap.get("Error"))).toMap()).build(); + } + + String keyspaceName = (String) resultMap.get("keyspace"); + EELFLoggerDelegate.mdcPut("keyspace", "( " + keyspaceName + " ) "); + try { + String lockName = lockId.substring(lockId.indexOf('$')+1, lockId.lastIndexOf('$')); + ReturnType lockStatus = MusicCore.acquireLock(lockName,lockId); + if ( lockStatus.getResult().equals(ResultType.SUCCESS)) { + response.status(Status.OK); + } else { + response.status(Status.BAD_REQUEST); + } + return response.entity(new JsonResponse(lockStatus.getResult()).setLock(lockId).setMessage(lockStatus.getMessage()).toMap()).build(); + } catch (Exception e) { + logger.error(EELFLoggerDelegate.errorLogger,AppMessages.INVALIDLOCK + lockId, ErrorSeverity.CRITICAL, + ErrorTypes.LOCKINGERROR, e); + return response.status(Status.BAD_REQUEST).entity(new JsonResponse(ResultType.FAILURE).setError("Unable to aquire lock").toMap()).build(); + } + } finally { + EELFLoggerDelegate.mdcRemove("keyspace"); + } + } + + + @POST + @Path("/acquire-with-lease/{lockId}") + @ApiOperation( + hidden = false, + value = " ** DEPRECATED ** - Aquire Lock with Lease", + notes = "Acquire the lock with a lease, where lease period is in Milliseconds.\n" + + "This will ensure that a lock will expire in set milliseconds.\n" + + "This is no longer available after v3.2.0", + response = Map.class) + @Consumes(MediaType.APPLICATION_JSON) + @Produces(MediaType.APPLICATION_JSON) + @ApiResponses(value={ + @ApiResponse(code=200, message = "Success",examples = @Example( value = { + @ExampleProperty(mediaType="application/json",value = + "{\"lock\" : {\"lock\" : \"$keyspace.table.rowId$<integer>\"," + + "\"lock-lease\" : \"6000\"}," + + "\"message\" : \"<integer> is the lock holder for the key\"," + + "\"status\" : \"SUCCESS\"}") + })), + @ApiResponse(code=400, message = "Failure",examples = @Example( value = { + @ExampleProperty(mediaType="application/json",value = + "{\"error\" : \"Unable to aquire lock\"," + + "\"status\" : \"FAILURE\"}") + })) + }) + @Deprecated + public Response accquireLockWithLease( + JsonLeasedLock lockObj, + @ApiParam(value="Lock Id",required=true) @PathParam("lockId") String lockId, + @ApiParam(value = "Minor Version",required = false) @HeaderParam(XMINORVERSION) String minorVersion, + @ApiParam(value = "Patch Version",required = false) @HeaderParam(XPATCHVERSION) String patchVersion, + @ApiParam(value = "Authorization", required = true) @HeaderParam(MusicUtil.AUTHORIZATION) String authorization, + @ApiParam(value = "AID", required = false, hidden = true) @HeaderParam("aid") String aid, + @ApiParam(value = "Application namespace",required = false, hidden = true) @HeaderParam("ns") String ns) throws Exception{ + try { + ResponseBuilder response = MusicUtil.buildVersionResponse(VERSION, minorVersion, patchVersion); + Map<String, Object> resultMap = MusicCore.validateLock(lockId); + if (resultMap.containsKey("Error")) { + logger.error(EELFLoggerDelegate.errorLogger,"", AppMessages.INCORRECTDATA ,ErrorSeverity.CRITICAL, ErrorTypes.GENERALSERVICEERROR); + response.status(Status.BAD_REQUEST); + return response.entity(new JsonResponse(ResultType.FAILURE).setError(String.valueOf(resultMap.get("Error"))).toMap()).build(); + } + String keyspaceName = (String) resultMap.get("keyspace"); + EELFLoggerDelegate.mdcPut("keyspace", "( " + keyspaceName + " ) "); + String lockName = lockId.substring(lockId.indexOf('$')+1, lockId.lastIndexOf('$')); + ReturnType lockLeaseStatus = MusicCore.acquireLockWithLease(lockName, lockId, lockObj.getLeasePeriod()); + if ( lockLeaseStatus.getResult().equals(ResultType.SUCCESS)) { + response.status(Status.OK); + } else { + response.status(Status.BAD_REQUEST); + } + return response.entity(new JsonResponse(lockLeaseStatus.getResult()).setLock(lockName) + .setMessage(lockLeaseStatus.getMessage()) + .setLockLease(String.valueOf(lockObj.getLeasePeriod())).toMap()).build(); + } finally { + EELFLoggerDelegate.mdcRemove("keyspace"); + } + + + } + + + @GET + @Path("/enquire/{lockname}") + @ApiOperation(value = "Get the top of the lock queue", + notes = "Gets the current single lockholder at top of lock queue", + response = Map.class) + @Produces(MediaType.APPLICATION_JSON) + @ApiResponses(value={ + @ApiResponse(code=200, message = "Success",examples = @Example( value = { + @ExampleProperty(mediaType="application/json",value = + "{\"lock\" : {\"lock\" : \"keyspace.table.rowId\"," + + "\"lock-holder\" : \"$tomtest.employees.tom$<integer>\"}}," + + "\"status\" : \"SUCCESS\"}") + })), + @ApiResponse(code=400, message = "Failure",examples = @Example( value = { + @ExampleProperty(mediaType="application/json",value = + "{\"error\" : \"Error Message\"," + + "\"status\" : \"FAILURE\"}") + })) + }) + public Response enquireLock( + @ApiParam(value="Lock Name",required=true) @PathParam("lockname") String lockName, + @ApiParam(value = "Minor Version",required = false) @HeaderParam(XMINORVERSION) String minorVersion, + @ApiParam(value = "Patch Version",required = false) @HeaderParam(XPATCHVERSION) String patchVersion, + @ApiParam(value = "Authorization", required = true) @HeaderParam(MusicUtil.AUTHORIZATION) String authorization, + @ApiParam(value = "AID", required = false, hidden = true) @HeaderParam("aid") String aid, + @ApiParam(value = "Application namespace", + required = false, hidden = true) @HeaderParam("ns") String ns) throws Exception{ + try { + ResponseBuilder response = MusicUtil.buildVersionResponse(VERSION, minorVersion, patchVersion); + Map<String, Object> resultMap = MusicCore.validateLock(lockName); + if (resultMap.containsKey("Error")) { + logger.error(EELFLoggerDelegate.errorLogger,"", AppMessages.INCORRECTDATA ,ErrorSeverity.CRITICAL, ErrorTypes.GENERALSERVICEERROR); + response.status(Status.BAD_REQUEST); + return response.entity(new JsonResponse(ResultType.FAILURE).setError(String.valueOf(resultMap.get("Error"))).toMap()).build(); + } + String keyspaceName = (String) resultMap.get("keyspace"); + EELFLoggerDelegate.mdcPut("keyspace", "( " + keyspaceName + " ) "); + String who = MusicCore.whoseTurnIsIt(lockName); + ResultType status = ResultType.SUCCESS; + String error = ""; + if ( who == null ) { + status = ResultType.FAILURE; + error = "There was a problem getting the lock holder"; + logger.error(EELFLoggerDelegate.errorLogger,"There was a problem getting the lock holder", AppMessages.INCORRECTDATA ,ErrorSeverity.CRITICAL, ErrorTypes.GENERALSERVICEERROR); + return response.status(Status.BAD_REQUEST).entity(new JsonResponse(status).setError(error).setLock(lockName).setLockHolder(who).toMap()).build(); + } + return response.status(Status.OK).entity(new JsonResponse(status).setError(error).setLock(lockName).setLockHolder(who).toMap()).build(); + } finally { + EELFLoggerDelegate.mdcRemove("keyspace"); + } + } + + @GET + @Path("/holders/{lockname}") + @ApiOperation(value = "Get Lock Holders", + notes = "Gets the current Lock Holders.\n" + + "Will return an array of READ Lock References.", + response = Map.class) + @Produces(MediaType.APPLICATION_JSON) + @ApiResponses(value={ + @ApiResponse(code=200, message = "Success",examples = @Example( value = { + @ExampleProperty(mediaType="application/json",value = + "{\"lock\" : {\"lock\" : \"keyspace.table.rowId\"," + + "\"lock-holder\" : [\"$keyspace.table.rowId$<integer1>\",\"$keyspace.table.rowId$<integer2>\"]}}," + + "\"status\" : \"SUCCESS\"}") + })), + @ApiResponse(code=400, message = "Failure",examples = @Example( value = { + @ExampleProperty(mediaType="application/json",value = + "{\"error\" : \"Error message\"," + + "\"status\" : \"FAILURE\"}") + })) + }) + public Response currentLockHolder(@ApiParam(value="Lock Name",required=true) @PathParam("lockname") String lockName, + @ApiParam(value = "Minor Version",required = false) @HeaderParam(XMINORVERSION) String minorVersion, + @ApiParam(value = "Patch Version",required = false) @HeaderParam(XPATCHVERSION) String patchVersion, + @ApiParam(value = "Authorization", required = true) @HeaderParam(MusicUtil.AUTHORIZATION) String authorization, + @ApiParam(value = "AID", required = false, hidden = true) @HeaderParam("aid") String aid, + @ApiParam(value = "Application namespace", + required = false, hidden = true) @HeaderParam("ns") String ns) { + try { + ResponseBuilder response = MusicUtil.buildVersionResponse(VERSION, minorVersion, patchVersion); + Map<String, Object> resultMap = MusicCore.validateLock(lockName); + if (resultMap.containsKey("Error")) { + logger.error(EELFLoggerDelegate.errorLogger, "", AppMessages.INCORRECTDATA, ErrorSeverity.CRITICAL, + ErrorTypes.GENERALSERVICEERROR); + response.status(Status.BAD_REQUEST); + return response.entity( + new JsonResponse(ResultType.FAILURE).setError(String.valueOf(resultMap.get("Error"))).toMap()) + .build(); + } + String keyspaceName = (String) resultMap.get("keyspace"); + List<String> who = MusicCore.getCurrentLockHolders(lockName); + ResultType status = ResultType.SUCCESS; + String error = ""; + if (who == null || who.isEmpty()) { + status = ResultType.FAILURE; + error = (who !=null && who.isEmpty()) ? "No lock holders for the key":"There was a problem getting the lock holder"; + logger.error(EELFLoggerDelegate.errorLogger, "There was a problem getting the lock holder", + AppMessages.INCORRECTDATA, ErrorSeverity.CRITICAL, ErrorTypes.GENERALSERVICEERROR); + return response.status(Status.BAD_REQUEST) + .entity(new JsonResponse(status).setError(error).setLock(lockName).setLockHolder(who).toMap()) + .build(); + } + return response.status(Status.OK) + .entity(new JsonResponse(status).setError(error).setLock(lockName).setLockHolder(who).setisLockHolders(true).toMap()) + .build(); + } finally { + EELFLoggerDelegate.mdcRemove("keyspace"); + } + } + + + @GET + @Path("/{lockname}") + @ApiOperation(value = "Lock State", + notes = "Returns current Lock State and Holder.", + response = Map.class,hidden = true) + @Produces(MediaType.APPLICATION_JSON) + @ApiResponses(value={ + @ApiResponse(code=200, message = "Success",examples = @Example( value = { + @ExampleProperty(mediaType="application/json",value = + "{\"lock\" : {\"lock\" : \"$keyspace.table.rowId$<integer>\"}," + + "\"message\" : \"<integer> is the lock holder for the key\"," + + "\"status\" : \"SUCCESS\"}") + })), + @ApiResponse(code=400, message = "Failure",examples = @Example( value = { + @ExampleProperty(mediaType="application/json",value = + "{\"error\" : \"Unable to aquire lock\"," + + "\"status\" : \"FAILURE\"}") + })) + }) + public Response currentLockState( + @ApiParam(value="Lock Name",required=true) @PathParam("lockname") String lockName, + @ApiParam(value = "Minor Version",required = false) @HeaderParam(XMINORVERSION) String minorVersion, + @ApiParam(value = "Patch Version",required = false) @HeaderParam(XPATCHVERSION) String patchVersion, + @ApiParam(value = "Authorization", required = true) @HeaderParam(MusicUtil.AUTHORIZATION) String authorization, + @ApiParam(value = "AID", required = false, hidden = true) @HeaderParam("aid") String aid, + @ApiParam(value = "Application namespace", + required = false, hidden = true) @HeaderParam("ns") String ns) { + try { + ResponseBuilder response = MusicUtil.buildVersionResponse(VERSION, minorVersion, patchVersion); + Map<String, Object> resultMap = MusicCore.validateLock(lockName); + if (resultMap.containsKey("Error")) { + logger.error(EELFLoggerDelegate.errorLogger,"", AppMessages.INCORRECTDATA ,ErrorSeverity.CRITICAL, ErrorTypes.GENERALSERVICEERROR); + response.status(Status.BAD_REQUEST); + return response.entity(new JsonResponse(ResultType.FAILURE).setError(String.valueOf(resultMap.get("Error"))).toMap()).build(); + } + String keyspaceName = (String) resultMap.get("keyspace"); + EELFLoggerDelegate.mdcPut("keyspace", "( "+keyspaceName+" ) "); + String who = MusicCore.whoseTurnIsIt(lockName); + ResultType status = ResultType.SUCCESS; + String error = ""; + if ( who == null ) { + status = ResultType.FAILURE; + error = "There was a problem getting the lock holder"; + logger.error(EELFLoggerDelegate.errorLogger,"There was a problem getting the lock holder", AppMessages.INCORRECTDATA ,ErrorSeverity.CRITICAL, ErrorTypes.GENERALSERVICEERROR); + return response.status(Status.BAD_REQUEST).entity(new JsonResponse(status).setError(error).setLock(lockName).setLockHolder(who).toMap()).build(); + } + return response.status(Status.OK).entity(new JsonResponse(status).setError(error).setLock(lockName).setLockHolder(who).toMap()).build(); + } finally { + EELFLoggerDelegate.mdcRemove("keyspace"); + } + + } + + /** + * + * deletes the process from the lock queue + * + * @param lockId + * @throws Exception + */ + @DELETE + @Path("/release/{lockreference}") + @ApiOperation(value = "Release Lock", + notes = "Releases the lock from the lock queue.", + response = Map.class) + @Produces(MediaType.APPLICATION_JSON) + @ApiResponses(value={ + @ApiResponse(code=200, message = "Success - UNLOCKED = Lock Removed.",examples = @Example( value = { + @ExampleProperty(mediaType="application/json",value = + "{\"lock\" : {\"lock\" : \"$keyspace.table.rowId$<integer>\"}," + + "\"lock-status\" : \"UNLOCKED\"}," + + "\"status\" : \"SUCCESS\"}") + })), + @ApiResponse(code=400, message = "Failure",examples = @Example( value = { + @ExampleProperty(mediaType="application/json",value = + "{\"error\" : \"Unable to aquire lock\"," + + "\"status\" : \"FAILURE\"}") + })) + }) + public Response unLock(@PathParam("lockreference") String lockId, + @ApiParam(value = "Minor Version",required = false) @HeaderParam(XMINORVERSION) String minorVersion, + @ApiParam(value = "Patch Version",required = false) @HeaderParam(XPATCHVERSION) String patchVersion, + @ApiParam(value = "Authorization", required = true) @HeaderParam(MusicUtil.AUTHORIZATION) String authorization, + @ApiParam(value = "AID", required = false, hidden = true) @HeaderParam("aid") String aid, + @ApiParam(value = "Application namespace", + required = false, hidden = true) @HeaderParam("ns") String ns) throws Exception{ + try { + ResponseBuilder response = MusicUtil.buildVersionResponse(VERSION, minorVersion, patchVersion); + Map<String, Object> resultMap = MusicCore.validateLock(lockId); + if (resultMap.containsKey("Error")) { + logger.error(EELFLoggerDelegate.errorLogger,"", AppMessages.INCORRECTDATA ,ErrorSeverity.CRITICAL, ErrorTypes.GENERALSERVICEERROR); + response.status(Status.BAD_REQUEST); + return response.entity(new JsonResponse(ResultType.FAILURE).setError(String.valueOf(resultMap.get("Error"))).toMap()).build(); + } + + String keyspaceName = (String) resultMap.get("keyspace"); + EELFLoggerDelegate.mdcPut("keyspace", "( "+keyspaceName+" ) "); + boolean voluntaryRelease = true; + MusicLockState mls = MusicCore.releaseLock(lockId,voluntaryRelease); + if(mls.getErrorMessage() != null) { + resultMap.put(ResultType.EXCEPTION.getResult(), mls.getErrorMessage()); + logger.error(EELFLoggerDelegate.errorLogger,"", AppMessages.INCORRECTDATA ,ErrorSeverity.CRITICAL, ErrorTypes.GENERALSERVICEERROR); + return response.status(Status.BAD_REQUEST).entity(resultMap).build(); + } + Map<String,Object> returnMap = null; + if (mls.getLockStatus() == MusicLockState.LockStatus.UNLOCKED) { + returnMap = new JsonResponse(ResultType.SUCCESS).setLock(lockId) + .setLockStatus(mls.getLockStatus()).toMap(); + response.status(Status.OK); + } + if (mls.getLockStatus() == MusicLockState.LockStatus.LOCKED) { + logger.error(EELFLoggerDelegate.errorLogger,"", AppMessages.LOCKINGERROR ,ErrorSeverity.CRITICAL, ErrorTypes.GENERALSERVICEERROR); + returnMap = new JsonResponse(ResultType.FAILURE).setLock(lockId) + .setLockStatus(mls.getLockStatus()).toMap(); + response.status(Status.BAD_REQUEST); + } + return response.entity(returnMap).build(); + } finally { + EELFLoggerDelegate.mdcRemove("keyspace"); + } + } + + /** + * + * @param lockName + * @throws Exception + */ + @Deprecated + @DELETE + @Path("/delete/{lockname}") + @ApiOperation( + hidden = true, + value = "-DEPRECATED- Delete Lock", response = Map.class, + notes = "-DEPRECATED- Delete the lock.") + @Produces(MediaType.APPLICATION_JSON) + @ApiResponses(value={ + @ApiResponse(code=200, message = "Success",examples = @Example( value = { + @ExampleProperty(mediaType="application/json",value = + "{\"status\" : \"SUCCESS\"}") + })), + @ApiResponse(code=400, message = "Failure",examples = @Example( value = { + @ExampleProperty(mediaType="application/json",value = + "{\"error\" : \"Error Message if any\"," + + "\"status\" : \"FAILURE\"}") + })) + }) + public Response deleteLock(@PathParam("lockname") String lockName, + @ApiParam(value = "Minor Version",required = false) @HeaderParam(XMINORVERSION) String minorVersion, + @ApiParam(value = "Patch Version",required = false) @HeaderParam(XPATCHVERSION) String patchVersion, + @ApiParam(value = "AID", required = false, hidden = true) @HeaderParam("aid") String aid, + @ApiParam(value = "Authorization", required = true) @HeaderParam(MusicUtil.AUTHORIZATION) String authorization, + @ApiParam(value = "Application namespace", + required = false, hidden = true) @HeaderParam("ns") String ns) throws Exception{ + try { + ResponseBuilder response = MusicUtil.buildVersionResponse(VERSION, minorVersion, patchVersion); + Map<String, Object> resultMap = MusicCore.validateLock(lockName); + if (resultMap.containsKey("Error")) { + logger.error(EELFLoggerDelegate.errorLogger,"", AppMessages.UNKNOWNERROR ,ErrorSeverity.CRITICAL, ErrorTypes.GENERALSERVICEERROR); + response.status(Status.BAD_REQUEST); + return response.entity(new JsonResponse(ResultType.FAILURE).setError(String.valueOf(resultMap.get("Error"))).toMap()).build(); + } + + String keyspaceName = (String) resultMap.get("keyspace"); + EELFLoggerDelegate.mdcPut("keyspace", "( " + keyspaceName + " ) "); + try{ + MusicCore.destroyLockRef(lockName); + }catch (Exception e) { + logger.error(EELFLoggerDelegate.errorLogger, e); + return response.status(Status.BAD_REQUEST).entity(new JsonResponse(ResultType.FAILURE).setError(e.getMessage()).toMap()).build(); + } + return response.status(Status.OK).entity(new JsonResponse(ResultType.SUCCESS).toMap()).build(); + } finally { + EELFLoggerDelegate.mdcRemove("keyspace"); + } + } + + + /** + * Puts the requesting process in the q for this lock. The corresponding + * node will be created if it did not already exist + * + * @param lockName + * @return + * @throws Exception + */ + @POST + @Path("/promote/{lockname}") + @ApiOperation(value = "Attempt to promote the lock for a single row.", + response = Map.class) + @Consumes(MediaType.APPLICATION_JSON) + @Produces(MediaType.APPLICATION_JSON) + @ApiResponses(value={ + @ApiResponse(code=200, message = "Success",examples = @Example( value = { + @ExampleProperty(mediaType="application/json",value = + "\"status\" : \"SUCCESS\"}") + })), + @ApiResponse(code=400, message = "Failure",examples = @Example( value = { + @ExampleProperty(mediaType="application/json",value = + "{\"error\" : \"Unable to promote lock\"," + + "\"status\" : \"FAILURE\"}") + })) + }) + public Response promoteLock( + @ApiParam(value="Lock Id",required=true) @PathParam("lockId") String lockId, + @ApiParam(value = "Minor Version",required = false) @HeaderParam(XMINORVERSION) String minorVersion, + @ApiParam(value = "Patch Version",required = false) @HeaderParam(XPATCHVERSION) String patchVersion, + @ApiParam(value = "Authorization", required = true) @HeaderParam(MusicUtil.AUTHORIZATION) String authorization) + throws Exception { + try { + ResponseBuilder response = MusicUtil.buildVersionResponse(VERSION, minorVersion, patchVersion); + Map<String, Object> resultMap = MusicCore.validateLock(lockId); + if (resultMap.containsKey("Error")) { + logger.error(EELFLoggerDelegate.errorLogger,"", AppMessages.INCORRECTDATA ,ErrorSeverity.CRITICAL, ErrorTypes.GENERALSERVICEERROR); + response.status(Status.BAD_REQUEST); + return response.entity(new JsonResponse(ResultType.FAILURE).setError(String.valueOf(resultMap.get("Error"))).toMap()).build(); + } + + String keyspaceName = (String) resultMap.get("keyspace"); + EELFLoggerDelegate.mdcPut("keyspace", "( " + keyspaceName + " ) "); + + try { + ReturnType lockStatus = MusicCore.promoteLock(lockId); + if ( lockStatus.getResult().equals(ResultType.SUCCESS)) { + response.status(Status.OK); + } else { + response.status(Status.BAD_REQUEST); + } + return response.entity(new JsonResponse(lockStatus.getResult()).setLock(lockId).setMessage(lockStatus.getMessage()).toMap()).build(); + } catch (Exception e) { + logger.error(EELFLoggerDelegate.errorLogger,AppMessages.INVALIDLOCK + lockId, ErrorSeverity.CRITICAL, + ErrorTypes.LOCKINGERROR, e); + return response.status(Status.BAD_REQUEST).entity(new JsonResponse(ResultType.FAILURE).setError("Unable to promote lock").toMap()).build(); + } + } finally { + EELFLoggerDelegate.mdcRemove("keyspace"); + } + } +} diff --git a/music-rest/src/main/java/org/onap/music/rest/RestMusicQAPI.java b/music-rest/src/main/java/org/onap/music/rest/RestMusicQAPI.java new file mode 100755 index 00000000..4def0e45 --- /dev/null +++ b/music-rest/src/main/java/org/onap/music/rest/RestMusicQAPI.java @@ -0,0 +1,441 @@ +/* + * ============LICENSE_START========================================== + * org.onap.music + * =================================================================== + * Copyright (c) 2017 AT&T Intellectual Property + * =================================================================== + * Modifications Copyright (C) 2019 IBM. + * Modifications Copyright (c) 2019 Samsung + * =================================================================== + * 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. + * + * ============LICENSE_END============================================= + * ==================================================================== + */ + +package org.onap.music.rest; + +import java.util.Map; +import javax.ws.rs.Consumes; +import javax.ws.rs.DELETE; +import javax.ws.rs.GET; +import javax.ws.rs.HeaderParam; +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.Context; +import javax.ws.rs.core.MediaType; +import javax.ws.rs.core.UriInfo; +import javax.ws.rs.core.Response; +import javax.ws.rs.core.Response.ResponseBuilder; +import javax.ws.rs.core.Response.Status; +import org.onap.music.datastore.jsonobjects.JsonDelete; +import org.onap.music.datastore.jsonobjects.JsonInsert; +import org.onap.music.datastore.jsonobjects.JsonTable; +import org.onap.music.datastore.jsonobjects.JsonUpdate; +import org.onap.music.eelf.logging.EELFLoggerDelegate; + +import org.onap.music.eelf.logging.format.AppMessages; +import org.onap.music.eelf.logging.format.ErrorSeverity; +import org.onap.music.eelf.logging.format.ErrorTypes; +import org.apache.commons.lang3.StringUtils; +import org.onap.music.datastore.MusicDataStoreHandle; +import org.onap.music.datastore.PreparedQueryObject; +import com.datastax.driver.core.ResultSet; +import org.onap.music.exceptions.MusicQueryException; +import org.onap.music.exceptions.MusicServiceException; +import org.onap.music.main.MusicCore; +import org.onap.music.main.MusicUtil; +import org.onap.music.main.ResultType; +import org.onap.music.response.jsonobjects.JsonResponse; + +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiOperation; +import io.swagger.annotations.ApiParam; + +@Path("/v2/priorityq/") +@Api(value = "Q Api",hidden = true) +public class RestMusicQAPI { + + private EELFLoggerDelegate logger = EELFLoggerDelegate.getLogger(RestMusicQAPI.class); + + + /** + * + * @param tableObj + * @param keyspace + * @param tablename + * @throws Exception + */ + + @POST + @Path("/keyspaces/{keyspace}/{qname}") // qname same as tablename + @ApiOperation(value = "Create Q", response = String.class) + @Consumes(MediaType.APPLICATION_JSON) + @Produces(MediaType.APPLICATION_JSON) + public Response createQ( + @ApiParam(value = "Major Version", required = true) @PathParam("version") String version, + @ApiParam(value = "Minor Version", required = false) @HeaderParam("X-minorVersion") String minorVersion, + @ApiParam(value = "Patch Version", required = false) @HeaderParam("X-patchVersion") String patchVersion, + @ApiParam(value = "AID", required = false) @HeaderParam("aid") String aid, + @ApiParam(value = "Application namespace", required = true) @HeaderParam("ns") String ns, + @ApiParam(value = "Authorization", required = true) @HeaderParam(MusicUtil.AUTHORIZATION) String authorization, + JsonTable tableObj, + @ApiParam(value = "Key Space", required = true) @PathParam("keyspace") String keyspace, + @ApiParam(value = "Table Name", required = true) @PathParam("qname") String tablename) throws Exception { + ResponseBuilder response = MusicUtil.buildVersionResponse(version, minorVersion, patchVersion); + + Map<String, String> fields = tableObj.getFields(); + if (fields == null) { + logger.error(EELFLoggerDelegate.errorLogger, "", AppMessages.MISSINGDATA, + ErrorSeverity.CRITICAL, ErrorTypes.DATAERROR); + return response.status(Status.BAD_REQUEST) + .entity(new JsonResponse(ResultType.FAILURE) + .setError("CreateQ/Required table fields are empty or not set").toMap()) + .build(); + } + + String primaryKey = tableObj.getPrimaryKey(); + String partitionKey = tableObj.getPartitionKey(); + String clusteringKey = tableObj.getClusteringKey(); + String filteringKey = tableObj.getFilteringKey(); + String clusteringOrder = tableObj.getClusteringOrder(); + + if(primaryKey == null) { + primaryKey = tableObj.getFields().get("PRIMARY KEY"); + } + + if ((primaryKey == null) && (partitionKey == null)) { + logger.error(EELFLoggerDelegate.errorLogger, "", AppMessages.MISSINGDATA, + ErrorSeverity.CRITICAL, ErrorTypes.DATAERROR); + return response.status(Status.BAD_REQUEST) + .entity(new JsonResponse(ResultType.FAILURE) + .setError("CreateQ: Partition key cannot be empty").toMap()) + .build(); + } + + if ((primaryKey == null) && (clusteringKey == null)) { + logger.error(EELFLoggerDelegate.errorLogger, "", AppMessages.MISSINGDATA, + ErrorSeverity.CRITICAL, ErrorTypes.DATAERROR); + return response.status(Status.BAD_REQUEST) + .entity(new JsonResponse(ResultType.FAILURE) + .setError("CreateQ: Clustering key cannot be empty").toMap()) + .build(); + } + + if (clusteringOrder == null) { + logger.error(EELFLoggerDelegate.errorLogger, "", AppMessages.MISSINGDATA, + ErrorSeverity.CRITICAL, ErrorTypes.DATAERROR); + return response.status(Status.BAD_REQUEST) + .entity(new JsonResponse(ResultType.FAILURE) + .setError("CreateQ: Clustering Order cannot be empty").toMap()) + .build(); + } + + if ((primaryKey!=null) && (partitionKey == null)) { + primaryKey = primaryKey.trim(); + int count1 = StringUtils.countMatches(primaryKey,')'); + int count2 = StringUtils.countMatches(primaryKey,'('); + if (count1 != count2) { + return response.status(Status.BAD_REQUEST).entity(new JsonResponse(ResultType.FAILURE) + .setError("CreateQ Error: primary key '(' and ')' do not match, primary key=" + primaryKey) + .toMap()).build(); + } + + if ( primaryKey.indexOf('(') == -1 || ( count2 == 1 && (primaryKey.lastIndexOf(')') +1) == primaryKey.length() ) ) { + if (primaryKey.contains(",") ) { + partitionKey= primaryKey.substring(0,primaryKey.indexOf(',')); + partitionKey=partitionKey.replaceAll("[\\(]+",""); + clusteringKey=primaryKey.substring(primaryKey.indexOf(',')+1); // make sure index + clusteringKey=clusteringKey.replaceAll("[)]+", ""); + } else { + partitionKey=primaryKey; + partitionKey=partitionKey.replaceAll("[\\)]+",""); + partitionKey=partitionKey.replaceAll("[\\(]+",""); + clusteringKey=""; + } + } else { + partitionKey= primaryKey.substring(0,primaryKey.indexOf(')')); + partitionKey=partitionKey.replaceAll("[\\(]+",""); + partitionKey = partitionKey.trim(); + clusteringKey= primaryKey.substring(primaryKey.indexOf(')')); + clusteringKey=clusteringKey.replaceAll("[\\(]+",""); + clusteringKey=clusteringKey.replaceAll("[\\)]+",""); + clusteringKey = clusteringKey.trim(); + if (clusteringKey.indexOf(',') == 0) clusteringKey=clusteringKey.substring(1); + clusteringKey = clusteringKey.trim(); + if (clusteringKey.equals(",") ) clusteringKey=""; // print error if needed ( ... ),) + } + } + + if (partitionKey.trim().isEmpty()) { + logger.error(EELFLoggerDelegate.errorLogger, "", AppMessages.MISSINGDATA, + ErrorSeverity.CRITICAL, ErrorTypes.DATAERROR); + return response.status(Status.BAD_REQUEST) + .entity(new JsonResponse(ResultType.FAILURE) + .setError("CreateQ: Partition key cannot be empty").toMap()) + .build(); + } + + if (clusteringKey.trim().isEmpty()) { + logger.error(EELFLoggerDelegate.errorLogger, "", AppMessages.MISSINGDATA, + ErrorSeverity.CRITICAL, ErrorTypes.DATAERROR); + return response.status(Status.BAD_REQUEST) + .entity(new JsonResponse(ResultType.FAILURE) + .setError("CreateQ: Clustering key cannot be empty").toMap()) + .build(); + } + + if((filteringKey != null) && (filteringKey.equalsIgnoreCase(partitionKey))) { + logger.error(EELFLoggerDelegate.errorLogger, "", AppMessages.MISSINGDATA, + ErrorSeverity.CRITICAL, ErrorTypes.DATAERROR); + return response.status(Status.BAD_REQUEST) + .entity(new JsonResponse(ResultType.FAILURE) + .setError("CreateQ: Filtering key cannot be same as Partition Key").toMap()) + .build(); + } + + return new RestMusicDataAPI().createTable(version, minorVersion, patchVersion, aid, ns, authorization, tableObj, keyspace, tablename); + } + + /** + * + * @param insObj + * @param keyspace + * @param tablename + * @throws Exception + */ + @POST + @Path("/keyspaces/{keyspace}/{qname}/rows") + @ApiOperation(value = "", response = Void.class) + @Consumes(MediaType.APPLICATION_JSON) + @Produces(MediaType.APPLICATION_JSON) + // public Map<String, Object> insertIntoQ( + public Response insertIntoQ( + @ApiParam(value = "Major Version", required = true) @PathParam("version") String version, + @ApiParam(value = "Minor Version", required = false) @HeaderParam("X-minorVersion") String minorVersion, + @ApiParam(value = "Patch Version", required = false) @HeaderParam("X-patchVersion") String patchVersion, + @ApiParam(value = "AID", required = false) @HeaderParam("aid") String aid, + @ApiParam(value = "Application namespace", required = true) @HeaderParam("ns") String ns, + @ApiParam(value = "Authorization", required = true) @HeaderParam(MusicUtil.AUTHORIZATION) String authorization, + JsonInsert insObj, + @ApiParam(value = "Key Space", required = true) @PathParam("keyspace") String keyspace, + @ApiParam(value = "Table Name", required = true) @PathParam("qname") String tablename) { + + ResponseBuilder response = MusicUtil.buildVersionResponse(version, minorVersion, patchVersion); + if (insObj.getValues().isEmpty()) { + logger.error(EELFLoggerDelegate.errorLogger, "", AppMessages.MISSINGDATA, + ErrorSeverity.CRITICAL, ErrorTypes.DATAERROR); + return response.status(Status.BAD_REQUEST).entity(new JsonResponse(ResultType.FAILURE) + .setError("Required HTTP Request body is missing.").toMap()).build(); + } + return new RestMusicDataAPI().insertIntoTable(version, minorVersion, patchVersion, aid, ns, + authorization, insObj, keyspace, tablename); + } + + /** + * + * @param updateObj + * @param keyspace + * @param tablename + * @param info + * @return + * @throws Exception + */ + @PUT + @Path("/keyspaces/{keyspace}/{qname}/rows") + @ApiOperation(value = "updateQ", response = String.class) + @Consumes(MediaType.APPLICATION_JSON) + @Produces(MediaType.APPLICATION_JSON) + public Response updateQ( + @ApiParam(value = "Major Version", required = true) @PathParam("version") String version, + @ApiParam(value = "Minor Version", required = false) @HeaderParam("X-minorVersion") String minorVersion, + @ApiParam(value = "Patch Version", required = false) @HeaderParam("X-patchVersion") String patchVersion, + @ApiParam(value = "AID", required = false) @HeaderParam("aid") String aid, + @ApiParam(value = "Application namespace", required = true) @HeaderParam("ns") String ns, + @ApiParam(value = "Authorization", required = true) @HeaderParam(MusicUtil.AUTHORIZATION) String authorization, + JsonUpdate updateObj, + @ApiParam(value = "Key Space", required = true) @PathParam("keyspace") String keyspace, + @ApiParam(value = "Table Name", required = true) @PathParam("qname") String tablename, + @Context UriInfo info) throws MusicServiceException, MusicQueryException { + + ResponseBuilder response = MusicUtil.buildVersionResponse(version, minorVersion, patchVersion); + if (updateObj.getValues().isEmpty()) { + logger.error(EELFLoggerDelegate.errorLogger, "", AppMessages.MISSINGDATA, + ErrorSeverity.CRITICAL, ErrorTypes.DATAERROR); + return response.status(Status.BAD_REQUEST) + .entity(new JsonResponse(ResultType.FAILURE) + .setError("Required HTTP Request body is missing. JsonUpdate updateObj.getValues() is empty. ") + .toMap()) + .build(); + } + return new RestMusicDataAPI().updateTable(version, minorVersion, patchVersion, aid, ns, + authorization,updateObj, keyspace, tablename, info); + } + + /** + * + * @param delObj + * @param keyspace + * @param tablename + * @param info + * + * @return + * @throws Exception + */ + + @DELETE + @Path("/keyspaces/{keyspace}/{qname}/rows") + @ApiOperation(value = "deleteQ", response = String.class) + @Consumes(MediaType.APPLICATION_JSON) + @Produces(MediaType.APPLICATION_JSON) + public Response deleteFromQ( + @ApiParam(value = "Major Version", required = true) @PathParam("version") String version, + @ApiParam(value = "Minor Version", required = false) @HeaderParam("X-minorVersion") String minorVersion, + @ApiParam(value = "Patch Version", required = false) @HeaderParam("X-patchVersion") String patchVersion, + @ApiParam(value = "AID", required = false) @HeaderParam("aid") String aid, + @ApiParam(value = "Application namespace", required = true) @HeaderParam("ns") String ns, + @ApiParam(value = "Authorization", required = true) @HeaderParam(MusicUtil.AUTHORIZATION) String authorization, + JsonDelete delObj, + @ApiParam(value = "Key Space", required = true) @PathParam("keyspace") String keyspace, + @ApiParam(value = "Table Name", required = true) @PathParam("qname") String tablename, + @Context UriInfo info) throws MusicServiceException, MusicQueryException { + // added checking as per RestMusicDataAPI + ResponseBuilder response = MusicUtil.buildVersionResponse(version, minorVersion, patchVersion); + if (delObj == null) { + logger.error(EELFLoggerDelegate.errorLogger, "", AppMessages.MISSINGDATA, + ErrorSeverity.CRITICAL, ErrorTypes.DATAERROR); + return response.status(Status.BAD_REQUEST).entity(new JsonResponse(ResultType.FAILURE) + .setError("deleteFromQ JsonDelete delObjis empty").toMap()).build(); + } + + return new RestMusicDataAPI().deleteFromTable(version, minorVersion, patchVersion, aid, ns, + authorization, delObj, keyspace, tablename, info); + } + + /** + * + * @param keyspace + * @param tablename + * @param info + * @return + * @throws Exception + */ + @GET + @Path("/keyspaces/{keyspace}/{qname}/peek") + @ApiOperation(value = "", response = Map.class) + @Produces(MediaType.APPLICATION_JSON) + //public Map<String, HashMap<String, Object>> peek( + public Response peek( + @ApiParam(value = "Major Version", required = true) @PathParam("version") String version, + @ApiParam(value = "Minor Version", required = false) @HeaderParam("X-minorVersion") String minorVersion, + @ApiParam(value = "Patch Version", required = false) @HeaderParam("X-patchVersion") String patchVersion, + @ApiParam(value = "AID", required = false) @HeaderParam("aid") String aid, + @ApiParam(value = "Application namespace", required = true) @HeaderParam("ns") String ns, + @ApiParam(value = "Authorization", required = true) @HeaderParam(MusicUtil.AUTHORIZATION) String authorization, + @ApiParam(value = "Key Space", required = true) @PathParam("keyspace") String keyspace, + @ApiParam(value = "Table Name", required = true) @PathParam("qname") String tablename, + @Context UriInfo info) { + int limit =1; //peek must return just the top row + // Map<String ,String> auth = new HashMap<>(); + // String userId =auth.get(MusicUtil.USERID); + // String password =auth.get(MusicUtil.PASSWORD); + ResponseBuilder response = MusicUtil.buildVersionResponse(version, minorVersion, patchVersion); + + PreparedQueryObject queryObject = new PreparedQueryObject(); + if (info.getQueryParameters() == null ) { //|| info.getQueryParameters().isEmpty()) + queryObject.appendQueryString( + "SELECT * FROM " + keyspace + "." + tablename + " LIMIT " + limit + ";"); + } else { + try { + queryObject = new RestMusicDataAPI().selectSpecificQuery(keyspace, tablename, info, limit); + } catch (MusicServiceException ex) { + logger.error(EELFLoggerDelegate.errorLogger, "", AppMessages.UNKNOWNERROR, + ErrorSeverity.WARN, ErrorTypes.GENERALSERVICEERROR, ex); + return response.status(Status.BAD_REQUEST) + .entity(new JsonResponse(ResultType.FAILURE).setError(ex.getMessage()).toMap()) + .build(); + } + } + + try { + ResultSet results = MusicCore.get(queryObject); + return response.status(Status.OK).entity(new JsonResponse(ResultType.SUCCESS) + .setDataResult(MusicDataStoreHandle.marshallResults(results)).toMap()).build(); + } catch (MusicServiceException ex) { + logger.error(EELFLoggerDelegate.errorLogger, "", AppMessages.UNKNOWNERROR, + ErrorSeverity.ERROR, ErrorTypes.MUSICSERVICEERROR, ex); + return response.status(Status.BAD_REQUEST) + .entity(new JsonResponse(ResultType.FAILURE).setError(ex.getMessage()).toMap()) + .build(); + } + } + + /** + * + * + * @param keyspace + * @param tablename + * @param info + * @return + * @throws Exception + */ + @GET + @Path("/keyspaces/{keyspace}/{qname}/filter") + @ApiOperation(value = "filter", response = Map.class) + @Produces(MediaType.APPLICATION_JSON) + // public Map<String, HashMap<String, Object>> filter( + public Response filter( + @ApiParam(value = "Major Version", required = true) @PathParam("version") String version, + @ApiParam(value = "Minor Version", required = false) @HeaderParam("X-minorVersion") String minorVersion, + @ApiParam(value = "Patch Version", required = false) @HeaderParam("X-patchVersion") String patchVersion, + @ApiParam(value = "AID", required = false) @HeaderParam("aid") String aid, + @ApiParam(value = "Application namespace", required = true) @HeaderParam("ns") String ns, + @ApiParam(value = "Authorization", required = true) @HeaderParam(MusicUtil.AUTHORIZATION) String authorization, + @ApiParam(value = "Key Space", required = true) @PathParam("keyspace") String keyspace, + @ApiParam(value = "Table Name", required = true) @PathParam("qname") String tablename, + @Context UriInfo info) throws Exception { + + return new RestMusicDataAPI().selectWithCritical(version, minorVersion, patchVersion, aid, ns, authorization,null, keyspace, tablename, info);// , limit) + + } + + /** + * + * @param tabObj + * @param keyspace + * @param tablename + * @throws Exception + */ + @DELETE + @ApiOperation(value = "DropQ", response = String.class) + @Path("/keyspaces/{keyspace}/{qname}") + @Produces(MediaType.APPLICATION_JSON) + public Response dropQ( + @ApiParam(value = "Major Version", required = true) @PathParam("version") String version, + @ApiParam(value = "Minor Version", + required = false) @HeaderParam("X-minorVersion") String minorVersion, + @ApiParam(value = "Patch Version", + required = false) @HeaderParam("X-patchVersion") String patchVersion, + @ApiParam(value = "AID", required = false) @HeaderParam("aid") String aid, + @ApiParam(value = "Application namespace", required = true) @HeaderParam("ns") String ns, + @ApiParam(value = "Authorization", required = true) @HeaderParam(MusicUtil.AUTHORIZATION) String authorization, + @ApiParam(value = "Key Space", required = true) @PathParam("keyspace") String keyspace, + @ApiParam(value = "Table Name", required = true) @PathParam("qname") String tablename) throws Exception { + + return new RestMusicDataAPI().dropTable(version, minorVersion, patchVersion, aid, ns, authorization, keyspace, tablename); + + } +} diff --git a/music-rest/src/main/java/org/onap/music/rest/RestMusicTestAPI.java b/music-rest/src/main/java/org/onap/music/rest/RestMusicTestAPI.java new file mode 100644 index 00000000..c1c04b09 --- /dev/null +++ b/music-rest/src/main/java/org/onap/music/rest/RestMusicTestAPI.java @@ -0,0 +1,70 @@ +/* + * ============LICENSE_START========================================== + * org.onap.music + * =================================================================== + * Copyright (c) 2017 AT&T Intellectual Property + * =================================================================== + * 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. + * + * ============LICENSE_END============================================= + * ==================================================================== + */ + +package org.onap.music.rest; + +import java.util.HashMap; +import java.util.Map; + +import javax.servlet.http.HttpServletResponse; +import javax.ws.rs.GET; +import javax.ws.rs.Path; +import javax.ws.rs.Produces; +import javax.ws.rs.core.Context; +import javax.ws.rs.core.MediaType; + +import org.onap.music.eelf.logging.EELFLoggerDelegate; +import org.onap.music.main.MusicUtil; + +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiOperation; + + +@Path("/v{version: [0-9]+}/test") +@Api(value="Test Api") +public class RestMusicTestAPI { + + @SuppressWarnings("unused") + private EELFLoggerDelegate logger =EELFLoggerDelegate.getLogger(RestMusicTestAPI.class); + + /** + * Returns a test JSON. This will confirm that REST is working. + * @return + */ + @GET + @Path("/") + @ApiOperation(value = "Get Test", response = Map.class) + @Produces(MediaType.APPLICATION_JSON) + public Map<String, HashMap<String, String>> simpleTests( + @Context HttpServletResponse response) { + response.addHeader("X-latestVersion",MusicUtil.getVersion()); + Map<String, HashMap<String, String>> testMap = new HashMap<>(); + for(int i=0; i < 3; i++){ + HashMap<String, String> innerMap = new HashMap<>(); + innerMap.put("Music Version",MusicUtil.getVersion()); + innerMap.put("Music Build",MusicUtil.getBuild()); + innerMap.put(i+1+"", i+2+""); + testMap.put(i+"", innerMap); + } + return testMap; + } +} diff --git a/music-rest/src/main/java/org/onap/music/rest/RestMusicVersionAPI.java b/music-rest/src/main/java/org/onap/music/rest/RestMusicVersionAPI.java new file mode 100644 index 00000000..8c86152e --- /dev/null +++ b/music-rest/src/main/java/org/onap/music/rest/RestMusicVersionAPI.java @@ -0,0 +1,82 @@ +/* + * ============LICENSE_START========================================== + * org.onap.music + * =================================================================== + * Copyright (c) 2017 AT&T Intellectual Property + * + * Modifications Copyright (C) 2019 IBM. + * =================================================================== + * 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. + * + * ============LICENSE_END============================================= + * ==================================================================== + */ + +package org.onap.music.rest; + +import java.util.Map; + +import javax.servlet.http.HttpServletResponse; +import javax.ws.rs.GET; +import javax.ws.rs.Path; +import javax.ws.rs.Produces; +import javax.ws.rs.core.Context; +import javax.ws.rs.core.MediaType; + +import org.onap.music.response.jsonobjects.JsonResponse; +import org.onap.music.eelf.logging.EELFLoggerDelegate; +import org.onap.music.main.MusicUtil; +import org.onap.music.main.ResultType; + +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiOperation; + + +@Path("/v{version: [0-9]+}/version") +@Api(value="Version Api") +public class RestMusicVersionAPI { + + private EELFLoggerDelegate logger = EELFLoggerDelegate.getLogger(RestMusicVersionAPI.class); + private static final String MUSIC_KEY = "MUSIC:"; + /** + * Get the version of MUSIC. + * @return + */ + @GET + @ApiOperation(value = "Get Version", response = Map.class) + @Produces(MediaType.APPLICATION_JSON) + public Map<String,Object> version(@Context HttpServletResponse response) { + logger.info("Replying to request for MUSIC version with MUSIC:" + MusicUtil.getVersion()); + response.addHeader("X-latestVersion",MusicUtil.getVersion()); + return new JsonResponse(ResultType.SUCCESS). + setMusicVersion(MUSIC_KEY + MusicUtil.getVersion()).toMap(); + } + + /** + * Get the version of MUSIC. + * @return + */ + @GET + @Path("/build") + @ApiOperation(value = "Get Version", response = Map.class) + @Produces(MediaType.APPLICATION_JSON) + public Map<String,Object> build(@Context HttpServletResponse response) { + logger.info("Replying to request for MUSIC build with MUSIC:" + MusicUtil.getBuild()); + response.addHeader("X-latestVersion",MusicUtil.getVersion()); + return new JsonResponse(ResultType.SUCCESS) + .setMusicBuild(MUSIC_KEY + MusicUtil.getBuild()) + .setMusicVersion(MUSIC_KEY + MusicUtil.getVersion()).toMap(); + } + + +}
\ No newline at end of file |