diff --git a/config/alfresco/public-rest-context.xml b/config/alfresco/public-rest-context.xml index bc21b1d21b..4d3b6227ea 100644 --- a/config/alfresco/public-rest-context.xml +++ b/config/alfresco/public-rest-context.xml @@ -861,6 +861,16 @@ + + + + + + + + + diff --git a/config/alfresco/templates/publicapi/org/alfresco/api/DiscoveryApiWebscript.get.desc.xml b/config/alfresco/templates/publicapi/org/alfresco/api/DiscoveryApiWebscript.get.desc.xml new file mode 100644 index 0000000000..bcadf83ba9 --- /dev/null +++ b/config/alfresco/templates/publicapi/org/alfresco/api/DiscoveryApiWebscript.get.desc.xml @@ -0,0 +1,10 @@ + + + Handles GET for the Discovery api + Returns repository information + /discovery + user + required + argument + public_api + \ No newline at end of file diff --git a/source/java/org/alfresco/rest/api/PublicApiTenantWebScriptServletRequest.java b/source/java/org/alfresco/rest/api/PublicApiTenantWebScriptServletRequest.java index 35b6c5adfd..c264769ce7 100644 --- a/source/java/org/alfresco/rest/api/PublicApiTenantWebScriptServletRequest.java +++ b/source/java/org/alfresco/rest/api/PublicApiTenantWebScriptServletRequest.java @@ -1,28 +1,28 @@ -/* - * #%L - * Alfresco Remote API - * %% - * Copyright (C) 2005 - 2016 Alfresco Software Limited - * %% - * This file is part of the Alfresco software. - * If the software was purchased under a paid Alfresco license, the terms of - * the paid license agreement will prevail. Otherwise, the software is - * provided under the following open source license terms: - * - * Alfresco is free software: you can redistribute it and/or modify - * it under the terms of the GNU Lesser General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Alfresco is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with Alfresco. If not, see . - * #L% - */ +/* + * #%L + * Alfresco Remote API + * %% + * Copyright (C) 2005 - 2016 Alfresco Software Limited + * %% + * This file is part of the Alfresco software. + * If the software was purchased under a paid Alfresco license, the terms of + * the paid license agreement will prevail. Otherwise, the software is + * provided under the following open source license terms: + * + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + * #L% + */ package org.alfresco.rest.api; import javax.servlet.http.HttpServletRequest; @@ -35,42 +35,49 @@ import org.springframework.extensions.webscripts.Runtime; public class PublicApiTenantWebScriptServletRequest extends TenantWebScriptServletRequest { - public static final String NETWORKS_PATH = "networks"; - public static final String NETWORK_PATH = "network"; - - public PublicApiTenantWebScriptServletRequest(Runtime container, HttpServletRequest req, Match serviceMatch, ServerProperties serverProperties) - { - super(container, req, serviceMatch, serverProperties); - } + public static final String NETWORKS_PATH = "networks"; + public static final String NETWORK_PATH = "network"; - @Override + public PublicApiTenantWebScriptServletRequest(Runtime container, HttpServletRequest req, Match serviceMatch, ServerProperties serverProperties) + { + super(container, req, serviceMatch, serverProperties); + } + + @Override protected void parse() { String realPathInfo = getRealPathInfo(); if(realPathInfo.equals("") || realPathInfo.equals("/")) { - // no tenant - "index" request - tenant = TenantUtil.DEFAULT_TENANT; + // no tenant - "index" request + tenant = TenantUtil.DEFAULT_TENANT; pathInfo = NETWORKS_PATH; } + else if (realPathInfo.equals("/discovery")) + { + // The '/discovery' API is special and doesn't need network info, however, + // we set the tenant to default, to satisfy PublicApiTenantAuthentication logic. + tenant = TenantUtil.DEFAULT_TENANT; + pathInfo = realPathInfo; + } else { - // optimisation - don't need to lowercase the whole path - if(realPathInfo.substring(0, 5).toLowerCase().equals("/cmis")) - { - // cmis service document, pass through as is and set tenant to "-default-". - tenant = TenantUtil.DEFAULT_TENANT; + // optimisation - don't need to lowercase the whole path + if(realPathInfo.substring(0, 5).toLowerCase().equals("/cmis")) + { + // cmis service document, pass through as is and set tenant to "-default-". + tenant = TenantUtil.DEFAULT_TENANT; pathInfo = realPathInfo; - } - else - { + } + else + { int idx = realPathInfo.indexOf('/', 1); - // remove tenant - tenant = realPathInfo.substring(1, idx == -1 ? realPathInfo.length() : idx); - pathInfo = realPathInfo.substring(tenant.length() + 1); - } + // remove tenant + tenant = realPathInfo.substring(1, idx == -1 ? realPathInfo.length() : idx); + pathInfo = realPathInfo.substring(tenant.length() + 1); + } } } } diff --git a/source/java/org/alfresco/rest/api/PublicApiTenantWebScriptServletRuntime.java b/source/java/org/alfresco/rest/api/PublicApiTenantWebScriptServletRuntime.java index 667fbc5eb0..8df1321ea1 100644 --- a/source/java/org/alfresco/rest/api/PublicApiTenantWebScriptServletRuntime.java +++ b/source/java/org/alfresco/rest/api/PublicApiTenantWebScriptServletRuntime.java @@ -1,28 +1,28 @@ -/* - * #%L - * Alfresco Remote API - * %% - * Copyright (C) 2005 - 2016 Alfresco Software Limited - * %% - * This file is part of the Alfresco software. - * If the software was purchased under a paid Alfresco license, the terms of - * the paid license agreement will prevail. Otherwise, the software is - * provided under the following open source license terms: - * - * Alfresco is free software: you can redistribute it and/or modify - * it under the terms of the GNU Lesser General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Alfresco is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with Alfresco. If not, see . - * #L% - */ +/* + * #%L + * Alfresco Remote API + * %% + * Copyright (C) 2005 - 2016 Alfresco Software Limited + * %% + * This file is part of the Alfresco software. + * If the software was purchased under a paid Alfresco license, the terms of + * the paid license agreement will prevail. Otherwise, the software is + * provided under the following open source license terms: + * + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + * #L% + */ package org.alfresco.rest.api; import java.io.IOException; @@ -44,12 +44,12 @@ public class PublicApiTenantWebScriptServletRuntime extends TenantWebScriptServl private static final Pattern CMIS_URI_PATTERN = Pattern.compile(".*/cmis/versions/[0-9]+\\.[0-9]+/.*"); private ApiAssistant apiAssistant; - public PublicApiTenantWebScriptServletRuntime(RuntimeContainer container, ServletAuthenticatorFactory authFactory, HttpServletRequest req, - HttpServletResponse res, ServerProperties serverProperties, ApiAssistant apiAssistant) - { - super(container, authFactory, req, res, serverProperties); + public PublicApiTenantWebScriptServletRuntime(RuntimeContainer container, ServletAuthenticatorFactory authFactory, HttpServletRequest req, + HttpServletResponse res, ServerProperties serverProperties, ApiAssistant apiAssistant) + { + super(container, authFactory, req, res, serverProperties); this.apiAssistant = apiAssistant; - } + } /* (non-Javadoc) * @see org.alfresco.web.scripts.WebScriptRuntime#getScriptUrl() @@ -81,27 +81,27 @@ public class PublicApiTenantWebScriptServletRuntime extends TenantWebScriptServl // NOTE: must contain at least root / and single character for tenant name if (pathInfo.length() < 2 || pathInfo.equals("/")) { - // url path has no tenant id -> get networks request - pathInfo = PublicApiTenantWebScriptServletRequest.NETWORKS_PATH; + // url path has no tenant id -> get networks request + pathInfo = PublicApiTenantWebScriptServletRequest.NETWORKS_PATH; } else { - if(!pathInfo.substring(0, 6).toLowerCase().equals("/cmis/")) - { - // remove tenant - int idx = pathInfo.indexOf('/', 1); - pathInfo = pathInfo.substring(idx == -1 ? pathInfo.length() : idx); - if(pathInfo.equals("") || pathInfo.equals("/")) - { - // url path is just a tenant id -> get network request - pathInfo = PublicApiTenantWebScriptServletRequest.NETWORK_PATH; - } - } + if(!pathInfo.substring(0, 6).toLowerCase().equals("/cmis/") && !pathInfo.equals("/discovery")) + { + // remove tenant + int idx = pathInfo.indexOf('/', 1); + pathInfo = pathInfo.substring(idx == -1 ? pathInfo.length() : idx); + if(pathInfo.equals("") || pathInfo.equals("/")) + { + // url path is just a tenant id -> get network request + pathInfo = PublicApiTenantWebScriptServletRequest.NETWORK_PATH; + } + } } return pathInfo; } - + /* (non-Javadoc) * @see org.alfresco.web.scripts.WebScriptRuntime#createRequest(org.alfresco.web.scripts.WebScriptMatch) */ @@ -110,13 +110,13 @@ public class PublicApiTenantWebScriptServletRuntime extends TenantWebScriptServl { // try // { - // make the request input stream a BufferedInputStream so that the first x bytes can be reused. + // make the request input stream a BufferedInputStream so that the first x bytes can be reused. // PublicApiHttpServletRequest wrapped = new PublicApiHttpServletRequest(req); - // TODO: construct org.springframework.extensions.webscripts.servlet.WebScriptServletResponse when - // org.alfresco.web.scripts.WebScriptServletResponse (deprecated) is removed - servletReq = new PublicApiTenantWebScriptServletRequest(this, req, match, serverProperties); - return servletReq; + // TODO: construct org.springframework.extensions.webscripts.servlet.WebScriptServletResponse when + // org.alfresco.web.scripts.WebScriptServletResponse (deprecated) is removed + servletReq = new PublicApiTenantWebScriptServletRequest(this, req, match, serverProperties); + return servletReq; // } // catch(IOException e) // { diff --git a/source/java/org/alfresco/rest/api/discovery/DiscoveryApiWebscript.java b/source/java/org/alfresco/rest/api/discovery/DiscoveryApiWebscript.java new file mode 100644 index 0000000000..e9b0921e95 --- /dev/null +++ b/source/java/org/alfresco/rest/api/discovery/DiscoveryApiWebscript.java @@ -0,0 +1,168 @@ +/* + * #%L + * Alfresco Remote API + * %% + * Copyright (C) 2005 - 2016 Alfresco Software Limited + * %% + * This file is part of the Alfresco software. + * If the software was purchased under a paid Alfresco license, the terms of + * the paid license agreement will prevail. Otherwise, the software is + * provided under the following open source license terms: + * + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + * #L% + */ +package org.alfresco.rest.api.discovery; + +import org.alfresco.rest.api.model.DiscoveryDetails; +import org.alfresco.rest.api.model.ModulePackage; +import org.alfresco.rest.api.model.RepositoryInfo; +import org.alfresco.rest.api.model.RepositoryInfo.LicenseInfo; +import org.alfresco.rest.api.model.RepositoryInfo.StatusInfo; +import org.alfresco.rest.api.model.RepositoryInfo.VersionInfo; +import org.alfresco.rest.framework.jacksonextensions.JacksonHelper; +import org.alfresco.rest.framework.tools.ApiAssistant; +import org.alfresco.rest.framework.tools.RecognizedParamsExtractor; +import org.alfresco.rest.framework.tools.ResponseWriter; +import org.alfresco.service.cmr.admin.RepoAdminService; +import org.alfresco.service.cmr.audit.AuditService; +import org.alfresco.service.cmr.module.ModuleDetails; +import org.alfresco.service.cmr.module.ModuleService; +import org.alfresco.service.cmr.quickshare.QuickShareService; +import org.alfresco.service.descriptor.Descriptor; +import org.alfresco.service.descriptor.DescriptorService; +import org.alfresco.util.PropertyCheck; +import org.json.simple.JSONObject; +import org.springframework.beans.factory.InitializingBean; +import org.springframework.extensions.webscripts.AbstractWebScript; +import org.springframework.extensions.webscripts.WebScriptRequest; +import org.springframework.extensions.webscripts.WebScriptResponse; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.List; + +/** + * @author Jamal Kaabi-Mofrad + */ +public class DiscoveryApiWebscript extends AbstractWebScript implements RecognizedParamsExtractor, ResponseWriter, InitializingBean +{ + private DescriptorService descriptorService; + private RepoAdminService repoAdminService; + private AuditService auditService; + private QuickShareService quickShareService; + private ModuleService moduleService; + private ApiAssistant assistant; + + public void setDescriptorService(DescriptorService descriptorService) + { + this.descriptorService = descriptorService; + } + + public void setRepoAdminService(RepoAdminService repoAdminService) + { + this.repoAdminService = repoAdminService; + } + + public void setAuditService(AuditService auditService) + { + this.auditService = auditService; + } + + public void setQuickShareService(QuickShareService quickShareService) + { + this.quickShareService = quickShareService; + } + + public void setModuleService(ModuleService moduleService) + { + this.moduleService = moduleService; + } + + public void setAssistant(ApiAssistant assistant) + { + this.assistant = assistant; + } + + @Override + public void afterPropertiesSet() throws Exception + { + PropertyCheck.mandatory(this, "descriptorService", descriptorService); + PropertyCheck.mandatory(this, "repoAdminService", repoAdminService); + PropertyCheck.mandatory(this, "auditService", auditService); + PropertyCheck.mandatory(this, "quickShareService", quickShareService); + PropertyCheck.mandatory(this, "moduleService", moduleService); + PropertyCheck.mandatory(this, "assistant", assistant); + } + + @Override + public void execute(WebScriptRequest webScriptRequest, WebScriptResponse webScriptResponse) throws IOException + { + try + { + DiscoveryDetails discoveryDetails = new DiscoveryDetails(getRepositoryInfo()); + // Write response + setResponse(webScriptResponse, DEFAULT_SUCCESS); + renderJsonResponse(webScriptResponse, discoveryDetails, assistant.getJsonHelper()); + } + catch (Exception exception) + { + renderException(exception, webScriptResponse, assistant); + } + } + + protected RepositoryInfo getRepositoryInfo() + { + LicenseInfo licenseInfo = null; + if(descriptorService.getLicenseDescriptor() != null) + { + licenseInfo = new LicenseInfo(descriptorService.getLicenseDescriptor()); + } + Descriptor serverDescriptor = descriptorService.getServerDescriptor(); + return new RepositoryInfo() + .setEdition(serverDescriptor.getEdition()) + .setVersion(new VersionInfo(serverDescriptor)) + .setLicense(licenseInfo) + .setModules(getModules()) + .setStatus(new StatusInfo() + .setReadOnly(repoAdminService.getUsage().isReadOnly()) + .setAuditEnabled(auditService.isAuditEnabled()) + .setQuickShareEnabled(quickShareService.isQuickShareEnabled())); + } + + private List getModules() + { + List details = moduleService.getAllModules(); + if (details.isEmpty()) + { + return null; + } + List packages = new ArrayList<>(details.size()); + for (ModuleDetails detail : details) + { + packages.add(ModulePackage.fromModuleDetails(detail)); + } + return packages; + } + + @Override + public void renderJsonResponse(final WebScriptResponse res, final Object toSerialize, final JacksonHelper jsonHelper) throws IOException + { + jsonHelper.withWriter(res.getOutputStream(), (generator, objectMapper) -> { + JSONObject obj = new JSONObject(); + obj.put("entry", toSerialize); + objectMapper.writeValue(generator, obj); + }); + } +} diff --git a/source/java/org/alfresco/rest/api/model/DiscoveryDetails.java b/source/java/org/alfresco/rest/api/model/DiscoveryDetails.java new file mode 100644 index 0000000000..e88b8f872c --- /dev/null +++ b/source/java/org/alfresco/rest/api/model/DiscoveryDetails.java @@ -0,0 +1,65 @@ +/* + * #%L + * Alfresco Remote API + * %% + * Copyright (C) 2005 - 2016 Alfresco Software Limited + * %% + * This file is part of the Alfresco software. + * If the software was purchased under a paid Alfresco license, the terms of + * the paid license agreement will prevail. Otherwise, the software is + * provided under the following open source license terms: + * + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + * #L% + */ +package org.alfresco.rest.api.model; + +/** + * Representation of the discovery information + * + * @author Jamal Kaabi-Mofrad + */ +public class DiscoveryDetails +{ + private RepositoryInfo repository; + + public DiscoveryDetails() + { + } + + public DiscoveryDetails(RepositoryInfo repository) + { + this.repository = repository; + } + + public RepositoryInfo getRepository() + { + return repository; + } + + public DiscoveryDetails setRepository(RepositoryInfo repository) + { + this.repository = repository; + return this; + } + + @Override + public String toString() + { + final StringBuilder sb = new StringBuilder(); + sb.append("DiscoveryDetails [repository=").append(repository) + .append(']'); + return sb.toString(); + } +} diff --git a/source/java/org/alfresco/rest/api/model/RepositoryInfo.java b/source/java/org/alfresco/rest/api/model/RepositoryInfo.java new file mode 100644 index 0000000000..fe5408ef33 --- /dev/null +++ b/source/java/org/alfresco/rest/api/model/RepositoryInfo.java @@ -0,0 +1,455 @@ +/* + * #%L + * Alfresco Remote API + * %% + * Copyright (C) 2005 - 2016 Alfresco Software Limited + * %% + * This file is part of the Alfresco software. + * If the software was purchased under a paid Alfresco license, the terms of + * the paid license agreement will prevail. Otherwise, the software is + * provided under the following open source license terms: + * + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + * #L% + */ +package org.alfresco.rest.api.model; + +import org.alfresco.service.descriptor.Descriptor; +import org.alfresco.service.license.LicenseDescriptor; +import org.apache.commons.lang.StringUtils; + +import java.util.Date; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.regex.Matcher; +import java.util.regex.Pattern; +import java.util.stream.Collectors; + +/** + * Representation of the repository information. + * + * @author Jamal Kaabi-Mofrad + */ +public class RepositoryInfo +{ + private String edition; + private VersionInfo version; + private LicenseInfo license; + private StatusInfo status; + private List modules; + + public String getEdition() + { + return edition; + } + + public RepositoryInfo setEdition(String edition) + { + this.edition = edition; + return this; + } + + public VersionInfo getVersion() + { + return version; + } + + public RepositoryInfo setVersion(VersionInfo version) + { + this.version = version; + return this; + } + + public LicenseInfo getLicense() + { + return license; + } + + public RepositoryInfo setLicense(LicenseInfo license) + { + this.license = license; + return this; + } + + public StatusInfo getStatus() + { + return status; + } + + public RepositoryInfo setStatus(StatusInfo status) + { + this.status = status; + return this; + } + + public List getModules() + { + return modules; + } + + public RepositoryInfo setModules(List modules) + { + this.modules = modules; + return this; + } + + @Override + public String toString() + { + final StringBuilder sb = new StringBuilder(450); + sb.append("RepositoryInfo [edition=").append(edition) + .append(", version=").append(version) + .append(", license=").append(license) + .append(", status=").append(status) + .append(", modules=").append(modules) + .append(']'); + return sb.toString(); + } + + /** + * Representation of the repository version information. + * + * @author Jamal Kaabi-Mofrad + */ + public static class VersionInfo + { + private static final Pattern HOTFIX_PATTERN = Pattern.compile("^[0-9]+$"); + private String major; + private String minor; + private String patch; + private String hotfix; + private int schema; + private String label; + private String display; + + // Default constructor required for test purposes + public VersionInfo() + { + } + + public VersionInfo(Descriptor descriptor) + { + this.major = descriptor.getVersionMajor(); + this.minor = descriptor.getVersionMinor(); + this.patch = descriptor.getVersionRevision(); + this.hotfix = getHotfix(descriptor.getVersionLabel()); + this.schema = descriptor.getSchema(); + this.label = descriptor.getVersionBuild(); + this.display = getVersionDisplay(); + } + + public String getMajor() + { + return major; + } + + public String getMinor() + { + return minor; + } + + public String getPatch() + { + return patch; + } + + public String getHotfix() + { + return hotfix; + } + + public int getSchema() + { + return schema; + } + + public String getLabel() + { + return label; + } + + public String getDisplay() + { + return display; + } + + private String getHotfix(String versionLabel) + { + /* + * if the label starts with a dot, then digit(s), or just digit(s), we return the number only. + * for anything else zero will be returned. + */ + if (StringUtils.isNotEmpty(versionLabel)) + { + if (versionLabel.startsWith(".")) + { + versionLabel = versionLabel.substring(1); + } + Matcher matcher = HOTFIX_PATTERN.matcher(versionLabel); + if (matcher.find()) + { + return versionLabel; + } + } + return Integer.toString(0); + } + + private String getVersionDisplay() + { + StringBuilder version = new StringBuilder(major); + version.append('.') + .append(minor) + .append('.') + .append(patch) + .append('.') + .append(getHotfix()); + + if (StringUtils.isNotEmpty(label)) + { + version.append(" (").append(label).append(") "); + } + version.append("schema ").append(schema); + + // Display example: "5.2.0.1 (r123456-b0) schema 10001" + return version.toString(); + } + + @Override + public String toString() + { + final StringBuilder sb = new StringBuilder(250); + sb.append("VersionInfo [major=").append(major) + .append(", minor=").append(minor) + .append(", patch=").append(patch) + .append(", hotfix=").append(hotfix) + .append(", schema=").append(schema) + .append(", label=").append(label) + .append(", display=").append(display) + .append(']'); + return sb.toString(); + } + } + + /** + * Representation of the license information. + * + * @author Jamal Kaabi-Mofrad + */ + public static class LicenseInfo + { + private Date issuedAt; + private Date expiresAt; + private Integer remainingDays; + private String holder; + private String mode; + private LicenseEntitlement entitlements; + + // Default constructor required for test purposes + public LicenseInfo() + { + } + + public LicenseInfo(LicenseDescriptor licenseDescriptor) + { + this.issuedAt = licenseDescriptor.getIssued(); + this.expiresAt = licenseDescriptor.getValidUntil(); + this.remainingDays = licenseDescriptor.getRemainingDays(); + this.holder = licenseDescriptor.getHolderOrganisation(); + this.mode = licenseDescriptor.getLicenseMode().name(); + this.entitlements = new LicenseEntitlement() + .setMaxDocs(licenseDescriptor.getMaxDocs()) + .setMaxUsers(licenseDescriptor.getMaxUsers()) + .setClusterEnabled(licenseDescriptor.isClusterEnabled()) + .setCryptodocEnabled(licenseDescriptor.isCryptodocEnabled()); + } + + public Date getIssuedAt() + { + return issuedAt; + } + + public Date getExpiresAt() + { + return expiresAt; + } + + public Integer getRemainingDays() + { + return remainingDays; + } + + public String getHolder() + { + return holder; + } + + public String getMode() + { + return mode; + } + + public LicenseEntitlement getEntitlements() + { + return entitlements; + } + + @Override + public String toString() + { + final StringBuilder sb = new StringBuilder(250); + sb.append("LicenseInfo [issuedAt=").append(issuedAt) + .append(", expiresAt=").append(expiresAt) + .append(", remainingDays=").append(remainingDays) + .append(", holder=").append(holder) + .append(", mode=").append(mode) + .append(", entitlements=").append(entitlements) + .append(']'); + return sb.toString(); + } + } + + /** + * Representation of the license's entitlement. + * + * @author Jamal Kaabi-Mofrad + */ + public static class LicenseEntitlement + { + private Long maxUsers; + private Long maxDocs; + private boolean isClusterEnabled; + private boolean isCryptodocEnabled; + + public LicenseEntitlement() + { + } + + public Long getMaxUsers() + { + return maxUsers; + } + + public LicenseEntitlement setMaxUsers(Long maxUsers) + { + this.maxUsers = maxUsers; + return this; + } + + public Long getMaxDocs() + { + return maxDocs; + } + + public LicenseEntitlement setMaxDocs(Long maxDocs) + { + this.maxDocs = maxDocs; + return this; + } + + public boolean getIsClusterEnabled() + { + return isClusterEnabled; + } + + public LicenseEntitlement setClusterEnabled(boolean clusterEnabled) + { + isClusterEnabled = clusterEnabled; + return this; + } + + public boolean getIsCryptodocEnabled() + { + return isCryptodocEnabled; + } + + public LicenseEntitlement setCryptodocEnabled(boolean cryptodocEnabled) + { + isCryptodocEnabled = cryptodocEnabled; + return this; + } + + @Override + public String toString() + { + final StringBuilder sb = new StringBuilder(100); + sb.append("LicenseEntitlement [maxUsers=").append(maxUsers) + .append(", maxDocs=").append(maxDocs) + .append(", isClusterEnabled=").append(isClusterEnabled) + .append(", isCryptodocEnabled=").append(isCryptodocEnabled) + .append(']'); + return sb.toString(); + } + } + + /** + * Representation of the repository status information. + * + * @author Jamal Kaabi-Mofrad + */ + public static class StatusInfo + { + private boolean isReadOnly; + private boolean isAuditEnabled; + private boolean isQuickShareEnabled; + + public StatusInfo() + { + } + + public boolean getIsReadOnly() + { + return isReadOnly; + } + + public StatusInfo setReadOnly(boolean readOnly) + { + isReadOnly = readOnly; + return this; + } + + public boolean getIsAuditEnabled() + { + return isAuditEnabled; + } + + public StatusInfo setAuditEnabled(boolean auditEnabled) + { + isAuditEnabled = auditEnabled; + return this; + } + + public boolean getIsQuickShareEnabled() + { + return isQuickShareEnabled; + } + + public StatusInfo setQuickShareEnabled(boolean quickShareEnabled) + { + isQuickShareEnabled = quickShareEnabled; + return this; + } + + @Override + public String toString() + { + final StringBuilder sb = new StringBuilder(250); + sb.append("StatusInfo [isReadOnly=").append(isReadOnly) + .append(", isAuditEnabled=").append(isAuditEnabled) + .append(", isQuickShareEnabled=").append(isQuickShareEnabled) + .append(']'); + return sb.toString(); + } + } +} diff --git a/source/test-java/org/alfresco/rest/api/tests/AbstractBaseApiTest.java b/source/test-java/org/alfresco/rest/api/tests/AbstractBaseApiTest.java index e53a160432..991920a3d3 100644 --- a/source/test-java/org/alfresco/rest/api/tests/AbstractBaseApiTest.java +++ b/source/test-java/org/alfresco/rest/api/tests/AbstractBaseApiTest.java @@ -328,6 +328,14 @@ public abstract class AbstractBaseApiTest extends EnterpriseTestApi return getSingle(url, entityId, null, expectedStatus); } + public HttpResponse get(String url, Map params, int expectedStatus) throws IOException + { + HttpResponse response = publicApiClient.get(url, params); + checkStatus(expectedStatus, response.getStatusCode()); + + return response; + } + protected HttpResponse getSingle(String url, String entityId, Map params, int expectedStatus) throws Exception { HttpResponse response = publicApiClient.get(getScope(), url, entityId, null, null, params); diff --git a/source/test-java/org/alfresco/rest/api/tests/ApiTest.java b/source/test-java/org/alfresco/rest/api/tests/ApiTest.java index 8e474ced25..5e65a23704 100644 --- a/source/test-java/org/alfresco/rest/api/tests/ApiTest.java +++ b/source/test-java/org/alfresco/rest/api/tests/ApiTest.java @@ -57,6 +57,7 @@ import org.junit.runners.Suite; AuthenticationsTest.class, ModulePackagesApiTest.class, WherePredicateApiTest.class, + DiscoveryApiTest.class, TestSites.class, TestNodeComments.class, TestFavouriteSites.class, diff --git a/source/test-java/org/alfresco/rest/api/tests/DiscoveryApiTest.java b/source/test-java/org/alfresco/rest/api/tests/DiscoveryApiTest.java new file mode 100644 index 0000000000..db6770039d --- /dev/null +++ b/source/test-java/org/alfresco/rest/api/tests/DiscoveryApiTest.java @@ -0,0 +1,315 @@ +/* + * #%L + * Alfresco Remote API + * %% + * Copyright (C) 2005 - 2016 Alfresco Software Limited + * %% + * This file is part of the Alfresco software. + * If the software was purchased under a paid Alfresco license, the terms of + * the paid license agreement will prevail. Otherwise, the software is + * provided under the following open source license terms: + * + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + * #L% + */ +package org.alfresco.rest.api.tests; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertNull; +import static org.junit.Assert.assertTrue; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; +import org.alfresco.rest.AbstractSingleNetworkSiteTest; +import org.alfresco.rest.api.discovery.DiscoveryApiWebscript; +import org.alfresco.rest.api.model.DiscoveryDetails; +import org.alfresco.rest.api.model.ModulePackage; +import org.alfresco.rest.api.model.RepositoryInfo; +import org.alfresco.rest.api.model.RepositoryInfo.LicenseEntitlement; +import org.alfresco.rest.api.model.RepositoryInfo.LicenseInfo; +import org.alfresco.rest.api.model.RepositoryInfo.StatusInfo; +import org.alfresco.rest.api.model.RepositoryInfo.VersionInfo; +import org.alfresco.rest.api.tests.client.HttpResponse; +import org.alfresco.rest.api.tests.util.RestApiUtil; +import org.alfresco.service.cmr.admin.RepoUsage.LicenseMode; +import org.alfresco.service.descriptor.Descriptor; +import org.alfresco.service.descriptor.DescriptorService; +import org.alfresco.service.license.LicenseDescriptor; +import org.joda.time.DateTime; +import org.junit.After; +import org.junit.Before; +import org.junit.Test; +import org.mockito.Mock; + +import java.util.Date; +import java.util.List; + +/** + * V1 REST API tests for retrieving detailed repository information. + *
    + *
  • {@literal :/alfresco/api/discovery}
  • + *
+ * + * @author Jamal Kaabi-Mofrad + */ +public class DiscoveryApiTest extends AbstractSingleNetworkSiteTest +{ + + private static final DateTime NOW = DateTime.now(); + + @Mock + private DescriptorService descriptorServiceMock; + @Mock + private Descriptor serverDescriptor; + @Mock + private LicenseDescriptor licenseDescriptorMock; + + private Date licenseIssuedAt; + private Date licenseExpiresAt; + + @Before + public void setup() throws Exception + { + super.setup(); + + this.licenseIssuedAt = NOW.toDate(); + this.licenseExpiresAt = NOW.plusDays(5).toDate(); + + // Mock the required descriptors + this.descriptorServiceMock = mock(DescriptorService.class); + this.serverDescriptor = mock(Descriptor.class); + this.licenseDescriptorMock = mock(LicenseDescriptor.class); + + when(descriptorServiceMock.getServerDescriptor()).thenReturn(serverDescriptor); + when(serverDescriptor.getEdition()).thenReturn("Enterprise"); + when(serverDescriptor.getVersionMajor()).thenReturn("5"); + when(serverDescriptor.getVersionMinor()).thenReturn("2"); + when(serverDescriptor.getVersionRevision()).thenReturn("1"); + when(serverDescriptor.getVersionLabel()).thenReturn(".3"); + when(serverDescriptor.getVersionBuild()).thenReturn("r123456-b0"); + when(serverDescriptor.getSchema()).thenReturn(10051); + + when(descriptorServiceMock.getLicenseDescriptor()).thenReturn(licenseDescriptorMock); + when(licenseDescriptorMock.getIssued()).thenReturn(this.licenseIssuedAt); + when(licenseDescriptorMock.getValidUntil()).thenReturn(this.licenseExpiresAt); + when(licenseDescriptorMock.getRemainingDays()).thenReturn(5); + when(licenseDescriptorMock.getLicenseMode()).thenReturn(LicenseMode.ENTERPRISE); + when(licenseDescriptorMock.getHolderOrganisation()).thenReturn("Alfresco Dev Test"); + when(licenseDescriptorMock.getMaxUsers()).thenReturn(20L); + when(licenseDescriptorMock.getMaxDocs()).thenReturn(1000L); + when(licenseDescriptorMock.isClusterEnabled()).thenReturn(true); + + // Override the descriptor service + DiscoveryApiWebscript discoveryApiWebscript = applicationContext + .getBean("webscript.org.alfresco.api.DiscoveryApiWebscript.get", DiscoveryApiWebscript.class); + discoveryApiWebscript.setDescriptorService(descriptorServiceMock); + } + + @After + public void tearDown() throws Exception + { + super.tearDown(); + } + + /** + * Tests get discovery. + *

GET:

+ * {@literal :/alfresco/api/discovery} + */ + @Test + public void testGetDiscovery() throws Exception + { + setRequestContext(null, user1, "wrongPassword"); + get("discovery", null, 401); + + setRequestContext(null, user1, null); + HttpResponse response = get("discovery", null, 200); + + DiscoveryDetails discoveryDetails = RestApiUtil.parseRestApiEntry(response.getJsonResponse(), DiscoveryDetails.class); + assertNotNull(discoveryDetails); + RepositoryInfo repositoryInfo = discoveryDetails.getRepository(); + assertNotNull(repositoryInfo); + assertEquals("Enterprise", repositoryInfo.getEdition()); + + // Check version info + VersionInfo versionInfo = repositoryInfo.getVersion(); + assertNotNull(versionInfo); + assertEquals("5", versionInfo.getMajor()); + assertEquals("2", versionInfo.getMinor()); + assertEquals("1", versionInfo.getPatch()); + assertEquals("3", versionInfo.getHotfix()); + assertEquals("r123456-b0", versionInfo.getLabel()); + assertEquals(10051, versionInfo.getSchema()); + assertEquals("5.2.1.3 (r123456-b0) schema 10051", versionInfo.getDisplay()); + + // Check license info + LicenseInfo licenseInfo = repositoryInfo.getLicense(); + assertNotNull(licenseInfo); + assertEquals(LicenseMode.ENTERPRISE.name(), licenseInfo.getMode()); + assertEquals(licenseIssuedAt.toString(), licenseInfo.getIssuedAt().toString()); + assertEquals(licenseExpiresAt.toString(), licenseInfo.getExpiresAt().toString()); + assertEquals(Integer.valueOf(5), licenseInfo.getRemainingDays()); + assertEquals("Alfresco Dev Test", licenseInfo.getHolder()); + LicenseEntitlement entitlements = licenseInfo.getEntitlements(); + assertNotNull(entitlements); + assertNotNull(entitlements.getMaxUsers()); + assertEquals(20L, entitlements.getMaxUsers().longValue()); + assertNotNull(entitlements.getMaxDocs()); + assertEquals(1000L, entitlements.getMaxDocs().longValue()); + assertTrue(entitlements.getIsClusterEnabled()); + assertFalse(entitlements.getIsCryptodocEnabled()); + + // Check status + StatusInfo statusInfo = repositoryInfo.getStatus(); + assertNotNull(statusInfo); + assertFalse(statusInfo.getIsReadOnly()); + assertTrue(statusInfo.getIsAuditEnabled()); + assertTrue(statusInfo.getIsQuickShareEnabled()); + + // Check modules + List modulePackageList = repositoryInfo.getModules(); + assertNotNull(modulePackageList); + } + + /** + * Tests get discovery. + *

GET:

+ * {@literal :/alfresco/api/discovery} + */ + @Test + public void testGetDiscovery_hotfixValue() throws Exception + { + /* + * The agreement was that if the hotfix value (versionLabel) does not follow the standard + * of "dot then digits" or just "digits", the API should return zero. + */ + + when(serverDescriptor.getVersionLabel()).thenReturn("4"); + setRequestContext(null, user1, null); + HttpResponse response = get("discovery", null, 200); + + DiscoveryDetails discoveryDetails = RestApiUtil.parseRestApiEntry(response.getJsonResponse(), DiscoveryDetails.class); + assertNotNull(discoveryDetails); + RepositoryInfo repositoryInfo = discoveryDetails.getRepository(); + assertNotNull(repositoryInfo); + assertEquals("Enterprise", repositoryInfo.getEdition()); + + // Check version info + VersionInfo versionInfo = repositoryInfo.getVersion(); + assertNotNull(versionInfo); + assertEquals("5", versionInfo.getMajor()); + assertEquals("2", versionInfo.getMinor()); + assertEquals("1", versionInfo.getPatch()); + assertEquals("4", versionInfo.getHotfix()); + assertEquals("r123456-b0", versionInfo.getLabel()); + assertEquals(10051, versionInfo.getSchema()); + assertEquals("5.2.1.4 (r123456-b0) schema 10051", versionInfo.getDisplay()); + + when(serverDescriptor.getVersionLabel()).thenReturn("d"); + response = get("discovery", null, 200); + + discoveryDetails = RestApiUtil.parseRestApiEntry(response.getJsonResponse(), DiscoveryDetails.class); + assertNotNull(discoveryDetails); + repositoryInfo = discoveryDetails.getRepository(); + assertNotNull(repositoryInfo); + // Check version info + versionInfo = repositoryInfo.getVersion(); + assertNotNull(versionInfo); + assertEquals("5", versionInfo.getMajor()); + assertEquals("2", versionInfo.getMinor()); + assertEquals("1", versionInfo.getPatch()); + assertEquals("0", versionInfo.getHotfix()); + assertEquals("r123456-b0", versionInfo.getLabel()); + assertEquals(10051, versionInfo.getSchema()); + assertEquals("5.2.1.0 (r123456-b0) schema 10051", versionInfo.getDisplay()); + + when(serverDescriptor.getVersionLabel()).thenReturn("39.4"); + response = get("discovery", null, 200); + + discoveryDetails = RestApiUtil.parseRestApiEntry(response.getJsonResponse(), DiscoveryDetails.class); + assertNotNull(discoveryDetails); + repositoryInfo = discoveryDetails.getRepository(); + assertNotNull(repositoryInfo); + // Check version info + versionInfo = repositoryInfo.getVersion(); + assertNotNull(versionInfo); + assertEquals("5", versionInfo.getMajor()); + assertEquals("2", versionInfo.getMinor()); + assertEquals("1", versionInfo.getPatch()); + assertEquals("0", versionInfo.getHotfix()); + assertEquals("r123456-b0", versionInfo.getLabel()); + assertEquals(10051, versionInfo.getSchema()); + assertEquals("5.2.1.0 (r123456-b0) schema 10051", versionInfo.getDisplay()); + } + + /** + * Tests get discovery. + *

GET:

+ * {@literal :/alfresco/api/discovery} + */ + @Test + public void testGetDiscovery_licenseEntitlement() throws Exception + { + // Override maxUsers + when(licenseDescriptorMock.getMaxUsers()).thenReturn(null); + + setRequestContext(null, user1, null); + HttpResponse response = get("discovery", null, 200); + + DiscoveryDetails discoveryDetails = RestApiUtil.parseRestApiEntry(response.getJsonResponse(), DiscoveryDetails.class); + assertNotNull(discoveryDetails); + RepositoryInfo repositoryInfo = discoveryDetails.getRepository(); + assertNotNull(repositoryInfo); + assertEquals("Enterprise", repositoryInfo.getEdition()); + + // Check license info + LicenseInfo licenseInfo = repositoryInfo.getLicense(); + assertNotNull(licenseInfo); + assertEquals(LicenseMode.ENTERPRISE.name(), licenseInfo.getMode()); + assertEquals(licenseIssuedAt.toString(), licenseInfo.getIssuedAt().toString()); + assertEquals(licenseExpiresAt.toString(), licenseInfo.getExpiresAt().toString()); + assertEquals(Integer.valueOf(5), licenseInfo.getRemainingDays()); + assertEquals("Alfresco Dev Test", licenseInfo.getHolder()); + LicenseEntitlement entitlements = licenseInfo.getEntitlements(); + assertNotNull(entitlements); + assertNull(entitlements.getMaxUsers()); + assertEquals(1000L, entitlements.getMaxDocs().longValue()); + assertTrue(entitlements.getIsClusterEnabled()); + assertFalse(entitlements.getIsCryptodocEnabled()); + + // Override entitlements + when(licenseDescriptorMock.getMaxDocs()).thenReturn(null); + when(licenseDescriptorMock.isClusterEnabled()).thenReturn(false); + when(licenseDescriptorMock.isCryptodocEnabled()).thenReturn(true); + + response = get("discovery", null, 200); + + discoveryDetails = RestApiUtil.parseRestApiEntry(response.getJsonResponse(), DiscoveryDetails.class); + assertNotNull(discoveryDetails); + repositoryInfo = discoveryDetails.getRepository(); + assertNotNull(repositoryInfo); + + // Check license info + licenseInfo = repositoryInfo.getLicense(); + assertNotNull(licenseInfo); + entitlements = licenseInfo.getEntitlements(); + assertNotNull(entitlements); + assertNull(entitlements.getMaxUsers()); + assertNull(entitlements.getMaxDocs()); + assertFalse(entitlements.getIsClusterEnabled()); + assertTrue(entitlements.getIsCryptodocEnabled()); + } +} +