diff --git a/.github/dependabot.yml b/.github/dependabot.yml index 0c09e3a..2766a7a 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -1,7 +1,7 @@ version: 2 updates: -- package-ecosystem: "maven" - directory: "/" - schedule: - interval: "daily" - time: "04:00" \ No newline at end of file + - package-ecosystem: "maven" + directory: "/" + schedule: + interval: "daily" + time: "04:00" \ No newline at end of file diff --git a/.travis-settings.xml b/.travis-settings.xml index 42544ad..2f935f7 100644 --- a/.travis-settings.xml +++ b/.travis-settings.xml @@ -1,24 +1,24 @@ - - - - sonatype-snapshots - Sonatype Snapshot Repository - https://s01.oss.sonatype.org/content/repositories/snapshots/ - - false - - - true - always - fail - - - - - - sonatype-snapshots - ${env.CI_DEPLOY_USERNAME} - ${env.CI_DEPLOY_PASSWORD} - - + + + + sonatype-snapshots + Sonatype Snapshot Repository + https://s01.oss.sonatype.org/content/repositories/snapshots/ + + false + + + true + always + fail + + + + + + sonatype-snapshots + ${env.CI_DEPLOY_USERNAME} + ${env.CI_DEPLOY_PASSWORD} + + diff --git a/.travis.yml b/.travis.yml index f39e1e3..8f593c2 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,30 +1,30 @@ version: "~> 1.0" git: - depth: 1 - quiet: true + depth: 1 + quiet: true language: java jdk: - - openjdk21 + - openjdk21 branches: - only: - - develop - - support/4.1.x - - support/4.0.x - - master + only: + - develop + - support/4.1.x + - support/4.0.x + - master cache: - directories: - - "$HOME/.m2" + directories: + - "$HOME/.m2" notifications: - slack: craftersoftware:7YiSb41ZG5rfMgiAY7Qgprc7 + slack: craftersoftware:7YiSb41ZG5rfMgiAY7Qgprc7 before_install: - - mkdir -p $HOME/.m2 - - cp .travis-settings.xml $HOME/.m2/settings.xml + - mkdir -p $HOME/.m2 + - cp .travis-settings.xml $HOME/.m2/settings.xml install: skip script: - - if [[ "$TRAVIS_PULL_REQUEST" == "false" ]]; then bash -c "mvn deploy --settings .travis-settings.xml"; fi - - if [[ "$TRAVIS_PULL_REQUEST" != "false" ]]; then bash -c "mvn clean install"; fi + - if [[ "$TRAVIS_PULL_REQUEST" == "false" ]]; then bash -c "mvn deploy --settings .travis-settings.xml"; fi + - if [[ "$TRAVIS_PULL_REQUEST" != "false" ]]; then bash -c "mvn clean install"; fi env: - global: - - secure: "RKIymLxx1swYPpES2yev6xdp54ySWV9PiMFUjaAar3vFVJtrK512NftcNc9vMok9i0PwGAX8CxNXJh6GSZg1kwHQIXNYkEXNfDWzS9YUIEeZi3TADsRNpLLso/lopcBe/DvRcbcUo0U+A0JpfyyHsCOtB6sMbe4gEnC8O96JqZaJk+Qmh6rIcikB0mki71FXcKY63mHJpcGlS2L597BVzuIbbO1eng1Uk/ji2wJhAOZFNtAqrdt7i9YC/8TT7U5b3OT27RstyYhRgJQZHvJadKISaOYdu1hFkdvUIRx7xmDCo+b1XyF6FBbHJgmyw6fqOqmX5uZ1oQjkZxEtYpmMGlYdWifLL1JKlDnG5ATavQha2rQPugy4e2DMxNfWB6Xs7fKtM9wxBDRhk+i1uh4rtqrCh2lL4s6jGI4gqKJ3Vqo2NbeYPM6+D3x7kL8LFlpWG0KMSBAGhUA8ZUDW422jLIVZS3KmjB72WWL0skwwlQ5Gdg8mxIgWUqbKDPwmFI8uMFxz2xRMQs0CbwGKH1UxpxO92Csuud7FlRlXrMv1gNrtzDMhxRXUc3kbZ0S0jULYAm7Av4EbMFBgsW6PrRQdEIe/nufnqE0AsrurXPUZORydJ3YxwmGRDbihWq9j96SaTBxB34t3QwLmDmX1szQuzL6QAixoL7kXfsbjwCbclXo=" - - secure: "HiUF2kZ/DphIURe671VrIRG6Nm8Q8szzgNTbLMQBFsbH7DneXTuAXHXqEVOE6xvac2PPPHc5KACfh3LDcJVZbzDCUGnMvy+yEYXBDOKi9wQYOiX5JwsYGdR/0N70oSevUm6pRxqp7VzsCyoasvatMLbBzm6XEq+Bu5SblAfOUuJaEiNYxqr6GMiX12DqkbWe814xi5ch8Z4Db4wFlVpz6jjfKVWiKAwenpFUzbl/pqhDx2RFkyWnpPQmSwaBIdkLkLrwEnTnVo4hKDqXboAq9pgDmGztC7dV4p+xwzGnHCNTJLxhYcRHFgr17/++0yP6HAIcx7h8jt67Kg6C8gtKYhz4ycSVMWKehov4FAwwCtO/GGlSNhAYlp/V1hexGaC+NuaMPQjA6imxc6nyzqefVByLIRNc2fa5px0ZHEtfckisMD+ioxTUZGcQcZYgB5V0143egk3Vui7KSdtwCYd99FbhEXNuDnLKhQKSqlMo2pyHYmg2NbMmUPd09UwJVEacQTwRS6JoOHDmyWxC9McDmQ2o8+jmyQ80TXl9bisJ+kUBSJf5cI4M10kl6uG6AtK15tRClPPC9sr+xvSymnx0cLPqUaabiHwN0S36AGcJPVms/0wIvci3l1UnkpzhP44LjUJ54Rliz3sFt5Xz2wiB0d8Cgh79s0dlYqEkQvaZL+4=" + global: + - secure: "RKIymLxx1swYPpES2yev6xdp54ySWV9PiMFUjaAar3vFVJtrK512NftcNc9vMok9i0PwGAX8CxNXJh6GSZg1kwHQIXNYkEXNfDWzS9YUIEeZi3TADsRNpLLso/lopcBe/DvRcbcUo0U+A0JpfyyHsCOtB6sMbe4gEnC8O96JqZaJk+Qmh6rIcikB0mki71FXcKY63mHJpcGlS2L597BVzuIbbO1eng1Uk/ji2wJhAOZFNtAqrdt7i9YC/8TT7U5b3OT27RstyYhRgJQZHvJadKISaOYdu1hFkdvUIRx7xmDCo+b1XyF6FBbHJgmyw6fqOqmX5uZ1oQjkZxEtYpmMGlYdWifLL1JKlDnG5ATavQha2rQPugy4e2DMxNfWB6Xs7fKtM9wxBDRhk+i1uh4rtqrCh2lL4s6jGI4gqKJ3Vqo2NbeYPM6+D3x7kL8LFlpWG0KMSBAGhUA8ZUDW422jLIVZS3KmjB72WWL0skwwlQ5Gdg8mxIgWUqbKDPwmFI8uMFxz2xRMQs0CbwGKH1UxpxO92Csuud7FlRlXrMv1gNrtzDMhxRXUc3kbZ0S0jULYAm7Av4EbMFBgsW6PrRQdEIe/nufnqE0AsrurXPUZORydJ3YxwmGRDbihWq9j96SaTBxB34t3QwLmDmX1szQuzL6QAixoL7kXfsbjwCbclXo=" + - secure: "HiUF2kZ/DphIURe671VrIRG6Nm8Q8szzgNTbLMQBFsbH7DneXTuAXHXqEVOE6xvac2PPPHc5KACfh3LDcJVZbzDCUGnMvy+yEYXBDOKi9wQYOiX5JwsYGdR/0N70oSevUm6pRxqp7VzsCyoasvatMLbBzm6XEq+Bu5SblAfOUuJaEiNYxqr6GMiX12DqkbWe814xi5ch8Z4Db4wFlVpz6jjfKVWiKAwenpFUzbl/pqhDx2RFkyWnpPQmSwaBIdkLkLrwEnTnVo4hKDqXboAq9pgDmGztC7dV4p+xwzGnHCNTJLxhYcRHFgr17/++0yP6HAIcx7h8jt67Kg6C8gtKYhz4ycSVMWKehov4FAwwCtO/GGlSNhAYlp/V1hexGaC+NuaMPQjA6imxc6nyzqefVByLIRNc2fa5px0ZHEtfckisMD+ioxTUZGcQcZYgB5V0143egk3Vui7KSdtwCYd99FbhEXNuDnLKhQKSqlMo2pyHYmg2NbMmUPd09UwJVEacQTwRS6JoOHDmyWxC9McDmQ2o8+jmyQ80TXl9bisJ+kUBSJf5cI4M10kl6uG6AtK15tRClPPC9sr+xvSymnx0cLPqUaabiHwN0S36AGcJPVms/0wIvci3l1UnkpzhP44LjUJ54Rliz3sFt5Xz2wiB0d8Cgh79s0dlYqEkQvaZL+4=" diff --git a/README.md b/README.md index 8ee2be0..eb02a8f 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,5 @@ # Crafter CLI + Commandline interface to CrafterCMS. Learn more at http://docs.craftercms.org. # Examples @@ -57,14 +58,19 @@ You can schedule content to be published: For a detailed list of commands & arguments run `crafter-cli help` # Community + ## Contributors + https://github.com/craftercms/craftercms/blob/develop/CONTRIBUTORS.md ## Code of Conduct + https://github.com/craftercms/craftercms/blob/develop/CODE_OF_CONDUCT.md ## Contributing + https://github.com/craftercms/craftercms/blob/develop/CONTRIBUTING.md ## Git Workflow + https://github.com/craftercms/craftercms/blob/develop/GIT_WORKFLOW.md diff --git a/distribution.xml b/distribution.xml index 7bd0cf2..d139bcd 100644 --- a/distribution.xml +++ b/distribution.xml @@ -17,24 +17,24 @@ --> - ${project.version} - - tar.gz - - ${project.build.directory} - false - - - ${project.build.directory}/${project.artifactId}/repo - repo - - maven-metadata-appassembler.xml - - - - ${project.build.directory}/${project.artifactId}/bin - bin - - + xsi:schemaLocation="http://maven.apache.org/ASSEMBLY/2.1.0 http://maven.apache.org/xsd/assembly-2.1.0.xsd"> + ${project.version} + + tar.gz + + ${project.build.directory} + false + + + ${project.build.directory}/${project.artifactId}/repo + repo + + maven-metadata-appassembler.xml + + + + ${project.build.directory}/${project.artifactId}/bin + bin + + diff --git a/pom.xml b/pom.xml index 5950844..3070c30 100644 --- a/pom.xml +++ b/pom.xml @@ -17,151 +17,167 @@ --> - 4.0.0 - cli - - - org.craftercms - craftercms - 5.0.0-SNAPSHOT - - - - - ${project.build.directory}/${project.artifactId} - - - - cli - - - - - org.apache.maven.plugins - maven-resources-plugin - - - - src/main/resources - true - - - - - - - - org.codehaus.gmavenplus - gmavenplus-plugin - - - - addSources - compile - - - - - true - - - - - - org.codehaus.mojo - appassembler-maven-plugin - - - package - - assemble - - - - - - - org.craftercms.cli.Main - crafter-cli - - - - - - - - org.apache.maven.plugins - maven-assembly-plugin - - - distribution.xml - - posix - - - - package - - single - - - - - - - - - - - - org.apache.groovy - groovy-all - pom - - - - - info.picocli - picocli - - - - - com.squareup.okhttp3 - okhttp - - - - - org.apache.commons - commons-csv - - - - - - sonatype-snapshots - Sonatype Snapshot Repository - https://s01.oss.sonatype.org/content/repositories/snapshots/ - - false - - - true - always - fail - - - - - - - sonatype-snapshots - https://s01.oss.sonatype.org/content/repositories/snapshots - - - sonatype-staging - https://s01.oss.sonatype.org/service/local/staging/deploy/maven2/ - - + 4.0.0 + cli + + + org.craftercms + craftercms + 5.0.0-SNAPSHOT + + + + + ${project.build.directory}/${project.artifactId} + + + + cli + + + org.apache.maven.plugins + maven-checkstyle-plugin + + https://raw.githubusercontent.com/craftercms/craftercms/refs/heads/develop/checkstyle.xml + + + + validate + validate + + check + + + + + + + + org.apache.maven.plugins + maven-resources-plugin + + + + src/main/resources + true + + + + + + + + org.codehaus.gmavenplus + gmavenplus-plugin + + + + addSources + compile + + + + + true + + + + + + org.codehaus.mojo + appassembler-maven-plugin + + + package + + assemble + + + + + + + org.craftercms.cli.Main + crafter-cli + + + + + + + + org.apache.maven.plugins + maven-assembly-plugin + + + distribution.xml + + posix + + + + package + + single + + + + + + + + + + + + org.apache.groovy + groovy-all + pom + + + + + info.picocli + picocli + + + + + com.squareup.okhttp3 + okhttp + + + + + org.apache.commons + commons-csv + + + + + + sonatype-snapshots + Sonatype Snapshot Repository + https://s01.oss.sonatype.org/content/repositories/snapshots/ + + false + + + true + always + fail + + + + + + + sonatype-snapshots + https://s01.oss.sonatype.org/content/repositories/snapshots + + + sonatype-staging + https://s01.oss.sonatype.org/service/local/staging/deploy/maven2/ + + diff --git a/src/main/groovy/org/craftercms/cli/Main.groovy b/src/main/groovy/org/craftercms/cli/Main.groovy index 8f4eb91..e5ae339 100644 --- a/src/main/groovy/org/craftercms/cli/Main.groovy +++ b/src/main/groovy/org/craftercms/cli/Main.groovy @@ -28,22 +28,22 @@ import org.craftercms.cli.commands.user.ListUsers import picocli.CommandLine @CommandLine.Command( - name = 'crafter-cli', usageHelpAutoWidth = true, - versionProvider = VersionProvider.class, mixinStandardHelpOptions = true, - subcommands = [CommandLine.HelpCommand, AddEnvironment, AddRemote, CreateSite, ListRemotes, SyncFrom, SyncTo, - ListSites, CopyPlugin, CreateUser, ListUsers, CreateAccessToken, PublishContent, CreateGroup, ListGroups, AddGroupMembers]) + name = 'crafter-cli', usageHelpAutoWidth = true, + versionProvider = VersionProvider.class, mixinStandardHelpOptions = true, + subcommands = [CommandLine.HelpCommand, AddEnvironment, AddRemote, CreateSite, ListRemotes, SyncFrom, SyncTo, + ListSites, CopyPlugin, CreateUser, ListUsers, CreateAccessToken, PublishContent, CreateGroup, ListGroups, AddGroupMembers]) class Main { - static def main(args) { - System.exit(new CommandLine(new Main()).execute(args)) - } + static def main(args) { + System.exit(new CommandLine(new Main()).execute(args)) + } - static class VersionProvider implements CommandLine.IVersionProvider { + static class VersionProvider implements CommandLine.IVersionProvider { - String[] getVersion() throws Exception { - return [getClass().getResource('version.txt').text] - } + String[] getVersion() throws Exception { + return [getClass().getResource('version.txt').text] + } - } + } } \ No newline at end of file diff --git a/src/main/groovy/org/craftercms/cli/commands/AbstractCommand.groovy b/src/main/groovy/org/craftercms/cli/commands/AbstractCommand.groovy index a83a9f9..68b0f89 100644 --- a/src/main/groovy/org/craftercms/cli/commands/AbstractCommand.groovy +++ b/src/main/groovy/org/craftercms/cli/commands/AbstractCommand.groovy @@ -24,54 +24,54 @@ import picocli.CommandLine.Model.CommandSpec abstract class AbstractCommand implements Runnable { - @CommandLine.Option(names = ['--config'], description = 'The folder to store configurations', - paramLabel = 'path') - File configFolder = new File(System.getProperty('user.home')) + @CommandLine.Option(names = ['--config'], description = 'The folder to store configurations', + paramLabel = 'path') + File configFolder = new File(System.getProperty('user.home')) - @CommandLine.Option(names = ['-p', '--profile'], description = 'The name of the profile') - String profile + @CommandLine.Option(names = ['-p', '--profile'], description = 'The name of the profile') + String profile - @CommandLine.Option(names = ['-e', '--environment'], required = true, description = 'The name of the environment') - String environment + @CommandLine.Option(names = ['-e', '--environment'], required = true, description = 'The name of the environment') + String environment - @CommandLine.Spec - CommandSpec commandSpec + @CommandLine.Spec + CommandSpec commandSpec - void run() { - additionalValidations() - try { - def config = loadConfig() - def client = HttpClient.getInstance(config) - run(client) - } catch (Exception e) { - println e.message - } - } + void run() { + additionalValidations() + try { + def config = loadConfig() + def client = HttpClient.getInstance(config) + run(client) + } catch (Exception e) { + println e.message + } + } - abstract def run(client) + abstract def run(client) - def additionalValidations() { + def additionalValidations() { - } + } - def saveConfig(config) { - def configFile = new File("$configFolder/.crafter${profile ? "/$profile" : ''}/${environment}") - if (configFile.exists()) { - throw new IllegalArgumentException('Environment already exists') - } - new FileTreeBuilder(configFolder) - .dir(".crafter${profile ? "/$profile" : ''}") { - file(environment, JsonOutput.toJson(config)) - } - } + def saveConfig(config) { + def configFile = new File("$configFolder/.crafter${profile ? "/$profile" : ''}/${environment}") + if (configFile.exists()) { + throw new IllegalArgumentException('Environment already exists') + } + new FileTreeBuilder(configFolder) + .dir(".crafter${profile ? "/$profile" : ''}") { + file(environment, JsonOutput.toJson(config)) + } + } - def loadConfig() { - def configFile = new File("$configFolder/.crafter${profile ? "/$profile" : ''}/${environment}") - if (!configFile.exists()) { - throw new IllegalArgumentException("Invalid profile or environment") - } - new JsonSlurper() - .parseText(configFile.text) - } + def loadConfig() { + def configFile = new File("$configFolder/.crafter${profile ? "/$profile" : ''}/${environment}") + if (!configFile.exists()) { + throw new IllegalArgumentException("Invalid profile or environment") + } + new JsonSlurper() + .parseText(configFile.text) + } } diff --git a/src/main/groovy/org/craftercms/cli/commands/AbstractSyncCommand.groovy b/src/main/groovy/org/craftercms/cli/commands/AbstractSyncCommand.groovy index d2f4ea5..ea525ec 100644 --- a/src/main/groovy/org/craftercms/cli/commands/AbstractSyncCommand.groovy +++ b/src/main/groovy/org/craftercms/cli/commands/AbstractSyncCommand.groovy @@ -22,14 +22,14 @@ import picocli.CommandLine abstract class AbstractSyncCommand extends AbstractCommand { - @CommandLine.Mixin - SiteOptions siteOptions + @CommandLine.Mixin + SiteOptions siteOptions - @CommandLine.Mixin - RemoteOptions remoteOptions + @CommandLine.Mixin + RemoteOptions remoteOptions - def additionalValidations() { - remoteOptions.validateAll() - } + def additionalValidations() { + remoteOptions.validateAll() + } } diff --git a/src/main/groovy/org/craftercms/cli/commands/AddEnvironment.groovy b/src/main/groovy/org/craftercms/cli/commands/AddEnvironment.groovy index 6c40868..ce4816b 100644 --- a/src/main/groovy/org/craftercms/cli/commands/AddEnvironment.groovy +++ b/src/main/groovy/org/craftercms/cli/commands/AddEnvironment.groovy @@ -24,107 +24,107 @@ import picocli.CommandLine @CommandLine.Command(name = 'add-environment', description = 'Adds the configuration to connect to CrafterCMS') class AddEnvironment extends AbstractCommand { - @CommandLine.Option(names = ['-u', '--url'], required = true, description = 'The URL of the server') - String url - - @CommandLine.ArgGroup(multiplicity = "1") - AuthOptions auth - - @CommandLine.Mixin - AccessTokenOptions tokenOptions - - @Override - void run() { - try { - def config = [:] - config.url = url - if (auth.accessToken) { - config.token = auth.accessToken - } - if (auth.basic?.username && auth.basic?.password) { - config.username = auth.basic?.username - config.password = auth.basic?.password - } - def client = HttpClient.getInstance(config) - - if (auth.basic?.generateToken) { - config.token = getTokenFromLogin(client) - client = HttpClient.resetClient(config) - config.token = CreateAccessToken.createAccessToken(client, - [label: tokenOptions.label ?: "JWT generated by CLI", expiresAt: tokenOptions.expiresAt], - true) - client = HttpClient.resetClient(config) - } - test(client) - saveConfig(config) - - println "Environment added" - } catch (Exception e) { - println e.message - } - } - - def test(client) { - try { - def path = '/studio/api/2/users/me.json' - def result = client.get(path) - def user = result.authenticatedUser - println "Authenticated as ${user.firstName} ${user.lastName} (${user.username})" - } catch (Exception e) { - throw new RuntimeException("Error authenticating user, please check your credentials", e) - } - } - - /** - * Get the JWT from API call to /studio/login - * and then /studio/refresh.json - * @param client - * @return the jwt from current session - */ - def getTokenFromLogin(client) { - def pathLogin = '/studio/login' - def pathRefresh = '/studio/refresh.json' - - def body = [ - username: auth.basic.username, - password: auth.basic.password, - _csrf: UUID.randomUUID().toString() - ] - - client.postForm(pathLogin, body) - def resultRefreshToken = client.get(pathRefresh) - if (resultRefreshToken) { - return resultRefreshToken.token - } - } - - def run(client) { - // All logic is in run because there is no info to build a client - } - - static class BasicAuthOptions { - - @CommandLine.Option(names = '--username', required = true, description = 'The username for authentication') - String username - - @CommandLine.Option(names = '--password', required = true, interactive = true, - description = 'The password for authentication') - String password - - @CommandLine.Option(names = '--generate-token', required = false, - description = 'Generate a JWT and use it for CLI authentication') - boolean generateToken - } - - static class AuthOptions { - - @CommandLine.ArgGroup(exclusive = false) - BasicAuthOptions basic - - @CommandLine.Option(names = '--token', required = false, interactive = true, - description = 'The access token for authentication') - String accessToken - - } + @CommandLine.Option(names = ['-u', '--url'], required = true, description = 'The URL of the server') + String url + + @CommandLine.ArgGroup(multiplicity = "1") + AuthOptions auth + + @CommandLine.Mixin + AccessTokenOptions tokenOptions + + @Override + void run() { + try { + def config = [:] + config.url = url + if (auth.accessToken) { + config.token = auth.accessToken + } + if (auth.basic?.username && auth.basic?.password) { + config.username = auth.basic?.username + config.password = auth.basic?.password + } + def client = HttpClient.getInstance(config) + + if (auth.basic?.generateToken) { + config.token = getTokenFromLogin(client) + client = HttpClient.resetClient(config) + config.token = CreateAccessToken.createAccessToken(client, + [label: tokenOptions.label ?: "JWT generated by CLI", expiresAt: tokenOptions.expiresAt], + true) + client = HttpClient.resetClient(config) + } + test(client) + saveConfig(config) + + println "Environment added" + } catch (Exception e) { + println e.message + } + } + + def test(client) { + try { + def path = '/studio/api/2/users/me.json' + def result = client.get(path) + def user = result.authenticatedUser + println "Authenticated as ${user.firstName} ${user.lastName} (${user.username})" + } catch (Exception e) { + throw new RuntimeException("Error authenticating user, please check your credentials", e) + } + } + + /** + * Get the JWT from API call to /studio/login + * and then /studio/refresh.json + * @param client + * @return the jwt from current session + */ + def getTokenFromLogin(client) { + def pathLogin = '/studio/login' + def pathRefresh = '/studio/refresh.json' + + def body = [ + username: auth.basic.username, + password: auth.basic.password, + _csrf : UUID.randomUUID().toString() + ] + + client.postForm(pathLogin, body) + def resultRefreshToken = client.get(pathRefresh) + if (resultRefreshToken) { + return resultRefreshToken.token + } + } + + def run(client) { + // All logic is in run because there is no info to build a client + } + + static class BasicAuthOptions { + + @CommandLine.Option(names = '--username', required = true, description = 'The username for authentication') + String username + + @CommandLine.Option(names = '--password', required = true, interactive = true, + description = 'The password for authentication') + String password + + @CommandLine.Option(names = '--generate-token', required = false, + description = 'Generate a JWT and use it for CLI authentication') + boolean generateToken + } + + static class AuthOptions { + + @CommandLine.ArgGroup(exclusive = false) + BasicAuthOptions basic + + @CommandLine.Option(names = '--token', required = false, interactive = true, + description = 'The access token for authentication') + String accessToken + + } } diff --git a/src/main/groovy/org/craftercms/cli/commands/group/AddGroupMembers.groovy b/src/main/groovy/org/craftercms/cli/commands/group/AddGroupMembers.groovy index 88f96f2..f55c08e 100644 --- a/src/main/groovy/org/craftercms/cli/commands/group/AddGroupMembers.groovy +++ b/src/main/groovy/org/craftercms/cli/commands/group/AddGroupMembers.groovy @@ -6,76 +6,76 @@ import picocli.CommandLine @CommandLine.Command(name = 'add-group-members', description = 'Adds one or more users to a group') class AddGroupMembers extends AbstractCommand { - @CommandLine.Spec - CommandLine.Model.CommandSpec commandSpec - - @CommandLine.Option(names = ['-gn', '--group-name'], required = true, description = 'Group name. Required if not bulk importing from a file.') - String groupName - - @CommandLine.Option(names = ['-u', '--users'], split = ',', required = true, description = 'Comma separated list of users to add') - String[] users - - def validateParameters() { - if (!groupName) { - throw new CommandLine.ParameterException(commandSpec.commandLine(), 'Missing required option group-name') - } - - if (!users) { - throw new CommandLine.ParameterException(commandSpec.commandLine(), 'Missing required option users') - } - } - - @Override - def run(client) { - validateParameters() - addMembers(client, groupName, users) - } - - /** - * Add one or more users to a group - * @param client HTTPClient object - * @param groupName group name - * @param users list of users to add - */ - static def addMembers(client, groupName, users) { - def response = getGroupIdByName(client, groupName) - - if (!response || !response.group) { - println "Error retrieving group '${groupName}'" - return - } - - def groupId = response.group.id - def path = "/studio/api/2/groups/${groupId}/members" - def body = [usernames: users] - - def result = client.post(path, body) - if (!result) { - println "Failed to add members '${users}' to group '${groupName}'" - return - } - - println "Members '${users}' added to group '${groupName}' succesfully" - } - - /** - * Get a group by name - * @param client HTTPClient object - * @param groupName group name - */ - static def getGroupIdByName(client, groupName) { - def path = "/studio/api/2/groups/by_name/${groupName}" - def result = client.get(path) - if (!result) { - println "Failed to retrieve group '${groupName}'" - return - } - - if (!result.group) { - println "Failed to retrieve group '${groupName}', error '${result.message}'" - return - } - - return result - } + @CommandLine.Spec + CommandLine.Model.CommandSpec commandSpec + + @CommandLine.Option(names = ['-gn', '--group-name'], required = true, description = 'Group name. Required if not bulk importing from a file.') + String groupName + + @CommandLine.Option(names = ['-u', '--users'], split = ',', required = true, description = 'Comma separated list of users to add') + String[] users + + def validateParameters() { + if (!groupName) { + throw new CommandLine.ParameterException(commandSpec.commandLine(), 'Missing required option group-name') + } + + if (!users) { + throw new CommandLine.ParameterException(commandSpec.commandLine(), 'Missing required option users') + } + } + + @Override + def run(client) { + validateParameters() + addMembers(client, groupName, users) + } + + /** + * Add one or more users to a group + * @param client HTTPClient object + * @param groupName group name + * @param users list of users to add + */ + static def addMembers(client, groupName, users) { + def response = getGroupIdByName(client, groupName) + + if (!response || !response.group) { + println "Error retrieving group '${groupName}'" + return + } + + def groupId = response.group.id + def path = "/studio/api/2/groups/${groupId}/members" + def body = [usernames: users] + + def result = client.post(path, body) + if (!result) { + println "Failed to add members '${users}' to group '${groupName}'" + return + } + + println "Members '${users}' added to group '${groupName}' succesfully" + } + + /** + * Get a group by name + * @param client HTTPClient object + * @param groupName group name + */ + static def getGroupIdByName(client, groupName) { + def path = "/studio/api/2/groups/by_name/${groupName}" + def result = client.get(path) + if (!result) { + println "Failed to retrieve group '${groupName}'" + return + } + + if (!result.group) { + println "Failed to retrieve group '${groupName}', error '${result.message}'" + return + } + + return result + } } \ No newline at end of file diff --git a/src/main/groovy/org/craftercms/cli/commands/group/CreateGroup.groovy b/src/main/groovy/org/craftercms/cli/commands/group/CreateGroup.groovy index fddbb23..c30417f 100644 --- a/src/main/groovy/org/craftercms/cli/commands/group/CreateGroup.groovy +++ b/src/main/groovy/org/craftercms/cli/commands/group/CreateGroup.groovy @@ -26,56 +26,56 @@ import java.nio.file.Paths @CommandLine.Command(name = 'create-group', description = 'Creates a group from command parameters or bulk create groups from a CSV file') class CreateGroup extends AbstractCommand { - @CommandLine.Mixin - GroupOptions groupOptions + @CommandLine.Mixin + GroupOptions groupOptions - @CommandLine.Option(names = ['-gi', '--group-import-file'], - description = 'Path of Group CSV file to import. The file rows should be: name, description.') - String groupFile + @CommandLine.Option(names = ['-gi', '--group-import-file'], + description = 'Path of Group CSV file to import. The file rows should be: name, description.') + String groupFile - @Override - def run(client) { - if (groupFile != null && !groupFile.isEmpty()) { - Paths.get(groupFile).withReader { reader -> - CSVFormat format = CSVFormat.DEFAULT.builder() - .setHeader() - .build() - CSVParser csv = new CSVParser(reader, format) - for (record in csv.iterator()) { - createGroup(client, record.toMap()) - } - } - } else if (!hasValidGroupOptions(groupOptions)) { - throw new CommandLine.ParameterException(commandSpec.commandLine(), 'Missing required options to create group.') - } else { - createGroup(client, groupOptions) - } - } + @Override + def run(client) { + if (groupFile != null && !groupFile.isEmpty()) { + Paths.get(groupFile).withReader { reader -> + CSVFormat format = CSVFormat.DEFAULT.builder() + .setHeader() + .build() + CSVParser csv = new CSVParser(reader, format) + for (record in csv.iterator()) { + createGroup(client, record.toMap()) + } + } + } else if (!hasValidGroupOptions(groupOptions)) { + throw new CommandLine.ParameterException(commandSpec.commandLine(), 'Missing required options to create group.') + } else { + createGroup(client, groupOptions) + } + } - /** - * Create a new group - * @param client HTTPClient object - * @param options create group options - */ - def createGroup(client, options) { - def params = [ - name: options.name, - desc: options.desc - ] + /** + * Create a new group + * @param client HTTPClient object + * @param options create group options + */ + def createGroup(client, options) { + def params = [ + name: options.name, + desc: options.desc + ] - def path = '/studio/api/2/groups' - def result = client.post(path, params) - if (result) { - println "${result.response.message}. Group: ${options.name}" - } - } + def path = '/studio/api/2/groups' + def result = client.post(path, params) + if (result) { + println "${result.response.message}. Group: ${options.name}" + } + } - /** - * Check if the options has required fields - * @param options command options - * @return true if valid, false otherwise - */ - def hasValidGroupOptions(options) { - return options && options.name - } + /** + * Check if the options has required fields + * @param options command options + * @return true if valid, false otherwise + */ + def hasValidGroupOptions(options) { + return options && options.name + } } \ No newline at end of file diff --git a/src/main/groovy/org/craftercms/cli/commands/group/ListGroups.groovy b/src/main/groovy/org/craftercms/cli/commands/group/ListGroups.groovy index d8cdf13..ea6f177 100644 --- a/src/main/groovy/org/craftercms/cli/commands/group/ListGroups.groovy +++ b/src/main/groovy/org/craftercms/cli/commands/group/ListGroups.groovy @@ -25,72 +25,72 @@ import picocli.CommandLine @CommandLine.Command(name = 'list-groups', description = 'List all the groups or search for groups by keyword or sort by a field.') class ListGroups extends AbstractCommand { - @CommandLine.Mixin - FilterAndPaginateOptions filterAndPaginateOptions + @CommandLine.Mixin + FilterAndPaginateOptions filterAndPaginateOptions - @Override - def run(Object client) { - def path = '/studio/api/2/groups' - def queryParams = [:] - if (filterAndPaginateOptions.keyword) { - queryParams.keyword = filterAndPaginateOptions.keyword - } - if (filterAndPaginateOptions.sort) { - queryParams.sort = filterAndPaginateOptions.sort - } - queryParams.offset = (filterAndPaginateOptions.offset ?: 0).toString() - queryParams.limit = (filterAndPaginateOptions.limit ?: 10).toString() - def result = client.get(path, queryParams) - if (!result) { - return - } + @Override + def run(Object client) { + def path = '/studio/api/2/groups' + def queryParams = [:] + if (filterAndPaginateOptions.keyword) { + queryParams.keyword = filterAndPaginateOptions.keyword + } + if (filterAndPaginateOptions.sort) { + queryParams.sort = filterAndPaginateOptions.sort + } + queryParams.offset = (filterAndPaginateOptions.offset ?: 0).toString() + queryParams.limit = (filterAndPaginateOptions.limit ?: 10).toString() + def result = client.get(path, queryParams) + if (!result) { + return + } - if (!result.groups) { - println "There are no groups" - return - } + if (!result.groups) { + println "There are no groups" + return + } - println "Total : ${result.total}" - println "Offset: ${result.offset}" - println "Limit : ${result.limit}" + println "Total : ${result.total}" + println "Offset: ${result.offset}" + println "Limit : ${result.limit}" - writeResult(result.groups) - } + writeResult(result.groups) + } - /** - * Get writer to output to file or standard output stream - * @return the writer - */ - def getWriter() { - if (filterAndPaginateOptions.output) { - def csvFile = new File(filterAndPaginateOptions.output) - return csvFile.newWriter() - } + /** + * Get writer to output to file or standard output stream + * @return the writer + */ + def getWriter() { + if (filterAndPaginateOptions.output) { + def csvFile = new File(filterAndPaginateOptions.output) + return csvFile.newWriter() + } - return new StringWriter() - } + return new StringWriter() + } - /** - * Write groups list to CSV format with a writer - * @param groups list of groups - */ - def writeResult(groups) { - def writer = getWriter() - def csvPrinter = new CSVPrinter(writer, CSVFormat.DEFAULT) - csvPrinter.printRecord(['Id', 'Externally Managed', 'Name', 'Description']) + /** + * Write groups list to CSV format with a writer + * @param groups list of groups + */ + def writeResult(groups) { + def writer = getWriter() + def csvPrinter = new CSVPrinter(writer, CSVFormat.DEFAULT) + csvPrinter.printRecord(['Id', 'Externally Managed', 'Name', 'Description']) - groups.each { - csvPrinter.printRecord([it.id, it.externallyManaged, it.name, it.desc]) - } + groups.each { + csvPrinter.printRecord([it.id, it.externallyManaged, it.name, it.desc]) + } - if (filterAndPaginateOptions.output) { - println "Saved to file ${filterAndPaginateOptions.output}." - } else { - println '----------' - println writer.toString() - } + if (filterAndPaginateOptions.output) { + println "Saved to file ${filterAndPaginateOptions.output}." + } else { + println '----------' + println writer.toString() + } - csvPrinter.close() - writer.close() - } + csvPrinter.close() + writer.close() + } } \ No newline at end of file diff --git a/src/main/groovy/org/craftercms/cli/commands/marketplace/CopyPlugin.groovy b/src/main/groovy/org/craftercms/cli/commands/marketplace/CopyPlugin.groovy index 6cbd080..9caff08 100644 --- a/src/main/groovy/org/craftercms/cli/commands/marketplace/CopyPlugin.groovy +++ b/src/main/groovy/org/craftercms/cli/commands/marketplace/CopyPlugin.groovy @@ -23,31 +23,31 @@ import picocli.CommandLine @CommandLine.Command(name = 'copy-plugin', description = 'Copies a plugin from a Studio local folder into a project') class CopyPlugin extends AbstractCommand { - @CommandLine.Mixin - SiteOptions siteOptions - - @CommandLine.Option(names = '--path', required = true, - description = 'The plugin source path (must be a local folder to Crafter Studio)') - String path - - @CommandLine.Option(names = '--param', description = 'Additional parameter for the plugin') - Map parameters - - def run(client) { - def body = [ - siteId : siteOptions.siteId, - path : path - ] - if (parameters) { - body.parameters = parameters - } - - def path = '/studio/api/2/marketplace/copy' - def result = client.post(path, body) - if (result) { - println "Copy plugin response" - println result.response.message - } - } + @CommandLine.Mixin + SiteOptions siteOptions + + @CommandLine.Option(names = '--path', required = true, + description = 'The plugin source path (must be a local folder to Crafter Studio)') + String path + + @CommandLine.Option(names = '--param', description = 'Additional parameter for the plugin') + Map parameters + + def run(client) { + def body = [ + siteId: siteOptions.siteId, + path : path + ] + if (parameters) { + body.parameters = parameters + } + + def path = '/studio/api/2/marketplace/copy' + def result = client.post(path, body) + if (result) { + println "Copy plugin response" + println result.response.message + } + } } diff --git a/src/main/groovy/org/craftercms/cli/commands/security/CreateAccessToken.groovy b/src/main/groovy/org/craftercms/cli/commands/security/CreateAccessToken.groovy index 1978387..4b48457 100644 --- a/src/main/groovy/org/craftercms/cli/commands/security/CreateAccessToken.groovy +++ b/src/main/groovy/org/craftercms/cli/commands/security/CreateAccessToken.groovy @@ -22,37 +22,37 @@ import picocli.CommandLine @CommandLine.Command(name = 'create-access-token', description = 'Creates an access token for the user') class CreateAccessToken extends AbstractCommand { - @CommandLine.Mixin - AccessTokenOptions tokenOptions - - @Override - def run(client) { - createAccessToken(client, tokenOptions) - } - - static def createAccessToken(client, options) { - createAccessToken(client, options, false) - } - - static def createAccessToken(client, options, returnToken) { - def params = [ - label: options.label, - expiresAt: options.expiresAt - ] - def path = '/studio/api/2/security/tokens' - def result = client.post(path, params) - - if (result) { - if (returnToken) { - return result.token.token - } - println "${result.response.message}. Token: ${result.token.token}" - } - } - - def additionalValidations() { - if (tokenOptions) { - tokenOptions.validate() - } - } + @CommandLine.Mixin + AccessTokenOptions tokenOptions + + @Override + def run(client) { + createAccessToken(client, tokenOptions) + } + + static def createAccessToken(client, options) { + createAccessToken(client, options, false) + } + + static def createAccessToken(client, options, returnToken) { + def params = [ + label : options.label, + expiresAt: options.expiresAt + ] + def path = '/studio/api/2/security/tokens' + def result = client.post(path, params) + + if (result) { + if (returnToken) { + return result.token.token + } + println "${result.response.message}. Token: ${result.token.token}" + } + } + + def additionalValidations() { + if (tokenOptions) { + tokenOptions.validate() + } + } } diff --git a/src/main/groovy/org/craftercms/cli/commands/site/AddRemote.groovy b/src/main/groovy/org/craftercms/cli/commands/site/AddRemote.groovy index bacf60b..7eb77c5 100644 --- a/src/main/groovy/org/craftercms/cli/commands/site/AddRemote.groovy +++ b/src/main/groovy/org/craftercms/cli/commands/site/AddRemote.groovy @@ -24,44 +24,44 @@ import picocli.CommandLine @CommandLine.Command(name = 'add-remote', description = 'Adds a remote repository to a project') class AddRemote extends AbstractCommand { - @CommandLine.Mixin - SiteOptions siteOptions + @CommandLine.Mixin + SiteOptions siteOptions - @CommandLine.Mixin - AuthOptions authAware + @CommandLine.Mixin + AuthOptions authAware - @CommandLine.Option(names = ['-n', '--name'], required = true, description = 'The name of the remote repository') - String remoteName + @CommandLine.Option(names = ['-n', '--name'], required = true, description = 'The name of the remote repository') + String remoteName - @CommandLine.Option(names = ['-u', '--url'], required = true, description = 'The url of the remote repository') - String remoteUrl + @CommandLine.Option(names = ['-u', '--url'], required = true, description = 'The url of the remote repository') + String remoteUrl - def run(client) { - def params = [ - siteId : siteOptions.siteId, - remoteName : remoteName, - remoteUrl : remoteUrl, - authenticationType: authAware.authType - ] - if (authAware.username) { - params.remoteUsername = authAware.username - } - if (authAware.password) { - params.remotePassword = authAware.password - } - if (authAware.token) { - params.remoteToken = authAware.token - } - if (authAware.privateKey) { - // trim to avoid issues with empty lines - params.remotePrivateKey = authAware.privateKey.text.trim() - } + def run(client) { + def params = [ + siteId : siteOptions.siteId, + remoteName : remoteName, + remoteUrl : remoteUrl, + authenticationType: authAware.authType + ] + if (authAware.username) { + params.remoteUsername = authAware.username + } + if (authAware.password) { + params.remotePassword = authAware.password + } + if (authAware.token) { + params.remoteToken = authAware.token + } + if (authAware.privateKey) { + // trim to avoid issues with empty lines + params.remotePrivateKey = authAware.privateKey.text.trim() + } - def path = '/studio/api/2/repository/add_remote.json' - def result = client.post(path, params) - if (result) { - println result.response.message - } - } + def path = '/studio/api/2/repository/add_remote.json' + def result = client.post(path, params) + if (result) { + println result.response.message + } + } } diff --git a/src/main/groovy/org/craftercms/cli/commands/site/CreateSite.groovy b/src/main/groovy/org/craftercms/cli/commands/site/CreateSite.groovy index 009ce38..5fc8413 100644 --- a/src/main/groovy/org/craftercms/cli/commands/site/CreateSite.groovy +++ b/src/main/groovy/org/craftercms/cli/commands/site/CreateSite.groovy @@ -25,96 +25,96 @@ import picocli.CommandLine @CommandLine.Command(name = 'create-site', description = 'Creates a project from a blueprint or a remote repository') class CreateSite extends AbstractCommand { - @CommandLine.Mixin - SiteOptions siteOptions - - @CommandLine.Mixin - AuthOptions authAware - - @CommandLine.Mixin - GitOptions gitOptions - - @CommandLine.Option(names = ['-d', '--description'], description = 'The description for the project') - String description - - @CommandLine.Option(names = '--blueprint', description = 'The id of the project blueprint') - String blueprint - - @CommandLine.Option(names = ['-o', '--orphan'], description = 'Discards the history from the remote repository') - boolean orphan - - @CommandLine.Option(names = '--singleBranch', description = 'Fetch only the given branch from the remote repository') - boolean singleBranch - - @CommandLine.Option(names = '--sandboxBranch', description = 'The name of the branch for the local repository') - String sandboxBranch - - @CommandLine.Option(names = ['--siteParam'], description = "Parameter for the blueprint") - Map siteParams - - def additionalValidations() { - if (gitOptions) { - gitOptions.validCombination() - } - if (!gitOptions || gitOptions.createOption == 'push' && !blueprint) { - throw new CommandLine.ParameterException(commandSpec.commandLine(), 'Missing required option blueprint') - } - } - - def run(client) { - def params = [ - site_id : siteOptions.siteId, - name : siteOptions.siteId, // TODO: Add support for site name in 3.2 - authentication_type: authAware.authType, - create_as_orphan : orphan, - single_branch : singleBranch, - create_option : gitOptions.createOption - ] - if (gitOptions) { - params.create_option = gitOptions.createOption - params.use_remote = gitOptions.remote - - if (gitOptions.remoteName) { - params.remote_name = gitOptions.remoteName - } - if (gitOptions.url) { - params.remote_url = gitOptions.url - } - if (gitOptions.remoteBranch) { - params.remote_branch = gitOptions.remoteBranch - } - if (authAware.username) { - params.remote_username = authAware.username - } - if (authAware.password) { - params.remote_password = authAware.password - } - if (authAware.token) { - params.remote_token = authAware.token - } - if (authAware.privateKey) { - // trim to avoid issues with empty lines - params.remote_private_key = authAware.privateKey.text.trim() - } - } - if (blueprint) { - params.blueprint = blueprint - } - if (sandboxBranch) { - params.sandbox_branch = sandboxBranch - } - if (description) { - params.description = description - } - if (siteParams) { - params.site_params = siteParams - } - - def path = '/studio/api/1/services/api/1/site/create.json' - def result = client.post(path, params) - if (result) { - println result.message - } - } + @CommandLine.Mixin + SiteOptions siteOptions + + @CommandLine.Mixin + AuthOptions authAware + + @CommandLine.Mixin + GitOptions gitOptions + + @CommandLine.Option(names = ['-d', '--description'], description = 'The description for the project') + String description + + @CommandLine.Option(names = '--blueprint', description = 'The id of the project blueprint') + String blueprint + + @CommandLine.Option(names = ['-o', '--orphan'], description = 'Discards the history from the remote repository') + boolean orphan + + @CommandLine.Option(names = '--singleBranch', description = 'Fetch only the given branch from the remote repository') + boolean singleBranch + + @CommandLine.Option(names = '--sandboxBranch', description = 'The name of the branch for the local repository') + String sandboxBranch + + @CommandLine.Option(names = ['--siteParam'], description = "Parameter for the blueprint") + Map siteParams + + def additionalValidations() { + if (gitOptions) { + gitOptions.validCombination() + } + if (!gitOptions || gitOptions.createOption == 'push' && !blueprint) { + throw new CommandLine.ParameterException(commandSpec.commandLine(), 'Missing required option blueprint') + } + } + + def run(client) { + def params = [ + site_id : siteOptions.siteId, + name : siteOptions.siteId, // TODO: Add support for site name in 3.2 + authentication_type: authAware.authType, + create_as_orphan : orphan, + single_branch : singleBranch, + create_option : gitOptions.createOption + ] + if (gitOptions) { + params.create_option = gitOptions.createOption + params.use_remote = gitOptions.remote + + if (gitOptions.remoteName) { + params.remote_name = gitOptions.remoteName + } + if (gitOptions.url) { + params.remote_url = gitOptions.url + } + if (gitOptions.remoteBranch) { + params.remote_branch = gitOptions.remoteBranch + } + if (authAware.username) { + params.remote_username = authAware.username + } + if (authAware.password) { + params.remote_password = authAware.password + } + if (authAware.token) { + params.remote_token = authAware.token + } + if (authAware.privateKey) { + // trim to avoid issues with empty lines + params.remote_private_key = authAware.privateKey.text.trim() + } + } + if (blueprint) { + params.blueprint = blueprint + } + if (sandboxBranch) { + params.sandbox_branch = sandboxBranch + } + if (description) { + params.description = description + } + if (siteParams) { + params.site_params = siteParams + } + + def path = '/studio/api/1/services/api/1/site/create.json' + def result = client.post(path, params) + if (result) { + println result.message + } + } } diff --git a/src/main/groovy/org/craftercms/cli/commands/site/ListRemotes.groovy b/src/main/groovy/org/craftercms/cli/commands/site/ListRemotes.groovy index 7dc5b63..c0564ba 100644 --- a/src/main/groovy/org/craftercms/cli/commands/site/ListRemotes.groovy +++ b/src/main/groovy/org/craftercms/cli/commands/site/ListRemotes.groovy @@ -23,27 +23,27 @@ import picocli.CommandLine @CommandLine.Command(name = 'list-remotes', description = 'List the remote repositories of a project') class ListRemotes extends AbstractCommand { - @CommandLine.Mixin - SiteOptions siteOptions + @CommandLine.Mixin + SiteOptions siteOptions - def run(client) { - def path = '/studio/api/2/repository/list_remotes.json' - def query = [siteId: siteOptions.siteId] - def result = client.get(path, query) - if (!result) { - return - } + def run(client) { + def path = '/studio/api/2/repository/list_remotes.json' + def query = [siteId: siteOptions.siteId] + def result = client.get(path, query) + if (!result) { + return + } - if (result.remotes) { - result.remotes.each { - println " ${it.name} (${it.url})" - it.branches.each { - println " - ${it}" - } - } - } else { - println "There are no remote repositories" - } - } + if (result.remotes) { + result.remotes.each { + println " ${it.name} (${it.url})" + it.branches.each { + println " - ${it}" + } + } + } else { + println "There are no remote repositories" + } + } } diff --git a/src/main/groovy/org/craftercms/cli/commands/site/ListSites.groovy b/src/main/groovy/org/craftercms/cli/commands/site/ListSites.groovy index 9fdcf04..99431a4 100644 --- a/src/main/groovy/org/craftercms/cli/commands/site/ListSites.groovy +++ b/src/main/groovy/org/craftercms/cli/commands/site/ListSites.groovy @@ -22,20 +22,20 @@ import picocli.CommandLine @CommandLine.Command(name = 'list-sites', description = 'List the projects that the current user can access') class ListSites extends AbstractCommand { - def run(client) { - def path = '/studio/api/2/users/me/sites.json' - def result = client.get(path) - if (!result) { - return - } + def run(client) { + def path = '/studio/api/2/users/me/sites.json' + def result = client.get(path) + if (!result) { + return + } - if (result.sites) { - result.sites.each { - println " ${it.name} (${it.siteId})" - } - } else { - println "There are no projects" - } - } + if (result.sites) { + result.sites.each { + println " ${it.name} (${it.siteId})" + } + } else { + println "There are no projects" + } + } } diff --git a/src/main/groovy/org/craftercms/cli/commands/site/PublishContent.groovy b/src/main/groovy/org/craftercms/cli/commands/site/PublishContent.groovy index 7d141d5..90b5256 100644 --- a/src/main/groovy/org/craftercms/cli/commands/site/PublishContent.groovy +++ b/src/main/groovy/org/craftercms/cli/commands/site/PublishContent.groovy @@ -24,60 +24,60 @@ import picocli.CommandLine @CommandLine.Command(name = 'publish-content', description = 'Publish content from a project or site.') class PublishContent extends AbstractCommand { - @CommandLine.Mixin - SiteOptions siteOptions + @CommandLine.Mixin + SiteOptions siteOptions - @CommandLine.Option(names = ['--items'], description = 'The items to publish') - String items + @CommandLine.Option(names = ['--items'], description = 'The items to publish') + String items - @CommandLine.Option(names = ['--optionalDependencies'], description = 'The optional dependencies') - String optionalDependencies + @CommandLine.Option(names = ['--optionalDependencies'], description = 'The optional dependencies') + String optionalDependencies - @CommandLine.Option(names = ['--publishingTarget'], description = 'The publishing target (live, staging)') - String publishingTarget + @CommandLine.Option(names = ['--publishingTarget'], description = 'The publishing target (live, staging)') + String publishingTarget - @CommandLine.Option(names = ['--schedule'], description = 'The schedule to publish.') - String schedule + @CommandLine.Option(names = ['--schedule'], description = 'The schedule to publish.') + String schedule - @CommandLine.Option(names = ['--comment'], description = 'The comment to add') - String comment + @CommandLine.Option(names = ['--comment'], description = 'The comment to add') + String comment - def additionalValidations() { - if (!items) { - throw new CommandLine.ParameterException(commandSpec.commandLine(), 'Missing required option items') - } - if (!publishingTarget) { - throw new CommandLine.ParameterException(commandSpec.commandLine(), 'Missing required option publishingTarget') - } - if (publishingTarget != 'live' && publishingTarget != 'staging') { - throw new CommandLine.ParameterException(commandSpec.commandLine(), 'Invalid publishing target. Use live or staging') - } - } + def additionalValidations() { + if (!items) { + throw new CommandLine.ParameterException(commandSpec.commandLine(), 'Missing required option items') + } + if (!publishingTarget) { + throw new CommandLine.ParameterException(commandSpec.commandLine(), 'Missing required option publishingTarget') + } + if (publishingTarget != 'live' && publishingTarget != 'staging') { + throw new CommandLine.ParameterException(commandSpec.commandLine(), 'Invalid publishing target. Use live or staging') + } + } - def run(client) { - // Tokenize items and optionalDependencies - def items = this.items.tokenize(',') - def optionalDependencies = this.optionalDependencies ? this.optionalDependencies.tokenize(',') : null + def run(client) { + // Tokenize items and optionalDependencies + def items = this.items.tokenize(',') + def optionalDependencies = this.optionalDependencies ? this.optionalDependencies.tokenize(',') : null - def path = '/studio/api/2/workflow/publish.json' - def query = [ - siteId : siteOptions.siteId, - items : items, - optionalDependencies: optionalDependencies, - publishingTarget : publishingTarget, - schedule : schedule, - comment : comment - ] - def result = client.post(path, query) - if (!result) { - return - } - if (schedule) { - println(result.response.message) - println "The selected content has been submitted to be published to ${publishingTarget} at ${schedule}" - } else { - println(result.response.message) - println "The selected content has been submitted to be published to ${publishingTarget}" - } - } + def path = '/studio/api/2/workflow/publish.json' + def query = [ + siteId : siteOptions.siteId, + items : items, + optionalDependencies: optionalDependencies, + publishingTarget : publishingTarget, + schedule : schedule, + comment : comment + ] + def result = client.post(path, query) + if (!result) { + return + } + if (schedule) { + println(result.response.message) + println "The selected content has been submitted to be published to ${publishingTarget} at ${schedule}" + } else { + println(result.response.message) + println "The selected content has been submitted to be published to ${publishingTarget}" + } + } } diff --git a/src/main/groovy/org/craftercms/cli/commands/site/SyncFrom.groovy b/src/main/groovy/org/craftercms/cli/commands/site/SyncFrom.groovy index a6b2e0d..aa92cc0 100644 --- a/src/main/groovy/org/craftercms/cli/commands/site/SyncFrom.groovy +++ b/src/main/groovy/org/craftercms/cli/commands/site/SyncFrom.groovy @@ -22,24 +22,24 @@ import picocli.CommandLine @CommandLine.Command(name = 'sync-from', description = 'Sync the content of a project from a remote repository') class SyncFrom extends AbstractSyncCommand { - @CommandLine.Option(names = ['-m', '--mergeStrategy'], description = 'The merge strategy to use', paramLabel = 'none|ours|theirs') - String mergeStrategy + @CommandLine.Option(names = ['-m', '--mergeStrategy'], description = 'The merge strategy to use', paramLabel = 'none|ours|theirs') + String mergeStrategy - def run(client) { - def params = [ - siteId : siteOptions.siteId, - remoteName : remoteOptions.remoteName, - remoteBranch: remoteOptions.remoteBranch - ] - if (mergeStrategy) { - params.mergeStrategy = mergeStrategy - } + def run(client) { + def params = [ + siteId : siteOptions.siteId, + remoteName : remoteOptions.remoteName, + remoteBranch: remoteOptions.remoteBranch + ] + if (mergeStrategy) { + params.mergeStrategy = mergeStrategy + } - def path = '/studio/api/2/repository/pull_from_remote.json' - def result = client.post(path, params) - if (result) { - println result.response.message - } - } + def path = '/studio/api/2/repository/pull_from_remote.json' + def result = client.post(path, params) + if (result) { + println result.response.message + } + } } diff --git a/src/main/groovy/org/craftercms/cli/commands/site/SyncTo.groovy b/src/main/groovy/org/craftercms/cli/commands/site/SyncTo.groovy index 425a3d5..27a81db 100644 --- a/src/main/groovy/org/craftercms/cli/commands/site/SyncTo.groovy +++ b/src/main/groovy/org/craftercms/cli/commands/site/SyncTo.groovy @@ -22,24 +22,24 @@ import picocli.CommandLine @CommandLine.Command(name = 'sync-to', description = 'Sync the content of a project to a remote repository') class SyncTo extends AbstractSyncCommand { - @CommandLine.Option(names = ['-f', '--force'], description = 'Forces the update of the remote repository') - boolean force + @CommandLine.Option(names = ['-f', '--force'], description = 'Forces the update of the remote repository') + boolean force - def run(client) { - def params = [ - siteId : siteOptions.siteId, - remoteName : remoteOptions.remoteName, - remoteBranch: remoteOptions.remoteBranch - ] - if (force) { - params.force = force - } + def run(client) { + def params = [ + siteId : siteOptions.siteId, + remoteName : remoteOptions.remoteName, + remoteBranch: remoteOptions.remoteBranch + ] + if (force) { + params.force = force + } - def path = '/studio/api/2/repository/push_to_remote.json' - def result = client.post(path, params) - if (result) { - println result.response.message - } - } + def path = '/studio/api/2/repository/push_to_remote.json' + def result = client.post(path, params) + if (result) { + println result.response.message + } + } } diff --git a/src/main/groovy/org/craftercms/cli/commands/user/CreateUser.groovy b/src/main/groovy/org/craftercms/cli/commands/user/CreateUser.groovy index d342f61..efa7989 100644 --- a/src/main/groovy/org/craftercms/cli/commands/user/CreateUser.groovy +++ b/src/main/groovy/org/craftercms/cli/commands/user/CreateUser.groovy @@ -26,61 +26,61 @@ import java.nio.file.Paths @CommandLine.Command(name = 'create-user', description = 'Creates a user from command parameters or bulk create users from a CSV file') class CreateUser extends AbstractCommand { - @CommandLine.Mixin - UserOptions userOptions + @CommandLine.Mixin + UserOptions userOptions - @CommandLine.Option(names = ['-bf', '--bulk-file'], - description = 'Absolute path of the CSV file to bulk create users. CSV file should have the header: username,password,firstName,lastName,email,enabled') - String bulkFile + @CommandLine.Option(names = ['-bf', '--bulk-file'], + description = 'Absolute path of the CSV file to bulk create users. CSV file should have the header: username,password,firstName,lastName,email,enabled') + String bulkFile - @Override - def run(client) { - if (bulkFile) { - Paths.get(bulkFile).withReader { reader -> - CSVFormat format = CSVFormat.DEFAULT.builder() - .setHeader() - .build() - CSVParser csv = new CSVParser(reader, format) - for (record in csv.iterator()) { - createUser(client, record.toMap()) - } - } - } else if (!hasValidUserOptions(userOptions)) { - throw new CommandLine.ParameterException(commandSpec.commandLine(), 'Missing required options to create user.') - } else { - createUser(client, userOptions) - } - } + @Override + def run(client) { + if (bulkFile) { + Paths.get(bulkFile).withReader { reader -> + CSVFormat format = CSVFormat.DEFAULT.builder() + .setHeader() + .build() + CSVParser csv = new CSVParser(reader, format) + for (record in csv.iterator()) { + createUser(client, record.toMap()) + } + } + } else if (!hasValidUserOptions(userOptions)) { + throw new CommandLine.ParameterException(commandSpec.commandLine(), 'Missing required options to create user.') + } else { + createUser(client, userOptions) + } + } - /** - * Create a new user - * @param client HTTPClient object - * @param options create user options - */ - def createUser(client, options) { - def params = [ - username: options.username, - password: options.password, - firstName: options.firstName, - lastName: options.lastName, - email: options.email, - enabled: options.enabled ?: true - ] + /** + * Create a new user + * @param client HTTPClient object + * @param options create user options + */ + def createUser(client, options) { + def params = [ + username : options.username, + password : options.password, + firstName: options.firstName, + lastName : options.lastName, + email : options.email, + enabled : options.enabled ?: true + ] - def path = '/studio/api/2/users' - def result = client.post(path, params) - if (result) { - println "${result.response.message}. User: ${options.username}" - } - } + def path = '/studio/api/2/users' + def result = client.post(path, params) + if (result) { + println "${result.response.message}. User: ${options.username}" + } + } - /** - * Check if the options has required fields - * @param options command options - * @return true if valid, false otherwise - */ - def hasValidUserOptions(options) { - return options && options.username && options.password - && options.email && options.firstName && options.lastName - } + /** + * Check if the options has required fields + * @param options command options + * @return true if valid, false otherwise + */ + def hasValidUserOptions(options) { + return options && options.username && options.password + && options.email && options.firstName && options.lastName + } } diff --git a/src/main/groovy/org/craftercms/cli/commands/user/ListUsers.groovy b/src/main/groovy/org/craftercms/cli/commands/user/ListUsers.groovy index 2eccb39..402ab3b 100644 --- a/src/main/groovy/org/craftercms/cli/commands/user/ListUsers.groovy +++ b/src/main/groovy/org/craftercms/cli/commands/user/ListUsers.groovy @@ -24,91 +24,91 @@ import org.apache.commons.csv.CSVPrinter @CommandLine.Command(name = 'list-users', description = 'Get all Studio users') class ListUsers extends AbstractCommand { - @CommandLine.Option(names = ['-si', '--site-id'], description = 'The project/site ID to filter users for a particular project/site') - String siteId - - @CommandLine.Option(names = ['-k', '--keyword'], description = 'The keyword to filter users') - String keyword - - @CommandLine.Option(names = ['-os', '--offset'], description = 'Offset of first record in the response. Default is 0.') - String offset - - @CommandLine.Option(names = ['-l', '--limit'], description = 'Number of records to return. Default is 10.') - String limit - - @CommandLine.Option(names = ['-s', '--sort'], - description = 'The fields to use for sorting, plus the asc or desc keyword case-insensitive. Multiple fields are separated by commas. Example: column1 ASC, column2 DESC.') - String sort - - @CommandLine.Option(names = ['-o', '--output'], description = 'CSV output file to output the users list') - String output - - @Override - def run(Object client) { - def path = '/studio/api/2/users' - def queryParams = [:] - if (siteId) { - queryParams.siteId = siteId - } - if (keyword) { - queryParams.keyword = keyword - } - if (sort) { - queryParams.sort = sort - } - queryParams.offset = (offset ?: 0).toString() - queryParams.limit = (limit ?: 10).toString() - def result = client.get(path, queryParams) - if (!result) { - return - } - - if (!result.users) { - println "There are no users" - return - } - - println "Total : ${result.total}" - println "Offset: ${result.offset}" - println "Limit : ${result.limit}" - - writeResult(result.users) - } - - /** - * Get writer to output to file or standard output stream - * @return the writer - */ - def getWriter() { - if (output) { - def csvFile = new File(output) - return csvFile.newWriter() - } - - return new StringWriter() - } - - /** - * Write users list to CSV format with a writer - * @param users list of users - */ - def writeResult(users) { - def writer = getWriter() - def csvPrinter = new CSVPrinter(writer, CSVFormat.DEFAULT) - csvPrinter.printRecord(['Id', 'Username', 'First Name', 'Last Name', 'Email', 'Enabled', 'Externally Managed']) - - users.each { - csvPrinter.printRecord([it.id, it.username, it.firstName, it.lastName, it.email, it.enabled, it.externallyManaged]) - } - - if (output) { - println "Saved to file ${output}." - } else { - println '----------' - println writer.toString() - } - - csvPrinter.close() - writer.close() - } + @CommandLine.Option(names = ['-si', '--site-id'], description = 'The project/site ID to filter users for a particular project/site') + String siteId + + @CommandLine.Option(names = ['-k', '--keyword'], description = 'The keyword to filter users') + String keyword + + @CommandLine.Option(names = ['-os', '--offset'], description = 'Offset of first record in the response. Default is 0.') + String offset + + @CommandLine.Option(names = ['-l', '--limit'], description = 'Number of records to return. Default is 10.') + String limit + + @CommandLine.Option(names = ['-s', '--sort'], + description = 'The fields to use for sorting, plus the asc or desc keyword case-insensitive. Multiple fields are separated by commas. Example: column1 ASC, column2 DESC.') + String sort + + @CommandLine.Option(names = ['-o', '--output'], description = 'CSV output file to output the users list') + String output + + @Override + def run(Object client) { + def path = '/studio/api/2/users' + def queryParams = [:] + if (siteId) { + queryParams.siteId = siteId + } + if (keyword) { + queryParams.keyword = keyword + } + if (sort) { + queryParams.sort = sort + } + queryParams.offset = (offset ?: 0).toString() + queryParams.limit = (limit ?: 10).toString() + def result = client.get(path, queryParams) + if (!result) { + return + } + + if (!result.users) { + println "There are no users" + return + } + + println "Total : ${result.total}" + println "Offset: ${result.offset}" + println "Limit : ${result.limit}" + + writeResult(result.users) + } + + /** + * Get writer to output to file or standard output stream + * @return the writer + */ + def getWriter() { + if (output) { + def csvFile = new File(output) + return csvFile.newWriter() + } + + return new StringWriter() + } + + /** + * Write users list to CSV format with a writer + * @param users list of users + */ + def writeResult(users) { + def writer = getWriter() + def csvPrinter = new CSVPrinter(writer, CSVFormat.DEFAULT) + csvPrinter.printRecord(['Id', 'Username', 'First Name', 'Last Name', 'Email', 'Enabled', 'Externally Managed']) + + users.each { + csvPrinter.printRecord([it.id, it.username, it.firstName, it.lastName, it.email, it.enabled, it.externallyManaged]) + } + + if (output) { + println "Saved to file ${output}." + } else { + println '----------' + println writer.toString() + } + + csvPrinter.close() + writer.close() + } } diff --git a/src/main/groovy/org/craftercms/cli/options/AccessTokenOptions.groovy b/src/main/groovy/org/craftercms/cli/options/AccessTokenOptions.groovy index 26b47c5..0e3fbf0 100644 --- a/src/main/groovy/org/craftercms/cli/options/AccessTokenOptions.groovy +++ b/src/main/groovy/org/craftercms/cli/options/AccessTokenOptions.groovy @@ -20,18 +20,18 @@ import picocli.CommandLine class AccessTokenOptions { - @CommandLine.Option(names = ['-la', '--label'], description = 'The label to set for the token') - String label + @CommandLine.Option(names = ['-la', '--label'], description = 'The label to set for the token') + String label - @CommandLine.Option(names = ['-et', '--expires-at'], description = 'The expiration date for the token in the format YYYY-MM-DDTHH:MM:SSZ (UTC)') - String expiresAt + @CommandLine.Option(names = ['-et', '--expires-at'], description = 'The expiration date for the token in the format YYYY-MM-DDTHH:MM:SSZ (UTC)') + String expiresAt - @CommandLine.Spec - CommandLine.Model.CommandSpec commandSpec + @CommandLine.Spec + CommandLine.Model.CommandSpec commandSpec - def validate() { - if (!label) { - throw new CommandLine.ParameterException(commandSpec.commandLine(), 'Missing required option label') - } - } + def validate() { + if (!label) { + throw new CommandLine.ParameterException(commandSpec.commandLine(), 'Missing required option label') + } + } } diff --git a/src/main/groovy/org/craftercms/cli/options/AuthOptions.groovy b/src/main/groovy/org/craftercms/cli/options/AuthOptions.groovy index 32cd2e7..f5e3a08 100644 --- a/src/main/groovy/org/craftercms/cli/options/AuthOptions.groovy +++ b/src/main/groovy/org/craftercms/cli/options/AuthOptions.groovy @@ -20,58 +20,58 @@ import picocli.CommandLine class AuthOptions { - @CommandLine.ArgGroup(exclusive = false) - Auth auth + @CommandLine.ArgGroup(exclusive = false) + Auth auth - static class Credentials { + static class Credentials { - @CommandLine.Option(names = '--password', description = 'The password for authentication') - String password + @CommandLine.Option(names = '--password', description = 'The password for authentication') + String password - @CommandLine.Option(names = '--token', description = 'The token for authentication') - String token + @CommandLine.Option(names = '--token', description = 'The token for authentication') + String token - @CommandLine.Option(names = '--key', description = 'The path of the private key for authentication') - File privateKey + @CommandLine.Option(names = '--key', description = 'The path of the private key for authentication') + File privateKey - } + } - static class Auth { + static class Auth { - @CommandLine.Option(names = ['--username'], description = 'The username for authentication') - String username + @CommandLine.Option(names = ['--username'], description = 'The username for authentication') + String username - @CommandLine.ArgGroup(multiplicity = '1') - Credentials credentials + @CommandLine.ArgGroup(multiplicity = '1') + Credentials credentials - } + } - def getAuthType() { - if (!auth) { - return 'none' - } else if (auth?.credentials?.password) { - return 'basic' - } else if (auth?.credentials?.token) { - return 'token' - } else { - return 'key' - } - } + def getAuthType() { + if (!auth) { + return 'none' + } else if (auth?.credentials?.password) { + return 'basic' + } else if (auth?.credentials?.token) { + return 'token' + } else { + return 'key' + } + } - def getUsername() { - auth?.username - } + def getUsername() { + auth?.username + } - def getPassword() { - auth?.credentials?.password - } + def getPassword() { + auth?.credentials?.password + } - def getToken() { - auth?.credentials?.token - } + def getToken() { + auth?.credentials?.token + } - def getPrivateKey() { - auth?.credentials?.privateKey - } + def getPrivateKey() { + auth?.credentials?.privateKey + } } diff --git a/src/main/groovy/org/craftercms/cli/options/FilterAndPaginateOptions.groovy b/src/main/groovy/org/craftercms/cli/options/FilterAndPaginateOptions.groovy index 3af15a2..031a76a 100644 --- a/src/main/groovy/org/craftercms/cli/options/FilterAndPaginateOptions.groovy +++ b/src/main/groovy/org/craftercms/cli/options/FilterAndPaginateOptions.groovy @@ -20,19 +20,19 @@ import picocli.CommandLine class FilterAndPaginateOptions { - @CommandLine.Option(names = ['-k', '--keyword'], description = 'A keyword to filter by') - String keyword + @CommandLine.Option(names = ['-k', '--keyword'], description = 'A keyword to filter by') + String keyword - @CommandLine.Option(names = ['-os', '--offset'], description = 'Offset of first record in the response. Default is 0.') - String offset + @CommandLine.Option(names = ['-os', '--offset'], description = 'Offset of first record in the response. Default is 0.') + String offset - @CommandLine.Option(names = ['-l', '--limit'], description = 'Number of records to return. Default is 10.') - String limit + @CommandLine.Option(names = ['-l', '--limit'], description = 'Number of records to return. Default is 10.') + String limit - @CommandLine.Option(names = ['-s', '--sort'], - description = 'The fields to use for sorting, plus the asc or desc keyword case-insensitive. Multiple fields are separated by commas. Example: column1 ASC, column2 DESC.') - String sort + @CommandLine.Option(names = ['-s', '--sort'], + description = 'The fields to use for sorting, plus the asc or desc keyword case-insensitive. Multiple fields are separated by commas. Example: column1 ASC, column2 DESC.') + String sort - @CommandLine.Option(names = ['-o', '--output'], description = 'Output file') - String output + @CommandLine.Option(names = ['-o', '--output'], description = 'Output file') + String output } \ No newline at end of file diff --git a/src/main/groovy/org/craftercms/cli/options/GitOptions.groovy b/src/main/groovy/org/craftercms/cli/options/GitOptions.groovy index ef2394d..ca283d9 100644 --- a/src/main/groovy/org/craftercms/cli/options/GitOptions.groovy +++ b/src/main/groovy/org/craftercms/cli/options/GitOptions.groovy @@ -21,61 +21,61 @@ import picocli.CommandLine.Model.CommandSpec class GitOptions { - @CommandLine.Mixin - RemoteOptions remoteOptions - - @CommandLine.ArgGroup - CreateOptions createOptions - - @CommandLine.Option(names = ['-r', '--remote'], description = 'Enable the options for using a remote repository') - boolean remote - - @CommandLine.Option(names = ['-u', '--url'], description = 'The URL of the remote repository') - String url - - @CommandLine.Spec - CommandSpec commandSpec - - def validCombination() { - if (remote && !url) { - throw new CommandLine.ParameterException(commandSpec.commandLine(), 'Missing required option url') - } - } - - def getCreateOption() { - if (!createOptions) { - return 'none' - } else if (createOptions.clone) { - return 'clone' - } else { - return 'push' - } - } - - def getRemoteName() { - remoteOptions.remoteName - } - - def getRemoteBranch() { - remoteOptions.remoteBranch - } - - static class CreateOptions { - - @CommandLine.Option(names = '--clone', description = 'Create a project cloning a remote repository') - boolean clone - - @CommandLine.Option(names = '--push', description = 'Create a project and push to a remote repository') - boolean push - - def getCreateOption() { - if (clone) { - return 'clone' - } else { - return 'push' - } - } - - } + @CommandLine.Mixin + RemoteOptions remoteOptions + + @CommandLine.ArgGroup + CreateOptions createOptions + + @CommandLine.Option(names = ['-r', '--remote'], description = 'Enable the options for using a remote repository') + boolean remote + + @CommandLine.Option(names = ['-u', '--url'], description = 'The URL of the remote repository') + String url + + @CommandLine.Spec + CommandSpec commandSpec + + def validCombination() { + if (remote && !url) { + throw new CommandLine.ParameterException(commandSpec.commandLine(), 'Missing required option url') + } + } + + def getCreateOption() { + if (!createOptions) { + return 'none' + } else if (createOptions.clone) { + return 'clone' + } else { + return 'push' + } + } + + def getRemoteName() { + remoteOptions.remoteName + } + + def getRemoteBranch() { + remoteOptions.remoteBranch + } + + static class CreateOptions { + + @CommandLine.Option(names = '--clone', description = 'Create a project cloning a remote repository') + boolean clone + + @CommandLine.Option(names = '--push', description = 'Create a project and push to a remote repository') + boolean push + + def getCreateOption() { + if (clone) { + return 'clone' + } else { + return 'push' + } + } + + } } diff --git a/src/main/groovy/org/craftercms/cli/options/GroupOptions.groovy b/src/main/groovy/org/craftercms/cli/options/GroupOptions.groovy index 580f9cc..f1bc82b 100644 --- a/src/main/groovy/org/craftercms/cli/options/GroupOptions.groovy +++ b/src/main/groovy/org/craftercms/cli/options/GroupOptions.groovy @@ -20,9 +20,9 @@ import picocli.CommandLine class GroupOptions { - @CommandLine.Option(names = ['-gn', '--group-name'], description = 'Group name. Required if not bulk importing from a file.') - String name + @CommandLine.Option(names = ['-gn', '--group-name'], description = 'Group name. Required if not bulk importing from a file.') + String name - @CommandLine.Option(names = ['-gd', '--group-desc'], description = 'Group description.') - String desc + @CommandLine.Option(names = ['-gd', '--group-desc'], description = 'Group description.') + String desc } \ No newline at end of file diff --git a/src/main/groovy/org/craftercms/cli/options/RemoteOptions.groovy b/src/main/groovy/org/craftercms/cli/options/RemoteOptions.groovy index c15728f..fabbfb3 100644 --- a/src/main/groovy/org/craftercms/cli/options/RemoteOptions.groovy +++ b/src/main/groovy/org/craftercms/cli/options/RemoteOptions.groovy @@ -21,22 +21,22 @@ import picocli.CommandLine.Model.CommandSpec class RemoteOptions { - @CommandLine.Option(names = ['-n', '--name'], description = 'The name of the remote') - String remoteName - - @CommandLine.Option(names = ['-b', '--branch'], description = 'The name of the remote branch') - String remoteBranch - - @CommandLine.Spec - CommandSpec commandSpec - - def validateAll() { - if (!remoteName) { - throw new CommandLine.ParameterException(commandSpec.commandLine(), "Missing required option remoteName") - } - if (!remoteBranch) { - throw new CommandLine.ParameterException(commandSpec.commandLine(), "Missing required option remoteBranch") - } - } + @CommandLine.Option(names = ['-n', '--name'], description = 'The name of the remote') + String remoteName + + @CommandLine.Option(names = ['-b', '--branch'], description = 'The name of the remote branch') + String remoteBranch + + @CommandLine.Spec + CommandSpec commandSpec + + def validateAll() { + if (!remoteName) { + throw new CommandLine.ParameterException(commandSpec.commandLine(), "Missing required option remoteName") + } + if (!remoteBranch) { + throw new CommandLine.ParameterException(commandSpec.commandLine(), "Missing required option remoteBranch") + } + } } diff --git a/src/main/groovy/org/craftercms/cli/options/SiteOptions.groovy b/src/main/groovy/org/craftercms/cli/options/SiteOptions.groovy index 9b9fcb7..718d3ea 100644 --- a/src/main/groovy/org/craftercms/cli/options/SiteOptions.groovy +++ b/src/main/groovy/org/craftercms/cli/options/SiteOptions.groovy @@ -20,7 +20,7 @@ import picocli.CommandLine class SiteOptions { - @CommandLine.Option(names = ['-s', '--siteId'], required = true, description = 'The id of the project') - String siteId + @CommandLine.Option(names = ['-s', '--siteId'], required = true, description = 'The id of the project') + String siteId } diff --git a/src/main/groovy/org/craftercms/cli/options/UserOptions.groovy b/src/main/groovy/org/craftercms/cli/options/UserOptions.groovy index 8bee23c..1dd2f57 100644 --- a/src/main/groovy/org/craftercms/cli/options/UserOptions.groovy +++ b/src/main/groovy/org/craftercms/cli/options/UserOptions.groovy @@ -20,21 +20,21 @@ import picocli.CommandLine class UserOptions { - @CommandLine.Option(names = ['-un', '--username'], description = 'Username. Required if --bulk-file is not specified.') - String username + @CommandLine.Option(names = ['-un', '--username'], description = 'Username. Required if --bulk-file is not specified.') + String username - @CommandLine.Option(names = ['-pw', '--password'], description = 'User\'s password. Required if --bulk-file is not specified.') - String password + @CommandLine.Option(names = ['-pw', '--password'], description = 'User\'s password. Required if --bulk-file is not specified.') + String password - @CommandLine.Option(names = ['-fn', '--firstName'], description = 'User\'s first name. Required if --bulk-file is not specified.') - String firstName + @CommandLine.Option(names = ['-fn', '--firstName'], description = 'User\'s first name. Required if --bulk-file is not specified.') + String firstName - @CommandLine.Option(names = ['-ln', '--lastName'], description = 'User\'s last name. Required if --bulk-file is not specified.') - String lastName + @CommandLine.Option(names = ['-ln', '--lastName'], description = 'User\'s last name. Required if --bulk-file is not specified.') + String lastName - @CommandLine.Option(names = ['-em', '--email'], description = 'User\'s email address. Required if --bulk-file is not specified.') - String email + @CommandLine.Option(names = ['-em', '--email'], description = 'User\'s email address. Required if --bulk-file is not specified.') + String email - @CommandLine.Option(names = ['-en', '--enabled'], description = 'Indicates if the user is enabled. Default is true.') - String enabled + @CommandLine.Option(names = ['-en', '--enabled'], description = 'Indicates if the user is enabled. Default is true.') + String enabled } diff --git a/src/main/groovy/org/craftercms/cli/utils/BasicCookieJar.groovy b/src/main/groovy/org/craftercms/cli/utils/BasicCookieJar.groovy index 4ecab0c..134c4f8 100644 --- a/src/main/groovy/org/craftercms/cli/utils/BasicCookieJar.groovy +++ b/src/main/groovy/org/craftercms/cli/utils/BasicCookieJar.groovy @@ -20,15 +20,15 @@ import okhttp3.CookieJar import okhttp3.HttpUrl class BasicCookieJar implements CookieJar { - List cookies = [] + List cookies = [] - @Override - void saveFromResponse(HttpUrl url, List cookies) { - this.cookies.addAll(cookies) - } + @Override + void saveFromResponse(HttpUrl url, List cookies) { + this.cookies.addAll(cookies) + } - @Override - List loadForRequest(HttpUrl url) { - return cookies - } + @Override + List loadForRequest(HttpUrl url) { + return cookies + } } diff --git a/src/main/groovy/org/craftercms/cli/utils/HttpClient.groovy b/src/main/groovy/org/craftercms/cli/utils/HttpClient.groovy index 0394738..9cc4a9f 100644 --- a/src/main/groovy/org/craftercms/cli/utils/HttpClient.groovy +++ b/src/main/groovy/org/craftercms/cli/utils/HttpClient.groovy @@ -21,128 +21,128 @@ import groovy.json.JsonBuilder import groovy.json.JsonSlurper class HttpClient { - private static HttpClient instance - private final String baseUri - private final String authorizationHeader - private final OkHttpClient client - private final BasicCookieJar cookieJar - - static final String AUTH_HEADER = 'Authorization' - - static HttpClient getInstance(config) { - if (instance == null) { - instance = new HttpClient(config) - } - return instance - } - - static HttpClient resetClient(config) { - instance = null - return getInstance(config) - } - - HttpClient(config) { - this.baseUri = config.url - this.cookieJar = new BasicCookieJar() - this.client = new OkHttpClient().newBuilder().cookieJar(cookieJar).build() - if (config.token) { - authorizationHeader = "Bearer ${config.token}" - } else { - authorizationHeader = "Basic ${"${config.username}:${config.password}".bytes.encodeBase64()}" - } - } - - /** - * HTTP GET method - * @param path API path - * @param queryParams URI query parameters - * @return response - */ - def get(String path, Map queryParams = null) { - HttpUrl.Builder httpBuilder = HttpUrl.parse(baseUri + path).newBuilder() - if (queryParams != null) { - queryParams.each { k, v -> httpBuilder.addQueryParameter(k, v) } - } - Request request = new Request.Builder() - .url(httpBuilder.build()) - .addHeader(AUTH_HEADER, authorizationHeader) - .build() - - Response response = client.newCall(request).execute() - if (!response.successful) { - displayResponseDetail(response) - return null - } - - return new JsonSlurper().parseText(response.body().string()) - } - - /** - * HTTP POST method - * @param path API path - * @param data post data - * @return response - */ - def post(String path, Map data) { - RequestBody body = RequestBody.create(new JsonBuilder(data).toString(), MediaType.parse('application/json; charset=utf-8')) - Request request = new Request.Builder() - .url(baseUri + path) - .post(body) - .addHeader(AUTH_HEADER, authorizationHeader) - .build() - - Response response = client.newCall(request).execute() - if (!response.successful) { - displayResponseDetail(response) - return null - } - - return new JsonSlurper().parseText(response.body().string()) - } - - /** - * HTTP POST method with form parameters - * @param path API path - * @param formParams form parameters - */ - def postForm(String path, Map formParams) { - FormBody.Builder formBuilder = new FormBody.Builder() - formParams.each { k, v -> formBuilder.add(k, v) } - RequestBody formBody = formBuilder.build() - Request request = new Request.Builder() - .url(baseUri + path) - .post(formBody) - .addHeader("Cookie", "XSRF-TOKEN=${formParams["_csrf"]}") - .build() - - Response response = client.newCall(request).execute() - if (!response.successful) { - displayResponseDetail(response) - } - } - - /** - * Display HTTP response object detail - * @param response HTTP response object - */ - private void displayResponseDetail(Response response) { - println "Status Code: ${response.code()}" - def body = new JsonSlurper().parseText(response.body().string()) - - // API 1 format - if (body.message) { - println "Error Message: ${body.message}" - return - } - - // API 2 format - println "Error Code: ${body.response.code}" - println "Error Message: ${body.response.message}" - if (body.response.remedialAction) { - println "Error Action: ${body.response.remedialAction}" - } - if (body.response.documentationUrl) { - println "Error Documentation: ${body.response.documentationUrl}" - } - } + private static HttpClient instance + private final String baseUri + private final String authorizationHeader + private final OkHttpClient client + private final BasicCookieJar cookieJar + + static final String AUTH_HEADER = 'Authorization' + + static HttpClient getInstance(config) { + if (instance == null) { + instance = new HttpClient(config) + } + return instance + } + + static HttpClient resetClient(config) { + instance = null + return getInstance(config) + } + + HttpClient(config) { + this.baseUri = config.url + this.cookieJar = new BasicCookieJar() + this.client = new OkHttpClient().newBuilder().cookieJar(cookieJar).build() + if (config.token) { + authorizationHeader = "Bearer ${config.token}" + } else { + authorizationHeader = "Basic ${"${config.username}:${config.password}".bytes.encodeBase64()}" + } + } + + /** + * HTTP GET method + * @param path API path + * @param queryParams URI query parameters + * @return response + */ + def get(String path, Map queryParams = null) { + HttpUrl.Builder httpBuilder = HttpUrl.parse(baseUri + path).newBuilder() + if (queryParams != null) { + queryParams.each { k, v -> httpBuilder.addQueryParameter(k, v) } + } + Request request = new Request.Builder() + .url(httpBuilder.build()) + .addHeader(AUTH_HEADER, authorizationHeader) + .build() + + Response response = client.newCall(request).execute() + if (!response.successful) { + displayResponseDetail(response) + return null + } + + return new JsonSlurper().parseText(response.body().string()) + } + + /** + * HTTP POST method + * @param path API path + * @param data post data + * @return response + */ + def post(String path, Map data) { + RequestBody body = RequestBody.create(new JsonBuilder(data).toString(), MediaType.parse('application/json; charset=utf-8')) + Request request = new Request.Builder() + .url(baseUri + path) + .post(body) + .addHeader(AUTH_HEADER, authorizationHeader) + .build() + + Response response = client.newCall(request).execute() + if (!response.successful) { + displayResponseDetail(response) + return null + } + + return new JsonSlurper().parseText(response.body().string()) + } + + /** + * HTTP POST method with form parameters + * @param path API path + * @param formParams form parameters + */ + def postForm(String path, Map formParams) { + FormBody.Builder formBuilder = new FormBody.Builder() + formParams.each { k, v -> formBuilder.add(k, v) } + RequestBody formBody = formBuilder.build() + Request request = new Request.Builder() + .url(baseUri + path) + .post(formBody) + .addHeader("Cookie", "XSRF-TOKEN=${formParams["_csrf"]}") + .build() + + Response response = client.newCall(request).execute() + if (!response.successful) { + displayResponseDetail(response) + } + } + + /** + * Display HTTP response object detail + * @param response HTTP response object + */ + private void displayResponseDetail(Response response) { + println "Status Code: ${response.code()}" + def body = new JsonSlurper().parseText(response.body().string()) + + // API 1 format + if (body.message) { + println "Error Message: ${body.message}" + return + } + + // API 2 format + println "Error Code: ${body.response.code}" + println "Error Message: ${body.response.message}" + if (body.response.remedialAction) { + println "Error Action: ${body.response.remedialAction}" + } + if (body.response.documentationUrl) { + println "Error Documentation: ${body.response.documentationUrl}" + } + } } \ No newline at end of file