├── nexus-repository-composer-it ├── src │ └── test │ │ ├── it-resources │ │ ├── list.json │ │ ├── packages-changed.json │ │ ├── rjkip-ftp-php-v1.1.0.zip │ │ ├── packages.json │ │ └── ftp-php.json │ │ └── java │ │ └── org │ │ └── sonatype │ │ └── nexus │ │ └── repository │ │ └── composer │ │ └── internal │ │ ├── fixtures │ │ ├── RepositoryRuleComposer.groovy │ │ └── ComposerRepoRecipes.groovy │ │ ├── ComposerClient.java │ │ └── ComposerITSupport.java └── pom.xml ├── nexus-repository-composer ├── src │ ├── test │ │ ├── resources │ │ │ └── org │ │ │ │ └── sonatype │ │ │ │ └── nexus │ │ │ │ └── repository │ │ │ │ └── composer │ │ │ │ └── internal │ │ │ │ ├── mergePackagesJson.input3.json │ │ │ │ ├── mergePackagesJson.output2.json │ │ │ │ ├── mergePackagesJson.input2.json │ │ │ │ ├── extractInfoFromZipballWithJson.zip │ │ │ │ ├── rewritePackagesJson.output1.json │ │ │ │ ├── extractInfoFromZipballWithoutJson.zip │ │ │ │ ├── rewritePackagesJson.output2.json │ │ │ │ ├── extractInfoFromZipBallWithJsonComposerArchived.zip │ │ │ │ ├── mergePackagesJson.input1.json │ │ │ │ ├── generatePackagesFromComponents.json │ │ │ │ ├── mergePackagesJson.output.json │ │ │ │ ├── rewriteProviderJson.output.json │ │ │ │ ├── rewritePackageJson.output.json │ │ │ │ ├── extractInfoFromZipballWithJson.composer.json │ │ │ │ ├── extractInfoFromZipballWithJsonComposerArchived.composer.json │ │ │ │ ├── getDistUrl.json │ │ │ │ ├── rewritePackageJson.input.json │ │ │ │ ├── rewriteProviderJson.input.json │ │ │ │ ├── rewritePackagesJson.input1.json │ │ │ │ ├── mergeProviderJson.input1.json │ │ │ │ ├── mergeProviderJson.input2.json │ │ │ │ └── mergeProviderJson.output.json │ │ └── java │ │ │ └── org │ │ │ └── sonatype │ │ │ └── nexus │ │ │ └── repository │ │ │ └── composer │ │ │ ├── AssetKindTest.java │ │ │ └── internal │ │ │ ├── browse │ │ │ └── ComposerBrowseNodeGeneratorTest.java │ │ │ ├── ComposerJsonExtractorTest.java │ │ │ ├── ComposerPathUtilsTest.java │ │ │ ├── ComposerProviderHandlerTest.java │ │ │ ├── ComposerMaintenanceFacetTest.java │ │ │ ├── ComposerGroupPackagesJsonHandlerTest.java │ │ │ ├── ComposerGroupProviderJsonHandlerTest.java │ │ │ └── hosted │ │ │ └── ComposerHostedFacetImplTest.java │ └── main │ │ ├── resources │ │ └── static │ │ │ └── rapture │ │ │ ├── resources │ │ │ └── nexus-repository-composer-debug.css │ │ │ └── NX │ │ │ └── composer │ │ │ ├── app │ │ │ ├── PluginConfig.js │ │ │ └── PluginStrings.js │ │ │ ├── util │ │ │ └── ComposerRepositoryUrls.js │ │ │ └── view │ │ │ └── repository │ │ │ └── recipe │ │ │ ├── ComposerGroup.js │ │ │ ├── ComposerHosted.js │ │ │ └── ComposerProxy.js │ │ └── java │ │ └── org │ │ └── sonatype │ │ └── nexus │ │ └── repository │ │ └── composer │ │ ├── store │ │ ├── ComposerAssetDAO.java │ │ ├── ComposerAssetBlobDAO.java │ │ ├── ComposerComponentDAO.java │ │ ├── ComposerContentRepositoryDAO.java │ │ └── ComposerStoreModule.java │ │ ├── internal │ │ ├── browse │ │ │ ├── ComposerBrowseNodeDAO.java │ │ │ ├── ComposerBrowseModule.java │ │ │ └── ComposerBrowseNodeGenerator.java │ │ ├── ui │ │ │ └── UiPluginDescriptorImpl.java │ │ ├── ComposerFormatSecurityContributor.java │ │ ├── proxy │ │ │ ├── ComposerPackageHandler.java │ │ │ └── ComposerProviderHandler.java │ │ ├── ComposerSecurityFacet.java │ │ ├── group │ │ │ ├── ComposerGroupPackagesJsonHandler.java │ │ │ ├── ComposerGroupPackageJsonHandler.java │ │ │ ├── ComposerGroupProviderJsonHandler.java │ │ │ └── ComposerGroupMergingHandler.java │ │ ├── cleanup │ │ │ └── ComposerCleanupPolicyConfiguration.java │ │ ├── ComposerAttributes.java │ │ ├── ComposerMaintenanceFacet.java │ │ ├── hosted │ │ │ └── ComposerHostedDownloadHandler.java │ │ ├── ComposerJsonExtractor.java │ │ ├── recipe │ │ │ ├── ComposerGroupRecipe.groovy │ │ │ ├── ComposerRecipeSupport.groovy │ │ │ ├── ComposerHostedRecipe.groovy │ │ │ └── ComposerProxyRecipe.groovy │ │ └── ComposerJsonMinifier.java │ │ ├── ComposerFormat.java │ │ ├── AssetKind.java │ │ ├── ComposerHostedFacet.java │ │ └── ComposerContentFacet.java └── pom.xml ├── .github ├── ISSUE_TEMPLATE.md ├── PULL_REQUEST_TEMPLATE.md └── CONTRIBUTING.md ├── headers.sh ├── .gitignore ├── .circleci ├── .maven.xml ├── circleci-readme.md └── config.yml ├── Dockerfile ├── header.txt ├── CONTRIBUTORS.md ├── SECURITY.md └── pom.xml /nexus-repository-composer-it/src/test/it-resources/list.json: -------------------------------------------------------------------------------- 1 | {"packageNames":["0.0.0/composer-include-files"]} 2 | -------------------------------------------------------------------------------- /nexus-repository-composer-it/src/test/it-resources/packages-changed.json: -------------------------------------------------------------------------------- 1 | {"providers-url":"http://localhost:10000/repository/composer-test-proxy/p/%package%.json","providers":{"0.0.0/composer-include-files":{"sha256":null}}} 2 | -------------------------------------------------------------------------------- /nexus-repository-composer-it/src/test/it-resources/rjkip-ftp-php-v1.1.0.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sonatype-nexus-community/nexus-repository-composer/HEAD/nexus-repository-composer-it/src/test/it-resources/rjkip-ftp-php-v1.1.0.zip -------------------------------------------------------------------------------- /nexus-repository-composer/src/test/resources/org/sonatype/nexus/repository/composer/internal/mergePackagesJson.input3.json: -------------------------------------------------------------------------------- 1 | { 2 | "metadata-url": "http://nexus.repo/base/repo3/p/%package%.json", 3 | "providers-url": "http://nexus.repo/base/repo3/p/%package%.json" 4 | } 5 | -------------------------------------------------------------------------------- /nexus-repository-composer/src/test/resources/org/sonatype/nexus/repository/composer/internal/mergePackagesJson.output2.json: -------------------------------------------------------------------------------- 1 | { 2 | "metadata-url": "http://nexus.repo/base/repo/p2/%package%.json", 3 | "providers-url": "http://nexus.repo/base/repo/p/%package%.json" 4 | } 5 | -------------------------------------------------------------------------------- /nexus-repository-composer/src/test/resources/org/sonatype/nexus/repository/composer/internal/mergePackagesJson.input2.json: -------------------------------------------------------------------------------- 1 | { 2 | "metadata-url": "http://nexus.repo/base/repo2/p2/%package%.json", 3 | "available-packages": [ 4 | "vendor2/project2", 5 | "vendor3/project3", 6 | "vendor4/project4" 7 | ] 8 | } 9 | -------------------------------------------------------------------------------- /nexus-repository-composer/src/test/resources/org/sonatype/nexus/repository/composer/internal/extractInfoFromZipballWithJson.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sonatype-nexus-community/nexus-repository-composer/HEAD/nexus-repository-composer/src/test/resources/org/sonatype/nexus/repository/composer/internal/extractInfoFromZipballWithJson.zip -------------------------------------------------------------------------------- /nexus-repository-composer/src/test/resources/org/sonatype/nexus/repository/composer/internal/rewritePackagesJson.output1.json: -------------------------------------------------------------------------------- 1 | { 2 | "metadata-url": "http://nexus.repo/base/repo/p2/%package%.json", 3 | "list" : "http://nexus.repo/base/repo/packages/list.json", 4 | "providers-url": "http://nexus.repo/base/repo/p/%package%.json" 5 | } 6 | -------------------------------------------------------------------------------- /nexus-repository-composer/src/test/resources/org/sonatype/nexus/repository/composer/internal/extractInfoFromZipballWithoutJson.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sonatype-nexus-community/nexus-repository-composer/HEAD/nexus-repository-composer/src/test/resources/org/sonatype/nexus/repository/composer/internal/extractInfoFromZipballWithoutJson.zip -------------------------------------------------------------------------------- /nexus-repository-composer/src/test/resources/org/sonatype/nexus/repository/composer/internal/rewritePackagesJson.output2.json: -------------------------------------------------------------------------------- 1 | { 2 | "metadata-url": "http://nexus.repo/base/repo/p2/%package%.json", 3 | "providers-url": "http://nexus.repo/base/repo/p/%package%.json", 4 | "available-package-patterns": [ 5 | "test2\/*" 6 | ] 7 | } 8 | -------------------------------------------------------------------------------- /nexus-repository-composer/src/test/resources/org/sonatype/nexus/repository/composer/internal/extractInfoFromZipBallWithJsonComposerArchived.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sonatype-nexus-community/nexus-repository-composer/HEAD/nexus-repository-composer/src/test/resources/org/sonatype/nexus/repository/composer/internal/extractInfoFromZipBallWithJsonComposerArchived.zip -------------------------------------------------------------------------------- /nexus-repository-composer/src/test/resources/org/sonatype/nexus/repository/composer/internal/mergePackagesJson.input1.json: -------------------------------------------------------------------------------- 1 | { 2 | "metadata-url": "http://nexus.repo/base/repo1/p2/%package%.json", 3 | "providers-url": "http://nexus.repo/base/repo1/p/%package%.json", 4 | "available-packages": [ 5 | "vendor1/project1", 6 | "vendor2/project2" 7 | ] 8 | } 9 | -------------------------------------------------------------------------------- /nexus-repository-composer/src/test/resources/org/sonatype/nexus/repository/composer/internal/generatePackagesFromComponents.json: -------------------------------------------------------------------------------- 1 | { 2 | "metadata-url": "http://nexus.repo/base/repo/p2/%package%.json", 3 | "providers-url": "http://nexus.repo/base/repo/p/%package%.json", 4 | "available-packages": [ 5 | "vendor1/project1", 6 | "vendor2/project2" 7 | ] 8 | } 9 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | Thanks for creating an issue! Please fill out this form so we can be 2 | sure to have all the information we need, and to minimize back and forth. 3 | 4 | * **What are you trying to do?** 5 | 6 | * **What feature or behavior is this required for?** 7 | 8 | * **How could we solve this issue? (Not knowing is okay!)** 9 | 10 | * **Anything else?** 11 | -------------------------------------------------------------------------------- /nexus-repository-composer/src/test/resources/org/sonatype/nexus/repository/composer/internal/mergePackagesJson.output.json: -------------------------------------------------------------------------------- 1 | { 2 | "metadata-url": "http://nexus.repo/base/repo/p2/%package%.json", 3 | "providers-url": "http://nexus.repo/base/repo/p/%package%.json", 4 | "available-packages": [ 5 | "vendor1/project1", 6 | "vendor2/project2", 7 | "vendor3/project3", 8 | "vendor4/project4" 9 | ] 10 | } 11 | -------------------------------------------------------------------------------- /headers.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | dirname=`dirname $0` 3 | dirname=`cd "$dirname" && pwd` 4 | cd "$dirname" 5 | 6 | op=$1; shift 7 | case "$op" in 8 | 'check' | 'format') 9 | ;; 10 | *) 11 | echo "usage: `basename $0` { check | format } [mvn-options]" 12 | exit 1 13 | esac 14 | 15 | # still depends on profiles defined in https://github.com/sonatype/buildsupport/blob/master/pom.xml 16 | mvn -f ./pom.xml -N -P license-${op} "$@" 17 | 18 | -------------------------------------------------------------------------------- /.github/PULL_REQUEST_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | (brief, plain english overview of your changes here) 2 | 3 | This pull request makes the following changes: 4 | * (your change here) 5 | * (another change here) 6 | * (etc) 7 | 8 | (If there are changes to the UI, please include a screenshot so we 9 | know what to look for!) 10 | 11 | (If there are changes to user behavior in general, please make sure to 12 | update the docs, as well) 13 | 14 | It relates to the following issue #s: 15 | * Fixes #X 16 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Eclipse 2 | .classpath 3 | .project 4 | .settings/ 5 | 6 | # Maven 7 | target/ 8 | *.ser 9 | *.ec 10 | .mvn/timing.properties 11 | .mvn/extensions.xml 12 | .mvn/maven.config 13 | 14 | # Intellij 15 | *.ipr 16 | *.iml 17 | *.iws 18 | .idea/ 19 | 20 | # OrientDB 21 | .orientdb_history 22 | 23 | # Other 24 | .DS_Store 25 | .clover 26 | *.log 27 | /*.rc 28 | atlassian-ide-plugin.xml 29 | dependency-reduced-pom.xml 30 | out 31 | # ci config for local ci build 32 | .circleci/local-config.yml 33 | -------------------------------------------------------------------------------- /.circleci/.maven.xml: -------------------------------------------------------------------------------- 1 | 3 | 4 | 5 | 6 | rso 7 | ${env.SONATYPE_USERNAME} 8 | ${env.SONATYPE_PASSWORD} 9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | # declaration of NEXUS_VERSION must appear before first FROM command 2 | # see: https://docs.docker.com/engine/reference/builder/#understand-how-arg-and-from-interact 3 | ARG NEXUS_VERSION=latest 4 | 5 | FROM maven:3-jdk-8-alpine AS build 6 | RUN apk add --no-cache git 7 | COPY . /nexus-repository-composer/ 8 | RUN cd /nexus-repository-composer/; \ 9 | mvn clean package -PbuildKar; 10 | 11 | FROM sonatype/nexus3:$NEXUS_VERSION 12 | 13 | ARG DEPLOY_DIR=/opt/sonatype/nexus/deploy/ 14 | USER root 15 | COPY --from=build /nexus-repository-composer/nexus-repository-composer/target/nexus-repository-composer-*-bundle.kar ${DEPLOY_DIR} 16 | USER nexus 17 | -------------------------------------------------------------------------------- /header.txt: -------------------------------------------------------------------------------- 1 | Sonatype Nexus (TM) Open Source Version 2 | Copyright (c) ${project.inceptionYear}-present Sonatype, Inc. 3 | All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. 4 | 5 | This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, 6 | which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. 7 | 8 | Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks 9 | of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the 10 | Eclipse Foundation. All other trademarks are the property of their respective owners. 11 | -------------------------------------------------------------------------------- /.circleci/circleci-readme.md: -------------------------------------------------------------------------------- 1 | CI Debug Notes 2 | ================ 3 | To validate some circleci stuff, I was able to run a “build locally” using the steps below. 4 | The local build runs in a docker container. 5 | 6 | * (Once) Install circleci client (`brew install circleci`) 7 | 8 | * Convert the “real” config.yml into a self-contained (non-workspace) config via: 9 | 10 | circleci config process .circleci/config.yml > .circleci/local-config.yml 11 | 12 | * Run a local build with the following command: 13 | 14 | circleci local execute -c .circleci/local-config.yml 'build_and_test' 15 | 16 | With the above command, those operations what cannot occur locally will show an error (like `Error: FAILED with error not supported`), but the build will proceed and can complete “successfully”, which allows you to verify scripts in your config, etc. 17 | -------------------------------------------------------------------------------- /nexus-repository-composer/src/main/resources/static/rapture/resources/nexus-repository-composer-debug.css: -------------------------------------------------------------------------------- 1 | /** 2 | * Sonatype Nexus (TM) Open Source Version 3 | * Copyright (c) 2018-present Sonatype, Inc. 4 | * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. 5 | * 6 | * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, 7 | * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. 8 | * 9 | * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks 10 | * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the 11 | * Eclipse Foundation. All other trademarks are the property of their respective owners. 12 | */ 13 | /* placeholder */ 14 | -------------------------------------------------------------------------------- /nexus-repository-composer/src/main/resources/static/rapture/NX/composer/app/PluginConfig.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Sonatype Nexus (TM) Open Source Version 3 | * Copyright (c) 2018-present Sonatype, Inc. 4 | * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. 5 | * 6 | * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, 7 | * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. 8 | * 9 | * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks 10 | * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the 11 | * Eclipse Foundation. All other trademarks are the property of their respective owners. 12 | */ 13 | Ext.define('NX.composer.app.PluginConfig', { 14 | '@aggregate_priority': 100, 15 | 16 | requires: [ 17 | 'NX.composer.app.PluginStrings', 18 | 'NX.composer.util.ComposerRepositoryUrls' 19 | ] 20 | }); 21 | -------------------------------------------------------------------------------- /nexus-repository-composer/src/main/java/org/sonatype/nexus/repository/composer/store/ComposerAssetDAO.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Sonatype Nexus (TM) Open Source Version 3 | * Copyright (c) 2024-present Sonatype, Inc. 4 | * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. 5 | * 6 | * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, 7 | * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. 8 | * 9 | * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks 10 | * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the 11 | * Eclipse Foundation. All other trademarks are the property of their respective owners. 12 | */ 13 | package org.sonatype.nexus.repository.composer.store; 14 | 15 | import org.sonatype.nexus.repository.content.store.AssetDAO; 16 | 17 | public interface ComposerAssetDAO 18 | extends AssetDAO { 19 | // nothing to add... 20 | } 21 | -------------------------------------------------------------------------------- /nexus-repository-composer/src/main/java/org/sonatype/nexus/repository/composer/store/ComposerAssetBlobDAO.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Sonatype Nexus (TM) Open Source Version 3 | * Copyright (c) 2024-present Sonatype, Inc. 4 | * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. 5 | * 6 | * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, 7 | * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. 8 | * 9 | * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks 10 | * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the 11 | * Eclipse Foundation. All other trademarks are the property of their respective owners. 12 | */ 13 | package org.sonatype.nexus.repository.composer.store; 14 | 15 | import org.sonatype.nexus.repository.content.store.AssetBlobDAO; 16 | 17 | public interface ComposerAssetBlobDAO 18 | extends AssetBlobDAO 19 | { 20 | // nothing to add... 21 | } 22 | -------------------------------------------------------------------------------- /nexus-repository-composer/src/main/java/org/sonatype/nexus/repository/composer/store/ComposerComponentDAO.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Sonatype Nexus (TM) Open Source Version 3 | * Copyright (c) 2024-present Sonatype, Inc. 4 | * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. 5 | * 6 | * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, 7 | * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. 8 | * 9 | * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks 10 | * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the 11 | * Eclipse Foundation. All other trademarks are the property of their respective owners. 12 | */ 13 | package org.sonatype.nexus.repository.composer.store; 14 | 15 | import org.sonatype.nexus.repository.content.store.ComponentDAO; 16 | 17 | public interface ComposerComponentDAO 18 | extends ComponentDAO 19 | { 20 | // nothing to add... 21 | } 22 | -------------------------------------------------------------------------------- /nexus-repository-composer/src/main/java/org/sonatype/nexus/repository/composer/internal/browse/ComposerBrowseNodeDAO.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Sonatype Nexus (TM) Open Source Version 3 | * Copyright (c) 2024-present Sonatype, Inc. 4 | * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. 5 | * 6 | * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, 7 | * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. 8 | * 9 | * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks 10 | * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the 11 | * Eclipse Foundation. All other trademarks are the property of their respective owners. 12 | */ 13 | package org.sonatype.nexus.repository.composer.internal.browse; 14 | 15 | import org.sonatype.nexus.repository.content.browse.store.BrowseNodeDAO; 16 | 17 | public interface ComposerBrowseNodeDAO 18 | extends BrowseNodeDAO { 19 | // nothing to add... 20 | } 21 | -------------------------------------------------------------------------------- /nexus-repository-composer/src/main/java/org/sonatype/nexus/repository/composer/store/ComposerContentRepositoryDAO.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Sonatype Nexus (TM) Open Source Version 3 | * Copyright (c) 2024-present Sonatype, Inc. 4 | * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. 5 | * 6 | * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, 7 | * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. 8 | * 9 | * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks 10 | * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the 11 | * Eclipse Foundation. All other trademarks are the property of their respective owners. 12 | */ 13 | package org.sonatype.nexus.repository.composer.store; 14 | 15 | import org.sonatype.nexus.repository.content.store.ContentRepositoryDAO; 16 | 17 | public interface ComposerContentRepositoryDAO 18 | extends ContentRepositoryDAO 19 | { 20 | // nothing to add... 21 | } 22 | -------------------------------------------------------------------------------- /nexus-repository-composer/src/main/java/org/sonatype/nexus/repository/composer/ComposerFormat.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Sonatype Nexus (TM) Open Source Version 3 | * Copyright (c) 2018-present Sonatype, Inc. 4 | * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. 5 | * 6 | * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, 7 | * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. 8 | * 9 | * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks 10 | * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the 11 | * Eclipse Foundation. All other trademarks are the property of their respective owners. 12 | */ 13 | package org.sonatype.nexus.repository.composer; 14 | 15 | import javax.inject.Named; 16 | import javax.inject.Singleton; 17 | 18 | import org.sonatype.nexus.repository.Format; 19 | 20 | /** 21 | * Class representing the Composer format. 22 | */ 23 | @Named(ComposerFormat.NAME) 24 | @Singleton 25 | public class ComposerFormat 26 | extends Format 27 | { 28 | public static final String NAME = "composer"; 29 | 30 | public ComposerFormat() { 31 | super(NAME); 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /nexus-repository-composer/src/main/resources/static/rapture/NX/composer/app/PluginStrings.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Sonatype Nexus (TM) Open Source Version 3 | * Copyright (c) 2018-present Sonatype, Inc. 4 | * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. 5 | * 6 | * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, 7 | * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. 8 | * 9 | * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks 10 | * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the 11 | * Eclipse Foundation. All other trademarks are the property of their respective owners. 12 | */ 13 | /*global Ext, NX*/ 14 | 15 | /** 16 | * Composer plugin strings. 17 | */ 18 | Ext.define('NX.composer.app.PluginStrings', { 19 | '@aggregate_priority': 90, 20 | 21 | singleton: true, 22 | requires: [ 23 | 'NX.I18n' 24 | ], 25 | 26 | keys: { 27 | SearchComposer_Group: 'Composer Repositories', 28 | SearchComposer_Text: 'Composer', 29 | SearchComposer_Description: 'Search for components in Composer repositories' 30 | } 31 | }, function(self) { 32 | NX.I18n.register(self); 33 | }); 34 | -------------------------------------------------------------------------------- /nexus-repository-composer/src/main/java/org/sonatype/nexus/repository/composer/internal/browse/ComposerBrowseModule.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Sonatype Nexus (TM) Open Source Version 3 | * Copyright (c) 2018-present Sonatype, Inc. 4 | * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. 5 | * 6 | * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, 7 | * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. 8 | * 9 | * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks 10 | * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the 11 | * Eclipse Foundation. All other trademarks are the property of their respective owners. 12 | */ 13 | package org.sonatype.nexus.repository.composer.internal.browse; 14 | 15 | import org.sonatype.nexus.repository.composer.ComposerFormat; 16 | import org.sonatype.nexus.repository.content.browse.store.FormatBrowseModule; 17 | 18 | import javax.inject.Named; 19 | 20 | /** 21 | * Configures the browse bindings for the maven format. 22 | */ 23 | @Named(ComposerFormat.NAME) 24 | public class ComposerBrowseModule 25 | extends FormatBrowseModule 26 | { 27 | // nothing to add... 28 | } 29 | -------------------------------------------------------------------------------- /nexus-repository-composer/src/main/java/org/sonatype/nexus/repository/composer/store/ComposerStoreModule.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Sonatype Nexus (TM) Open Source Version 3 | * Copyright (c) 2024-present Sonatype, Inc. 4 | * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. 5 | * 6 | * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, 7 | * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. 8 | * 9 | * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks 10 | * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the 11 | * Eclipse Foundation. All other trademarks are the property of their respective owners. 12 | */ 13 | package org.sonatype.nexus.repository.composer.store; 14 | 15 | import org.sonatype.nexus.repository.composer.ComposerFormat; 16 | import org.sonatype.nexus.repository.content.store.FormatStoreModule; 17 | 18 | import javax.inject.Named; 19 | 20 | /** 21 | * Configures the content store bindings for a Composer format. 22 | */ 23 | @Named(ComposerFormat.NAME) 24 | public class ComposerStoreModule 25 | extends FormatStoreModule { 29 | } 30 | -------------------------------------------------------------------------------- /nexus-repository-composer-it/src/test/java/org/sonatype/nexus/repository/composer/internal/fixtures/RepositoryRuleComposer.groovy: -------------------------------------------------------------------------------- 1 | /* 2 | * Sonatype Nexus (TM) Open Source Version 3 | * Copyright (c) 2018-present Sonatype, Inc. 4 | * All rights reserved. Includes the third-party code listed at http://links.sonatype 5 | * .com/products/nexus/oss/attributions. 6 | * 7 | * This program and the accompanying materials are made available under the terms of the Eclipse Public License 8 | * Version 1.0, 9 | * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. 10 | * 11 | * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are 12 | * trademarks 13 | * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the 14 | * Eclipse Foundation. All other trademarks are the property of their respective owners. 15 | */ 16 | package org.sonatype.nexus.repository.composer.internal.fixtures 17 | 18 | import javax.inject.Provider 19 | 20 | import org.sonatype.nexus.repository.manager.RepositoryManager 21 | import org.sonatype.nexus.testsuite.testsupport.fixtures.RepositoryRule 22 | 23 | class RepositoryRuleComposer 24 | extends RepositoryRule 25 | implements ComposerRepoRecipes 26 | { 27 | RepositoryRuleComposer(final Provider repositoryManagerProvider) { 28 | super(repositoryManagerProvider) 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /CONTRIBUTORS.md: -------------------------------------------------------------------------------- 1 | 15 | A lot of awesome people have contributed to this project! Here they are: 16 | 17 | Sonatype internal people, in alphabetical order by username: 18 | 19 | * [@DarthHater](https://github.com/darthhater/) (Jeffry Hesse) 20 | * [@doddi](https://github.com/doddi/) (Mark Dodgson) 21 | * [@fjmilens3](https://github.com/fjmilens3/) (Frederick Milens) 22 | * [@jlstephens89](https://github.com/jlstephens89/) (Joseph Stephens) 23 | 24 | External contributors: 25 | 26 | * [@TheBay0r](https://github.com/TheBay0r) (Stefan Schacherl) 27 | * [@Elendev](https://github.com/Elendev) (Jonas Renaudot) 28 | * [@stefaanneyts](https://github.com/stefaanneyts) (Stefaan Neyts) 29 | -------------------------------------------------------------------------------- /nexus-repository-composer-it/src/test/it-resources/packages.json: -------------------------------------------------------------------------------- 1 | {"packages":[],"notify":"https://packagist.org/downloads/%package%","notify-batch":"https://packagist.org/downloads/","providers-url":"/p/%package%$%hash%.json","metadata-url":"/p2/%package%.json","search":"https://packagist.org/search.json?q=%query%&type=%type%","provider-includes":{"p/provider-2013$%hash%.json":{"sha256":"cab932951e91e2b9ec613ef883adc38ed52a48f9e23e8a2baa02ec49b650bbb3"},"p/provider-2014$%hash%.json":{"sha256":"d0401e6a04adab24c42c34d83e2009b2cf693265727b596634abf86edb72261f"},"p/provider-2015$%hash%.json":{"sha256":"3c56c6be8b4e8d9afbedd4da4fde7cbea7461961efe4de56897eb82d2811042f"},"p/provider-2016$%hash%.json":{"sha256":"484e71121232e53874430a6c53394218da3e552cda4d8bcca0a61d613efd27fb"},"p/provider-2017$%hash%.json":{"sha256":"55b9009c4c8190343d3c9578893d221979d01b1932275bf28b308309c32fa3ca"},"p/provider-2018$%hash%.json":{"sha256":"93f158c2b271d16bff7f9c2eec7c10820cedba5cf5db4f8eac9eb66063fd7df2"},"p/provider-2019-01$%hash%.json":{"sha256":"349cd385968423a155088c8c2935004274715aee5de6280c6fa12c18a4ea7c15"},"p/provider-2019-04$%hash%.json":{"sha256":"c64ecd2711550f1aff08be79f8a1429f9081dc7ec3dbe242d531bb664026c844"},"p/provider-2019-07$%hash%.json":{"sha256":"84d28cbd7676aaa7151a835abb5cc4c887c80de984be70f1b09d3f5823477bea"},"p/provider-archived$%hash%.json":{"sha256":"732a818b5d8ef852b04d73ac2a5685ad369f4debfe1a0d36f237e62f39b688de"},"p/provider-latest$%hash%.json":{"sha256":"a22f4df6424192a11a32d5a9bcd475f14c4a83595746f57a391f208b6144a41f"}}} 2 | -------------------------------------------------------------------------------- /nexus-repository-composer-it/src/test/java/org/sonatype/nexus/repository/composer/internal/ComposerClient.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Sonatype Nexus (TM) Open Source Version 3 | * Copyright (c) 2018-present Sonatype, Inc. 4 | * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. 5 | * 6 | * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, 7 | * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. 8 | * 9 | * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks 10 | * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the 11 | * Eclipse Foundation. All other trademarks are the property of their respective owners. 12 | */ 13 | package org.sonatype.nexus.repository.composer.internal; 14 | 15 | import java.net.URI; 16 | 17 | import org.sonatype.nexus.testsuite.testsupport.FormatClientSupport; 18 | 19 | import org.apache.http.client.protocol.HttpClientContext; 20 | import org.apache.http.impl.client.CloseableHttpClient; 21 | 22 | public class ComposerClient 23 | extends FormatClientSupport 24 | { public ComposerClient( 25 | final CloseableHttpClient httpClient, 26 | final HttpClientContext httpClientContext, 27 | final URI repositoryBaseUri) 28 | { 29 | super(httpClient, httpClientContext, repositoryBaseUri); 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /nexus-repository-composer/src/main/resources/static/rapture/NX/composer/util/ComposerRepositoryUrls.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Sonatype Nexus (TM) Open Source Version 3 | * Copyright (c) 2018-present Sonatype, Inc. 4 | * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. 5 | * 6 | * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, 7 | * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. 8 | * 9 | * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks 10 | * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the 11 | * Eclipse Foundation. All other trademarks are the property of their respective owners. 12 | */ 13 | Ext.define('NX.composer.util.ComposerRepositoryUrls', { 14 | '@aggregate_priority': 90, 15 | 16 | singleton: true, 17 | requires: [ 18 | 'NX.coreui.util.RepositoryUrls', 19 | 'NX.util.Url' 20 | ] 21 | }, function(self) { 22 | NX.coreui.util.RepositoryUrls.addRepositoryUrlStrategy('composer', function(me, assetModel) { 23 | var repositoryName = assetModel.get('repositoryName'), 24 | assetName = assetModel.get('name').replace(/^\//, ''); 25 | return NX.util.Url.asLink( 26 | NX.util.Url.baseUrl + '/repository/' + encodeURIComponent(repositoryName) + '/' + encodeURI(assetName), 27 | assetName); 28 | }); 29 | }); 30 | -------------------------------------------------------------------------------- /nexus-repository-composer/src/main/java/org/sonatype/nexus/repository/composer/internal/ui/UiPluginDescriptorImpl.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Sonatype Nexus (TM) Open Source Version 3 | * Copyright (c) 2018-present Sonatype, Inc. 4 | * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. 5 | * 6 | * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, 7 | * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. 8 | * 9 | * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks 10 | * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the 11 | * Eclipse Foundation. All other trademarks are the property of their respective owners. 12 | */ 13 | package org.sonatype.nexus.repository.composer.internal.ui; 14 | 15 | import javax.annotation.Priority; 16 | import javax.inject.Named; 17 | import javax.inject.Singleton; 18 | 19 | import org.sonatype.nexus.rapture.UiPluginDescriptorSupport; 20 | 21 | /** 22 | * Plugin descriptor that registers the plugin-specific UI components (and Javascript) with the repository manager. 23 | */ 24 | @Named 25 | @Singleton 26 | @Priority(Integer.MAX_VALUE - 200) 27 | public class UiPluginDescriptorImpl 28 | extends UiPluginDescriptorSupport 29 | { 30 | public UiPluginDescriptorImpl() { 31 | super("nexus-repository-composer"); 32 | setNamespace("NX.composer"); 33 | setConfigClassName("NX.composer.app.PluginConfig"); 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /nexus-repository-composer/src/main/java/org/sonatype/nexus/repository/composer/internal/ComposerFormatSecurityContributor.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Sonatype Nexus (TM) Open Source Version 3 | * Copyright (c) 2018-present Sonatype, Inc. 4 | * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. 5 | * 6 | * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, 7 | * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. 8 | * 9 | * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks 10 | * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the 11 | * Eclipse Foundation. All other trademarks are the property of their respective owners. 12 | */ 13 | package org.sonatype.nexus.repository.composer.internal; 14 | 15 | import javax.inject.Inject; 16 | import javax.inject.Named; 17 | import javax.inject.Singleton; 18 | 19 | import org.sonatype.nexus.repository.Format; 20 | import org.sonatype.nexus.repository.composer.ComposerFormat; 21 | import org.sonatype.nexus.repository.security.RepositoryFormatSecurityContributor; 22 | 23 | /** 24 | * Boilerplate security contributor for the Composer format. 25 | */ 26 | @Named 27 | @Singleton 28 | public class ComposerFormatSecurityContributor 29 | extends RepositoryFormatSecurityContributor 30 | { 31 | @Inject 32 | public ComposerFormatSecurityContributor(@Named(ComposerFormat.NAME) final Format format) { 33 | super(format); 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /nexus-repository-composer/src/main/resources/static/rapture/NX/composer/view/repository/recipe/ComposerGroup.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Sonatype Nexus (TM) Open Source Version 3 | * Copyright (c) 2018-present Sonatype, Inc. 4 | * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. 5 | * 6 | * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, 7 | * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. 8 | * 9 | * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks 10 | * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the 11 | * Eclipse Foundation. All other trademarks are the property of their respective owners. 12 | */ 13 | /*global Ext, NX*/ 14 | 15 | /** 16 | * Configuration settings in the UI for a Composer group recipe. 17 | */ 18 | Ext.define('NX.composer.view.repository.recipe.ComposerGroup', { 19 | extend: 'NX.coreui.view.repository.RepositorySettingsForm', 20 | alias: 'widget.nx-coreui-repository-composer-group', 21 | requires: [ 22 | 'NX.coreui.view.repository.facet.StorageFacet', 23 | 'NX.coreui.view.repository.facet.GroupFacet' 24 | ], 25 | 26 | /** 27 | * @override 28 | */ 29 | initComponent: function() { 30 | var me = this; 31 | 32 | me.items = [ 33 | {xtype: 'nx-coreui-repository-storage-facet'}, 34 | {xtype: 'nx-coreui-repository-group-facet', format: 'composer'} 35 | ]; 36 | 37 | me.callParent(); 38 | } 39 | }); 40 | -------------------------------------------------------------------------------- /nexus-repository-composer/src/main/java/org/sonatype/nexus/repository/composer/AssetKind.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Sonatype Nexus (TM) Open Source Version 3 | * Copyright (c) 2018-present Sonatype, Inc. 4 | * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. 5 | * 6 | * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, 7 | * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. 8 | * 9 | * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks 10 | * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the 11 | * Eclipse Foundation. All other trademarks are the property of their respective owners. 12 | */ 13 | package org.sonatype.nexus.repository.composer; 14 | 15 | import org.sonatype.nexus.repository.cache.CacheControllerHolder.CacheType; 16 | 17 | import static org.sonatype.nexus.repository.cache.CacheControllerHolder.CONTENT; 18 | import static org.sonatype.nexus.repository.cache.CacheControllerHolder.METADATA; 19 | 20 | /** 21 | * Enumeration defining the valid asset kinds for a Composer-format repository. 22 | */ 23 | public enum AssetKind 24 | { 25 | ZIPBALL(CONTENT), 26 | PACKAGES(METADATA), 27 | LIST(METADATA), 28 | PROVIDER(METADATA), 29 | PACKAGE(METADATA); 30 | 31 | private final CacheType cacheType; 32 | 33 | AssetKind(final CacheType cacheType) { 34 | this.cacheType = cacheType; 35 | } 36 | 37 | public CacheType getCacheType() { 38 | return cacheType; 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /nexus-repository-composer/src/main/java/org/sonatype/nexus/repository/composer/internal/proxy/ComposerPackageHandler.java: -------------------------------------------------------------------------------- 1 | package org.sonatype.nexus.repository.composer.internal.proxy; 2 | 3 | import org.sonatype.nexus.repository.composer.internal.ComposerJsonProcessor; 4 | import org.sonatype.nexus.repository.http.HttpResponses; 5 | import org.sonatype.nexus.repository.http.HttpStatus; 6 | import org.sonatype.nexus.repository.view.Context; 7 | import org.sonatype.nexus.repository.view.Handler; 8 | import org.sonatype.nexus.repository.view.Response; 9 | 10 | import javax.annotation.Nonnull; 11 | import javax.inject.Inject; 12 | 13 | import static com.google.common.base.Preconditions.checkNotNull; 14 | 15 | public class ComposerPackageHandler 16 | implements Handler 17 | { 18 | public static final String DO_NOT_REWRITE = "ComposerProviderHandler.doNotRewrite"; 19 | 20 | private final ComposerJsonProcessor composerJsonProcessor; 21 | 22 | @Inject 23 | public ComposerPackageHandler(final ComposerJsonProcessor composerJsonProcessor) { 24 | this.composerJsonProcessor = checkNotNull(composerJsonProcessor); 25 | } 26 | 27 | @Nonnull 28 | @Override 29 | public Response handle(@Nonnull final Context context) throws Exception { 30 | Response response = context.proceed(); 31 | if (!Boolean.parseBoolean(context.getRequest().getAttributes().get(DO_NOT_REWRITE, String.class))) { 32 | if (response.getStatus().getCode() == HttpStatus.OK && response.getPayload() != null) { 33 | response = HttpResponses 34 | .ok(composerJsonProcessor.rewritePackageJson(context.getRepository(), response.getPayload())); 35 | } 36 | } 37 | return response; 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /nexus-repository-composer-it/src/test/java/org/sonatype/nexus/repository/composer/internal/fixtures/ComposerRepoRecipes.groovy: -------------------------------------------------------------------------------- 1 | /* 2 | * Sonatype Nexus (TM) Open Source Version 3 | * Copyright (c) 2018-present Sonatype, Inc. 4 | * All rights reserved. Includes the third-party code listed at http://links.sonatype 5 | * .com/products/nexus/oss/attributions. 6 | * 7 | * This program and the accompanying materials are made available under the terms of the Eclipse Public License 8 | * Version 1.0, 9 | * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. 10 | * 11 | * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are 12 | * trademarks 13 | * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the 14 | * Eclipse Foundation. All other trademarks are the property of their respective owners. 15 | */ 16 | package org.sonatype.nexus.repository.composer.internal.fixtures 17 | 18 | import javax.annotation.Nonnull 19 | 20 | import org.sonatype.nexus.repository.Repository 21 | import org.sonatype.nexus.repository.config.Configuration 22 | import org.sonatype.nexus.testsuite.testsupport.fixtures.ConfigurationRecipes 23 | 24 | import groovy.transform.CompileStatic 25 | 26 | /** 27 | * Factory for composer {@link Repository} {@link Configuration} 28 | */ 29 | @CompileStatic 30 | trait ComposerRepoRecipes 31 | extends ConfigurationRecipes 32 | { 33 | @Nonnull 34 | Repository createComposerProxy(final String name, 35 | final String remoteUrl) 36 | { 37 | createRepository(createProxy(name, 'composer-proxy', remoteUrl)) 38 | } 39 | 40 | abstract Repository createRepository(final Configuration configuration) 41 | } 42 | -------------------------------------------------------------------------------- /nexus-repository-composer/src/main/resources/static/rapture/NX/composer/view/repository/recipe/ComposerHosted.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Sonatype Nexus (TM) Open Source Version 3 | * Copyright (c) 2018-present Sonatype, Inc. 4 | * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. 5 | * 6 | * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, 7 | * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. 8 | * 9 | * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks 10 | * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the 11 | * Eclipse Foundation. All other trademarks are the property of their respective owners. 12 | */ 13 | /*global Ext, NX*/ 14 | 15 | /** 16 | * Configuration settings in the UI for a Composer hosted recipe. 17 | */ 18 | Ext.define('NX.composer.view.repository.recipe.ComposerHosted', { 19 | extend: 'NX.coreui.view.repository.RepositorySettingsForm', 20 | alias: 'widget.nx-coreui-repository-composer-hosted', 21 | requires: [ 22 | 'NX.coreui.view.repository.facet.StorageFacet', 23 | 'NX.coreui.view.repository.facet.StorageFacetHosted', 24 | 'NX.coreui.view.repository.facet.CleanupPolicyFacet' 25 | ], 26 | 27 | /** 28 | * @override 29 | */ 30 | initComponent: function () { 31 | var me = this; 32 | 33 | me.items = [ 34 | {xtype: 'nx-coreui-repository-storage-facet'}, 35 | {xtype: 'nx-coreui-repository-storage-hosted-facet', writePolicy: 'ALLOW'}, 36 | {xtype: 'nx-coreui-repository-cleanup-policy-facet'} 37 | ]; 38 | 39 | me.callParent(); 40 | } 41 | }); 42 | -------------------------------------------------------------------------------- /nexus-repository-composer/src/main/java/org/sonatype/nexus/repository/composer/internal/ComposerSecurityFacet.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Sonatype Nexus (TM) Open Source Version 3 | * Copyright (c) 2018-present Sonatype, Inc. 4 | * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. 5 | * 6 | * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, 7 | * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. 8 | * 9 | * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks 10 | * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the 11 | * Eclipse Foundation. All other trademarks are the property of their respective owners. 12 | */ 13 | package org.sonatype.nexus.repository.composer.internal; 14 | 15 | import javax.inject.Inject; 16 | import javax.inject.Named; 17 | 18 | import org.sonatype.nexus.repository.security.ContentPermissionChecker; 19 | import org.sonatype.nexus.repository.security.SecurityFacetSupport; 20 | import org.sonatype.nexus.repository.security.VariableResolverAdapter; 21 | 22 | /** 23 | * Composer format security facet. 24 | */ 25 | @Named 26 | public class ComposerSecurityFacet 27 | extends SecurityFacetSupport 28 | { 29 | @Inject 30 | public ComposerSecurityFacet(final ComposerFormatSecurityContributor securityContributor, 31 | @Named("simple") final VariableResolverAdapter variableResolverAdapter, 32 | final ContentPermissionChecker contentPermissionChecker) 33 | { 34 | super(securityContributor, variableResolverAdapter, contentPermissionChecker); 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /nexus-repository-composer/src/test/resources/org/sonatype/nexus/repository/composer/internal/rewriteProviderJson.output.json: -------------------------------------------------------------------------------- 1 | { 2 | "packages": { 3 | "vendor1/project1": { 4 | "branch1": { 5 | "name": "vendor1/project1", 6 | "description": "Description 1", 7 | "keywords": [], 8 | "homepage": "", 9 | "version": "branch1", 10 | "version_normalized": "branch1", 11 | "license": [ 12 | "MIT" 13 | ], 14 | "authors": [ 15 | { 16 | "name": "Author 1", 17 | "homepage": "http://example.com/author1" 18 | } 19 | ], 20 | "dist": { 21 | "type": "zip", 22 | "url": "http://nexus.repo/base/repo/vendor1/project1/branch1/vendor1-project1-branch1.zip", 23 | "reference": "48954c0fa210437795be418e708b379598333d0a", 24 | "shasum": "" 25 | }, 26 | "time": "2018-01-09T15:53:01+00:00", 27 | "uid": 1 28 | }, 29 | "branch2": { 30 | "name": "vendor2/project2", 31 | "description": "Description 2", 32 | "keywords": [], 33 | "homepage": "", 34 | "version": "branch2", 35 | "version_normalized": "branch2", 36 | "license": [ 37 | "MIT" 38 | ], 39 | "authors": [ 40 | { 41 | "name": "Author 2", 42 | "homepage": "http://example.com/author2" 43 | } 44 | ], 45 | "dist": { 46 | "type": "zip", 47 | "url": "http://nexus.repo/base/repo/vendor1/project1/branch2/vendor1-project1-branch2.zip", 48 | "reference": "be418e708b379598333d0a48954c0fa210437795", 49 | "shasum": "" 50 | }, 51 | "time": "2018-01-09T15:53:01+00:00", 52 | "uid": 2 53 | } 54 | } 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /nexus-repository-composer/src/test/resources/org/sonatype/nexus/repository/composer/internal/rewritePackageJson.output.json: -------------------------------------------------------------------------------- 1 | { 2 | "minified": "composer/2.0", 3 | "packages": { 4 | "vendor1/project1": [ 5 | { 6 | "name": "vendor1/project1", 7 | "description": "Description 2", 8 | "keywords": [], 9 | "homepage": "", 10 | "version": "v1.2.3", 11 | "version_normalized": "1.2.3.0", 12 | "license": [ 13 | "MIT" 14 | ], 15 | "authors": [ 16 | { 17 | "name": "Author 1", 18 | "homepage": "https://example.com/author1" 19 | } 20 | ], 21 | "dist": { 22 | "type": "zip", 23 | "url": "http://nexus.repo/base/repo/vendor1/project1/v1.2.3/vendor1-project1-v1.2.3.zip", 24 | "reference": "f1d2d2f924e986ac86fdf7b36c94bcdf32beec15", 25 | "shasum": "" 26 | }, 27 | "time": "2024-09-03T15:30:00+00:00", 28 | "uid": 2 29 | }, 30 | { 31 | "name": "vendor1/project1", 32 | "description": "Description 1", 33 | "keywords": [], 34 | "homepage": "", 35 | "version": "v1.0.0", 36 | "version_normalized": "1.0.0.0", 37 | "license": [ 38 | "MIT" 39 | ], 40 | "authors": [ 41 | { 42 | "name": "Author 1", 43 | "homepage": "https://example.com/author1" 44 | } 45 | ], 46 | "dist": { 47 | "type": "zip", 48 | "url": "http://nexus.repo/base/repo/vendor1/project1/v1.0.0/vendor1-project1-v1.0.0.zip", 49 | "reference": "48954c0fa210437795be418e708b379598333d0a", 50 | "shasum": "" 51 | }, 52 | "time": "2018-01-09T15:53:01+00:00", 53 | "uid": 1 54 | } 55 | ] 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /nexus-repository-composer/src/test/resources/org/sonatype/nexus/repository/composer/internal/extractInfoFromZipballWithJson.composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "vendor/project", 3 | "version" : "1.2.3", 4 | "description": "Test description", 5 | "keywords": ["keyword1", "keyword2", "keyword3"], 6 | "homepage": "http://www.example.com/", 7 | "time": "2008-05-15", 8 | "type": "library", 9 | "license": "MIT", 10 | "authors": [ 11 | { 12 | "name": "Author1", 13 | "email": "author1@example.com", 14 | "homepage": "http://www.example.com/author1" 15 | }, 16 | { 17 | "name": "Author2", 18 | "email": "author2@example.com", 19 | "homepage": "http://www.example.com/author2" 20 | } 21 | ], 22 | "support": { 23 | "email": "email@example.com", 24 | "issues": "http://www.example.com/issues", 25 | "forum": "http://www.example.com/forum", 26 | "wiki": "http://www.example.com/wiki", 27 | "issues": "irc://irc.example.com/irc", 28 | "source": "http://www.example.com/source", 29 | "docs": "http://www.example.com/docs", 30 | "rss": "http://www.example.com/rss" 31 | }, 32 | "require": { 33 | "php": "^5.3.2 || ^7.0", 34 | "symfony/console": "^2.7 || ^3.0 || ^4.0", 35 | "symfony/finder": "^2.7 || ^3.0 || ^4.0", 36 | "symfony/process": "^2.7 || ^3.0 || ^4.0", 37 | "symfony/filesystem": "^2.7 || ^3.0 || ^4.0" 38 | }, 39 | "require-dev": { 40 | "phpunit/phpunit": "^4.8.35 || ^5.7", 41 | "phpunit/phpunit-mock-objects": "^2.3 || ^3.0" 42 | }, 43 | "config": { 44 | "platform": { 45 | "php": "5.3.9" 46 | } 47 | }, 48 | "bin": ["bin/example"], 49 | "extra": { 50 | "branch-alias": { 51 | "dev-master": "1.7-dev" 52 | } 53 | }, 54 | "scripts": { 55 | "test": "phpunit" 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /nexus-repository-composer/src/test/java/org/sonatype/nexus/repository/composer/AssetKindTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Sonatype Nexus (TM) Open Source Version 3 | * Copyright (c) 2018-present Sonatype, Inc. 4 | * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. 5 | * 6 | * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, 7 | * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. 8 | * 9 | * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks 10 | * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the 11 | * Eclipse Foundation. All other trademarks are the property of their respective owners. 12 | */ 13 | package org.sonatype.nexus.repository.composer; 14 | 15 | import org.sonatype.goodies.testsupport.TestSupport; 16 | 17 | import org.junit.Test; 18 | 19 | import static org.hamcrest.MatcherAssert.assertThat; 20 | import static org.hamcrest.Matchers.is; 21 | import static org.sonatype.nexus.repository.cache.CacheControllerHolder.CONTENT; 22 | import static org.sonatype.nexus.repository.cache.CacheControllerHolder.METADATA; 23 | import static org.sonatype.nexus.repository.composer.AssetKind.LIST; 24 | import static org.sonatype.nexus.repository.composer.AssetKind.PACKAGES; 25 | import static org.sonatype.nexus.repository.composer.AssetKind.PROVIDER; 26 | import static org.sonatype.nexus.repository.composer.AssetKind.ZIPBALL; 27 | 28 | public class AssetKindTest 29 | extends TestSupport 30 | { 31 | @Test 32 | public void cacheTypes() { 33 | assertThat(LIST.getCacheType(), is(METADATA)); 34 | assertThat(PROVIDER.getCacheType(), is(METADATA)); 35 | assertThat(PACKAGES.getCacheType(), is(METADATA)); 36 | assertThat(ZIPBALL.getCacheType(), is(CONTENT)); 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /nexus-repository-composer/src/test/resources/org/sonatype/nexus/repository/composer/internal/extractInfoFromZipballWithJsonComposerArchived.composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "vendor/project", 3 | "version" : "1.2.3", 4 | "description": "Test description", 5 | "keywords": ["keyword1", "keyword2", "keyword3"], 6 | "homepage": "http://www.example.com/", 7 | "time": "2008-05-15", 8 | "type": "library", 9 | "license": "MIT", 10 | "authors": [ 11 | { 12 | "name": "Author1", 13 | "email": "author1@example.com", 14 | "homepage": "http://www.example.com/author1" 15 | }, 16 | { 17 | "name": "Author2", 18 | "email": "author2@example.com", 19 | "homepage": "http://www.example.com/author2" 20 | } 21 | ], 22 | "support": { 23 | "email": "email@example.com", 24 | "issues": "http://www.example.com/issues", 25 | "forum": "http://www.example.com/forum", 26 | "wiki": "http://www.example.com/wiki", 27 | "issues": "irc://irc.example.com/irc", 28 | "source": "http://www.example.com/source", 29 | "docs": "http://www.example.com/docs", 30 | "rss": "http://www.example.com/rss" 31 | }, 32 | "require": { 33 | "php": "^5.3.2 || ^7.0", 34 | "symfony/console": "^2.7 || ^3.0 || ^4.0", 35 | "symfony/finder": "^2.7 || ^3.0 || ^4.0", 36 | "symfony/process": "^2.7 || ^3.0 || ^4.0", 37 | "symfony/filesystem": "^2.7 || ^3.0 || ^4.0" 38 | }, 39 | "require-dev": { 40 | "phpunit/phpunit": "^4.8.35 || ^5.7", 41 | "phpunit/phpunit-mock-objects": "^2.3 || ^3.0" 42 | }, 43 | "config": { 44 | "platform": { 45 | "php": "5.3.9" 46 | } 47 | }, 48 | "bin": ["bin/example"], 49 | "extra": { 50 | "branch-alias": { 51 | "dev-master": "1.7-dev" 52 | } 53 | }, 54 | "scripts": { 55 | "test": "phpunit" 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /nexus-repository-composer/src/main/resources/static/rapture/NX/composer/view/repository/recipe/ComposerProxy.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Sonatype Nexus (TM) Open Source Version 3 | * Copyright (c) 2018-present Sonatype, Inc. 4 | * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. 5 | * 6 | * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, 7 | * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. 8 | * 9 | * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks 10 | * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the 11 | * Eclipse Foundation. All other trademarks are the property of their respective owners. 12 | */ 13 | /*global Ext, NX*/ 14 | 15 | /** 16 | * Configuration settings in the UI for a Composer proxy recipe. 17 | */ 18 | Ext.define('NX.composer.view.repository.recipe.ComposerProxy', { 19 | extend: 'NX.coreui.view.repository.RepositorySettingsForm', 20 | alias: 'widget.nx-coreui-repository-composer-proxy', 21 | requires: [ 22 | 'NX.coreui.view.repository.facet.ProxyFacet', 23 | 'NX.coreui.view.repository.facet.StorageFacet', 24 | 'NX.coreui.view.repository.facet.HttpClientFacet', 25 | 'NX.coreui.view.repository.facet.NegativeCacheFacet', 26 | 'NX.coreui.view.repository.facet.CleanupPolicyFacet' 27 | ], 28 | 29 | /** 30 | * @override 31 | */ 32 | initComponent: function() { 33 | var me = this; 34 | 35 | me.items = [ 36 | {xtype: 'nx-coreui-repository-proxy-facet'}, 37 | {xtype: 'nx-coreui-repository-storage-facet'}, 38 | {xtype: 'nx-coreui-repository-negativecache-facet'}, 39 | {xtype: 'nx-coreui-repository-httpclient-facet-with-preemptive-auth'}, 40 | {xtype: 'nx-coreui-repository-cleanup-policy-facet'} 41 | ]; 42 | 43 | me.callParent(); 44 | } 45 | }); 46 | -------------------------------------------------------------------------------- /nexus-repository-composer/src/main/java/org/sonatype/nexus/repository/composer/internal/group/ComposerGroupPackagesJsonHandler.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Sonatype Nexus (TM) Open Source Version 3 | * Copyright (c) 2018-present Sonatype, Inc. 4 | * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. 5 | * 6 | * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, 7 | * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. 8 | * 9 | * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks 10 | * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the 11 | * Eclipse Foundation. All other trademarks are the property of their respective owners. 12 | */ 13 | package org.sonatype.nexus.repository.composer.internal.group; 14 | 15 | import java.util.List; 16 | 17 | import javax.inject.Inject; 18 | import javax.inject.Named; 19 | import javax.inject.Singleton; 20 | 21 | import org.sonatype.nexus.repository.Repository; 22 | import org.sonatype.nexus.repository.composer.internal.ComposerJsonProcessor; 23 | import org.sonatype.nexus.repository.view.Content; 24 | import org.sonatype.nexus.repository.view.Payload; 25 | 26 | import static com.google.common.base.Preconditions.checkNotNull; 27 | 28 | /** 29 | * Handler for merging packages.json files together. 30 | */ 31 | @Named 32 | @Singleton 33 | public class ComposerGroupPackagesJsonHandler 34 | extends ComposerGroupMergingHandler 35 | { 36 | private final ComposerJsonProcessor composerJsonProcessor; 37 | 38 | @Inject 39 | public ComposerGroupPackagesJsonHandler(final ComposerJsonProcessor composerJsonProcessor) { 40 | this.composerJsonProcessor = checkNotNull(composerJsonProcessor); 41 | } 42 | 43 | @Override 44 | protected Content merge(final Repository repository, final List payloads) throws Exception { 45 | return composerJsonProcessor.mergePackagesJson(repository, payloads); 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /nexus-repository-composer/src/main/java/org/sonatype/nexus/repository/composer/internal/cleanup/ComposerCleanupPolicyConfiguration.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Sonatype Nexus (TM) Open Source Version 3 | * Copyright (c) 2017-present Sonatype, Inc. 4 | * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. 5 | * 6 | * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, 7 | * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. 8 | * 9 | * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks 10 | * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the 11 | * Eclipse Foundation. All other trademarks are the property of their respective owners. 12 | */ 13 | package org.sonatype.nexus.repository.composer.internal.cleanup; 14 | 15 | import java.util.Map; 16 | 17 | import javax.inject.Named; 18 | import javax.inject.Singleton; 19 | 20 | import org.sonatype.nexus.cleanup.config.CleanupPolicyConfiguration; 21 | import org.sonatype.nexus.repository.composer.ComposerFormat; 22 | 23 | import com.google.common.collect.ImmutableMap; 24 | 25 | import static org.sonatype.nexus.cleanup.config.CleanupPolicyConstants.REGEX_KEY; 26 | import static org.sonatype.nexus.repository.search.index.SearchConstants.IS_PRERELEASE_KEY; 27 | import static org.sonatype.nexus.repository.search.index.SearchConstants.LAST_BLOB_UPDATED_KEY; 28 | import static org.sonatype.nexus.repository.search.index.SearchConstants.LAST_DOWNLOADED_KEY; 29 | 30 | /** 31 | * @since 3.next 32 | */ 33 | @Named(ComposerFormat.NAME) 34 | @Singleton 35 | public class ComposerCleanupPolicyConfiguration 36 | implements CleanupPolicyConfiguration 37 | { 38 | @Override 39 | public Map getConfiguration() { 40 | return ImmutableMap.of(LAST_BLOB_UPDATED_KEY, true, 41 | LAST_DOWNLOADED_KEY, true, 42 | IS_PRERELEASE_KEY, false, 43 | REGEX_KEY, true); 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /nexus-repository-composer/src/test/resources/org/sonatype/nexus/repository/composer/internal/getDistUrl.json: -------------------------------------------------------------------------------- 1 | { 2 | "packages": { 3 | "vendor1\/project1": { 4 | "1.0.0": { 5 | "name": "vendor1\/project1", 6 | "description": "Description 1", 7 | "keywords": [], 8 | "homepage": "", 9 | "version": "1.0.0", 10 | "version_normalized": "1.0.0", 11 | "license": [ 12 | "MIT" 13 | ], 14 | "authors": [ 15 | { 16 | "name": "Author 1", 17 | "homepage": "http:\/\/example.com/author1" 18 | } 19 | ], 20 | "source": { 21 | "type": "git", 22 | "url": "https:\/\/git.example.com\/vendor1\/project1.git", 23 | "reference": "48954c0fa210437795be418e708b379598333d0a" 24 | }, 25 | "dist": { 26 | "type": "zip", 27 | "url": "https:\/\/git.example.com\/zipball\/48954c0fa210437795be418e708b379598333d0a", 28 | "reference": "48954c0fa210437795be418e708b379598333d0a", 29 | "shasum": "" 30 | }, 31 | "time": "2018-01-09T15:53:01+00:00", 32 | "uid": 1 33 | }, 34 | "2.0.0": { 35 | "name": "vendor1\/project1", 36 | "description": "Description 1", 37 | "keywords": [], 38 | "homepage": "", 39 | "version": "2.0.0", 40 | "version_normalized": "2.0.0", 41 | "license": [ 42 | "MIT" 43 | ], 44 | "authors": [ 45 | { 46 | "name": "Author 1", 47 | "homepage": "http:\/\/example.com/author1" 48 | } 49 | ], 50 | "source": { 51 | "type": "git", 52 | "url": "https:\/\/git.example.com\/vendor1\/project1.git", 53 | "reference": "418e708b379598333d0a48954c0fa210437795be" 54 | }, 55 | "dist": { 56 | "type": "zip", 57 | "url": "https:\/\/git.example.com\/zipball\/418e708b379598333d0a48954c0fa210437795be", 58 | "reference": "418e708b379598333d0a48954c0fa210437795be", 59 | "shasum": "" 60 | }, 61 | "time": "2018-01-09T15:53:01+00:00", 62 | "uid": 1 63 | } 64 | } 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /nexus-repository-composer/src/main/java/org/sonatype/nexus/repository/composer/internal/group/ComposerGroupPackageJsonHandler.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Sonatype Nexus (TM) Open Source Version 3 | * Copyright (c) 2018-present Sonatype, Inc. 4 | * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. 5 | * 6 | * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, 7 | * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. 8 | * 9 | * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks 10 | * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the 11 | * Eclipse Foundation. All other trademarks are the property of their respective owners. 12 | */ 13 | package org.sonatype.nexus.repository.composer.internal.group; 14 | 15 | import org.sonatype.nexus.repository.Repository; 16 | import org.sonatype.nexus.repository.composer.internal.ComposerJsonProcessor; 17 | import org.sonatype.nexus.repository.view.Content; 18 | import org.sonatype.nexus.repository.view.Payload; 19 | 20 | import javax.inject.Inject; 21 | import javax.inject.Named; 22 | import javax.inject.Singleton; 23 | import java.io.IOException; 24 | import java.time.OffsetDateTime; 25 | import java.util.List; 26 | 27 | import static com.google.common.base.Preconditions.checkNotNull; 28 | 29 | /** 30 | * Handler for merging package JSON files together. 31 | */ 32 | @Named 33 | @Singleton 34 | public class ComposerGroupPackageJsonHandler 35 | extends ComposerGroupMergingHandler 36 | { 37 | private final ComposerJsonProcessor composerJsonProcessor; 38 | 39 | @Inject 40 | public ComposerGroupPackageJsonHandler(final ComposerJsonProcessor composerJsonProcessor) { 41 | this.composerJsonProcessor = checkNotNull(composerJsonProcessor); 42 | } 43 | 44 | @Override 45 | protected Content merge(final Repository repository, final List payloads) throws IOException { 46 | return composerJsonProcessor.mergePackageJson(repository, payloads, OffsetDateTime.now()); 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /nexus-repository-composer/src/main/java/org/sonatype/nexus/repository/composer/internal/group/ComposerGroupProviderJsonHandler.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Sonatype Nexus (TM) Open Source Version 3 | * Copyright (c) 2018-present Sonatype, Inc. 4 | * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. 5 | * 6 | * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, 7 | * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. 8 | * 9 | * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks 10 | * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the 11 | * Eclipse Foundation. All other trademarks are the property of their respective owners. 12 | */ 13 | package org.sonatype.nexus.repository.composer.internal.group; 14 | 15 | import java.io.IOException; 16 | import java.time.OffsetDateTime; 17 | import java.util.List; 18 | 19 | import javax.inject.Inject; 20 | import javax.inject.Named; 21 | import javax.inject.Singleton; 22 | 23 | import org.sonatype.nexus.repository.Repository; 24 | import org.sonatype.nexus.repository.composer.internal.ComposerJsonProcessor; 25 | import org.sonatype.nexus.repository.view.Content; 26 | import org.sonatype.nexus.repository.view.Payload; 27 | 28 | import static com.google.common.base.Preconditions.checkNotNull; 29 | 30 | /** 31 | * Handler for merging provider JSON files together. 32 | */ 33 | @Named 34 | @Singleton 35 | public class ComposerGroupProviderJsonHandler 36 | extends ComposerGroupMergingHandler 37 | { 38 | private final ComposerJsonProcessor composerJsonProcessor; 39 | 40 | @Inject 41 | public ComposerGroupProviderJsonHandler(final ComposerJsonProcessor composerJsonProcessor) { 42 | this.composerJsonProcessor = checkNotNull(composerJsonProcessor); 43 | } 44 | 45 | @Override 46 | protected Content merge(final Repository repository, final List payloads) throws IOException { 47 | return composerJsonProcessor.mergeProviderJson(repository, payloads, OffsetDateTime.now()); 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /nexus-repository-composer/src/test/resources/org/sonatype/nexus/repository/composer/internal/rewritePackageJson.input.json: -------------------------------------------------------------------------------- 1 | { 2 | "minified": "composer/2.0", 3 | "packages": { 4 | "vendor1/project1": [ 5 | { 6 | "name": "vendor1/project1", 7 | "description": "Description 2", 8 | "keywords": [], 9 | "homepage": "", 10 | "version": "v1.2.3", 11 | "version_normalized": "1.2.3.0", 12 | "license": [ 13 | "MIT" 14 | ], 15 | "authors": [ 16 | { 17 | "name": "Author 1", 18 | "homepage": "https://example.com/author1" 19 | } 20 | ], 21 | "source": { 22 | "type": "git", 23 | "url": "https://git.example.com/vendor1/project1.git", 24 | "reference": "f1d2d2f924e986ac86fdf7b36c94bcdf32beec15" 25 | }, 26 | "dist": { 27 | "type": "zip", 28 | "url": "https://git.example.com/zipball/48954c0fa210437795be418e708b379598333d0a", 29 | "reference": "f1d2d2f924e986ac86fdf7b36c94bcdf32beec15", 30 | "shasum": "" 31 | }, 32 | "time": "2024-09-03T15:30:00+00:00", 33 | "uid": 2 34 | }, 35 | { 36 | "name": "vendor1/project1", 37 | "description": "Description 1", 38 | "keywords": [], 39 | "homepage": "", 40 | "version": "v1.0.0", 41 | "version_normalized": "1.0.0.0", 42 | "license": [ 43 | "MIT" 44 | ], 45 | "authors": [ 46 | { 47 | "name": "Author 1", 48 | "homepage": "https://example.com/author1" 49 | } 50 | ], 51 | "source": { 52 | "type": "git", 53 | "url": "https://git.example.com/vendor1/project1.git", 54 | "reference": "48954c0fa210437795be418e708b379598333d0a" 55 | }, 56 | "dist": { 57 | "type": "zip", 58 | "url": "https://git.example.com/zipball/48954c0fa210437795be418e708b379598333d0a", 59 | "reference": "48954c0fa210437795be418e708b379598333d0a", 60 | "shasum": "" 61 | }, 62 | "time": "2018-01-09T15:53:01+00:00", 63 | "uid": 1 64 | } 65 | ] 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /nexus-repository-composer/src/test/resources/org/sonatype/nexus/repository/composer/internal/rewriteProviderJson.input.json: -------------------------------------------------------------------------------- 1 | { 2 | "packages": { 3 | "vendor1\/project1": { 4 | "branch1": { 5 | "name": "vendor1\/project1", 6 | "description": "Description 1", 7 | "keywords": [], 8 | "homepage": "", 9 | "version": "branch1", 10 | "version_normalized": "branch1", 11 | "license": [ 12 | "MIT" 13 | ], 14 | "authors": [ 15 | { 16 | "name": "Author 1", 17 | "homepage": "http:\/\/example.com/author1" 18 | } 19 | ], 20 | "source": { 21 | "type": "git", 22 | "url": "https:\/\/git.example.com\/vendor1\/project1.git", 23 | "reference": "48954c0fa210437795be418e708b379598333d0a" 24 | }, 25 | "dist": { 26 | "type": "zip", 27 | "url": "https:\/\/git.example.com\/zipball\/48954c0fa210437795be418e708b379598333d0a", 28 | "reference": "48954c0fa210437795be418e708b379598333d0a", 29 | "shasum": "" 30 | }, 31 | "time": "2018-01-09T15:53:01+00:00", 32 | "uid": 1 33 | }, 34 | "branch2": { 35 | "name": "vendor2\/project2", 36 | "description": "Description 2", 37 | "keywords": [], 38 | "homepage": "", 39 | "version": "branch2", 40 | "version_normalized": "branch2", 41 | "license": [ 42 | "MIT" 43 | ], 44 | "authors": [ 45 | { 46 | "name": "Author 2", 47 | "homepage": "http:\/\/example.com/author2" 48 | } 49 | ], 50 | "source": { 51 | "type": "git", 52 | "url": "https:\/\/git.example.com\/vendor2\/project1.git", 53 | "reference": "be418e708b379598333d0a48954c0fa210437795" 54 | }, 55 | "dist": { 56 | "type": "zip", 57 | "url": "https:\/\/git.example.com\/zipball\/be418e708b379598333d0a48954c0fa210437795", 58 | "reference": "be418e708b379598333d0a48954c0fa210437795", 59 | "shasum": "" 60 | }, 61 | "time": "2018-01-09T15:53:01+00:00", 62 | "uid": 2 63 | } 64 | } 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /nexus-repository-composer/src/main/java/org/sonatype/nexus/repository/composer/ComposerHostedFacet.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Sonatype Nexus (TM) Open Source Version 3 | * Copyright (c) 2018-present Sonatype, Inc. 4 | * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. 5 | * 6 | * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, 7 | * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. 8 | * 9 | * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks 10 | * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the 11 | * Eclipse Foundation. All other trademarks are the property of their respective owners. 12 | */ 13 | package org.sonatype.nexus.repository.composer; 14 | 15 | import org.sonatype.nexus.repository.Facet; 16 | import org.sonatype.nexus.repository.content.fluent.FluentAsset; 17 | import org.sonatype.nexus.repository.view.Content; 18 | import org.sonatype.nexus.repository.view.Payload; 19 | 20 | import javax.annotation.Nullable; 21 | import java.io.IOException; 22 | import java.util.Optional; 23 | 24 | /** 25 | * Interface defining the features supported by Composer repository hosted facets. 26 | */ 27 | @Facet.Exposed 28 | public interface ComposerHostedFacet 29 | extends Facet 30 | { 31 | FluentAsset upload(String vendor, String project, String version, String sourceType, String sourceUrl, 32 | String sourceReference, Payload payload) throws IOException; 33 | 34 | Content getPackagesJson() throws IOException; 35 | 36 | Content getListJson(String filter) throws IOException; 37 | 38 | Content getProviderJson(String vendor, String project) throws IOException; 39 | 40 | Content getPackageJson(String vendor, String project) throws IOException; 41 | 42 | Optional rebuildPackageJson(String vendor, String project) throws IOException; 43 | 44 | Optional rebuildProviderJson(String vendor, String project) throws IOException; 45 | 46 | @Nullable 47 | Content getZipball(String path) throws IOException; 48 | } 49 | -------------------------------------------------------------------------------- /nexus-repository-composer/src/main/java/org/sonatype/nexus/repository/composer/ComposerContentFacet.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Sonatype Nexus (TM) Open Source Version 3 | * Copyright (c) 2018-present Sonatype, Inc. 4 | * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. 5 | * 6 | * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, 7 | * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. 8 | * 9 | * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks 10 | * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the 11 | * Eclipse Foundation. All other trademarks are the property of their respective owners. 12 | */ 13 | package org.sonatype.nexus.repository.composer; 14 | 15 | import java.io.IOException; 16 | import java.io.InputStream; 17 | import java.util.Optional; 18 | 19 | import javax.annotation.Nullable; 20 | 21 | import org.sonatype.nexus.repository.Facet; 22 | import org.sonatype.nexus.repository.cache.CacheInfo; 23 | import org.sonatype.nexus.repository.content.facet.ContentFacet; 24 | import org.sonatype.nexus.repository.content.fluent.FluentAsset; 25 | import org.sonatype.nexus.repository.view.Content; 26 | import org.sonatype.nexus.repository.view.Payload; 27 | import org.sonatype.nexus.repository.view.payloads.TempBlob; 28 | 29 | /** 30 | * Content facet used for getting assets from storage and putting assets into storage for a Composer-format repository. 31 | */ 32 | @Facet.Exposed 33 | public interface ComposerContentFacet 34 | extends ContentFacet 35 | { 36 | 37 | Optional getAsset(String path); 38 | 39 | Optional get(String path); 40 | 41 | Content put(String path, Payload payload, AssetKind assetKind) throws IOException; 42 | 43 | FluentAsset put(String path, Payload payload, String sourceType, String sourceUrl, String sourceReference) throws IOException; 44 | 45 | TempBlob getTempBlob(Payload payload); 46 | 47 | TempBlob getTempBlob(InputStream in, @Nullable String contentType); 48 | 49 | void setCacheInfo(String path, Content content, CacheInfo cacheInfo) throws IOException; 50 | } 51 | -------------------------------------------------------------------------------- /nexus-repository-composer/src/main/java/org/sonatype/nexus/repository/composer/internal/ComposerAttributes.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Sonatype Nexus (TM) Open Source Version 3 | * Copyright (c) 2018-present Sonatype, Inc. 4 | * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. 5 | * 6 | * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, 7 | * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. 8 | * 9 | * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks 10 | * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the 11 | * Eclipse Foundation. All other trademarks are the property of their respective owners. 12 | */ 13 | package org.sonatype.nexus.repository.composer.internal; 14 | 15 | /** 16 | * Format attributes specific to Composer. 17 | */ 18 | public final class ComposerAttributes 19 | { 20 | public static final String P_NAME = "name"; 21 | 22 | public static final String P_DESCRIPTION = "description"; 23 | 24 | public static final String P_VERSION = "version"; 25 | 26 | public static final String P_TYPE = "type"; 27 | 28 | public static final String P_KEYWORDS = "keywords"; 29 | 30 | public static final String P_HOMEPAGE = "homepage"; 31 | 32 | public static final String P_TIME = "time"; 33 | 34 | public static final String P_LICENSE = "license"; 35 | 36 | public static final String P_AUTHORS = "authors"; 37 | 38 | public static final String P_SUPPORT_EMAIL = "support_email"; 39 | 40 | public static final String P_SUPPORT_ISSUES = "support_issues"; 41 | 42 | public static final String P_SUPPORT_FORUM = "support_forum"; 43 | 44 | public static final String P_SUPPORT_WIKI = "support_wiki"; 45 | 46 | public static final String P_SUPPORT_IRC = "support_irc"; 47 | 48 | public static final String P_SUPPORT_SOURCE = "support_source"; 49 | 50 | public static final String P_SUPPORT_DOCS = "support_docs"; 51 | 52 | public static final String P_SUPPORT_RSS = "support_rss"; 53 | 54 | public static final String P_VENDOR = "vendor"; 55 | 56 | public static final String P_PROJECT = "project"; 57 | } 58 | -------------------------------------------------------------------------------- /.circleci/config.yml: -------------------------------------------------------------------------------- 1 | version: 2.1 2 | 3 | orbs: 4 | maven: circleci/maven@1.4.1 5 | circleci-maven-release-orb: sonatype-nexus-community/circleci-maven-release-orb@0.0.20 6 | 7 | release-args: &release-args 8 | mvn-release-perform-command: mvn --batch-mode release:perform -s .circleci/.maven.xml -PbuildKar 9 | ssh-fingerprints: "73:40:5d:60:85:c4:02:7e:79:10:30:6e:36:8d:4b:02" 10 | context: rso-base 11 | filters: 12 | branches: 13 | only: master 14 | 15 | jobs: 16 | build_and_test: 17 | docker: 18 | - image: 'cimg/openjdk:17.0' 19 | steps: 20 | - checkout 21 | - maven/with_cache: 22 | verify_dependencies: false 23 | steps: 24 | - run: 25 | name: Run Maven Build 26 | command: | 27 | mvn clean --batch-mode verify -PbuildKar -Dit 28 | - run: 29 | name: Save test results 30 | command: | 31 | mkdir -p ~/project/artifacts/junit/ 32 | find . -type f -regex ".*/target/surefire-reports/.*xml" -exec cp {} ~/project/artifacts/junit/ \; 33 | find . -type f -regex ".*/target/failsafe-reports/.*xml" -exec cp {} ~/project/artifacts/junit/ \; 34 | mkdir -p ~/project/artifacts/it-reports/ 35 | find . -type f -regex ".*/target/it-reports/.*" -exec cp {} ~/project/artifacts/it-reports/ \; 36 | when: always 37 | - store_test_results: 38 | path: ~/project/artifacts/junit 39 | - store_artifacts: 40 | path: ~/project/artifacts/it-reports 41 | 42 | 43 | workflows: 44 | build-branch: 45 | jobs: 46 | - build_and_test: 47 | filters: 48 | branches: 49 | ignore: master 50 | 51 | run-release: 52 | jobs: 53 | - approve-release: 54 | type: approval 55 | filters: 56 | branches: 57 | only: master 58 | - circleci-maven-release-orb/run-maven-release: 59 | requires: 60 | - approve-release 61 | <<: *release-args 62 | 63 | release-from-master: 64 | jobs: 65 | - build_and_test: 66 | filters: 67 | branches: 68 | only: master 69 | - circleci-maven-release-orb/run-maven-release: 70 | requires: 71 | - build_and_test 72 | <<: *release-args 73 | -------------------------------------------------------------------------------- /.github/CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | 15 | ## How to be a contributor to this project 16 | 17 | ### Are you submitting a pull request? 18 | 19 | * Use [our codestyle](https://github.com/sonatype/codestyle). If we get a PR that doesn't match it, there will be 20 | much shaming throughout the land. If you use an editor besides Eclipse or IntelliJ, adapt the codestyle and submit a PR 21 | there :) 22 | * Fill out a CLA for us, so we can sort out all the legal parts of contributing. You can get all the information for 23 | this [here](https://help.sonatype.com/display/NXRM3/Bundle+Development#BundleDevelopment-ContributingBundles). You may go, this is for your book, is it 24 | applicable for this repo? Yes, absolutely. Follow the CLA process and email in your form. We are working on a way to 25 | make this simpler, as well. 26 | * Make sure to fill out an issue for your PR, so that we have traceability as to what you are trying to fix, 27 | versus how you fixed it. 28 | * Try to fix one thing per pull request! Many people work on this code, so the more focused your changes are, the less 29 | of a headache other people will have when they merge their work in. 30 | * Ensure your Pull Request passes tests either locally or via CI (it will run automatically on your PR) 31 | * Make sure to add yourself or your organization to CONTRIBUTORS.md as a part of your PR, if you are new to the project! 32 | * If you're stuck, ask our [gitter channel](https://gitter.im/sonatype/nexus-developers)! There are a number of 33 | experienced programmers who are happy to help with learning and troubleshooting. 34 | 35 | ### Are you new and looking to dive in? 36 | 37 | * Check our issues to see if there is something you can dive in to. 38 | * Come hang out with us at our [gitter channel](https://gitter.im/sonatype/nexus-developers). 39 | -------------------------------------------------------------------------------- /nexus-repository-composer/src/main/java/org/sonatype/nexus/repository/composer/internal/proxy/ComposerProviderHandler.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Sonatype Nexus (TM) Open Source Version 3 | * Copyright (c) 2018-present Sonatype, Inc. 4 | * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. 5 | * 6 | * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, 7 | * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. 8 | * 9 | * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks 10 | * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the 11 | * Eclipse Foundation. All other trademarks are the property of their respective owners. 12 | */ 13 | package org.sonatype.nexus.repository.composer.internal.proxy; 14 | 15 | import javax.annotation.Nonnull; 16 | import javax.inject.Inject; 17 | 18 | import org.sonatype.nexus.repository.composer.internal.ComposerJsonProcessor; 19 | import org.sonatype.nexus.repository.http.HttpResponses; 20 | import org.sonatype.nexus.repository.http.HttpStatus; 21 | import org.sonatype.nexus.repository.view.Context; 22 | import org.sonatype.nexus.repository.view.Handler; 23 | import org.sonatype.nexus.repository.view.Response; 24 | 25 | import static com.google.common.base.Preconditions.checkNotNull; 26 | 27 | /** 28 | * Handler that rewrites the content of responses containing Composer provider JSON files so that they point to the 29 | * proxy repository rather than the repository being proxied. 30 | */ 31 | public class ComposerProviderHandler 32 | implements Handler 33 | { 34 | public static final String DO_NOT_REWRITE = "ComposerProviderHandler.doNotRewrite"; 35 | 36 | private final ComposerJsonProcessor composerJsonProcessor; 37 | 38 | @Inject 39 | public ComposerProviderHandler(final ComposerJsonProcessor composerJsonProcessor) { 40 | this.composerJsonProcessor = checkNotNull(composerJsonProcessor); 41 | } 42 | 43 | @Nonnull 44 | @Override 45 | public Response handle(@Nonnull final Context context) throws Exception { 46 | Response response = context.proceed(); 47 | if (!Boolean.parseBoolean(context.getRequest().getAttributes().get(DO_NOT_REWRITE, String.class))) { 48 | if (response.getStatus().getCode() == HttpStatus.OK && response.getPayload() != null) { 49 | response = HttpResponses 50 | .ok(composerJsonProcessor.rewriteProviderJson(context.getRepository(), response.getPayload())); 51 | } 52 | } 53 | return response; 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /nexus-repository-composer/src/main/java/org/sonatype/nexus/repository/composer/internal/ComposerMaintenanceFacet.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Sonatype Nexus (TM) Open Source Version 3 | * Copyright (c) 2018-present Sonatype, Inc. 4 | * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. 5 | * 6 | * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, 7 | * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. 8 | * 9 | * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks 10 | * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the 11 | * Eclipse Foundation. All other trademarks are the property of their respective owners. 12 | */ 13 | package org.sonatype.nexus.repository.composer.internal; 14 | 15 | import com.google.common.collect.ImmutableSet; 16 | import org.sonatype.nexus.repository.Facet; 17 | import org.sonatype.nexus.repository.composer.ComposerHostedFacet; 18 | import org.sonatype.nexus.repository.content.Component; 19 | import org.sonatype.nexus.repository.content.maintenance.LastAssetMaintenanceFacet; 20 | 21 | import javax.inject.Named; 22 | import java.io.IOException; 23 | import java.util.Optional; 24 | import java.util.Set; 25 | 26 | /** 27 | * Facet for maintenance of Composer artifacts. 28 | */ 29 | @Facet.Exposed 30 | @Named 31 | public class ComposerMaintenanceFacet 32 | extends LastAssetMaintenanceFacet 33 | { 34 | @Override 35 | public Set deleteComponent(final Component component) { 36 | ImmutableSet.Builder deletedPaths = ImmutableSet.builder(); 37 | deletedPaths.addAll(super.deleteComponent(component)); 38 | 39 | String vendor = component.namespace(); 40 | String project = component.name(); 41 | 42 | Optional hostedFacet = composerHosted(); 43 | if (hostedFacet.isPresent()) { 44 | try { 45 | if (!hostedFacet.get().rebuildPackageJson(vendor, project).isPresent()) { 46 | deletedPaths.add(ComposerPathUtils.buildPackagePath(vendor, project)); 47 | } 48 | } catch (IOException e) { 49 | // update failed 50 | } 51 | try { 52 | if (!hostedFacet.get().rebuildProviderJson(vendor, project).isPresent()) { 53 | deletedPaths.add(ComposerPathUtils.buildProviderPath(vendor, project)); 54 | } 55 | } catch (IOException e) { 56 | // update failed 57 | } 58 | } 59 | 60 | return deletedPaths.build(); 61 | } 62 | 63 | private Optional composerHosted() { 64 | return optionalFacet(ComposerHostedFacet.class); 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /nexus-repository-composer/src/main/java/org/sonatype/nexus/repository/composer/internal/group/ComposerGroupMergingHandler.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Sonatype Nexus (TM) Open Source Version 3 | * Copyright (c) 2018-present Sonatype, Inc. 4 | * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. 5 | * 6 | * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, 7 | * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. 8 | * 9 | * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks 10 | * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the 11 | * Eclipse Foundation. All other trademarks are the property of their respective owners. 12 | */ 13 | package org.sonatype.nexus.repository.composer.internal.group; 14 | 15 | import java.util.List; 16 | import java.util.Map; 17 | import java.util.stream.Collectors; 18 | 19 | import javax.annotation.Nonnull; 20 | 21 | import org.sonatype.nexus.repository.Repository; 22 | import org.sonatype.nexus.repository.group.GroupFacet; 23 | import org.sonatype.nexus.repository.group.GroupHandler; 24 | import org.sonatype.nexus.repository.http.HttpResponses; 25 | import org.sonatype.nexus.repository.http.HttpStatus; 26 | import org.sonatype.nexus.repository.view.Content; 27 | import org.sonatype.nexus.repository.view.Context; 28 | import org.sonatype.nexus.repository.view.Payload; 29 | import org.sonatype.nexus.repository.view.Response; 30 | 31 | import static org.sonatype.nexus.repository.http.HttpConditions.makeConditional; 32 | import static org.sonatype.nexus.repository.http.HttpConditions.makeUnconditional; 33 | 34 | /** 35 | * Abstract handler for merging in the context of a Composer group repository, with merging left to concrete 36 | * implementations of the class. 37 | */ 38 | public abstract class ComposerGroupMergingHandler 39 | extends GroupHandler 40 | { 41 | @Override 42 | protected final Response doGet(@Nonnull final Context context, 43 | @Nonnull final GroupHandler.DispatchedRepositories dispatched) 44 | throws Exception 45 | { 46 | Repository repository = context.getRepository(); 47 | GroupFacet groupFacet = repository.facet(GroupFacet.class); 48 | 49 | makeUnconditional(context.getRequest()); 50 | Map responses; 51 | try { 52 | responses = getAll(context, groupFacet.members(), dispatched); 53 | } 54 | finally { 55 | makeConditional(context.getRequest()); 56 | } 57 | 58 | List payloads = responses.values().stream() 59 | .filter(response -> response.getStatus().getCode() == HttpStatus.OK && response.getPayload() != null) 60 | .map(Response::getPayload) 61 | .collect(Collectors.toList()); 62 | if (payloads.isEmpty()) { 63 | return notFoundResponse(context); 64 | } 65 | return HttpResponses.ok(merge(repository, payloads)); 66 | } 67 | 68 | protected abstract Content merge(final Repository repository, final List payloads) throws Exception; 69 | } 70 | -------------------------------------------------------------------------------- /nexus-repository-composer/src/test/resources/org/sonatype/nexus/repository/composer/internal/rewritePackagesJson.input1.json: -------------------------------------------------------------------------------- 1 | { 2 | "packages": [], 3 | "notify-batch": "https://packages.example.com/downloads/", 4 | "providers-url": "/p/%package%$%hash%.json", 5 | "metadata-url": "/p2/%package%.json", 6 | "metadata-changes-url": "https://packages.example.com/metadata/changes.json", 7 | "search": "https://packages.example.com/search.json?q=%query%&type=%type%", 8 | "list": "https://packages.example.com/packages/list.json", 9 | "security-advisories": { 10 | "metadata": true, 11 | "api-url": "https://packages.example.com/api/security-advisories/" 12 | }, 13 | "providers-url": "https://packages.example.com/providers/%package%.json", 14 | "warning": "Support for Composer 1 will be shutdown on August 1st 2025. You should upgrade to Composer 2. See https://blog.packagist.com/shutting-down-packagist-org-support-for-composer-1-x/", 15 | "warning-versions": "<1.99", 16 | "provider-includes": { 17 | "p/provider-2013$%hash%.json": { 18 | "sha256": "3a1a08510ebf88e41f79e49f1f2c75dafdaac1a7c0c0ce8cddd27ce0cc632b80" 19 | }, 20 | "p/provider-2014$%hash%.json": { 21 | "sha256": "4bd95b482146ccc2c330931ddb55443fafcbe6689052f87ca2bcd4d1e8ccdc7a" 22 | }, 23 | "p/provider-2015$%hash%.json": { 24 | "sha256": "5a760411c2529cd81efb90a3c56c28d08bf25bf30011b0957db641110f1f1f64" 25 | }, 26 | "p/provider-2016$%hash%.json": { 27 | "sha256": "ce85bbb0a192b0e4fa56c4df27535bb69797cdae0cc5ac6f14a0b15f602f9c9b" 28 | }, 29 | "p/provider-2017$%hash%.json": { 30 | "sha256": "dc670722818538f4c622edcc705cc453a4686ac234fd95a156969f07e9e55a29" 31 | }, 32 | "p/provider-2018$%hash%.json": { 33 | "sha256": "8ed52948d4faedc29988d1bd819c2fa0e2984c882a61d4e176f14e09eb95151f" 34 | }, 35 | "p/provider-2019$%hash%.json": { 36 | "sha256": "f0ec1097d32505433841a8176c6702da27fd37e16d5fa0d7b6ac3845bb0795fd" 37 | }, 38 | "p/provider-2020$%hash%.json": { 39 | "sha256": "2dd9d8130a43efa50734fa2ef387ab92b7d2ba1e0a38e79657f6d95325422812" 40 | }, 41 | "p/provider-2021$%hash%.json": { 42 | "sha256": "9de69427354f1b70f6635011a4f5a38cac03a84a5143ac80dfe11158625b165a" 43 | }, 44 | "p/provider-2022$%hash%.json": { 45 | "sha256": "8fd318bf990cefc69494ca16800973de19e0e5f39409baf11da443bc33821544" 46 | }, 47 | "p/provider-2023$%hash%.json": { 48 | "sha256": "2316930423015692b8b28d00266ddc8570d48573af3dc92ea54a1ad11305ade6" 49 | }, 50 | "p/provider-2023-10$%hash%.json": { 51 | "sha256": "c2edd75751df0075da1dadb8678597290587a6c10f26d6d8fd3e8aa93421c701" 52 | }, 53 | "p/provider-2024-01$%hash%.json": { 54 | "sha256": "b8b43aefcc4e46c54f3845aca40604545c4ce6d9ce4b76421d5e4fc7b35d2d69" 55 | }, 56 | "p/provider-2024-04$%hash%.json": { 57 | "sha256": "d2552bab94277b78403d4b2dda4e3af3e9cc721ff165f2da91d8c171ef9e6407" 58 | }, 59 | "p/provider-2024-07$%hash%.json": { 60 | "sha256": "ff698f838bac765ea2b1b42162e28fdec39200185847276d898145b861ac2bb8" 61 | }, 62 | "p/provider-archived$%hash%.json": { 63 | "sha256": "b94a6bc514d870cd951d4fc99dcd83194173ff7391f68f11325cfc02ae3681c1" 64 | }, 65 | "p/provider-latest$%hash%.json": { 66 | "sha256": "c885deff93d6d57ff803e2816811f6476804f4c4fd4a6e233079081459a183c0" 67 | } 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /nexus-repository-composer/src/main/java/org/sonatype/nexus/repository/composer/internal/hosted/ComposerHostedDownloadHandler.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Sonatype Nexus (TM) Open Source Version 3 | * Copyright (c) 2018-present Sonatype, Inc. 4 | * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. 5 | * 6 | * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, 7 | * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. 8 | * 9 | * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks 10 | * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the 11 | * Eclipse Foundation. All other trademarks are the property of their respective owners. 12 | */ 13 | package org.sonatype.nexus.repository.composer.internal.hosted; 14 | 15 | import javax.annotation.Nonnull; 16 | import javax.annotation.Nullable; 17 | import javax.inject.Named; 18 | import javax.inject.Singleton; 19 | 20 | import org.sonatype.nexus.repository.Repository; 21 | import org.sonatype.nexus.repository.composer.AssetKind; 22 | import org.sonatype.nexus.repository.composer.ComposerHostedFacet; 23 | import org.sonatype.nexus.repository.http.HttpResponses; 24 | import org.sonatype.nexus.repository.view.Content; 25 | import org.sonatype.nexus.repository.view.Context; 26 | import org.sonatype.nexus.repository.view.Handler; 27 | import org.sonatype.nexus.repository.view.Response; 28 | 29 | import static org.sonatype.nexus.repository.composer.internal.ComposerPathUtils.buildZipballPath; 30 | import static org.sonatype.nexus.repository.composer.internal.ComposerPathUtils.getProjectToken; 31 | import static org.sonatype.nexus.repository.composer.internal.ComposerPathUtils.getVendorToken; 32 | 33 | /** 34 | * Download handler for Composer hosted repositories. 35 | */ 36 | @Named 37 | @Singleton 38 | public class ComposerHostedDownloadHandler 39 | implements Handler 40 | { 41 | @Nonnull 42 | @Override 43 | public Response handle(@Nonnull final Context context) throws Exception { 44 | Repository repository = context.getRepository(); 45 | ComposerHostedFacet hostedFacet = repository.facet(ComposerHostedFacet.class); 46 | AssetKind assetKind = context.getAttributes().require(AssetKind.class); 47 | switch (assetKind) { 48 | case PACKAGES: 49 | return HttpResponses.ok(hostedFacet.getPackagesJson()); 50 | case LIST: 51 | return responseFor(hostedFacet.getListJson(context.getRequest().getParameters().get("filter"))); 52 | case PROVIDER: 53 | return responseFor(hostedFacet.getProviderJson(getVendorToken(context), getProjectToken(context))); 54 | case PACKAGE: 55 | return responseFor(hostedFacet.getPackageJson(getVendorToken(context), getProjectToken(context))); 56 | case ZIPBALL: 57 | return responseFor(hostedFacet.getZipball(buildZipballPath(context))); 58 | default: 59 | throw new IllegalStateException("Unexpected assetKind: " + assetKind); 60 | } 61 | } 62 | 63 | private Response responseFor(@Nullable final Content content) { 64 | if (content == null) { 65 | return HttpResponses.notFound(); 66 | } 67 | return HttpResponses.ok(content); 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /nexus-repository-composer/src/test/java/org/sonatype/nexus/repository/composer/internal/browse/ComposerBrowseNodeGeneratorTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Sonatype Nexus (TM) Open Source Version 3 | * Copyright (c) 2024-present Sonatype, Inc. 4 | * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. 5 | * 6 | * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, 7 | * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. 8 | * 9 | * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks 10 | * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the 11 | * Eclipse Foundation. All other trademarks are the property of their respective owners. 12 | */ 13 | package org.sonatype.nexus.repository.composer.internal.browse; 14 | 15 | import org.junit.Test; 16 | import org.sonatype.goodies.testsupport.TestSupport; 17 | import org.sonatype.nexus.repository.browse.node.BrowsePath; 18 | import org.sonatype.nexus.repository.content.Component; 19 | import org.sonatype.nexus.repository.content.store.AssetData; 20 | import org.sonatype.nexus.repository.content.store.ComponentData; 21 | 22 | import java.util.Arrays; 23 | import java.util.List; 24 | 25 | import static org.junit.Assert.assertEquals; 26 | 27 | public class ComposerBrowseNodeGeneratorTest 28 | extends TestSupport 29 | { 30 | private final ComposerBrowseNodeGenerator underTest = new ComposerBrowseNodeGenerator(); 31 | 32 | @Test 33 | public void testComputeAssetPaths() { 34 | AssetData asset = new AssetData(); 35 | asset.setPath("/v3nd0r/p4ck4g3/13.3.7/v3nd0r-p4ck4g3-13.3.7.zip"); 36 | asset.setComponent(createComponent("v3nd0r", "p4ck4g3", "13.3.7")); 37 | List browsePaths = underTest.computeAssetPaths(asset); 38 | assertEquals( 39 | Arrays.asList( 40 | new BrowsePath("v3nd0r", "/v3nd0r/"), 41 | new BrowsePath("p4ck4g3", "/v3nd0r/p4ck4g3/"), 42 | new BrowsePath("13.3.7", "/v3nd0r/p4ck4g3/13.3.7/"), 43 | new BrowsePath("v3nd0r-p4ck4g3-13.3.7.zip", "/v3nd0r/p4ck4g3/13.3.7/v3nd0r-p4ck4g3-13.3.7.zip") 44 | ), 45 | browsePaths 46 | ); 47 | } 48 | @Test 49 | public void computeComponentPaths() { 50 | AssetData asset = new AssetData(); 51 | asset.setPath("/v3nd0r/p4ck4g3/13.3.7/v3nd0r-p4ck4g3-13.3.7.zip"); 52 | asset.setComponent(createComponent("v3nd0r", "p4ck4g3", "13.3.7")); 53 | List browsePaths = underTest.computeAssetPaths(asset); 54 | assertEquals( 55 | Arrays.asList( 56 | new BrowsePath("v3nd0r", "/v3nd0r/"), 57 | new BrowsePath("p4ck4g3", "/v3nd0r/p4ck4g3/"), 58 | new BrowsePath("13.3.7", "/v3nd0r/p4ck4g3/13.3.7/"), 59 | new BrowsePath("v3nd0r-p4ck4g3-13.3.7.zip", "/v3nd0r/p4ck4g3/13.3.7/v3nd0r-p4ck4g3-13.3.7.zip") 60 | ), 61 | browsePaths 62 | ); 63 | } 64 | 65 | private static Component createComponent(String vendor, String packageName, String version) { 66 | ComponentData componentData = new ComponentData(); 67 | componentData.setRepositoryId(1); 68 | componentData.setComponentId(1); 69 | componentData.setNamespace(vendor); 70 | componentData.setName(packageName); 71 | componentData.setVersion(version); 72 | return componentData; 73 | } 74 | } 75 | -------------------------------------------------------------------------------- /SECURITY.md: -------------------------------------------------------------------------------- 1 | 8 | 9 | # Reporting Security Vulnerabilities 10 | 11 | ## When to report 12 | 13 | First check 14 | [Important advisories of known security vulnerabilities in Sonatype products](https://support.sonatype.com/hc/en-us/sections/203012668-Security-Advisories) 15 | to see if this has been previously reported. 16 | 17 | ## How to report 18 | 19 | Please email reports regarding security related issues you find to [mailto:security@sonatype.com](security@sonatype.com). 20 | 21 | Use our public key below to keep your message safe. 22 | 23 | ## What to include 24 | 25 | Please use a descriptive subject line in your email report. 26 | 27 | Your name and/or affiliation. 28 | 29 | A detailed technical description of the vulnerability, attack scenario and where 30 | possible, how we can reproduce your findings. 31 | 32 | Provide us with a secure way to respond. 33 | 34 | ## What to expect 35 | 36 | Your email will be acknowledged within 1 - 2 business days, and you'll receive a 37 | more detailed response to your email within 7 business days. 38 | 39 | We ask that everyone please follow responsible disclosure practices and allow 40 | time for us to release a fix prior to public release. 41 | 42 | Once an issue is reported, Sonatype uses the following disclosure process: 43 | 44 | When a report is received, we confirm the issue and determine its severity. 45 | 46 | If third-party services or software require mitigation before publication, those 47 | projects will be notified. 48 | 49 | ## Our public key 50 | 51 | ```console 52 | -----BEGIN PUBLIC KEY BLOCK----- 53 | mQENBFF+a9ABCADQWSAAU7w9i71Zn3TQ6k7lT9x57cRdtX7V709oeN/c/1it+gCw 54 | onmmCyf4ypor6XcPSOasp/x0s3hVuf6YfMbI0tSwJUWWihrmoPGIXtmiSOotQE0Q 55 | Sav41xs3YyI9LzQB4ngZR/nhp4YhioD1dVorD6LGXk08rvl2ikoqHwTagbEXZJY7 56 | 3VYhW6JHbZTLwCsfyg6uaSYF1qXfUxHPOiHYKNbhK/tM3giX+9ld/7xi+9f4zEFQ 57 | eX9wcRTdgdDOAqDOK7MV30KXagSqvW0MgEYtKX6q4KjjRzBYjkiTdFW/yMXub/Bs 58 | 5UckxHTCuAmvpr5J0HIUeLtXi1QCkijyn8HJABEBAAG0KVNvbmF0eXBlIFNlY3Vy 59 | aXR5IDxzZWN1cml0eUBzb25hdHlwZS5jb20+iQE4BBMBAgAiBQJRfmvQAhsDBgsJ 60 | CAcDAgYVCAIJCgsEFgIDAQIeAQIXgAAKCRAgkmxsNtgwfUzbCACLtCgieq1kJOqo 61 | 2i136ND5ZOj31zIzNENLn8dhSg5zQwTHOcntWAtS8uCNq4fSlslwvlbPYWTLD7fE 62 | iJn1z7BCU8gBk+pkAJJFWEPweMVt+9bYQ4HfKceGbJeuwBBhS34SK9ZIp9gfxxfA 63 | oTm0aGYwKR5wH3sqL/mrhwKhPt9wXR4qwlE635STEX8wzJ5SBqf3ArJUtCp1rzgR 64 | Dx+DiZed5HE1pOI2Kyb6O80bm485WThPXxpvp3bfzTNYoGzeLi/F7WkmgggkXxsT 65 | Pyd0sSx0B/MO4lJtQvEBlIHDFno9mXa30fKl+rzp2geG5UxNHJUjaC5JhfWLEXEX 66 | wV0ErBsmuQENBFF+a9ABCADXj04+GLIz8VCaZH554nUHEhaKoiIXH3Tj7UiMZDqy 67 | o4WIw2RFaCQNA8T0R5Q0yxINU146JQMbA2SN59AGcGYZcajyEvTR7tLG0meMO6S0 68 | JWpkX7s3xaC0s+5SJ/ba00oHGzW0aotgzG9BWA5OniNHK7zZKMVu7M80M/wB1RvK 69 | x775hAeJ+8F9MDJ+ijydBtaOfDdkbg+0kU1xR6Io+vVLPk38ghlWU8QFP4/B0oWi 70 | jK4xiDqK6cG7kyH9kC9nau+ckH8MrJ/RzEpsc4GRwqS4IEnvHWe7XbgydWS1bCp6 71 | 8uP5ma3d02elQmSEa+PABIPKnZcAf1YKLr9O/+IzEdOhABEBAAGJAR8EGAECAAkF 72 | AlF+a9ACGwwACgkQIJJsbDbYMH3WzAf/XOm4YQZFOgG2h9d03m8me8d1vrYico+0 73 | pBYU9iCozLgamM4er9Efb+XzfLvNVKuqyR0cgvGszukIPQYeX58DMrZ07C+E0wDZ 74 | bG+ZAYXT5GqsHkSVnMCVIfyJNLjR4sbVzykyVtnccBL6bP3jxbCP1jJdT7bwiKre 75 | 1jQjvyoL0yIegdiN/oEdmx52Fqjt4NkQsp4sk625UBFTVISr22bnf60ZIGgrRbAP 76 | DU1XMdIrmqmhEEQcXMp4CeflDMksOmaIeAUkZY7eddnXMwQDJTnz5ziCal+1r0R3 77 | dh0XISRG0NkiLEXeGkrs7Sn7BAAsTsaH/1zU6YbvoWlMlHYT6EarFQ== =sFGt 78 | -----END PUBLIC KEY BLOCK----- 79 | ``` 80 | -------------------------------------------------------------------------------- /nexus-repository-composer/src/test/java/org/sonatype/nexus/repository/composer/internal/ComposerJsonExtractorTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Sonatype Nexus (TM) Open Source Version 3 | * Copyright (c) 2018-present Sonatype, Inc. 4 | * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. 5 | * 6 | * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, 7 | * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. 8 | * 9 | * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks 10 | * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the 11 | * Eclipse Foundation. All other trademarks are the property of their respective owners. 12 | */ 13 | package org.sonatype.nexus.repository.composer.internal; 14 | 15 | import java.io.InputStream; 16 | import java.io.InputStreamReader; 17 | import java.nio.charset.StandardCharsets; 18 | import java.util.Map; 19 | 20 | import org.sonatype.goodies.testsupport.TestSupport; 21 | import org.sonatype.nexus.blobstore.api.Blob; 22 | 23 | import com.fasterxml.jackson.databind.ObjectMapper; 24 | import com.google.common.io.CharStreams; 25 | import org.junit.Before; 26 | import org.junit.Test; 27 | import org.mockito.Mock; 28 | 29 | import static org.hamcrest.MatcherAssert.assertThat; 30 | import static org.hamcrest.Matchers.is; 31 | import static org.mockito.Mockito.when; 32 | import static org.skyscreamer.jsonassert.JSONAssert.assertEquals; 33 | 34 | public class ComposerJsonExtractorTest 35 | extends TestSupport 36 | { 37 | @Mock 38 | private Blob blob; 39 | 40 | private ComposerJsonExtractor underTest; 41 | 42 | @Before 43 | public void setUp() { 44 | underTest = new ComposerJsonExtractor(); 45 | } 46 | 47 | @Test 48 | public void extractInfoFromZipballWithJson() throws Exception { 49 | String expected; 50 | try (InputStream in = getClass().getResourceAsStream("extractInfoFromZipballWithJson.composer.json")) { 51 | expected = CharStreams.toString(new InputStreamReader(in, StandardCharsets.UTF_8)); 52 | } 53 | String actual; 54 | try (InputStream in = getClass().getResourceAsStream("extractInfoFromZipballWithJson.zip")) { 55 | when(blob.getInputStream()).thenReturn(in); 56 | actual = new ObjectMapper().writeValueAsString(underTest.extractFromZip(blob)); 57 | } 58 | assertEquals(expected, actual, true); 59 | } 60 | 61 | @Test 62 | public void extractInfoFromZipballWithJsonComposerArchived() throws Exception { 63 | String expected; 64 | try (InputStream in = getClass().getResourceAsStream("extractInfoFromZipballWithJsonComposerArchived.composer.json")) { 65 | expected = CharStreams.toString(new InputStreamReader(in, StandardCharsets.UTF_8)); 66 | } 67 | String actual; 68 | try (InputStream in = getClass().getResourceAsStream("extractInfoFromZipBallWithJsonComposerArchived.zip")) { 69 | when(blob.getInputStream()).thenReturn(in); 70 | actual = new ObjectMapper().writeValueAsString(underTest.extractFromZip(blob)); 71 | } 72 | assertEquals(expected, actual, true); 73 | } 74 | 75 | @Test 76 | public void extractInfoFromZipballWithoutJson() throws Exception { 77 | Map results; 78 | try (InputStream in = getClass().getResourceAsStream("extractInfoFromZipballWithoutJson.zip")) { 79 | when(blob.getInputStream()).thenReturn(in); 80 | results = underTest.extractFromZip(blob); 81 | } 82 | assertThat(results.isEmpty(), is(true)); 83 | } 84 | } 85 | -------------------------------------------------------------------------------- /nexus-repository-composer/src/test/resources/org/sonatype/nexus/repository/composer/internal/mergeProviderJson.input1.json: -------------------------------------------------------------------------------- 1 | { 2 | "packages": { 3 | "vendor1/project1": { 4 | "1.0.0": { 5 | "name": "vendor1/project1", 6 | "description": "Description 1", 7 | "keywords": ["keyword1"], 8 | "homepage": "http://www.example.com/homepage1", 9 | "version": "1.0.0", 10 | "version_normalized": "1.0.0", 11 | "license": [ 12 | "MIT" 13 | ], 14 | "authors": [ 15 | { 16 | "name": "Author 1", 17 | "homepage": "http://example.com/author1" 18 | } 19 | ], 20 | "dist": { 21 | "type": "zip", 22 | "url": "http://nexus.repo/base/repo/vendor1/project1/1.0.0/vendor1-project1-1.0.0.zip", 23 | "reference": "48954c0fa210437795be418e708b379598333d0a", 24 | "shasum": "" 25 | }, 26 | "time": "2018-01-09T15:53:01+00:00", 27 | "uid": 1, 28 | "autoload": { 29 | "psr-4": { 30 | "psr-1-key": "psr-1-value" 31 | } 32 | }, 33 | "autoload-dev": { 34 | "psr-4": { 35 | "psr-1-key": "psr-1-value" 36 | } 37 | }, 38 | "include-path": [ 39 | "include-path-1" 40 | ], 41 | "replace": { 42 | "replace-1": "replace-1-value" 43 | }, 44 | "require": { 45 | "dependency-1": "version-1" 46 | }, 47 | "require-dev": { 48 | "dev-dependency-1": "dev-version-1" 49 | }, 50 | "suggest": { 51 | "suggest-1": "description-1" 52 | }, 53 | "bin": [ 54 | "bin-1" 55 | ], 56 | "conflict": { 57 | "conflict-1": "version-1" 58 | }, 59 | "extra": { 60 | "branch-alias": { 61 | "branch-1": "version-1" 62 | } 63 | }, 64 | "provide" : { 65 | "provide-1" : "version-1" 66 | }, 67 | "target-dir" : "target-dir-1", 68 | "scripts": { 69 | "scripts-1": [ 70 | "script-1" 71 | ] 72 | }, 73 | "type" : "type-1-value" 74 | } 75 | }, 76 | "vendor3/project3": { 77 | "2.0.0": { 78 | "name": "vendor3/project3", 79 | "description": "Description 3", 80 | "keywords": ["keyword3"], 81 | "homepage": "http://www.example.com/homepage3", 82 | "version": "2.0.0", 83 | "version_normalized": "2.0.0", 84 | "license": [ 85 | "MIT" 86 | ], 87 | "authors": [ 88 | { 89 | "name": "Author 2", 90 | "homepage": "http://example.com/author2" 91 | } 92 | ], 93 | "dist": { 94 | "type": "zip", 95 | "url": "http://nexus.repo/base/repo/vendor1/project1/2.0.0/vendor1-project1-2.0.0.zip", 96 | "reference": "be418e708b379598333d0a48954c0fa210437795", 97 | "shasum": "" 98 | }, 99 | "time": "2018-01-09T15:53:01+00:00", 100 | "uid": 2, 101 | "autoload": { 102 | "psr-4": { 103 | "psr-2-key": "psr-2-value" 104 | } 105 | }, 106 | "require": { 107 | "dependency-2": "version-2" 108 | }, 109 | "require-dev": { 110 | "dev-dependency-2": "dev-version-2" 111 | }, 112 | "suggest": { 113 | "suggest-2": "description-2" 114 | }, 115 | "type" : "type-2-value" 116 | } 117 | } 118 | } 119 | } 120 | -------------------------------------------------------------------------------- /nexus-repository-composer/src/main/java/org/sonatype/nexus/repository/composer/internal/browse/ComposerBrowseNodeGenerator.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Sonatype Nexus (TM) Open Source Version 3 | * Copyright (c) 2024-present Sonatype, Inc. 4 | * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. 5 | * 6 | * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, 7 | * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. 8 | * 9 | * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks 10 | * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the 11 | * Eclipse Foundation. All other trademarks are the property of their respective owners. 12 | */ 13 | package org.sonatype.nexus.repository.composer.internal.browse; 14 | 15 | import org.sonatype.nexus.repository.browse.node.BrowsePath; 16 | import org.sonatype.nexus.repository.composer.ComposerFormat; 17 | import org.sonatype.nexus.repository.content.Asset; 18 | import org.sonatype.nexus.repository.content.Component; 19 | import org.sonatype.nexus.repository.content.browse.ComponentPathBrowseNodeGenerator; 20 | 21 | import javax.inject.Named; 22 | import javax.inject.Singleton; 23 | import java.util.ArrayList; 24 | import java.util.List; 25 | 26 | import static com.google.common.base.Preconditions.checkNotNull; 27 | import static org.sonatype.nexus.common.text.Strings2.isBlank; 28 | import static org.sonatype.nexus.repository.browse.node.BrowsePathBuilder.appendPath; 29 | import static org.sonatype.nexus.repository.browse.node.BrowsePathBuilder.fromPaths; 30 | 31 | /** 32 | * Browse node generator for Composer. 33 | */ 34 | @Singleton 35 | @Named(ComposerFormat.NAME) 36 | public class ComposerBrowseNodeGenerator 37 | extends ComponentPathBrowseNodeGenerator { 38 | 39 | @Override 40 | public List computeAssetPaths(final Asset asset) { 41 | checkNotNull(asset); 42 | 43 | return asset.component().map(component -> { 44 | 45 | // place asset under component, but use its true path as the request path for permission checks 46 | List assetPaths = computeComponentPaths(asset); 47 | appendPath(assetPaths, lastSegment(asset.path()), asset.path()); 48 | return assetPaths; 49 | 50 | }).orElseGet(() -> super.computeAssetPaths(asset)); 51 | } 52 | 53 | /** 54 | * Generates browse nodes to the component using the standard Maven layout. 55 | */ 56 | @Override 57 | public List computeComponentPaths(final Asset asset) { 58 | checkNotNull(asset); 59 | 60 | Component component = asset.component().get(); // NOSONAR: caller guarantees this 61 | 62 | List componentPath = pathToArtifactFolder(component); 63 | 64 | String version = component.version(); 65 | if (!isBlank(version) && !version.equals(componentPath.get(componentPath.size() - 1))) { 66 | componentPath.add(version); 67 | } 68 | 69 | return fromPaths(componentPath, true); 70 | } 71 | 72 | /** 73 | * Generates a path to the artifact folder using the standard Composer layout. 74 | *
    75 | *
  • Schema: /vendor/project/version/ 76 | *
77 | */ 78 | private List pathToArtifactFolder(Component component) { 79 | List paths = new ArrayList<>(); 80 | 81 | String vendor = component.namespace(); 82 | String project = component.name(); 83 | String version = component.version(); 84 | 85 | paths.add(vendor); 86 | paths.add(project); 87 | paths.add(version); 88 | 89 | return paths; 90 | } 91 | } 92 | -------------------------------------------------------------------------------- /nexus-repository-composer/src/test/resources/org/sonatype/nexus/repository/composer/internal/mergeProviderJson.input2.json: -------------------------------------------------------------------------------- 1 | { 2 | "packages" : { 3 | "vendor1/project1" : { 4 | "1.0.0" : { 5 | "name": "vendor1/project1", 6 | "version": "1.0.0", 7 | "dist" : { 8 | "url" : "http://nexus.repo/base/member2/vendor1/project1/1.0.0/vendor1-project1-1.0.0.zip", 9 | "type" : "zip", 10 | "reference" : "ref1", 11 | "shasum" : "sha1" 12 | }, 13 | "uid" : 1345846687, 14 | "time" : "1982-06-04T16:30:00+00:00", 15 | "autoload": { 16 | "psr-4": { 17 | "psr-3-key": "psr-3-value" 18 | } 19 | }, 20 | "require": { 21 | "dependency-3": "version-3" 22 | }, 23 | "require-dev": { 24 | "dev-dependency-3": "dev-version-3" 25 | }, 26 | "suggest": { 27 | "suggest-3": "description-3" 28 | }, 29 | "type": "type-1-value" 30 | }, 31 | "2.0.0" : { 32 | "name": "vendor1/project1", 33 | "version": "2.0.0", 34 | "dist" : { 35 | "url" : "http://nexus.repo/base/member2/vendor1/project1/2.0.0/vendor1-project1-2.0.0.zip", 36 | "type" : "zip", 37 | "reference" : "ref2", 38 | "shasum" : "sha2" 39 | }, 40 | "uid" : 1700253429, 41 | "time" : "2008-05-15T16:30:00+00:00", 42 | "autoload": { 43 | "psr-4": { 44 | "psr-4-key": "psr-4-value" 45 | } 46 | }, 47 | "require": { 48 | "dependency-4": "version-4" 49 | }, 50 | "require-dev": { 51 | "dev-dependency-4": "dev-version-4" 52 | }, 53 | "suggest": { 54 | "suggest-4": "description-4" 55 | }, 56 | "type": "type-2-value" 57 | } 58 | }, 59 | "vendor2/project2" : { 60 | "3.0.0" : { 61 | "name": "vendor2/project2", 62 | "version": "3.0.0", 63 | "dist" : { 64 | "url" : "http://nexus.repo/base/member2/vendor2/project2/3.0.0/vendor2-project2-3.0.0.zip", 65 | "type" : "zip", 66 | "reference" : "ref3", 67 | "shasum" : "sha3" 68 | }, 69 | "uid" : 1398386772, 70 | "time" : "1979-07-11T16:30:00+00:00", 71 | "autoload": { 72 | "psr-4": { 73 | "psr-5-key": "psr-5-value" 74 | } 75 | }, 76 | "require": { 77 | "dependency-5": "version-5" 78 | }, 79 | "require-dev": { 80 | "dev-dependency-5": "dev-version-5" 81 | }, 82 | "suggest": { 83 | "suggest-5": "description-5" 84 | }, 85 | "type": "type-3-value" 86 | }, 87 | "4.0.0" : { 88 | "name": "vendor2/project2", 89 | "version": "4.0.0", 90 | "dist" : { 91 | "url" : "http://nexus.repo/base/member2/vendor2/project2/4.0.0/vendor2-project2-4.0.0.zip", 92 | "type" : "zip", 93 | "reference" : "ref4", 94 | "shasum" : "sha4" 95 | }, 96 | "uid" : 237122898, 97 | "time" : "2008-05-15T16:30:00+00:00", 98 | "autoload": { 99 | "psr-4": { 100 | "psr-6-key": "psr-6-value" 101 | } 102 | }, 103 | "require": { 104 | "dependency-6": "version-6" 105 | }, 106 | "require-dev": { 107 | "dev-dependency-6": "dev-version-6" 108 | }, 109 | "suggest": { 110 | "suggest-6": "description-6" 111 | }, 112 | "type": "type-4-value" 113 | } 114 | } 115 | } 116 | } 117 | -------------------------------------------------------------------------------- /nexus-repository-composer/src/main/java/org/sonatype/nexus/repository/composer/internal/ComposerJsonExtractor.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Sonatype Nexus (TM) Open Source Version 3 | * Copyright (c) 2018-present Sonatype, Inc. 4 | * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. 5 | * 6 | * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, 7 | * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. 8 | * 9 | * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks 10 | * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the 11 | * Eclipse Foundation. All other trademarks are the property of their respective owners. 12 | */ 13 | package org.sonatype.nexus.repository.composer.internal; 14 | 15 | import java.io.IOException; 16 | import java.io.InputStream; 17 | import java.util.Collections; 18 | import java.util.Map; 19 | 20 | import javax.annotation.Nullable; 21 | import javax.inject.Named; 22 | import javax.inject.Singleton; 23 | 24 | import org.sonatype.goodies.common.ComponentSupport; 25 | import org.sonatype.nexus.blobstore.api.Blob; 26 | 27 | import com.fasterxml.jackson.core.type.TypeReference; 28 | import com.fasterxml.jackson.databind.ObjectMapper; 29 | import org.apache.commons.compress.archivers.ArchiveEntry; 30 | import org.apache.commons.compress.archivers.ArchiveException; 31 | import org.apache.commons.compress.archivers.ArchiveInputStream; 32 | import org.apache.commons.compress.archivers.ArchiveStreamFactory; 33 | 34 | /** 35 | * Utility class for extracting the contents of a package's {@code composer.json} file and returning it as a map. 36 | */ 37 | @Named 38 | @Singleton 39 | public class ComposerJsonExtractor 40 | extends ComponentSupport 41 | { 42 | private final TypeReference> typeReference = new TypeReference>() { }; 43 | 44 | private final ObjectMapper mapper = new ObjectMapper(); 45 | 46 | private final ArchiveStreamFactory archiveStreamFactory = new ArchiveStreamFactory(); 47 | 48 | /** 49 | * Extracts the contents for the first matching {@code composer.json} file (of which there should only be one) as a 50 | * map representing the parsed JSON content. If no such file is found then an empty map is returned. 51 | */ 52 | public Map extractFromZip(final Blob blob) throws IOException { 53 | try (InputStream is = blob.getInputStream()) { 54 | try (ArchiveInputStream ais = archiveStreamFactory.createArchiveInputStream(ArchiveStreamFactory.ZIP, is)) { 55 | ArchiveEntry entry = ais.getNextEntry(); 56 | while (entry != null) { 57 | Map contents = processEntry(ais, entry); 58 | if (!contents.isEmpty()) { 59 | return contents; 60 | } 61 | entry = ais.getNextEntry(); 62 | } 63 | } 64 | return Collections.emptyMap(); 65 | } 66 | catch (ArchiveException e) { 67 | throw new IOException("Error reading from archive", e); 68 | } 69 | } 70 | 71 | /** 72 | * Processes a single entry in the archive. If the entry is the composer.json then the attributes will be extracted. 73 | * If not, the entry is skipped. 74 | */ 75 | private Map processEntry(final ArchiveInputStream stream, final ArchiveEntry entry) throws IOException 76 | { 77 | if (isComposerJsonFilename(entry.getName())) { 78 | return mapper.readValue(stream, typeReference); 79 | } 80 | return Collections.emptyMap(); 81 | } 82 | 83 | /** 84 | * Returns a boolean indicating if the associated file path (from an archive file) represents the {@code 85 | * composer.json} file. 86 | */ 87 | private boolean isComposerJsonFilename(final String entryName) { 88 | int filenameIndex = entryName.indexOf("/composer.json"); 89 | int separatorIndex = entryName.indexOf("/"); 90 | return entryName.equals("composer.json") || (filenameIndex >= 0 && filenameIndex == separatorIndex); 91 | } 92 | } 93 | -------------------------------------------------------------------------------- /nexus-repository-composer/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4.0.0 4 | 5 | 6 | org.sonatype.nexus.plugins 7 | composer-parent 8 | 0.1.10-SNAPSHOT 9 | ../pom.xml 10 | 11 | 12 | nexus-repository-composer 13 | bundle 14 | 15 | 16 | 17 | org.sonatype.nexus 18 | nexus-plugin-api 19 | provided 20 | 21 | 22 | org.sonatype.nexus 23 | nexus-repository-content 24 | provided 25 | true 26 | 27 | 28 | org.apache.commons 29 | commons-compress 30 | 31 | 32 | org.sonatype.nexus 33 | nexus-testsupport 34 | test 35 | 36 | 37 | org.sonatype.nexus 38 | nexus-cleanup 39 | provided 40 | 41 | 42 | org.sonatype.nexus 43 | nexus-rapture 44 | provided 45 | 46 | 47 | org.xmlunit 48 | xmlunit-core 49 | test 50 | 51 | 52 | org.tukaani 53 | xz 54 | 1.8 55 | 56 | 57 | org.skyscreamer 58 | jsonassert 59 | 1.5.0 60 | test 61 | 62 | 66 | 67 | org.apache.geronimo.specs 68 | geronimo-atinject_1.0_spec 69 | 1.0 70 | test 71 | 72 | 73 | 74 | 75 | 76 | 77 | org.sonatype.nexus.buildsupport 78 | extjs-maven-plugin 79 | 80 | NX.composer 81 | 82 | 83 | 84 | org.apache.karaf.tooling 85 | karaf-maven-plugin 86 | 87 | 88 | org.sonatype.plugins 89 | yuicompressor-maven-plugin 90 | 91 | 92 | maven-surefire-plugin 93 | 94 | false 95 | 96 | 97 | 100 | 101 | maven-install-plugin 102 | 103 | 104 | verify 105 | 106 | install 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | -------------------------------------------------------------------------------- /nexus-repository-composer-it/src/test/it-resources/ftp-php.json: -------------------------------------------------------------------------------- 1 | { 2 | "packages": { 3 | "rjkip/ftp-php": { 4 | "dev-master": { 5 | "name": "rjkip/ftp-php", 6 | "description": "A small, easy-to-use library for accessing FTP servers.", 7 | "keywords": [], 8 | "homepage": "", 9 | "version": "dev-master", 10 | "version_normalized": "9999999-dev", 11 | "license": [], 12 | "authors": [ 13 | { 14 | "name": "David Grudl" 15 | }, 16 | { 17 | "name": "Reinier Kip", 18 | "email": "github@reinierkip.nl", 19 | "role": "Developer" 20 | } 21 | ], 22 | "dist": { 23 | "url": "/rjkip/ftp-php/dev-master/rjkip-ftp-php-dev-master.zip", 24 | "type": "zip", 25 | "reference": "69869b3fe417172f3481e6245494843c31037366", 26 | "shasum": "" 27 | }, 28 | "type": "library", 29 | "time": "2014-01-13T19:24:51+00:00", 30 | "autoload": { 31 | "psr-0": { 32 | "FtpPhp\\": "src" 33 | } 34 | }, 35 | "require": { 36 | "php": ">=5.3" 37 | }, 38 | "uid": 15804 39 | }, 40 | "v1.0.0": { 41 | "name": "rjkip/ftp-php", 42 | "description": "A small, easy-to-use library for accessing FTP servers.", 43 | "keywords": [], 44 | "homepage": "", 45 | "version": "v1.0.0", 46 | "version_normalized": "1.0.0.0", 47 | "license": [], 48 | "authors": [ 49 | { 50 | "name": "David Grudl" 51 | }, 52 | { 53 | "name": "Reinier Kip", 54 | "email": "github@reinierkip.nl", 55 | "role": "Developer" 56 | } 57 | ], 58 | "dist": { 59 | "url": "/rjkip/ftp-php/v1.0.0/rjkip-ftp-php-v1.0.0.zip", 60 | "type": "zip", 61 | "reference": "f71bb2a8646f210f439ca96488ea37f74866e5d1", 62 | "shasum": "" 63 | }, 64 | "type": "library", 65 | "time": "2012-07-27T10:01:43+00:00", 66 | "autoload": { 67 | "psr-0": { 68 | "FtpPhp\\": "src" 69 | } 70 | }, 71 | "require": { 72 | "php": ">=5.3" 73 | }, 74 | "uid": 15818 75 | }, 76 | "v1.0.1": { 77 | "name": "rjkip/ftp-php", 78 | "description": "A small, easy-to-use library for accessing FTP servers.", 79 | "keywords": [], 80 | "homepage": "", 81 | "version": "v1.0.1", 82 | "version_normalized": "1.0.1.0", 83 | "license": [], 84 | "authors": [ 85 | { 86 | "name": "David Grudl" 87 | }, 88 | { 89 | "name": "Reinier Kip", 90 | "email": "github@reinierkip.nl", 91 | "role": "Developer" 92 | } 93 | ], 94 | "dist": { 95 | "url": "/rjkip/ftp-php/v1.0.1/rjkip-ftp-php-v1.0.1.zip", 96 | "type": "zip", 97 | "reference": "8767a90886226bbfd9d12c61b0cf51ab171d814d", 98 | "shasum": "" 99 | }, 100 | "type": "library", 101 | "time": "2012-07-27T11:59:48+00:00", 102 | "autoload": { 103 | "psr-0": { 104 | "FtpPhp\\": "src" 105 | } 106 | }, 107 | "require": { 108 | "php": ">=5.3" 109 | }, 110 | "uid": 15823 111 | }, 112 | "v1.1.0": { 113 | "name": "rjkip/ftp-php", 114 | "description": "A small, easy-to-use library for accessing FTP servers.", 115 | "keywords": [], 116 | "homepage": "", 117 | "version": "v1.1.0", 118 | "version_normalized": "1.1.0.0", 119 | "license": [], 120 | "authors": [ 121 | { 122 | "name": "David Grudl" 123 | }, 124 | { 125 | "name": "Reinier Kip", 126 | "email": "github@reinierkip.nl", 127 | "role": "Developer" 128 | } 129 | ], 130 | "dist": { 131 | "url": "/rjkip/ftp-php/v1.1.0/rjkip-ftp-php-v1.1.0.zip", 132 | "type": "zip", 133 | "reference": "69869b3fe417172f3481e6245494843c31037366", 134 | "shasum": "" 135 | }, 136 | "type": "library", 137 | "time": "2014-01-13T19:24:51+00:00", 138 | "autoload": { 139 | "psr-0": { 140 | "FtpPhp\\": "src" 141 | } 142 | }, 143 | "require": { 144 | "php": ">=5.3" 145 | }, 146 | "uid": 120076 147 | } 148 | } 149 | } 150 | } 151 | -------------------------------------------------------------------------------- /nexus-repository-composer/src/test/java/org/sonatype/nexus/repository/composer/internal/ComposerPathUtilsTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Sonatype Nexus (TM) Open Source Version 3 | * Copyright (c) 2018-present Sonatype, Inc. 4 | * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. 5 | * 6 | * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, 7 | * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. 8 | * 9 | * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks 10 | * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the 11 | * Eclipse Foundation. All other trademarks are the property of their respective owners. 12 | */ 13 | package org.sonatype.nexus.repository.composer.internal; 14 | 15 | import java.util.Map; 16 | 17 | import org.sonatype.goodies.testsupport.TestSupport; 18 | import org.sonatype.nexus.common.collect.AttributesMap; 19 | import org.sonatype.nexus.repository.view.Context; 20 | import org.sonatype.nexus.repository.view.matchers.token.TokenMatcher; 21 | 22 | import org.junit.Test; 23 | import org.mockito.Mock; 24 | 25 | import static org.hamcrest.MatcherAssert.assertThat; 26 | import static org.hamcrest.Matchers.is; 27 | import static org.mockito.Mockito.when; 28 | import static org.sonatype.nexus.repository.composer.internal.recipe.ComposerRecipeSupport.NAME_TOKEN; 29 | import static org.sonatype.nexus.repository.composer.internal.recipe.ComposerRecipeSupport.PROJECT_TOKEN; 30 | import static org.sonatype.nexus.repository.composer.internal.recipe.ComposerRecipeSupport.VENDOR_TOKEN; 31 | import static org.sonatype.nexus.repository.composer.internal.recipe.ComposerRecipeSupport.VERSION_TOKEN; 32 | 33 | public class ComposerPathUtilsTest 34 | extends TestSupport 35 | { 36 | @Mock 37 | private Context context; 38 | 39 | @Mock 40 | private AttributesMap contextAttributes; 41 | 42 | @Mock 43 | private TokenMatcher.State state; 44 | 45 | @Mock 46 | private Map tokens; 47 | 48 | @Test 49 | public void buildZipballPathFromContextWithNameToken() { 50 | when(context.getAttributes()).thenReturn(contextAttributes); 51 | when(contextAttributes.require(TokenMatcher.State.class)).thenReturn(state); 52 | when(state.getTokens()).thenReturn(tokens); 53 | 54 | when(tokens.get(VENDOR_TOKEN)).thenReturn("testvendor"); 55 | when(tokens.get(PROJECT_TOKEN)).thenReturn("testproject"); 56 | when(tokens.get(VERSION_TOKEN)).thenReturn("1.2.3"); 57 | when(tokens.get(NAME_TOKEN)).thenReturn("name"); 58 | 59 | assertThat(ComposerPathUtils.buildZipballPath(context), is("/testvendor/testproject/1.2.3/name.zip")); 60 | } 61 | 62 | @Test 63 | public void buildZipballPathFromContextWithoutNameToken() { 64 | when(context.getAttributes()).thenReturn(contextAttributes); 65 | when(contextAttributes.require(TokenMatcher.State.class)).thenReturn(state); 66 | when(state.getTokens()).thenReturn(tokens); 67 | 68 | when(tokens.get(VENDOR_TOKEN)).thenReturn("testvendor"); 69 | when(tokens.get(PROJECT_TOKEN)).thenReturn("testproject"); 70 | when(tokens.get(VERSION_TOKEN)).thenReturn("1.2.3"); 71 | 72 | assertThat(ComposerPathUtils.buildZipballPath(context), 73 | is("/testvendor/testproject/1.2.3/testvendor-testproject-1.2.3.zip")); 74 | } 75 | 76 | @Test 77 | public void buildZipballPathFromValues() { 78 | when(context.getAttributes()).thenReturn(contextAttributes); 79 | when(contextAttributes.require(TokenMatcher.State.class)).thenReturn(state); 80 | when(state.getTokens()).thenReturn(tokens); 81 | 82 | assertThat(ComposerPathUtils.buildZipballPath("testvendor", "testproject", "1.2.3"), 83 | is("/testvendor/testproject/1.2.3/testvendor-testproject-1.2.3.zip")); 84 | } 85 | 86 | @Test 87 | public void buildProviderPathFromTokens() { 88 | when(context.getAttributes()).thenReturn(contextAttributes); 89 | when(contextAttributes.require(TokenMatcher.State.class)).thenReturn(state); 90 | when(state.getTokens()).thenReturn(tokens); 91 | 92 | when(tokens.get(VENDOR_TOKEN)).thenReturn("testvendor"); 93 | when(tokens.get(PROJECT_TOKEN)).thenReturn("testproject"); 94 | 95 | assertThat(ComposerPathUtils.buildProviderPath(context), is("/p/testvendor/testproject.json")); 96 | } 97 | 98 | @Test 99 | public void buildProviderPathFromValues() { 100 | assertThat(ComposerPathUtils.buildProviderPath("testvendor", "testproject"), is("/p/testvendor/testproject.json")); 101 | } 102 | } 103 | -------------------------------------------------------------------------------- /nexus-repository-composer/src/test/java/org/sonatype/nexus/repository/composer/internal/ComposerProviderHandlerTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Sonatype Nexus (TM) Open Source Version 3 | * Copyright (c) 2018-present Sonatype, Inc. 4 | * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. 5 | * 6 | * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, 7 | * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. 8 | * 9 | * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks 10 | * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the 11 | * Eclipse Foundation. All other trademarks are the property of their respective owners. 12 | */ 13 | package org.sonatype.nexus.repository.composer.internal; 14 | 15 | import org.sonatype.goodies.testsupport.TestSupport; 16 | import org.sonatype.nexus.common.collect.AttributesMap; 17 | import org.sonatype.nexus.repository.Repository; 18 | import org.sonatype.nexus.repository.composer.internal.proxy.ComposerProviderHandler; 19 | import org.sonatype.nexus.repository.view.Context; 20 | import org.sonatype.nexus.repository.view.Payload; 21 | import org.sonatype.nexus.repository.view.Request; 22 | import org.sonatype.nexus.repository.view.Response; 23 | import org.sonatype.nexus.repository.view.Status; 24 | 25 | import org.junit.Before; 26 | import org.junit.Test; 27 | import org.mockito.Mock; 28 | 29 | import static org.hamcrest.MatcherAssert.assertThat; 30 | import static org.hamcrest.Matchers.is; 31 | import static org.hamcrest.Matchers.not; 32 | import static org.mockito.Mockito.verify; 33 | import static org.mockito.Mockito.verifyNoMoreInteractions; 34 | import static org.mockito.Mockito.when; 35 | import static org.sonatype.nexus.repository.composer.internal.proxy.ComposerProviderHandler.DO_NOT_REWRITE; 36 | import static org.sonatype.nexus.repository.http.HttpStatus.INTERNAL_SERVER_ERROR; 37 | import static org.sonatype.nexus.repository.http.HttpStatus.OK; 38 | 39 | public class ComposerProviderHandlerTest 40 | extends TestSupport 41 | { 42 | @Mock 43 | private ComposerJsonProcessor composerJsonProcessor; 44 | 45 | @Mock 46 | private Context context; 47 | 48 | @Mock 49 | private Request request; 50 | 51 | @Mock 52 | private Response response; 53 | 54 | @Mock 55 | private Repository repository; 56 | 57 | @Mock 58 | private AttributesMap requestAttributes; 59 | 60 | @Mock 61 | private Payload payload; 62 | 63 | @Mock 64 | private Payload rewrittenPayload; 65 | 66 | @Mock 67 | private Status status; 68 | 69 | private ComposerProviderHandler underTest; 70 | 71 | @Before 72 | public void setUp() throws Exception { 73 | when(composerJsonProcessor.rewriteProviderJson(repository, payload)).thenReturn(rewrittenPayload); 74 | 75 | when(context.getRequest()).thenReturn(request); 76 | when(context.getRepository()).thenReturn(repository); 77 | when(context.proceed()).thenReturn(response); 78 | 79 | when(request.getAttributes()).thenReturn(requestAttributes); 80 | when(requestAttributes.get(DO_NOT_REWRITE, String.class)).thenReturn("false"); 81 | 82 | when(response.getStatus()).thenReturn(status); 83 | when(response.getPayload()).thenReturn(payload); 84 | when(status.getCode()).thenReturn(OK); 85 | 86 | underTest = new ComposerProviderHandler(composerJsonProcessor); 87 | } 88 | 89 | @Test 90 | public void handleWithRewriteDisabled() throws Exception { 91 | when(requestAttributes.get(DO_NOT_REWRITE, String.class)).thenReturn("true"); 92 | 93 | Response output = underTest.handle(context); 94 | 95 | assertThat(output, is(response)); 96 | verifyNoMoreInteractions(composerJsonProcessor); 97 | } 98 | 99 | @Test 100 | public void handleWithRewriteEnabled() throws Exception { 101 | Response output = underTest.handle(context); 102 | 103 | assertThat(output, is(not(response))); 104 | assertThat(output.getPayload(), is(rewrittenPayload)); 105 | 106 | verify(composerJsonProcessor).rewriteProviderJson(repository, payload); 107 | } 108 | 109 | @Test 110 | public void handleWithRewriteEnabledNotOK() throws Exception { 111 | when(status.getCode()).thenReturn(INTERNAL_SERVER_ERROR); 112 | 113 | Response output = underTest.handle(context); 114 | 115 | assertThat(output, is(response)); 116 | verifyNoMoreInteractions(composerJsonProcessor); 117 | } 118 | 119 | @Test 120 | public void handleWithRewriteEnabledNoPayload() throws Exception { 121 | when(response.getPayload()).thenReturn(null); 122 | 123 | Response output = underTest.handle(context); 124 | 125 | assertThat(output, is(response)); 126 | verifyNoMoreInteractions(composerJsonProcessor); 127 | } 128 | } 129 | -------------------------------------------------------------------------------- /nexus-repository-composer/src/test/java/org/sonatype/nexus/repository/composer/internal/ComposerMaintenanceFacetTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Sonatype Nexus (TM) Open Source Version 3 | * Copyright (c) 2018-present Sonatype, Inc. 4 | * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. 5 | * 6 | * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, 7 | * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. 8 | * 9 | * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks 10 | * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the 11 | * Eclipse Foundation. All other trademarks are the property of their respective owners. 12 | */ 13 | package org.sonatype.nexus.repository.composer.internal; 14 | 15 | import org.junit.Before; 16 | import org.junit.Test; 17 | import org.mockito.Mock; 18 | import org.sonatype.goodies.testsupport.TestSupport; 19 | import org.sonatype.nexus.repository.Repository; 20 | import org.sonatype.nexus.repository.composer.ComposerHostedFacet; 21 | import org.sonatype.nexus.repository.content.Component; 22 | import org.sonatype.nexus.repository.content.facet.ContentFacet; 23 | import org.sonatype.nexus.repository.content.fluent.FluentAsset; 24 | import org.sonatype.nexus.repository.content.fluent.FluentComponent; 25 | import org.sonatype.nexus.repository.content.fluent.FluentComponents; 26 | import org.sonatype.nexus.repository.view.Content; 27 | 28 | import java.io.IOException; 29 | import java.util.Arrays; 30 | import java.util.HashSet; 31 | import java.util.Optional; 32 | import java.util.Set; 33 | 34 | import static java.util.Collections.singleton; 35 | import static org.junit.Assert.assertEquals; 36 | import static org.mockito.Mockito.when; 37 | 38 | public class ComposerMaintenanceFacetTest 39 | extends TestSupport 40 | { 41 | private static final String REPOSITORY_NAME = "test-repository-name"; 42 | 43 | private static final String VENDOR = "test-vendor"; 44 | 45 | private static final String PROJECT = "test-project"; 46 | private static final String ZIPBALL_PATH = "/" + VENDOR + "/" + PROJECT + "/1.0.0/" + VENDOR + "-" + PROJECT + "-1.0.0.zip"; 47 | private static final String PROVIDER_PATH = "/p/" + VENDOR + "/" + PROJECT + ".json"; 48 | private static final String PACKAGE_PATH = "/p2/" + VENDOR + "/" + PROJECT + ".json"; 49 | 50 | 51 | @Mock 52 | private Repository repository; 53 | 54 | @Mock 55 | private ContentFacet contentFacet; 56 | 57 | @Mock 58 | private ComposerHostedFacet hostedFacet; 59 | 60 | @Mock 61 | private Component component; 62 | 63 | @Mock 64 | private FluentComponents fluentComponents; 65 | 66 | @Mock 67 | private FluentComponent fluentComponent; 68 | 69 | @Mock 70 | private FluentAsset fluentAsset; 71 | 72 | @Mock 73 | private Content content; 74 | 75 | private ComposerMaintenanceFacet underTest; 76 | 77 | @Before 78 | public void setUp() throws Exception { 79 | when(repository.getName()).thenReturn(REPOSITORY_NAME); 80 | when(repository.facet(ContentFacet.class)).thenReturn(contentFacet); 81 | when(repository.optionalFacet(ComposerHostedFacet.class)).thenReturn(Optional.of(hostedFacet)); 82 | 83 | when(contentFacet.components()).thenReturn(fluentComponents); 84 | when(fluentComponents.with(component)).thenReturn(fluentComponent); 85 | when(fluentComponent.assets()).thenReturn(singleton(fluentAsset)); 86 | when(fluentAsset.delete()).thenReturn(true); 87 | when(fluentAsset.path()).thenReturn(ZIPBALL_PATH); 88 | 89 | when(component.namespace()).thenReturn(VENDOR); 90 | when(component.name()).thenReturn(PROJECT); 91 | 92 | underTest = new ComposerMaintenanceFacet(); 93 | underTest.attach(repository); 94 | } 95 | 96 | @Test 97 | public void testDeleteComponent() throws IOException { 98 | when(hostedFacet.rebuildPackageJson(VENDOR, PROJECT)).thenReturn(Optional.of(content)); 99 | when(hostedFacet.rebuildProviderJson(VENDOR, PROJECT)).thenReturn(Optional.of(content)); 100 | 101 | Set deletedPaths = underTest.deleteComponent(component); 102 | assertEquals(singleton(ZIPBALL_PATH), deletedPaths); 103 | } 104 | 105 | @Test 106 | public void testDeleteComponentLast() throws IOException { 107 | when(hostedFacet.rebuildPackageJson(VENDOR, PROJECT)).thenReturn(Optional.empty()); 108 | when(hostedFacet.rebuildProviderJson(VENDOR, PROJECT)).thenReturn(Optional.empty()); 109 | 110 | Set deletedPaths = underTest.deleteComponent(component); 111 | assertEquals(new HashSet<>(Arrays.asList(ZIPBALL_PATH, PROVIDER_PATH, PACKAGE_PATH)), deletedPaths); 112 | } 113 | 114 | @Test 115 | public void testDeleteProxyComponent() { 116 | when(repository.optionalFacet(ComposerHostedFacet.class)).thenReturn(Optional.empty()); 117 | Set deletedPaths = underTest.deleteComponent(component); 118 | assertEquals(singleton(ZIPBALL_PATH), deletedPaths); 119 | } 120 | } 121 | -------------------------------------------------------------------------------- /nexus-repository-composer/src/main/java/org/sonatype/nexus/repository/composer/internal/recipe/ComposerGroupRecipe.groovy: -------------------------------------------------------------------------------- 1 | /* 2 | * Sonatype Nexus (TM) Open Source Version 3 | * Copyright (c) 2017-present Sonatype, Inc. 4 | * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. 5 | * 6 | * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, 7 | * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. 8 | * 9 | * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks 10 | * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the 11 | * Eclipse Foundation. All other trademarks are the property of their respective owners. 12 | */ 13 | package org.sonatype.nexus.repository.composer.internal.recipe 14 | 15 | import org.sonatype.nexus.repository.composer.AssetKind 16 | import org.sonatype.nexus.repository.composer.ComposerFormat 17 | import org.sonatype.nexus.repository.composer.internal.group.ComposerGroupPackageJsonHandler 18 | import org.sonatype.nexus.repository.composer.internal.group.ComposerGroupPackagesJsonHandler 19 | import org.sonatype.nexus.repository.composer.internal.group.ComposerGroupProviderJsonHandler 20 | 21 | import javax.annotation.Nonnull 22 | import javax.inject.Inject 23 | import javax.inject.Named 24 | import javax.inject.Provider 25 | import javax.inject.Singleton 26 | 27 | import org.sonatype.nexus.common.upgrade.AvailabilityVersion; 28 | import org.sonatype.nexus.repository.Format 29 | import org.sonatype.nexus.repository.Repository 30 | import org.sonatype.nexus.repository.Type 31 | import org.sonatype.nexus.repository.group.GroupFacetImpl 32 | import org.sonatype.nexus.repository.group.GroupHandler 33 | import org.sonatype.nexus.repository.http.HttpHandlers 34 | import org.sonatype.nexus.repository.types.GroupType 35 | import org.sonatype.nexus.repository.view.ConfigurableViewFacet 36 | import org.sonatype.nexus.repository.view.Router 37 | import org.sonatype.nexus.repository.view.ViewFacet 38 | 39 | /** 40 | * Recipe for creating a Composer group repository. 41 | */ 42 | @AvailabilityVersion(from = "1.0") 43 | @Named(ComposerGroupRecipe.NAME) 44 | @Singleton 45 | class ComposerGroupRecipe 46 | extends ComposerRecipeSupport 47 | { 48 | public static final String NAME = 'composer-group' 49 | 50 | @Inject 51 | Provider groupFacet 52 | 53 | @Inject 54 | GroupHandler standardGroupHandler 55 | 56 | @Inject 57 | ComposerGroupPackagesJsonHandler packagesJsonHandler 58 | 59 | @Inject 60 | ComposerGroupProviderJsonHandler providerJsonHandler 61 | 62 | @Inject 63 | ComposerGroupPackageJsonHandler packageJsonHandler 64 | 65 | @Inject 66 | ComposerGroupRecipe(@Named(GroupType.NAME) final Type type, @Named(ComposerFormat.NAME) final Format format) { 67 | super(type, format) 68 | } 69 | 70 | @Override 71 | void apply(@Nonnull final Repository repository) throws Exception { 72 | repository.attach(contentFacet.get()) 73 | repository.attach(groupFacet.get()) 74 | repository.attach(securityFacet.get()) 75 | repository.attach(configure(viewFacet.get())) 76 | repository.attach(searchFacet.get()) 77 | repository.attach(maintenanceFacet.get()) 78 | repository.attach(browseFacet.get()) 79 | } 80 | 81 | /** 82 | * Configure {@link ViewFacet}. 83 | */ 84 | private ViewFacet configure(final ConfigurableViewFacet facet) { 85 | Router.Builder builder = new Router.Builder() 86 | 87 | builder.route(packagesMatcher() 88 | .handler(timingHandler) 89 | .handler(assetKindHandler.rcurry(AssetKind.PACKAGES)) 90 | .handler(securityHandler) 91 | .handler(exceptionHandler) 92 | .handler(handlerContributor) 93 | .handler(packagesJsonHandler) 94 | .create()) 95 | 96 | builder.route(providerMatcher() 97 | .handler(timingHandler) 98 | .handler(assetKindHandler.rcurry(AssetKind.PROVIDER)) 99 | .handler(securityHandler) 100 | .handler(exceptionHandler) 101 | .handler(handlerContributor) 102 | .handler(providerJsonHandler) 103 | .create()) 104 | 105 | builder.route(packageMatcher() 106 | .handler(timingHandler) 107 | .handler(assetKindHandler.rcurry(AssetKind.PACKAGE)) 108 | .handler(securityHandler) 109 | .handler(exceptionHandler) 110 | .handler(handlerContributor) 111 | .handler(packageJsonHandler) 112 | .create()) 113 | 114 | builder.route(zipballMatcher() 115 | .handler(timingHandler) 116 | .handler(assetKindHandler.rcurry(AssetKind.ZIPBALL)) 117 | .handler(securityHandler) 118 | .handler(exceptionHandler) 119 | .handler(handlerContributor) 120 | .handler(standardGroupHandler) 121 | .create()) 122 | 123 | addBrowseUnsupportedRoute(builder) 124 | 125 | builder.defaultHandlers(HttpHandlers.notFound()) 126 | 127 | facet.configure(builder.create()) 128 | 129 | return facet 130 | } 131 | } 132 | -------------------------------------------------------------------------------- /nexus-repository-composer/src/main/java/org/sonatype/nexus/repository/composer/internal/ComposerJsonMinifier.java: -------------------------------------------------------------------------------- 1 | package org.sonatype.nexus.repository.composer.internal; 2 | 3 | import javax.inject.Named; 4 | import javax.inject.Singleton; 5 | import java.util.*; 6 | 7 | @Named 8 | @Singleton 9 | public class ComposerJsonMinifier { 10 | 11 | private static final String V2_FORMAT = "composer/2.0"; 12 | private static final String UNSET_VALUE = "__unset"; 13 | private static final String MINIFIED_KEY = "minified"; 14 | private static final String PACKAGES_KEY = "packages"; 15 | 16 | public void expand(Map json) { 17 | Object format = json.get(MINIFIED_KEY); 18 | if (!(format instanceof String) || !format.equals(V2_FORMAT)) { 19 | return; 20 | } 21 | 22 | Map> packages = new LinkedHashMap<>(); 23 | 24 | if (json.get(PACKAGES_KEY) instanceof Map) { 25 | Map packagesMap = (Map) json.get(PACKAGES_KEY); 26 | for (String packageName : packagesMap.keySet()) { 27 | List packageVersions = (List) packagesMap.get(packageName); 28 | 29 | Map expandedVersion = null; 30 | List expandedVersions = new ArrayList<>(); 31 | 32 | for (Object versionObject : packageVersions) { 33 | if (!(versionObject instanceof Map)) { 34 | continue; 35 | } 36 | 37 | Map version = (Map) versionObject; 38 | 39 | if (expandedVersion == null) { 40 | expandedVersions.add(version); 41 | expandedVersion = new LinkedHashMap<>(version); 42 | continue; 43 | } 44 | 45 | for (Map.Entry versionData : version.entrySet()) { 46 | if (versionData.getValue() instanceof String && versionData.getValue().equals(UNSET_VALUE)) { 47 | expandedVersion.remove(versionData.getKey()); 48 | } else { 49 | expandedVersion.put(versionData.getKey(), versionData.getValue()); 50 | } 51 | } 52 | 53 | expandedVersions.add(expandedVersion); 54 | expandedVersion = new LinkedHashMap<>(expandedVersion); 55 | } 56 | 57 | packages.put(packageName, expandedVersions); 58 | } 59 | } 60 | 61 | json.put(PACKAGES_KEY, packages); 62 | json.remove(MINIFIED_KEY); 63 | } 64 | 65 | public void minify(Map json) { 66 | Map> packages = new LinkedHashMap<>(); 67 | 68 | if (json.get(PACKAGES_KEY) instanceof Map) { 69 | Map packagesMap = (Map) json.get(PACKAGES_KEY); 70 | for (String packageName : packagesMap.keySet()) { 71 | List packageVersions = (List) packagesMap.get(packageName); 72 | 73 | Map lastKnownVersionData = null; 74 | List minifiedVersions = new ArrayList<>(); 75 | 76 | for (Object versionObject : packageVersions) { 77 | if (!(versionObject instanceof Map)) { 78 | continue; 79 | } 80 | 81 | Map version = (Map) versionObject; 82 | 83 | if (lastKnownVersionData == null) { 84 | minifiedVersions.add(version); 85 | lastKnownVersionData = new LinkedHashMap<>(version); 86 | continue; 87 | } 88 | 89 | Map minifiedVersion = new LinkedHashMap<>(); 90 | 91 | for (Map.Entry versionData : version.entrySet()) { 92 | boolean lastContains = lastKnownVersionData.containsKey(versionData.getKey()); 93 | Object lastData = lastKnownVersionData.get(versionData.getKey()); 94 | Object currentData = versionData.getValue(); 95 | 96 | if (!lastContains || !Objects.equals(lastData, currentData)) { 97 | minifiedVersion.put(versionData.getKey(), currentData); 98 | lastKnownVersionData.put(versionData.getKey(), currentData); 99 | } 100 | } 101 | 102 | Iterator> it = lastKnownVersionData.entrySet().iterator(); 103 | while (it.hasNext()) { 104 | Map.Entry lastData = it.next(); 105 | if (!version.containsKey(lastData.getKey())) { 106 | minifiedVersion.put(lastData.getKey(), UNSET_VALUE); 107 | it.remove(); 108 | } 109 | } 110 | 111 | minifiedVersions.add(minifiedVersion); 112 | } 113 | 114 | packages.put(packageName, minifiedVersions); 115 | } 116 | } 117 | 118 | json.put(PACKAGES_KEY, packages); 119 | json.put(MINIFIED_KEY, V2_FORMAT); 120 | } 121 | } 122 | -------------------------------------------------------------------------------- /pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 12 | 13 | 4.0.0 14 | 15 | 16 | org.sonatype.nexus.plugins 17 | nexus-plugins 18 | 3.71.0-06 19 | 20 | 21 | composer-parent 22 | 0.1.10-SNAPSHOT 23 | pom 24 | 25 | ${project.groupId}:${project.artifactId}-parent 26 | 2018 27 | 28 | 29 | nexus-repository-composer 30 | nexus-repository-composer-it 31 | 32 | 33 | 34 | scm:git:git@github.com:sonatype-nexus-community/nexus-repository-composer.git 35 | HEAD 36 | 37 | 38 | 39 | ossrh 40 | https://oss.sonatype.org/content/repositories/snapshots 41 | 42 | 43 | 44 | 45 | 3.71.0-06 46 | 47 | 48 | 49 | 50 | 51 | rso-public-grid 52 | https://repository.sonatype.org/content/groups/sonatype-public-grid/ 53 | 54 | 55 | 56 | 57 | 58 | 59 | org.sonatype.plugins 60 | nexus-staging-maven-plugin 61 | true 62 | 63 | rso 64 | https://repository.sonatype.org/ 65 | true 66 | 67 | 68 | 69 | 70 | maven-enforcer-plugin 71 | 3.4.1 72 | 73 | 74 | enforce-java 75 | 76 | enforce 77 | 78 | 79 | 80 | 81 | 82 | 85 | 86 | [1.8,18) 87 | 88 | 91 | 92 | [3.3.3,) 93 | 94 | 95 | 96 | rso-public-grid 97 | 98 | 99 | 100 | 101 | 102 | 103 | maven-release-plugin 104 | 105 | 106 | true 107 | gpg-sign 108 | 113 | -DskipTests 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | gpg-sign 122 | 125 | 126 | 127 | 128 | maven-gpg-plugin 129 | 3.2.6 130 | 131 | 132 | sign-artifacts 133 | verify 134 | 135 | sign 136 | 137 | 138 | 139 | 140 | 141 | 142 | 143 | 144 | 145 | -------------------------------------------------------------------------------- /nexus-repository-composer-it/src/test/java/org/sonatype/nexus/repository/composer/internal/ComposerITSupport.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Sonatype Nexus (TM) Open Source Version 3 | * Copyright (c) 2018-present Sonatype, Inc. 4 | * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. 5 | * 6 | * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, 7 | * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. 8 | * 9 | * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks 10 | * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the 11 | * Eclipse Foundation. All other trademarks are the property of their respective owners. 12 | */ 13 | package org.sonatype.nexus.repository.composer.internal; 14 | 15 | import org.apache.http.HttpHost; 16 | import org.apache.http.auth.AuthScope; 17 | import org.apache.http.auth.UsernamePasswordCredentials; 18 | import org.apache.http.client.AuthCache; 19 | import org.apache.http.client.CredentialsProvider; 20 | import org.apache.http.client.config.CookieSpecs; 21 | import org.apache.http.client.config.RequestConfig; 22 | import org.apache.http.client.protocol.HttpClientContext; 23 | import org.apache.http.conn.ssl.NoopHostnameVerifier; 24 | import org.apache.http.conn.ssl.SSLConnectionSocketFactory; 25 | import org.apache.http.conn.ssl.TrustSelfSignedStrategy; 26 | import org.apache.http.impl.auth.BasicScheme; 27 | import org.apache.http.impl.client.BasicAuthCache; 28 | import org.apache.http.impl.client.BasicCredentialsProvider; 29 | import org.apache.http.impl.client.HttpClientBuilder; 30 | import org.apache.http.impl.client.HttpClients; 31 | import org.apache.http.ssl.SSLContexts; 32 | import org.junit.Rule; 33 | import org.sonatype.nexus.pax.exam.NexusPaxExamSupport; 34 | import org.sonatype.nexus.repository.Repository; 35 | import org.sonatype.nexus.repository.composer.internal.fixtures.RepositoryRuleComposer; 36 | import org.sonatype.nexus.repository.content.facet.ContentFacet; 37 | import org.sonatype.nexus.repository.content.fluent.FluentAsset; 38 | import org.sonatype.nexus.repository.manager.RepositoryManager; 39 | import org.sonatype.nexus.testsuite.testsupport.NexusBaseITSupport; 40 | 41 | import javax.annotation.Nonnull; 42 | import javax.inject.Inject; 43 | import javax.net.ssl.SSLContext; 44 | import java.io.FileInputStream; 45 | import java.net.URL; 46 | import java.security.KeyStore; 47 | import java.util.Optional; 48 | 49 | import static com.google.common.base.Preconditions.checkNotNull; 50 | 51 | public class ComposerITSupport 52 | extends NexusBaseITSupport 53 | { 54 | 55 | @Inject 56 | protected RepositoryManager repositoryManager; 57 | 58 | @Rule 59 | public RepositoryRuleComposer repos = new RepositoryRuleComposer(() -> repositoryManager); 60 | 61 | public ComposerITSupport() { 62 | testData.addDirectory(NexusPaxExamSupport.resolveBaseFile("target/it-resources/composer")); 63 | } 64 | 65 | @Nonnull 66 | protected ComposerClient composerClient(final Repository repository) throws Exception { 67 | checkNotNull(repository); 68 | 69 | return composerClient(repositoryBaseUrl(repository)); 70 | } 71 | 72 | protected ComposerClient composerClient(final URL repositoryUrl) throws Exception { 73 | return new ComposerClient( 74 | clientBuilder(repositoryUrl).build(), 75 | clientContext(), 76 | repositoryUrl.toURI() 77 | ); 78 | } 79 | 80 | @Nonnull 81 | protected URL repositoryBaseUrl(final Repository repository) { 82 | return resolveUrl(nexusUrl(), "/repository/" + repository.getName() + "/"); 83 | } 84 | 85 | protected HttpClientBuilder clientBuilder(final URL nexusUrl) throws Exception { 86 | AuthScope scope = new AuthScope(nexusUrl.getHost(), -1); 87 | CredentialsProvider credentialsProvider = new BasicCredentialsProvider(); 88 | credentialsProvider.setCredentials(scope, new UsernamePasswordCredentials("admin", "admin123")); 89 | 90 | KeyStore trustStore = KeyStore.getInstance(KeyStore.getDefaultType()); 91 | try (FileInputStream instream = new FileInputStream(resolveBaseFile("target/it-resources/ssl/client.jks"))) { 92 | trustStore.load(instream, "password".toCharArray()); 93 | } 94 | SSLContext context = SSLContexts.custom().loadTrustMaterial(trustStore, new TrustSelfSignedStrategy()).build(); 95 | 96 | return HttpClients.custom() 97 | .setDefaultRequestConfig(RequestConfig.custom().setCookieSpec(CookieSpecs.DEFAULT).build()) 98 | .setDefaultCredentialsProvider(credentialsProvider) 99 | .setSSLSocketFactory(new SSLConnectionSocketFactory(context, NoopHostnameVerifier.INSTANCE)); 100 | } 101 | 102 | /** 103 | * @return Context with preemptive auth enabled for Nexus 104 | */ 105 | protected HttpClientContext clientContext() { 106 | String hostname = nexusUrl().getHost(); 107 | AuthCache authCache = new BasicAuthCache(); 108 | HttpHost hostHttp = new HttpHost(hostname, nexusUrl().getPort(), "http"); 109 | HttpHost hostHttps = new HttpHost(hostname, nexusUrl().getPort(), "https"); 110 | authCache.put(hostHttp, new BasicScheme()); 111 | authCache.put(hostHttps, new BasicScheme()); 112 | 113 | HttpClientContext context = HttpClientContext.create(); 114 | context.setAuthCache(authCache); 115 | 116 | return context; 117 | } 118 | 119 | public static Optional findAsset(Repository repository, String path) { 120 | if (!path.startsWith("/")) { 121 | path = "/" + path; 122 | } 123 | return repository.facet(ContentFacet.class).assets().path(path).find(); 124 | } 125 | } 126 | -------------------------------------------------------------------------------- /nexus-repository-composer/src/test/java/org/sonatype/nexus/repository/composer/internal/ComposerGroupPackagesJsonHandlerTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Sonatype Nexus (TM) Open Source Version 3 | * Copyright (c) 2018-present Sonatype, Inc. 4 | * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. 5 | * 6 | * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, 7 | * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. 8 | * 9 | * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks 10 | * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the 11 | * Eclipse Foundation. All other trademarks are the property of their respective owners. 12 | */ 13 | package org.sonatype.nexus.repository.composer.internal; 14 | 15 | import org.junit.Before; 16 | import org.junit.Test; 17 | import org.mockito.Mock; 18 | import org.sonatype.goodies.testsupport.TestSupport; 19 | import org.sonatype.nexus.common.collect.AttributesMap; 20 | import org.sonatype.nexus.repository.Repository; 21 | import org.sonatype.nexus.repository.composer.internal.group.ComposerGroupPackagesJsonHandler; 22 | import org.sonatype.nexus.repository.group.GroupFacet; 23 | import org.sonatype.nexus.repository.view.*; 24 | 25 | import static java.util.Arrays.asList; 26 | import static java.util.Collections.singletonList; 27 | import static org.hamcrest.MatcherAssert.assertThat; 28 | import static org.hamcrest.Matchers.is; 29 | import static org.hamcrest.Matchers.notNullValue; 30 | import static org.mockito.ArgumentMatchers.eq; 31 | import static org.mockito.Mockito.verify; 32 | import static org.mockito.Mockito.when; 33 | import static org.sonatype.nexus.repository.http.HttpMethods.GET; 34 | import static org.sonatype.nexus.repository.http.HttpStatus.INTERNAL_SERVER_ERROR; 35 | import static org.sonatype.nexus.repository.http.HttpStatus.OK; 36 | 37 | public class ComposerGroupPackagesJsonHandlerTest 38 | extends TestSupport 39 | { 40 | @Mock 41 | private Content content; 42 | 43 | @Mock 44 | private Request request; 45 | 46 | @Mock 47 | private Context context; 48 | 49 | @Mock 50 | private Repository repository; 51 | 52 | @Mock 53 | private GroupFacet groupFacet; 54 | 55 | @Mock 56 | private Repository memberRepository1; 57 | 58 | @Mock 59 | private Repository memberRepository2; 60 | 61 | @Mock 62 | private ViewFacet memberRepository1ViewFacet; 63 | 64 | @Mock 65 | private ViewFacet memberRepository2ViewFacet; 66 | 67 | @Mock 68 | private Response response1; 69 | 70 | @Mock 71 | private Response response2; 72 | 73 | @Mock 74 | private Status status1; 75 | 76 | @Mock 77 | private Status status2; 78 | 79 | @Mock 80 | private Status status3; 81 | 82 | @Mock 83 | private Payload payload1; 84 | 85 | @Mock 86 | private Payload payload2; 87 | 88 | @Mock 89 | private ComposerJsonProcessor composerJsonProcessor; 90 | 91 | private ComposerGroupPackagesJsonHandler underTest; 92 | 93 | @Before 94 | public void setUp() throws Exception { 95 | when(context.getRepository()).thenReturn(repository); 96 | when(context.getRequest()).thenReturn(request); 97 | when(context.getAttributes()).thenReturn(new AttributesMap()); 98 | 99 | when(request.getAction()).thenReturn(GET); 100 | when(request.getAttributes()).thenReturn(new AttributesMap()); 101 | when(request.getHeaders()).thenReturn(new Headers()); 102 | 103 | when(repository.facet(GroupFacet.class)).thenReturn(groupFacet); 104 | when(groupFacet.members()).thenReturn(asList(memberRepository1, memberRepository2)); 105 | 106 | when(memberRepository1.getName()).thenReturn("member1"); 107 | when(memberRepository1.facet(ViewFacet.class)).thenReturn(memberRepository1ViewFacet); 108 | 109 | when(memberRepository2.getName()).thenReturn("member2"); 110 | when(memberRepository2.facet(ViewFacet.class)).thenReturn(memberRepository2ViewFacet); 111 | 112 | when(memberRepository1ViewFacet.dispatch(request, context)).thenReturn(response1); 113 | when(memberRepository2ViewFacet.dispatch(request, context)).thenReturn(response2); 114 | 115 | when(response1.getStatus()).thenReturn(status1); 116 | when(response1.getPayload()).thenReturn(payload1); 117 | 118 | when(response2.getStatus()).thenReturn(status2); 119 | when(response2.getPayload()).thenReturn(payload2); 120 | 121 | when(status1.getCode()).thenReturn(OK); 122 | when(status2.getCode()).thenReturn(OK); 123 | 124 | underTest = new ComposerGroupPackagesJsonHandler(composerJsonProcessor); 125 | } 126 | 127 | @Test 128 | public void mergeContents() throws Exception { 129 | Response result = underTest.handle(context); 130 | 131 | assertThat(result.getStatus(), is(notNullValue())); 132 | assertThat(result.getStatus().getCode(), is(OK)); 133 | 134 | verify(composerJsonProcessor).mergePackagesJson(eq(repository), eq(asList(payload1, payload2))); 135 | } 136 | 137 | @Test 138 | public void ignoreNonOkResponse() throws Exception { 139 | when(status1.getCode()).thenReturn(INTERNAL_SERVER_ERROR); 140 | 141 | Response result = underTest.handle(context); 142 | 143 | assertThat(result.getStatus(), is(notNullValue())); 144 | assertThat(result.getStatus().getCode(), is(OK)); 145 | 146 | verify(composerJsonProcessor).mergePackagesJson(eq(repository), eq(singletonList(payload2))); 147 | } 148 | 149 | @Test 150 | public void ignoreResponseWithoutPayload() throws Exception { 151 | when(response1.getPayload()).thenReturn(null); 152 | 153 | Response result = underTest.handle(context); 154 | 155 | assertThat(result.getStatus(), is(notNullValue())); 156 | assertThat(result.getStatus().getCode(), is(OK)); 157 | 158 | verify(composerJsonProcessor).mergePackagesJson(eq(repository), eq(singletonList(payload2))); 159 | } 160 | } 161 | -------------------------------------------------------------------------------- /nexus-repository-composer/src/test/java/org/sonatype/nexus/repository/composer/internal/ComposerGroupProviderJsonHandlerTest.java: -------------------------------------------------------------------------------- 1 | package org.sonatype.nexus.repository.composer.internal; 2 | /* 3 | * Sonatype Nexus (TM) Open Source Version 4 | * Copyright (c) 2018-present Sonatype, Inc. 5 | * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. 6 | * 7 | * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, 8 | * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. 9 | * 10 | * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks 11 | * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the 12 | * Eclipse Foundation. All other trademarks are the property of their respective owners. 13 | */ 14 | 15 | import org.junit.Before; 16 | import org.junit.Test; 17 | import org.mockito.Mock; 18 | import org.sonatype.goodies.testsupport.TestSupport; 19 | import org.sonatype.nexus.common.collect.AttributesMap; 20 | import org.sonatype.nexus.repository.Repository; 21 | import org.sonatype.nexus.repository.composer.internal.group.ComposerGroupProviderJsonHandler; 22 | import org.sonatype.nexus.repository.group.GroupFacet; 23 | import org.sonatype.nexus.repository.view.*; 24 | 25 | import java.time.OffsetDateTime; 26 | 27 | import static java.util.Arrays.asList; 28 | import static java.util.Collections.singletonList; 29 | import static org.hamcrest.MatcherAssert.assertThat; 30 | import static org.hamcrest.Matchers.is; 31 | import static org.hamcrest.Matchers.notNullValue; 32 | import static org.mockito.ArgumentMatchers.any; 33 | import static org.mockito.ArgumentMatchers.eq; 34 | import static org.mockito.Mockito.verify; 35 | import static org.mockito.Mockito.when; 36 | import static org.sonatype.nexus.repository.http.HttpMethods.GET; 37 | import static org.sonatype.nexus.repository.http.HttpStatus.INTERNAL_SERVER_ERROR; 38 | import static org.sonatype.nexus.repository.http.HttpStatus.OK; 39 | 40 | public class ComposerGroupProviderJsonHandlerTest 41 | extends TestSupport 42 | { 43 | 44 | @Mock 45 | private Request request; 46 | 47 | @Mock 48 | private Context context; 49 | 50 | @Mock 51 | private Repository repository; 52 | 53 | @Mock 54 | private GroupFacet groupFacet; 55 | 56 | @Mock 57 | private Repository memberRepository1; 58 | 59 | @Mock 60 | private Repository memberRepository2; 61 | 62 | @Mock 63 | private ViewFacet memberRepository1ViewFacet; 64 | 65 | @Mock 66 | private ViewFacet memberRepository2ViewFacet; 67 | 68 | @Mock 69 | private Response response1; 70 | 71 | @Mock 72 | private Response response2; 73 | 74 | @Mock 75 | private Status status1; 76 | 77 | @Mock 78 | private Status status2; 79 | 80 | @Mock 81 | private Payload payload1; 82 | 83 | @Mock 84 | private Payload payload2; 85 | 86 | @Mock 87 | private ComposerJsonProcessor composerJsonProcessor; 88 | 89 | private ComposerGroupProviderJsonHandler underTest; 90 | 91 | @Before 92 | public void setUp() throws Exception { 93 | when(context.getRepository()).thenReturn(repository); 94 | when(context.getRequest()).thenReturn(request); 95 | when(context.getAttributes()).thenReturn(new AttributesMap()); 96 | 97 | when(request.getAction()).thenReturn(GET); 98 | when(request.getAttributes()).thenReturn(new AttributesMap()); 99 | when(request.getHeaders()).thenReturn(new Headers()); 100 | 101 | when(repository.facet(GroupFacet.class)).thenReturn(groupFacet); 102 | when(groupFacet.members()).thenReturn(asList(memberRepository1, memberRepository2)); 103 | 104 | when(memberRepository1.getName()).thenReturn("member1"); 105 | when(memberRepository1.facet(ViewFacet.class)).thenReturn(memberRepository1ViewFacet); 106 | 107 | when(memberRepository2.getName()).thenReturn("member2"); 108 | when(memberRepository2.facet(ViewFacet.class)).thenReturn(memberRepository2ViewFacet); 109 | 110 | when(memberRepository1ViewFacet.dispatch(request, context)).thenReturn(response1); 111 | when(memberRepository2ViewFacet.dispatch(request, context)).thenReturn(response2); 112 | 113 | when(response1.getStatus()).thenReturn(status1); 114 | when(response1.getPayload()).thenReturn(payload1); 115 | 116 | when(response2.getStatus()).thenReturn(status2); 117 | when(response2.getPayload()).thenReturn(payload2); 118 | 119 | when(status1.getCode()).thenReturn(OK); 120 | when(status2.getCode()).thenReturn(OK); 121 | 122 | underTest = new ComposerGroupProviderJsonHandler(composerJsonProcessor); 123 | } 124 | 125 | @Test 126 | public void mergeContents() throws Exception { 127 | Response result = underTest.handle(context); 128 | 129 | assertThat(result.getStatus(), is(notNullValue())); 130 | assertThat(result.getStatus().getCode(), is(OK)); 131 | 132 | verify(composerJsonProcessor) 133 | .mergeProviderJson(eq(repository), eq(asList(payload1, payload2)), any(OffsetDateTime.class)); 134 | } 135 | 136 | @Test 137 | public void ignoreNonOkResponse() throws Exception { 138 | when(status1.getCode()).thenReturn(INTERNAL_SERVER_ERROR); 139 | 140 | Response result = underTest.handle(context); 141 | 142 | assertThat(result.getStatus(), is(notNullValue())); 143 | assertThat(result.getStatus().getCode(), is(OK)); 144 | 145 | verify(composerJsonProcessor).mergeProviderJson(eq(repository), eq(singletonList(payload2)), any(OffsetDateTime.class)); 146 | } 147 | 148 | @Test 149 | public void ignoreResponseWithoutPayload() throws Exception { 150 | when(response1.getPayload()).thenReturn(null); 151 | 152 | Response result = underTest.handle(context); 153 | 154 | assertThat(result.getStatus(), is(notNullValue())); 155 | assertThat(result.getStatus().getCode(), is(OK)); 156 | 157 | verify(composerJsonProcessor).mergeProviderJson(eq(repository), eq(singletonList(payload2)), any(OffsetDateTime.class)); 158 | } 159 | } 160 | -------------------------------------------------------------------------------- /nexus-repository-composer/src/test/resources/org/sonatype/nexus/repository/composer/internal/mergeProviderJson.output.json: -------------------------------------------------------------------------------- 1 | { 2 | "packages" : { 3 | "vendor1/project1" : { 4 | "1.0.0" : { 5 | "name": "vendor1/project1", 6 | "version": "1.0.0", 7 | "description": "Description 1", 8 | "keywords": ["keyword1"], 9 | "homepage": "http://www.example.com/homepage1", 10 | "authors": [ 11 | { 12 | "name": "Author 1", 13 | "homepage": "http://example.com/author1" 14 | } 15 | ], 16 | "license": [ 17 | "MIT" 18 | ], 19 | "dist" : { 20 | "url" : "http://nexus.repo/base/repo/vendor1/project1/1.0.0/vendor1-project1-1.0.0.zip", 21 | "type" : "zip", 22 | "reference": "48954c0fa210437795be418e708b379598333d0a", 23 | "shasum": "" 24 | }, 25 | "uid" : 3457661917, 26 | "time" : "2018-01-09T15:53:01+00:00", 27 | "autoload": { 28 | "psr-4": { 29 | "psr-1-key": "psr-1-value" 30 | } 31 | }, 32 | "autoload-dev": { 33 | "psr-4": { 34 | "psr-1-key": "psr-1-value" 35 | } 36 | }, 37 | "include-path": [ 38 | "include-path-1" 39 | ], 40 | "replace": { 41 | "replace-1": "replace-1-value" 42 | }, 43 | "require": { 44 | "dependency-1": "version-1" 45 | }, 46 | "require-dev": { 47 | "dev-dependency-1": "dev-version-1" 48 | }, 49 | "suggest": { 50 | "suggest-1": "description-1" 51 | }, 52 | "bin": [ 53 | "bin-1" 54 | ], 55 | "conflict": { 56 | "conflict-1": "version-1" 57 | }, 58 | "extra": { 59 | "branch-alias": { 60 | "branch-1": "version-1" 61 | } 62 | }, 63 | "provide" : { 64 | "provide-1" : "version-1" 65 | }, 66 | "target-dir" : "target-dir-1", 67 | "scripts": { 68 | "scripts-1": [ 69 | "script-1" 70 | ] 71 | }, 72 | "type" : "type-1-value" 73 | }, 74 | "2.0.0" : { 75 | "name": "vendor1/project1", 76 | "version": "2.0.0", 77 | "dist" : { 78 | "url" : "http://nexus.repo/base/repo/vendor1/project1/2.0.0/vendor1-project1-2.0.0.zip", 79 | "type" : "zip", 80 | "reference": "ref2", 81 | "shasum": "sha2" 82 | }, 83 | "uid" : 4088247242, 84 | "time" : "2008-05-15T16:30:00+00:00", 85 | "autoload": { 86 | "psr-4": { 87 | "psr-4-key": "psr-4-value" 88 | } 89 | }, 90 | "require": { 91 | "dependency-4": "version-4" 92 | }, 93 | "require-dev": { 94 | "dev-dependency-4": "dev-version-4" 95 | }, 96 | "suggest": { 97 | "suggest-4": "description-4" 98 | }, 99 | "type" : "type-2-value" 100 | } 101 | }, 102 | "vendor2/project2" : { 103 | "3.0.0" : { 104 | "name": "vendor2/project2", 105 | "version": "3.0.0", 106 | "dist" : { 107 | "url" : "http://nexus.repo/base/repo/vendor2/project2/3.0.0/vendor2-project2-3.0.0.zip", 108 | "type" : "zip", 109 | "reference": "ref3", 110 | "shasum": "sha3" 111 | }, 112 | "uid" : 861509421, 113 | "time" : "1979-07-11T16:30:00+00:00", 114 | "autoload": { 115 | "psr-4": { 116 | "psr-5-key": "psr-5-value" 117 | } 118 | }, 119 | "require": { 120 | "dependency-5": "version-5" 121 | }, 122 | "require-dev": { 123 | "dev-dependency-5": "dev-version-5" 124 | }, 125 | "suggest": { 126 | "suggest-5": "description-5" 127 | }, 128 | "type" : "type-3-value" 129 | }, 130 | "4.0.0" : { 131 | "name": "vendor2/project2", 132 | "version": "4.0.0", 133 | "dist" : { 134 | "url" : "http://nexus.repo/base/repo/vendor2/project2/4.0.0/vendor2-project2-4.0.0.zip", 135 | "type" : "zip", 136 | "reference": "ref4", 137 | "shasum": "sha4" 138 | }, 139 | "uid" : 1702476377, 140 | "time" : "2008-05-15T16:30:00+00:00", 141 | "autoload": { 142 | "psr-4": { 143 | "psr-6-key": "psr-6-value" 144 | } 145 | }, 146 | "require": { 147 | "dependency-6": "version-6" 148 | }, 149 | "require-dev": { 150 | "dev-dependency-6": "dev-version-6" 151 | }, 152 | "suggest": { 153 | "suggest-6": "description-6" 154 | }, 155 | "type" : "type-4-value" 156 | } 157 | }, 158 | "vendor3/project3" : { 159 | "2.0.0": { 160 | "name": "vendor3/project3", 161 | "version": "2.0.0", 162 | "description": "Description 3", 163 | "keywords": ["keyword3"], 164 | "homepage": "http://www.example.com/homepage3", 165 | "authors": [ 166 | { 167 | "name": "Author 2", 168 | "homepage": "http://example.com/author2" 169 | } 170 | ], 171 | "license": [ 172 | "MIT" 173 | ], 174 | "dist": { 175 | "url": "http://nexus.repo/base/repo/vendor3/project3/2.0.0/vendor3-project3-2.0.0.zip", 176 | "type": "zip", 177 | "reference": "be418e708b379598333d0a48954c0fa210437795", 178 | "shasum": "" 179 | }, 180 | "uid": 3572654212, 181 | "time": "2018-01-09T15:53:01+00:00", 182 | "autoload": { 183 | "psr-4": { 184 | "psr-2-key": "psr-2-value" 185 | } 186 | }, 187 | "require": { 188 | "dependency-2": "version-2" 189 | }, 190 | "require-dev": { 191 | "dev-dependency-2": "dev-version-2" 192 | }, 193 | "suggest": { 194 | "suggest-2": "description-2" 195 | }, 196 | "type" : "type-2-value" 197 | } 198 | } 199 | } 200 | } 201 | -------------------------------------------------------------------------------- /nexus-repository-composer/src/test/java/org/sonatype/nexus/repository/composer/internal/hosted/ComposerHostedFacetImplTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Sonatype Nexus (TM) Open Source Version 3 | * Copyright (c) 2018-present Sonatype, Inc. 4 | * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. 5 | * 6 | * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, 7 | * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. 8 | * 9 | * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks 10 | * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the 11 | * Eclipse Foundation. All other trademarks are the property of their respective owners. 12 | */ 13 | package org.sonatype.nexus.repository.composer.internal.hosted; 14 | 15 | import com.google.common.collect.ImmutableMap; 16 | import org.junit.Before; 17 | import org.junit.Test; 18 | import org.mockito.ArgumentCaptor; 19 | import org.mockito.Mock; 20 | import org.sonatype.goodies.testsupport.TestSupport; 21 | import org.sonatype.nexus.repository.Repository; 22 | import org.sonatype.nexus.repository.composer.ComposerContentFacet; 23 | import org.sonatype.nexus.repository.composer.internal.ComposerJsonProcessor; 24 | import org.sonatype.nexus.repository.content.fluent.FluentComponent; 25 | import org.sonatype.nexus.repository.content.fluent.FluentComponents; 26 | import org.sonatype.nexus.repository.content.fluent.FluentQuery; 27 | import org.sonatype.nexus.repository.content.fluent.internal.FluentComponentQueryImpl; 28 | import org.sonatype.nexus.repository.view.Content; 29 | import org.sonatype.nexus.repository.view.Payload; 30 | 31 | import java.util.Map; 32 | import java.util.Optional; 33 | 34 | import static org.hamcrest.MatcherAssert.assertThat; 35 | import static org.hamcrest.Matchers.is; 36 | import static org.mockito.Mockito.*; 37 | 38 | public class ComposerHostedFacetImplTest 39 | extends TestSupport 40 | { 41 | private static final String VENDOR = "vendor"; 42 | 43 | private static final String PROJECT = "project"; 44 | 45 | private static final String VERSION = "version"; 46 | 47 | private static final String SRC_TYPE = "src-type"; 48 | 49 | private static final String SRC_URL = "src-url"; 50 | 51 | private static final String SRC_REF = "src-ref"; 52 | 53 | private static final String ZIPBALL_PATH = "/vendor/project/version/vendor-project-version.zip"; 54 | 55 | private static final String PROVIDER_PATH = "/p/vendor/project.json"; 56 | 57 | @Mock 58 | private Repository repository; 59 | 60 | @Mock 61 | private ComposerContentFacet composerContentFacet; 62 | 63 | @Mock 64 | private ComposerJsonProcessor composerJsonProcessor; 65 | 66 | @Mock 67 | private Payload payload; 68 | 69 | @Mock 70 | private Content content; 71 | 72 | @Mock 73 | private FluentComponents components; 74 | 75 | private ComposerHostedFacetImpl underTest; 76 | 77 | @Before 78 | public void setUp() throws Exception { 79 | when(repository.facet(ComposerContentFacet.class)).thenReturn(composerContentFacet); 80 | when(composerContentFacet.components()).thenReturn(components); 81 | 82 | underTest = spy(new ComposerHostedFacetImpl(composerJsonProcessor)); 83 | underTest.attach(repository); 84 | } 85 | 86 | @Test 87 | public void testUpload() throws Exception { 88 | underTest.upload(VENDOR, PROJECT, VERSION, SRC_TYPE, SRC_URL, SRC_REF, payload); 89 | verify(composerContentFacet).put(ZIPBALL_PATH, payload, SRC_TYPE, SRC_URL, SRC_REF); 90 | } 91 | 92 | @Test 93 | public void testGetZipball() throws Exception { 94 | when(composerContentFacet.get(ZIPBALL_PATH)).thenReturn(Optional.of(content)); 95 | assertThat(underTest.getZipball(ZIPBALL_PATH), is(content)); 96 | } 97 | 98 | @Test 99 | public void testGetPackagesJson() throws Exception { 100 | when(composerJsonProcessor.generatePackagesFromComponents(repository, components)).thenReturn(content); 101 | assertThat(underTest.getPackagesJson(), is(content)); 102 | } 103 | 104 | @Test 105 | public void testGetListJson() throws Exception { 106 | // Without filter 107 | when(composerJsonProcessor.generateListFromComponents(components)).thenReturn(content); 108 | assertThat(underTest.getListJson(null), is(content)); 109 | 110 | // With filter 111 | FluentQuery query = mock(FluentComponentQueryImpl.class); 112 | when(components.byFilter("namespace LIKE #{filterParams.vendor} AND name LIKE #{filterParams.project}", 113 | ImmutableMap.of("vendor", "test", "project", "%"))).thenReturn(query); 114 | when(composerJsonProcessor.generateListFromComponents(query)).thenReturn(content); 115 | assertThat(underTest.getListJson("test/*"), is(content)); 116 | 117 | when(components.byFilter("namespace LIKE #{filterParams.vendor} AND name LIKE #{filterParams.project}", 118 | ImmutableMap.of("vendor", "%abc%", "project", "pr0_j3cT"))).thenReturn(query); 119 | when(composerJsonProcessor.generateListFromComponents(query)).thenReturn(content); 120 | assertThat(underTest.getListJson("*abc**/pr0_j3cT"), is(content)); 121 | 122 | // Invalid filter 123 | when(composerJsonProcessor.generateListFromComponents(null)).thenReturn(content); 124 | assertThat(underTest.getListJson("In\\al1d"), is(content)); 125 | } 126 | 127 | @Test 128 | public void testGetProviderJson() throws Exception { 129 | when(composerContentFacet.get(PROVIDER_PATH)).thenReturn(Optional.of(content)); 130 | assertThat(underTest.getProviderJson(VENDOR, PROJECT), is(content)); 131 | } 132 | 133 | @Test 134 | public void testBuildProviderJson() throws Exception { 135 | ArgumentCaptor filter = ArgumentCaptor.forClass(String.class); 136 | ArgumentCaptor> filterArgs = ArgumentCaptor.forClass(Map.class); 137 | 138 | FluentQuery query = mock(FluentQuery.class); 139 | when(components.byFilter(filter.capture(), filterArgs.capture())).thenReturn(query); 140 | when(composerJsonProcessor.buildProviderJson(repository, composerContentFacet, query)) 141 | .thenReturn(Optional.of(content)); 142 | 143 | Optional res = underTest.rebuildProviderJson(VENDOR, PROJECT); 144 | assertThat(res.isPresent(), is(true)); 145 | assertThat(res.get(), is(content)); 146 | assertThat(filter.getValue(), is("namespace = #{filterParams.vendor} AND name = #{filterParams.project}")); 147 | assertThat(filterArgs.getValue(), is(ImmutableMap.of("vendor", VENDOR, "project", PROJECT))); 148 | } 149 | } 150 | -------------------------------------------------------------------------------- /nexus-repository-composer-it/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4.0.0 4 | 5 | 6 | org.sonatype.nexus.plugins 7 | composer-parent 8 | 0.1.10-SNAPSHOT 9 | ../pom.xml 10 | 11 | 12 | nexus-repository-composer-it 13 | bundle 14 | 15 | 16 | 19 | 20 | org.sonatype.nexus 21 | nexus-pax-exam 22 | test 23 | 24 | 25 | 26 | org.ops4j.pax.exam 27 | pax-exam-features 28 | xml 29 | 30 | 31 | 34 | 35 | org.sonatype.nexus 36 | nexus-core 37 | test 38 | 39 | 40 | 41 | org.sonatype.nexus.testsuite 42 | nexus-repository-testsupport 43 | ${nxrm-version} 44 | test 45 | 46 | 47 | org.sonatype.nexus.testsuite 48 | nexus-repository-testsupport 49 | ${nxrm-version} 50 | features 51 | xml 52 | test 53 | 54 | 55 | 56 | org.sonatype.nexus 57 | nexus-bootstrap 58 | test 59 | 60 | 61 | 62 | org.sonatype.nexus 63 | nexus-rapture 64 | test 65 | 66 | 67 | 68 | org.sonatype.nexus 69 | nexus-repository-content 70 | test 71 | 72 | 73 | 74 | org.sonatype.nexus 75 | nexus-rest 76 | test 77 | 78 | 79 | 80 | org.sonatype.nexus 81 | nexus-rest-client 82 | test 83 | 84 | 85 | 86 | org.sonatype.nexus 87 | nexus-rest-jackson2 88 | test 89 | 90 | 91 | 92 | org.sonatype.nexus 93 | nexus-script 94 | test 95 | 96 | 97 | 98 | org.sonatype.nexus 99 | nexus-extdirect 100 | test 101 | 102 | 103 | 104 | org.sonatype.nexus 105 | nexus-test-common 106 | 107 | 108 | 111 | 112 | 113 | org.sonatype.nexus.plugins 114 | nexus-repository-composer 115 | ${project.version} 116 | test 117 | 118 | 119 | 122 | 123 | 124 | org.sonatype.nexus.assemblies 125 | nexus-base-template 126 | zip 127 | test 128 | 129 | 130 | * 131 | * 132 | 133 | 134 | 135 | 136 | 137 | org.sonatype.nexus.plugins 138 | nexus-blobstore-tasks 139 | test 140 | 141 | 142 | 143 | org.sonatype.nexus.testsuite 144 | nexus-it-suite-data 145 | ${nxrm-version} 146 | test 147 | 148 | 149 | 150 | 151 | 152 | 155 | 156 | org.apache.servicemix.tooling 157 | depends-maven-plugin 158 | 159 | 160 | 163 | 164 | org.apache.karaf.tooling 165 | karaf-maven-plugin 166 | 167 | 168 | fetch-pax-exam-features 169 | generate-resources 170 | 171 | features-add-to-repository 172 | 173 | 174 | 175 | file:${project.artifactMap(org.ops4j.pax.exam:pax-exam-features).file} 176 | 177 | 178 | 179 | 180 | 181 | 182 | org.apache.maven.plugins 183 | maven-antrun-plugin 184 | 185 | 186 | unpack-testsuite-data 187 | process-test-resources 188 | 189 | run 190 | 191 | 192 | 193 | 194 | 195 | 196 | 197 | 198 | 199 | 200 | 201 | 202 | 203 | 204 | 205 | 206 | -------------------------------------------------------------------------------- /nexus-repository-composer/src/main/java/org/sonatype/nexus/repository/composer/internal/recipe/ComposerRecipeSupport.groovy: -------------------------------------------------------------------------------- 1 | /* 2 | * Sonatype Nexus (TM) Open Source Version 3 | * Copyright (c) 2018-present Sonatype, Inc. 4 | * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. 5 | * 6 | * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, 7 | * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. 8 | * 9 | * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks 10 | * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the 11 | * Eclipse Foundation. All other trademarks are the property of their respective owners. 12 | */ 13 | package org.sonatype.nexus.repository.composer.internal.recipe 14 | 15 | import groovy.transform.CompileStatic 16 | import org.sonatype.nexus.repository.composer.AssetKind 17 | import org.sonatype.nexus.repository.composer.ComposerContentFacet 18 | import org.sonatype.nexus.repository.composer.internal.ComposerMaintenanceFacet 19 | import org.sonatype.nexus.repository.composer.internal.ComposerSecurityFacet 20 | import org.sonatype.nexus.repository.content.browse.BrowseFacet 21 | 22 | import javax.inject.Inject 23 | import javax.inject.Provider 24 | 25 | import org.sonatype.nexus.common.db.DatabaseCheck 26 | import org.sonatype.nexus.repository.Format 27 | import org.sonatype.nexus.repository.RecipeSupport 28 | import org.sonatype.nexus.repository.Type 29 | import org.sonatype.nexus.repository.http.PartialFetchHandler 30 | import org.sonatype.nexus.repository.search.index.SearchIndexFacet 31 | import org.sonatype.nexus.repository.security.SecurityHandler 32 | import org.sonatype.nexus.repository.view.ConfigurableViewFacet 33 | import org.sonatype.nexus.repository.view.Context 34 | import org.sonatype.nexus.repository.view.Route.Builder 35 | import org.sonatype.nexus.repository.view.handlers.ConditionalRequestHandler 36 | import org.sonatype.nexus.repository.view.handlers.ContentHeadersHandler 37 | import org.sonatype.nexus.repository.view.handlers.ExceptionHandler 38 | import org.sonatype.nexus.repository.view.handlers.HandlerContributor 39 | import org.sonatype.nexus.repository.view.handlers.TimingHandler 40 | import org.sonatype.nexus.repository.view.matchers.ActionMatcher 41 | import org.sonatype.nexus.repository.view.matchers.LiteralMatcher 42 | import org.sonatype.nexus.repository.view.matchers.logic.LogicMatchers 43 | import org.sonatype.nexus.repository.view.matchers.token.TokenMatcher 44 | 45 | import static org.sonatype.nexus.repository.http.HttpMethods.GET 46 | import static org.sonatype.nexus.repository.http.HttpMethods.HEAD 47 | import static org.sonatype.nexus.repository.http.HttpMethods.PUT 48 | 49 | /** 50 | * Abstract superclass containing methods and constants common to most Composer repository recipes. 51 | */ 52 | @CompileStatic 53 | abstract class ComposerRecipeSupport 54 | extends RecipeSupport 55 | { 56 | public static final String VENDOR_TOKEN = 'vendor' 57 | 58 | public static final String PROJECT_TOKEN = 'project' 59 | 60 | public static final String VERSION_TOKEN = 'version' 61 | 62 | public static final String NAME_TOKEN = 'name' 63 | 64 | public static final String PACKAGE_FIELD_NAME = "package"; 65 | 66 | public static final String SOURCE_TYPE_FIELD_NAME = 'src-type'; 67 | 68 | public static final String SOURCE_URL_FIELD_NAME = 'src-url'; 69 | 70 | public static final String SOURCE_REFERENCE_FIELD_NAME = 'src-ref'; 71 | 72 | private DatabaseCheck databaseCheck; 73 | 74 | @Inject 75 | Provider contentFacet 76 | 77 | @Inject 78 | Provider maintenanceFacet 79 | 80 | @Inject 81 | Provider securityFacet 82 | 83 | @Inject 84 | Provider viewFacet 85 | 86 | @Inject 87 | Provider searchFacet 88 | 89 | @Inject 90 | Provider browseFacet 91 | 92 | @Inject 93 | ExceptionHandler exceptionHandler 94 | 95 | @Inject 96 | TimingHandler timingHandler 97 | 98 | @Inject 99 | SecurityHandler securityHandler 100 | 101 | @Inject 102 | PartialFetchHandler partialFetchHandler 103 | 104 | @Inject 105 | ConditionalRequestHandler conditionalRequestHandler 106 | 107 | @Inject 108 | ContentHeadersHandler contentHeadersHandler 109 | 110 | @Inject 111 | HandlerContributor handlerContributor 112 | 113 | protected ComposerRecipeSupport(final Type type, final Format format) { 114 | super(type, format) 115 | } 116 | 117 | Closure assetKindHandler = { Context context, AssetKind value -> 118 | context.attributes.set(AssetKind, value) 119 | return context.proceed() 120 | } 121 | 122 | static Builder packagesMatcher() { 123 | new Builder().matcher( 124 | LogicMatchers.and( 125 | new ActionMatcher(GET, HEAD), 126 | new LiteralMatcher('/packages.json') 127 | )) 128 | } 129 | 130 | static Builder listMatcher() { 131 | new Builder().matcher( 132 | LogicMatchers.and( 133 | new ActionMatcher(GET, HEAD), 134 | new LiteralMatcher('/packages/list.json') 135 | )) 136 | } 137 | 138 | static Builder providerMatcher() { 139 | new Builder().matcher( 140 | LogicMatchers.and( 141 | new ActionMatcher(GET, HEAD), 142 | new TokenMatcher('/p/{vendor:.+}/{project:.+}.json') 143 | )) 144 | } 145 | 146 | static Builder packageMatcher() { 147 | new Builder().matcher( 148 | LogicMatchers.and( 149 | new ActionMatcher(GET, HEAD), 150 | new TokenMatcher('/p2/{vendor:.+}/{project:.+}.json') 151 | )) 152 | } 153 | 154 | static Builder zipballMatcher() { 155 | new Builder().matcher( 156 | LogicMatchers.and( 157 | new ActionMatcher(GET, HEAD), 158 | new TokenMatcher('/{vendor:.+}/{project:.+}/{version:.+}/{name:.+}.zip') 159 | )) 160 | } 161 | 162 | static Builder uploadMatcher() { 163 | new Builder().matcher( 164 | LogicMatchers.and( 165 | new ActionMatcher(PUT), 166 | new TokenMatcher('/packages/upload/{vendor:.+}/{project:.+}/{version:.+}') 167 | )) 168 | } 169 | 170 | @Inject 171 | public void setDatabaseCheck(final DatabaseCheck databaseCheck) { 172 | this.databaseCheck = databaseCheck; 173 | } 174 | 175 | @Override 176 | public boolean isFeatureEnabled() { 177 | if (databaseCheck != null && !databaseCheck.isAllowedByVersion(getClass())) { 178 | return false; 179 | } 180 | 181 | return true; 182 | } 183 | } 184 | -------------------------------------------------------------------------------- /nexus-repository-composer/src/main/java/org/sonatype/nexus/repository/composer/internal/recipe/ComposerHostedRecipe.groovy: -------------------------------------------------------------------------------- 1 | /* 2 | * Sonatype Nexus (TM) Open Source Version 3 | * Copyright (c) 2018-present Sonatype, Inc. 4 | * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. 5 | * 6 | * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, 7 | * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. 8 | * 9 | * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks 10 | * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the 11 | * Eclipse Foundation. All other trademarks are the property of their respective owners. 12 | */ 13 | package org.sonatype.nexus.repository.composer.internal.recipe 14 | 15 | import org.sonatype.nexus.repository.composer.ComposerFormat 16 | import org.sonatype.nexus.repository.composer.internal.hosted.ComposerHostedDownloadHandler 17 | import org.sonatype.nexus.repository.composer.ComposerHostedFacet 18 | import org.sonatype.nexus.repository.composer.internal.hosted.ComposerHostedUploadHandler 19 | import org.sonatype.nexus.repository.view.handlers.LastDownloadedHandler 20 | 21 | import javax.annotation.Nonnull 22 | import javax.inject.Inject 23 | import javax.inject.Named 24 | import javax.inject.Provider 25 | import javax.inject.Singleton 26 | 27 | import org.sonatype.nexus.common.upgrade.AvailabilityVersion; 28 | import org.sonatype.nexus.repository.Format 29 | import org.sonatype.nexus.repository.Repository 30 | import org.sonatype.nexus.repository.Type 31 | import org.sonatype.nexus.repository.http.HttpHandlers 32 | import org.sonatype.nexus.repository.types.HostedType 33 | import org.sonatype.nexus.repository.view.ConfigurableViewFacet 34 | import org.sonatype.nexus.repository.view.Router 35 | import org.sonatype.nexus.repository.view.ViewFacet 36 | 37 | import static org.sonatype.nexus.repository.composer.AssetKind.LIST 38 | import static org.sonatype.nexus.repository.composer.AssetKind.PACKAGE 39 | import static org.sonatype.nexus.repository.composer.AssetKind.PACKAGES 40 | import static org.sonatype.nexus.repository.composer.AssetKind.PROVIDER 41 | import static org.sonatype.nexus.repository.composer.AssetKind.ZIPBALL 42 | 43 | /** 44 | * Recipe for creating a Composer hosted repository. 45 | */ 46 | @AvailabilityVersion(from = "1.0") 47 | @Named(ComposerHostedRecipe.NAME) 48 | @Singleton 49 | class ComposerHostedRecipe 50 | extends ComposerRecipeSupport 51 | { 52 | public static final String NAME = 'composer-hosted' 53 | 54 | @Inject 55 | Provider hostedFacet 56 | 57 | @Inject 58 | LastDownloadedHandler lastDownloadedHandler 59 | 60 | @Inject 61 | ComposerHostedUploadHandler uploadHandler 62 | 63 | @Inject 64 | ComposerHostedDownloadHandler downloadHandler 65 | 66 | @Inject 67 | ComposerHostedRecipe(@Named(HostedType.NAME) final Type type, @Named(ComposerFormat.NAME) final Format format) { 68 | super(type, format) 69 | } 70 | 71 | @Override 72 | void apply(@Nonnull final Repository repository) throws Exception { 73 | repository.attach(contentFacet.get()) 74 | repository.attach(securityFacet.get()) 75 | repository.attach(configure(viewFacet.get())) 76 | repository.attach(hostedFacet.get()) 77 | repository.attach(maintenanceFacet.get()) 78 | repository.attach(searchFacet.get()) 79 | repository.attach(browseFacet.get()) 80 | } 81 | 82 | /** 83 | * Configure {@link ViewFacet}. 84 | */ 85 | private ViewFacet configure(final ConfigurableViewFacet facet) { 86 | Router.Builder builder = new Router.Builder() 87 | 88 | builder.route(packagesMatcher() 89 | .handler(timingHandler) 90 | .handler(assetKindHandler.rcurry(PACKAGES)) 91 | .handler(securityHandler) 92 | .handler(exceptionHandler) 93 | .handler(handlerContributor) 94 | .handler(conditionalRequestHandler) 95 | .handler(partialFetchHandler) 96 | .handler(contentHeadersHandler) 97 | .handler(downloadHandler) 98 | .create()) 99 | 100 | builder.route(listMatcher() 101 | .handler(timingHandler) 102 | .handler(assetKindHandler.rcurry(LIST)) 103 | .handler(securityHandler) 104 | .handler(exceptionHandler) 105 | .handler(handlerContributor) 106 | .handler(conditionalRequestHandler) 107 | .handler(partialFetchHandler) 108 | .handler(contentHeadersHandler) 109 | .handler(downloadHandler) 110 | .create()) 111 | 112 | builder.route(providerMatcher() 113 | .handler(timingHandler) 114 | .handler(assetKindHandler.rcurry(PROVIDER)) 115 | .handler(securityHandler) 116 | .handler(exceptionHandler) 117 | .handler(handlerContributor) 118 | .handler(conditionalRequestHandler) 119 | .handler(partialFetchHandler) 120 | .handler(contentHeadersHandler) 121 | .handler(lastDownloadedHandler) 122 | .handler(downloadHandler) 123 | .create()) 124 | 125 | builder.route(packageMatcher() 126 | .handler(timingHandler) 127 | .handler(assetKindHandler.rcurry(PACKAGE)) 128 | .handler(securityHandler) 129 | .handler(exceptionHandler) 130 | .handler(handlerContributor) 131 | .handler(conditionalRequestHandler) 132 | .handler(partialFetchHandler) 133 | .handler(contentHeadersHandler) 134 | .handler(lastDownloadedHandler) 135 | .handler(downloadHandler) 136 | .create()) 137 | 138 | builder.route(zipballMatcher() 139 | .handler(timingHandler) 140 | .handler(assetKindHandler.rcurry(ZIPBALL)) 141 | .handler(securityHandler) 142 | .handler(exceptionHandler) 143 | .handler(handlerContributor) 144 | .handler(conditionalRequestHandler) 145 | .handler(partialFetchHandler) 146 | .handler(contentHeadersHandler) 147 | .handler(lastDownloadedHandler) 148 | .handler(downloadHandler) 149 | .create()) 150 | 151 | builder.route(uploadMatcher() 152 | .handler(timingHandler) 153 | .handler(assetKindHandler.rcurry(ZIPBALL)) 154 | .handler(securityHandler) 155 | .handler(exceptionHandler) 156 | .handler(handlerContributor) 157 | .handler(conditionalRequestHandler) 158 | .handler(partialFetchHandler) 159 | .handler(contentHeadersHandler) 160 | .handler(uploadHandler) 161 | .create()) 162 | 163 | addBrowseUnsupportedRoute(builder) 164 | 165 | builder.defaultHandlers(HttpHandlers.notFound()) 166 | 167 | facet.configure(builder.create()) 168 | 169 | return facet 170 | } 171 | } 172 | -------------------------------------------------------------------------------- /nexus-repository-composer/src/main/java/org/sonatype/nexus/repository/composer/internal/recipe/ComposerProxyRecipe.groovy: -------------------------------------------------------------------------------- 1 | /* 2 | * Sonatype Nexus (TM) Open Source Version 3 | * Copyright (c) 2018-present Sonatype, Inc. 4 | * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. 5 | * 6 | * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, 7 | * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. 8 | * 9 | * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks 10 | * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the 11 | * Eclipse Foundation. All other trademarks are the property of their respective owners. 12 | */ 13 | package org.sonatype.nexus.repository.composer.internal.recipe 14 | 15 | import org.sonatype.nexus.repository.composer.AssetKind 16 | import org.sonatype.nexus.repository.composer.ComposerFormat 17 | import org.sonatype.nexus.repository.composer.internal.proxy.ComposerPackageHandler 18 | import org.sonatype.nexus.repository.composer.internal.proxy.ComposerProviderHandler 19 | import org.sonatype.nexus.repository.composer.internal.proxy.ComposerProxyFacet 20 | 21 | import javax.annotation.Nonnull 22 | import javax.inject.Inject 23 | import javax.inject.Named 24 | import javax.inject.Provider 25 | import javax.inject.Singleton 26 | 27 | import org.sonatype.nexus.common.upgrade.AvailabilityVersion; 28 | import org.sonatype.nexus.repository.Format 29 | import org.sonatype.nexus.repository.Repository 30 | import org.sonatype.nexus.repository.Type 31 | import org.sonatype.nexus.repository.cache.NegativeCacheFacet 32 | import org.sonatype.nexus.repository.cache.NegativeCacheHandler 33 | import org.sonatype.nexus.repository.http.HttpHandlers 34 | import org.sonatype.nexus.repository.httpclient.HttpClientFacet 35 | import org.sonatype.nexus.repository.proxy.ProxyHandler 36 | import org.sonatype.nexus.repository.purge.PurgeUnusedFacet 37 | import org.sonatype.nexus.repository.types.ProxyType 38 | import org.sonatype.nexus.repository.view.ConfigurableViewFacet 39 | import org.sonatype.nexus.repository.view.Router 40 | import org.sonatype.nexus.repository.view.ViewFacet 41 | 42 | /** 43 | * Recipe for creating a Composer proxy repository. 44 | */ 45 | @AvailabilityVersion(from = "1.0") 46 | @Named(ComposerProxyRecipe.NAME) 47 | @Singleton 48 | class ComposerProxyRecipe 49 | extends ComposerRecipeSupport 50 | { 51 | public static final String NAME = 'composer-proxy' 52 | 53 | @Inject 54 | Provider proxyFacet 55 | 56 | @Inject 57 | Provider negativeCacheFacet 58 | 59 | @Inject 60 | Provider purgeUnusedFacet 61 | 62 | @Inject 63 | NegativeCacheHandler negativeCacheHandler 64 | 65 | @Inject 66 | ProxyHandler proxyHandler 67 | 68 | @Inject 69 | Provider httpClientFacet 70 | 71 | @Inject 72 | ComposerProviderHandler composerProviderHandler 73 | 74 | @Inject 75 | ComposerPackageHandler composerPackageHandler 76 | 77 | @Inject 78 | ComposerProxyRecipe(@Named(ProxyType.NAME) final Type type, @Named(ComposerFormat.NAME) final Format format) { 79 | super(type, format) 80 | } 81 | 82 | @Override 83 | void apply(@Nonnull final Repository repository) throws Exception { 84 | repository.attach(contentFacet.get()) 85 | repository.attach(securityFacet.get()) 86 | repository.attach(configure(viewFacet.get())) 87 | repository.attach(httpClientFacet.get()) 88 | repository.attach(negativeCacheFacet.get()) 89 | repository.attach(proxyFacet.get()) 90 | repository.attach(searchFacet.get()) 91 | repository.attach(browseFacet.get()) 92 | repository.attach(purgeUnusedFacet.get()) 93 | repository.attach(maintenanceFacet.get()) 94 | } 95 | 96 | private ViewFacet configure(final ConfigurableViewFacet facet) { 97 | Router.Builder builder = new Router.Builder() 98 | 99 | builder.route(packagesMatcher() 100 | .handler(timingHandler) 101 | .handler(assetKindHandler.rcurry(AssetKind.PACKAGES)) 102 | .handler(securityHandler) 103 | .handler(exceptionHandler) 104 | .handler(handlerContributor) 105 | .handler(negativeCacheHandler) 106 | .handler(conditionalRequestHandler) 107 | .handler(partialFetchHandler) 108 | .handler(contentHeadersHandler) 109 | .handler(proxyHandler) 110 | .create()) 111 | 112 | builder.route(listMatcher() 113 | .handler(timingHandler) 114 | .handler(assetKindHandler.rcurry(AssetKind.LIST)) 115 | .handler(securityHandler) 116 | .handler(exceptionHandler) 117 | .handler(handlerContributor) 118 | .handler(negativeCacheHandler) 119 | .handler(conditionalRequestHandler) 120 | .handler(partialFetchHandler) 121 | .handler(contentHeadersHandler) 122 | .handler(proxyHandler) 123 | .create()) 124 | 125 | builder.route(providerMatcher() 126 | .handler(timingHandler) 127 | .handler(assetKindHandler.rcurry(AssetKind.PROVIDER)) 128 | .handler(securityHandler) 129 | .handler(exceptionHandler) 130 | .handler(handlerContributor) 131 | .handler(negativeCacheHandler) 132 | .handler(conditionalRequestHandler) 133 | .handler(partialFetchHandler) 134 | .handler(contentHeadersHandler) 135 | .handler(composerProviderHandler) 136 | .handler(proxyHandler) 137 | .create()) 138 | 139 | builder.route(packageMatcher() 140 | .handler(timingHandler) 141 | .handler(assetKindHandler.rcurry(AssetKind.PACKAGE)) 142 | .handler(securityHandler) 143 | .handler(exceptionHandler) 144 | .handler(handlerContributor) 145 | .handler(negativeCacheHandler) 146 | .handler(conditionalRequestHandler) 147 | .handler(partialFetchHandler) 148 | .handler(contentHeadersHandler) 149 | .handler(composerPackageHandler) 150 | .handler(proxyHandler) 151 | .create()) 152 | 153 | builder.route(zipballMatcher() 154 | .handler(timingHandler) 155 | .handler(assetKindHandler.rcurry(AssetKind.ZIPBALL)) 156 | .handler(securityHandler) 157 | .handler(exceptionHandler) 158 | .handler(handlerContributor) 159 | .handler(negativeCacheHandler) 160 | .handler(conditionalRequestHandler) 161 | .handler(partialFetchHandler) 162 | .handler(contentHeadersHandler) 163 | .handler(proxyHandler) 164 | .create()) 165 | 166 | addBrowseUnsupportedRoute(builder) 167 | 168 | builder.defaultHandlers(HttpHandlers.notFound()) 169 | 170 | facet.configure(builder.create()) 171 | 172 | return facet 173 | } 174 | } 175 | --------------------------------------------------------------------------------