diff --git a/.gitignore b/.gitignore index 6f7067af..ccd53fb8 100644 --- a/.gitignore +++ b/.gitignore @@ -4,6 +4,10 @@ .classpath .project .settings -*.iml */bin/ test-output/ +.zanata-cache/ + +# idea +*.idea/ +*.iml diff --git a/settings.xml b/.travis-settings.xml similarity index 100% rename from settings.xml rename to .travis-settings.xml diff --git a/.travis.yml b/.travis.yml index 737d6d7f..adf214d4 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,6 +1,17 @@ language: java + +# Use the 'true' command to avoid up-front dependency fetching, for faster builds +# See http://docs.travis-ci.com/user/languages/java/#Dependency-Management install: true -script: mvn --batch-mode --settings settings.xml test -Dgwt.validateOnly -Darquillian.jboss.home=/dev/null + +script: | + mvn test \ + --batch-mode \ + --settings .travis-settings.xml \ + -Darquillian.jboss.home=/dev/null \ + -Dgwt.validateOnly \ + -DstaticAnalysis + jdk: - openjdk7 - oraclejdk7 @@ -8,5 +19,3 @@ jdk: matrix: fast_finish: true -# allow_failures: -# - jdk: oraclejdk8 diff --git a/docs/command-hook.md b/docs/command-hook.md new file mode 100644 index 00000000..eaaea465 --- /dev/null +++ b/docs/command-hook.md @@ -0,0 +1,38 @@ +Custom commands can be specified in zanata.xml. Custom commands can be almost any command that can run on the command line, and can be configured to run before or after a Zanata client command. This feature was added in version 3.3.0 of the CLI client and the Maven Plugin, and cannot be used in earlier versions. + +To specify one or more custom commands: + + 1. Add a `` element in zanata.xml within the `` element. + 1. For each Zanata command that will have custom commands attached, add a `` element that specifies the command as an attribute. + 1. For each custom command to run before a Zanata command, add a `` element with the command as its body. + 1. For each custom command to run after a Zanata command, add an `` element with the command as its body. + +For example, the following elements within the top-level element will generate a .pot file from a man page before push, clean up the generated file after push, and will generate a translated German man page after pull then remove all downloaded .po files. + +```xml + + + +... + + + + po4a-gettextize -f man -m manpage.1 -p manpage.pot + rm -f manpage.pot + + + po4a-translate -f man -m manpage.1 -p trans/de/manpage.po -l manpage.de.1 --keep 1 + rm -rf trans + + + +... + + +``` + +Multiple commands of the same type (i.e. "before" or "after") within a hook will be run in the order that they are specified in zanata.xml. e.g. when running pull with the above config, po4a will always run before rm. If any of these commands fails, the whole operation is aborted. e.g. when running push, if po4a fails then push and rm will not be run, and if push fails then rm will not be run. + +Note that some commands (such as 'cd') do not work as custom commands. The ranges of commands that work and that do not work as custom commands have not yet been thoroughly investigated. + +The return value of each custom command is displayed after the command is run. A return value of 0 usually indicates success, and any other number usually indicates an error. Console output from custom commands is not yet displayed or logged. \ No newline at end of file diff --git a/docs/commands/init.md b/docs/commands/init.md new file mode 100644 index 00000000..a017aa93 --- /dev/null +++ b/docs/commands/init.md @@ -0,0 +1,45 @@ +To initialise a project from the command line, the `init` command can be used. This command will guide you through the steps necessary to set up a new or existing project, and start using Zanata. + +These instructions assume that you have installed zanata-cli version 3.4.0 or higher as shown in [Installing the Client](installation). + + + +## Getting started + +The first thing to do is to type the following command in the console: `zanata-cli init` + +The client will proceed to ask which of your preferred Zanata servers it needs to use to register your new project (this information is taken from your `zanata.ini` file; if the desired server does not appear in the list, it should be added to this file). + +```bash +[INFO] Loading project config from zanata.xml +[INFO] Loading user config from /home/camunoz/.config/zanata.ini + Found servers in zanata.ini: + 1) http://localhost:8080/zanata/ + 2) https://translate.jboss.org/ + 3) https://translate.zanata.org/zanata/ +[?] Which Zanata server do you want to use? +``` + +Select the Zanata server to use by typing in the number and hiting ENTER. The client will now ask whether you want to create a new project, or use an existing one from your instance: + +```bash +[?] Do you want to 1) select an existing project or 2) create a new one (1/2)? +``` + +According to your selection the client will ask for information about your new project and proceed to create it, or it will give you an option to select an existing one from the server. + +```bash +[?] What project ID/slug do you want to have (must start and end with a letter or number, and contain only letters, numbers, underscores and hyphens): +$ my-project-id +[?] What project name do you want to have: +$ My Project Name +[?] What is your project type ([utf8properties, properties, gettext, podir, xliff, xml, file])? +$ podir +[>] Project created. Now it's time to create a version to host your files. + +[?] What version ID/slug do you want to have: +$ master +[>] Version created. +``` + +the Zanata client will continue to ask questions and provide information on the next steps necessary to get you started with your project on Zanata. \ No newline at end of file diff --git a/docs/commands/pull.md b/docs/commands/pull.md new file mode 100644 index 00000000..2d086676 --- /dev/null +++ b/docs/commands/pull.md @@ -0,0 +1,41 @@ +To download documents from your project-version, the command-line client's `pull` command can be used. + +These instructions assume that you have installed zanata-cli as shown in [Installing the Client](installation), and have saved user and project configuration as shown in [Configuring the Client][configuration]. + + +## Translation Document Download + +The basic command for downloading documents is `zanata-cli pull`. The pull command should always be run from the directory that contains `zanata.xml` for your project (find information about `zanata.xml` at [Configuring the Client][configuration]). + +At the time of writing, you will have to specify source and translation directories with the command, even though the default pull command will pull only translated documents from the server. This will be fixed in a future version. This means that the simplest pull command is: + +```bash +zanata-cli pull -s src -t trans +``` + + +This command will: + + 1. look up the locales to pull from `zanata.xml` (all the enabled locales by default). + 1. display the current settings and list of locales that will be downloaded. + 1. confirm that you want to proceed with the download. + 1. download translated versions of any documents that have any translations. + +Documents with no translations will not be downloaded unless specifically requested by adding the `--create-skeletons` option. + +To download only a few locales, use the `-l` or `--locales` option. For example, to download only Japanese and Russian translations, I might run `zanata-cli pull -s src -t trans -l ja,ru`. You can also modify the locales in `zanata.xml` if you will be consistently specifying a different set of locales. + +For a full list of the available options for pull, run `zanata-cli help pull` + + +## Source Document Download + +The pull command can also download source documents. This is generally only for reference purposes since source documents cannot be changed on the server. + +To pull translations instead of source documents, add the option `--pull-type source`, like so: + +``` +zanata-cli pull --pull-type source -s src -t source +``` + +To pull source and translation documents together, use `--pull-type both`. diff --git a/docs/commands/push.md b/docs/commands/push.md new file mode 100644 index 00000000..b05d6280 --- /dev/null +++ b/docs/commands/push.md @@ -0,0 +1,60 @@ +To upload documents to your project-version, the command-line client's `push` command can be used. + +These instructions assume that you have installed zanata-cli as shown in [Installing the Client](installation), and have saved user and project configuration as shown in [Configuring the Client][configuration]. + + + +## Source Document Upload + +The basic command for uploading documents is `zanata-cli push`. The push command should always be run from the directory that contains `zanata.xml` for your project (find information about `zanata.xml` at [Configuring the Client][configuration.md]). + +At the time of writing, you will have to specify source and translation directories with the command, even though the default push command will push only source documents to the server. This will be fixed in a future version. This means that the simplest push command is: + +`zanata-cli push -s src -t trans` + +This command will: + + 1. search for source documents in a directory named `src` and any of its subdirectories. + 1. display the current settings and list of source documents that were found. + 1. confirm that you want to proceed with the upload. + 1. upload the located source documents to the server. + +*Note:* if your source files are in the same directory as non-source files that have the same extension, such as when using Java `.properties` files for both translation and configuration, the `--includes` or `--excludes` option should be used to tell Zanata which files it should push. + +For a full list of the available options for push, run `zanata-cli help push` + + +## Translation Document Upload + +The push command can also upload translations. This is mainly for use when translation has started on your project before it has moved to Zanata. Translators can also use this command to upload translations, although translation on the Zanata website is safer. + +To push translations instead of source documents, add the option `--push-type trans`, like so: + +```bash +zanata-cli push --push-type trans -s src -t trans +``` + +To push source and translation documents together, use `--push-type both`. + +These commands will upload all available translations for the locales specified in `zanata.xml`, unless the `-l` or `--locales` option is used to specify a smaller set of locales. For example: `-l ja,de`. + +*Note:* translation documents must be named appropriately to match a source document. The appropriate name depends on your project type. For example, if the above command is used for a Java Properties project with a source document `src/strings/messages.properties`, a Japanese translation for the document would be at `trans/strings/messages_ja.properties`. + +```bash +src + strings + messages.properties +trans + strings + messages_ja.properties +``` + +If you are unsure about the layout and naming for translation files in the selected project type, you can do a trial pull and look at the output. This can be done by copying `zanata.xml` to an empty folder and running a pull command such as: + +```bash +zanata-cli pull -s src -t trans --create-skeletons +``` + +The option `--create-skeletons` is used to make sure files will be written even if there are no translations. + +For more information on the pull command, see [Document Download with Client](/commands/pull). diff --git a/docs/configuration.md b/docs/configuration.md new file mode 100644 index 00000000..e893a1fe --- /dev/null +++ b/docs/configuration.md @@ -0,0 +1,152 @@ +Zanata-cli requires User Configuration and Project-Version Configuration. + +## User Configuration + +User configuration stores your credentials so that zanata-cli can prove to the server that requests are from you rather than an imposter. The information in your user config should be kept secret. + +zanata-cli expects to find user configuration in `.config/zanata.ini` within your user directory. + +To add configuration for a Zanata server: + + 1. Use your favourite text editor to create or open `zanata.ini` in `~/.config/`. + 1. Sign into the Zanata server and navigate to the user settings page + 1. Ensure that an API Key is shown. If you do not have an API Key, click 'Generate API Key' now. +![User settings page](/images/302-user-settings.png) + + 1. Copy the contents of the text-box labeled 'Configuration [zanata.ini]'. + 1. Paste the copied lines into `zanata.ini` and save the file. + + +## Project-Version Configuration + +Project configuration stores information about a project-version, and should be kept in the project directory. + +zanata-cli expects to find project-version configuration in a file named `zanata.xml` in the project directory. + +To add project-version configuration to your project directory: + + 1. Sign into the Zanata server and navigate to the appropriate version of your project. + 1. Click the `Download config file` link to initiate download of `zanata.xml`. +![Download config file link on version page](/images/350-version-config-file.png) + + 1. Save `zanata.xml` in your project directory. + + +These steps should be repeated for each project-version before using any zanata-cli commands for the project-version. + +You can customize `zanata.xml` with command hooks so that other tools will automatically run before or after Zanata commands. Read about command hooks at the [command hook](command-hook.md). + +## Source directory and translation directory + +To prevent the need to specify the source and translation directories on the command-line, they can be specified in zanata.xml. The source and translation directories are specified in the `` and `` elements respectively, as shown below. Both paths must be relative to the directory that contains zanata.xml. + +```xml +po +. +``` + +## Locale Configuration + +***Note:*** Locale configuration in zanata.xml is being phased out, and this configuration will be specified on the server instead. When your project is on a server version that allows locale configuration and you have a client version that supports server locales, you should remove this configuration. Check the details in [feature request 1156236](https://bugzilla.redhat.com/show_bug.cgi?id=1156236). + +The `zanata.xml` will contain a list of locales so that the client knows which locales to push and pull to/from the Zanata server. When downloaded from the Zanata server, the list will have the locales as specified by the server itself. It will look something like this: + +```xml + + es + ja + fr + zh-Hant-TW + ... + +``` + +Sometimes the way locales are named in your project files doesn't match Zanata's locale nomenclature, so it's necessary to create a mapping between the two. You can achieve this in the client by modifying the locale entries in `zanata.xml`. + +For instance, if one of your files is called `myfile/es.po` and your project in Zanata has the `es-ES` locale, then your client mappng would look like this: + +```xml + es-ES +``` + +### Translation files mapping rules + +You can also customize the way translation files are found when pushing, as well as the location they will be saved to when pulling. +{% highlight xml %} + + + {locale}/{path}/{filename}.po + {path}/{locale_with_underscore}.po + +{% endhighlight %} + +In the example above, `pattern` identifies a source file, and the contents of the `rule` element specify how translation files will be stored. + +The `pattern` attribute is a [glob](http://en.wikipedia.org/wiki/Glob_(programming)) matching pattern to your source document file(s). You can define more than one rule and apply each rule to a specific set of source documents using different patterns. The **first** matched rule will be applied to the file. + +`pattern` is optional. If not specified, the rule will be applied to all source documents in your project. +The actual rule consists of literal path and placeholders/variables. + +Supported placeholders/variables are: + + 1. **{path}** is the path between source document root (what you define as src-dir option) and the final file. + 1. **{filename}** the source document name without leading path and extension. + 1. **{locale}** the locale for the translation file. If you use "map-from" argument in your locale mapping, this will be the map-from value. + 1. **{locale\_with\_underscore}** same as above except all hyphens '-' will be replaced with underscores '_'. This is typically used in properties and gettext projects. + 1. **{extension}** the source document file extension + +For example, if you have the following file structure (where `{projectRoot}` is the root directory of your project and contains zanata.xml): + +``` +{projectRoot}/ + templates/messages/kdeedu/kalzium.pot + templates/messages/kdeedu/artikulate.pot + de-DE/messages/kdeedu/kalzium.po + de-DE/messages/keeedu/artikulate.po + ... + zanata.xml +``` + +Here we have two source documents (with "pot" extension) and two translation documents (with "po" extension) for the locale "de-DE". + +You can then use below configuration: +{% highlight xml %} +templates +. + + {locale}/{path}/{filename}.po + +{% endhighlight %} + +Explanation: Since you have defined `` as `templates`, the source document `templates/messages/kdeedu/kalzium.pot` will have its path extracted relative to `{projectRoot}/templates`, which gives the relative path `messages/kdeedu/kalzium.pot`. The relative path then will be partitioned into several tokens to form the following variables: + +``` +{path} = 'messages/kdeedu/' +{filename} = 'kalzium' +{locale} = 'de-DE' +{locale_with_underscore} = 'de_DE' +{extension} = 'pot' +``` + +> **NOTE** the relative path `messages/kdeedu/kalzium.pot` will be the document's unique identifier inside Zanata. +> If you change `src-dir` setting later, e.g. to ".", which results in a change of the relative path to `templates/messages/kdeedu/kalzium.pot`, +> pushing again will create a new document with the new path as its unique identifier, and the old document will be considered obsolete and will not be visible to anyone. +> The old document's translations will not be copied to the new document automatically, but they will appear as Translation Memory matches. This can be confusing and frustrating for translators. + +As the rule is defined as `{locale}/{path}/{filename}.po`, for locale `de-DE`, + +- source file kalzium.pot's translation file will be written to or read from `{projectRoot}/de-DE/messages/kdeedu/kalzium.po`. +- source file artikulate.pot's translation file will be written to or read from `{projectRoot}/de-DE/messages/kdeedu/artikulate.po`. + +You can also replace `{locale}` with `{locale_with_underscore}` if you want all your locales to use underscore instead of hyphen. e.g. `de-DE` will become `de_DE` which results in translation files written to or read from `{projectRoot}/de_DE/messages/kdeedu/kalzium.po`. + +The mapping rules configuration is optional in zanata.xml. If not specified, standard rules are applied according to your [project type](http://docs.zanata.org/en/latest/user-guide/projects/project-types). + + 1. gettext: `{path}/{locale_with_underscore}.po` + 1. podir: `{locale}/{path}/{filename}.po` + 1. properties: `{path}/{filename}_{locale_with_underscore}.{extension}` + 1. utf8properties: `{path}/{filename}_{locale_with_underscore}.{extension}` + 1. xliff: `{path}/{filename}_{locale_with_underscore}.{extension}` + 1. xml: `{path}/{filename}_{locale_with_underscore}.{extension}` + 1. file: `{locale}/{path}/{filename}.{extension}` + diff --git a/docs/images/302-user-settings.png b/docs/images/302-user-settings.png new file mode 100644 index 00000000..451b4aab Binary files /dev/null and b/docs/images/302-user-settings.png differ diff --git a/docs/images/350-version-config-file.png b/docs/images/350-version-config-file.png new file mode 100644 index 00000000..5f8262ae Binary files /dev/null and b/docs/images/350-version-config-file.png differ diff --git a/docs/index.md b/docs/index.md new file mode 100644 index 00000000..1d437ce4 --- /dev/null +++ b/docs/index.md @@ -0,0 +1,49 @@ +![Zanata](http://assets-zanata.rhcloud.com/master/assets/img/logo/logo-128.png) + +[Zanata.org](http://zanata.org) | [Twitter](http://www.twitter.com/#!/zanatatm) | [Blog](http://blog-zanatatm.rhcloud.com/) | [IRC@Freenode](http://webchat.freenode.net/?channels=zanata) + + +Zanata Command line Client +============= + +The Zanata Command line client (CLI) is the perfect way to connect to a Zanata server to +push and pull content. + +Features +-------- + +- Initialize a Translation project from the command line. +- Push source content to the Zanata server. +- Pull translated content from the Zanata server. + +Installation +------------ + +For installation instructions please see the [Installation Section](installation) + +Contribute +---------- + +- [Issue Tracker](http://bugzilla.redhat.com/buglist.cgi?product=Zanata) +- [Source Code on GitHub](http://github.com/zanata-client) + +Contact us +------- + + + +License +------- +Zanata is Free software, licensed under the [LGPL](http://www.gnu.org/licenses/lgpl-2.1.html). + +Copyright © 2015, Red Hat, Inc. \ No newline at end of file diff --git a/docs/installation.md b/docs/installation.md new file mode 100644 index 00000000..090d6f93 --- /dev/null +++ b/docs/installation.md @@ -0,0 +1,40 @@ +The Zanata client (zanata-cli) can be installed on most systems. Installation is easiest under Fedora, other systems can use Apache Ivy for installation. Manual installation is also possible. + + +## Installation on Fedora + +If you are using Fedora, zanata-cli is available through the `yum` package manager. + +```bash +sudo yum install zanata-client +``` + +*Note:* If you are not running the latest version of Fedora, the latest version of the client may not be available, so you might want to try the Ivy version (below). + +## Installation with Ivy + +See [Ivy installation](/ivy/installation) for more information. + +## Manual Installation + +To manually install zanata-cli: + + 1. Navigate to [zanata-cli on Maven Central](http://search.maven.org/#search%7Cga%7C1%7Cg%3A%22org.zanata%22%20AND%20a%3A%22zanata-cli%22). + 1. Download either `dist.zip` or `dist.tar.gz`. + + 1. Extract the contents of the archive to your location of choice. + 1. Create a symbolic link to the `zanata-cli` script in the bin directory of the extracted archive. e.g. from the archive directory, run `sudo ln -s bin/zanata-cli /usr/local/bin/zanata-cli`. + + 1. (optional) you can also enable tab-autocomplete for the client if you use bash as your terminal shell. This can be done by copying or linking the `zanata-cli-completion` script from the bin directory to `/etc/bash_completion.d/`. e.g. `ln -s bin/zanata-cli-completion /etc/bash_completion.d/zanata-cli-completion`. + + +## Nightly Builds + +If you like to live dangerously, the client nightly relase is available. This may have newer features, but is not guaranteed to be stable. + +The latest nightly build is available as an archive that can be installed manually. To install the latest nightly build: + + 1. Open [Client nightly builds](http://repository-zanata.forge.cloudbees.com/snapshot/org/zanata/zanata-cli/). + 1. Open the directory showing the highest version number. + 1. Download either of the distributable archives (ending with `-dist.zip` or `-dist.tar.gz`). + 1. Install as per manual installation instructions above. \ No newline at end of file diff --git a/docs/ivy/installation.md b/docs/ivy/installation.md new file mode 100644 index 00000000..2f12850a --- /dev/null +++ b/docs/ivy/installation.md @@ -0,0 +1,12 @@ +The Ivy distribution of the client is a small script that will download the client the first time it is run. This distribution requires Apache Ivy to run. + +1. See [Setup Ivy](/ivy/setup) for Apache Ivy installation. +2. Save [this script](https://raw.github.com/zanata/zanata-client-ivy/master/zanata-cli) somewhere on your path, and make sure it is executable. For example, assuming you have `~/bin in $PATH`, + +``` +cd ~/bin +wget https://raw.github.com/zanata/zanata-client-ivy/master/zanata-cli +chmod 755 zanata-cli +``` + +*Note:* It's a good idea to check for a new version of zanata-cli once in a while (especially when a new version of Zanata server is released). diff --git a/docs/ivy/setup.md b/docs/ivy/setup.md new file mode 100644 index 00000000..4ba6bd5d --- /dev/null +++ b/docs/ivy/setup.md @@ -0,0 +1,21 @@ +### RHEL/Fedora + +For RHEL 6, you will need to enable EPEL repository, with a command like this: + +``` +sudo rpm -Uvh http://dl.fedoraproject.org/pub/epel/6/x86_64/epel-release-6-8.noarch.rpm +``` + +Then install Ivy (this should work on Fedora too): + +``` +sudo yum install apache-ivy +``` + +*Note:* The Ivy client will download required files the first time it is run, which may take several minutes. It will show `resolving dependencies...` while this is happening. + +### Other systems + +Download an [Ivy binary distribution](http://ant.apache.org/ivy/download.cgi), extract Ivy's jar file somewhere, and set the environment variable `IVY_JAR` to point to it. + +For example: `export IVY_JAR=~/apps/apache-ivy-2.3.0/ivy-2.3.0.jar` \ No newline at end of file diff --git a/docs/maven-plugin/commands.md b/docs/maven-plugin/commands.md new file mode 100644 index 00000000..d5d7477c --- /dev/null +++ b/docs/maven-plugin/commands.md @@ -0,0 +1,63 @@ +If your project uses Apache Maven, you can use Zanata's Maven Plugin rather than the command line client. The Maven Plugin can be used for non-Maven projects, but the same functionality is available in zanata-cli without the need to install or configure Maven. + +The following instructions assume that you have installed and configured the Zanata Maven Plugin. Instructions for installation and configuration are available at [Installing the Maven Plugin](/maven-plugin/installation) and [Configuring the Maven Plugin](/maven-plugin/configuration) + +## Basic Maven Plugin commands + +Commands and options for the Maven Plugin are similar to commands and options for zanata-cli, but with different syntax. + +### Help + +To see an overview of commands, use + +```bash +mvn zanata:help +``` + +and for detailed help for a particular command, use something like + +``` +mvn zanata:help -Ddetail=true -Dgoal=push +``` + +These are equivalent to commands `zanata-cli help` and `zanata-cli help push` in zanata-cli. + +*Note:* an online view of the same help information can be viewed at [Maven Plugin Reference](https://zanata.ci.cloudbees.com/job/zanata-client-site/site/zanata-maven-plugin/plugin-info.html). + +### Push + +The basic push command is + +```bash +mvn zanata:push +``` + +This will look for source documents in the location for `srcDir` specified in `pom.xml` and upload them to the server. If `srcDir` is not specified in `pom.xml`, the current directory will be used. + +The source directory can be overridden on the command line as shown here: + +```bash +mvn zanata:push -Dzanata.srcDir="src/messages" +``` + +This will look for source strings in "src/messages". + +More detail on the push command can be found at [Document Upload with Client](/commands/push) + +### Pull + +The basic pull command is + +```bash +mvn zanata:pull +``` + +This will download translated documents from the server and save them in the location for `transDir` specified in `pom.xml`. If `transDir` is not specified in `pom.xml`, the current directory will be used. + +To download the source documents as well, specify pull type 'both' as shown here: + +```bash +mvn zanata:pull -Dzanata.pullType="both" +``` + +More detail on the pull command can be found at [Document Download with Client](/commands/pull) diff --git a/docs/maven-plugin/configuration.md b/docs/maven-plugin/configuration.md new file mode 100644 index 00000000..900b5f80 --- /dev/null +++ b/docs/maven-plugin/configuration.md @@ -0,0 +1,65 @@ +To use the Maven Plugin, some Zanata configuration files and an installation of Apache Maven are needed. Some additional configuration will let you use a shorter command to run the Maven Plugin. + +For Apache Maven installation, see [Installing the Maven Plugin](/maven-plugin/installation). + + +## Zanata Project Configuration + +The Zanata Maven plugin uses the general configuration files `zanata.ini` and `zanata.xml` in the same way as zanata-cli. For instructions on setting up these files, see [Configuring the Client](configuration). + +In addition, parameters such as source directory can be specified in `pom.xml` so that they are not needed on the command line. + + +## Command Configuration + +When Maven is installed, a verbose command can be used to run the Zanata Maven Plugin: + +```bash +mvn org.zanata:zanata-maven-plugin::help +``` + +The following instructions will allow the concise form of the command to be used instead: + +```bash +mvn zanata:help +``` + + +### Global Command Configuration + +To use the concise form of commands for any project, open `~/.m2/settings.xml` and ensure that pluginGroup `org.zanata` is present in pluginGroups: + +```xml + + ... + + org.zanata + ... + + ... + +``` + +This allows maven to use the latest version of the Zanata plugin when any `mvn zanata:*` command is run. + + +### Per-Project Command Configuration + +The plugin can be added to a single project by adding some elements to the project's `pom.xml` file. + +The following shows a build entry in `pom.xml` that will use version 3.4.0 of the Zanata plugin, and specifies the current directory (`.`) for source documents. This is equivalent to specifying the source directory on the command line as `-Dzanata.srcDir="."`. + +```xml + + + + org.zanata + zanata-maven-plugin + 3.4.0 + + . + + + + +``` diff --git a/docs/maven-plugin/installation.md b/docs/maven-plugin/installation.md new file mode 100644 index 00000000..56cdb618 --- /dev/null +++ b/docs/maven-plugin/installation.md @@ -0,0 +1,35 @@ +If your project uses Apache Maven, you can use Zanata's Maven Plugin rather than the command line client. The Maven Plugin can be used for non-Maven projects, but the same functionality is available in zanata-cli without the need to install or configure Maven. + +With Maven installed, the plugin can be run in the verbose form with no additional setup, or can be set up to use the concise form globally or for a single poject. The first time a command is run, the plugin and all its dependencies will be downloaded, which may take a while. + +## Prerequisite - Install Maven + +Zanata Maven plugin requires Apache Maven to be installed. Maven can be downloaded from [The Apache Maven website](http://maven.apache.org/) or installed using your operating system's package manager (e.g. `yum install maven`). + + +## Verbose Command (No Additional Setup) + +It is possible to run the Zanata plugin without any special setup once Maven is installed. + +This is done by specifying the full plugin id including version on the command line. For example, the help command can be run with: + +```bash +mvn org.zanata:zanata-maven-plugin::help +``` + +The instructions in other sections will assume that you are using the concise form of the command, so if you choose to use the verbose form you should use + +``` +mvn org.zanata:zanata-maven-plugin:: +``` +anywhere you see `mvn zanata:` in the instructions. + +## Concise Command + +Using global or per-project setup will allow the use of the concise form of the command, which would shorten the help command to: + +```bash +mvn zanata:help +``` + +See "Global Command Configuration" under [Configuring the Maven Plugin](/maven-plugin/configuration) for details. diff --git a/mkdocs.yml b/mkdocs.yml new file mode 100644 index 00000000..a1ed7a56 --- /dev/null +++ b/mkdocs.yml @@ -0,0 +1,24 @@ +site_name: Zanata Client +docs_dir: docs +repo_name: GitHub +repo_url: http://www.github.com/zanata/zanata-client +theme: readthedocs +# theme_dir: For custom themes + +pages: +- Home: 'index.md' +- User Guide: + - Installation: 'installation.md' + - Configuration: 'configuration.md' + - Command Hook: 'command-hook.md' +- Ivy: + - Installation: 'ivy/installation.md' + - Setup: 'ivy/setup.md' +- Maven Plugin: + - Installation: 'maven-plugin/installation.md' + - Configuration: 'maven-plugin/configuration.md' + - Commands: 'maven-plugin/commands.md' +- Commands: + - init: 'commands/init.md' + - push: 'commands/push.md' + - pull: 'commands/pull.md' diff --git a/pom.xml b/pom.xml index 7a546199..2e6e7423 100644 --- a/pom.xml +++ b/pom.xml @@ -2,13 +2,13 @@ 4.0.0 client - 3.6.1-SNAPSHOT + 3.7.0-SNAPSHOT Zanata client modules pom org.zanata zanata-parent - 20 + 23 ../parent @@ -35,11 +35,10 @@ - 3.4.1 - 3.3.0 + 3.7.2 + 3.7.1 3.0.1.Final - https://raw.githubusercontent.com/zanata/zanata-parent/zanata-parent-20/checkstyle.xml - https://raw.github.com/zanata/zanata-parent/zanata-parent-20/checkstyle-suppressions.xml + 1.17.1 @@ -75,6 +74,22 @@ commons-logging commons-logging + + org.jboss.resteasy + resteasy-jaxrs + + + org.jboss.resteasy + resteasy-jaxb-provider + + + org.jboss.resteasy + jaxrs-api + + + org.jboss.resteasy + resteasy-multipart-provider + @@ -204,6 +219,7 @@ + stub-server zanata-cli zanata-client-commands zanata-maven-plugin diff --git a/stub-server/pom.xml b/stub-server/pom.xml new file mode 100644 index 00000000..7affd071 --- /dev/null +++ b/stub-server/pom.xml @@ -0,0 +1,174 @@ + + + 4.0.0 + + org.zanata + client + 3.7.0-SNAPSHOT + + stub-server + stub-server + + + + scm:git:git://github.com/zanata/zanata-client.git + scm:git:git@github.com:zanata/zanata-client.git + https://github.com/zanata/zanata-client + HEAD + + + + UTF-8 + 9.0.3.v20130506 + + + + org.jboss.resteasy + jaxrs-api + + + org.zanata + zanata-common-api + + + + com.sun.jersey + jersey-core + + + + + org.zanata + zanata-adapter-po + + + org.eclipse.jetty + jetty-server + ${jetty.version} + + + org.eclipse.jetty + jetty-servlet + ${jetty.version} + + + org.eclipse.jetty + jetty-servlets + ${jetty.version} + + + + org.jboss.resteasy + async-http-servlet-3.0 + ${resteasy.version} + + + org.jboss.resteasy + resteasy-jaxrs + + + net.jcip + jcip-annotations + + + + + org.apache.httpcomponents + httpclient + + + org.jboss.resteasy + resteasy-jaxb-provider + + + org.jboss.resteasy + resteasy-multipart-provider + + + javax.servlet + servlet-api + + + commons-logging + commons-logging + + + + + org.scannotation + scannotation + 1.0.3 + + + javassist + javassist + + + + + org.jboss.resteasy + resteasy-jackson-provider + + + + org.codehaus.jackson + jackson-core-asl + + + + org.codehaus.jackson + jackson-jaxrs + + + + org.codehaus.jackson + jackson-mapper-asl + + + + org.codehaus.jackson + jackson-xc + + + + junit + junit + compile + + + + com.google.guava + guava + + + + + + + maven-surefire-plugin + + false + + + + org.basepom.maven + duplicate-finder-maven-plugin + + + + about.html + + + + + + + + + + + + diff --git a/stub-server/src/main/java/org/zanata/rest/service/MockAccountResource.java b/stub-server/src/main/java/org/zanata/rest/service/MockAccountResource.java new file mode 100644 index 00000000..74ea22b6 --- /dev/null +++ b/stub-server/src/main/java/org/zanata/rest/service/MockAccountResource.java @@ -0,0 +1,53 @@ +/* + * Copyright 2014, Red Hat, Inc. and individual contributors + * as indicated by the @author tags. See the copyright.txt file in the + * distribution for a full listing of individual contributors. + * + * This is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this software; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA, or see the FSF site: http://www.fsf.org. + */ + +package org.zanata.rest.service; + +import javax.ws.rs.Path; +import javax.ws.rs.core.Context; +import javax.ws.rs.core.Response; +import javax.ws.rs.core.UriInfo; + +import org.zanata.rest.dto.Account; + +/** + * @author Patrick Huang pahuang@redhat.com + */ +@Path(AccountResource.SERVICE_PATH) +public class MockAccountResource implements AccountResource { + @Context + UriInfo uriInfo; + private Account account = + new Account("admin@zanata.org", "Administrator", "admin", + "guesswhat"); + + @Override + public Response get() { + return Response.ok(account).build(); + } + + @Override + public Response put(Account account) { + return Response.created(uriInfo.getRequestUri()).build(); + } +} + diff --git a/stub-server/src/main/java/org/zanata/rest/service/MockAsynchronousProcessResource.java b/stub-server/src/main/java/org/zanata/rest/service/MockAsynchronousProcessResource.java new file mode 100644 index 00000000..0b0733f4 --- /dev/null +++ b/stub-server/src/main/java/org/zanata/rest/service/MockAsynchronousProcessResource.java @@ -0,0 +1,79 @@ +/* + * Copyright 2014, Red Hat, Inc. and individual contributors + * as indicated by the @author tags. See the copyright.txt file in the + * distribution for a full listing of individual contributors. + * + * This is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this software; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA, or see the FSF site: http://www.fsf.org. + */ + +package org.zanata.rest.service; + +import java.util.Set; +import javax.ws.rs.DefaultValue; +import javax.ws.rs.Path; + +import org.zanata.common.LocaleId; +import org.zanata.rest.dto.ProcessStatus; +import org.zanata.rest.dto.resource.Resource; +import org.zanata.rest.dto.resource.TranslationsResource; + +/** + * @author Patrick Huang pahuang@redhat.com + */ +@Path(AsynchronousProcessResource.SERVICE_PATH) +public class MockAsynchronousProcessResource implements + AsynchronousProcessResource { + @Override + public ProcessStatus startSourceDocCreation(String idNoSlash, + String projectSlug, String iterationSlug, Resource resource, + Set extensions, @DefaultValue("true") boolean copytrans) { + return MockResourceUtil.notUsedByClient(); + } + + @Override + public ProcessStatus startSourceDocCreationOrUpdate(String idNoSlash, + String projectSlug, String iterationSlug, Resource resource, + Set extensions, @DefaultValue("true") boolean copytrans) { + ProcessStatus processStatus = new ProcessStatus(); + processStatus.setStatusCode(ProcessStatus.ProcessStatusCode.Running); + processStatus.setPercentageComplete(50); + processStatus.setUrl("process1"); + return processStatus; + } + + @Override + public ProcessStatus startTranslatedDocCreationOrUpdate(String idNoSlash, + String projectSlug, String iterationSlug, LocaleId locale, + TranslationsResource translatedDoc, Set extensions, + String merge, @DefaultValue("false") boolean myTrans) { + ProcessStatus processStatus = new ProcessStatus(); + processStatus.setStatusCode(ProcessStatus.ProcessStatusCode.Running); + processStatus.setPercentageComplete(50); + processStatus.setUrl("process2"); + return processStatus; + } + + @Override + public ProcessStatus getProcessStatus(String processId) { + ProcessStatus processStatus = new ProcessStatus(); + processStatus.setStatusCode(ProcessStatus.ProcessStatusCode.Finished); + processStatus.setPercentageComplete(100); + processStatus.setUrl(processId); + return processStatus; + } +} + diff --git a/stub-server/src/main/java/org/zanata/rest/service/MockCopyTransResource.java b/stub-server/src/main/java/org/zanata/rest/service/MockCopyTransResource.java new file mode 100644 index 00000000..965cac0a --- /dev/null +++ b/stub-server/src/main/java/org/zanata/rest/service/MockCopyTransResource.java @@ -0,0 +1,52 @@ +/* + * Copyright 2014, Red Hat, Inc. and individual contributors + * as indicated by the @author tags. See the copyright.txt file in the + * distribution for a full listing of individual contributors. + * + * This is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this software; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA, or see the FSF site: http://www.fsf.org. + */ + +package org.zanata.rest.service; + +import javax.ws.rs.Path; + +import org.zanata.rest.dto.CopyTransStatus; + +/** + * @author Patrick Huang + * pahuang@redhat.com + */ +@Path(CopyTransResource.SERVICE_PATH) +public class MockCopyTransResource implements CopyTransResource { + @Override + public CopyTransStatus startCopyTrans(String projectSlug, + String iterationSlug, String docId) { + CopyTransStatus copyTransStatus = new CopyTransStatus(); + copyTransStatus.setInProgress(true); + copyTransStatus.setPercentageComplete(50); + return copyTransStatus; + } + + @Override + public CopyTransStatus getCopyTransStatus(String projectSlug, + String iterationSlug, String docId) { + CopyTransStatus copyTransStatus = new CopyTransStatus(); + copyTransStatus.setInProgress(false); + copyTransStatus.setPercentageComplete(100); + return copyTransStatus; + } +} + diff --git a/stub-server/src/main/java/org/zanata/rest/service/MockFileResource.java b/stub-server/src/main/java/org/zanata/rest/service/MockFileResource.java new file mode 100644 index 00000000..f4c262a1 --- /dev/null +++ b/stub-server/src/main/java/org/zanata/rest/service/MockFileResource.java @@ -0,0 +1,185 @@ +/* + * Copyright 2014, Red Hat, Inc. and individual contributors + * as indicated by the @author tags. See the copyright.txt file in the + * distribution for a full listing of individual contributors. + * + * This is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this software; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA, or see the FSF site: http://www.fsf.org. + */ + +package org.zanata.rest.service; + +import java.io.IOException; +import java.io.OutputStream; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; + +import javax.ws.rs.Path; +import javax.ws.rs.WebApplicationException; +import javax.ws.rs.core.GenericEntity; +import javax.ws.rs.core.MediaType; +import javax.ws.rs.core.Response; +import javax.ws.rs.core.StreamingOutput; + +import org.jboss.resteasy.annotations.providers.multipart.MultipartForm; +import org.zanata.adapter.po.PoWriter2; +import org.zanata.common.ContentState; +import org.zanata.common.DocumentType; +import org.zanata.common.LocaleId; +import org.zanata.common.ProjectType; +import org.zanata.rest.DocumentFileUploadForm; +import org.zanata.rest.StringSet; +import org.zanata.rest.dto.ChunkUploadResponse; +import org.zanata.rest.dto.Project; +import org.zanata.rest.dto.resource.Resource; +import org.zanata.rest.dto.resource.TextFlow; +import org.zanata.rest.dto.resource.TextFlowTarget; +import org.zanata.rest.dto.resource.TranslationsResource; + +import static org.zanata.common.ProjectType.Podir; + +/** + * @author Patrick Huang pahuang@redhat.com + */ +@Path(FileResource.SERVICE_PATH) +public class MockFileResource implements FileResource { + @Override + @Deprecated + public Response acceptedFileTypes() { + StringSet extensions = new StringSet(""); + for (DocumentType docType : ProjectType + .getSupportedSourceFileTypes(ProjectType.File)) { + extensions.addAll(docType.getSourceExtensions()); + } + for (DocumentType docType : ProjectType + .getSupportedSourceFileTypes(ProjectType.Gettext)) { + extensions + .addAll(docType.getSourceExtensions()); + } + return Response.ok(extensions.toString()).build(); + } + + @Override + public Response acceptedFileTypeList() { + GenericEntity> genericEntity = + new GenericEntity>(ProjectType.fileProjectSourceDocTypes()) {}; + return Response.ok(genericEntity).build(); + } + + @Override + public Response uploadSourceFile(String projectSlug, String iterationSlug, + String docId, @MultipartForm DocumentFileUploadForm uploadForm) { + return Response.status(Response.Status.CREATED).entity( + new ChunkUploadResponse(1L, 1, false, + "Upload of new source document successful.")) + .build(); + } + + @Override + public Response uploadTranslationFile(String projectSlug, + String iterationSlug, String localeId, String docId, String merge, + @MultipartForm DocumentFileUploadForm uploadForm) { + return Response.ok( + new ChunkUploadResponse(1L, 1, false, + "Translations uploaded successfully")) + .build(); + } + + @Override + public Response downloadSourceFile(String projectSlug, String iterationSlug, + String fileType, final String docId) { + StreamingOutput output = new StreamingOutput() { + + @Override + public void write(OutputStream output) + throws IOException, WebApplicationException { + PoWriter2 writer = new PoWriter2(false, false); + Resource doc = sampleResource(docId); + writer.writePot(output, "UTF-8", doc); + } + }; + return Response + .ok() + .header("Content-Disposition", + "attachment; filename=\"" + docId + + ".pot\"").type(MediaType.TEXT_PLAIN) + .entity(output).build(); + } + + private static Resource sampleResource(String docId) { + Resource doc = new Resource(docId); + doc.getTextFlows().add(new TextFlow("hello", LocaleId.EN_US, + "hello world")); + return doc; + } + + @Override + public Response downloadTranslationFile(String projectSlug, + String iterationSlug, String locale, String fileExtension, + final String docId) { + StreamingOutput output = new StreamingOutput() { + + @Override + public void write(OutputStream output) + throws IOException, WebApplicationException { + PoWriter2 writer = new PoWriter2(false, false); + writer.writePo(output, "UTF-8", sampleResource(docId), + sampleTransResource()); + } + }; + return Response.ok() + .header("Content-Disposition", + "attachment; filename=\"" + + docId + ".po\"").type(MediaType.TEXT_PLAIN) + .entity(output).build(); + } + + private static TranslationsResource sampleTransResource() { + TranslationsResource resource = new TranslationsResource(); + resource.getExtensions(true); + TextFlowTarget hello = new TextFlowTarget("hello"); + hello.getExtensions(true); + hello.setState(ContentState.Translated); + hello.setContents("hola mundo"); + resource.getTextFlowTargets().add(hello); + return resource; + } + + @Override + public Response download(final String downloadId) { + StreamingOutput output = new StreamingOutput() { + + @Override + public void write(OutputStream output) + throws IOException, WebApplicationException { + PoWriter2 writer = new PoWriter2(false, false); + writer.writePo(output, "UTF-8", sampleResource(downloadId), + sampleTransResource()); + } + }; + return Response.ok() + .header("Content-Disposition", + "attachment; filename=\"" + + downloadId + ".po\"") + .type(MediaType.TEXT_PLAIN) + .entity(output).build(); + } +} + diff --git a/stub-server/src/main/java/org/zanata/rest/service/MockGlossaryResource.java b/stub-server/src/main/java/org/zanata/rest/service/MockGlossaryResource.java new file mode 100644 index 00000000..dfae97bd --- /dev/null +++ b/stub-server/src/main/java/org/zanata/rest/service/MockGlossaryResource.java @@ -0,0 +1,66 @@ +/* + * Copyright 2014, Red Hat, Inc. and individual contributors + * as indicated by the @author tags. See the copyright.txt file in the + * distribution for a full listing of individual contributors. + * + * This is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this software; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA, or see the FSF site: http://www.fsf.org. + */ + +package org.zanata.rest.service; + +import javax.ws.rs.Path; +import javax.ws.rs.core.Context; +import javax.ws.rs.core.Response; +import javax.ws.rs.core.UriInfo; + +import org.zanata.common.LocaleId; +import org.zanata.rest.dto.Glossary; + +/** + * @author Patrick Huang + * pahuang@redhat.com + */ +@Path(GlossaryResource.SERVICE_PATH) +public class MockGlossaryResource implements GlossaryResource { + @Context + UriInfo uriInfo; + + @Override + public Response getEntries() { + return MockResourceUtil.notUsedByClient(); + } + + @Override + public Response get(LocaleId locale) { + return MockResourceUtil.notUsedByClient(); + } + + @Override + public Response put(Glossary messageBody) { + return Response.created(uriInfo.getAbsolutePath()).build(); + } + + @Override + public Response deleteGlossary(LocaleId locale) { + return Response.ok().build(); + } + + @Override + public Response deleteGlossaries() { + return Response.ok().build(); + } +} + diff --git a/stub-server/src/main/java/org/zanata/rest/service/MockProjectIterationResource.java b/stub-server/src/main/java/org/zanata/rest/service/MockProjectIterationResource.java new file mode 100644 index 00000000..ff16612e --- /dev/null +++ b/stub-server/src/main/java/org/zanata/rest/service/MockProjectIterationResource.java @@ -0,0 +1,71 @@ +/* + * Copyright 2014, Red Hat, Inc. and individual contributors + * as indicated by the @author tags. See the copyright.txt file in the + * distribution for a full listing of individual contributors. + * + * This is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this software; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA, or see the FSF site: http://www.fsf.org. + */ + +package org.zanata.rest.service; + +import javax.ws.rs.Path; +import javax.ws.rs.core.Context; +import javax.ws.rs.core.MediaType; +import javax.ws.rs.core.Response; +import javax.ws.rs.core.UriInfo; + +import org.zanata.rest.dto.ProjectIteration; + +/** + * @author Patrick Huang pahuang@redhat.com + */ +@Path(ProjectIterationResource.SERVICE_PATH) +public class MockProjectIterationResource implements ProjectIterationResource { + @Context + UriInfo uriInfo; + + @Override + public Response head() { + return MockResourceUtil.notUsedByClient(); + } + + @Override + public Response get() { + return Response.ok(new ProjectIteration("master")).build(); + } + + @Override + public Response put(ProjectIteration project) { + return Response.created(uriInfo.getRequestUri()).build(); + } + + @Override + public Response sampleConfiguration() { + String config = + new StringBuilder() + .append("\n") + .append("\n") + .append(" ") + .append(uriInfo.getBaseUri()) + .append("\n") + .append(" about-fedora\n") + .append(" master\n") + .append("").toString(); + return Response.ok(config, MediaType.TEXT_PLAIN_TYPE).build(); + } +} + diff --git a/stub-server/src/main/java/org/zanata/rest/service/MockProjectResource.java b/stub-server/src/main/java/org/zanata/rest/service/MockProjectResource.java new file mode 100644 index 00000000..4ee00f04 --- /dev/null +++ b/stub-server/src/main/java/org/zanata/rest/service/MockProjectResource.java @@ -0,0 +1,57 @@ +/* + * Copyright 2014, Red Hat, Inc. and individual contributors + * as indicated by the @author tags. See the copyright.txt file in the + * distribution for a full listing of individual contributors. + * + * This is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this software; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA, or see the FSF site: http://www.fsf.org. + */ + +package org.zanata.rest.service; + +import javax.ws.rs.Path; +import javax.ws.rs.core.Context; +import javax.ws.rs.core.Response; +import javax.ws.rs.core.UriInfo; + +import org.zanata.common.ProjectType; +import org.zanata.rest.dto.Project; + +/** + * @author Patrick Huang pahuang@redhat.com + */ +@Path(ProjectResource.SERVICE_PATH) +public class MockProjectResource implements ProjectResource { + @Context + UriInfo uriInfo; + + @Override + public Response head() { + return MockResourceUtil.notUsedByClient(); + } + + @Override + public Response get() { + return Response.ok(new Project("about-fedora", "About Fedora", + ProjectType.Podir.name().toLowerCase())).build(); + } + + @Override + public Response put(Project project) { + return Response.created(uriInfo.getRequestUri()).build(); + } +} + diff --git a/stub-server/src/main/java/org/zanata/rest/service/MockProjectsResource.java b/stub-server/src/main/java/org/zanata/rest/service/MockProjectsResource.java new file mode 100644 index 00000000..f911eed1 --- /dev/null +++ b/stub-server/src/main/java/org/zanata/rest/service/MockProjectsResource.java @@ -0,0 +1,52 @@ +/* + * Copyright 2014, Red Hat, Inc. and individual contributors + * as indicated by the @author tags. See the copyright.txt file in the + * distribution for a full listing of individual contributors. + * + * This is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this software; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA, or see the FSF site: http://www.fsf.org. + */ + +package org.zanata.rest.service; + +import static org.zanata.common.ProjectType.Podir; + +import javax.ws.rs.Path; +import javax.ws.rs.core.GenericEntity; +import javax.ws.rs.core.Response; + +import org.zanata.rest.dto.Project; + +/** + * @author Patrick Huang pahuang@redhat.com + */ +@Path(ProjectsResource.SERVICE_PATH) +public class MockProjectsResource implements ProjectsResource { + + private static final Project[] PROJECTS = new Project[] { + new Project("about-fedora", "About Fedora", + Podir.name().toLowerCase()) + }; + + @Override + public Response get() { + GenericEntity genericEntity = + new GenericEntity(PROJECTS) { + }; + return Response.ok(genericEntity).build(); + } +} + diff --git a/stub-server/src/main/java/org/zanata/rest/service/MockResourceUtil.java b/stub-server/src/main/java/org/zanata/rest/service/MockResourceUtil.java new file mode 100644 index 00000000..4960825a --- /dev/null +++ b/stub-server/src/main/java/org/zanata/rest/service/MockResourceUtil.java @@ -0,0 +1,47 @@ +/* + * Copyright 2014, Red Hat, Inc. and individual contributors + * as indicated by the @author tags. See the copyright.txt file in the + * distribution for a full listing of individual contributors. + * + * This is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this software; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA, or see the FSF site: http://www.fsf.org. + */ + +package org.zanata.rest.service; + +import java.util.Set; + +import javax.ws.rs.core.Response; + +import com.google.common.base.Preconditions; + +/** + * @author Patrick Huang + * pahuang@redhat.com + */ +class MockResourceUtil { + static void validateExtensions(Set extensions) { + Preconditions.checkArgument(extensions == null || extensions.isEmpty() + || extensions.contains("gettext") + || extensions.contains("comment")); + } + + static T notUsedByClient() { + throw new UnsupportedOperationException("Not being used by client"); + } + + +} + diff --git a/stub-server/src/main/java/org/zanata/rest/service/MockResourcesApplication.java b/stub-server/src/main/java/org/zanata/rest/service/MockResourcesApplication.java new file mode 100644 index 00000000..cdb63d31 --- /dev/null +++ b/stub-server/src/main/java/org/zanata/rest/service/MockResourcesApplication.java @@ -0,0 +1,55 @@ +/* + * Copyright 2014, Red Hat, Inc. and individual contributors + * as indicated by the @author tags. See the copyright.txt file in the + * distribution for a full listing of individual contributors. + * + * This is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this software; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA, or see the FSF site: http://www.fsf.org. + */ + +package org.zanata.rest.service; + +import java.util.Set; +import javax.ws.rs.core.Application; + +import com.google.common.collect.ImmutableSet; + +/** + * @author Patrick Huang pahuang@redhat.com + */ +public class MockResourcesApplication extends Application { + private static final ImmutableSet> services = ImmutableSet + .of( + MockVersionResource.class, + MockSourceDocResource.class, + MockTranslatedDocResource.class, + MockStatisticsResource.class, + MockFileResource.class, + MockProjectsResource.class, + MockProjectIterationResource.class, + MockProjectResource.class, + MockGlossaryResource.class, + MockCopyTransResource.class, + MockAccountResource.class, + MockAsynchronousProcessResource.class + ); + + @Override + public Set> getClasses() { + return services; + } +} + diff --git a/stub-server/src/main/java/org/zanata/rest/service/MockSourceDocResource.java b/stub-server/src/main/java/org/zanata/rest/service/MockSourceDocResource.java new file mode 100644 index 00000000..e4f9b807 --- /dev/null +++ b/stub-server/src/main/java/org/zanata/rest/service/MockSourceDocResource.java @@ -0,0 +1,94 @@ +/* + * Copyright 2014, Red Hat, Inc. and individual contributors + * as indicated by the @author tags. See the copyright.txt file in the + * distribution for a full listing of individual contributors. + * + * This is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this software; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA, or see the FSF site: http://www.fsf.org. + */ + +package org.zanata.rest.service; + +import java.util.Collection; +import java.util.Date; +import java.util.Set; +import javax.ws.rs.DefaultValue; +import javax.ws.rs.Path; +import javax.ws.rs.core.EntityTag; +import javax.ws.rs.core.GenericEntity; +import javax.ws.rs.core.Response; + +import org.zanata.rest.dto.resource.Resource; +import org.zanata.rest.dto.resource.ResourceMeta; + +/** + * @author Patrick Huang pahuang@redhat.com + */ +@Path(SourceDocResource.SERVICE_PATH) +public class MockSourceDocResource implements SourceDocResource { + + @Override + public Response head() { + return Response.ok(new EntityTag(new Date().toString())).build(); + } + + @Override + public Response get(Set extensions) { + MockResourceUtil.validateExtensions(extensions); + Collection samples = + new ResourceMeta("about-fedora").createSamples(); + GenericEntity> entity = + new GenericEntity>(samples) { + }; + return Response.ok(entity).build(); + } + + @Override + public Response post(Resource resource, Set extensions, + @DefaultValue("true") boolean copyTrans) { + return MockResourceUtil.notUsedByClient(); + } + + @Override + public Response getResource(String idNoSlash, Set extensions) { + MockResourceUtil.validateExtensions(extensions); + return Response.ok(new Resource(idNoSlash)).build(); + } + + @Override + public Response putResource(String idNoSlash, Resource resource, + Set extensions, @DefaultValue("true") boolean copyTrans) { + MockResourceUtil.validateExtensions(extensions); + return Response.ok(resource.getName()).build(); + } + + @Override + public Response deleteResource(String idNoSlash) { + return Response.ok().build(); + } + + @Override + public Response getResourceMeta(String idNoSlash, Set extensions) { + return MockResourceUtil.notUsedByClient(); + } + + @Override + public Response putResourceMeta(String idNoSlash, ResourceMeta resourceMeta, + Set extensions) { + return MockResourceUtil.notUsedByClient(); + } +} + diff --git a/stub-server/src/main/java/org/zanata/rest/service/MockStatisticsResource.java b/stub-server/src/main/java/org/zanata/rest/service/MockStatisticsResource.java new file mode 100644 index 00000000..f6de9387 --- /dev/null +++ b/stub-server/src/main/java/org/zanata/rest/service/MockStatisticsResource.java @@ -0,0 +1,82 @@ +/* + * Copyright 2014, Red Hat, Inc. and individual contributors + * as indicated by the @author tags. See the copyright.txt file in the + * distribution for a full listing of individual contributors. + * + * This is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this software; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA, or see the FSF site: http://www.fsf.org. + */ + +package org.zanata.rest.service; + +import javax.ws.rs.DefaultValue; +import javax.ws.rs.Path; + +import org.zanata.common.LocaleId; +import org.zanata.common.TransUnitCount; +import org.zanata.rest.dto.stats.ContainerTranslationStatistics; +import org.zanata.rest.dto.stats.TranslationStatistics; +import org.zanata.rest.dto.stats.contribution.BaseContributionStatistic; +import org.zanata.rest.dto.stats.contribution.ContributionStatistics; +import org.zanata.rest.dto.stats.contribution.LocaleStatistics; + +/** + * @author Patrick Huang pahuang@redhat.com + */ +@Path(StatisticsResource.SERVICE_PATH) +public class MockStatisticsResource implements StatisticsResource { + + @Override + public ContainerTranslationStatistics getStatistics(String projectSlug, + String iterationSlug, + @DefaultValue("false") boolean includeDetails, + @DefaultValue("false") boolean includeWordStats, String[] locales) { + return generateStatistics(iterationSlug, locales); + } + + private ContainerTranslationStatistics generateStatistics( + String id, String[] locales) { + ContainerTranslationStatistics stats = + new ContainerTranslationStatistics(); + stats.setId(id); + for (String locale : locales) { + stats.addStats(new TranslationStatistics(new TransUnitCount( + 100, 0, + 0, 100, 0), locale.trim())); + } + return stats; + } + + @Override + public ContainerTranslationStatistics getStatistics(String projectSlug, + String iterationSlug, String docId, + @DefaultValue("false") boolean includeWordStats, String[] locales) { + return generateStatistics(docId, locales); + } + + @Override + public ContributionStatistics getContributionStatistics(String projectSlug, + String versionSlug, String username, String dateRange) { + ContributionStatistics contributionStatistics = + new ContributionStatistics(); + LocaleStatistics localeStatistics = new LocaleStatistics(); + localeStatistics.put(new LocaleId("zh"), new BaseContributionStatistic( + 100, 90, 100, 0)); + contributionStatistics.put(username, localeStatistics); + return contributionStatistics; + } +} + diff --git a/stub-server/src/main/java/org/zanata/rest/service/MockTranslatedDocResource.java b/stub-server/src/main/java/org/zanata/rest/service/MockTranslatedDocResource.java new file mode 100644 index 00000000..a3de7f45 --- /dev/null +++ b/stub-server/src/main/java/org/zanata/rest/service/MockTranslatedDocResource.java @@ -0,0 +1,65 @@ +/* + * Copyright 2014, Red Hat, Inc. and individual contributors + * as indicated by the @author tags. See the copyright.txt file in the + * distribution for a full listing of individual contributors. + * + * This is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this software; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA, or see the FSF site: http://www.fsf.org. + */ + +package org.zanata.rest.service; + +import java.util.Set; +import javax.ws.rs.DefaultValue; +import javax.ws.rs.HeaderParam; +import javax.ws.rs.Path; +import javax.ws.rs.core.Response; + +import org.zanata.common.LocaleId; +import org.zanata.rest.dto.resource.TextFlowTarget; +import org.zanata.rest.dto.resource.TranslationsResource; + +/** + * @author Patrick Huang pahuang@redhat.com + */ +@Path(TranslatedDocResource.SERVICE_PATH) +public class MockTranslatedDocResource implements TranslatedDocResource { + + @Override + public Response getTranslations(String idNoSlash, LocaleId locale, + Set extensions, boolean createSkeletons, + @HeaderParam("If-None-Match") String eTag) { + MockResourceUtil.validateExtensions(extensions); + TranslationsResource transResource = new TranslationsResource(); + transResource.getTextFlowTargets().add(new TextFlowTarget(idNoSlash)); + return Response.ok(transResource).build(); + } + + @Override + public Response deleteTranslations(String idNoSlash, LocaleId locale) { + return MockResourceUtil.notUsedByClient(); + } + + @Override + public Response putTranslations(String idNoSlash, LocaleId locale, + TranslationsResource messageBody, Set extensions, + @DefaultValue("auto") String merge) { + // used by PublicanPush only + MockResourceUtil.validateExtensions(extensions); + return Response.ok().build(); + } +} + diff --git a/stub-server/src/main/java/org/zanata/rest/service/MockVersionResource.java b/stub-server/src/main/java/org/zanata/rest/service/MockVersionResource.java new file mode 100644 index 00000000..27791933 --- /dev/null +++ b/stub-server/src/main/java/org/zanata/rest/service/MockVersionResource.java @@ -0,0 +1,45 @@ +/* + * Copyright 2014, Red Hat, Inc. and individual contributors + * as indicated by the @author tags. See the copyright.txt file in the + * distribution for a full listing of individual contributors. + * + * This is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this software; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA, or see the FSF site: http://www.fsf.org. + */ + +package org.zanata.rest.service; + +import java.util.Date; +import javax.ws.rs.Path; +import javax.ws.rs.core.Response; + +import org.zanata.rest.dto.VersionInfo; + +/** + * @author Patrick Huang pahuang@redhat.com + */ +@Path(VersionResource.SERVICE_PATH) +public class MockVersionResource implements VersionResource { + + @Override + public Response get() { + return Response.ok( + new VersionInfo("3.6.0-SNAPSHOT", new Date().toString(), + "unknown")) + .build(); + } +} + diff --git a/stub-server/src/main/java/org/zanata/rest/service/StubbingServerRule.java b/stub-server/src/main/java/org/zanata/rest/service/StubbingServerRule.java new file mode 100644 index 00000000..5179b1fc --- /dev/null +++ b/stub-server/src/main/java/org/zanata/rest/service/StubbingServerRule.java @@ -0,0 +1,81 @@ +/* + * Copyright 2014, Red Hat, Inc. and individual contributors + * as indicated by the @author tags. See the copyright.txt file in the + * distribution for a full listing of individual contributors. + * + * This is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this software; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA, or see the FSF site: http://www.fsf.org. + */ + +package org.zanata.rest.service; + +import java.net.URI; + +import org.eclipse.jetty.server.Server; +import org.eclipse.jetty.servlet.ServletContextHandler; +import org.eclipse.jetty.servlet.ServletHolder; +import org.jboss.resteasy.plugins.server.servlet.HttpServlet30Dispatcher; +import org.junit.rules.TestRule; +import org.junit.runner.Description; +import org.junit.runners.model.Statement; +import com.google.common.base.Throwables; + +/** + * This will start up a jetty server and host stubbed Zanata rest resources. All + * the resource implementation will either return fixed response or, if not used + * by client right now, throw exception. + * + * @author Patrick Huang pahuang@redhat.com + */ +public class StubbingServerRule implements TestRule { + private static Server server; + + public StubbingServerRule() { + startServerIfRequired(); + } + + private static void startServerIfRequired() { + if (server != null && server.isStarted()) { + return; + } + server = new Server(0); + ServletContextHandler context = + new ServletContextHandler(ServletContextHandler.NO_SESSIONS); + context.setContextPath("/"); + ServletHolder holder = + new ServletHolder(new HttpServlet30Dispatcher()); + holder.setInitParameter("javax.ws.rs.Application", + MockResourcesApplication.class.getCanonicalName()); + context.addServlet(holder, "/*"); + server.setHandler(context); + server.setStopAtShutdown(true); + try { + server.start(); + } catch (Exception e) { + throw Throwables.propagate(e); + } + } + + @Override + public Statement apply(final Statement base, Description description) { + return base; + } + + public URI getServerBaseUri() { + return server.getURI(); + } +} + diff --git a/zanata-cli/etc/scripts/zanata-cli b/zanata-cli/etc/scripts/zanata-cli index 93ed0194..c5cce3f3 100755 --- a/zanata-cli/etc/scripts/zanata-cli +++ b/zanata-cli/etc/scripts/zanata-cli @@ -1,4 +1,15 @@ #!/bin/bash -e -SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" -PROJ_DIR=$SCRIPT_DIR/../.. -exec mvn -f $PROJ_DIR/pom.xml -q exec:java -Dexec.args="$*" + +# Full path to script even $0 has symlinks. +# Reference: http://stackoverflow.com/a/246128/345718 +SOURCE="${BASH_SOURCE[0]}" +while [ -h "$SOURCE" ]; do # resolve $SOURCE until the file is no longer a symlink + DIR="$( cd -P "$( dirname "$SOURCE" )" && pwd )" + SOURCE="$(readlink "$SOURCE")" + [[ ${SOURCE} != /* ]] && SOURCE="$DIR/$SOURCE" # if $SOURCE was a relative symlink, we need to resolve it relative to the path where the symlink file was located +done +SCRIPT_DIR="$( cd -P "$( dirname "$SOURCE" )" && pwd )" + +PROJ_DIR=${SCRIPT_DIR}/../.. +exec mvn -f ${PROJ_DIR}/pom.xml -q exec:java -Dexec.args="$*" + diff --git a/zanata-cli/pom.xml b/zanata-cli/pom.xml index db2c8ef9..f5f65904 100644 --- a/zanata-cli/pom.xml +++ b/zanata-cli/pom.xml @@ -4,7 +4,7 @@ org.zanata client - 3.6.1-SNAPSHOT + 3.7.0-SNAPSHOT zanata-cli Zanata command-line client diff --git a/zanata-cli/src/main/java/org/zanata/client/ZanataClient.java b/zanata-cli/src/main/java/org/zanata/client/ZanataClient.java index df73ffb8..01993924 100644 --- a/zanata-cli/src/main/java/org/zanata/client/ZanataClient.java +++ b/zanata-cli/src/main/java/org/zanata/client/ZanataClient.java @@ -211,10 +211,9 @@ private void printHelp(PrintWriter out) { out.println(); out.println("Available commands:"); for (String cmd : OPTIONS.keySet()) { - out.println(" " + cmd /* - * + ": " + OPTIONS.get(cmd).newInstance(). - * getCommandDescription() - */); + out.println(" " + cmd); + // + ": " + OPTIONS.get(cmd).newInstance(). + // getCommandDescription() } } diff --git a/zanata-cli/src/test/java/org/zanata/client/BashCompletionGenerator.java b/zanata-cli/src/test/java/org/zanata/client/BashCompletionGenerator.java index 96b169b7..2a57bfa6 100644 --- a/zanata-cli/src/test/java/org/zanata/client/BashCompletionGenerator.java +++ b/zanata-cli/src/test/java/org/zanata/client/BashCompletionGenerator.java @@ -4,6 +4,7 @@ import java.io.IOException; import java.lang.reflect.AccessibleObject; import java.nio.charset.Charset; +import java.util.Comparator; import java.util.List; import java.util.Map; import java.util.Set; @@ -40,8 +41,15 @@ public class BashCompletionGenerator { private List baseCommands; private List - com.google.code.findbugs - annotations - provided + com.sun.jersey + jersey-client + ${jersey.version} - org.jboss.resteasy - jaxrs-api - ${resteasy.version} - - - org.jboss.resteasy - resteasy-jaxrs - - - net.jcip - jcip-annotations - - + com.google.code.findbugs + annotations + provided @@ -80,6 +70,11 @@ slf4j-api + + com.sun.jersey + jersey-core + ${jersey.version} + junit junit @@ -175,4 +170,12 @@ + + + + src/test/resources + false + + + diff --git a/zanata-client-commands/src/main/java/org/zanata/client/commands/AbstractPushPullOptionsImpl.java b/zanata-client-commands/src/main/java/org/zanata/client/commands/AbstractPushPullOptionsImpl.java index 19c35f42..ab43ef30 100644 --- a/zanata-client-commands/src/main/java/org/zanata/client/commands/AbstractPushPullOptionsImpl.java +++ b/zanata-client-commands/src/main/java/org/zanata/client/commands/AbstractPushPullOptionsImpl.java @@ -52,8 +52,9 @@ public abstract class AbstractPushPullOptionsImpl * Override the parent method as the push and pull commands can have locales * specified via command line. * - * @return The locale map list taking into account the global locales in - * zanata.xml as well as the command line argument ones. + * @return The locale map list taking into account the global locales + * configured as well as the command line argument + * ones. */ @Override public LocaleList getLocaleMapList() { diff --git a/zanata-client-commands/src/main/java/org/zanata/client/commands/BasicOptions.java b/zanata-client-commands/src/main/java/org/zanata/client/commands/BasicOptions.java index 322bd346..3cf317ef 100644 --- a/zanata-client-commands/src/main/java/org/zanata/client/commands/BasicOptions.java +++ b/zanata-client-commands/src/main/java/org/zanata/client/commands/BasicOptions.java @@ -49,9 +49,9 @@ public interface BasicOptions { boolean isQuietSet(); - public boolean isInteractiveMode(); + boolean isInteractiveMode(); - public void setInteractiveMode(boolean interactiveMode); + void setInteractiveMode(boolean interactiveMode); /** * Used to generate the command line interface and its usage help. This name @@ -60,7 +60,7 @@ public interface BasicOptions { * * @return */ - public String getCommandName(); + String getCommandName(); /** * Used to generate CLI usage help. This description should preferably match @@ -68,9 +68,9 @@ public interface BasicOptions { * * @return */ - public String getCommandDescription(); + String getCommandDescription(); - public @Nonnull List getCommandHooks(); + @Nonnull List getCommandHooks(); - public void setCommandHooks(@Nonnull List commandHooks); + void setCommandHooks(@Nonnull List commandHooks); } diff --git a/zanata-client-commands/src/main/java/org/zanata/client/commands/ConfigurableCommand.java b/zanata-client-commands/src/main/java/org/zanata/client/commands/ConfigurableCommand.java index 322a3e71..6bdd6819 100644 --- a/zanata-client-commands/src/main/java/org/zanata/client/commands/ConfigurableCommand.java +++ b/zanata-client-commands/src/main/java/org/zanata/client/commands/ConfigurableCommand.java @@ -30,7 +30,7 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.zanata.client.config.CommandHook; -import org.zanata.rest.client.ZanataProxyFactory; +import org.zanata.rest.client.RestClientFactory; /** * Base class for commands which supports configuration by the user's zanata.ini @@ -41,19 +41,18 @@ public abstract class ConfigurableCommand implements ZanataCommand { private final O opts; - private ZanataProxyFactory requestFactory; + private RestClientFactory clientFactory; private boolean deprecated; private String deprecationMessage; private static final Logger log = LoggerFactory .getLogger(ConfigurableCommand.class); - public ConfigurableCommand(O opts, ZanataProxyFactory factory) { + public ConfigurableCommand(O opts, RestClientFactory clientFactory) { this.opts = opts; - if (factory != null) - this.requestFactory = factory; - else - this.requestFactory = OptionsUtil.createRequestFactory(opts); + this.clientFactory = + clientFactory == null ? OptionsUtil.createClientFactory(opts) + : clientFactory; } public ConfigurableCommand(O opts) { @@ -77,8 +76,12 @@ public O getOpts() { return opts; } - public ZanataProxyFactory getRequestFactory() { - return requestFactory; + public RestClientFactory getClientFactory() { + return clientFactory; + } + + protected final void setClientFactory(RestClientFactory clientFactory) { + this.clientFactory = clientFactory; } @Override diff --git a/zanata-client-commands/src/main/java/org/zanata/client/commands/ConfigurableOptions.java b/zanata-client-commands/src/main/java/org/zanata/client/commands/ConfigurableOptions.java index af94e989..acb688fb 100644 --- a/zanata-client-commands/src/main/java/org/zanata/client/commands/ConfigurableOptions.java +++ b/zanata-client-commands/src/main/java/org/zanata/client/commands/ConfigurableOptions.java @@ -18,37 +18,37 @@ public interface ConfigurableOptions extends BasicOptions { /** * API key for accessing the REST API. Defaults to the value in zanata.ini. */ - public String getKey(); + String getKey(); - public void setKey(String key); + void setKey(String key); /** * Base URL for the server. Defaults to the value in zanata.xml. */ - public URL getUrl(); + URL getUrl(); - public void setUrl(URL url); + void setUrl(URL url); /** * Client configuration file. */ - public File getUserConfig(); + File getUserConfig(); - public void setUserConfig(File userConfig); + void setUserConfig(File userConfig); /** * Username for accessing the REST API. Defaults to the value in zanata.ini. */ - public String getUsername(); + String getUsername(); - public void setUsername(String username); + void setUsername(String username); /** * Enable HTTP message logging. */ - public boolean getLogHttp(); + boolean getLogHttp(); - public void setLogHttp(boolean traceLogging); + void setLogHttp(boolean traceLogging); /** * Disable SSL certificate verification when connecting to Zanata host by @@ -56,13 +56,12 @@ public interface ConfigurableOptions extends BasicOptions { */ boolean isDisableSSLCert(); - public - void setDisableSSLCert(boolean disableSSLCert); + void setDisableSSLCert(boolean disableSSLCert); /** * Use to disable check for presence of username and API key before running command. * * @return true if this command should fail when username or API key is absent. */ - public boolean isAuthRequired(); + boolean isAuthRequired(); } diff --git a/zanata-client-commands/src/main/java/org/zanata/client/commands/ConfigurableProjectCommand.java b/zanata-client-commands/src/main/java/org/zanata/client/commands/ConfigurableProjectCommand.java index 51a68be9..25bf59bc 100644 --- a/zanata-client-commands/src/main/java/org/zanata/client/commands/ConfigurableProjectCommand.java +++ b/zanata-client-commands/src/main/java/org/zanata/client/commands/ConfigurableProjectCommand.java @@ -21,7 +21,7 @@ package org.zanata.client.commands; import org.zanata.client.exceptions.ConfigException; -import org.zanata.rest.client.ZanataProxyFactory; +import org.zanata.rest.client.RestClientFactory; /** * Base class for commands which supports configuration by the user's zanata.ini @@ -41,8 +41,12 @@ public abstract class ConfigurableProjectCommand documentTypes = projectType.getSourceFileTypes(); + for (DocumentType docType: documentTypes) { + if (docType.getSourceExtensions().contains( + qualifiedSrcDocName.getExtension())) { + return true; + } + } + return false; } /** @@ -108,9 +116,9 @@ private boolean matchFileExtensionWithProjectType( */ public String getRelativeTransFilePathForSourceDoc( QualifiedSrcDocName qualifiedSrcDocName, - @Nonnull LocaleMapping localeMapping) { + @Nonnull LocaleMapping localeMapping, Optional translationFileExtension) { EnumMap map = - parseToMap(qualifiedSrcDocName.getFullName(), localeMapping); + parseToMap(qualifiedSrcDocName.getFullName(), localeMapping, translationFileExtension); String transFilePath = mappingRule.getRule(); for (Map.Entry entry : map.entrySet()) { @@ -125,11 +133,15 @@ public String getRelativeTransFilePathForSourceDoc( @VisibleForTesting protected static EnumMap parseToMap( - @Nonnull String sourceFile, @Nonnull LocaleMapping localeMapping) { + @Nonnull String sourceFile, @Nonnull LocaleMapping localeMapping, + Optional translationFileExtension) { EnumMap parts = new EnumMap(Placeholders.class); File file = new File(sourceFile); - String extension = FilenameUtils.getExtension(sourceFile); + + String extension = + translationFileExtension.isPresent() ? translationFileExtension.get() + : FilenameUtils.getExtension(sourceFile); String filename = FilenameUtils.removeExtension(file.getName()); parts.put(Placeholders.extension, extension); parts.put(Placeholders.filename, filename); diff --git a/zanata-client-commands/src/main/java/org/zanata/client/commands/ListLocalCommand.java b/zanata-client-commands/src/main/java/org/zanata/client/commands/ListLocalCommand.java index e4564acc..c5344a53 100644 --- a/zanata-client-commands/src/main/java/org/zanata/client/commands/ListLocalCommand.java +++ b/zanata-client-commands/src/main/java/org/zanata/client/commands/ListLocalCommand.java @@ -2,7 +2,6 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import org.zanata.rest.client.ZanataProxyFactory; /** * @author Sean Flanigan @@ -14,13 +13,8 @@ public class ListLocalCommand extends private static final Logger log = LoggerFactory .getLogger(ListLocalCommand.class); - public ListLocalCommand(ConfigurableProjectOptions opts, - ZanataProxyFactory factory) { - super(opts, factory); - } - public ListLocalCommand(ConfigurableProjectOptions opts) { - this(opts, null); + super(opts); } @Override diff --git a/zanata-client-commands/src/main/java/org/zanata/client/commands/ListRemoteCommand.java b/zanata-client-commands/src/main/java/org/zanata/client/commands/ListRemoteCommand.java index 1ad954c4..059294e1 100644 --- a/zanata-client-commands/src/main/java/org/zanata/client/commands/ListRemoteCommand.java +++ b/zanata-client-commands/src/main/java/org/zanata/client/commands/ListRemoteCommand.java @@ -20,15 +20,13 @@ */ package org.zanata.client.commands; -import org.jboss.resteasy.client.ClientResponse; +import java.util.List; + import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import org.zanata.rest.client.ClientUtility; -import org.zanata.rest.client.ISourceDocResource; +import org.zanata.rest.client.SourceDocResourceClient; import org.zanata.rest.dto.resource.ResourceMeta; -import java.util.List; - /** * @author Sean Flanigan * @@ -47,19 +45,15 @@ public void run() throws Exception { log.info("Server: " + getOpts().getUrl()); log.info("Project: " + getOpts().getProj()); log.info("Version: " + getOpts().getProjectVersion()); - ISourceDocResource sourceDocResource = - getRequestFactory().getSourceDocResource(getOpts().getProj(), - getOpts().getProjectVersion()); - ClientResponse> response = - sourceDocResource.get(null); - ClientUtility.checkResult( - response, - getRequestFactory().getResourceURI(getOpts().getProj(), - getOpts().getProjectVersion())); - List list = response.getEntity(); + SourceDocResourceClient client = getClientFactory() + .getSourceDocResourceClient( + getOpts().getProj(), getOpts().getProjectVersion()); + + List list = client.getResourceMeta(null); for (ResourceMeta doc : list) { System.out.println(doc.getName()); } } } + diff --git a/zanata-client-commands/src/main/java/org/zanata/client/commands/Messages.java b/zanata-client-commands/src/main/java/org/zanata/client/commands/Messages.java index 1c98992d..cd36068e 100644 --- a/zanata-client-commands/src/main/java/org/zanata/client/commands/Messages.java +++ b/zanata-client-commands/src/main/java/org/zanata/client/commands/Messages.java @@ -20,6 +20,7 @@ */ package org.zanata.client.commands; +import java.text.MessageFormat; import java.util.ResourceBundle; import org.slf4j.Logger; @@ -45,4 +46,9 @@ public static String _(String key) { key); return key; } + + public static String format(String key, Object... args) { + String template = _(key); + return MessageFormat.format(template, args); + } } diff --git a/zanata-client-commands/src/main/java/org/zanata/client/commands/OptionsUtil.java b/zanata-client-commands/src/main/java/org/zanata/client/commands/OptionsUtil.java index c8c7645f..0a9e8753 100644 --- a/zanata-client-commands/src/main/java/org/zanata/client/commands/OptionsUtil.java +++ b/zanata-client-commands/src/main/java/org/zanata/client/commands/OptionsUtil.java @@ -2,6 +2,7 @@ import java.io.File; import java.net.URISyntaxException; +import java.util.List; import javax.xml.bind.JAXBContext; import javax.xml.bind.JAXBException; @@ -15,14 +16,19 @@ import org.zanata.client.config.ConfigUtil; import org.zanata.client.config.FileMappingRule; import org.zanata.client.config.LocaleList; +import org.zanata.client.config.LocaleMapping; import org.zanata.client.config.ZanataConfig; import org.zanata.client.exceptions.ConfigException; -import org.zanata.rest.client.ZanataProxyFactory; +import org.zanata.rest.client.ProjectIterationLocalesClient; +import org.zanata.rest.client.RestClientFactory; +import org.zanata.rest.dto.LocaleDetails; import org.zanata.util.VersionUtility; import com.google.common.annotations.VisibleForTesting; +import com.google.common.base.Function; import com.google.common.base.Joiner; import com.google.common.base.Preconditions; import com.google.common.collect.ImmutableList; +import com.google.common.collect.Lists; import static org.zanata.client.commands.ConsoleInteractor.DisplayMode.Question; import static org.zanata.client.commands.ConsoleInteractor.DisplayMode.Warning; @@ -42,6 +48,7 @@ public class OptionsUtil { */ public static void applyConfigFiles(ConfigurableOptions opts) throws ConfigurationException, JAXBException { + boolean shouldFetchLocalesFromServer = false; if (opts instanceof ConfigurableProjectOptions) { ConfigurableProjectOptions projOpts = (ConfigurableProjectOptions) opts; @@ -59,6 +66,15 @@ public static void applyConfigFiles(ConfigurableOptions opts) // zanata.ini, // so we apply it first applyProjectConfig(projOpts, projectConfig); + boolean localesDefinedInFile = + projectConfig.getLocales() != null + && !projectConfig.getLocales().isEmpty(); + if (localesDefinedInFile) { + ConsoleInteractorImpl console = new ConsoleInteractorImpl(opts); + console.printfln(Warning, _("locales.in.config.deprecated")); + } else { + shouldFetchLocalesFromServer = true; + } } else { log.warn("Project config file '{}' not found; ignoring.", projectConfigFile); @@ -77,6 +93,34 @@ public static void applyConfigFiles(ConfigurableOptions opts) opts.getUserConfig()); } } + // we have to wait until user config has been applied + if (shouldFetchLocalesFromServer) { + ConfigurableProjectOptions projectOptions = + (ConfigurableProjectOptions) opts; + LocaleList localeMappings = fetchLocalesFromServer(projectOptions); + projectOptions.setLocaleMapList(localeMappings); + } + } + + private static LocaleList fetchLocalesFromServer( + ConfigurableProjectOptions projectOpts) { + LocaleList localeList = new LocaleList(); + ProjectIterationLocalesClient projectIterationLocalesClient = + createClientFactoryWithoutVersionCheck(projectOpts) + .getProjectLocalesClient(projectOpts.getProj(), + projectOpts.getProjectVersion()); + List localeMappings = + Lists.transform(projectIterationLocalesClient.getLocales(), + new Function() { + @Override + public LocaleMapping apply(LocaleDetails input) { + return input == null ? null : new LocaleMapping( + input.getLocaleId().getId(), + input.getAlias()); + } + }); + localeList.addAll(localeMappings); + return localeList; } /** @@ -101,14 +145,14 @@ private static void applyProjectConfig(ConfigurableProjectOptions opts, } applySrcDirAndTransDirFromProjectConfig(opts, config); applyIncludesAndExcludesFromProjectConfig(opts, config); - LocaleList locales = config.getLocales(); - opts.setLocaleMapList(locales); + LocaleList localesInFile = config.getLocales(); + opts.setLocaleMapList(localesInFile); if (opts.getCommandHooks().isEmpty() && config.getHooks() != null) { opts.setCommandHooks(config.getHooks()); } opts.setFileMappingRules(config.getRules()); - checkPotentialMistakesInRules(opts, new ConsoleInteractorImpl()); + checkPotentialMistakesInRules(opts, new ConsoleInteractorImpl(opts)); } /** @@ -268,22 +312,6 @@ public static void applyUserConfig(ConfigurableOptions opts, } } - /** - * Creates proxy factory that will perform an eager REST version check. - */ - public static ZanataProxyFactory createRequestFactory( - ConfigurableOptions opts) { - try { - checkMandatoryOptsForRequestFactory(opts); - return new ZanataProxyFactory(opts.getUrl().toURI(), - opts.getUsername(), opts.getKey(), - VersionUtility.getAPIVersionInfo(), opts.getLogHttp(), - opts.isDisableSSLCert()); - } catch (URISyntaxException e) { - throw new ConfigException(e); - } - } - private static void checkMandatoryOptsForRequestFactory( ConfigurableOptions opts) { if (opts.getUrl() == null) { @@ -300,30 +328,52 @@ private static void checkMandatoryOptsForRequestFactory( } } + public static String stripValidHolders(String rule) { + String temp = rule; + for (Placeholders placeholder : Placeholders.values()) { + temp = temp.replace(placeholder.holder(), ""); + } + return temp; + } + /** - * Creates proxy factory that will NOT perform an eager REST version check. - * You can call - * org.zanata.rest.client.ZanataProxyFactory#performVersionCheck() - * afterwards. + * Creates rest client factory that will perform an eager REST version check. */ - public static ZanataProxyFactory createRequestFactoryWithoutVersionCheck( - ConfigurableProjectOptions opts) { + public static RestClientFactory + createClientFactory( + O opts) { + checkMandatoryOptsForRequestFactory(opts); try { - checkMandatoryOptsForRequestFactory(opts); - return new ZanataProxyFactory(opts.getUrl().toURI(), - opts.getUsername(), opts.getKey(), - VersionUtility.getAPIVersionInfo(), opts.getLogHttp(), - opts.isDisableSSLCert(), false); + RestClientFactory restClientFactory = + new RestClientFactory(opts.getUrl().toURI(), + opts.getUsername(), opts.getKey(), + VersionUtility.getAPIVersionInfo(), + opts.getLogHttp(), + opts.isDisableSSLCert()); + restClientFactory.performVersionCheck(); + return restClientFactory; } catch (URISyntaxException e) { throw new ConfigException(e); } } - public static String stripValidHolders(String rule) { - String temp = rule; - for (Placeholders placeholder : Placeholders.values()) { - temp = temp.replace(placeholder.holder(), ""); + /** + * Creates rest client factory that will NOT perform an eager REST version + * check. You can call + * org.zanata.rest.client.RestClientFactory#performVersionCheck() + * afterwards. + */ + public static RestClientFactory + createClientFactoryWithoutVersionCheck( + O opts) { + checkMandatoryOptsForRequestFactory(opts); + try { + return new RestClientFactory(opts.getUrl().toURI(), + opts.getUsername(), opts.getKey(), + VersionUtility.getAPIVersionInfo(), opts.getLogHttp(), + opts.isDisableSSLCert()); + } catch (URISyntaxException e) { + throw new ConfigException(e); } - return temp; } } diff --git a/zanata-client-commands/src/main/java/org/zanata/client/commands/PublicanPullCommand.java b/zanata-client-commands/src/main/java/org/zanata/client/commands/PublicanPullCommand.java deleted file mode 100644 index 4e22c1e6..00000000 --- a/zanata-client-commands/src/main/java/org/zanata/client/commands/PublicanPullCommand.java +++ /dev/null @@ -1,134 +0,0 @@ -package org.zanata.client.commands; - -import java.net.URI; -import java.util.List; - -import javax.ws.rs.core.Response; - -import org.jboss.resteasy.client.ClientResponse; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.zanata.adapter.po.PoWriter2; -import org.zanata.client.config.LocaleList; -import org.zanata.client.config.LocaleMapping; -import org.zanata.client.exceptions.ConfigException; -import org.zanata.common.LocaleId; -import org.zanata.rest.RestUtil; -import org.zanata.rest.StringSet; -import org.zanata.rest.client.ClientUtility; -import org.zanata.rest.client.ISourceDocResource; -import org.zanata.rest.client.ITranslatedDocResource; -import org.zanata.rest.client.ZanataProxyFactory; -import org.zanata.rest.dto.resource.Resource; -import org.zanata.rest.dto.resource.ResourceMeta; -import org.zanata.rest.dto.resource.TranslationsResource; - -/** - * @author Sean Flanigan sflaniga@redhat.com - * - * @deprecated - * @see org.zanata.client.commands.pull.PullCommand - */ -public class PublicanPullCommand extends - ConfigurableProjectCommand { - private static final Logger log = LoggerFactory - .getLogger(PublicanPullCommand.class); - - private final ISourceDocResource sourceDocResource; - private final ITranslatedDocResource translationResources; - private final URI uri; - - public PublicanPullCommand(PublicanPullOptions opts, - ZanataProxyFactory factory, ISourceDocResource sourceDocResource, - ITranslatedDocResource translationResources, URI uri) { - super(opts, factory); - this.sourceDocResource = sourceDocResource; - this.translationResources = translationResources; - this.uri = uri; - deprecate("please use \"pull\" with project type \"" - + PROJECT_TYPE_PUBLICAN + "\""); - } - - private PublicanPullCommand(PublicanPullOptions opts, - ZanataProxyFactory factory) { - this(opts, factory, factory.getSourceDocResource(opts.getProj(), - opts.getProjectVersion()), factory.getTranslatedDocResource( - opts.getProj(), opts.getProjectVersion()), factory - .getResourceURI(opts.getProj(), opts.getProjectVersion())); - } - - public PublicanPullCommand(PublicanPullOptions opts) { - this(opts, OptionsUtil.createRequestFactory(opts)); - } - - @Override - protected String getProjectType() { - return PROJECT_TYPE_PUBLICAN; - } - - @Override - public void run() throws Exception { - log.info("Server: {}", getOpts().getUrl()); - log.info("Project: {}", getOpts().getProj()); - log.info("Version: {}", getOpts().getProjectVersion()); - log.info("Username: {}", getOpts().getUsername()); - if (getOpts().getExportPot()) { - log.info("Exporting source and target (translation) documents"); - log.info("POT directory (originals): {}", getOpts().getDstDirPot()); - } else { - log.info("Exporting target documents (translations) only"); - } - log.info("PO base directory (translations): {}", getOpts().getDstDir()); - - LocaleList locales = getOpts().getLocaleMapList(); - if (locales == null) - throw new ConfigException("no locales specified"); - PoWriter2 poWriter = new PoWriter2(); - StringSet extensions = new StringSet("gettext;comment"); - - ClientResponse> listResponse = - sourceDocResource.get(null); - ClientUtility.checkResult(listResponse, uri); - List resourceMetaList = listResponse.getEntity(); - for (ResourceMeta resourceMeta : resourceMetaList) { - String docName = resourceMeta.getName(); - // TODO follow a Link - String docUri = RestUtil.convertToDocumentURIId(docName); - ClientResponse resourceResponse = - sourceDocResource.getResource(docUri, extensions); - ClientUtility.checkResult(resourceResponse, uri); - Resource doc = resourceResponse.getEntity(); - if (getOpts().getExportPot()) { - log.info("writing POT for document {}", docName); - poWriter.writePotToDir(getOpts().getDstDirPot(), doc); - } - - for (LocaleMapping locMapping : locales) { - LocaleId locale = new LocaleId(locMapping.getLocale()); - - ClientResponse transResponse = - translationResources.getTranslations(docUri, locale, - extensions); - // ignore 404 (no translation yet for specified document) - if (transResponse.getResponseStatus() == Response.Status.NOT_FOUND) { - log.info( - "no translations found in locale {} for document {}", - locale, docName); - continue; - } - ClientUtility.checkResult(transResponse, uri); - TranslationsResource targetDoc = transResponse.getEntity(); - - String localeDir = locMapping.getLocalLocale(); - log.info( - "writing PO translations in locale {} for document {}", - locale, docName); - poWriter.writePo(getOpts().getDstDir(), doc, localeDir, - targetDoc); - } - } - - } - -} diff --git a/zanata-client-commands/src/main/java/org/zanata/client/commands/PublicanPullOptions.java b/zanata-client-commands/src/main/java/org/zanata/client/commands/PublicanPullOptions.java deleted file mode 100644 index 3825c5ca..00000000 --- a/zanata-client-commands/src/main/java/org/zanata/client/commands/PublicanPullOptions.java +++ /dev/null @@ -1,35 +0,0 @@ -package org.zanata.client.commands; - -import java.io.File; - -import org.kohsuke.args4j.Option; - -/** - * @author Sean Flanigan sflaniga@redhat.com - * - * @deprecated - * @see org.zanata.client.commands.pull.PullOptions - */ -public interface PublicanPullOptions extends ConfigurableProjectOptions { - - @Option( - aliases = { "-d" }, - name = "--dst", - metaVar = "DIR", - required = true, - usage = "Base directory for publican files (with subdirectory \"pot\" and locale directories)") - public - void setDstDir(File dstDir); - - public void setDstDirPot(File dstDirPot); - - public File getDstDir(); - - public File getDstDirPot(); - - boolean getExportPot(); - - void setExportPot(boolean exportPot); - -} diff --git a/zanata-client-commands/src/main/java/org/zanata/client/commands/PublicanPullOptionsImpl.java b/zanata-client-commands/src/main/java/org/zanata/client/commands/PublicanPullOptionsImpl.java deleted file mode 100644 index 5e987dc5..00000000 --- a/zanata-client-commands/src/main/java/org/zanata/client/commands/PublicanPullOptionsImpl.java +++ /dev/null @@ -1,80 +0,0 @@ -package org.zanata.client.commands; - -import java.io.File; - -import org.kohsuke.args4j.Option; - -/** - * @author Sean Flanigan sflaniga@redhat.com - * - * @deprecated - * @see org.zanata.client.commands.pull.PullOptions - */ -public class PublicanPullOptionsImpl extends ConfigurableProjectOptionsImpl - implements PublicanPullOptions { - private File dstDir; - private File dstDirPot; - private boolean exportPot; - - @Override - public ZanataCommand initCommand() { - return new PublicanPullCommand(this); - } - - @Override - public String getCommandName() { - return "publican-pull"; - } - - @Override - public String getCommandDescription() { - return "Pulls translated text from Zanata. DEPRECATED: use 'pull' with projectType 'podir'"; - } - - @Option( - aliases = { "-d" }, - name = "--dst", - metaVar = "DIR", - required = true, - usage = "Base directory for publican files (with subdirectory \"pot\" and locale directories)") - @Override - public - void setDstDir(File dstDir) { - this.dstDir = dstDir; - if (dstDirPot == null) - dstDirPot = new File(dstDir, "pot"); - } - - @Override - public File getDstDir() { - return dstDir; - } - - @Override - public void setDstDirPot(File dstDirPot) { - this.dstDirPot = dstDirPot; - } - - @Override - public File getDstDirPot() { - return dstDirPot; - } - - @Override - public boolean getExportPot() { - return exportPot; - } - - @Override - @Option(name = "--export-pot", - usage = "Export source text from Zanata to local POT files") - public void setExportPot(boolean exportPot) { - this.exportPot = exportPot; - } - - @Override - public boolean isAuthRequired() { - return false; - } -} diff --git a/zanata-client-commands/src/main/java/org/zanata/client/commands/PublicanPushCommand.java b/zanata-client-commands/src/main/java/org/zanata/client/commands/PublicanPushCommand.java deleted file mode 100644 index 3f4a2ce6..00000000 --- a/zanata-client-commands/src/main/java/org/zanata/client/commands/PublicanPushCommand.java +++ /dev/null @@ -1,269 +0,0 @@ -package org.zanata.client.commands; - -import java.io.BufferedInputStream; -import java.io.Console; -import java.io.File; -import java.io.FileInputStream; -import java.io.IOException; -import java.io.StringWriter; -import java.net.URI; -import java.util.HashSet; -import java.util.List; -import java.util.Set; - -import javax.xml.bind.JAXBContext; -import javax.xml.bind.Marshaller; - -import org.apache.commons.io.filefilter.AndFileFilter; -import org.jboss.resteasy.client.ClientResponse; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.xml.sax.InputSource; -import org.zanata.adapter.po.PoReader2; -import org.zanata.client.commands.gettext.PublicanUtil; -import org.zanata.client.config.LocaleMapping; -import org.zanata.common.LocaleId; -import org.zanata.rest.JaxbUtil; -import org.zanata.rest.RestUtil; -import org.zanata.rest.StringSet; -import org.zanata.rest.client.ClientUtility; -import org.zanata.rest.client.ISourceDocResource; -import org.zanata.rest.client.ITranslatedDocResource; -import org.zanata.rest.client.ZanataProxyFactory; -import org.zanata.rest.dto.resource.Resource; -import org.zanata.rest.dto.resource.ResourceMeta; -import org.zanata.rest.dto.resource.TranslationsResource; - -/** - * @author Sean Flanigan sflaniga@redhat.com - * - * @deprecated - * @see org.zanata.client.commands.push.PushCommand - */ -public class PublicanPushCommand extends - ConfigurableProjectCommand { - private static final Logger log = LoggerFactory - .getLogger(PublicanPushCommand.class); - - private final ISourceDocResource sourceDocResource; - private final ITranslatedDocResource translationResources; - private final URI uri; - - public PublicanPushCommand(PublicanPushOptions opts, - ZanataProxyFactory factory, ISourceDocResource sourceDocResource, - ITranslatedDocResource translationResources, URI uri) { - super(opts, factory); - this.sourceDocResource = sourceDocResource; - this.translationResources = translationResources; - this.uri = uri; - deprecate("please use \"push\" with project type \"" - + PROJECT_TYPE_PUBLICAN + "\""); - } - - private PublicanPushCommand(PublicanPushOptions opts, - ZanataProxyFactory factory) { - this(opts, factory, factory.getSourceDocResource(opts.getProj(), - opts.getProjectVersion()), factory.getTranslatedDocResource( - opts.getProj(), opts.getProjectVersion()), factory - .getResourceURI(opts.getProj(), opts.getProjectVersion())); - } - - public PublicanPushCommand(PublicanPushOptions opts) { - this(opts, OptionsUtil.createRequestFactory(opts)); - } - - @Override - protected String getProjectType() { - return PROJECT_TYPE_PUBLICAN; - } - - @Override - public void run() throws Exception { - log.info("Server: {}", getOpts().getUrl()); - log.info("Project: {}", getOpts().getProj()); - log.info("Version: {}", getOpts().getProjectVersion()); - log.info("Username: {}", getOpts().getUsername()); - log.info("Source language: {}", getOpts().getSourceLang()); - log.info("Copy previous translations: {}", getOpts().getCopyTrans()); - log.info("Merge type: {}", getOpts().getMergeType()); - if (getOpts().getImportPo()) { - log.info("Importing source and target documents"); - } else { - log.info("Importing source documents only"); - } - log.info("POT directory (originals): {}", getOpts().getSrcDirPot()); - if (getOpts().getImportPo()) { - log.info("PO base directory (translations): {}", getOpts() - .getSrcDir()); - } - File potDir = getOpts().getSrcDirPot(); - - if (!potDir.exists()) { - throw new RuntimeException("directory '" + potDir - + "' does not exist - check srcDir and srcDirPot options"); - } - - Console console = System.console(); - if (getOpts().isInteractiveMode()) { - if (console == null) - throw new RuntimeException( - "console not available: please run maven from a console, or use batch mode (mvn -B)"); - } - - if (getOpts().getImportPo()) { - log.warn("importPo option is set: existing translations on server will be overwritten/deleted"); - if (getOpts().isInteractiveMode()) { - console.printf("This will overwrite/delete any existing documents AND TRANSLATIONS on the server.\n"); - console.printf("Are you sure (y/n)? "); - expectYes(console); - } - } else if (getOpts().isInteractiveMode()) { - console.printf("This will overwrite/delete any existing documents on the server.\n"); - console.printf("Are you sure (y/n)? "); - expectYes(console); - } - - JAXBContext jc = null; - if (log.isDebugEnabled() || getOpts().getValidate()) { - jc = - JAXBContext.newInstance(Resource.class, - TranslationsResource.class); - } - Marshaller m = null; - if (log.isDebugEnabled()) { - m = jc.createMarshaller(); - m.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true); - } - - // NB we don't load all the docs into a HashMap, because that would - // waste - // memory - Set localDocNames = new HashSet(); - // populate localDocNames by looking in pot directory - - String[] potFiles = - PublicanUtil.findPotFiles(potDir, new AndFileFilter()); - for (String potName : potFiles) { - String docName = StringUtil.removeFileExtension(potName, ".pot"); - localDocNames.add(docName); - } - - ClientResponse> getResponse = - sourceDocResource.get(null); - ClientUtility.checkResult(getResponse, uri); - List remoteDocList = getResponse.getEntity(); - for (ResourceMeta doc : remoteDocList) { - // NB ResourceMeta.name = HDocument.docId - String docName = doc.getName(); - String docUri = RestUtil.convertToDocumentURIId(docName); - if (!localDocNames.contains(docName)) { - log.info("deleting resource {} from server", docName); - ClientResponse deleteResponse = - sourceDocResource.deleteResource(docUri); - ClientUtility.checkResult(deleteResponse, uri); - } - } - List locales = null; - if (getOpts().getImportPo()) { - if (getOpts().getLocaleMapList() != null) { - locales = - PublicanUtil.findLocales(getOpts().getSrcDir(), - getOpts().getLocaleMapList()); - if (locales.size() == 0) { - log.warn("option 'importPo' is set, but none of the configured locale directories was found (check zanata.xml)"); - } - } else { - locales = PublicanUtil.findLocales(getOpts().getSrcDir()); - if (locales.size() == 0) { - log.warn("option 'importPo' is set, but no locale directories were found"); - } else { - log.info("option 'importPo' is set, but no locales specified in configuration: importing " - + locales.size() + " directories"); - } - } - } - - PoReader2 poReader = new PoReader2(); - for (String docName : localDocNames) { - String docUri = RestUtil.convertToDocumentURIId(docName); - File potFile = new File(potDir, docName + ".pot"); - Resource srcDoc; - BufferedInputStream bis = - new BufferedInputStream(new FileInputStream(potFile)); - try { - InputSource potInputSource = new InputSource(bis); - potInputSource.setEncoding("utf8"); - // load 'srcDoc' from pot/${docID}.pot - srcDoc = - poReader.extractTemplate(potInputSource, new LocaleId( - getOpts().getSourceLang()), docName); - } finally { - bis.close(); - } - if (log.isDebugEnabled()) { - StringWriter writer = new StringWriter(); - m.marshal(srcDoc, writer); - log.debug("{}", writer); - } - if (getOpts().getValidate()) { - JaxbUtil.validateXml(srcDoc, jc); - } - StringSet extensions = new StringSet("comment;gettext"); - log.info("pushing source document [name={}] to server", - srcDoc.getName()); - boolean copyTrans = getOpts().getCopyTrans(); - ClientResponse putResponse = - sourceDocResource.putResource(docUri, srcDoc, extensions, - copyTrans); - ClientUtility.checkResult(putResponse, uri); - - if (getOpts().getImportPo()) { - for (LocaleMapping locale : locales) { - File localeDir = - new File(getOpts().getSrcDir(), - locale.getLocalLocale()); - File poFile = new File(localeDir, docName + ".po"); - if (poFile.canRead()) { - TranslationsResource targetDoc; - BufferedInputStream bis2 = - new BufferedInputStream(new FileInputStream( - poFile)); - try { - InputSource inputSource = new InputSource(bis2); - inputSource.setEncoding("utf8"); - targetDoc = poReader.extractTarget(inputSource); - } finally { - bis2.close(); - } - if (log.isDebugEnabled()) { - StringWriter writer = new StringWriter(); - m.marshal(targetDoc, writer); - log.debug("{}", writer); - } - if (getOpts().getValidate()) { - JaxbUtil.validateXml(targetDoc, jc); - } - log.info( - "pushing target document [name={} client-locale={}] to server [locale={}]", - new Object[] { srcDoc.getName(), - locale.getLocalLocale(), - locale.getLocale() }); - ClientResponse putTransResponse = - translationResources.putTranslations(docUri, - new LocaleId(locale.getLocale()), - targetDoc, extensions, getOpts() - .getMergeType()); - ClientUtility.checkResult(putTransResponse, uri); - String entity = - putTransResponse.getEntity(String.class); - if (entity != null && !entity.isEmpty()) { - log.warn("{}", entity); - } - } - } - } - } - } - -} diff --git a/zanata-client-commands/src/main/java/org/zanata/client/commands/PublicanPushOptions.java b/zanata-client-commands/src/main/java/org/zanata/client/commands/PublicanPushOptions.java deleted file mode 100644 index a68f048f..00000000 --- a/zanata-client-commands/src/main/java/org/zanata/client/commands/PublicanPushOptions.java +++ /dev/null @@ -1,26 +0,0 @@ -package org.zanata.client.commands; - -import java.io.File; - -/** - * - * @author Sean Flanigan sflaniga@redhat.com - * @deprecated - * @see org.zanata.client.commands.push.PushOptions - */ -public interface PublicanPushOptions extends ConfigurableProjectOptions { - public File getSrcDir(); - - public File getSrcDirPot(); - - public String getSourceLang(); - - public boolean getImportPo(); - - public boolean getCopyTrans(); - - public boolean getValidate(); - - public String getMergeType(); -} diff --git a/zanata-client-commands/src/main/java/org/zanata/client/commands/PublicanPushOptionsImpl.java b/zanata-client-commands/src/main/java/org/zanata/client/commands/PublicanPushOptionsImpl.java deleted file mode 100644 index 5263e035..00000000 --- a/zanata-client-commands/src/main/java/org/zanata/client/commands/PublicanPushOptionsImpl.java +++ /dev/null @@ -1,131 +0,0 @@ -package org.zanata.client.commands; - -import java.io.File; - -import org.kohsuke.args4j.Option; - -/** - * @author Sean Flanigan sflaniga@redhat.com - * - * @deprecated - * @see org.zanata.client.commands.push.PushOptions - */ -public class PublicanPushOptionsImpl extends ConfigurableProjectOptionsImpl - implements PublicanPushOptions { - private File srcDir; - private File srcDirPot; - - private String sourceLang = "en-US"; - - private boolean importPo; - private boolean copyTrans = true; - private boolean validate; - private String mergeType = "auto"; - - public PublicanPushOptionsImpl() { - super(); - } - - @Override - public String getCommandName() { - return "publican-push"; - } - - @Override - public String getCommandDescription() { - return "Publishes publican source text to Zanata so that it can be translated. DEPRECATED: use 'push' with projectType 'podir'"; - } - - @Override - public PublicanPushCommand initCommand() { - return new PublicanPushCommand(this); - } - - @Option( - aliases = { "-s" }, - name = "--src", - metaVar = "DIR", - required = true, - usage = "Base directory for publican files (with subdirectory \"pot\" and optional locale directories)") - public - void setSrcDir(File srcDir) { - this.srcDir = srcDir; - if (srcDirPot == null) - srcDirPot = new File(srcDir, "pot"); - } - - @Option( - name = "--src-pot", - metaVar = "DIR", - required = false, - usage = "Override base directory for publican POT files (defaults to \"pot\" under --src directory)") - public - void setSrcDirPot(File srcDirPot) { - this.srcDirPot = srcDirPot; - } - - @Option(aliases = { "-l" }, name = "--src-lang", - usage = "Language of source (defaults to en-US)") - public void setSourceLang(String sourceLang) { - this.sourceLang = sourceLang; - } - - @Option( - name = "--import-po", - usage = "Import translations from local PO files to Zanata, overwriting or erasing existing translations (DANGER!)") - public - void setImportPo(boolean importPo) { - this.importPo = importPo; - } - - @Option(name = "--validate", - usage = "Validate XML before sending request to server") - public void setValidate(boolean validate) { - this.validate = validate; - } - - @Override - public boolean getValidate() { - return validate; - } - - @Override - public boolean getImportPo() { - return importPo; - } - - @Override - public boolean getCopyTrans() { - return copyTrans; - } - - @Option( - name = "--no-copy-trans", - usage = "Don't copy latest translation from equivalent documents from other versions of the same project") - public - void setNoCopyTrans(boolean noCopyTrans) { - this.copyTrans = !noCopyTrans; - } - - @Override - public File getSrcDir() { - return srcDir; - } - - @Override - public File getSrcDirPot() { - return srcDirPot; - } - - @Override - public String getSourceLang() { - return sourceLang; - } - - @Override - public String getMergeType() { - return mergeType; - } - -} diff --git a/zanata-client-commands/src/main/java/org/zanata/client/commands/PushPullCommand.java b/zanata-client-commands/src/main/java/org/zanata/client/commands/PushPullCommand.java index a7d41387..9a425fd4 100644 --- a/zanata-client-commands/src/main/java/org/zanata/client/commands/PushPullCommand.java +++ b/zanata-client-commands/src/main/java/org/zanata/client/commands/PushPullCommand.java @@ -21,37 +21,44 @@ package org.zanata.client.commands; -import java.io.Console; import java.io.File; import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.IOException; import java.io.StringWriter; -import java.net.URI; import java.util.ArrayList; import java.util.List; +import java.util.Map; import javax.xml.bind.JAXBContext; import javax.xml.bind.JAXBException; import javax.xml.bind.Marshaller; -import org.jboss.resteasy.client.ClientResponse; +import com.google.common.annotations.VisibleForTesting; +import com.google.common.base.Optional; +import com.google.common.collect.ImmutableMap; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import org.zanata.client.commands.pull.PullOptions; import org.zanata.client.config.LocaleList; import org.zanata.client.config.LocaleMapping; import org.zanata.client.etag.ETagCache; import org.zanata.client.etag.ETagCacheReaderWriter; import org.zanata.client.exceptions.ConfigException; -import org.zanata.rest.client.ClientUtility; -import org.zanata.rest.client.ISourceDocResource; -import org.zanata.rest.client.ITranslatedDocResource; -import org.zanata.rest.client.ZanataProxyFactory; +import org.zanata.common.LocaleId; +import org.zanata.rest.client.RestClientFactory; +import org.zanata.rest.client.SourceDocResourceClient; +import org.zanata.rest.client.StatisticsResourceClient; +import org.zanata.rest.client.TransDocResourceClient; import org.zanata.rest.dto.resource.Resource; import org.zanata.rest.dto.resource.ResourceMeta; import org.zanata.rest.dto.resource.TranslationsResource; +import org.zanata.rest.dto.stats.ContainerTranslationStatistics; +import org.zanata.rest.dto.stats.TranslationStatistics; import org.zanata.util.PathUtil; +import static org.zanata.client.commands.ConsoleInteractor.DisplayMode.Question; + /** * @author Sean Flanigan sflaniga@redhat.com @@ -64,46 +71,38 @@ public abstract class PushPullCommand extends protected static final String PROJECT_TYPE_OFFLINE_PO = "offlinepo"; - protected final ISourceDocResource sourceDocResource; - protected final ITranslatedDocResource translationResources; - protected URI uri; protected ETagCache eTagCache; private Marshaller marshaller; private String modulePrefix; + protected SourceDocResourceClient sourceDocResourceClient; + protected TransDocResourceClient transDocResourceClient; + protected final StatisticsResourceClient statsClient; - public PushPullCommand(O opts, ZanataProxyFactory factory, - ISourceDocResource sourceDocResource, - ITranslatedDocResource translationResources, URI uri) { - super(opts, factory); - this.sourceDocResource = sourceDocResource; - this.translationResources = translationResources; - this.uri = uri; + public PushPullCommand(O opts, RestClientFactory clientFactory) { + super(opts, clientFactory); this.modulePrefix = opts.getEnableModules() ? getOpts().getCurrentModule() + opts.getModuleSuffix() : ""; this.loadETagCache(); - } - - private PushPullCommand(O opts, ZanataProxyFactory factory) { - this(opts, factory, factory.getSourceDocResource(opts.getProj(), - opts.getProjectVersion()), factory.getTranslatedDocResource( - opts.getProj(), opts.getProjectVersion()), factory - .getResourceURI(opts.getProj(), opts.getProjectVersion())); + sourceDocResourceClient = + getClientFactory().getSourceDocResourceClient(opts.getProj(), + opts.getProjectVersion()); + transDocResourceClient = + getClientFactory().getTransDocResourceClient(opts.getProj(), + opts.getProjectVersion()); + statsClient = getClientFactory().getStatisticsClient(); } public PushPullCommand(O opts) { - this(opts, OptionsUtil.createRequestFactory(opts)); + this(opts, OptionsUtil.createClientFactory( + opts)); } protected void confirmWithUser(String message) throws IOException { if (getOpts().isInteractiveMode()) { - Console console = System.console(); - if (console == null) { - throw new RuntimeException( - "console not available: please run Maven from a console, or use batch mode option (-B)"); - } - console.printf(message + "\nAre you sure (y/n)? "); - expectYes(console); + ConsoleInteractor console = new ConsoleInteractorImpl(getOpts()); + console.printf(Question, message + "\nAre you sure (y/n)? "); + console.expectYes(); } } @@ -171,24 +170,22 @@ protected List getQualifiedDocNamesForCurrentModuleFromServer() { // TODO use a cache which will be accessible to all invocations protected List getDocListForProjectIterationFromServer() { - ClientResponse> getResponse = - sourceDocResource.get(null); - ClientUtility.checkResult(getResponse, uri); - List remoteDocList = getResponse.getEntity(); - return remoteDocList; + return sourceDocResourceClient.getResourceMeta(null); } /** * Filters the project's list of locales * * @param projectLocales - * locales defined by the project's zanata.xml + * locales defined by the project on the server or in zanata.xml + * (only for backward compatibility) * @param locales * locales requested by the user (eg Maven param, command line * option) * @return the filtered list of locales * @throws ConfigException - * if one of the locales was not found in zanata.xml + * if one of the requested locales was not found on the server + * or in zanata.xml (only for backward compatibility) */ public static LocaleList getLocaleMapList(LocaleList projectLocales, String[] locales) { @@ -212,7 +209,8 @@ public static LocaleList getLocaleMapList(LocaleList projectLocales, if (!foundLocale) { throw new ConfigException("Specified locale '" + locale - + "' was not found in zanata.xml!"); + + "' was not found! Available locales: " + + projectLocales); } } return effectiveLocales; @@ -254,4 +252,125 @@ protected void storeETagCache() { } } + protected Map> getDocsTranslatedPercent( + LocaleList locales) { + ContainerTranslationStatistics statistics = + getDetailStatisticsForProjectVersion(locales); + List statsPerDoc = + statistics.getDetailedStats(); + ImmutableMap.Builder> docIdToStatsBuilder = + ImmutableMap.builder(); + for (ContainerTranslationStatistics docStats : statsPerDoc) { + String docId = docStats.getId(); + List statsPerLocale = docStats.getStats(); + ImmutableMap.Builder localeToStatsBuilder = + ImmutableMap.builder(); + + for (TranslationStatistics statsForSingleLocale : statsPerLocale) { + // TODO server statistics API should return locale with alias + TranslatedPercent translatedPercent = + new TranslatedPercent(statsForSingleLocale.getTotal(), + statsForSingleLocale.getTranslatedOnly(), + statsForSingleLocale.getApproved()); + + localeToStatsBuilder.put( + new LocaleId(statsForSingleLocale.getLocale()), + translatedPercent); + } + Map localeStats = + localeToStatsBuilder.build(); + docIdToStatsBuilder.put(docId, localeStats); + } + return docIdToStatsBuilder.build(); + } + + @VisibleForTesting + protected ContainerTranslationStatistics getDetailStatisticsForProjectVersion( + LocaleList locales) { + String[] localesOnServer = new String[locales.size()]; + for (int i = 0; i < locales.size(); i++) { + localesOnServer[i] = locales.get(i).getLocale(); + } + return statsClient + .getStatistics(getOpts().getProj(), + getOpts().getProjectVersion(), true, false, localesOnServer); + } + + /** + * this stats map will have docId as key, the value is another map with + * localeId as key and translated percent as value. + * It's optional if we require statistics to determine which file to pull. + * In cases where statistics is not required, + * i.e. pull source only or minimum percent is set to 0, this will be + * Optional.absence(). + * + * @param pullTarget whether we need to pull translation target + * @param locales + * @return either detailed document statistics or optional.absence() + */ + protected Optional>> prepareStatsIfApplicable( + boolean pullTarget, LocaleList locales) { + + Optional>> optionalStats = + Optional.absent(); + if (needToGetStatistics(pullTarget)) { + optionalStats = Optional.of(getDocsTranslatedPercent(locales)); + } + return optionalStats; + } + + protected boolean needToGetStatistics(boolean pullTarget) { + return pullTarget && getOpts() instanceof PullOptions && + ((PullOptions) getOpts()).getMinDocPercent() > 0; + } + + protected boolean shouldPullThisLocale( + Optional>> optionalStats, + String localDocName, LocaleId serverLocale) { + int minDocPercent = ((PullOptions) getOpts()).getMinDocPercent(); + if (log.isDebugEnabled() && optionalStats.isPresent()) { + log.debug("{} for locale {} is translated {}%", localDocName, + serverLocale, optionalStats.get() + .get(localDocName).get(serverLocale) + .getTranslatedPercent()); + } + return !optionalStats.isPresent() + || optionalStats.get().get(localDocName).get(serverLocale) + .isAboveThreshold(minDocPercent); + } + + protected static class TranslatedPercent { + private final double translatedPercent; + private final long total; + private final long translated; + private final long approved; + + public TranslatedPercent(long total, long translated, long approved) { + this.total = total; + this.translated = translated; + this.approved = approved; + if (total == 0) { + // in some case the document has no content, we want to pull the + // translation file regardless + translatedPercent = 100; + } else { + translatedPercent = (translated + approved) * 100.0 / total; + + } + } + + public boolean isAboveThreshold(int minimumPercent) { + // if minimum percent is 100, we will compare exact number so that + // rounding issue won't affect the result + if (minimumPercent == 100) { + return total == translated + approved; + } else { + return translatedPercent >= minimumPercent; + } + } + + public double getTranslatedPercent() { + return translatedPercent; + } + } } diff --git a/zanata-client-commands/src/main/java/org/zanata/client/commands/PushPullOptions.java b/zanata-client-commands/src/main/java/org/zanata/client/commands/PushPullOptions.java index dbd8d417..bd0763ab 100644 --- a/zanata-client-commands/src/main/java/org/zanata/client/commands/PushPullOptions.java +++ b/zanata-client-commands/src/main/java/org/zanata/client/commands/PushPullOptions.java @@ -55,7 +55,7 @@ public interface PushPullOptions extends ConfigurableProjectOptions { File getTransDir(); - public String getFromDoc(); + String getFromDoc(); /** * This name should represent the exact parameter as it would be entered on @@ -63,7 +63,7 @@ public interface PushPullOptions extends ConfigurableProjectOptions { * parameter to the argument. This is so that the argument can be appended * directly to the parameter name. */ - public String buildFromDocArgument(String argValue); + String buildFromDocArgument(String argValue); boolean getEnableModules(); diff --git a/zanata-client-commands/src/main/java/org/zanata/client/commands/PutProjectCommand.java b/zanata-client-commands/src/main/java/org/zanata/client/commands/PutProjectCommand.java index 4f1fc468..ffd8a045 100644 --- a/zanata-client-commands/src/main/java/org/zanata/client/commands/PutProjectCommand.java +++ b/zanata-client-commands/src/main/java/org/zanata/client/commands/PutProjectCommand.java @@ -1,16 +1,8 @@ package org.zanata.client.commands; -import java.io.IOException; -import java.net.URI; -import java.net.URISyntaxException; - -import javax.xml.bind.JAXBException; - -import org.jboss.resteasy.client.ClientResponse; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import org.zanata.rest.client.ClientUtility; -import org.zanata.rest.client.IProjectResource; +import org.zanata.rest.client.ProjectClient; import org.zanata.rest.dto.Project; /** @@ -26,8 +18,7 @@ public PutProjectCommand(PutProjectOptions opts) { } @Override - public void run() throws JAXBException, URISyntaxException, IOException, - Exception { + public void run() throws Exception { Project project = new Project(); project.setId(getOpts().getProjectSlug()); project.setName(getOpts().getProjectName()); @@ -45,11 +36,8 @@ public void run() throws JAXBException, URISyntaxException, IOException, log.debug("{}", project); // send project to rest api - IProjectResource projResource = - getRequestFactory().getProject(getOpts().getProjectSlug()); - URI uri = getRequestFactory().getProjectURI(getOpts().getProjectSlug()); - - ClientResponse response = projResource.put(project); - ClientUtility.checkResult(response, uri); + ProjectClient client = getClientFactory().getProjectClient( + getOpts().getProjectSlug()); + client.put(project); } } diff --git a/zanata-client-commands/src/main/java/org/zanata/client/commands/PutUserCommand.java b/zanata-client-commands/src/main/java/org/zanata/client/commands/PutUserCommand.java index 27cba5cc..62a6e59f 100644 --- a/zanata-client-commands/src/main/java/org/zanata/client/commands/PutUserCommand.java +++ b/zanata-client-commands/src/main/java/org/zanata/client/commands/PutUserCommand.java @@ -1,16 +1,13 @@ package org.zanata.client.commands; -import java.net.URI; -import java.util.Arrays; -import java.util.HashSet; - -import org.jboss.resteasy.client.ClientResponse; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import org.zanata.rest.client.ClientUtility; -import org.zanata.rest.client.IAccountResource; +import org.zanata.rest.client.AccountClient; import org.zanata.rest.dto.Account; +import com.google.common.base.Splitter; +import com.google.common.collect.Sets; + /** * @author Sean Flanigan * @@ -31,19 +28,15 @@ public void run() throws Exception { account.setPasswordHash(getOpts().getUserPasswordHash()); account.setApiKey(getOpts().getUserKey()); account.setEnabled(!getOpts().isUserDisabled()); - account.setRoles(new HashSet(Arrays.asList(getOpts() - .getUserRoles().split(",")))); - account.setTribes(new HashSet(Arrays.asList(getOpts() - .getUserLangs().split(",")))); + Splitter splitter = Splitter.on(",").trimResults().omitEmptyStrings(); + account.setRoles(Sets.newHashSet(splitter.split(getOpts().getUserRoles()))); + account.setTribes( + Sets.newHashSet(splitter.split(getOpts().getUserLangs()))); log.debug("{}", account); - IAccountResource iterResource = - getRequestFactory().getAccount(getOpts().getUserUsername()); - URI uri = - getRequestFactory().getAccountURI(getOpts().getUserUsername()); - ClientResponse response = iterResource.put(account); - ClientUtility.checkResult(response, uri); + getClientFactory().getAccountClient().put( + getOpts().getUserUsername(), account); } } diff --git a/zanata-client-commands/src/main/java/org/zanata/client/commands/PutVersionCommand.java b/zanata-client-commands/src/main/java/org/zanata/client/commands/PutVersionCommand.java index 05335308..805b0993 100644 --- a/zanata-client-commands/src/main/java/org/zanata/client/commands/PutVersionCommand.java +++ b/zanata-client-commands/src/main/java/org/zanata/client/commands/PutVersionCommand.java @@ -1,12 +1,8 @@ package org.zanata.client.commands; -import java.net.URI; - -import org.jboss.resteasy.client.ClientResponse; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import org.zanata.rest.client.ClientUtility; -import org.zanata.rest.client.IProjectIterationResource; +import org.zanata.rest.client.ProjectIterationClient; import org.zanata.rest.dto.ProjectIteration; /** @@ -35,16 +31,12 @@ public void run() throws Exception { } log.debug("{}", version); - IProjectIterationResource iterResource = - getRequestFactory().getProjectIteration( - getOpts().getVersionProject(), - getOpts().getVersionSlug()); - URI uri = - getRequestFactory().getProjectIterationURI( + ProjectIterationClient client = getClientFactory() + .getProjectIterationClient( getOpts().getVersionProject(), getOpts().getVersionSlug()); - ClientResponse response = iterResource.put(version); - ClientUtility.checkResult(response, uri); + + client.put(version); } } diff --git a/zanata-client-commands/src/main/java/org/zanata/client/commands/SystemExitStrategy.java b/zanata-client-commands/src/main/java/org/zanata/client/commands/SystemExitStrategy.java index bf794cfd..6957531e 100644 --- a/zanata-client-commands/src/main/java/org/zanata/client/commands/SystemExitStrategy.java +++ b/zanata-client-commands/src/main/java/org/zanata/client/commands/SystemExitStrategy.java @@ -1,6 +1,6 @@ package org.zanata.client.commands; -@edu.umd.cs.findbugs.annotations.SuppressWarnings({ "DM_EXIT" }) +@edu.umd.cs.findbugs.annotations.SuppressFBWarnings({ "DM_EXIT" }) public class SystemExitStrategy implements AppAbortStrategy { @Override public void abort(String msg) { diff --git a/zanata-client-commands/src/main/java/org/zanata/client/commands/TransFileResolver.java b/zanata-client-commands/src/main/java/org/zanata/client/commands/TransFileResolver.java index 62b2731d..96baf5fa 100644 --- a/zanata-client-commands/src/main/java/org/zanata/client/commands/TransFileResolver.java +++ b/zanata-client-commands/src/main/java/org/zanata/client/commands/TransFileResolver.java @@ -80,16 +80,16 @@ public TransFileResolver(ConfigurableProjectOptions opts) { * @return translation destination */ public File resolveTransFile(QualifiedSrcDocName qualifiedSrcDocName, - LocaleMapping localeMapping) { + LocaleMapping localeMapping, Optional translationFileExtension) { Optional fileOptional = tryGetTransFileFromProjectMappingRules(qualifiedSrcDocName, - localeMapping); + localeMapping, translationFileExtension); if (fileOptional.isPresent()) { return fileOptional.get(); } else { ProjectType projectType = getProjectType(); return getDefaultTransFileFromProjectType(qualifiedSrcDocName, - localeMapping, projectType); + localeMapping, projectType, translationFileExtension); } } @@ -107,7 +107,8 @@ public File getTransFile(UnqualifiedSrcDocName unqualifiedSrcDocName, LocaleMapping localeMapping) { QualifiedSrcDocName qualifiedSrcDocName = unqualifiedSrcDocName.toQualifiedDocName(getProjectType()); - return resolveTransFile(qualifiedSrcDocName, localeMapping); + return resolveTransFile(qualifiedSrcDocName, localeMapping, + Optional.absent()); } private ProjectType getProjectType() { @@ -121,17 +122,18 @@ private ProjectType getProjectType() { private File getDefaultTransFileFromProjectType( QualifiedSrcDocName qualifiedSrcDocName, LocaleMapping localeMapping, - ProjectType projectType) { + ProjectType projectType, Optional translationFileExtension) { FileMappingRule rule = PROJECT_TYPE_FILE_MAPPING_RULES.get(projectType); checkState(rule != null, _("no.default.mapping"), projectType); String relativePath = new FileMappingRuleHandler(rule, projectType, opts) .getRelativeTransFilePathForSourceDoc(qualifiedSrcDocName, - localeMapping); + localeMapping, translationFileExtension); return new File(opts.getTransDir(), relativePath); } private Optional tryGetTransFileFromProjectMappingRules( - QualifiedSrcDocName qualifiedSrcDocName, LocaleMapping localeMapping) { + QualifiedSrcDocName qualifiedSrcDocName, LocaleMapping localeMapping, + Optional translationFileExtension) { List fileMappingRules = opts.getFileMappingRules(); // TODO may need to sort the rules. put rules without pattern to last for (FileMappingRule rule : fileMappingRules) { @@ -141,7 +143,7 @@ private Optional tryGetTransFileFromProjectMappingRules( String relativePath = handler .getRelativeTransFilePathForSourceDoc( qualifiedSrcDocName, - localeMapping); + localeMapping, translationFileExtension); return Optional.of(new File(opts.getTransDir(), relativePath)); } } diff --git a/zanata-client-commands/src/main/java/org/zanata/client/commands/UpdateChecker.java b/zanata-client-commands/src/main/java/org/zanata/client/commands/UpdateChecker.java index b4d3f61f..aa523f6a 100644 --- a/zanata-client-commands/src/main/java/org/zanata/client/commands/UpdateChecker.java +++ b/zanata-client-commands/src/main/java/org/zanata/client/commands/UpdateChecker.java @@ -20,19 +20,17 @@ */ package org.zanata.client.commands; -import java.io.BufferedReader; +import static org.zanata.client.commands.ConsoleInteractorImpl.AnswerValidator; +import static org.zanata.client.commands.Messages._; +import static org.zanata.util.VersionUtility.getVersionInfo; + import java.io.BufferedWriter; import java.io.File; import java.io.FileInputStream; -import java.io.FileReader; import java.io.IOException; import java.io.InputStreamReader; import java.util.regex.Matcher; import java.util.regex.Pattern; -import javax.ws.rs.client.Client; -import javax.ws.rs.client.ClientBuilder; -import javax.ws.rs.client.WebTarget; -import javax.ws.rs.core.Response; import org.apache.commons.io.output.FileWriterWithEncoding; import org.fedorahosted.openprops.Properties; @@ -43,15 +41,16 @@ import org.joda.time.format.DateTimeFormatter; import org.slf4j.Logger; import org.slf4j.LoggerFactory; + import com.google.common.annotations.VisibleForTesting; import com.google.common.base.Charsets; import com.google.common.base.Optional; import com.google.common.base.Preconditions; import com.google.common.base.Throwables; - -import static org.zanata.client.commands.ConsoleInteractorImpl.*; -import static org.zanata.client.commands.Messages._; -import static org.zanata.util.VersionUtility.getVersionInfo; +import com.sun.jersey.api.client.Client; +import com.sun.jersey.api.client.ClientResponse; +import com.sun.jersey.api.client.WebResource; +import com.sun.jersey.api.client.config.DefaultClientConfig; /** * This class checks whether there is newer version of client available. It will @@ -189,8 +188,7 @@ public void checkNewerVersion() { props.setProperty(LAST_CHECKED, today); props.store(new BufferedWriter(new FileWriterWithEncoding( updateMarker, Charsets.UTF_8)), null); - } - catch (IOException e) { + } catch (IOException e) { log.warn("failed to load file {}", updateMarker); } } @@ -202,19 +200,23 @@ public void checkNewerVersion() { * @return latest version of client in sonatype oss */ private Optional checkLatestVersion(ConsoleInteractor console) { - Response response; + ClientResponse response; try { - Client client = ClientBuilder.newBuilder().build(); - WebTarget target = - client.target(sonatypeRestUrl) + DefaultClientConfig clientConfig = + new DefaultClientConfig(); + + Client client = com.sun.jersey.api.client.Client.create( + clientConfig); + WebResource target = + client.resource(sonatypeRestUrl) .path("artifact/maven/resolve") .queryParam("g", "org.zanata") .queryParam("a", "client") .queryParam("p", "pom") .queryParam("v", "LATEST") .queryParam("r", "releases"); - response = target.request().get(); - if (response.getStatus() != Response.Status.OK.getStatusCode()) { + response = target.get(ClientResponse.class); + if (response.getClientResponseStatus() != ClientResponse.Status.OK) { log.debug( "Failed to resolve latest client artifact [status {}]. Ignored", response.getStatus()); @@ -228,7 +230,7 @@ private Optional checkLatestVersion(ConsoleInteractor console) { } // cheap xml parsing String payload = - response.readEntity(String.class).replaceAll("\\n", ""); + response.getEntity(String.class).replaceAll("\\n", ""); Pattern pattern = Pattern.compile("^.+(.+).+"); Matcher matcher = pattern.matcher(payload); return matcher.matches() ? Optional.of(matcher.group(1)) : Optional diff --git a/zanata-client-commands/src/main/java/org/zanata/client/commands/gettext/PublicanUtil.java b/zanata-client-commands/src/main/java/org/zanata/client/commands/gettext/PublicanUtil.java index cc32272f..7cb92b76 100644 --- a/zanata-client-commands/src/main/java/org/zanata/client/commands/gettext/PublicanUtil.java +++ b/zanata-client-commands/src/main/java/org/zanata/client/commands/gettext/PublicanUtil.java @@ -2,21 +2,13 @@ import java.io.File; import java.io.FileFilter; -import java.io.IOException; import java.util.ArrayList; -import java.util.Collection; -import java.util.Iterator; import java.util.List; -import org.apache.commons.io.FileUtils; -import org.apache.commons.io.filefilter.AndFileFilter; -import org.apache.commons.io.filefilter.SuffixFileFilter; -import org.apache.commons.io.filefilter.TrueFileFilter; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.zanata.client.config.LocaleList; import org.zanata.client.config.LocaleMapping; -import org.zanata.util.PathUtil; /** * @@ -31,25 +23,6 @@ public class PublicanUtil { private PublicanUtil() { } - public static String[] findPotFiles(File potDir, AndFileFilter fileFilter) - throws IOException { - SuffixFileFilter extensionFilter = new SuffixFileFilter(".pot"); - fileFilter.addFileFilter(extensionFilter); - - Collection files = - FileUtils.listFiles(potDir, fileFilter, TrueFileFilter.TRUE); - - String[] potFiles = new String[files.size()]; - Iterator iter = files.iterator(); - - for (int i = 0; i < potFiles.length; i++) { - File potFile = iter.next(); - String relativePath = PathUtil.getSubPath(potFile, potDir); - potFiles[i] = relativePath; - } - return potFiles; - } - public static File[] findLocaleDirs(File srcDir) { File[] localeDirs; localeDirs = srcDir.listFiles(new FileFilter() { diff --git a/zanata-client-commands/src/main/java/org/zanata/client/commands/glossary/delete/GlossaryDeleteCommand.java b/zanata-client-commands/src/main/java/org/zanata/client/commands/glossary/delete/GlossaryDeleteCommand.java index c234ebcb..da6c98e6 100644 --- a/zanata-client-commands/src/main/java/org/zanata/client/commands/glossary/delete/GlossaryDeleteCommand.java +++ b/zanata-client-commands/src/main/java/org/zanata/client/commands/glossary/delete/GlossaryDeleteCommand.java @@ -22,15 +22,13 @@ import org.apache.commons.lang.StringUtils; -import org.jboss.resteasy.client.ClientResponse; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.zanata.client.commands.ConfigurableCommand; import org.zanata.client.commands.OptionsUtil; import org.zanata.common.LocaleId; -import org.zanata.rest.client.ClientUtility; -import org.zanata.rest.client.IGlossaryResource; -import org.zanata.rest.client.ZanataProxyFactory; +import org.zanata.rest.client.GlossaryClient; +import org.zanata.rest.client.RestClientFactory; /** * @@ -41,22 +39,16 @@ public class GlossaryDeleteCommand extends ConfigurableCommand { private static final Logger log = LoggerFactory .getLogger(GlossaryDeleteCommand.class); - - private final IGlossaryResource glossaryResource; + private final GlossaryClient glossaryClient; public GlossaryDeleteCommand(GlossaryDeleteOptions opts, - ZanataProxyFactory factory, IGlossaryResource glossaryResource) { - super(opts, factory); - this.glossaryResource = glossaryResource; - } - - private GlossaryDeleteCommand(GlossaryDeleteOptions opts, - ZanataProxyFactory factory) { - this(opts, factory, factory.getGlossaryResource()); + RestClientFactory clientFactory) { + super(opts, clientFactory); + glossaryClient = getClientFactory().getGlossaryClient(); } public GlossaryDeleteCommand(GlossaryDeleteOptions opts) { - this(opts, OptionsUtil.createRequestFactory(opts)); + this(opts, OptionsUtil.createClientFactory(opts)); } @Override @@ -66,17 +58,14 @@ public void run() throws Exception { log.info("Locale to delete: {}", getOpts().getlang()); log.info("Delete entire glossary?: {}", getOpts().getAllGlossary()); - ClientResponse response; - if (getOpts().getAllGlossary()) { - response = glossaryResource.deleteGlossaries(); + glossaryClient.deleteAll(); } else if (!StringUtils.isEmpty(getOpts().getlang())) { - response = - glossaryResource.deleteGlossary(new LocaleId(getOpts() - .getlang())); + glossaryClient.delete(new LocaleId(getOpts() + .getlang())); } else { throw new RuntimeException("Option 'zanata.lang' is required."); } - ClientUtility.checkResult(response); } } + diff --git a/zanata-client-commands/src/main/java/org/zanata/client/commands/glossary/push/GlossaryPushCommand.java b/zanata-client-commands/src/main/java/org/zanata/client/commands/glossary/push/GlossaryPushCommand.java index 3fdcc8f8..c085b482 100644 --- a/zanata-client-commands/src/main/java/org/zanata/client/commands/glossary/push/GlossaryPushCommand.java +++ b/zanata-client-commands/src/main/java/org/zanata/client/commands/glossary/push/GlossaryPushCommand.java @@ -31,7 +31,6 @@ import org.apache.commons.io.FilenameUtils; import org.apache.commons.lang.StringUtils; -import org.jboss.resteasy.client.ClientResponse; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.zanata.adapter.glossary.AbstractGlossaryPushReader; @@ -41,9 +40,8 @@ import org.zanata.client.commands.OptionsUtil; import org.zanata.client.config.LocaleMapping; import org.zanata.common.LocaleId; -import org.zanata.rest.client.ClientUtility; -import org.zanata.rest.client.IGlossaryResource; -import org.zanata.rest.client.ZanataProxyFactory; +import org.zanata.rest.client.GlossaryClient; +import org.zanata.rest.client.RestClientFactory; import org.zanata.rest.dto.Glossary; /** @@ -58,21 +56,17 @@ public class GlossaryPushCommand extends private static final Map glossaryReaders = new HashMap(); - private final IGlossaryResource glossaryResource; + private final GlossaryClient client; public GlossaryPushCommand(GlossaryPushOptions opts, - ZanataProxyFactory factory, IGlossaryResource glossaryResource) { - super(opts, factory); - this.glossaryResource = glossaryResource; + RestClientFactory clientFactory) { + super(opts, clientFactory); + client = getClientFactory().getGlossaryClient(); } - private GlossaryPushCommand(GlossaryPushOptions opts, - ZanataProxyFactory factory) { - this(opts, factory, factory.getGlossaryResource()); - } public GlossaryPushCommand(GlossaryPushOptions opts) { - this(opts, OptionsUtil.createRequestFactory(opts)); + this(opts, OptionsUtil.createClientFactory(opts)); glossaryReaders.put("po", new GlossaryPoReader( getLocaleFromMap(getOpts().getSourceLang()), @@ -162,9 +156,7 @@ public void run() throws Exception { int totalDone = 0; for (Glossary glossary : glossaries) { log.debug(glossary.toString()); - ClientResponse response = glossaryResource.put(glossary); - ClientUtility.checkResult(response); - response.releaseConnection(); + client.put(glossary); totalDone = totalDone + glossary.getGlossaryEntries().size(); log.info("Pushed " + totalDone + " of " + totalEntries + " entries"); } diff --git a/zanata-client-commands/src/main/java/org/zanata/client/commands/init/InitCommand.java b/zanata-client-commands/src/main/java/org/zanata/client/commands/init/InitCommand.java index 53a2d927..ade6664d 100644 --- a/zanata-client-commands/src/main/java/org/zanata/client/commands/init/InitCommand.java +++ b/zanata-client-commands/src/main/java/org/zanata/client/commands/init/InitCommand.java @@ -40,7 +40,6 @@ import org.apache.commons.io.FileUtils; import org.apache.log4j.Level; import org.apache.log4j.LogManager; -import org.jboss.resteasy.client.ClientResponse; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.zanata.client.commands.ConfigurableCommand; @@ -50,11 +49,13 @@ import org.zanata.client.commands.OptionsUtil; import org.zanata.client.config.ZanataConfig; import org.zanata.client.util.VersionComparator; -import org.zanata.rest.client.ZanataProxyFactory; +import org.zanata.rest.client.ProjectIterationClient; +import org.zanata.rest.client.RestClientFactory; import com.google.common.annotations.VisibleForTesting; import com.google.common.base.Preconditions; import com.google.common.base.Strings; +import com.google.common.base.Throwables; /** * @author Patrick Huang { private static final Logger log = LoggerFactory .getLogger(InitCommand.class); - private static final ZanataProxyFactory mockFactory = - MockZanataProxyFactory.mockFactory; private static final String ITERATION_URL = "%siteration/view/%s/%s"; private ConsoleInteractor console; - private ZanataProxyFactory requestFactory; private ProjectConfigHandler projectConfigHandler; private UserConfigHandler userConfigHandler; public InitCommand(InitOptions opts) { - // we don't have all mandatory information yet. Can't create a real - // proxy factory. - super(opts, mockFactory); - console = new ConsoleInteractorImpl(); + // we don't have all mandatory information yet (server URL etc) + super(opts, new RestClientFactory() {}); + console = new ConsoleInteractorImpl(opts); projectConfigHandler = new ProjectConfigHandler(console, getOpts()); userConfigHandler = new UserConfigHandler(console, getOpts()); } + @VisibleForTesting + protected InitCommand(InitOptions opts, ConsoleInteractor console) { + this(opts, console, new RestClientFactory() {}); + } + @VisibleForTesting protected InitCommand(InitOptions opts, ConsoleInteractor console, - ZanataProxyFactory requestFactory) { - super(opts, mockFactory); + RestClientFactory restClientFactory) { + super(opts, restClientFactory); this.console = console; - this.requestFactory = requestFactory; projectConfigHandler = new ProjectConfigHandler(console, getOpts()); userConfigHandler = new UserConfigHandler(console, getOpts()); @@ -98,6 +99,8 @@ protected void run() throws Exception { // Search for zanata.ini userConfigHandler.verifyUserConfig(); + setClientFactory(OptionsUtil.createClientFactory(getOpts())); + ensureServerVersion(); // If there's a zanata.xml, ask the user @@ -105,9 +108,8 @@ protected void run() throws Exception { // Select or create a project and version new ProjectPrompt(console, getOpts(), - getRequestFactory(), new ProjectIterationPrompt(console, getOpts(), - getRequestFactory())) + getClientFactory()), getClientFactory()) .selectOrCreateNewProjectAndVersion(); advancedSettingsReminder(); @@ -137,7 +139,7 @@ protected void run() throws Exception { @VisibleForTesting protected void ensureServerVersion() { String serverVersion = - getRequestFactory().getServerVersionInfo().getVersionNo(); + getClientFactory().getServerVersionInfo().getVersionNo(); if (new VersionComparator().compare(serverVersion, "3.4.0") < 0) { console.printfln(Warning, _("server.incompatible")); @@ -205,24 +207,14 @@ private static String getProjectIterationUrl(URL server, String projectSlug, } - public static void offerRetryOnServerError(ClientResponse response, + public static void offerRetryOnServerError(Exception e, ConsoleInteractor consoleInteractor) { consoleInteractor.printfln(Warning, _("server.error"), - response.getEntity(String.class)); + Throwables.getRootCause(e).getMessage()); consoleInteractor.printf(Question, _("server.error.try.again")); consoleInteractor.expectYes(); } - public ZanataProxyFactory getRequestFactory() { - if (requestFactory != null) { - return requestFactory; - } else { - requestFactory = OptionsUtil.createRequestFactory(getOpts()); - console.blankLine(); - return requestFactory; - } - } - /** * Downloads the zanata.xml config file using REST api. * @@ -236,20 +228,23 @@ public ZanataProxyFactory getRequestFactory() { @VisibleForTesting protected void downloadZanataXml(String projectId, String iterationId, File configFileDest) throws IOException { - ClientResponse response = - getRequestFactory().getProjectIteration(projectId, iterationId) - .sampleConfiguration(); - if (response.getStatus() >= 399) { - offerRetryOnServerError(response, console); + ProjectIterationClient projectIterationClient = getClientFactory() + .getProjectIterationClient(projectId, iterationId); + + String content; + try { + content = projectIterationClient.sampleConfiguration(); + } catch (Exception e) { + offerRetryOnServerError(e, console); downloadZanataXml(projectId, iterationId, configFileDest); return; } + boolean created = configFileDest.createNewFile(); Preconditions.checkState(created, "Can not create %s. Make sure permission is writable.", configFileDest); - String content = (String) response.getEntity(String.class); log.debug("project config from the server:\n{}", content); FileUtils.write(configFileDest, content, UTF_8); getOpts().setProjectConfig(configFileDest); @@ -281,13 +276,4 @@ protected void writeToConfig(File srcDir, String includes, String excludes, getOpts().getProjectConfig()); } - // we don't have all mandatory information yet - private static class MockZanataProxyFactory extends ZanataProxyFactory { - private static final MockZanataProxyFactory mockFactory = - new MockZanataProxyFactory(); - - private MockZanataProxyFactory() { - super(); - } - } } diff --git a/zanata-client-commands/src/main/java/org/zanata/client/commands/init/ProjectIterationPrompt.java b/zanata-client-commands/src/main/java/org/zanata/client/commands/init/ProjectIterationPrompt.java index ae81bda1..2d1e60e9 100644 --- a/zanata-client-commands/src/main/java/org/zanata/client/commands/init/ProjectIterationPrompt.java +++ b/zanata-client-commands/src/main/java/org/zanata/client/commands/init/ProjectIterationPrompt.java @@ -20,25 +20,31 @@ */ package org.zanata.client.commands.init; +import static com.google.common.base.Strings.isNullOrEmpty; +import static org.zanata.client.commands.ConsoleInteractor.DisplayMode.Confirmation; +import static org.zanata.client.commands.ConsoleInteractor.DisplayMode.Hint; +import static org.zanata.client.commands.ConsoleInteractor.DisplayMode.Question; +import static org.zanata.client.commands.ConsoleInteractorImpl.AnswerValidatorImpl.expect; +import static org.zanata.client.commands.Messages._; + import java.util.List; -import org.jboss.resteasy.client.ClientResponse; import org.zanata.client.commands.ConsoleInteractor; +import org.zanata.client.commands.ConsoleInteractorImpl; import org.zanata.common.EntityStatus; -import org.zanata.rest.client.ZanataProxyFactory; +import org.zanata.rest.client.ProjectClient; +import org.zanata.rest.client.ProjectIterationClient; +import org.zanata.rest.client.RestClientFactory; import org.zanata.rest.dto.Project; import org.zanata.rest.dto.ProjectIteration; import com.google.common.annotations.VisibleForTesting; +import com.google.common.base.Joiner; import com.google.common.base.Predicate; +import com.google.common.base.Strings; import com.google.common.collect.Iterables; import com.google.common.collect.Lists; - -import static org.zanata.client.commands.ConsoleInteractor.DisplayMode.Confirmation; -import static org.zanata.client.commands.ConsoleInteractor.DisplayMode.Hint; -import static org.zanata.client.commands.ConsoleInteractor.DisplayMode.Question; -import static org.zanata.client.commands.ConsoleInteractorImpl.AnswerValidatorImpl.expect; -import static org.zanata.client.commands.Messages._; +import com.sun.jersey.api.client.UniformInterfaceException; /** * @author Patrick Huang versionIndexes = Lists.newArrayList(); @@ -87,15 +92,33 @@ protected void selectVersion() { consoleInteractor .printf("%d)", oneBasedIndex) .printfln(Hint, iteration.getId()); + oneBasedIndex++; } consoleInteractor.printf(Question, _("select.version.prompt")); String selection = consoleInteractor.expectAnswerWithRetry(expect(versionIndexes)); - String versionId = - Iterables.get(activeIterations, - (Integer.valueOf(selection) - 1)) - .getId(); + ProjectIteration projectIteration = Iterables.get(activeIterations, + (Integer.parseInt(selection) - 1)); + String versionId = projectIteration.getId(); opts.setProjectVersion(versionId); + opts.setProjectType(resolveProjectType(project, projectIteration)); + } + + private String resolveProjectType(Project project, + ProjectIteration projectIteration) { + if (!isNullOrEmpty(projectIteration.getProjectType())) { + return projectIteration.getProjectType().toLowerCase(); + } else if (!isNullOrEmpty(project.getDefaultType())) { + return project.getDefaultType().toLowerCase(); + } else { + String projectTypes = + Joiner.on(", ").join(ProjectPrompt.PROJECT_TYPE_LIST); + consoleInteractor.printfln(Question, _("project.type.prompt"), + projectTypes); + return consoleInteractor.expectAnswerWithRetry( + ConsoleInteractorImpl.AnswerValidatorImpl + .expect(ProjectPrompt.PROJECT_TYPE_LIST)); + } } @VisibleForTesting @@ -104,14 +127,13 @@ protected void createNewVersion() { String versionId = consoleInteractor.expectAnyAnswer(); ProjectIteration iteration = new ProjectIteration(versionId); iteration.setProjectType(opts.getProjectType()); - ClientResponse response = - proxyFactory.getProjectIteration(opts.getProj(), versionId) - .put(iteration); - if (response.getStatus() >= 399) { - InitCommand.offerRetryOnServerError(response, consoleInteractor); + try { + clientFactory.getProjectIterationClient(opts.getProj(), versionId) + .put(iteration); + } catch (UniformInterfaceException ex) { + InitCommand.offerRetryOnServerError(ex, consoleInteractor); createNewVersion(); } - response.releaseConnection(); opts.setProjectVersion(versionId); consoleInteractor.printfln(Confirmation, _("project.version.created")); diff --git a/zanata-client-commands/src/main/java/org/zanata/client/commands/init/ProjectPrompt.java b/zanata-client-commands/src/main/java/org/zanata/client/commands/init/ProjectPrompt.java index e707b151..dbe9273a 100644 --- a/zanata-client-commands/src/main/java/org/zanata/client/commands/init/ProjectPrompt.java +++ b/zanata-client-commands/src/main/java/org/zanata/client/commands/init/ProjectPrompt.java @@ -20,15 +20,23 @@ */ package org.zanata.client.commands.init; +import static org.zanata.client.commands.ConsoleInteractor.DisplayMode.Confirmation; +import static org.zanata.client.commands.ConsoleInteractor.DisplayMode.Hint; +import static org.zanata.client.commands.ConsoleInteractor.DisplayMode.Question; +import static org.zanata.client.commands.ConsoleInteractorImpl.AnswerValidatorImpl; +import static org.zanata.client.commands.Messages._; + import java.util.Collections; import java.util.List; -import org.jboss.resteasy.client.ClientResponse; import org.zanata.client.commands.ConsoleInteractor; import org.zanata.common.EntityStatus; import org.zanata.common.ProjectType; -import org.zanata.rest.client.ZanataProxyFactory; +import org.zanata.rest.client.ProjectClient; +import org.zanata.rest.client.ProjectsClient; +import org.zanata.rest.client.RestClientFactory; import org.zanata.rest.dto.Project; + import com.google.common.annotations.VisibleForTesting; import com.google.common.base.Function; import com.google.common.base.Joiner; @@ -37,13 +45,7 @@ import com.google.common.collect.ImmutableList; import com.google.common.collect.Iterables; import com.google.common.collect.Lists; - -import static org.zanata.client.commands.ConsoleInteractor.DisplayMode.Confirmation; -import static org.zanata.client.commands.ConsoleInteractor.DisplayMode.Hint; -import static org.zanata.client.commands.ConsoleInteractor.DisplayMode.Question; -import static org.zanata.client.commands.ConsoleInteractorImpl.*; -import static org.zanata.client.commands.Messages._; -import static org.zanata.client.commands.StringUtil.indent; +import com.sun.jersey.api.client.UniformInterfaceException; /** * @author Patrick Huang @@ -54,18 +56,21 @@ class ProjectPrompt { private final InitOptions opts; private final ProjectIterationPrompt projectIterationPrompt; - private final ZanataProxyFactory proxyFactory; + private final RestClientFactory clientFactory; // state variables private List allProjects = Collections.emptyList(); private List filteredProjects = Collections.emptyList(); + public static final List PROJECT_TYPE_LIST = + Lists.transform(Lists.newArrayList(ProjectType.values()), + new ProjectTypeToStringFunction()); ProjectPrompt(ConsoleInteractor consoleInteractor, InitOptions opts, - ZanataProxyFactory proxyFactory, - ProjectIterationPrompt projectIterationPrompt) { + ProjectIterationPrompt projectIterationPrompt, + RestClientFactory clientFactory) { this.consoleInteractor = consoleInteractor; this.opts = opts; - this.proxyFactory = proxyFactory; this.projectIterationPrompt = projectIterationPrompt; + this.clientFactory = clientFactory; } /** @@ -99,7 +104,7 @@ protected void selectProject() { selectProject(); return; } - Project project = filteredProjects.get(Integer.valueOf(selection) - 1); + Project project = filteredProjects.get(Integer.parseInt(selection) - 1); String projectId = project.getId(); opts.setProj(projectId); // TODO server returns Upper case project type!!! @@ -123,7 +128,7 @@ private boolean selectionIsFilter(String selection, List filteredProjects) { boolean isNumber = selection.matches("\\d+"); if (isNumber) { - Integer indexNum = Integer.valueOf(selection) - 1; + Integer indexNum = Integer.parseInt(selection) - 1; // if input is a valid index number boolean isValidIndex = indexNum >= 0 && indexNum < filteredProjects.size(); @@ -159,8 +164,7 @@ private void ensureActiveProjects() { // TODO add optional query param to search projects (limit return // values) Project[] projectsArray = - proxyFactory.getProjectsResource().get() - .getEntity(Project[].class); + clientFactory.getProjectsClient().getProjects(); allProjects = ImmutableList.copyOf(Iterables .filter(Lists.newArrayList(projectsArray), new Predicate() { @@ -189,21 +193,21 @@ protected void createNewProject() { String projectId = consoleInteractor.expectAnyAnswer(); consoleInteractor.printfln(Question, _("project.name.prompt")); String projectName = consoleInteractor.expectAnyAnswer(); - List projectTypeList = - Lists.transform(Lists.newArrayList(ProjectType.values()), - new ProjectTypeToStringFunction()); - String projectTypes = Joiner.on(", ").join(projectTypeList); + String projectTypes = Joiner.on(", ").join(PROJECT_TYPE_LIST); consoleInteractor.printfln(Question, _("project.type.prompt"), projectTypes); String projectType = consoleInteractor.expectAnswerWithRetry( - AnswerValidatorImpl.expect(projectTypeList)); - ClientResponse response = proxyFactory.getProject(projectId) - .put(new Project(projectId, projectName, projectType)); - if (response.getStatus() >= 399) { - InitCommand.offerRetryOnServerError(response, consoleInteractor); - createNewProject(); + AnswerValidatorImpl.expect(PROJECT_TYPE_LIST)); + ProjectClient projectClient = clientFactory.getProjectClient(projectId); + Project project = new Project(projectId, projectName, projectType); + try { + projectClient.put(project); + } catch (UniformInterfaceException e) { + if (e.getResponse().getStatus() >= 399) { + InitCommand.offerRetryOnServerError(e, consoleInteractor); + createNewProject(); + } } - response.releaseConnection(); consoleInteractor.printfln(Confirmation, _("project.created")); opts.setProj(projectId); opts.setProjectType(projectType); diff --git a/zanata-client-commands/src/main/java/org/zanata/client/commands/init/SourceConfigPrompt.java b/zanata-client-commands/src/main/java/org/zanata/client/commands/init/SourceConfigPrompt.java index 7407a9cc..0df88386 100644 --- a/zanata-client-commands/src/main/java/org/zanata/client/commands/init/SourceConfigPrompt.java +++ b/zanata-client-commands/src/main/java/org/zanata/client/commands/init/SourceConfigPrompt.java @@ -36,9 +36,9 @@ import org.zanata.client.commands.push.PushCommand; import org.zanata.client.commands.push.PushOptions; import org.zanata.client.commands.push.PushOptionsImpl; -import org.zanata.rest.client.ZanataProxyFactory; - -import com.google.common.base.Throwables; +import org.zanata.rest.client.AsyncProcessClient; +import org.zanata.rest.client.CopyTransClient; +import org.zanata.rest.client.RestClientFactory; import static org.zanata.client.commands.ConsoleInteractor.DisplayMode.Hint; import static org.zanata.client.commands.ConsoleInteractor.DisplayMode.Question; @@ -85,13 +85,13 @@ public SourceConfigPrompt( pushOptions.setKey(opts.getKey()); pushOptions.setLocaleMapList(opts.getLocaleMapList()); - ZanataProxyFactory proxyFactory = OptionsUtil - .createRequestFactoryWithoutVersionCheck(pushOptions); + RestClientFactory clientFactory = + OptionsUtil.createClientFactoryWithoutVersionCheck(pushOptions); pushCommand = - new PushCommand(pushOptions, proxyFactory, null, - null, proxyFactory - .getResourceURI(pushOptions.getProj(), - pushOptions.getProjectVersion())); + new PushCommand(pushOptions, + clientFactory.getCopyTransClient(), + clientFactory.getAsyncProcessClient(), + clientFactory); } SourceConfigPrompt promptUser() throws Exception { @@ -176,8 +176,7 @@ private static String getUsageFromOptionAnnotation( Option.class).usage() // the usage text is not very well formatted (contains new line) .replaceAll(System.getProperty("line.separator"), " "); - } - catch (NoSuchMethodException e) { + } catch (NoSuchMethodException e) { log.error("can not find method: {} on class {}", methodName, optionsClass); return methodName; } diff --git a/zanata-client-commands/src/main/java/org/zanata/client/commands/init/TransConfigPrompt.java b/zanata-client-commands/src/main/java/org/zanata/client/commands/init/TransConfigPrompt.java index a516d2d7..8534d67b 100644 --- a/zanata-client-commands/src/main/java/org/zanata/client/commands/init/TransConfigPrompt.java +++ b/zanata-client-commands/src/main/java/org/zanata/client/commands/init/TransConfigPrompt.java @@ -31,7 +31,7 @@ import org.zanata.client.commands.pull.PullStrategy; import org.zanata.client.config.LocaleList; import org.zanata.client.config.LocaleMapping; -import org.zanata.rest.client.ZanataProxyFactory; +import org.zanata.rest.client.RestClientFactory; import com.google.common.base.Function; import com.google.common.collect.ImmutableSet; import com.google.common.collect.Iterables; @@ -73,11 +73,11 @@ public TransConfigPrompt(ConsoleInteractor console, pullOptions.setProjectVersion(opts.getProjectVersion()); pullOptions.setProjectType(opts.getProjectType()); pullOptions.setLocaleMapList(opts.getLocaleMapList()); - ZanataProxyFactory proxyFactory = - OptionsUtil.createRequestFactoryWithoutVersionCheck(opts); - pullCommand = new PullCommand(pullOptions, proxyFactory, null, null, - proxyFactory.getResourceURI(opts.getProj(), - opts.getProjectVersion())); + + RestClientFactory clientFactory = + OptionsUtil.createClientFactoryWithoutVersionCheck(opts); + pullCommand = new PullCommand(pullOptions, + clientFactory); } TransConfigPrompt promptUser() throws Exception { diff --git a/zanata-client-commands/src/main/java/org/zanata/client/commands/init/UserConfigHandler.java b/zanata-client-commands/src/main/java/org/zanata/client/commands/init/UserConfigHandler.java index 4f3f0b6c..75d2046c 100644 --- a/zanata-client-commands/src/main/java/org/zanata/client/commands/init/UserConfigHandler.java +++ b/zanata-client-commands/src/main/java/org/zanata/client/commands/init/UserConfigHandler.java @@ -88,7 +88,7 @@ protected void verifyUserConfig() throws Exception { consoleInteractor.printf(Question, _("which.server")); String chosenNumber = consoleInteractor.expectAnswerWithRetry(expect(answers)); - URL url = serverUrls.get(Integer.valueOf(chosenNumber) - 1); + URL url = serverUrls.get(Integer.parseInt(chosenNumber) - 1); consoleInteractor.printfln(Confirmation, _("server.selection"), url); opts.setUrl(url); } diff --git a/zanata-client-commands/src/main/java/org/zanata/client/commands/pull/PropertiesStrategy.java b/zanata-client-commands/src/main/java/org/zanata/client/commands/pull/PropertiesStrategy.java index 30a16a67..bc5270c6 100644 --- a/zanata-client-commands/src/main/java/org/zanata/client/commands/pull/PropertiesStrategy.java +++ b/zanata-client-commands/src/main/java/org/zanata/client/commands/pull/PropertiesStrategy.java @@ -54,7 +54,7 @@ public boolean needsDocToWriteTrans() { @Override public void writeSrcFile(Resource doc) throws IOException { - PropWriter.write(doc, getOpts().getSrcDir()); + PropWriter.writeSource(doc, getOpts().getSrcDir(), PropWriter.CHARSET.Latin1); } @Override @@ -62,14 +62,9 @@ public FileDetails writeTransFile(Resource doc, String docName, LocaleMapping localeMapping, TranslationsResource targetDoc) throws IOException { boolean createSkeletons = getOpts().getCreateSkeletons(); - if (createSkeletons) { - PropWriter.write(doc, targetDoc, getOpts().getTransDir(), docName, - localeMapping.getJavaLocale(), true); - } else { - PropWriter.write(null, targetDoc, getOpts().getTransDir(), docName, - localeMapping.getJavaLocale(), false); - } - + PropWriter.writeTranslations(createSkeletons ? doc : null, targetDoc, + getOpts().getTransDir(), docName, + localeMapping.getJavaLocale(), PropWriter.CHARSET.Latin1, createSkeletons); return null; } diff --git a/zanata-client-commands/src/main/java/org/zanata/client/commands/pull/PullCommand.java b/zanata-client-commands/src/main/java/org/zanata/client/commands/pull/PullCommand.java index 81cdbb6d..39215e6f 100644 --- a/zanata-client-commands/src/main/java/org/zanata/client/commands/pull/PullCommand.java +++ b/zanata-client-commands/src/main/java/org/zanata/client/commands/pull/PullCommand.java @@ -4,7 +4,6 @@ import java.io.IOException; import java.lang.reflect.Constructor; import java.lang.reflect.InvocationTargetException; -import java.net.URI; import java.util.HashMap; import java.util.List; import java.util.Map; @@ -12,10 +11,8 @@ import java.util.TreeSet; import javax.ws.rs.core.HttpHeaders; -import javax.ws.rs.core.Response; import org.apache.commons.lang.StringUtils; -import org.jboss.resteasy.client.ClientResponse; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.zanata.client.commands.PushPullCommand; @@ -27,14 +24,17 @@ import org.zanata.common.LocaleId; import org.zanata.common.io.FileDetails; import org.zanata.rest.RestUtil; -import org.zanata.rest.client.ClientUtility; -import org.zanata.rest.client.ISourceDocResource; -import org.zanata.rest.client.ITranslatedDocResource; -import org.zanata.rest.client.ZanataProxyFactory; +import org.zanata.rest.client.ClientUtil; +import org.zanata.rest.client.RestClientFactory; import org.zanata.rest.dto.resource.Resource; import org.zanata.rest.dto.resource.TranslationsResource; import org.zanata.util.HashUtil; +import com.google.common.annotations.VisibleForTesting; +import com.google.common.base.Optional; +import com.google.common.collect.Lists; +import com.sun.jersey.api.client.ClientResponse; + /** * @author Sean Flanigan sflaniga@redhat.com @@ -62,10 +62,8 @@ public PullCommand(PullOptions opts) { super(opts); } - public PullCommand(PullOptions opts, ZanataProxyFactory factory, - ISourceDocResource sourceDocResource, - ITranslatedDocResource translationResources, URI uri) { - super(opts, factory, sourceDocResource, translationResources, uri); + public PullCommand(PullOptions opts, RestClientFactory clientFactory) { + super(opts, clientFactory); } public PullStrategy createStrategy(PullOptions opts) @@ -128,12 +126,16 @@ public static void logOptions(Logger logger, PullOptions opts) { logger.info("Pulling target documents (translations) only"); logger.info("Target-language base directory (translations): {}", opts.getTransDir()); + logger.info("Minimum accepted translation percentage (message based): {}%", + opts.getMinDocPercent()); } else { logger.info("Pulling source and target (translation) documents"); logger.info("Source-language directory (originals): {}", opts.getSrcDir()); logger.info("Target-language base directory (translations): {}", opts.getTransDir()); + logger.info("Minimum accepted translation percentage (message based): {}%", + opts.getMinDocPercent()); } } @@ -142,10 +144,10 @@ public void run() throws Exception { logOptions(); LocaleList locales = getOpts().getLocaleMapList(); - if (locales == null && (getOpts().getPullType() != PushPullType.Source)) + if (locales == null && (getOpts().getPullType() != PushPullType.Source)) { throw new ConfigException("no locales specified"); - PullStrategy strat = createStrategy( - getOpts()); + } + PullStrategy strat = createStrategy(getOpts()); if (strat.isTransOnly() && getOpts().getPullType() == PushPullType.Source) { @@ -197,18 +199,26 @@ && getOpts().getPullType() == PushPullType.Source) { pullSrc = false; } + if (needToGetStatistics(pullTarget)) { + log.info("Setting minimum document completion percentage may potentially increase the processing time."); + } + if (pullSrc) { log.warn("Pull Type set to '" + pullType + "': existing source-language files may be overwritten/deleted"); confirmWithUser("This will overwrite/delete any existing documents and translations in the above directories.\n"); } else { - confirmWithUser("This will overwrite/delete any existing translations in the above directory.\n"); + confirmWithUser( + "This will overwrite/delete any existing translations in the above directory.\n"); } if (getOpts().getPurgeCache()) { eTagCache.clear(); } + Optional>> optionalStats = + prepareStatsIfApplicable(pullTarget, locales); + for (String qualifiedDocName : docsToPull) { try { @@ -219,11 +229,8 @@ && getOpts().getPullType() == PushPullType.Source) { RestUtil.convertToDocumentURIId(qualifiedDocName); boolean createSkeletons = getOpts().getCreateSkeletons(); if (strat.needsDocToWriteTrans() || pullSrc || createSkeletons) { - ClientResponse resourceResponse = - sourceDocResource.getResource(docUri, - strat.getExtensions()); - ClientUtility.checkResult(resourceResponse, uri); - doc = resourceResponse.getEntity(); + doc = sourceDocResourceClient.getResource(docUri, + strat.getExtensions()); doc.setName(localDocName); } if (pullSrc) { @@ -231,88 +238,25 @@ && getOpts().getPullType() == PushPullType.Source) { } if (pullTarget) { + List skippedLocales = Lists.newArrayList(); for (LocaleMapping locMapping : locales) { LocaleId locale = new LocaleId(locMapping.getLocale()); - String eTag = null; File transFile = strat.getTransFileToWrite(localDocName, locMapping); - ETagCacheEntry eTagCacheEntry = - eTagCache.findEntry(localDocName, - locale.getId()); - - if (getOpts().getUseCache() && eTagCacheEntry != null) { - // Check the last updated date on the file matches - // what's in the cache - // only then use the cached ETag - if (transFile.exists() - && Long.toString(transFile.lastModified()) - .equals(eTagCacheEntry - .getLocalFileTime())) { - eTag = eTagCacheEntry.getServerETag(); - } - } - ClientResponse transResponse = - translationResources.getTranslations(docUri, - locale, strat.getExtensions(), - createSkeletons, eTag); - - // ignore 404 (no translation yet for specified - // document) - if (transResponse.getResponseStatus() == Response.Status.NOT_FOUND) { - if (!createSkeletons) { - log.info( - "No translations found in locale {} for document {}", - locale, localDocName); - // We need to release connection. - // see - // http://stackoverflow.com/questions/4612573/exception-using-httprequest-execute-invalid-use-of-singleclientconnmanager-c - transResponse.releaseConnection(); - } else { - // Write the skeleton - writeTargetDoc(strat, localDocName, locMapping, - doc, null, - transResponse.getResponseHeaders() - .getFirst(HttpHeaders.ETAG)); - } - } - // 304 NOT MODIFIED (the document can stay the same) - else if (transResponse.getResponseStatus() == Response.Status.NOT_MODIFIED) { - log.info( - "No changes in translations for locale {} and document {}", - locale, localDocName); - - // Check the file's MD5 matches what's stored in the - // cache. If not, it needs to be fetched again (with - // no etag) - String fileChecksum = - HashUtil.getMD5Checksum(transFile); - if (!fileChecksum.equals(eTagCacheEntry - .getLocalFileMD5())) { - transResponse = - translationResources.getTranslations( - docUri, locale, - strat.getExtensions(), - createSkeletons, null); - ClientUtility.checkResult(transResponse, uri); - // rewrite the target document - writeTargetDoc(strat, localDocName, locMapping, - doc, transResponse.getEntity(), - transResponse.getResponseHeaders() - .getFirst(HttpHeaders.ETAG)); - } + if (shouldPullThisLocale(optionalStats, localDocName, locale)) { + pullDocForLocale(strat, doc, localDocName, docUri, + createSkeletons, locMapping, transFile); } else { - ClientUtility.checkResult(transResponse, uri); - TranslationsResource targetDoc = - transResponse.getEntity(); - - // Write the target document - writeTargetDoc(strat, localDocName, locMapping, - doc, targetDoc, - transResponse.getResponseHeaders() - .getFirst(HttpHeaders.ETAG)); + skippedLocales.add(locale); } + + } + if (!skippedLocales.isEmpty()) { + log.info( + "Translation file for document {} for locales {} are skipped due to insufficient completed percentage", + localDocName, skippedLocales); } // write the cache @@ -321,7 +265,7 @@ else if (transResponse.getResponseStatus() == Response.Status.NOT_MODIFIED) { } catch (RuntimeException e) { String message = - "Operation failed: "+e.getMessage()+"\n\n" + "Operation failed: " + e.getMessage() + "\n\n" + " To retry from the last document, please set the following option(s):\n\n" + " "; if (getOpts().getEnableModules()) { @@ -343,6 +287,86 @@ else if (transResponse.getResponseStatus() == Response.Status.NOT_MODIFIED) { } + @VisibleForTesting + protected void pullDocForLocale(PullStrategy strat, Resource doc, + String localDocName, String docUri, boolean createSkeletons, + LocaleMapping locMapping, + File transFile) throws IOException { + LocaleId locale = new LocaleId(locMapping.getLocale()); + String eTag = null; + ETagCacheEntry eTagCacheEntry = + eTagCache.findEntry(localDocName, + locale.getId()); + + if (getOpts().getUseCache() && eTagCacheEntry != null) { + // Check the last updated date on the file matches + // what's in the cache + // only then use the cached ETag + if (transFile.exists() + && Long.toString(transFile.lastModified()) + .equals(eTagCacheEntry + .getLocalFileTime())) { + eTag = eTagCacheEntry.getServerETag(); + } + } + + ClientResponse transResponse = + transDocResourceClient.getTranslations(docUri, + locale, strat.getExtensions(), + createSkeletons, eTag); + + // ignore 404 (no translation yet for specified + // document) + if (transResponse.getClientResponseStatus() == ClientResponse.Status.NOT_FOUND) { + if (!createSkeletons) { + log.info( + "No translations found in locale {} for document {}", + locale, localDocName); + } else { + // Write the skeleton + writeTargetDoc(strat, localDocName, locMapping, + doc, null, + transResponse.getHeaders() + .getFirst(HttpHeaders.ETAG)); + } + } else if (transResponse.getClientResponseStatus() == ClientResponse.Status.NOT_MODIFIED) { + // 304 NOT MODIFIED (the document can stay the same) + log.info( + "No changes in translations for locale {} and document {}", + locale, localDocName); + + // Check the file's MD5 matches what's stored in the + // cache. If not, it needs to be fetched again (with + // no etag) + String fileChecksum = + HashUtil.getMD5Checksum(transFile); + if (!fileChecksum.equals(eTagCacheEntry + .getLocalFileMD5())) { + transResponse = + transDocResourceClient.getTranslations( + docUri, locale, + strat.getExtensions(), + createSkeletons, null); + ClientUtil.checkResult(transResponse); + // rewrite the target document + writeTargetDoc(strat, localDocName, locMapping, + doc, transResponse.getEntity(TranslationsResource.class), + transResponse.getHeaders() + .getFirst(HttpHeaders.ETAG)); + } + } else { + ClientUtil.checkResult(transResponse); + TranslationsResource targetDoc = + transResponse.getEntity(TranslationsResource.class); + + // Write the target document + writeTargetDoc(strat, localDocName, locMapping, + doc, targetDoc, + transResponse.getHeaders() + .getFirst(HttpHeaders.ETAG)); + } + } + /** * Returns a list with all documents before fromDoc removed. * diff --git a/zanata-client-commands/src/main/java/org/zanata/client/commands/pull/PullOptions.java b/zanata-client-commands/src/main/java/org/zanata/client/commands/pull/PullOptions.java index 2494c143..eb8ea04d 100644 --- a/zanata-client-commands/src/main/java/org/zanata/client/commands/pull/PullOptions.java +++ b/zanata-client-commands/src/main/java/org/zanata/client/commands/pull/PullOptions.java @@ -43,4 +43,6 @@ public interface PullOptions extends PushPullOptions { boolean getUseCache(); boolean isContinueAfterError(); + + int getMinDocPercent(); } diff --git a/zanata-client-commands/src/main/java/org/zanata/client/commands/pull/PullOptionsImpl.java b/zanata-client-commands/src/main/java/org/zanata/client/commands/pull/PullOptionsImpl.java index c735c96f..e1703a1c 100644 --- a/zanata-client-commands/src/main/java/org/zanata/client/commands/pull/PullOptionsImpl.java +++ b/zanata-client-commands/src/main/java/org/zanata/client/commands/pull/PullOptionsImpl.java @@ -26,6 +26,7 @@ import org.zanata.client.commands.BooleanValueHandler; import org.zanata.client.commands.ZanataCommand; import org.zanata.client.commands.PushPullType; +import com.google.common.base.Preconditions; /** * @author Sean Flanigan private boolean useCache = DEFAULT_USE_CACHE; private boolean purgeCache = DEFAULT_PURGE_CACHE; private boolean continueAfterError = DEFAULT_CONTINUE_AFTER_ERROR; + private int minDocPercent = 0; @Override public ZanataCommand initCommand() { @@ -73,7 +75,7 @@ public String getCommandDescription() { @Option(aliases = { "-l" }, name = "--locales", metaVar = "LOCALE1,LOCALE2", usage = "Locales to pull from the server.\n" - + "By default all locales in zanata.xml will be pulled.") + + "By default all locales configured will be pulled.") public void setLocales(String locales) { this.locales = locales.split(","); } @@ -165,8 +167,37 @@ public boolean isContinueAfterError() { return continueAfterError; } + @Override + public int getMinDocPercent() { + return this.minDocPercent; + } + + @Option(name = "--min-doc-percent", metaVar = "PERCENT", + usage = "Accepts integer from 0 to 100. Only pull translation documents which are at least PERCENT % (message based) completed.\n" + + "Please note specifying this option may cause longer time to pull for a large project") + public void setMinDocPercent(int minDocPercent) { + Preconditions + .checkArgument(minDocPercent >= 0 && minDocPercent <= 100, + "--min-doc-percent should be an integer from 0 to 100"); + this.minDocPercent = minDocPercent; + } + @Override public boolean isAuthRequired() { return false; } + + @Option(name = "--use-cache", usage = "Whether to use an Entity cache when fetching documents.\n" + + "When using the cache, documents that have been retrieved previously and have not changed since then will not be retrieved again.\n" + + "Default is " + DEFAULT_USE_CACHE + ".") + public void setUseCache(boolean useCache) { + this.useCache = useCache; + } + + @Option(name = "--purge-cache", usage = "Whether to purge the cache before performing the pull operation.\n" + + "This means that all documents will be fetched from the server anew.\n" + + "Default is " + DEFAULT_PURGE_CACHE + ".") + public void setPurgeCache(boolean purgeCache) { + this.purgeCache = purgeCache; + } } diff --git a/zanata-client-commands/src/main/java/org/zanata/client/commands/pull/RawPullCommand.java b/zanata-client-commands/src/main/java/org/zanata/client/commands/pull/RawPullCommand.java index efaaa4d7..6df41791 100644 --- a/zanata-client-commands/src/main/java/org/zanata/client/commands/pull/RawPullCommand.java +++ b/zanata-client-commands/src/main/java/org/zanata/client/commands/pull/RawPullCommand.java @@ -22,15 +22,13 @@ import java.io.IOException; import java.io.InputStream; -import java.net.URI; import java.util.List; +import java.util.Map; import java.util.SortedSet; import java.util.TreeSet; -import javax.ws.rs.core.Response; -import javax.ws.rs.core.Response.Status; - -import org.jboss.resteasy.client.ClientResponse; +import com.google.common.collect.Lists; +import org.apache.commons.io.FilenameUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.zanata.client.commands.PushPullCommand; @@ -39,13 +37,14 @@ import org.zanata.client.config.LocaleMapping; import org.zanata.client.exceptions.ConfigException; import org.zanata.common.LocaleId; -import org.zanata.rest.client.ClientUtility; -import org.zanata.rest.client.IFileResource; -import org.zanata.rest.client.ISourceDocResource; -import org.zanata.rest.client.ITranslatedDocResource; -import org.zanata.rest.client.ZanataProxyFactory; +import org.zanata.rest.client.ClientUtil; +import org.zanata.rest.client.FileResourceClient; +import org.zanata.rest.client.RestClientFactory; import org.zanata.rest.service.FileResource; + import com.google.common.annotations.VisibleForTesting; +import com.google.common.base.Optional; +import com.sun.jersey.api.client.ClientResponse; /** * @@ -57,21 +56,19 @@ public class RawPullCommand extends PushPullCommand { private static final Logger log = LoggerFactory .getLogger(RawPullCommand.class); - private IFileResource fileResource; + private FileResourceClient fileResourceClient; public RawPullCommand(PullOptions opts) { super(opts); - this.fileResource = getRequestFactory().getFileResource(); + fileResourceClient = getClientFactory().getFileResourceClient(); } @VisibleForTesting public RawPullCommand(PullOptions opts, - ZanataProxyFactory factory, - ISourceDocResource sourceDocResource, - ITranslatedDocResource translationResources, - URI uri, IFileResource fileResource) { - super(opts, factory, sourceDocResource, translationResources, uri); - this.fileResource = fileResource; + FileResourceClient fileResourceClient, + RestClientFactory clientFactory) { + super(opts, clientFactory); + this.fileResourceClient = fileResourceClient; } @Override @@ -139,6 +136,10 @@ public void run() throws IOException { boolean pullTarget = pullType == PushPullType.Both || pullType == PushPullType.Trans; + if (needToGetStatistics(pullTarget)) { + log.info("Setting minimum document completion percentage may potentially increase the processing time."); + } + if (pullSrc) { log.warn("Pull Type set to '" + pullType @@ -148,6 +149,9 @@ public void run() throws IOException { confirmWithUser("This will overwrite/delete any existing translations in the above directory.\n"); } + Optional>> optionalStats = + prepareStatsIfApplicable(pullTarget, locales); + for (String qualifiedDocName : docsToPull) { // TODO add filtering by file type? e.g. pull all dtd documents // only. @@ -157,20 +161,19 @@ public void run() throws IOException { if (pullSrc) { ClientResponse response = - fileResource.downloadSourceFile( + fileResourceClient.downloadSourceFile( getOpts().getProj(), getOpts() .getProjectVersion(), FileResource.FILETYPE_RAW_SOURCE_DOCUMENT, qualifiedDocName); - if (response.getResponseStatus() == Status.NOT_FOUND) { + if (response.getClientResponseStatus() == ClientResponse.Status.NOT_FOUND) { log.warn( "No source document file is available for [{}]. Skipping.", qualifiedDocName); } else { - ClientUtility.checkResult(response, uri); - InputStream srcDoc = - (InputStream) response - .getEntity(InputStream.class); + ClientUtil.checkResult(response); + InputStream srcDoc = response + .getEntity(InputStream.class); if (srcDoc != null) { try { strat.writeSrcFile(localDocName, srcDoc); @@ -191,36 +194,28 @@ public void run() throws IOException { FileResource.FILETYPE_TRANSLATED_APPROVED; } + List skippedLocales = Lists.newArrayList(); for (LocaleMapping locMapping : locales) { LocaleId locale = new LocaleId(locMapping.getLocale()); - ClientResponse response = - fileResource.downloadTranslationFile(getOpts() - .getProj(), getOpts() - .getProjectVersion(), locale.getId(), - fileExtension, qualifiedDocName); - if (response.getResponseStatus() == Response.Status.NOT_FOUND) { - log.info( - "No translation document file found in locale {} for document [{}]", - locale, qualifiedDocName); + + if (shouldPullThisLocale(optionalStats, localDocName, locale)) { + pullDocForLocale(strat, qualifiedDocName, localDocName, + fileExtension, + locMapping, locale); } else { - ClientUtility.checkResult(response, uri); - InputStream transDoc = - (InputStream) response - .getEntity(InputStream.class); - if (transDoc != null) { - try { - strat.writeTransFile(localDocName, locMapping, - transDoc); - } finally { - transDoc.close(); - } - } + skippedLocales.add(locale); } + + } + if (!skippedLocales.isEmpty()) { + log.info( + "Translation file for document {} for locales {} are skipped due to insufficient completed percentage", + localDocName, skippedLocales); } } } catch (IOException | RuntimeException e) { log.error( - "Operation failed: "+e.getMessage()+"\n\n" + "Operation failed: " + e.getMessage() + "\n\n" + " To retry from the last document, please add the option: {}\n", getOpts().buildFromDocArgument(qualifiedDocName)); throw new RuntimeException(e.getMessage(), e); @@ -228,4 +223,38 @@ public void run() throws IOException { } } + private void pullDocForLocale(RawPullStrategy strat, + String qualifiedDocName, String localDocName, String fileExtension, + LocaleMapping locMapping, LocaleId locale) throws IOException { + ClientResponse response = + fileResourceClient.downloadTranslationFile(getOpts() + .getProj(), getOpts() + .getProjectVersion(), locale.getId(), + fileExtension, qualifiedDocName); + if (response.getClientResponseStatus() == ClientResponse.Status.NOT_FOUND) { + log.info( + "No translation document file found in locale {} for document [{}]", + locale, qualifiedDocName); + } else { + ClientUtil.checkResult(response); + InputStream transDoc = response.getEntity(InputStream.class); + if (transDoc != null) { + try { + String fileName = + ClientUtil.getFileNameFromHeader( + response.getHeaders()); + String targetFileExt = FilenameUtils + .getExtension(fileName); + + Optional translationFileExtension = + Optional.fromNullable(targetFileExt); + + strat.writeTransFile(localDocName, + locMapping, transDoc, translationFileExtension); + } finally { + transDoc.close(); + } + } + } + } } diff --git a/zanata-client-commands/src/main/java/org/zanata/client/commands/pull/RawPullStrategy.java b/zanata-client-commands/src/main/java/org/zanata/client/commands/pull/RawPullStrategy.java index 0575314c..b4a7068c 100644 --- a/zanata-client-commands/src/main/java/org/zanata/client/commands/pull/RawPullStrategy.java +++ b/zanata-client-commands/src/main/java/org/zanata/client/commands/pull/RawPullStrategy.java @@ -33,6 +33,8 @@ import org.zanata.client.config.LocaleMapping; import org.zanata.util.PathUtil; +import com.google.common.base.Optional; + /** * * @author David Mason, translationFileExtension) throws IOException { if (transFile == null) { throw new RuntimeException("no data for downloaded file " + localDocName); } File file = new TransFileResolver(opts).resolveTransFile( - QualifiedSrcDocName.from(localDocName), - localeMapping); + QualifiedSrcDocName.from(localDocName), + localeMapping, translationFileExtension); logAndStreamToFile(transFile, file); } diff --git a/zanata-client-commands/src/main/java/org/zanata/client/commands/pull/UTF8PropertiesStrategy.java b/zanata-client-commands/src/main/java/org/zanata/client/commands/pull/UTF8PropertiesStrategy.java index f630a872..e12e55c0 100644 --- a/zanata-client-commands/src/main/java/org/zanata/client/commands/pull/UTF8PropertiesStrategy.java +++ b/zanata-client-commands/src/main/java/org/zanata/client/commands/pull/UTF8PropertiesStrategy.java @@ -42,7 +42,7 @@ protected UTF8PropertiesStrategy(PullOptions opts) { @Override public void writeSrcFile(Resource doc) throws IOException { - PropWriter.writeUTF8(doc, getOpts().getSrcDir()); + PropWriter.writeSource(doc, getOpts().getSrcDir(), PropWriter.CHARSET.UTF8); } @Override @@ -50,14 +50,10 @@ public FileDetails writeTransFile(Resource doc, String docName, LocaleMapping localeMapping, TranslationsResource targetDoc) throws IOException { boolean createSkeletons = getOpts().getCreateSkeletons(); - if (createSkeletons) { - PropWriter.writeUTF8(doc, targetDoc, getOpts().getTransDir(), - docName, localeMapping.getJavaLocale(), true); - } else { - PropWriter.writeUTF8(null, targetDoc, getOpts().getTransDir(), - docName, localeMapping.getJavaLocale(), false); - } - + PropWriter.writeTranslations(createSkeletons ? doc : null, targetDoc, + getOpts().getTransDir(), + docName, localeMapping.getJavaLocale(), + PropWriter.CHARSET.UTF8, createSkeletons); return null; } diff --git a/zanata-client-commands/src/main/java/org/zanata/client/commands/push/GettextDirStrategy.java b/zanata-client-commands/src/main/java/org/zanata/client/commands/push/GettextDirStrategy.java index 7f0e843d..df568748 100644 --- a/zanata-client-commands/src/main/java/org/zanata/client/commands/push/GettextDirStrategy.java +++ b/zanata-client-commands/src/main/java/org/zanata/client/commands/push/GettextDirStrategy.java @@ -45,7 +45,7 @@ List findLocales(String srcDocName) { LocaleList localeListInConfig = getOpts().getLocaleMapList(); if (localeListInConfig == null || localeListInConfig.isEmpty()) { - log.warn("No locale list in configuration (check zanata.xml)"); + log.warn("No locale list in configuration (check your server settings)"); return localesFoundOnDisk; } @@ -62,7 +62,7 @@ List findLocales(String srcDocName) { if (localesFoundOnDisk.size() == 0) { log.warn( "'pushType' is set to '{}', but none of the configured locale " - + "files was found (check zanata.xml)", getOpts() + + "files was found (check your server settings)", getOpts() .getPushType()); } @@ -85,7 +85,7 @@ protected void checkSrcFileNames(String projectType, String[] srcFiles, String warningMsg = "Found source file path starting with pot, perhaps you want to set source directory to pot?"; ConsoleInteractorImpl console = - new ConsoleInteractorImpl(); + new ConsoleInteractorImpl(getOpts()); log.warn(warningMsg); if (isInteractive) { console.printfln(warningMsg); diff --git a/zanata-client-commands/src/main/java/org/zanata/client/commands/push/GettextPushStrategy.java b/zanata-client-commands/src/main/java/org/zanata/client/commands/push/GettextPushStrategy.java index b4ae0fba..dcc63126 100644 --- a/zanata-client-commands/src/main/java/org/zanata/client/commands/push/GettextPushStrategy.java +++ b/zanata-client-commands/src/main/java/org/zanata/client/commands/push/GettextPushStrategy.java @@ -30,7 +30,7 @@ List findLocales(String srcDocName) { final LocaleList localeListInConfig = getOpts().getLocaleMapList(); if (localeListInConfig == null || localeListInConfig.isEmpty()) { - log.warn("No locale list in configuration (check zanata.xml)"); + log.warn("No locale list in configuration (check your server settings)"); return Collections.emptyList(); } @@ -46,7 +46,7 @@ List findLocales(String srcDocName) { // for all remaining po files we give a warning for (File transFile : transFilesOnDisk) { log.warn( - "Skipping file {}; no locale entry found in zanata.xml", + "Skipping file {}; no locale entry found from project config", transFile); } return localeListInConfig; diff --git a/zanata-client-commands/src/main/java/org/zanata/client/commands/push/OfflinePoStrategy.java b/zanata-client-commands/src/main/java/org/zanata/client/commands/push/OfflinePoStrategy.java index c44451d4..dda495cd 100644 --- a/zanata-client-commands/src/main/java/org/zanata/client/commands/push/OfflinePoStrategy.java +++ b/zanata-client-commands/src/main/java/org/zanata/client/commands/push/OfflinePoStrategy.java @@ -22,16 +22,13 @@ import java.io.File; import java.io.IOException; -import java.net.URI; import java.util.HashSet; import java.util.List; import java.util.Set; import com.google.common.collect.ImmutableList; -import org.jboss.resteasy.client.ClientResponse; import org.zanata.adapter.po.PoReader2; -import org.zanata.rest.client.ClientUtility; -import org.zanata.rest.client.ISourceDocResource; +import org.zanata.rest.client.SourceDocResourceClient; import org.zanata.rest.dto.resource.Resource; import org.zanata.rest.dto.resource.ResourceMeta; @@ -42,15 +39,12 @@ * href="mailto:damason@redhat.com">damason@redhat.com */ public class OfflinePoStrategy extends GettextDirStrategy { - private final ISourceDocResource sourceDocResource; - private final PoReader2 poReader; + private SourceDocResourceClient client; + private final PoReader2 poReader = new PoReader2(true); - private final URI uri; - public OfflinePoStrategy(ISourceDocResource sourceDocResource, URI uri) { - this.sourceDocResource = sourceDocResource; - this.uri = uri; - poReader = new PoReader2(true); + public OfflinePoStrategy(SourceDocResourceClient client) { + this.client = client; } @Override @@ -72,10 +66,7 @@ public Set findDocNames(File srcDir, ImmutableList includes, ImmutableList excludes, boolean useDefaultExclude, boolean caseSensitive, boolean excludeLocaleFilenames) throws IOException { - ClientResponse> getResponse = - sourceDocResource.get(null); - ClientUtility.checkResult(getResponse, uri); - List remoteDocList = getResponse.getEntity(); + List remoteDocList = client.getResourceMeta(null); Set localDocNames = new HashSet(); for (ResourceMeta doc : remoteDocList) { localDocNames.add(doc.getName()); diff --git a/zanata-client-commands/src/main/java/org/zanata/client/commands/push/PropertiesStrategy.java b/zanata-client-commands/src/main/java/org/zanata/client/commands/push/PropertiesStrategy.java index 621ad1a9..58a6ade3 100644 --- a/zanata-client-commands/src/main/java/org/zanata/client/commands/push/PropertiesStrategy.java +++ b/zanata-client-commands/src/main/java/org/zanata/client/commands/push/PropertiesStrategy.java @@ -28,8 +28,11 @@ import java.util.Set; import com.google.common.collect.ImmutableList; + +import org.apache.commons.io.Charsets; import org.apache.commons.io.FilenameUtils; import org.zanata.adapter.properties.PropReader; +import org.zanata.adapter.properties.PropWriter; import org.zanata.client.commands.TransFileResolver; import org.zanata.client.commands.UnqualifiedSrcDocName; import org.zanata.client.commands.push.PushCommand.TranslationResourcesVisitor; @@ -49,17 +52,17 @@ */ public class PropertiesStrategy extends AbstractPushStrategy { // "8859_1" is used in Properties.java... - private static final String ISO_8859_1 = "ISO-8859-1"; +// private static final String ISO_8859_1 = "ISO-8859-1"; private PropReader propReader; - private final String charset; + private final PropWriter.CHARSET charset; public PropertiesStrategy() { - this(ISO_8859_1); + this(PropWriter.CHARSET.Latin1); } - public PropertiesStrategy(String charset) { + public PropertiesStrategy(PropWriter.CHARSET charset) { super(new StringSet("comment"), ".properties"); this.charset = charset; } diff --git a/zanata-client-commands/src/main/java/org/zanata/client/commands/push/PushCommand.java b/zanata-client-commands/src/main/java/org/zanata/client/commands/push/PushCommand.java index bfe4bd5c..4bd49df2 100644 --- a/zanata-client-commands/src/main/java/org/zanata/client/commands/push/PushCommand.java +++ b/zanata-client-commands/src/main/java/org/zanata/client/commands/push/PushCommand.java @@ -2,7 +2,6 @@ import java.io.File; import java.io.IOException; -import java.net.URI; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; @@ -16,13 +15,10 @@ import java.util.regex.Matcher; import java.util.regex.Pattern; -import javax.ws.rs.core.Response; - import org.apache.commons.lang.StringUtils; -import org.jboss.resteasy.client.ClientResponse; -import org.jboss.resteasy.client.ClientResponseFailure; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import org.zanata.adapter.properties.PropWriter; import org.zanata.adapter.xliff.XliffCommon.ValidationType; import org.zanata.client.commands.PushPullCommand; import org.zanata.client.commands.PushPullType; @@ -33,21 +29,23 @@ import org.zanata.common.MergeType; import org.zanata.rest.RestUtil; import org.zanata.rest.StringSet; -import org.zanata.rest.client.ClientUtility; -import org.zanata.rest.client.ISourceDocResource; -import org.zanata.rest.client.ITranslatedDocResource; -import org.zanata.rest.client.ZanataProxyFactory; +import org.zanata.rest.client.AsyncProcessClient; +import org.zanata.rest.client.CopyTransClient; +import org.zanata.rest.client.RestClientFactory; import org.zanata.rest.dto.CopyTransStatus; import org.zanata.rest.dto.ProcessStatus; import org.zanata.rest.dto.resource.Resource; import org.zanata.rest.dto.resource.ResourceMeta; import org.zanata.rest.dto.resource.TextFlowTarget; import org.zanata.rest.dto.resource.TranslationsResource; -import org.zanata.rest.service.AsynchronousProcessResource; -import org.zanata.rest.service.CopyTransResource; import com.google.common.base.Predicate; -import com.google.common.collect.Collections2; +import com.google.common.base.Strings; +import com.sun.jersey.api.client.ClientResponse; +import com.sun.jersey.api.client.UniformInterfaceException; + +import static com.google.common.collect.Collections2.filter; +import static com.google.common.collect.Iterables.all; /** * @author Sean Flanigan { private static final Logger log = LoggerFactory .getLogger(PushCommand.class); private static final int POLL_PERIOD = 250; - private static final String UTF_8 = "UTF-8"; private static final Map strategies = new HashMap(); - private CopyTransResource copyTransResource; - private AsynchronousProcessResource asyncProcessResource; + private CopyTransClient copyTransClient; + private AsyncProcessClient asyncProcessClient; - public static interface TranslationResourcesVisitor { + public interface TranslationResourcesVisitor { void visit(LocaleMapping locale, TranslationsResource targetDoc); } { strategies.put(PROJECT_TYPE_UTF8_PROPERTIES, new PropertiesStrategy( - UTF_8)); + PropWriter.CHARSET.UTF8)); strategies.put(PROJECT_TYPE_PROPERTIES, new PropertiesStrategy()); strategies.put(PROJECT_TYPE_GETTEXT, new GettextPushStrategy()); strategies.put(PROJECT_TYPE_PUBLICAN, new GettextDirStrategy()); @@ -80,24 +77,26 @@ public static interface TranslationResourcesVisitor { strategies.put(PROJECT_TYPE_XML, new XmlStrategy()); strategies.put( PROJECT_TYPE_OFFLINE_PO, - new OfflinePoStrategy(getRequestFactory().getSourceDocResource( - getOpts().getProj(), getOpts().getProjectVersion()), - uri)); + new OfflinePoStrategy(getClientFactory() + .getSourceDocResourceClient(getOpts().getProj(), + getOpts() + .getProjectVersion()))); } public PushCommand(PushOptions opts) { super(opts); - copyTransResource = getRequestFactory().getCopyTransResource(); - asyncProcessResource = - getRequestFactory().getAsynchronousProcessResource(); + copyTransClient = getClientFactory().getCopyTransClient(); + asyncProcessClient = getClientFactory().getAsyncProcessClient(); } - public PushCommand(PushOptions opts, ZanataProxyFactory factory, - ISourceDocResource sourceDocResource, - ITranslatedDocResource translationResources, URI uri) { - super(opts, factory, sourceDocResource, translationResources, uri); - copyTransResource = factory.getCopyTransResource(); - asyncProcessResource = factory.getAsynchronousProcessResource(); + public PushCommand(PushOptions opts, + CopyTransClient copyTransClient, + AsyncProcessClient asyncProcessClient, + RestClientFactory clientFactory) { + super(opts, + clientFactory); + this.copyTransClient = copyTransClient; + this.asyncProcessClient = asyncProcessClient; } public AbstractPushStrategy getStrategy(PushOptions pushOptions) { @@ -160,12 +159,15 @@ public static void logOptions(Logger logger, PushOptions opts) { logger.info("Pushing source and target documents"); logger.info("Locales to push: {}", opts.getLocaleMapList()); } + logger.info("Current directory: {}", System.getProperty("user.dir")); logger.info("Source directory (originals): {}", opts.getSrcDir()); if (opts.getPushType() == PushPullType.Both || opts.getPushType() == PushPullType.Trans) { logger.info("Target base directory (translations): {}", opts.getTransDir()); + + logger.info("Is my translations: {}", opts.isMyTrans()); } if (opts.getFromDoc() != null) { logger.info("From document: {}", opts.getFromDoc()); @@ -340,7 +342,7 @@ private void pushCurrentModule() throws IOException, RuntimeException { if (pushTrans() && getOpts().getLocaleMapList() == null) { throw new ConfigException("pushType set to '" + getOpts().getPushType() - + "', but zanata.xml contains no "); + + "', but project has no locales configured"); } if (pushTrans()) { @@ -424,7 +426,7 @@ public void visit(LocaleMapping locale, } } catch (Exception e) { String message = - "Operation failed: "+e.getMessage()+"\n\n" + "Operation failed: " + e.getMessage() + "\n\n" + " To retry from the last document, please set the following option(s):\n\n" + " "; if (getOpts().getEnableModules()) { @@ -441,6 +443,15 @@ public void visit(LocaleMapping locale, qualifiedDocName(localDocName)) + "\n\n."; log.error(message); + if (e instanceof UniformInterfaceException) { + String entity = + ((UniformInterfaceException) e).getResponse() + .getEntity(String.class); + + throw new RuntimeException(String.format( + "%n * Error Message: %s;%n * Response From Server: %s]", + e.getMessage(), entity)); + } throw new RuntimeException(e.getMessage(), e); } } @@ -453,14 +464,25 @@ private static void stripUntranslatedEntriesIfMergeTypeIsNotImport( if (!MergeType.IMPORT.name().equalsIgnoreCase(mergeType)) { List originalTargets = translationResources.getTextFlowTargets(); - Collection untranslatedEntries = Collections2 - .filter(originalTargets, + final Predicate blankStringPredicate = + new Predicate() { + @Override + public boolean apply(String input) { + return Strings.isNullOrEmpty(input); + } + }; + + Collection untranslatedEntries = + filter(originalTargets, new Predicate() { @Override public boolean apply( TextFlowTarget input) { - return input == null || - input.getState().isUntranslated(); + // it's unsafe to rely on content state (plural entries) + return input == null + || input.getContents().isEmpty() + || all(input.getContents(), + blankStringPredicate); } }); log.debug( @@ -537,7 +559,7 @@ private void pushSrcDocToServer(final String docUri, final Resource srcDoc, // is deprecated. // see PushCommand.copyTransForDocument ProcessStatus status = - asyncProcessResource.startSourceDocCreationOrUpdate(docUri, + asyncProcessClient.startSourceDocCreationOrUpdate(docUri, getOpts().getProj(), getOpts().getProjectVersion(), srcDoc, extensions, false); @@ -566,7 +588,7 @@ private void pushSrcDocToServer(final String docUri, final Resource srcDoc, case NotAccepted: // try to submit the process again status = - asyncProcessResource + asyncProcessClient .startSourceDocCreationOrUpdate(docUri, getOpts().getProj(), getOpts() .getProjectVersion(), @@ -576,8 +598,9 @@ private void pushSrcDocToServer(final String docUri, final Resource srcDoc, break; } - wait(POLL_PERIOD); // Wait before retrying - status = asyncProcessResource.getProcessStatus(status.getUrl()); + // Wait before retrying + wait(POLL_PERIOD); + status = asyncProcessClient.getProcessStatus(status.getUrl()); } ConsoleUtils.endProgressFeedback(); @@ -653,11 +676,12 @@ private void pushTargetDocToServer(final String docUri, ConsoleUtils.startProgressFeedback(); ProcessStatus status = - asyncProcessResource.startTranslatedDocCreationOrUpdate( + asyncProcessClient.startTranslatedDocCreationOrUpdate( docUri, getOpts().getProj(), getOpts() .getProjectVersion(), new LocaleId(locale.getLocale()), targetDoc, - extensions, getOpts().getMergeType()); + extensions, getOpts().getMergeType(), + getOpts().isMyTrans()); boolean waitForCompletion = true; @@ -685,20 +709,22 @@ private void pushTargetDocToServer(final String docUri, case NotAccepted: // try to submit the process again status = - asyncProcessResource + asyncProcessClient .startTranslatedDocCreationOrUpdate(docUri, getOpts().getProj(), getOpts() .getProjectVersion(), new LocaleId(locale.getLocale()), - targetDoc, extensions, getOpts() - .getMergeType()); + targetDoc, extensions, + getOpts().getMergeType(), + getOpts().isMyTrans()); ConsoleUtils .setProgressFeedbackMessage("Waiting for other clients ..."); break; } - wait(POLL_PERIOD); // Wait before retrying - status = asyncProcessResource.getProcessStatus(status.getUrl()); + // Wait before retrying + wait(POLL_PERIOD); + status = asyncProcessClient.getProcessStatus(status.getUrl()); } ConsoleUtils.endProgressFeedback(); @@ -721,10 +747,7 @@ private void deleteSourceDocFromServer(String qualifiedDocName) { if (!getOpts().isDryRun()) { log.info("deleting resource {} from server", qualifiedDocName); String docUri = RestUtil.convertToDocumentURIId(qualifiedDocName); - ClientResponse deleteResponse = - sourceDocResource.deleteResource(docUri); - ClientUtility.checkResult(deleteResponse, uri); - deleteResponse.releaseConnection(); + sourceDocResourceClient.deleteResource(docUri); } else { log.info( "deleting resource {} from server (skipped due to dry run)", @@ -739,7 +762,7 @@ private void copyTransForDocument(String docName) { } log.info("Running Copy Trans for " + docName); try { - this.copyTransResource.startCopyTrans(getOpts().getProj(), + this.copyTransClient.startCopyTrans(getOpts().getProj(), getOpts().getProjectVersion(), docName); } catch (Exception ex) { log.warn("Could not start Copy Trans for above document. Proceeding"); @@ -749,12 +772,12 @@ private void copyTransForDocument(String docName) { try { copyTransStatus = - this.copyTransResource.getCopyTransStatus(getOpts() + this.copyTransClient.getCopyTransStatus(getOpts() .getProj(), getOpts().getProjectVersion(), docName); - } catch (ClientResponseFailure failure) { + } catch (UniformInterfaceException failure) { // 404 - Probably because of an old server - if (failure.getResponse().getResponseStatus() == Response.Status.NOT_FOUND) { - if (getRequestFactory() + if (failure.getResponse().getClientResponseStatus() == ClientResponse.Status.NOT_FOUND) { + if (getClientFactory() .compareToServerVersion("1.8.0-SNAPSHOT") < 0) { log.warn("Copy Trans not started (Incompatible server version.)"); return; @@ -768,8 +791,7 @@ private void copyTransForDocument(String docName) { } else { throw new RuntimeException( "Problem invoking copy trans: [Server response code:" - + failure.getResponse().getResponseStatus() - .getStatusCode() + "]"); + + failure.getResponse().getStatus() + "]"); } } ConsoleUtils.startProgressFeedback(); @@ -783,7 +805,7 @@ private void copyTransForDocument(String docName) { ConsoleUtils.setProgressFeedbackMessage(copyTransStatus .getPercentageComplete() + "%"); copyTransStatus = - this.copyTransResource.getCopyTransStatus(getOpts() + this.copyTransClient.getCopyTransStatus(getOpts() .getProj(), getOpts().getProjectVersion(), docName); } ConsoleUtils.endProgressFeedback(); diff --git a/zanata-client-commands/src/main/java/org/zanata/client/commands/push/PushOptions.java b/zanata-client-commands/src/main/java/org/zanata/client/commands/push/PushOptions.java index e7553e4b..34b24edd 100644 --- a/zanata-client-commands/src/main/java/org/zanata/client/commands/push/PushOptions.java +++ b/zanata-client-commands/src/main/java/org/zanata/client/commands/push/PushOptions.java @@ -54,4 +54,6 @@ public interface PushOptions extends PushPullOptions { public String getValidate(); + public boolean isMyTrans(); + } diff --git a/zanata-client-commands/src/main/java/org/zanata/client/commands/push/PushOptionsImpl.java b/zanata-client-commands/src/main/java/org/zanata/client/commands/push/PushOptionsImpl.java index ba386fb3..85c895b7 100644 --- a/zanata-client-commands/src/main/java/org/zanata/client/commands/push/PushOptionsImpl.java +++ b/zanata-client-commands/src/main/java/org/zanata/client/commands/push/PushOptionsImpl.java @@ -40,6 +40,7 @@ public class PushOptionsImpl extends AbstractPushPullOptionsImpl private static final boolean DEF_CASE_SENSITIVE = true; private static final boolean DEF_EXCLUDE_LOCALES = true; private static final boolean DEF_COPYTRANS = true; + private static final boolean DEF_MY_TRANS = false; private static final int DEF_CHUNK_SIZE = 1024 * 1024; /** @see org.zanata.common.MergeType for options */ private static final String DEF_MERGE_TYPE = "AUTO"; @@ -58,6 +59,7 @@ public class PushOptionsImpl extends AbstractPushPullOptionsImpl private String sourceLang = "en-US"; private String validate; + private boolean myTrans = DEF_MY_TRANS; @Override public ZanataCommand initCommand() { @@ -92,7 +94,7 @@ public void setSourceLang(String sourceLang) { @Option(aliases = { "-l" }, name = "--locales", metaVar = "LOCALE1,LOCALE2,...", usage = "Locales to push to the server.\n" - + "By default all locales in zanata.xml will be pushed.") + + "By default all locales configured will be pushed.") public void setLocales(String locales) { this.locales = locales.split(","); } @@ -187,8 +189,27 @@ public ImmutableList getFileTypes() { return fileTypes; } + private static final String fileTypeHelp = "File types to locate and transmit to the server. \n" + + "Default file extension will be used unless it is being specified. \n" + + "Pattern: TYPE[extension;extension],TYPE[extension] \n" + + "Supported types: \n" + + "\t XML_DOCUMENT_TYPE_DEFINITION[xml] \n" + + "\t PLAIN_TEXT[txt] \n" + + "\t IDML[idml] \n" + + "\t HTML[html;htm] \n" + + "\t OPEN_DOCUMENT_TEXT[odt] \n" + + "\t OPEN_DOCUMENT_PRESENTATION[odp] \n" + + "\t OPEN_DOCUMENT_GRAPHICS[odg] \n" + + "\t OPEN_DOCUMENT_SPREADSHEET[ods] \n" + + "\t SUBTITLE[srt;sbt;sub;vtt] \n" + + "\t GETTEXT[pot] \n" + + "\t PROPERTIES[properties] \n" + + "\t PROPERTIES_UTF8[properties] \n" + + "\t XLIFF[xml] \n" + + "Usage --file-types \"XML_DOCUMENT_TYPE_DEFINITION,IDML[txt]\""; + @Option(name = "--file-types", metaVar = "TYPES", - usage = "File types to locate and transmit to the server.") + usage = fileTypeHelp) public void setFileTypes(String fileTypes) { this.fileTypes = ImmutableList.copyOf(StringUtil.split(fileTypes, ",")); } @@ -216,8 +237,8 @@ public boolean getExcludeLocaleFilenames() { @Option( name = "--exclude-locale-filenames", handler = BooleanValueHandler.class, - usage = "Exclude filenames which match locales in zanata.xml (other than the\n" - + "source locale). For instance, if zanata.xml includes de and fr,\n" + usage = "Exclude filenames which match project configured locales (other than the\n" + + "source locale). For instance, if project includes de and fr,\n" + "then the files messages_de.properties and messages_fr.properties\n" + "will not be treated as source files.\n" + "NB: This parameter will be ignored for some project types which use\n" @@ -242,4 +263,15 @@ void setValidate(String validate) { this.validate = validate; } + @Override + public boolean isMyTrans() { + return myTrans; + } + + @Option( + name = "--my-trans", + usage = "Indicates all uploaded translations were translated by you ") + public void setMyTrans(boolean myTrans) { + this.myTrans = myTrans; + } } diff --git a/zanata-client-commands/src/main/java/org/zanata/client/commands/push/RawPushCommand.java b/zanata-client-commands/src/main/java/org/zanata/client/commands/push/RawPushCommand.java index 43f53d67..5540cc77 100644 --- a/zanata-client-commands/src/main/java/org/zanata/client/commands/push/RawPushCommand.java +++ b/zanata-client-commands/src/main/java/org/zanata/client/commands/push/RawPushCommand.java @@ -27,36 +27,52 @@ import java.io.FileNotFoundException; import java.io.IOException; import java.io.InputStream; -import java.net.URI; import java.security.DigestInputStream; import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; -import java.util.ArrayList; import java.util.Arrays; +import java.util.Collections; +import java.util.HashMap; +import java.util.HashSet; import java.util.Iterator; import java.util.List; +import java.util.Map; +import java.util.NoSuchElementException; +import java.util.Set; import java.util.SortedSet; import java.util.TreeSet; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +import javax.annotation.Nullable; -import com.google.common.collect.ImmutableList; import org.apache.commons.codec.binary.Hex; -import org.jboss.resteasy.client.ClientResponse; +import org.apache.commons.io.FilenameUtils; +import org.apache.commons.lang.StringUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import org.zanata.client.commands.ConsoleInteractor; +import org.zanata.client.commands.ConsoleInteractorImpl; +import org.zanata.client.commands.Messages; import org.zanata.client.commands.PushPullCommand; import org.zanata.client.commands.PushPullType; import org.zanata.client.commands.push.RawPushStrategy.TranslationFilesVisitor; import org.zanata.client.config.LocaleMapping; import org.zanata.client.exceptions.ConfigException; +import org.zanata.client.exceptions.InvalidUserInputException; import org.zanata.client.util.ConsoleUtils; +import org.zanata.common.DocumentType; import org.zanata.rest.DocumentFileUploadForm; -import org.zanata.rest.StringSet; -import org.zanata.rest.client.IFileResource; -import org.zanata.rest.client.ISourceDocResource; -import org.zanata.rest.client.ITranslatedDocResource; -import org.zanata.rest.client.ZanataProxyFactory; +import org.zanata.rest.client.FileResourceClient; +import org.zanata.rest.client.RestClientFactory; import org.zanata.rest.dto.ChunkUploadResponse; +import com.google.common.annotations.VisibleForTesting; +import com.google.common.base.Optional; +import com.google.common.collect.ImmutableList; + +import static org.zanata.client.commands.ConsoleInteractor.DisplayMode; + /** * Command to send files directly to the server without parsing on the client. * @@ -68,24 +84,173 @@ public class RawPushCommand extends PushPullCommand { private static final Logger log = LoggerFactory .getLogger(PushCommand.class); - protected final IFileResource fileResource; + private static final Pattern fileNameExtensionsPattern = Pattern.compile( + "(?:([^\\[]*)?(?:\\[(.*?)\\])?)"); + + private final ConsoleInteractor consoleInteractor; + + private FileResourceClient client; public RawPushCommand(PushOptions opts) { super(opts); - this.fileResource = getRequestFactory().getFileResource(); + client = getClientFactory().getFileResourceClient(); + consoleInteractor = new ConsoleInteractorImpl(opts); + } + + public RawPushCommand(PushOptions opts, RestClientFactory clientFactory) { + super(opts, clientFactory); + client = getClientFactory().getFileResourceClient(); + consoleInteractor = new ConsoleInteractorImpl(opts); + } + + @VisibleForTesting + protected RawPushCommand(PushOptions opts, RestClientFactory clientFactory, + ConsoleInteractor console) { + super(opts, clientFactory); + client = getClientFactory().getFileResourceClient(); + this.consoleInteractor = console; } - public RawPushCommand(PushOptions opts, ZanataProxyFactory factory, - ISourceDocResource sourceDocResource, - ITranslatedDocResource translationResources, URI uri) { - super(opts, factory, sourceDocResource, translationResources, uri); - this.fileResource = factory.getFileResource(); + /** + * Extract extensions from input string + */ + public List extractExtensions(String typeWithExtension) { + Matcher matcher = fileNameExtensionsPattern.matcher(typeWithExtension); + if (matcher.find()) { + String rawExtensions = matcher.group(2); + if (!StringUtils.isEmpty(rawExtensions)) { + return Arrays.asList(rawExtensions.split(";")); + } + } + return Collections.emptyList(); + } + + /** + * Extract fileType from input string + * input pattern: fileType[extension;extension], [extension;extension] + * + * @param typeWithExtension + * @return result[0] - type, result[1] - extensions. e.g [txt,html] + */ + public @Nullable String extractType(String typeWithExtension) { + Matcher matcher = fileNameExtensionsPattern.matcher(typeWithExtension); + if (matcher.find()) { + return matcher.group(1); + } + return null; + } + + /** + * Validate inputFileType to make sure it's not empty + * + * @param inputFileType + * @param inputExtensions + * @param acceptedTypes + */ + private void validateInputFileType(String inputFileType, + List inputExtensions, List acceptedTypes) { + if (StringUtils.isNotBlank(inputFileType)) { + return; + } + if (inputExtensions.isEmpty()) { + //throw error if inputFileType and inputExtensions is empty + throw new InvalidUserInputException( + "Invalid expression for '--file-types' option"); + } else { + //suggest --file-types options for this extension + for (DocumentType docType: acceptedTypes) { + for (String extension: docType.getSourceExtensions()) { + if (inputExtensions.contains(extension)) { + String msg = + Messages.format( + "file.type.suggestFromExtension", + docType, + extension, docType); + throw new InvalidUserInputException(msg); + } + } + } + } + } + + /** + * Return map of validated type with set of extensions + * + * Validate user input file types against server accepted file types + * + * Valid input - properties_utf8,properties[txt],plain_text[md;markdown] + * + * - Each file type is only input once - e.g. + * - valid: "html,properties,txt" + * - invalid: "html,properties,html" + * - Same file extension cannot be in multiple file type - e.g. plain_text[txt],properties[txt] + * + * @param acceptedTypes + * @param inputFileTypes + */ + private Map> validateFileTypes( + List acceptedTypes, List inputFileTypes) { + + Map> filteredFileTypes = new HashMap<>(); + + for (String typeWithExtension : inputFileTypes) { + String fileType = extractType(typeWithExtension); + List extensions = extractExtensions(typeWithExtension); + + validateInputFileType(fileType, extensions, acceptedTypes); + + DocumentType inputFileType = DocumentType.getByName(fileType); + + //skip file type if its not supported + if (inputFileType == null) { + String msg = Messages.format("file.type.typeNotSupported", fileType); + consoleInteractor.printfln(DisplayMode.Warning, msg); + continue; + } + + //throw error if file type is input more than once + if (filteredFileTypes.containsKey(inputFileType)) { + String msg = + Messages.format("file.type.duplicateFileType", fileType); + log.error(msg); + throw new RuntimeException(msg); + } + + Set filteredExtensions = new HashSet<>(extensions); + + /** + * Use the extensions from typeWithExtension input if exists, + * otherwise, use the extensions from server. + */ + filteredExtensions = + filteredExtensions.isEmpty() ? inputFileType.getSourceExtensions() + : filteredExtensions; + + //throw error if same file extension found in multiple file type + for (Map.Entry> entry: filteredFileTypes.entrySet()) { + for (String filteredExtension: entry.getValue()) { + if (filteredExtensions.contains(filteredExtension)) { + String msg = + Messages.format( + "file.type.conflictExtension", + fileType, entry.getKey().name(), + filteredExtension); + log.error(msg); + throw new RuntimeException(msg); + } + } + } + filteredFileTypes.put(inputFileType, filteredExtensions); + } + return filteredFileTypes; } @Override public void run() throws IOException { PushCommand.logOptions(log, getOpts()); - log.warn("Using EXPERIMENTAL project type 'file'."); + + consoleInteractor.printfln(DisplayMode.Warning, + "Using EXPERIMENTAL project type 'file'."); // only supporting single module for now @@ -94,7 +259,9 @@ public void run() throws IOException { boolean enableModules = getOpts().getEnableModules(); // TODO remove when modules implemented if (enableModules) { - log.warn("enableModules=true but multi-modules not yet supported for this command. Using single module push."); + consoleInteractor + .printfln(DisplayMode.Warning, + "enableModules=true but multi-modules not yet supported for this command. Using single module push."); } throw new RuntimeException("directory '" + sourceDir @@ -105,38 +272,34 @@ public void run() throws IOException { RawPushStrategy strat = new RawPushStrategy(); strat.setPushOptions(getOpts()); - ImmutableList.Builder builder = ImmutableList.builder(); - - ClientResponse response = fileResource.acceptedFileTypes(); @SuppressWarnings("MismatchedQueryAndUpdateOfCollection") - StringSet serverAcceptedTypes = new StringSet(response.getEntity()); - for (String type : getOpts().getFileTypes()) { - if (serverAcceptedTypes.contains(type)) { - builder.add(type); - } else { - log.warn( - "Requested type '{}' is not supported by the target server and will be ignored.", - type); - } - } + List serverAcceptedTypes = client.acceptedFileTypes(); - ImmutableList types = builder.build(); + Map> filteredDocTypes = + validateFileTypes(serverAcceptedTypes, getOpts().getFileTypes()); - if (types.isEmpty()) { + if (filteredDocTypes.isEmpty()) { log.info("no valid types specified; nothing to do"); return; } + ImmutableList.Builder sourceFileExtensionsBuilder = ImmutableList.builder(); + for (Set filteredSourceExtensions : filteredDocTypes.values()) { + sourceFileExtensionsBuilder.addAll(filteredSourceExtensions); + } + String[] srcFiles = strat.getSrcFiles(sourceDir, getOpts().getIncludes(), getOpts() - .getExcludes(), types, true, getOpts() + .getExcludes(), sourceFileExtensionsBuilder.build(), true, getOpts() .getCaseSensitive()); SortedSet localDocNames = new TreeSet(Arrays.asList(srcFiles)); // TODO handle obsolete document deletion - log.warn("Obsolete document removal is not yet implemented, no documents will be removed from the server."); + consoleInteractor + .printfln(DisplayMode.Warning, + "Obsolete document removal is not yet implemented, no documents will be removed from the server."); SortedSet docsToPush = localDocNames; if (getOpts().getFromDoc() != null) { @@ -159,12 +322,17 @@ public void run() throws IOException { .getCurrentModule()); return; } else { - log.info("Found source documents:"); + consoleInteractor.printfln("Found source documents:"); for (String docName : localDocNames) { if (docsToPush.contains(docName)) { - log.info(" {}", docName); + DocumentType fileType = getFileType(filteredDocTypes, + FilenameUtils.getExtension(docName)); + consoleInteractor.printfln(" " + + Messages.format("push.info.documentToPush", + docName, fileType.name())); } else { - log.info("(to skip) {}", docName); + consoleInteractor.printfln( + Messages.format("push.info.skipDocument", docName)); } } } @@ -174,17 +342,13 @@ public void run() throws IOException { if (getOpts().getLocaleMapList() == null) throw new ConfigException("pushType set to '" + getOpts().getPushType() - + "', but zanata.xml contains no "); - log.warn("pushType set to '" - + getOpts().getPushType() - + "': existing translations on server may be overwritten/deleted"); + + "', but project has no locales configured"); + consoleInteractor.printfln(DisplayMode.Warning, Messages.format( + "push.warn.overrideTranslations", getOpts().getPushType())); if (getOpts().getPushType() == PushPullType.Both) { - confirmWithUser("This will overwrite existing documents AND TRANSLATIONS on the server.\n"); // , - // and - // delete - // obsolete - // documents.\n"); + confirmWithUser("This will overwrite existing documents AND TRANSLATIONS on the server.\n"); + // , and delete obsolete documents.\n"); } else if (getOpts().getPushType() == PushPullType.Trans) { confirmWithUser("This will overwrite existing TRANSLATIONS on the server.\n"); } @@ -197,16 +361,17 @@ public void run() throws IOException { for (final String localDocName : docsToPush) { try { - final String fileType = getExtensionFor(localDocName); + final String srcExtension = FilenameUtils.getExtension(localDocName); + final DocumentType fileType = getFileType(filteredDocTypes, + srcExtension); final String qualifiedDocName = qualifiedDocName(localDocName); - boolean sourcePushed; if (getOpts().getPushType() == PushPullType.Source || getOpts().getPushType() == PushPullType.Both) { if (!getOpts().isDryRun()) { - sourcePushed = + boolean sourcePushed = pushSourceDocumentToServer(sourceDir, localDocName, qualifiedDocName, - fileType); + fileType.name()); // ClientUtility.checkResult(putResponse, uri); if (!sourcePushed) { hasErrors = true; @@ -220,6 +385,10 @@ public void run() throws IOException { if (getOpts().getPushType() == PushPullType.Trans || getOpts().getPushType() == PushPullType.Both) { + + Optional translationFileExtension = + getTranslationFileExtension(fileType, srcExtension); + strat.visitTranslationFiles(localDocName, new TranslationFilesVisitor() { @@ -230,14 +399,14 @@ public void visit(LocaleMapping locale, locale.getLocale(), qualifiedDocName); pushDocumentToServer(qualifiedDocName, - fileType, locale.getLocale(), + fileType.name(), locale.getLocale(), translatedDoc); } - }); + }, translationFileExtension); } } catch (IOException | RuntimeException e) { log.error( - "Operation failed: "+e.getMessage()+"\n\n" + "Operation failed: " + e.getMessage() + "\n\n" + " To retry from the last document, please add the option: {}\n", getOpts().buildFromDocArgument(localDocName)); throw new RuntimeException(e.getMessage(), e); @@ -252,16 +421,35 @@ public void visit(LocaleMapping locale, } /** - * @param localDocName - * @return extension of document (all characters after final '.'), or null - * if no characters after a final . are found. + * Get translation file extension in given docType with the srcExtension. + * If no extension found, return source file extension. + * + * @param docType + * @param srcExtension + */ + private Optional getTranslationFileExtension(DocumentType docType, + String srcExtension) { + String transFileExtension = docType.getExtensions().get(srcExtension); + + return StringUtils.isEmpty(transFileExtension) ? Optional + .of(srcExtension) : Optional.of(transFileExtension); + } + + /** + * Search and return file type with given source file extension + * + * @param fileTypes + * @param srcExtension */ - private String getExtensionFor(final String localDocName) { - if (localDocName == null || localDocName.length() == 0 - || localDocName.endsWith(".") || !localDocName.contains(".")) { - return null; + private @Nullable DocumentType getFileType(Map> fileTypes, + String srcExtension) { + for (Map.Entry> entry : fileTypes.entrySet()) { + + if (entry.getValue().contains(srcExtension)) { + return entry.getKey(); + } } - return localDocName.substring(localDocName.lastIndexOf('.') + 1); + return null; } /** @@ -303,16 +491,14 @@ private void pushDocumentToServer(String docId, String fileType, DocumentFileUploadForm uploadForm = generateUploadForm(true, true, fileType, md5hash, docFile.length(), fileStream); - ClientResponse response = - uploadDocumentPart(docId, locale, uploadForm); - checkChunkUploadStatus(response); + uploadDocumentPart(docId, locale, uploadForm); } } else { try (StreamChunker chunker = new StreamChunker(docFile, getOpts().getChunkSize())) { log.info(" transmitting file [{}] as {} chunks", docFile.getAbsolutePath(), chunker.totalChunks()); - ClientResponse uploadResponse; + ChunkUploadResponse uploadResponse; DocumentFileUploadForm uploadForm; Long uploadId = null; @@ -332,9 +518,8 @@ private void pushDocumentToServer(String docId, String fileType, } uploadResponse = uploadDocumentPart(docId, locale, uploadForm); - checkChunkUploadStatus(uploadResponse); if (isFirst) { - uploadId = uploadResponse.getEntity().getUploadId(); + uploadId = uploadResponse.getUploadId(); if (uploadId == null) { throw new RuntimeException( "server did not return upload id"); @@ -348,15 +533,6 @@ private void pushDocumentToServer(String docId, String fileType, } } - private void checkChunkUploadStatus( - ClientResponse uploadResponse) { - if (uploadResponse.getStatus() >= 300) { - throw new RuntimeException("Server returned error status: " - + uploadResponse.getStatus() + ". Error message: " - + uploadResponse.getEntity().getErrorMessage()); - } - } - private DocumentFileUploadForm generateUploadForm(boolean isFirst, boolean isLast, String fileType, String md5hash, long streamSize, InputStream fileStream) { @@ -370,21 +546,21 @@ private DocumentFileUploadForm generateUploadForm(boolean isFirst, return uploadForm; } - private ClientResponse uploadDocumentPart( + private ChunkUploadResponse uploadDocumentPart( String docName, String locale, DocumentFileUploadForm uploadForm) { ConsoleUtils.startProgressFeedback(); - ClientResponse response; + ChunkUploadResponse response; if (locale == null) { response = - fileResource.uploadSourceFile(getOpts().getProj(), + client.uploadSourceFile(getOpts().getProj(), getOpts().getProjectVersion(), docName, uploadForm); } else { response = - fileResource.uploadTranslationFile(getOpts().getProj(), + client.uploadTranslationFile(getOpts().getProj(), getOpts().getProjectVersion(), locale, docName, getOpts().getMergeType(), uploadForm); } - log.debug("response from server: {}", response.getEntity()); + log.debug("response from server: {}", response); ConsoleUtils.endProgressFeedback(); return response; } @@ -457,11 +633,6 @@ public int getRemainingChunks() { } private InputStream getNextChunk() { - if (chunksRetrieved == totalChunkCount) { - throw new IllegalStateException( - "getNextChunk() must not be called after all chunks have been retrieved"); - } - try { actualChunkSize = fileStream.read(buffer); } catch (IOException e) { @@ -493,7 +664,12 @@ public boolean hasNext() { @Override public InputStream next() { - return getNextChunk(); + if (hasNext()) { + return getNextChunk(); + } else { + throw new NoSuchElementException( + "getNextChunk() must not be called after all chunks have been retrieved"); + } } @Override diff --git a/zanata-client-commands/src/main/java/org/zanata/client/commands/push/RawPushStrategy.java b/zanata-client-commands/src/main/java/org/zanata/client/commands/push/RawPushStrategy.java index a4c1f919..112a7353 100644 --- a/zanata-client-commands/src/main/java/org/zanata/client/commands/push/RawPushStrategy.java +++ b/zanata-client-commands/src/main/java/org/zanata/client/commands/push/RawPushStrategy.java @@ -28,6 +28,8 @@ import org.zanata.client.commands.TransFileResolver; import org.zanata.client.config.LocaleMapping; +import com.google.common.base.Optional; + /** * Strategy for uploading documents using the document file upload API methods. * @@ -55,15 +57,15 @@ public static interface TranslationFilesVisitor { * @param visitor */ public void visitTranslationFiles(String sourceDocument, - TranslationFilesVisitor visitor) { + TranslationFilesVisitor visitor, Optional translationExtension) { if (getOpts().getLocaleMapList() == null) { - log.error("Locale mapping list not found, unable to push translations. Check mapping in zanata.xml"); + log.error("Locale mapping list not found, unable to push translations. Check your server settings."); return; } for (LocaleMapping localeMapping : getOpts().getLocaleMapList()) { File translationFile = new TransFileResolver(getOpts()) .resolveTransFile(QualifiedSrcDocName.from( - sourceDocument), localeMapping); + sourceDocument), localeMapping, translationExtension); if (translationFile.canRead()) { visitor.visit(localeMapping, translationFile); diff --git a/zanata-client-commands/src/main/java/org/zanata/client/commands/stats/ConsoleStatisticsOutput.java b/zanata-client-commands/src/main/java/org/zanata/client/commands/stats/ConsoleStatisticsOutput.java index 832e5bf5..8ce6da12 100644 --- a/zanata-client-commands/src/main/java/org/zanata/client/commands/stats/ConsoleStatisticsOutput.java +++ b/zanata-client-commands/src/main/java/org/zanata/client/commands/stats/ConsoleStatisticsOutput.java @@ -78,7 +78,7 @@ public int compare(TranslationStatistics o1, TranslationStatistics s = stats.get(i); data[i] = new Object[] { s.getLocale(), s.getUnit(), s.getTotal(), - s.getTranslated(), s.getNeedReview(), + s.getTranslatedAndApproved(), s.getDraft(), s.getUntranslated(), s.getLastTranslated() }; } diff --git a/zanata-client-commands/src/main/java/org/zanata/client/commands/stats/CsvStatisticsOutput.java b/zanata-client-commands/src/main/java/org/zanata/client/commands/stats/CsvStatisticsOutput.java index a4608ad3..898f95a9 100644 --- a/zanata-client-commands/src/main/java/org/zanata/client/commands/stats/CsvStatisticsOutput.java +++ b/zanata-client-commands/src/main/java/org/zanata/client/commands/stats/CsvStatisticsOutput.java @@ -81,8 +81,8 @@ private void writeToCsv(ContainerTranslationStatistics statistics, writer.writeNext(new String[] { transStats.getLocale(), transStats.getUnit().toString(), Long.toString(transStats.getTotal()), - Long.toString(transStats.getTranslated()), - Long.toString(transStats.getNeedReview()), + Long.toString(transStats.getTranslatedAndApproved()), + Long.toString(transStats.getDraft()), Long.toString(transStats.getUntranslated()), transStats.getLastTranslated() }); } diff --git a/zanata-client-commands/src/main/java/org/zanata/client/commands/stats/GetStatisticsCommand.java b/zanata-client-commands/src/main/java/org/zanata/client/commands/stats/GetStatisticsCommand.java index 1e47d92b..3d27f772 100644 --- a/zanata-client-commands/src/main/java/org/zanata/client/commands/stats/GetStatisticsCommand.java +++ b/zanata-client-commands/src/main/java/org/zanata/client/commands/stats/GetStatisticsCommand.java @@ -20,16 +20,14 @@ */ package org.zanata.client.commands.stats; -import java.util.ArrayList; -import java.util.List; - import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.zanata.client.commands.ConfigurableCommand; +import org.zanata.client.config.LocaleList; import org.zanata.client.config.LocaleMapping; import org.zanata.client.util.ConsoleUtils; +import org.zanata.rest.client.StatisticsResourceClient; import org.zanata.rest.dto.stats.ContainerTranslationStatistics; -import org.zanata.rest.service.StatisticsResource; /** * @author Carlos Munoz { private static final Logger log = LoggerFactory .getLogger(GetStatisticsCommand.class); + private final StatisticsResourceClient client; - private StatisticsResource statsResource; public GetStatisticsCommand(GetStatisticsOptions opts) { super(opts); - statsResource = getRequestFactory().getStatisticsResource(); + client = getClientFactory().getStatisticsClient(); } @Override @@ -53,13 +51,13 @@ public void run() throws Exception { String[] localeListArg = null; if (getOpts().getLocaleMapList() != null) { - List localeList = new ArrayList(); - + localeListArg = new String[getOpts().getLocaleMapList().size()]; // Get the locales from the mappings list - for (LocaleMapping locMapping : getOpts().getLocaleMapList()) { - localeList.add(locMapping.getLocale()); + LocaleList localeMapList = getOpts().getLocaleMapList(); + for (int i = 0; i < localeMapList.size(); i++) { + LocaleMapping locMapping = localeMapList.get(i); + localeListArg[i] = locMapping.getLocale(); } - localeListArg = localeList.toArray(new String[] {}); } ContainerTranslationStatistics containerStats; @@ -88,15 +86,14 @@ public void run() throws Exception { // Document Id not specified if (getOpts().getDocumentId() == null) { containerStats = - statsResource.getStatistics(getOpts().getProj(), getOpts() + client.getStatistics(getOpts().getProj(), getOpts() .getProjectVersion(), getOpts().getIncludeDetails(), getOpts() .getIncludeWordLevelStats(), localeListArg); - } - // Otherwise, stats for the single document - else { + } else { + // Otherwise, stats for the single document containerStats = - statsResource + client .getStatistics(getOpts().getProj(), getOpts() .getProjectVersion(), getOpts() .getDocumentId(), getOpts() @@ -110,17 +107,18 @@ public void run() throws Exception { // Select the format (output) ContainerStatisticsCommandOutput statsOutput; - // csv - if ("csv".equalsIgnoreCase(getOpts().getFormat())) { - statsOutput = new CsvStatisticsOutput(); - } else { - if (!"console".equalsIgnoreCase(getOpts().getFormat())) { + switch (getOpts().getFormat()) { + case "csv": + statsOutput = new CsvStatisticsOutput(); + break; + case "console": + statsOutput = new ConsoleStatisticsOutput(); + break; + default: log.warn( "Invalid format type '{}', using console format instead.", getOpts().getFormat()); - } - // Default: console - statsOutput = new ConsoleStatisticsOutput(); + statsOutput = new ConsoleStatisticsOutput(); } statsOutput.write(containerStats); diff --git a/zanata-client-commands/src/main/java/org/zanata/client/exceptions/InvalidUserInputException.java b/zanata-client-commands/src/main/java/org/zanata/client/exceptions/InvalidUserInputException.java new file mode 100644 index 00000000..277c58bb --- /dev/null +++ b/zanata-client-commands/src/main/java/org/zanata/client/exceptions/InvalidUserInputException.java @@ -0,0 +1,15 @@ +package org.zanata.client.exceptions; + +/** + * @author Alex Eng aeng@redhat.com + */ +public class InvalidUserInputException extends RuntimeException { + + public InvalidUserInputException() { + super(); + } + + public InvalidUserInputException(String message) { + super(message); + } +} diff --git a/zanata-client-commands/src/main/resources/prompts.properties b/zanata-client-commands/src/main/resources/prompts.properties index 7f09c78b..59283e73 100644 --- a/zanata-client-commands/src/main/resources/prompts.properties +++ b/zanata-client-commands/src/main/resources/prompts.properties @@ -89,3 +89,21 @@ no.default.mapping=Can not find default translation file mapping rule for projec invalid.rule=Rule defined is not valid (must contain locale): %s unrecognized.variables=Valid variables are %s. Anything else in '%s' will be treated as literal value. confirm.rule=Continue with current mapping rule(s) (y/n)? + +locales.in.config.deprecated=Locale mappings are now handled using locale aliases on the server, so locale mappings in the project config file (zanata.xml) are now deprecated.\nPlease add a locale alias in the project language settings to replace each locale mapping in zanata.xml, then remove the section from zanata.xml. + +# {0} - fileType, {1} - file extension, {2} - fileType +file.type.suggestFromExtension=Please use file type "{0}" for extension "{1}", e.g --file-types {2} +# {0} - fileType +file.type.duplicateFileType=File type "{0}" cannot be used more than once in option --file-types. +# {0} - fileType +file.type.typeNotSupported=Requested file type "{0}" is not supported by the target server and will be ignored. +# {0} - fileType1, {1} - fileType2, {2} - extension +file.type.conflictExtension=You have used extension "{2}" in both "{0}" and "{1}" for --file-types option. Same extension can only be used once. + +# {0} - pushType +push.warn.overrideTranslations=pushType set to "{0}". Existing translations on server may be overwritten/deleted. +# {0} - document name +push.info.skipDocument=(to skip) {0} +# {0} - document name, {1} - fileType +push.info.documentToPush={0} [{1}] \ No newline at end of file diff --git a/zanata-client-commands/src/test/java/org/zanata/client/MockServerRule.java b/zanata-client-commands/src/test/java/org/zanata/client/MockServerRule.java index 87da461e..efed9c39 100644 --- a/zanata-client-commands/src/test/java/org/zanata/client/MockServerRule.java +++ b/zanata-client-commands/src/test/java/org/zanata/client/MockServerRule.java @@ -21,17 +21,20 @@ package org.zanata.client; +import static org.mockito.Matchers.any; +import static org.mockito.Matchers.anySetOf; +import static org.mockito.Matchers.anyString; +import static org.mockito.Matchers.eq; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + import java.io.InputStream; -import java.lang.String; import java.net.URI; import java.util.Collections; import java.util.List; import java.util.Set; -import javax.ws.rs.core.MultivaluedHashMap; -import javax.ws.rs.core.Response; - -import org.jboss.resteasy.client.ClientResponse; import org.junit.rules.ExternalResource; import org.mockito.ArgumentCaptor; import org.mockito.Captor; @@ -45,15 +48,16 @@ import org.zanata.client.commands.push.PushOptionsImpl; import org.zanata.client.commands.push.RawPushCommand; import org.zanata.client.config.LocaleList; +import org.zanata.common.DocumentType; import org.zanata.common.LocaleId; import org.zanata.common.ProjectType; import org.zanata.rest.DocumentFileUploadForm; -import org.zanata.rest.client.IAsynchronousProcessResource; -import org.zanata.rest.client.ICopyTransResource; -import org.zanata.rest.client.IFileResource; -import org.zanata.rest.client.ISourceDocResource; -import org.zanata.rest.client.ITranslatedDocResource; -import org.zanata.rest.client.ZanataProxyFactory; +import org.zanata.rest.client.AsyncProcessClient; +import org.zanata.rest.client.CopyTransClient; +import org.zanata.rest.client.FileResourceClient; +import org.zanata.rest.client.RestClientFactory; +import org.zanata.rest.client.SourceDocResourceClient; +import org.zanata.rest.client.TransDocResourceClient; import org.zanata.rest.dto.ChunkUploadResponse; import org.zanata.rest.dto.ProcessStatus; import org.zanata.rest.dto.resource.Resource; @@ -61,46 +65,25 @@ import org.zanata.rest.dto.resource.TranslationsResource; import org.zanata.rest.service.FileResource; -import com.google.common.base.Joiner; import com.google.common.base.Throwables; - -import static org.mockito.Matchers.any; -import static org.mockito.Matchers.anySetOf; -import static org.mockito.Matchers.anyString; -import static org.mockito.Matchers.eq; -import static org.mockito.Mockito.times; -import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.when; +import com.sun.jersey.api.client.ClientResponse; +import com.sun.jersey.core.util.MultivaluedMapImpl; /** * Test rule to set up push and/or pull command(s) which will interact with a - * mock REST proxy factory and mocked REST resources. + * mockito mocked REST clients. * * @author Patrick Huang pahuang@redhat.com */ public class MockServerRule extends ExternalResource { - private URI uri; // async process statuses private String mockProcessId = "MockServerRuleProcess"; private ProcessStatus finished = new ProcessStatus(); private ProcessStatus running = new ProcessStatus(); private PullOptionsImpl pullOpts; - @Mock - private IAsynchronousProcessResource asyncResource; - @Mock - private ICopyTransResource copyTransResource; - @Mock - private IFileResource fileResource; - @Mock - private ZanataProxyFactory factory; - @Mock - private ISourceDocResource sourceDocResource; - @Mock - private ITranslatedDocResource transDocResource; + private PushOptionsImpl pushOpts; - @Mock - private ClientResponse> remoteDocListResponse; @Captor private ArgumentCaptor resourceCaptor; @Captor @@ -112,25 +95,28 @@ public class MockServerRule extends ExternalResource { @Captor private ArgumentCaptor transResourceCaptor; @Mock - private ClientResponse resourceResponse; - @Mock - private ClientResponse transResourceResponse; - @Mock - private ClientResponse acceptedFilesResponse; - @Mock - private ClientResponse chunkUploadResponse; + private ClientResponse transResourceResponse; @Captor private ArgumentCaptor uploadFormCaptor; @Mock private ClientResponse downloadSourceResponse; @Mock private ClientResponse downloadTransResponse; + @Mock + private RestClientFactory clientFactory; + @Mock + private CopyTransClient copyTransClient; + @Mock + private AsyncProcessClient asyncClient; + @Mock + private SourceDocResourceClient sourceDocClient; + @Mock + private TransDocResourceClient transDocClient; + @Mock + private FileResourceClient fileResourceClient; public MockServerRule() { MockitoAnnotations.initMocks(this); - when(factory.getCopyTransResource()).thenReturn(copyTransResource); - when(factory.getAsynchronousProcessResource()).thenReturn(asyncResource); - when(factory.getFileResource()).thenReturn(fileResource); // async process statuses running.setUrl(mockProcessId); running.setStatusCode(ProcessStatus.ProcessStatusCode.Running); @@ -164,8 +150,7 @@ private static URI setUrl(ConfigurableProjectOptions opts) { URI uri = new URI("http://localhost:8888/zanata"); opts.setUrl(uri.toURL()); return uri; - } - catch (Exception e) { + } catch (Exception e) { throw Throwables.propagate(e); } } @@ -181,29 +166,31 @@ private static URI setUrl(ConfigurableProjectOptions opts) { * @return push command */ public PushCommand createPushCommand() { - when(sourceDocResource.get(null)).thenReturn(remoteDocListResponse); - // this assumes no obsolete documents on server - when(remoteDocListResponse.getStatus()).thenReturn(200); - when(remoteDocListResponse.getEntity()).thenReturn( - Collections. emptyList()); + when(clientFactory.getSourceDocResourceClient(pushOpts.getProj(), + pullOpts.getProjectVersion())).thenReturn(sourceDocClient); + when(sourceDocClient.getResourceMeta(null)).thenReturn( + Collections.emptyList()); // this assumes async push is always success when( - asyncResource.startSourceDocCreationOrUpdate( + asyncClient.startSourceDocCreationOrUpdate( anyString(), eq(pushOpts.getProj()), eq(pushOpts.getProjectVersion()), any(Resource.class), anySetOf(String.class), eq(false))) .thenReturn(running); when( - asyncResource.startTranslatedDocCreationOrUpdate( + asyncClient.startTranslatedDocCreationOrUpdate( docIdCaptor.capture(), eq(pushOpts.getProj()), eq(pushOpts.getProjectVersion()), localeIdCaptor.capture(), transResourceCaptor.capture(), - extensionCaptor.capture(), eq(pushOpts.getMergeType()))) + extensionCaptor.capture(), eq(pushOpts.getMergeType()), + eq(pushOpts.isMyTrans()))) .thenReturn(running); - when(asyncResource.getProcessStatus(mockProcessId)) + when(asyncClient.getProcessStatus(mockProcessId)) .thenReturn(finished); - return new PushCommand(pushOpts, factory, sourceDocResource, transDocResource, uri); + return new PushCommand(pushOpts, + copyTransClient, + asyncClient, clientFactory); } public PushOptionsImpl getPushOpts() { @@ -231,18 +218,18 @@ public ArgumentCaptor getTransResourceCaptor() { } public void verifyPushSource() { - verify(asyncResource).startSourceDocCreationOrUpdate( + verify(asyncClient).startSourceDocCreationOrUpdate( docIdCaptor.capture(), eq(pushOpts.getProj()), eq(pushOpts.getProjectVersion()), resourceCaptor.capture(), extensionCaptor.capture(), eq(false)); } public void verifyPushTranslation() { - verify(asyncResource).startTranslatedDocCreationOrUpdate( + verify(asyncClient).startTranslatedDocCreationOrUpdate( docIdCaptor.capture(), eq(pushOpts.getProj()), eq(pushOpts.getProjectVersion()), localeIdCaptor.capture(), transResourceCaptor.capture(), extensionCaptor.capture(), - eq(pushOpts.getMergeType())); + eq(pushOpts.getMergeType()), eq(pushOpts.isMyTrans())); } public PullOptionsImpl getPullOpts() { @@ -264,27 +251,29 @@ public PullOptionsImpl getPullOpts() { public PullCommand createPullCommand(List remoteDocList, Resource resourceOnServer, TranslationsResource transResourceOnServer) { + when(clientFactory.getSourceDocResourceClient(anyString(), anyString())) + .thenReturn(sourceDocClient); // return provided remote doc meta list - when(sourceDocResource.get(null)).thenReturn(remoteDocListResponse); - when(remoteDocListResponse.getEntity()).thenReturn(remoteDocList); + when(sourceDocClient.getResourceMeta(null)).thenReturn(remoteDocList); // return provided server resource - when(sourceDocResource.getResource(anyString(), anySetOf(String.class))) - .thenReturn(resourceResponse); - when(resourceResponse.getStatus()).thenReturn(200); - when(resourceResponse.getEntity()).thenReturn(resourceOnServer); + when(sourceDocClient.getResource(anyString(), anySetOf(String.class))) + .thenReturn(resourceOnServer); + when( + clientFactory.getTransDocResourceClient(pullOpts.getProj(), + pullOpts.getProjectVersion())).thenReturn( + transDocClient); // return provided server translation when( - transDocResource.getTranslations(anyString(), + transDocClient.getTranslations(anyString(), any(LocaleId.class), anySetOf(String.class), eq(getPullOpts().getCreateSkeletons()), anyString())) .thenReturn(transResourceResponse); when(transResourceResponse.getStatus()).thenReturn(200); - when(transResourceResponse.getResponseHeaders()).thenReturn( - new MultivaluedHashMap()); - when(transResourceResponse.getEntity()).thenReturn( - transResourceOnServer); - return new PullCommand(pullOpts, factory, sourceDocResource, - transDocResource, uri); + when(transResourceResponse.getHeaders()).thenReturn( + new MultivaluedMapImpl()); + when(transResourceResponse.getEntity(TranslationsResource.class)) + .thenReturn(transResourceOnServer); + return new PullCommand(pullOpts, clientFactory); } /** @@ -296,39 +285,40 @@ public PullCommand createPullCommand(List remoteDocList, * @return raw push command */ public RawPushCommand createRawPushCommand() { - when(fileResource.acceptedFileTypes()) - .thenReturn(acceptedFilesResponse); - List fileTypes = - ProjectType.getSupportedSourceFileTypes(ProjectType.File); - when(acceptedFilesResponse.getEntity()).thenReturn( - Joiner.on(";").join(fileTypes)); + when(clientFactory.getFileResourceClient()).thenReturn( + fileResourceClient); + + List documentTypes = + ProjectType.fileProjectSourceDocTypes(); + + when(fileResourceClient.acceptedFileTypes()).thenReturn( + documentTypes); + + ChunkUploadResponse uploadResponse = + new ChunkUploadResponse(1L, 1, false, "Upload successful"); // push source - when(fileResource.uploadSourceFile(eq(pushOpts.getProj()), + when(fileResourceClient.uploadSourceFile(eq(pushOpts.getProj()), eq(pushOpts.getProjectVersion()), anyString(), any( - DocumentFileUploadForm.class))).thenReturn(chunkUploadResponse); + DocumentFileUploadForm.class))).thenReturn(uploadResponse); // push translation when( - fileResource.uploadTranslationFile(eq(pushOpts.getProj()), + fileResourceClient.uploadTranslationFile(eq(pushOpts.getProj()), eq(pushOpts.getProjectVersion()), anyString(), anyString(), anyString(), any(DocumentFileUploadForm.class))).thenReturn( - chunkUploadResponse); - when(chunkUploadResponse.getStatus()).thenReturn(201); - when(chunkUploadResponse.getEntity()).thenReturn( - new ChunkUploadResponse(1L, 1, false, "Upload successful")); - return new RawPushCommand(pushOpts, factory, sourceDocResource, - transDocResource, uri); + uploadResponse); + return new RawPushCommand(pushOpts, clientFactory); } public void verifyPushRawFileSource(int numOfSource) { - verify(fileResource, times(numOfSource)).uploadSourceFile( + verify(fileResourceClient, times(numOfSource)).uploadSourceFile( eq(pushOpts.getProj()), eq(pushOpts.getProjectVersion()), docIdCaptor.capture(), uploadFormCaptor.capture()); } public void verifyPushRawFileTranslation(int numOfTrans) { - verify(fileResource, times(numOfTrans)).uploadTranslationFile( + verify(fileResourceClient, times(numOfTrans)).uploadTranslationFile( eq(pushOpts.getProj()), eq(pushOpts.getProjectVersion()), anyString(), docIdCaptor.capture(), @@ -350,29 +340,33 @@ public void verifyPushRawFileTranslation(int numOfTrans) { public RawPullCommand createRawPullCommand( List remoteDocList, InputStream sourceFileStream, InputStream transFileStream) { + when( + clientFactory.getSourceDocResourceClient(pullOpts.getProj(), + pullOpts.getProjectVersion())).thenReturn( + sourceDocClient); + when(clientFactory.getFileResourceClient()).thenReturn(fileResourceClient); // return provided remote doc meta list - when(sourceDocResource.get(null)).thenReturn(remoteDocListResponse); - when(remoteDocListResponse.getEntity()).thenReturn(remoteDocList); + when(sourceDocClient.getResourceMeta(null)).thenReturn(remoteDocList); // return provided source stream - when(fileResource.downloadSourceFile( + when(fileResourceClient.downloadSourceFile( eq(pullOpts.getProj()), eq(pullOpts.getProjectVersion()), eq(FileResource.FILETYPE_RAW_SOURCE_DOCUMENT), anyString())).thenReturn(downloadSourceResponse); - when(downloadSourceResponse.getResponseStatus()).thenReturn( - Response.Status.OK); + when(downloadSourceResponse.getClientResponseStatus()).thenReturn( + ClientResponse.Status.OK); when(downloadSourceResponse.getStatus()).thenReturn(200); when(downloadSourceResponse.getEntity(InputStream.class)) .thenReturn(sourceFileStream); // return provide translation stream - when(fileResource.downloadTranslationFile(eq(pullOpts.getProj()), + when(fileResourceClient.downloadTranslationFile(eq(pullOpts.getProj()), eq(pullOpts.getProjectVersion()), anyString(), anyString(), anyString())).thenReturn(downloadTransResponse); when(downloadTransResponse.getStatus()).thenReturn(200); - when(downloadTransResponse.getResponseStatus()).thenReturn( - Response.Status.OK); + when(downloadTransResponse.getClientResponseStatus()).thenReturn( + ClientResponse.Status.OK); when(downloadTransResponse.getEntity(InputStream.class)) .thenReturn(transFileStream); - return new RawPullCommand(pullOpts, factory, sourceDocResource, - transDocResource, uri, fileResource); + return new RawPullCommand(pullOpts, fileResourceClient, clientFactory); } } + diff --git a/zanata-client-commands/src/test/java/org/zanata/client/TestProjectGenerator.java b/zanata-client-commands/src/test/java/org/zanata/client/TestProjectGenerator.java index 7d5a95a8..490251a2 100644 --- a/zanata-client-commands/src/test/java/org/zanata/client/TestProjectGenerator.java +++ b/zanata-client-commands/src/test/java/org/zanata/client/TestProjectGenerator.java @@ -2,7 +2,9 @@ import java.io.File; import java.io.FilenameFilter; +import java.net.MalformedURLException; import java.net.URI; +import java.net.URISyntaxException; import java.net.URL; import java.util.Map; @@ -11,13 +13,14 @@ import org.zanata.client.commands.ConfigurableProjectOptions; import org.zanata.client.commands.OptionsUtil; import org.zanata.common.ProjectType; -import org.zanata.rest.client.ClientUtility; -import org.zanata.rest.client.IProjectIterationResource; -import org.zanata.rest.client.IProjectResource; -import org.zanata.rest.client.ZanataProxyFactory; +import org.zanata.rest.client.ProjectClient; +import org.zanata.rest.client.ProjectIterationClient; +import org.zanata.rest.client.RestClientFactory; import org.zanata.rest.dto.Project; import org.zanata.rest.dto.ProjectIteration; + import com.google.common.base.Preconditions; +import com.google.common.base.Throwables; import com.google.common.collect.ImmutableMap; /** @@ -91,8 +94,8 @@ public void ensureProjectOnServer(ConfigurableProjectOptions opts, opts.setUrl(new URI(instance.getUrl()).toURL()); opts.setUsername(instance.getUsername()); opts.setKey(instance.getKey()); - ZanataProxyFactory requestFactory = - OptionsUtil.createRequestFactory(opts); + + RestClientFactory clientFactory = OptionsUtil.createClientFactory(opts); Project projectDTO = createProjectDTO(projectRootMap.get(projectType)); String projectSlug = projectDTO.getId(); @@ -100,14 +103,9 @@ public void ensureProjectOnServer(ConfigurableProjectOptions opts, String iterationSlug = iteration.getId(); // create project and version - IProjectResource projectResource = - requestFactory.getProject(projectSlug); - ClientUtility.checkResultAndReleaseConnection( - projectResource.put(projectDTO)); - IProjectIterationResource iterationResource = - requestFactory.getProjectIteration(projectSlug, iterationSlug); - ClientUtility.checkResultAndReleaseConnection( - iterationResource.put(iteration)); + clientFactory.getProjectClient(projectSlug).put(projectDTO); + clientFactory.getProjectIterationClient(projectSlug, iterationSlug) + .put(iteration); } /** @@ -161,6 +159,14 @@ public String getUrl() { return url; } + public URL getURL() { + try { + return new URI(url).toURL(); + } catch (MalformedURLException | URISyntaxException e) { + throw Throwables.propagate(e); + } + } + public String getUsername() { return username; } diff --git a/zanata-client-commands/src/test/java/org/zanata/client/TestUtils.java b/zanata-client-commands/src/test/java/org/zanata/client/TestUtils.java index 79cd870a..ee05172f 100644 --- a/zanata-client-commands/src/test/java/org/zanata/client/TestUtils.java +++ b/zanata-client-commands/src/test/java/org/zanata/client/TestUtils.java @@ -21,9 +21,16 @@ package org.zanata.client; +import java.io.File; +import java.io.IOException; +import java.net.URL; + +import org.apache.commons.codec.Charsets; +import org.apache.commons.io.IOUtils; import org.zanata.client.commands.ConfigurableProjectOptions; import org.zanata.client.config.LocaleMapping; import com.google.common.base.Optional; +import com.google.common.base.Preconditions; /** * @author Patrick Huang @@ -43,4 +50,24 @@ public static LocaleMapping createAndAddLocaleMapping(String localeId, opts.getLocaleMapList().add(mapping); return mapping; } + + public static String readFromClasspath(String relativePath) + throws IOException { + URL resource = loadFromClasspath(relativePath); + return IOUtils.toString(resource, Charsets.UTF_8); + } + + public static File fileFromClasspath(String relativePath) { + URL url = loadFromClasspath(relativePath); + return new File(url.getFile()); + } + + private static URL loadFromClasspath(String relativePath) { + URL resource = + Thread.currentThread().getContextClassLoader() + .getResource(relativePath); + Preconditions.checkArgument(resource != null, "%s not found", + relativePath); + return resource; + } } diff --git a/zanata-client-commands/src/test/java/org/zanata/client/commands/DummyResponse.java b/zanata-client-commands/src/test/java/org/zanata/client/commands/DummyResponse.java deleted file mode 100644 index 38695e24..00000000 --- a/zanata-client-commands/src/test/java/org/zanata/client/commands/DummyResponse.java +++ /dev/null @@ -1,233 +0,0 @@ -package org.zanata.client.commands; - -import java.lang.annotation.Annotation; -import java.lang.reflect.Type; -import java.net.URI; -import java.util.Date; -import java.util.Locale; -import java.util.Map; -import java.util.Set; -import javax.ws.rs.core.EntityTag; -import javax.ws.rs.core.MediaType; -import javax.ws.rs.core.MultivaluedMap; -import javax.ws.rs.core.NewCookie; - -import org.jboss.resteasy.client.ClientResponse; -import org.jboss.resteasy.spi.Link; -import org.jboss.resteasy.spi.LinkHeader; -import org.jboss.resteasy.util.GenericType; - -/** - * @author Sean Flanigan sflaniga@redhat.com - * - * @param - */ -public class DummyResponse extends ClientResponse { - private final Status status; - private final T entity; - - public DummyResponse(Status status, T entity) { - super(); - this.status = status; - this.entity = entity; - } - - @Override - public T getEntity() { - return entity; - } - - @Override - public T2 getEntity(Class arg0) { - return null; - } - - @Override - public T2 getEntity(GenericType arg0) { - return null; - } - - @Override - public T2 getEntity(Class arg0, Type arg1) { - return null; - } - - @Override - public T2 getEntity(GenericType arg0, Annotation[] arg1) { - return null; - } - - @Override - public T2 getEntity(Class arg0, Type arg1, Annotation[] arg2) { - return null; - } - - @Override - public Link getHeaderAsLink(String arg0) { - return null; - } - - @Override - public MultivaluedMap getHeaders() { - return null; - } - - @Override - public MultivaluedMap getResponseHeaders() { - return null; - } - - @Override - public Link getLocationLink() { - return null; - } - - @Override - public StatusType getStatusInfo() { - return null; - } - - @Override - public T readEntity(Class entityType) { - return null; - } - - @Override - public T readEntity(javax.ws.rs.core.GenericType entityType) { - return null; - } - - @Override - public T readEntity(Class entityType, Annotation[] annotations) { - return null; - } - - @Override - public T readEntity(javax.ws.rs.core.GenericType entityType, - Annotation[] annotations) { - return null; - } - - @Override - public boolean hasEntity() { - return false; - } - - @Override - public boolean bufferEntity() { - return false; - } - - @Override - public void close() { - } - - @Override - public MediaType getMediaType() { - return null; - } - - @Override - public Locale getLanguage() { - return null; - } - - @Override - public int getLength() { - return 0; - } - - @Override - public Set getAllowedMethods() { - return null; - } - - @Override - public Map getCookies() { - return null; - } - - @Override - public EntityTag getEntityTag() { - return null; - } - - @Override - public Date getDate() { - return null; - } - - @Override - public Date getLastModified() { - return null; - } - - @Override - public Set getLinks() { - return null; - } - - @Override - public boolean hasLink(String relation) { - return false; - } - - @Override - public javax.ws.rs.core.Link getLink(String relation) { - return null; - } - - @Override - public javax.ws.rs.core.Link.Builder getLinkBuilder(String relation) { - return null; - } - - @Override - public MultivaluedMap getStringHeaders() { - return null; - } - - @Override - public String getHeaderString(String name) { - return null; - } - - @Override - public LinkHeader getLinkHeader() { - return null; - } - - @Override - public URI getLocation() { - return null; - } - - @Override - public Status getResponseStatus() { - return status; - } - - @Override - public void releaseConnection() { - } - - @Override - public int getStatus() { - return status.getStatusCode(); - } - - @Override - public MultivaluedMap getMetadata() { - return null; - } - - @Override - public Map getAttributes() { - return null; - } - - @Override - public void resetStream() { - } -} diff --git a/zanata-client-commands/src/test/java/org/zanata/client/commands/FileMappingRuleHandlerTest.java b/zanata-client-commands/src/test/java/org/zanata/client/commands/FileMappingRuleHandlerTest.java index 09d34141..310fd726 100644 --- a/zanata-client-commands/src/test/java/org/zanata/client/commands/FileMappingRuleHandlerTest.java +++ b/zanata-client-commands/src/test/java/org/zanata/client/commands/FileMappingRuleHandlerTest.java @@ -35,6 +35,8 @@ import org.zanata.client.config.LocaleMapping; import org.zanata.common.ProjectType; +import com.google.common.base.Optional; + public class FileMappingRuleHandlerTest { private ConfigurableProjectOptions opts = new PushOptionsImpl(); @@ -77,7 +79,7 @@ public void ifNoPatternWillUseProjectType() { ProjectType.Gettext, opts); assertThat(handler.getRelativeTransFilePathForSourceDoc( QualifiedSrcDocName.from("message.pot"), - new LocaleMapping("zh")), Matchers.equalTo("zh.po")); + new LocaleMapping("zh"), Optional.absent()), Matchers.equalTo("zh.po")); } private String getTransFile(String sourceFile, String locale, String rule, @@ -86,14 +88,14 @@ private String getTransFile(String sourceFile, String locale, String rule, new FileMappingRule("**/*", rule), projectType, opts); return handler.getRelativeTransFilePathForSourceDoc( QualifiedSrcDocName.from(sourceFile), - new LocaleMapping(locale)); + new LocaleMapping(locale), Optional.absent()); } @Test public void canGetPartsFromQualifiedDocName() { EnumMap map = FileMappingRuleHandler.parseToMap("foo/message.pot", - new LocaleMapping("zh-CN", "zh-Hans")); + new LocaleMapping("zh-CN", "zh-Hans"), Optional.absent()); assertThat(map, Matchers.hasEntry(Placeholders.path, "foo")); assertThat(map, Matchers.hasEntry(Placeholders.filename, "message")); assertThat(map, Matchers.hasEntry(Placeholders.extension, "pot")); @@ -101,6 +103,18 @@ public void canGetPartsFromQualifiedDocName() { assertThat(map, Matchers.hasEntry(Placeholders.localeWithUnderscore, "zh_Hans")); } + @Test + public void canGetPartsFromQualifiedDocName2() { + EnumMap map = + FileMappingRuleHandler.parseToMap("foo/message.pot", + new LocaleMapping("zh-CN", "zh-Hans"), Optional.of("po")); + assertThat(map, Matchers.hasEntry(Placeholders.path, "foo")); + assertThat(map, Matchers.hasEntry(Placeholders.filename, "message")); + assertThat(map, Matchers.hasEntry(Placeholders.extension, "po")); + assertThat(map, Matchers.hasEntry(Placeholders.locale, "zh-Hans")); + assertThat(map, Matchers.hasEntry(Placeholders.localeWithUnderscore, "zh_Hans")); + } + @Test public void canTestApplicable() { opts.setSrcDir(new File(".")); diff --git a/zanata-client-commands/src/test/java/org/zanata/client/commands/HTTPMockContainer.java b/zanata-client-commands/src/test/java/org/zanata/client/commands/HTTPMockContainer.java index 61eb2d7d..aede63e0 100644 --- a/zanata-client-commands/src/test/java/org/zanata/client/commands/HTTPMockContainer.java +++ b/zanata-client-commands/src/test/java/org/zanata/client/commands/HTTPMockContainer.java @@ -106,15 +106,6 @@ public HTTPMockContainer build() { return new HTTPMockContainer(mapBuilder.build()); } - public static String readFromClasspath(String relativePath) - throws IOException { - URL resource = - Thread.currentThread().getContextClassLoader() - .getResource(relativePath); - Preconditions.checkArgument(resource != null, "%s not found", - relativePath); - return IOUtils.toString(resource, Charsets.UTF_8); - } } private static class StatusAndContent { diff --git a/zanata-client-commands/src/test/java/org/zanata/client/commands/OptionsUtilTest.java b/zanata-client-commands/src/test/java/org/zanata/client/commands/OptionsUtilTest.java index f230ec2b..7b90eac8 100644 --- a/zanata-client-commands/src/test/java/org/zanata/client/commands/OptionsUtilTest.java +++ b/zanata-client-commands/src/test/java/org/zanata/client/commands/OptionsUtilTest.java @@ -132,6 +132,6 @@ public void willThrowExceptionIfRuleIsInvalid() { OptionsUtil.checkPotentialMistakesInRules(opts, console); - verify(console).printfln(Warning, _("invalid.rule"),rule); + verify(console).printfln(Warning, _("invalid.rule"), rule); } } diff --git a/zanata-client-commands/src/test/java/org/zanata/client/commands/TransFileResolverTest.java b/zanata-client-commands/src/test/java/org/zanata/client/commands/TransFileResolverTest.java index bc085f23..d7bf6cd2 100644 --- a/zanata-client-commands/src/test/java/org/zanata/client/commands/TransFileResolverTest.java +++ b/zanata-client-commands/src/test/java/org/zanata/client/commands/TransFileResolverTest.java @@ -1,6 +1,7 @@ package org.zanata.client.commands; +import com.google.common.base.Optional; import com.google.common.collect.Lists; import org.junit.Before; import org.junit.Test; @@ -35,14 +36,15 @@ public void canGetTransFileUsingRule() { "{path}/{filename}_{locale_with_underscore}.{extension}"))); File gettext = resolver.resolveTransFile(QualifiedSrcDocName.from( - "gcc/po/gcc.pot"), new LocaleMapping("de-DE")); + "gcc/po/gcc.pot"), new LocaleMapping("de-DE"), Optional + .absent()); assertThat(gettext.getPath(), equalTo("./gcc/po/de_DE.po")); File prop = resolver .resolveTransFile(QualifiedSrcDocName.from( "src/main/resources/messages.properties"), - new LocaleMapping("zh")); + new LocaleMapping("zh"), Optional.absent()); assertThat(prop.getPath(), equalTo( "./src/main/resources/messages_zh.properties")); } @@ -53,7 +55,7 @@ public void canGetTransFileUsingProjectTypeIfNoRuleIsApplicable() { opts.setProjectType("file"); File noMatching = resolver .resolveTransFile(QualifiedSrcDocName.from( - "doc/marketing.odt"), new LocaleMapping("ja")); + "doc/marketing.odt"), new LocaleMapping("ja"), Optional.absent()); assertThat(noMatching.getPath(), equalTo("./ja/doc/marketing.odt")); } diff --git a/zanata-client-commands/src/test/java/org/zanata/client/commands/init/InitCommandTest.java b/zanata-client-commands/src/test/java/org/zanata/client/commands/init/InitCommandTest.java index 61f82335..3c868406 100644 --- a/zanata-client-commands/src/test/java/org/zanata/client/commands/init/InitCommandTest.java +++ b/zanata-client-commands/src/test/java/org/zanata/client/commands/init/InitCommandTest.java @@ -2,17 +2,14 @@ import static org.hamcrest.MatcherAssert.assertThat; import static org.mockito.Mockito.when; -import static org.zanata.client.commands.HTTPMockContainer.Builder.readFromClasspath; +import static org.zanata.client.TestUtils.readFromClasspath; import java.io.File; import java.io.IOException; -import java.net.InetSocketAddress; -import java.net.URI; import java.util.List; import org.apache.commons.io.FileUtils; import org.hamcrest.Matchers; -import org.junit.After; import org.junit.Before; import org.junit.Rule; import org.junit.Test; @@ -20,15 +17,12 @@ import org.junit.rules.TemporaryFolder; import org.mockito.Mock; import org.mockito.MockitoAnnotations; -import org.simpleframework.http.core.Container; -import org.simpleframework.http.core.ContainerServer; -import org.simpleframework.transport.connect.SocketConnection; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.zanata.client.commands.ConsoleInteractor; -import org.zanata.client.commands.HTTPMockContainer; import org.zanata.client.commands.Messages; -import org.zanata.rest.client.ZanataProxyFactory; +import org.zanata.rest.client.ProjectIterationClient; +import org.zanata.rest.client.RestClientFactory; import org.zanata.rest.dto.VersionInfo; import com.google.common.base.Charsets; @@ -46,53 +40,31 @@ public class InitCommandTest { private InitCommand command; private InitOptionsImpl opts; - private SocketConnection connection; @Mock private ConsoleInteractor console; @Mock - private ZanataProxyFactory requestFactory; + private RestClientFactory clientFactory; + @Mock + private ProjectIterationClient projectIterationClient; @Before public void setUp() throws IOException { MockitoAnnotations.initMocks(this); opts = new InitOptionsImpl(); - command = new InitCommand(opts, console, null); + command = new InitCommand(opts, console, clientFactory); } - @After - public void cleanUp() throws IOException { - if (connection != null) { - connection.close(); - } - } - - private void startMockServer(Container container) throws IOException { - ContainerServer server = new ContainerServer(container); - connection = new SocketConnection(server); - InetSocketAddress address = (InetSocketAddress) connection - .connect(new InetSocketAddress(0)); - int port = address.getPort(); - - opts.setUrl(URI.create("http://localhost:" + port).toURL()); - opts.setUsername("admin"); - opts.setKey("abcde"); - opts.setLogHttp(false); + @Test + public void createCommandWithoutMandatoryOptionsWillNotCauseException() { + // we don't have server url etc yet + command = new InitCommand(opts, console); } @Test public void willDownloadProjectConfigFromServer() throws IOException { - String configContent = - readFromClasspath("serverresponse/projectConfig.xml"); - HTTPMockContainer container = - HTTPMockContainer.Builder - .builder() - .onPathReturnOk( - Matchers.endsWith("/version"), - readFromClasspath("serverresponse/version.xml")) - .onPathReturnOk( - Matchers.endsWith("/config"), - configContent).build(); - startMockServer(container); + when(clientFactory.getProjectIterationClient("gcc", "master")).thenReturn(projectIterationClient); + when(projectIterationClient.sampleConfiguration()).thenReturn( + readFromClasspath("serverresponse/projectConfig.xml")); File configFileDest = new File(tempFolder.getRoot(), "zanata.xml"); command.downloadZanataXml("gcc", "master", configFileDest); @@ -100,7 +72,7 @@ public void willDownloadProjectConfigFromServer() throws IOException { assertThat(configFileDest.exists(), Matchers.is(true)); List lines = FileUtils.readLines(configFileDest, Charsets.UTF_8); String content = Joiner.on("\n").join(lines); - assertThat(content, Matchers.equalTo(configContent)); + assertThat(content, Matchers.containsString("")); assertThat(opts.getProjectConfig(), Matchers.equalTo(configFileDest)); } @@ -130,9 +102,9 @@ public void willQuitIfServerApiVersionDoesNotSupportInit() expectException.expectMessage(Matchers.equalTo(Messages ._("server.incompatible"))); - when(requestFactory.getServerVersionInfo()).thenReturn( + when(clientFactory.getServerVersionInfo()).thenReturn( new VersionInfo("3.3.1", "unknown", "unknown")); - command = new InitCommand(opts, console, requestFactory); + command = new InitCommand(opts, console, clientFactory); command.ensureServerVersion(); } diff --git a/zanata-client-commands/src/test/java/org/zanata/client/commands/init/ProjectIterationPromptTest.java b/zanata-client-commands/src/test/java/org/zanata/client/commands/init/ProjectIterationPromptTest.java index cda88046..9f7abe6b 100644 --- a/zanata-client-commands/src/test/java/org/zanata/client/commands/init/ProjectIterationPromptTest.java +++ b/zanata-client-commands/src/test/java/org/zanata/client/commands/init/ProjectIterationPromptTest.java @@ -1,9 +1,12 @@ package org.zanata.client.commands.init; +import static org.hamcrest.MatcherAssert.assertThat; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + import java.io.IOException; import org.hamcrest.Matchers; -import org.jboss.resteasy.client.ClientResponse; import org.junit.Before; import org.junit.Test; import org.mockito.ArgumentCaptor; @@ -13,39 +16,32 @@ import org.zanata.client.commands.ConsoleInteractor; import org.zanata.client.commands.MockConsoleInteractor; import org.zanata.common.EntityStatus; -import org.zanata.rest.client.IProjectIterationResource; -import org.zanata.rest.client.IProjectResource; -import org.zanata.rest.client.ZanataProxyFactory; +import org.zanata.rest.client.ProjectClient; +import org.zanata.rest.client.ProjectIterationClient; +import org.zanata.rest.client.RestClientFactory; import org.zanata.rest.dto.Project; import org.zanata.rest.dto.ProjectIteration; import com.google.common.collect.Lists; -import static org.hamcrest.MatcherAssert.assertThat; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.when; - public class ProjectIterationPromptTest { private ProjectIterationPrompt prompt; private InitOptionsImpl opts; - @Mock - private ZanataProxyFactory requestFactory; - @Mock - private IProjectIterationResource iterationResource; - @Mock - private ClientResponse response; + @Captor private ArgumentCaptor iterationCaptor; @Mock - private IProjectResource projectResource; + private RestClientFactory clientFactory; + @Mock + private ProjectIterationClient projectIterationClient; + @Mock + private ProjectClient projectClient; @Before public void setUp() { MockitoAnnotations.initMocks(this); opts = new InitOptionsImpl(); prompt = null; - } @Test @@ -62,25 +58,27 @@ public void willGuideUserToSelectVersion() { opts.setProj(projectId); opts.setProjectType(projectType); - when(requestFactory.getProject(projectId)) - .thenReturn(projectResource); - when(projectResource.get()).thenReturn(response); - when(response.getEntity(Project.class)).thenReturn(project); - + when(clientFactory.getProjectClient(opts.getProj())) + .thenReturn(projectClient); + when(projectClient.get()).thenReturn(project); + when(clientFactory.getProjectIterationClient(opts.getProj(), "4.8.2")) + .thenReturn(projectIterationClient); // we want to select the second active version ConsoleInteractor console = MockConsoleInteractor.predefineAnswers("2"); - prompt = new ProjectIterationPrompt(console, opts, requestFactory); + prompt = new ProjectIterationPrompt(console, opts, clientFactory); prompt.selectVersion(); - verify(projectResource).get(); + verify(projectClient).get(); assertThat(opts.getProjectVersion(), Matchers.equalTo("4.8.2")); + assertThat(opts.getProjectType(), Matchers.equalTo("gettext")); } private static ProjectIteration newIteration(String id, EntityStatus status) { ProjectIteration iteration = new ProjectIteration(id); iteration.setStatus(status); + iteration.setProjectType("gettext"); return iteration; } @@ -94,17 +92,13 @@ public void willGuideUserToCreateNewVersion() opts.setProjectType(projectType); ConsoleInteractor console = MockConsoleInteractor.predefineAnswers(versionId); - prompt = new ProjectIterationPrompt(console, opts, requestFactory); - - when(requestFactory.getProjectIteration(projectId, versionId)) - .thenReturn(iterationResource); - when(iterationResource.put(iterationCaptor.capture())).thenReturn( - response); - when(response.getStatus()).thenReturn(201); + prompt = new ProjectIterationPrompt(console, opts, clientFactory); + when(clientFactory.getProjectIterationClient(opts.getProj(), versionId)) + .thenReturn(projectIterationClient); prompt.createNewVersion(); - verify(iterationResource).put(iterationCaptor.capture()); + verify(projectIterationClient).put(iterationCaptor.capture()); ProjectIteration iteration = iterationCaptor.getValue(); assertThat(iteration.getId(), Matchers.equalTo(versionId)); assertThat(iteration.getProjectType(), Matchers.equalTo(projectType)); diff --git a/zanata-client-commands/src/test/java/org/zanata/client/commands/init/ProjectPromptTest.java b/zanata-client-commands/src/test/java/org/zanata/client/commands/init/ProjectPromptTest.java index 7abc5358..a43bb7e0 100644 --- a/zanata-client-commands/src/test/java/org/zanata/client/commands/init/ProjectPromptTest.java +++ b/zanata-client-commands/src/test/java/org/zanata/client/commands/init/ProjectPromptTest.java @@ -1,106 +1,74 @@ package org.zanata.client.commands.init; import static org.hamcrest.MatcherAssert.assertThat; +import static org.mockito.Mockito.doNothing; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; -import static org.zanata.client.commands.HTTPMockContainer.Builder.readFromClasspath; import java.io.IOException; -import java.net.InetSocketAddress; -import java.net.URI; import java.util.List; import org.hamcrest.Matchers; -import org.jboss.resteasy.client.ClientResponse; -import org.junit.After; import org.junit.Before; import org.junit.Test; -import org.mockito.Answers; import org.mockito.ArgumentCaptor; import org.mockito.Captor; import org.mockito.Mock; import org.mockito.MockitoAnnotations; -import org.simpleframework.http.core.Container; -import org.simpleframework.http.core.ContainerServer; -import org.simpleframework.transport.connect.Connection; -import org.simpleframework.transport.connect.SocketConnection; import org.zanata.client.commands.ConsoleInteractor; -import org.zanata.client.commands.HTTPMockContainer; import org.zanata.client.commands.MockConsoleInteractor; -import org.zanata.client.commands.OptionsUtil; -import org.zanata.rest.client.IProjectResource; -import org.zanata.rest.client.IProjectsResource; -import org.zanata.rest.client.ZanataProxyFactory; +import org.zanata.common.EntityStatus; +import org.zanata.rest.client.ProjectClient; +import org.zanata.rest.client.ProjectsClient; +import org.zanata.rest.client.RestClientFactory; import org.zanata.rest.dto.Project; +import org.zanata.rest.dto.ProjectIteration; + import com.google.common.collect.Lists; public class ProjectPromptTest { private ProjectPrompt prompt; - private Connection connection; private InitOptions opts; - private ZanataProxyFactory proxyFactory; @Mock private ProjectIterationPrompt iterationPrompt; - @Mock - private ClientResponse response; - @Mock - private IProjectResource projectResource; + @Captor private ArgumentCaptor projectCaptor; + @Mock + private RestClientFactory clientFactory; + @Mock + private ProjectsClient projectsClient; + @Mock + private ProjectClient projectClient; @Before public void setUp() { MockitoAnnotations.initMocks(this); opts = new InitOptionsImpl(); + when(clientFactory.getProjectsClient()).thenReturn(projectsClient); } - @After - public void cleanUp() throws IOException { - if (connection != null) { - connection.close(); - } - } - - private void startMockServer(Container container) throws IOException { - ContainerServer server = new ContainerServer(container); - connection = new SocketConnection(server); - InetSocketAddress address = (InetSocketAddress) connection - .connect(new InetSocketAddress(0)); - int port = address.getPort(); - - opts.setUrl(URI.create("http://localhost:" + port).toURL()); - opts.setUsername("admin"); - opts.setKey("abcde"); - opts.setLogHttp(false); - proxyFactory = OptionsUtil.createRequestFactory(opts); - } - - // this test sets up a localhost mock server and simulate full REST round - // trip. @Test public void willShowAllActiveProjectsIfUserChooseToSelect() throws IOException { - HTTPMockContainer container = - HTTPMockContainer.Builder - .builder() - .onPathReturnOk( - Matchers.endsWith("/version"), - readFromClasspath("serverresponse/version.xml")) - .onPathReturnOk( - Matchers.endsWith("/projects"), - readFromClasspath("serverresponse/projects.xml")) - .onPathReturnOk( - Matchers.endsWith("/projects/p/gcc"), - readFromClasspath("serverresponse/iteration.xml")) - .build(); - startMockServer(container); + Project project = new Project("gcc", "gnu C compiler", "gettext"); + ProjectIteration iteration = new ProjectIteration("master"); + iteration.setStatus(EntityStatus.ACTIVE); + project.getIterations(true).add(iteration); + when(projectsClient.getProjects()).thenReturn( + new Project[] { project }); + when(clientFactory.getProjectClient("gcc")).thenReturn(projectClient); + when(projectClient.get()).thenReturn(project); + ConsoleInteractor console = MockConsoleInteractor.predefineAnswers("1", "1", "1", "1"); prompt = - new ProjectPrompt(console, opts, proxyFactory, - new ProjectIterationPrompt(console, opts, proxyFactory)); + new ProjectPrompt(console, opts, + new ProjectIterationPrompt(console, opts, + clientFactory), + clientFactory); prompt.selectOrCreateNewProjectAndVersion(); @@ -111,12 +79,7 @@ public void willShowAllActiveProjectsIfUserChooseToSelect() @Test public void willFilterAllProjectsIfUserTypeLetters() { - proxyFactory = mock(ZanataProxyFactory.class); - - IProjectsResource projectsResource = mock(IProjectsResource.class, - Answers.RETURNS_DEEP_STUBS.get()); - when(proxyFactory.getProjectsResource()).thenReturn(projectsResource); - when(projectsResource.get().getEntity(Project[].class)) + when(projectsClient.getProjects()) .thenReturn(new Project[] { makeProject("project-1", "project one"), makeProject("project-2", "project two"), @@ -125,11 +88,12 @@ public void willFilterAllProjectsIfUserTypeLetters() { // Given: user input ConsoleInteractor console = MockConsoleInteractor.predefineAnswers( - "99", // part of project name + // part of project name + "99", "1"); prompt = - new ProjectPrompt(console, opts, proxyFactory, - mock(ProjectIterationPrompt.class)); + new ProjectPrompt(console, opts, + mock(ProjectIterationPrompt.class), clientFactory); prompt.selectProject(); assertThat(opts.getProj(), Matchers.equalTo("project-99")); @@ -148,8 +112,7 @@ private static Project makeProject(String slug, String name) { public void canFilterProject() { prompt = new ProjectPrompt(mock(ConsoleInteractor.class), opts, - mock(ZanataProxyFactory.class), - mock(ProjectIterationPrompt.class)); + mock(ProjectIterationPrompt.class), clientFactory); Project gcc = makeProject("gcc", "gnu c compiler"); Project aboutFedora = makeProject("about-fedora", "about fedora"); @@ -178,19 +141,15 @@ public void willGuideUserIfUserChooseToCreateNewProjectAndVersion() MockConsoleInteractor.predefineAnswers(projectId, "C compiler", projectType, versionId); - proxyFactory = mock(ZanataProxyFactory.class); - - when(proxyFactory.getProject(projectId)).thenReturn(projectResource); - when(projectResource.put(projectCaptor.capture())).thenReturn(response); - when(response.getStatus()).thenReturn(201); + when(clientFactory.getProjectClient("gcc")).thenReturn(projectClient); + doNothing().when(projectClient).put(projectCaptor.capture()); prompt = - new ProjectPrompt(console, opts, proxyFactory, - iterationPrompt); + new ProjectPrompt(console, opts, + iterationPrompt, clientFactory); prompt.createNewProject(); - verify(proxyFactory).getProject(projectId); - verify(projectResource).put(projectCaptor.capture()); + verify(projectClient).put(projectCaptor.capture()); verify(iterationPrompt).createNewVersion(); Project project = projectCaptor.getValue(); assertThat(project.getId(), Matchers.equalTo(projectId)); diff --git a/zanata-client-commands/src/test/java/org/zanata/client/commands/pull/PublicanPullCommandTest.java b/zanata-client-commands/src/test/java/org/zanata/client/commands/pull/PublicanPullCommandTest.java deleted file mode 100644 index 4fc46830..00000000 --- a/zanata-client-commands/src/test/java/org/zanata/client/commands/pull/PublicanPullCommandTest.java +++ /dev/null @@ -1,141 +0,0 @@ -package org.zanata.client.commands.pull; - -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.when; - -import java.io.File; -import java.net.URI; -import java.util.ArrayList; -import java.util.List; - -import javax.ws.rs.core.Response.Status; - -import org.junit.Before; -import org.junit.Test; -import org.mockito.Mock; -import org.mockito.MockitoAnnotations; -import org.zanata.client.commands.DummyResponse; -import org.zanata.client.commands.OptionsUtil; -import org.zanata.client.commands.PublicanPullCommand; -import org.zanata.client.commands.PublicanPullOptions; -import org.zanata.client.commands.PublicanPullOptionsImpl; -import org.zanata.client.commands.ZanataCommand; -import org.zanata.client.config.LocaleList; -import org.zanata.client.config.LocaleMapping; -import org.zanata.common.LocaleId; -import org.zanata.rest.RestUtil; -import org.zanata.rest.StringSet; -import org.zanata.rest.client.ISourceDocResource; -import org.zanata.rest.client.ITranslatedDocResource; -import org.zanata.rest.client.ZanataProxyFactory; -import org.zanata.rest.dto.resource.Resource; -import org.zanata.rest.dto.resource.ResourceMeta; -import org.zanata.rest.dto.resource.TranslationsResource; - -public class PublicanPullCommandTest { - @Mock - private ISourceDocResource mockSourceDocResource; - - @Mock - private ITranslatedDocResource mockTranslationResources; - - @Before - public void beforeMethod() { - MockitoAnnotations.initMocks(this); - } - - @Test - public void publicanPullPo() throws Exception { - publicanPull(false, false); - } - - @Test - public void publicanPullPotAndPo() throws Exception { - publicanPull(true, false); - } - - @Test - public void publicanPullPotAndPoWithLocaleMapping() throws Exception { - publicanPull(true, true); - } - - @SuppressWarnings("deprecation") - private void publicanPull(boolean exportPot, boolean mapLocale) - throws Exception { - PublicanPullOptions opts = new PublicanPullOptionsImpl(); - String projectSlug = "project"; - opts.setProj(projectSlug); - String versionSlug = "1.0"; - opts.setProjectVersion(versionSlug); - opts.setDstDir(new File("target/test-output/test2")); - opts.setExportPot(exportPot); - opts.setProjectConfig(new File("src/test/resources/test2/zanata.xml")); - opts.setLocaleMapList(new LocaleList()); - OptionsUtil.applyConfigFiles(opts); - if (mapLocale) { - LocaleList locales = new LocaleList(); - locales.add(new LocaleMapping("ja", "ja-JP")); - opts.setLocaleMapList(locales); - } - - List resourceMetaList = new ArrayList(); - resourceMetaList.add(new ResourceMeta("RPM")); - resourceMetaList.add(new ResourceMeta("sub/RPM")); - - when(mockSourceDocResource.get(null)).thenReturn( - new DummyResponse>(Status.OK, - resourceMetaList)); - - Resource rpmResource = new Resource("RPM"); - - StringSet ext = new StringSet("comment;gettext"); - when( - mockSourceDocResource.getResource( - RestUtil.convertToDocumentURIId(rpmResource.getName()), - ext)).thenReturn( - new DummyResponse(Status.OK, rpmResource)); - - Resource subRpmResource = new Resource("sub/RPM"); - when( - mockSourceDocResource.getResource(RestUtil - .convertToDocumentURIId(subRpmResource.getName()), ext)) - .thenReturn( - new DummyResponse(Status.OK, subRpmResource)); - - LocaleId expectedLocale; - if (mapLocale) - expectedLocale = new LocaleId("ja"); - else - expectedLocale = new LocaleId("ja-JP"); - TranslationsResource rpmTransJa = new TranslationsResource(); - mockExpectGetTranslationsAndReturnResponse("RPM", expectedLocale, - rpmTransJa); - mockExpectGetTranslationsAndReturnResponse("sub/RPM", expectedLocale, - null); - ZanataProxyFactory mockRequestFactory = mock(ZanataProxyFactory.class); - - ZanataCommand cmd = - new PublicanPullCommand(opts, mockRequestFactory, - mockSourceDocResource, mockTranslationResources, - new URI("http://example.com/")); - cmd.runWithActions(); - } - - private void mockExpectGetTranslationsAndReturnResponse(String id, - LocaleId locale, TranslationsResource entity) { - String docUri = RestUtil.convertToDocumentURIId(id); - StringSet ext = new StringSet("comment;gettext"); - if (entity != null) { - when(mockTranslationResources.getTranslations(docUri, locale, ext)) - .thenReturn( - new DummyResponse(Status.OK, - entity)); - } else { - when(mockTranslationResources.getTranslations(docUri, locale, ext)) - .thenReturn( - new DummyResponse( - Status.NOT_FOUND, null)); - } - } - -} diff --git a/zanata-client-commands/src/test/java/org/zanata/client/commands/pull/PullCommandTest.java b/zanata-client-commands/src/test/java/org/zanata/client/commands/pull/PullCommandTest.java new file mode 100644 index 00000000..4f18e919 --- /dev/null +++ b/zanata-client-commands/src/test/java/org/zanata/client/commands/pull/PullCommandTest.java @@ -0,0 +1,286 @@ +/* + * Copyright 2015, Red Hat, Inc. and individual contributors + * as indicated by the @author tags. See the copyright.txt file in the + * distribution for a full listing of individual contributors. + * + * This is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this software; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA, or see the FSF site: http://www.fsf.org. + */ + +package org.zanata.client.commands.pull; + +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.verifyNoMoreInteractions; +import static org.mockito.Mockito.verifyZeroInteractions; +import static org.mockito.Mockito.when; + +import java.io.File; +import java.io.IOException; +import java.util.List; + +import org.junit.Before; +import org.junit.Test; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; +import org.zanata.client.config.LocaleList; +import org.zanata.client.config.LocaleMapping; +import org.zanata.common.LocaleId; +import org.zanata.common.ProjectType; +import org.zanata.common.TransUnitCount; +import org.zanata.rest.StringSet; +import org.zanata.rest.client.RestClientFactory; +import org.zanata.rest.client.SourceDocResourceClient; +import org.zanata.rest.client.StatisticsResourceClient; +import org.zanata.rest.client.TransDocResourceClient; +import org.zanata.rest.dto.resource.Resource; +import org.zanata.rest.dto.stats.ContainerTranslationStatistics; +import org.zanata.rest.dto.stats.TranslationStatistics; + +import com.google.common.collect.Lists; + +public class PullCommandTest { + public static final StringSet EXTENSIONS = new StringSet("comment"); + @Mock + private RestClientFactory restClientFactory; + private PullOptionsImpl opts; + private final String projectSlug = "project"; + private final String versionSlug = "master"; + @Mock + private SourceDocResourceClient sourceClient; + @Mock + private TransDocResourceClient transClient; + @Mock + private StatisticsResourceClient statsClient; + private LocaleList locales; + private PullCommand pullCommand; + + @Before + public void setUp() { + MockitoAnnotations.initMocks(this); + opts = new PullOptionsImpl(); + opts.setProj(projectSlug); + opts.setProjectVersion(versionSlug); + opts.setProjectType(ProjectType.Properties.name().toLowerCase()); + opts.setBatchMode(true); + + when( + restClientFactory.getSourceDocResourceClient(projectSlug, + versionSlug)).thenReturn( + sourceClient); + when(restClientFactory.getTransDocResourceClient(projectSlug, + versionSlug)).thenReturn(transClient); + when(restClientFactory.getStatisticsClient()).thenReturn(statsClient); + + locales = new LocaleList(); + opts.setLocaleMapList(locales); + pullCommand = new PullCommand(opts, restClientFactory); + } + + @Test + public void pullSourceOnlyWillIgnoreMinimumPercent() throws Exception { + locales.add(new LocaleMapping("zh")); + locales.add(new LocaleMapping("de")); + opts.setDryRun(true); + // Given: pull-type is source only and minimum-doc-percent is set to 80 + opts.setPullType("source"); + opts.setMinDocPercent(80); + when(sourceClient + .getResource("file1", EXTENSIONS)).thenReturn( + new Resource()); + pullCommand = new PullCommand(opts, restClientFactory) { + @Override + protected List + getQualifiedDocNamesForCurrentModuleFromServer() { + return Lists.newArrayList("file1"); + } + }; + + // When: + pullCommand.run(); + + // Then: + verifyZeroInteractions(statsClient, transClient); + } + + @Test + public void pullTransOnlyWillIgnoreMinimumPercentIfItIsZero() + throws Exception { + locales.add(new LocaleMapping("zh")); + locales.add(new LocaleMapping("de")); + opts.setDryRun(true); + // Given: pull-type is trans only and minimum-doc-percent is set to 0 + opts.setPullType("trans"); + opts.setMinDocPercent(0); + + pullCommand = new PullCommand(opts, restClientFactory) { + @Override + protected List + getQualifiedDocNamesForCurrentModuleFromServer() { + return Lists.newArrayList("file1"); + } + + @Override + protected void pullDocForLocale(PullStrategy strat, Resource doc, + String localDocName, String docUri, + boolean createSkeletons, + LocaleMapping locMapping, File transFile) + throws IOException { + // pretend we are pulling + transClient.getTranslations(docUri, + new LocaleId(locMapping.getLocale()), EXTENSIONS, + createSkeletons, null); + } + }; + + // When: + pullCommand.run(); + + // Then: + verifyZeroInteractions(statsClient); + verify(transClient).getTranslations("file1", new LocaleId("zh"), + EXTENSIONS, false, + null); + verify(transClient).getTranslations("file1", new LocaleId("de"), + EXTENSIONS, false, + null); + } + + @Test + public void pullTransOnlyWillUseMinimumPercentIfItIsNotZero() + throws Exception { + locales.add(new LocaleMapping("zh")); + locales.add(new LocaleMapping("de")); + opts.setDryRun(true); + // Given: pull-type is trans only and minimum-doc-percent is set to 80 + opts.setPullType("trans"); + opts.setMinDocPercent(80); + + ContainerTranslationStatistics statistics = + new ContainerTranslationStatistics(); + ContainerTranslationStatistics docStats = + new ContainerTranslationStatistics(); + docStats.setId("file1"); + statistics.addDetailedStats(docStats); + // zh has 100 approved + TranslationStatistics zhLocaleStats = + new TranslationStatistics(new TransUnitCount(100, 0, 0, 0, 0), + "zh"); + // de has 39 approved, 21 fuzzy and 40 translated so 79% translated + TranslationStatistics deLocaleStats = + new TranslationStatistics(new TransUnitCount(39, 21, 0, 40, 0), + "de"); + docStats.addStats(zhLocaleStats); + docStats.addStats(deLocaleStats); + + when(statsClient + .getStatistics(projectSlug, versionSlug, true, false, new String[] {"zh", "de"})) + .thenReturn(statistics); + + pullCommand = new PullCommand(opts, restClientFactory) { + @Override + protected List + getQualifiedDocNamesForCurrentModuleFromServer() { + return Lists.newArrayList("file1"); + } + + @Override + protected void pullDocForLocale(PullStrategy strat, Resource doc, + String localDocName, String docUri, + boolean createSkeletons, + LocaleMapping locMapping, File transFile) + throws IOException { + // pretend we are pulling + transClient.getTranslations(docUri, + new LocaleId(locMapping.getLocale()), EXTENSIONS, + createSkeletons, null); + } + }; + + // When: + pullCommand.run(); + + // Then: translation for "de" will not be pulled + verify(statsClient).getStatistics(projectSlug, versionSlug, true, + false, new String[] {"zh", "de"}); + verify(transClient).getTranslations("file1", new LocaleId("zh"), + EXTENSIONS, false, + null); + verifyNoMoreInteractions(transClient); + } + + @Test + public void whenMinimumPercentIsSetTo100ItWillUseTotalNumber() + throws Exception { + locales.add(new LocaleMapping("zh")); + locales.add(new LocaleMapping("de")); + opts.setDryRun(true); + // Given: pull-type is trans only and minimum-doc-percent is set to 100 + opts.setPullType("trans"); + opts.setMinDocPercent(100); + + ContainerTranslationStatistics statistics = + new ContainerTranslationStatistics(); + ContainerTranslationStatistics docStats = + new ContainerTranslationStatistics(); + docStats.setId("file1"); + statistics.addDetailedStats(docStats); + // zh has 100000 approved + TranslationStatistics zhLocaleStats = + new TranslationStatistics(new TransUnitCount(100000, 0, 0, 0, 0), + "zh"); + // de has 99999 approved, 1 untranslated so 99.999% translated + TranslationStatistics deLocaleStats = + new TranslationStatistics(new TransUnitCount(99999, 0, 1, 0, 0), + "de"); + docStats.addStats(zhLocaleStats); + docStats.addStats(deLocaleStats); + + when(statsClient + .getStatistics(projectSlug, versionSlug, true, false, new String[] {"zh", "de"})) + .thenReturn(statistics); + + pullCommand = new PullCommand(opts, restClientFactory) { + @Override + protected List + getQualifiedDocNamesForCurrentModuleFromServer() { + return Lists.newArrayList("file1"); + } + + @Override + protected void pullDocForLocale(PullStrategy strat, Resource doc, + String localDocName, String docUri, + boolean createSkeletons, + LocaleMapping locMapping, File transFile) + throws IOException { + // pretend we are pulling + transClient.getTranslations(docUri, + new LocaleId(locMapping.getLocale()), EXTENSIONS, + createSkeletons, null); + } + }; + + // When: + pullCommand.run(); + + // Then: translation for "de" will not be pulled + verify(statsClient).getStatistics(projectSlug, versionSlug, true, + false, new String[] {"zh", "de"}); + verify(transClient).getTranslations("file1", new LocaleId("zh"), + EXTENSIONS, false, + null); + verifyNoMoreInteractions(transClient); + } + +} diff --git a/zanata-client-commands/src/test/java/org/zanata/client/commands/pull/RawPullStrategyTest.java b/zanata-client-commands/src/test/java/org/zanata/client/commands/pull/RawPullStrategyTest.java index f60cd278..9d82f924 100644 --- a/zanata-client-commands/src/test/java/org/zanata/client/commands/pull/RawPullStrategyTest.java +++ b/zanata-client-commands/src/test/java/org/zanata/client/commands/pull/RawPullStrategyTest.java @@ -39,6 +39,7 @@ import org.zanata.client.config.FileMappingRule; import org.zanata.client.config.LocaleMapping; +import com.google.common.base.Optional; import com.google.common.collect.Lists; public class RawPullStrategyTest { @@ -64,7 +65,7 @@ public void canWriteToFileWithoutMapping() throws IOException { when(transFile.read(any(byte[].class))).thenReturn(-1); strategy.writeTransFile("foo/test.odt", new LocaleMapping("de"), - transFile); + transFile, Optional.absent()); assertThat( new File(tempTransFileRule.getTransDir(), "de/foo/test.odt").exists(), @@ -78,7 +79,7 @@ public void canWriteToFileWithMapping() throws IOException { when(transFile.read(any(byte[].class))).thenReturn(-1); strategy.writeTransFile("foo/test.odt", new LocaleMapping("de"), - transFile); + transFile, Optional.absent()); assertThat( new File(tempTransFileRule.getTransDir(), "de/test.odt").exists(), diff --git a/zanata-client-commands/src/test/java/org/zanata/client/commands/push/PropertiesPushStrategyTest.java b/zanata-client-commands/src/test/java/org/zanata/client/commands/push/PropertiesPushStrategyTest.java index 838d02bf..0bbdafdd 100644 --- a/zanata-client-commands/src/test/java/org/zanata/client/commands/push/PropertiesPushStrategyTest.java +++ b/zanata-client-commands/src/test/java/org/zanata/client/commands/push/PropertiesPushStrategyTest.java @@ -15,6 +15,7 @@ import org.junit.Test; import org.mockito.Mock; import org.mockito.MockitoAnnotations; +import org.zanata.adapter.properties.PropWriter; import org.zanata.rest.dto.resource.Resource; import org.zanata.util.PathUtil; @@ -49,7 +50,7 @@ public void utf8() throws Exception { } finally { fos.close(); } - PropertiesStrategy strat = new PropertiesStrategy("UTF-8"); + PropertiesStrategy strat = new PropertiesStrategy(PropWriter.CHARSET.UTF8); strat.setPushOptions(opts); strat.init(); diff --git a/zanata-client-commands/src/test/java/org/zanata/client/commands/push/PublicanPushCommandTest.java b/zanata-client-commands/src/test/java/org/zanata/client/commands/push/PublicanPushCommandTest.java deleted file mode 100644 index 54189a0f..00000000 --- a/zanata-client-commands/src/test/java/org/zanata/client/commands/push/PublicanPushCommandTest.java +++ /dev/null @@ -1,125 +0,0 @@ -package org.zanata.client.commands.push; - -import static org.mockito.Matchers.eq; -import static org.mockito.Matchers.isNotNull; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.when; - -import java.io.File; -import java.net.URI; -import java.util.ArrayList; -import java.util.List; - -import javax.ws.rs.core.Response.Status; - -import org.jboss.resteasy.client.ClientResponse; -import org.junit.Before; -import org.junit.Test; -import org.mockito.Answers; -import org.mockito.Mock; -import org.mockito.MockitoAnnotations; -import org.zanata.client.commands.DummyResponse; -import org.zanata.client.commands.OptionsUtil; -import org.zanata.client.commands.PublicanPushCommand; -import org.zanata.client.commands.PublicanPushOptionsImpl; -import org.zanata.client.commands.ZanataCommand; -import org.zanata.client.config.LocaleList; -import org.zanata.client.config.LocaleMapping; -import org.zanata.common.LocaleId; -import org.zanata.rest.StringSet; -import org.zanata.rest.client.ISourceDocResource; -import org.zanata.rest.client.ITranslatedDocResource; -import org.zanata.rest.client.ZanataProxyFactory; -import org.zanata.rest.dto.resource.Resource; -import org.zanata.rest.dto.resource.ResourceMeta; -import org.zanata.rest.dto.resource.TranslationsResource; - -public class PublicanPushCommandTest { - @Mock(answer = Answers.RETURNS_MOCKS) - ISourceDocResource mockSourceDocResource; - - @Mock - ITranslatedDocResource mockTranslationResources; - - @Before - public void beforeMethod() { - MockitoAnnotations.initMocks(this); - } - - @Test - public void publicanPushPot() throws Exception { - publicanPush(false, false); - } - - @Test - public void publicanPushPotAndPo() throws Exception { - publicanPush(true, false); - } - - @Test - public void publicanPushPotAndPoWithLocaleMapping() throws Exception { - publicanPush(true, true); - } - - @SuppressWarnings("deprecation") - private void publicanPush(boolean importPo, boolean mapLocale) - throws Exception { - PublicanPushOptionsImpl opts = new PublicanPushOptionsImpl(); - opts.setInteractiveMode(false); - String projectSlug = "project"; - opts.setProj(projectSlug); - String versionSlug = "1.0"; - opts.setProjectVersion(versionSlug); - opts.setSrcDir(new File("src/test/resources/test1")); - opts.setImportPo(importPo); - OptionsUtil.applyConfigFiles(opts); - if (mapLocale) { - LocaleList locales = new LocaleList(); - locales.add(new LocaleMapping("ja", "ja-JP")); - opts.setLocaleMapList(locales); - } - - List resourceMetaList = new ArrayList(); - resourceMetaList.add(new ResourceMeta("obsolete")); - resourceMetaList.add(new ResourceMeta("RPM")); - when(mockSourceDocResource.get(null)).thenReturn( - new DummyResponse>(Status.OK, - resourceMetaList)); - - final ClientResponse okResponse = - new DummyResponse(Status.OK, null); - when(mockSourceDocResource.deleteResource("obsolete")).thenReturn( - okResponse); - StringSet extensionSet = new StringSet("gettext;comment"); - when( - mockSourceDocResource.putResource(eq("RPM"), - (Resource) isNotNull(), eq(extensionSet), eq(true))) - .thenReturn(okResponse); - when( - mockSourceDocResource.putResource(eq("sub,RPM"), - (Resource) isNotNull(), eq(extensionSet), eq(true))) - .thenReturn(okResponse); - - if (importPo) { - LocaleId expectedLocale; - if (mapLocale) - expectedLocale = new LocaleId("ja"); - else - expectedLocale = new LocaleId("ja-JP"); - when( - mockTranslationResources.putTranslations(eq("RPM"), - eq(expectedLocale), - (TranslationsResource) isNotNull(), - eq(extensionSet), eq("auto"))).thenReturn( - okResponse); - } - ZanataProxyFactory mockRequestFactory = mock(ZanataProxyFactory.class); - - ZanataCommand cmd = - new PublicanPushCommand(opts, mockRequestFactory, - mockSourceDocResource, mockTranslationResources, - new URI("http://example.com/")); - cmd.runWithActions(); - } - -} diff --git a/zanata-client-commands/src/test/java/org/zanata/client/commands/push/PushCommandTest.java b/zanata-client-commands/src/test/java/org/zanata/client/commands/push/PushCommandTest.java index 74645101..a71a9e66 100644 --- a/zanata-client-commands/src/test/java/org/zanata/client/commands/push/PushCommandTest.java +++ b/zanata-client-commands/src/test/java/org/zanata/client/commands/push/PushCommandTest.java @@ -1,34 +1,31 @@ package org.zanata.client.commands.push; import static org.junit.Assert.assertEquals; +import static org.mockito.Matchers.any; import static org.mockito.Matchers.anyString; import static org.mockito.Matchers.eq; -import static org.mockito.Matchers.notNull; import static org.mockito.Mockito.when; import static org.mockito.MockitoAnnotations.initMocks; +import static org.zanata.client.TestUtils.fileFromClasspath; import java.io.File; -import java.net.URI; import java.util.ArrayList; import java.util.List; -import javax.ws.rs.core.Response.Status; - -import org.jboss.resteasy.client.ClientResponse; +import org.junit.Before; import org.junit.Test; import org.mockito.Mock; -import org.zanata.client.commands.DummyResponse; +import org.zanata.client.TestUtils; import org.zanata.client.commands.OptionsUtil; import org.zanata.client.commands.ZanataCommand; import org.zanata.client.config.LocaleList; import org.zanata.client.config.LocaleMapping; import org.zanata.common.LocaleId; import org.zanata.rest.StringSet; -import org.zanata.rest.client.IAsynchronousProcessResource; -import org.zanata.rest.client.ICopyTransResource; -import org.zanata.rest.client.ISourceDocResource; -import org.zanata.rest.client.ITranslatedDocResource; -import org.zanata.rest.client.ZanataProxyFactory; +import org.zanata.rest.client.AsyncProcessClient; +import org.zanata.rest.client.CopyTransClient; +import org.zanata.rest.client.RestClientFactory; +import org.zanata.rest.client.SourceDocResourceClient; import org.zanata.rest.dto.CopyTransStatus; import org.zanata.rest.dto.ProcessStatus; import org.zanata.rest.dto.resource.Resource; @@ -38,18 +35,22 @@ public class PushCommandTest { @Mock - ZanataProxyFactory mockRequestFactory; - @Mock - ISourceDocResource mockSourceDocResource; + private RestClientFactory clientFactory; @Mock - ITranslatedDocResource mockTranslationResources; + private SourceDocResourceClient sourceDocResourceClient; @Mock - ICopyTransResource mockCopyTransResource; + private AsyncProcessClient asyncProcessClient; @Mock - IAsynchronousProcessResource mockAsynchronousProcessResource; + private CopyTransClient copyTransClient; - public PushCommandTest() throws Exception { + @Before + public void setUp() { initMocks(this); + when(clientFactory.getSourceDocResourceClient(anyString(), anyString())) + .thenReturn(sourceDocResourceClient); + when(clientFactory.getAsyncProcessClient()).thenReturn( + asyncProcessClient); + when(clientFactory.getCopyTransClient()).thenReturn(copyTransClient); } @Test @@ -130,13 +131,13 @@ private void checkSplitResult(int listSize, int batchSize) throws Exception { opts.setProj(projectSlug); String versionSlug = "1.0"; opts.setProjectVersion(versionSlug); - opts.setSrcDir(new File("src/test/resources/test1/pot")); + opts.setSrcDir(fileFromClasspath("test1/pot")); if (pushTrans) { opts.setPushType("both"); } else { opts.setPushType("source"); } - opts.setTransDir(new File("src/test/resources/test1")); + opts.setTransDir(fileFromClasspath("test1")); opts.setProjectType("podir"); // opts.setNoCopyTrans(false); opts.setCopyTrans(true); @@ -153,27 +154,19 @@ private void checkSplitResult(int listSize, int batchSize) throws Exception { opts.setLocaleMapList(locales); OptionsUtil.applyConfigFiles(opts); - when(mockRequestFactory.getCopyTransResource()).thenReturn( - mockCopyTransResource); - when(mockRequestFactory.getAsynchronousProcessResource()).thenReturn( - mockAsynchronousProcessResource); - - return new PushCommand(opts, mockRequestFactory, mockSourceDocResource, - mockTranslationResources, new URI("http://example.com/")); + return new PushCommand(opts, + clientFactory.getCopyTransClient(), + clientFactory.getAsyncProcessClient(), clientFactory); } private void push(boolean pushTrans, boolean mapLocale) throws Exception { List resourceMetaList = new ArrayList(); resourceMetaList.add(new ResourceMeta("obsolete")); resourceMetaList.add(new ResourceMeta("RPM")); - when(mockSourceDocResource.get(null)).thenReturn( - new DummyResponse>(Status.OK, - resourceMetaList)); - - final ClientResponse okResponse = - new DummyResponse(Status.OK, null); - when(mockSourceDocResource.deleteResource("obsolete")).thenReturn( - okResponse); + when(sourceDocResourceClient.getResourceMeta(null)).thenReturn( + resourceMetaList); + when(sourceDocResourceClient.deleteResource("obsolete")).thenReturn( + null); StringSet extensionSet = new StringSet("gettext;comment"); // TODO These calls now use a false copyTrans value (2.0) but they // invoke the copy Trans resource. Still need to add Copy Trans resource @@ -186,23 +179,23 @@ private void push(boolean pushTrans, boolean mapLocale) throws Exception { mockStatus.setStatusCode(ProcessStatus.ProcessStatusCode.Finished); mockStatus.setMessages(new ArrayList()); when( - mockAsynchronousProcessResource.startSourceDocCreationOrUpdate( + asyncProcessClient.startSourceDocCreationOrUpdate( eq("RPM"), anyString(), anyString(), - (Resource) notNull(), eq(extensionSet), eq(false))) + any(Resource.class), eq(extensionSet), eq(false))) .thenReturn(mockStatus); when( - mockAsynchronousProcessResource.startSourceDocCreationOrUpdate( + asyncProcessClient.startSourceDocCreationOrUpdate( eq("sub,RPM"), anyString(), anyString(), - (Resource) notNull(), eq(extensionSet), eq(false))) + any(Resource.class), eq(extensionSet), eq(false))) .thenReturn(mockStatus); - when(mockAsynchronousProcessResource.getProcessStatus(anyString())) + when(asyncProcessClient.getProcessStatus(anyString())) .thenReturn(mockStatus); CopyTransStatus mockCopyTransStatus = new CopyTransStatus(); mockCopyTransStatus.setInProgress(false); mockCopyTransStatus.setPercentageComplete(100); when( - mockCopyTransResource.getCopyTransStatus(anyString(), + copyTransClient.getCopyTransStatus(anyString(), anyString(), anyString())).thenReturn( mockCopyTransStatus); @@ -214,13 +207,13 @@ private void push(boolean pushTrans, boolean mapLocale) throws Exception { expectedLocale = new LocaleId("ja-JP"); } when( - mockAsynchronousProcessResource + asyncProcessClient .startTranslatedDocCreationOrUpdate(eq("RPM"), anyString(), anyString(), eq(expectedLocale), - (TranslationsResource) notNull(), - eq(extensionSet), eq("auto"))).thenReturn( - mockStatus); + any(TranslationsResource.class), + eq(extensionSet), eq("auto"), eq(false))). + thenReturn(mockStatus); // when(mockTranslationResources.putTranslations(eq("RPM"), // eq(expectedLocale), (TranslationsResource) notNull(), // eq(extensionSet), eq("auto"))) diff --git a/zanata-client-commands/src/test/java/org/zanata/client/commands/push/RawPushCommandTest.java b/zanata-client-commands/src/test/java/org/zanata/client/commands/push/RawPushCommandTest.java new file mode 100644 index 00000000..c7ba4e86 --- /dev/null +++ b/zanata-client-commands/src/test/java/org/zanata/client/commands/push/RawPushCommandTest.java @@ -0,0 +1,96 @@ +package org.zanata.client.commands.push; + +import java.util.List; + +import org.junit.Before; +import org.junit.Test; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; +import org.zanata.client.commands.ConsoleInteractor; +import org.zanata.rest.client.RestClientFactory; + +import static org.hamcrest.Matchers.contains; +import static org.hamcrest.Matchers.containsInAnyOrder; +import static org.hamcrest.Matchers.equalTo; +import static org.junit.Assert.assertThat; +import static org.mockito.Mockito.when; + +/** + * @author Alex Eng aeng@redhat.com + */ +public class RawPushCommandTest { + + @Mock + private PushOptions opts; + @Mock + private RestClientFactory clientFactory; + @Mock + private ConsoleInteractor console; + + private RawPushCommand command; + + @Before + public void setup() { + MockitoAnnotations.initMocks(this); + when(opts.getProj()).thenReturn("project"); + when(opts.getProjectVersion()).thenReturn("version"); + when(opts.getProjectType()).thenReturn("File"); + command = new RawPushCommand(opts, clientFactory, console); + } + + @Test + public void extractFileTypeWithExtensionTest() { + String fileNameAndExtension = "properties[xml]"; + String type = command.extractType(fileNameAndExtension); + assertThat(type, equalTo("properties")); + } + + @Test + public void extractFileTypeWithoutExtensionTest() { + String fileNameAndExtension = "properties"; + String type = command.extractType(fileNameAndExtension); + assertThat(type, equalTo("properties")); + } + + @Test + public void extractFileTypeOnlyExtensionTest() { + String fileNameAndExtension = "[xml]"; + String type = command.extractType(fileNameAndExtension); + assertThat(type, equalTo("")); + } + + @Test + public void extractExtensionWithTypeTest() { + String fileNameAndExtension = "properties[xml]"; + List extensions = command.extractExtensions(fileNameAndExtension); + assertThat(extensions, contains("xml")); + } + + @Test + public void extractExtensionWithTypeTest2() { + String fileNameAndExtension = "properties[xml;html]"; + List extensions = command.extractExtensions(fileNameAndExtension); + assertThat(extensions, containsInAnyOrder("xml", "html")); + } + + @Test + public void extractExtensionWithoutTypeTest() { + String fileNameAndExtension = "[xml]"; + List extensions = command.extractExtensions(fileNameAndExtension); + assertThat(extensions, contains("xml")); + } + + @Test + public void extractExtensionWithoutTypeTest2() { + String fileNameAndExtension = "[xml;html]"; + List extensions = command.extractExtensions(fileNameAndExtension); + assertThat(extensions, containsInAnyOrder("xml", "html")); + } + + @Test + public void extractExtensionOnlyTypeTest() { + String fileNameAndExtension = "properties"; + List extensions = command.extractExtensions(fileNameAndExtension); + assertThat(extensions.size(), equalTo(0)); + } +} diff --git a/zanata-client-commands/src/test/java/org/zanata/client/commands/push/RawPushStrategyTest.java b/zanata-client-commands/src/test/java/org/zanata/client/commands/push/RawPushStrategyTest.java index f1b737ee..84cbdb28 100644 --- a/zanata-client-commands/src/test/java/org/zanata/client/commands/push/RawPushStrategyTest.java +++ b/zanata-client-commands/src/test/java/org/zanata/client/commands/push/RawPushStrategyTest.java @@ -87,7 +87,8 @@ public void canVisitTranslationFileWithoutFileMapping() throws IOException { tempFileRule.createTransFileRelativeToTransDir( "zh-Hans/src/test.odt"); - strategy.visitTranslationFiles("src/test.odt", visitor); + strategy.visitTranslationFiles("src/test.odt", visitor, + Optional.absent()); verify(visitor).visit(eq(deMapping), fileCaptor.capture()); assertThat(fileCaptor.getValue(), equalTo(deTransFile)); @@ -121,7 +122,8 @@ public void canVisitTranslationFileUsingFileMapping() throws IOException { new FileMappingRule("**/*.odt", "{locale}/{filename}.{extension}"))); - strategy.visitTranslationFiles("src/test.odt", visitor); + strategy.visitTranslationFiles("src/test.odt", visitor, + Optional.absent()); verify(visitor).visit(eq(deMapping), fileCaptor.capture()); assertThat(fileCaptor.getValue(), equalTo(deTransFile)); diff --git a/zanata-client-commands/src/test/java/org/zanata/client/commands/push/XliffPushStrategyTest.java b/zanata-client-commands/src/test/java/org/zanata/client/commands/push/XliffPushStrategyTest.java index e5780aa5..5a4726fa 100644 --- a/zanata-client-commands/src/test/java/org/zanata/client/commands/push/XliffPushStrategyTest.java +++ b/zanata-client-commands/src/test/java/org/zanata/client/commands/push/XliffPushStrategyTest.java @@ -18,6 +18,7 @@ import org.junit.Test; import org.mockito.Mock; import org.mockito.MockitoAnnotations; +import org.zanata.client.TestUtils; import org.zanata.client.commands.push.PushCommand.TranslationResourcesVisitor; import org.zanata.client.config.LocaleList; import org.zanata.client.config.LocaleMapping; @@ -34,8 +35,8 @@ public class XliffPushStrategyTest { private ImmutableList include; private ImmutableList exclude; - private final File sourceDir = new File("src/test/resources/xliffDir"); - private final File sourceDir2 = new File("src/test/resources/xliffDir2"); + private final File sourceDir = TestUtils.fileFromClasspath("xliffDir"); + private final File sourceDir2 = TestUtils.fileFromClasspath("xliffDir2"); private static final String sourceLocale = "en-US"; diff --git a/zanata-client-commands/src/test/java/org/zanata/client/config/ConfigUtilTest.java b/zanata-client-commands/src/test/java/org/zanata/client/config/ConfigUtilTest.java index 3d535110..475506d0 100644 --- a/zanata-client-commands/src/test/java/org/zanata/client/config/ConfigUtilTest.java +++ b/zanata-client-commands/src/test/java/org/zanata/client/config/ConfigUtilTest.java @@ -5,6 +5,7 @@ import org.apache.commons.configuration.HierarchicalINIConfiguration; import org.apache.commons.configuration.SubnodeConfiguration; import org.junit.Test; +import org.zanata.client.TestUtils; import static org.junit.Assert.assertEquals; @@ -12,8 +13,8 @@ public class ConfigUtilTest { @Test public void testReadUser() throws Exception { HierarchicalINIConfiguration config = - new HierarchicalINIConfiguration( - "src/test/resources/zanata.ini"); + new HierarchicalINIConfiguration(TestUtils.fileFromClasspath( + "zanata.ini").getAbsolutePath()); SubnodeConfiguration servers = config.getSection("servers"); String url = "https://translate.jboss.org/"; String username = "joe"; diff --git a/zanata-client-commands/src/test/java/org/zanata/client/integraion/PushPullFileProjectITCase.java b/zanata-client-commands/src/test/java/org/zanata/client/integration/PushPullFileProjectITCase.java similarity index 94% rename from zanata-client-commands/src/test/java/org/zanata/client/integraion/PushPullFileProjectITCase.java rename to zanata-client-commands/src/test/java/org/zanata/client/integration/PushPullFileProjectITCase.java index 2d9c5cf5..5780e112 100644 --- a/zanata-client-commands/src/test/java/org/zanata/client/integraion/PushPullFileProjectITCase.java +++ b/zanata-client-commands/src/test/java/org/zanata/client/integration/PushPullFileProjectITCase.java @@ -19,13 +19,10 @@ * 02110-1301 USA, or see the FSF site: http://www.fsf.org. */ -package org.zanata.client.integraion; +package org.zanata.client.integration; -import java.io.BufferedInputStream; import java.io.File; import java.io.InputStream; -import java.io.StringBufferInputStream; -import java.io.StringReader; import java.util.ArrayList; import org.apache.commons.codec.Charsets; @@ -39,20 +36,14 @@ import org.zanata.client.MockServerRule; import org.zanata.client.TestProjectGenerator; import org.zanata.client.commands.ConfigurableProjectOptions; -import org.zanata.client.commands.pull.PullCommand; import org.zanata.client.commands.pull.PullOptionsImpl; import org.zanata.client.commands.pull.RawPullCommand; import org.zanata.client.commands.push.PushOptionsImpl; import org.zanata.client.commands.push.RawPushCommand; import org.zanata.client.config.FileMappingRule; import org.zanata.client.config.LocaleMapping; -import org.zanata.common.LocaleId; import org.zanata.common.ProjectType; -import org.zanata.rest.dto.resource.Resource; import org.zanata.rest.dto.resource.ResourceMeta; -import org.zanata.rest.dto.resource.TextFlow; -import org.zanata.rest.dto.resource.TextFlowTarget; -import org.zanata.rest.dto.resource.TranslationsResource; import com.google.common.collect.Lists; import static org.hamcrest.MatcherAssert.assertThat; @@ -193,3 +184,4 @@ public void pullFileProjectUsingFileMapping() throws Exception { assertThat(new File(pullBaseDir, "ods/zh_CN/test-ods.ods").exists(), is(true)); } } + diff --git a/zanata-client-commands/src/test/java/org/zanata/client/integraion/PushPullGettextITCase.java b/zanata-client-commands/src/test/java/org/zanata/client/integration/PushPullGettextITCase.java similarity index 99% rename from zanata-client-commands/src/test/java/org/zanata/client/integraion/PushPullGettextITCase.java rename to zanata-client-commands/src/test/java/org/zanata/client/integration/PushPullGettextITCase.java index 1290cb02..4743e331 100644 --- a/zanata-client-commands/src/test/java/org/zanata/client/integraion/PushPullGettextITCase.java +++ b/zanata-client-commands/src/test/java/org/zanata/client/integration/PushPullGettextITCase.java @@ -1,7 +1,6 @@ -package org.zanata.client.integraion; +package org.zanata.client.integration; import java.io.File; -import java.io.IOException; import java.util.Set; import org.hamcrest.Matchers; @@ -176,3 +175,4 @@ public void pullGettextProjectUsingFileMapping() throws Exception { } } + diff --git a/zanata-maven-plugin/pom.xml b/zanata-maven-plugin/pom.xml index 05da0674..c58980b1 100644 --- a/zanata-maven-plugin/pom.xml +++ b/zanata-maven-plugin/pom.xml @@ -4,7 +4,7 @@ org.zanata client - 3.6.1-SNAPSHOT + 3.7.0-SNAPSHOT zanata-maven-plugin diff --git a/zanata-maven-plugin/src/main/java/org/zanata/maven/AbstractPullMojo.java b/zanata-maven-plugin/src/main/java/org/zanata/maven/AbstractPullMojo.java index 4aeee35a..5602b2ed 100644 --- a/zanata-maven-plugin/src/main/java/org/zanata/maven/AbstractPullMojo.java +++ b/zanata-maven-plugin/src/main/java/org/zanata/maven/AbstractPullMojo.java @@ -27,6 +27,7 @@ import org.zanata.client.commands.pull.PullOptions; import org.zanata.client.commands.pull.RawPullCommand; +import com.google.common.base.Preconditions; import edu.umd.cs.findbugs.annotations.SuppressFBWarnings; /** @@ -107,11 +108,23 @@ public abstract class AbstractPullMojo extends */ private boolean continueAfterError = false; + /** + * Accepts integer from 0 to 100. Only pull translation documents which are + * at least PERCENT % (message based) completed. Please note specifying this + * option may cause longer time to pull for a large project. + * + * @parameter expression="${zanata.minDocPercent}" default-value="0" + */ + private int minDocPercent = 0; + /** * */ public AbstractPullMojo() { super(); + Preconditions + .checkArgument(minDocPercent >= 0 && minDocPercent <= 100, + "zanata.minDocPercent should be an integer from 0 to 100"); } public PushPullCommand initCommand() { @@ -174,4 +187,9 @@ public String getCommandName() { public boolean isAuthRequired() { return false; } + + @Override + public int getMinDocPercent() { + return minDocPercent; + } } diff --git a/zanata-maven-plugin/src/main/java/org/zanata/maven/AbstractPushMojo.java b/zanata-maven-plugin/src/main/java/org/zanata/maven/AbstractPushMojo.java index 80019be6..bf7c5ce8 100644 --- a/zanata-maven-plugin/src/main/java/org/zanata/maven/AbstractPushMojo.java +++ b/zanata-maven-plugin/src/main/java/org/zanata/maven/AbstractPushMojo.java @@ -123,10 +123,10 @@ public PushPullCommand initCommand() { private boolean caseSensitive = true; /** - * Exclude filenames which match locales in zanata.xml (other than the - * source locale). For instance, if zanata.xml includes de and fr, then the - * files messages_de.properties and messages_fr.properties will not be - * treated as source files. + * Exclude filenames which match locales configured for the project (other + * than the source locale). For instance, if project includes de and fr, + * then the files messages_de.properties and messages_fr.properties will not + * be treated as source files. *

* NB: This parameter will be ignored for some project types which use * different file naming conventions (eg podir, gettext). @@ -146,6 +146,14 @@ public PushPullCommand initCommand() { */ private String validate = "content"; + /** + * Indicates if all uploaded translations were translated by you. + * + * @parameter expression="${zanata.myTrans}" + * default-value="false" + */ + private boolean myTrans = false; + @Override public String getSourceLang() { return sourceLang; @@ -207,4 +215,8 @@ public String getValidate() { return validate; } + @Override + public boolean isMyTrans() { + return myTrans; + } } diff --git a/zanata-maven-plugin/src/main/java/org/zanata/maven/AbstractPushPullMojo.java b/zanata-maven-plugin/src/main/java/org/zanata/maven/AbstractPushPullMojo.java index 776aad9e..b1c4338d 100644 --- a/zanata-maven-plugin/src/main/java/org/zanata/maven/AbstractPushPullMojo.java +++ b/zanata-maven-plugin/src/main/java/org/zanata/maven/AbstractPushPullMojo.java @@ -131,8 +131,9 @@ private String toMavenModuleID(MavenProject module) { private String fromDoc; /** - * Locales to push to/pull from the server. By default all locales in - * zanata.xml will be pushed/pulled. Usage: + * Locales to push to/pull from the server. By default all locales + * configured for the project will be pushed/pulled. + * Usage: * -Dzanata.locales=locale1,locale2,locale3 * * @parameter expression="${zanata.locales}" diff --git a/zanata-maven-plugin/src/main/java/org/zanata/maven/PublicanPullMojo.java b/zanata-maven-plugin/src/main/java/org/zanata/maven/PublicanPullMojo.java deleted file mode 100644 index 564215dd..00000000 --- a/zanata-maven-plugin/src/main/java/org/zanata/maven/PublicanPullMojo.java +++ /dev/null @@ -1,96 +0,0 @@ -package org.zanata.maven; - -import java.io.File; - -import org.zanata.client.commands.PublicanPullCommand; -import org.zanata.client.commands.PublicanPullOptions; - -/** - * Pulls translated text from Zanata. DEPRECATED: use 'pull' with projectType - * 'podir' goal. - * - * @goal publican-pull - * @requiresOnline true - * @author Sean Flanigan - * @deprecated - * @see PullSimpleMojo - */ -public class PublicanPullMojo extends - ConfigurableProjectMojo implements - PublicanPullOptions { - - /** - * Base directory for publican files (with subdirectory "pot" and optional - * locale directories), although the location of "pot" can be overridden - * with the dstDirPot option. - * - * @parameter expression="${zanata.dstDir}" - * @required - */ - private File dstDir; - - /** - * Base directory for pot files. - * - * @parameter expression="${zanata.dstDirPot}" - * default-value="${zanata.dstDir}/pot" - * @required - */ - private File dstDirPot; - - /** - * Export source text from Zanata to local POT files, overwriting or erasing - * existing POT files (DANGER!) - * - * @parameter expression="${zanata.exportPot}" - */ - private boolean exportPot; - - public PublicanPullMojo() throws Exception { - super(); - } - - public PublicanPullCommand initCommand() { - return new PublicanPullCommand(this); - } - - @Override - public void setDstDir(File dstDir) { - this.dstDir = dstDir; - } - - @Override - public void setDstDirPot(File dstDirPot) { - this.dstDirPot = dstDirPot; - } - - @Override - public File getDstDir() { - return dstDir; - } - - @Override - public File getDstDirPot() { - return dstDirPot; - } - - @Override - public boolean getExportPot() { - return exportPot; - } - - @Override - public void setExportPot(boolean exportPot) { - this.exportPot = exportPot; - } - - @Override - public String getCommandName() { - return "publican-pull"; - } - - @Override - public boolean isAuthRequired() { - return false; - } -} diff --git a/zanata-maven-plugin/src/main/java/org/zanata/maven/PublicanPushMojo.java b/zanata-maven-plugin/src/main/java/org/zanata/maven/PublicanPushMojo.java deleted file mode 100644 index 7e9530d7..00000000 --- a/zanata-maven-plugin/src/main/java/org/zanata/maven/PublicanPushMojo.java +++ /dev/null @@ -1,125 +0,0 @@ -package org.zanata.maven; - -import java.io.File; - -import org.zanata.client.commands.PublicanPushCommand; -import org.zanata.client.commands.PublicanPushOptions; - -/** - * Pushes publican source text to a Zanata project version so that it can be - * translated. DEPRECATED: use 'push' with projectType 'podir'. - * - * @goal publican-push - * @requiresOnline true - * @author Sean Flanigan - * @deprecated - * @see PushSimpleMojo - */ -public class PublicanPushMojo extends - ConfigurableProjectMojo implements - PublicanPushOptions { - - public PublicanPushMojo() throws Exception { - super(); - } - - @Override - public PublicanPushCommand initCommand() { - return new PublicanPushCommand(this); - } - - /** - * Base directory for publican files (with subdirectory "pot" and optional - * locale directories), although the location of "pot" can be overridden - * with the srcDirPot option. - * - * @parameter expression="${zanata.srcDir}" - * @required - */ - private File srcDir; - - /** - * Base directory for pot files. - * - * @parameter expression="${zanata.srcDirPot}" - * default-value="${zanata.srcDir}/pot" - */ - private File srcDirPot; - - /** - * Language of source (defaults to en-US) - * - * @parameter expression="${zanata.sourceLang}" - */ - private String sourceLang = "en-US"; - - /** - * Import/merge translations from local PO files to the server, overwriting - * or erasing existing translations (DANGER!) - * - * @parameter expression="${zanata.importPo}" - */ - private boolean importPo; - - /** - * Whether the server should copy latest translations from equivalent - * messages/documents in the database. - * - * @parameter expression="${zanata.copyTrans}" default-value="true" - */ - private boolean copyTrans; - - /** - * Should the client validate XML before sending request to server - * (debugging). - * - * @parameter expression="${zanata.validate}" - */ - private boolean validate; - - /** - * Merge type: "auto" (default) or "import" (DANGER!). - * - * @parameter expression="${zanata.merge}" default-value="auto" - */ - private String merge; - - @Override - public File getSrcDir() { - return srcDir; - } - - @Override - public File getSrcDirPot() { - return srcDirPot; - } - - @Override - public String getSourceLang() { - return sourceLang; - } - - @Override - public boolean getImportPo() { - return importPo; - } - - @Override - public boolean getCopyTrans() { - return copyTrans; - } - - public boolean getValidate() { - return validate; - } - - @Override - public String getMergeType() { - return merge; - } - - @Override - public String getCommandName() { - return "publican-push"; - } -} diff --git a/zanata-maven-plugin/src/main/java/org/zanata/maven/PushSimpleMojo.java b/zanata-maven-plugin/src/main/java/org/zanata/maven/PushSimpleMojo.java index 48b310ab..c8dce97b 100644 --- a/zanata-maven-plugin/src/main/java/org/zanata/maven/PushSimpleMojo.java +++ b/zanata-maven-plugin/src/main/java/org/zanata/maven/PushSimpleMojo.java @@ -41,6 +41,7 @@ public boolean getEnableModules() { @Override public boolean getDeleteObsoleteModules() { - return false; // False for Simple push + // False for Simple push + return false; } } diff --git a/zanata-maven-plugin/src/test/java/org/zanata/maven/PushMojoTest.java b/zanata-maven-plugin/src/test/java/org/zanata/maven/PushMojoTest.java index 8be6f944..3d20bba2 100644 --- a/zanata-maven-plugin/src/test/java/org/zanata/maven/PushMojoTest.java +++ b/zanata-maven-plugin/src/test/java/org/zanata/maven/PushMojoTest.java @@ -1,7 +1,5 @@ package org.zanata.maven; -import java.util.Arrays; - import com.google.common.collect.ImmutableList; import org.zanata.client.commands.push.PushCommand; import org.zanata.client.commands.PushPullType; diff --git a/zanata-maven-plugin/src/test/java/org/zanata/maven/ZanataMojoTest.java b/zanata-maven-plugin/src/test/java/org/zanata/maven/ZanataMojoTest.java index ce34301c..b4a4ef89 100644 --- a/zanata-maven-plugin/src/test/java/org/zanata/maven/ZanataMojoTest.java +++ b/zanata-maven-plugin/src/test/java/org/zanata/maven/ZanataMojoTest.java @@ -1,6 +1,7 @@ package org.zanata.maven; import java.io.File; +import java.net.URL; import org.apache.maven.plugin.Mojo; import org.apache.maven.plugin.testing.AbstractMojoTestCase; @@ -34,7 +35,9 @@ protected void tearDown() throws Exception { } protected void applyPomParams(String pomFile) throws Exception { - File testPom = getTestFile("src/test/resources/push-test/" + pomFile); + URL resource = Thread.currentThread().getContextClassLoader() + .getResource("push-test/" + pomFile); + File testPom = new File(resource.getFile()); // This will work with "mvn test", but not with Eclipse's JUnit runner: // PushSimpleMojo mojo = (PushSimpleMojo) lookupMojo("push", testPom); // assertNotNull(mojo); diff --git a/zanata-rest-client/pom.xml b/zanata-rest-client/pom.xml index 842a376f..9539bef8 100644 --- a/zanata-rest-client/pom.xml +++ b/zanata-rest-client/pom.xml @@ -6,7 +6,7 @@ org.zanata client - 3.6.1-SNAPSHOT + 3.7.0-SNAPSHOT jar @@ -20,37 +20,95 @@ + + + org.jboss.resteasy + jaxrs-api + test + org.zanata zanata-common-api + + org.zanata + zanata-adapter-po + - junit - junit + org.zanata + stub-server + ${project.version} test + - org.jboss.resteasy - jaxrs-api - ${resteasy.version} + com.sun.jersey + jersey-core + ${jersey.version} - - org.jboss.resteasy - resteasy-jaxrs - - - net.jcip - jcip-annotations - - + com.sun.jersey + jersey-client + ${jersey.version} - org.jboss.resteasy - resteasy-client - ${resteasy.version} + com.sun.jersey.contribs + jersey-multipart + ${jersey.version} + + + + org.codehaus.jackson + jackson-core-asl + + + + org.codehaus.jackson + jackson-jaxrs + + + + org.codehaus.jackson + jackson-mapper-asl + + + org.codehaus.jackson + jackson-xc + + + + log4j + log4j + + + org.slf4j + slf4j-log4j12 + + + + commons-codec + commons-codec + + + + org.hamcrest + hamcrest-core + test + + + org.hamcrest + hamcrest-library + test + + + + junit + junit + test + + commons-beanutils commons-beanutils @@ -61,10 +119,7 @@ - - org.apache.httpcomponents - httpclient - + org.slf4j slf4j-api @@ -78,6 +133,24 @@ guava + + org.mockito + mockito-core + test + + + + + + org.basepom.maven + duplicate-finder-maven-plugin + + false + + + + + diff --git a/zanata-rest-client/src/main/java/org/zanata/rest/client/AcceptTypeFilter.java b/zanata-rest-client/src/main/java/org/zanata/rest/client/AcceptTypeFilter.java new file mode 100644 index 00000000..6e1c7119 --- /dev/null +++ b/zanata-rest-client/src/main/java/org/zanata/rest/client/AcceptTypeFilter.java @@ -0,0 +1,49 @@ +/* + * Copyright 2014, Red Hat, Inc. and individual contributors + * as indicated by the @author tags. See the copyright.txt file in the + * distribution for a full listing of individual contributors. + * + * This is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this software; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA, or see the FSF site: http://www.fsf.org. + */ + +package org.zanata.rest.client; + +import javax.ws.rs.core.MultivaluedMap; + +import com.sun.jersey.api.client.ClientHandlerException; +import com.sun.jersey.api.client.ClientRequest; +import com.sun.jersey.api.client.ClientResponse; +import com.sun.jersey.api.client.filter.ClientFilter; + +/** + * This adds a generic Accept header to all request. It is a workaround for + * RESTEasy 2 service. Clients requiring specific Accpet type can override it. + * + * @author Patrick Huang pahuang@redhat.com + */ +public class AcceptTypeFilter extends ClientFilter { + @Override + public ClientResponse handle(ClientRequest cr) + throws ClientHandlerException { + MultivaluedMap headers = cr.getHeaders(); + // make sure we have at least one Accept header otherwise jersey will + // insert an "Accept: text/html, image/gif, image/jpeg, *" which breaks + // RESTEasy 2 + headers.add("Accept", "application/*"); + return getNext().handle(cr); + } +} diff --git a/zanata-rest-client/src/main/java/org/zanata/rest/client/AccountClient.java b/zanata-rest-client/src/main/java/org/zanata/rest/client/AccountClient.java new file mode 100644 index 00000000..940892aa --- /dev/null +++ b/zanata-rest-client/src/main/java/org/zanata/rest/client/AccountClient.java @@ -0,0 +1,60 @@ +/* + * Copyright 2014, Red Hat, Inc. and individual contributors + * as indicated by the @author tags. See the copyright.txt file in the + * distribution for a full listing of individual contributors. + * + * This is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this software; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA, or see the FSF site: http://www.fsf.org. + */ + +package org.zanata.rest.client; + +import java.net.URI; + +import org.zanata.rest.MediaTypes; +import org.zanata.rest.dto.Account; + +import com.sun.jersey.api.client.WebResource; + +/** + * @author Patrick Huang pahuang@redhat.com + */ +public class AccountClient { + private final RestClientFactory factory; + private final URI baseUri; + + AccountClient(RestClientFactory factory) { + this.factory = factory; + this.baseUri = factory.getBaseUri(); + } + + public Account get(String username) { + return webResource(username) + .get(Account.class); + } + + public void put(String username, Account account) { + webResource(username).type( + MediaTypes.APPLICATION_ZANATA_ACCOUNT_XML) + .put(account); + } + + private WebResource webResource(String username) { + return factory.getClient().resource(baseUri) + .path("accounts").path("u") + .path(username); + } +} diff --git a/zanata-rest-client/src/main/java/org/zanata/rest/client/ApiKeyHeaderDecorator.java b/zanata-rest-client/src/main/java/org/zanata/rest/client/ApiKeyHeaderDecorator.java deleted file mode 100644 index 1b875a9c..00000000 --- a/zanata-rest-client/src/main/java/org/zanata/rest/client/ApiKeyHeaderDecorator.java +++ /dev/null @@ -1,75 +0,0 @@ -package org.zanata.rest.client; - -import javax.ws.rs.ext.Provider; - -import org.apache.commons.lang.StringUtils; -import org.jboss.resteasy.annotations.interception.ClientInterceptor; -import org.jboss.resteasy.client.ClientResponse; -import org.jboss.resteasy.spi.interception.ClientExecutionContext; -import org.jboss.resteasy.spi.interception.ClientExecutionInterceptor; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.zanata.rest.RestConstant; - -@Provider -@ClientInterceptor -public class ApiKeyHeaderDecorator implements ClientExecutionInterceptor { - private static Logger log = LoggerFactory - .getLogger(ApiKeyHeaderDecorator.class); - private String apiKey; - private String username; - private String ver; - - public ApiKeyHeaderDecorator() { - } - - public ApiKeyHeaderDecorator(String username, String apiKey, String ver) { - this.username = username; - this.apiKey = apiKey; - this.ver = ver; - } - - @SuppressWarnings("rawtypes") - @Override - public ClientResponse execute(ClientExecutionContext ctx) throws Exception { - if(StringUtils.isNotEmpty(username)) { - ctx.getRequest().getHeadersAsObjects() - .add(RestConstant.HEADER_USERNAME, username); - } - if(StringUtils.isNotEmpty(apiKey)) { - ctx.getRequest().getHeadersAsObjects() - .add(RestConstant.HEADER_API_KEY, apiKey); - } - ctx.getRequest().getHeadersAsObjects() - .add(RestConstant.HEADER_VERSION_NO, ver); - try { - return ctx.proceed(); - } catch (Error e) { - // NB Seam/RestEasy doesn't log these exceptions fully for some - // reason - log.warn("error processing request", e); - throw e; - } catch (Exception e) { - // NB Seam/RestEasy doesn't log these exceptions fully for some - // reason - log.warn("exception processing request", e); - throw e; - } - } - - public String getApiKey() { - return apiKey; - } - - public void setApiKey(String apiKey) { - this.apiKey = apiKey; - } - - public String getUsername() { - return username; - } - - public void setUsername(String username) { - this.username = username; - } -} diff --git a/zanata-rest-client/src/main/java/org/zanata/rest/client/ApiKeyHeaderFilter.java b/zanata-rest-client/src/main/java/org/zanata/rest/client/ApiKeyHeaderFilter.java new file mode 100644 index 00000000..77c67af4 --- /dev/null +++ b/zanata-rest-client/src/main/java/org/zanata/rest/client/ApiKeyHeaderFilter.java @@ -0,0 +1,70 @@ +/* + * Copyright 2014, Red Hat, Inc. and individual contributors + * as indicated by the @author tags. See the copyright.txt file in the + * distribution for a full listing of individual contributors. + * + * This is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this software; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA, or see the FSF site: http://www.fsf.org. + */ + +package org.zanata.rest.client; + +import javax.ws.rs.core.MultivaluedMap; +import javax.ws.rs.ext.Provider; + +import org.zanata.rest.RestConstant; + +import com.google.common.annotations.VisibleForTesting; +import com.sun.jersey.api.client.ClientHandlerException; +import com.sun.jersey.api.client.ClientRequest; +import com.sun.jersey.api.client.ClientResponse; +import com.sun.jersey.api.client.filter.ClientFilter; + +@Provider +public class ApiKeyHeaderFilter extends ClientFilter { + private String apiKey; + private String username; + private String ver; + + public ApiKeyHeaderFilter(String username, String apiKey, String ver) { + this.username = username; + this.apiKey = apiKey; + this.ver = ver; + } + + @Override + public ClientResponse handle(ClientRequest cr) + throws ClientHandlerException { + MultivaluedMap headers = cr.getHeaders(); + headers.add(RestConstant.HEADER_USERNAME, username); + headers.add(RestConstant.HEADER_API_KEY, apiKey); + headers.add(RestConstant.HEADER_VERSION_NO, ver); + return handleNext(cr); + } + + @VisibleForTesting + protected ClientResponse handleNext(ClientRequest cr) { + return getNext().handle(cr); + } + + public String getUsername() { + return username; + } + + public void setUsername(String username) { + this.username = username; + } +} + diff --git a/zanata-rest-client/src/main/java/org/zanata/rest/client/AsyncProcessClient.java b/zanata-rest-client/src/main/java/org/zanata/rest/client/AsyncProcessClient.java new file mode 100644 index 00000000..d7c19bcf --- /dev/null +++ b/zanata-rest-client/src/main/java/org/zanata/rest/client/AsyncProcessClient.java @@ -0,0 +1,106 @@ +/* + * Copyright 2014, Red Hat, Inc. and individual contributors + * as indicated by the @author tags. See the copyright.txt file in the + * distribution for a full listing of individual contributors. + * + * This is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this software; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA, or see the FSF site: http://www.fsf.org. + */ + +package org.zanata.rest.client; + +import java.net.URI; +import java.util.Set; +import javax.ws.rs.DefaultValue; + +import org.zanata.common.LocaleId; +import org.zanata.rest.dto.ProcessStatus; +import org.zanata.rest.dto.resource.Resource; +import org.zanata.rest.dto.resource.TranslationsResource; +import org.zanata.rest.service.AsynchronousProcessResource; +import com.sun.jersey.api.client.Client; +import com.sun.jersey.api.client.WebResource; + +/** + * @author Patrick Huang pahuang@redhat.com + */ +public class AsyncProcessClient implements AsynchronousProcessResource { + private final RestClientFactory factory; + private final URI baseUri; + + AsyncProcessClient(RestClientFactory factory) { + this.factory = factory; + baseUri = factory.getBaseUri(); + } + + @Override + public ProcessStatus startSourceDocCreation(String idNoSlash, + String projectSlug, String iterationSlug, Resource resource, + Set extensions, @DefaultValue("true") boolean copytrans) { + throw new UnsupportedOperationException( + "Not supported. Use startSourceDocCreationOrUpdate instead."); + } + + @Override + public ProcessStatus startSourceDocCreationOrUpdate(String idNoSlash, + String projectSlug, String iterationSlug, Resource resource, + Set extensions, @DefaultValue("true") boolean copytrans) { + Client client = factory.getClient(); + CacheResponseFilter filter = new CacheResponseFilter(); + client.addFilter(filter); + WebResource webResource = client.resource(baseUri) + .path(AsynchronousProcessResource.SERVICE_PATH) + .path("projects").path("p").path(projectSlug) + .path("iterations").path("i").path(iterationSlug) + .path("r").path(idNoSlash); + webResource + .queryParams(ClientUtil.asMultivaluedMap("ext", extensions)) + .queryParam("copyTrans", String.valueOf(copytrans)) + .put(resource); + client.removeFilter(filter); + return filter.getEntity(ProcessStatus.class); + } + + @Override + public ProcessStatus startTranslatedDocCreationOrUpdate(String idNoSlash, + String projectSlug, String iterationSlug, LocaleId locale, + TranslationsResource translatedDoc, Set extensions, + String merge, @DefaultValue("false") boolean myTrans) { + Client client = factory.getClient(); + CacheResponseFilter filter = new CacheResponseFilter(); + client.addFilter(filter); + WebResource webResource = client.resource(baseUri) + .path(AsynchronousProcessResource.SERVICE_PATH) + .path("projects").path("p").path(projectSlug) + .path("iterations").path("i").path(iterationSlug) + .path("r").path(idNoSlash) + .path("translations").path(locale.toString()); + webResource + .queryParams(ClientUtil.asMultivaluedMap("ext", extensions)) + .queryParam("merge", merge) + .queryParam("assignCreditToUploader", String.valueOf(myTrans)) + .put(translatedDoc); + client.removeFilter(filter); + return filter.getEntity(ProcessStatus.class); + } + + @Override + public ProcessStatus getProcessStatus(String processId) { + return factory.getClient().resource(baseUri) + .path(AsynchronousProcessResource.SERVICE_PATH) + .path(processId).get(ProcessStatus.class); + } +} diff --git a/zanata-rest-client/src/main/java/org/zanata/rest/client/CacheResponseFilter.java b/zanata-rest-client/src/main/java/org/zanata/rest/client/CacheResponseFilter.java new file mode 100644 index 00000000..eb92abfd --- /dev/null +++ b/zanata-rest-client/src/main/java/org/zanata/rest/client/CacheResponseFilter.java @@ -0,0 +1,65 @@ +/* + * Copyright 2014, Red Hat, Inc. and individual contributors + * as indicated by the @author tags. See the copyright.txt file in the + * distribution for a full listing of individual contributors. + * + * This is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this software; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA, or see the FSF site: http://www.fsf.org. + */ + +package org.zanata.rest.client; + +import com.google.common.base.Optional; +import com.google.common.base.Preconditions; +import com.sun.jersey.api.client.ClientHandlerException; +import com.sun.jersey.api.client.ClientRequest; +import com.sun.jersey.api.client.ClientResponse; +import com.sun.jersey.api.client.GenericType; +import com.sun.jersey.api.client.filter.ClientFilter; + +/** + * This is a workaround that jersey client don't support put/post returning + * response. + * + * @author Patrick Huang pahuang@redhat.com + */ +class CacheResponseFilter extends ClientFilter { + private Optional cachedClientResponse = Optional.absent(); + + @Override + public ClientResponse handle(ClientRequest cr) + throws ClientHandlerException { + ClientResponse response = getNext().handle(cr); + response.bufferEntity(); + cachedClientResponse = Optional.of(response); + return response; + } + + public T getEntity(Class type) { + checkState(); + return cachedClientResponse.get().getEntity(type); + } + + public T getEntity(GenericType genericType) { + checkState(); + return cachedClientResponse.get().getEntity(genericType); + } + + private void checkState() { + Preconditions.checkState(cachedClientResponse.isPresent(), + "No cached ClientResponse. Did you forget to add this filter?"); + } +} diff --git a/zanata-rest-client/src/main/java/org/zanata/rest/client/ClientUtil.java b/zanata-rest-client/src/main/java/org/zanata/rest/client/ClientUtil.java new file mode 100644 index 00000000..bdb747cf --- /dev/null +++ b/zanata-rest-client/src/main/java/org/zanata/rest/client/ClientUtil.java @@ -0,0 +1,134 @@ +/* + * Copyright 2014, Red Hat, Inc. and individual contributors + * as indicated by the @author tags. See the copyright.txt file in the + * distribution for a full listing of individual contributors. + * + * This is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this software; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA, or see the FSF site: http://www.fsf.org. + */ + +package org.zanata.rest.client; + +import java.net.MalformedURLException; +import java.net.URI; +import java.net.URISyntaxException; +import java.net.URL; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +import javax.ws.rs.core.MultivaluedMap; +import javax.ws.rs.core.Response; + +import com.google.common.base.Strings; +import com.sun.jersey.api.client.ClientResponse; +import com.sun.jersey.core.util.MultivaluedMapImpl; +import org.apache.commons.lang.StringUtils; + +/** + * @author Patrick Huang pahuang@redhat.com + */ +public class ClientUtil { + + static MultivaluedMap asMultivaluedMap( + String paramKey, Iterable values) { + MultivaluedMapImpl map = new MultivaluedMapImpl(); + if (values == null) { + return map; + } + for (String extension : values) { + map.add(paramKey, extension); + } + return map; + } + + public static void checkResult(ClientResponse response) { + ClientResponse.Status responseStatus = + response.getClientResponseStatus(); + int statusCode = response.getStatus(); + + if (responseStatus == ClientResponse.Status.UNAUTHORIZED) { + throw new RuntimeException("Incorrect username/password"); + } else if (responseStatus == ClientResponse.Status.SERVICE_UNAVAILABLE) { + throw new RuntimeException("Service is currently unavailable. " + + "Please check outage notification or try again later."); + } else if (responseStatus == ClientResponse.Status.MOVED_PERMANENTLY + || statusCode == 302) { + // if server returns a redirect (most likely due to http to https + // redirect), we don't want to bury this information in a xml + // marshalling exception. + String movedTo = response.getHeaders().getFirst("Location"); + + String message; + if (!Strings.isNullOrEmpty(movedTo)) { + String baseUrl = getBaseURL(movedTo); + message = "Server returned a redirect to:" + baseUrl + + ". You must change your url option or config file."; + } else { + message = + "Server returned a redirect. You must change your url option or config file."; + } + throw new RuntimeException(message); + } else if (statusCode >= 399) { + String annotString = ""; + String uriString = ""; + String entity = ""; + try { + entity = ": " + response.getEntity(String.class); + } finally { + // ignore + } + String msg = + "operation returned " + + statusCode + + " (" + + Response.Status.fromStatusCode(statusCode) + ")" + + entity + uriString + + annotString; + throw new RuntimeException(msg); + } + } + + public static String getBaseURL(String movedTo) { + try { + URL url = new URI(movedTo).toURL(); + int pathIndex = movedTo.lastIndexOf(url.getPath()); + return movedTo.substring(0, pathIndex) + "/"; + } catch (MalformedURLException | URISyntaxException e) { + return movedTo; + } + } + + /** + * Extract filename from response header. + * + * e.g. Content-Disposition=[attachment; filename="RPM.po"] + * + * @param headers + * @return + */ + public static String getFileNameFromHeader(MultivaluedMap headers) { + final String CONTENT_DISPOSITION_HEADER = "Content-Disposition"; + final String FILENAME_PATTERN = "filename=\"(.*?)\"$"; + + String contentDisposition = headers.getFirst(CONTENT_DISPOSITION_HEADER); + if (StringUtils.isEmpty(contentDisposition)) { + return null; + } + Pattern p = Pattern.compile(FILENAME_PATTERN); + Matcher m = p.matcher(contentDisposition); + return m.find() ? m.group(1) : null; + } +} diff --git a/zanata-rest-client/src/main/java/org/zanata/rest/client/ClientUtility.java b/zanata-rest-client/src/main/java/org/zanata/rest/client/ClientUtility.java deleted file mode 100644 index b0d1a5c5..00000000 --- a/zanata-rest-client/src/main/java/org/zanata/rest/client/ClientUtility.java +++ /dev/null @@ -1,91 +0,0 @@ -package org.zanata.rest.client; - -import java.net.MalformedURLException; -import java.net.URI; -import java.net.URISyntaxException; -import java.net.URL; -import java.util.Arrays; - -import javax.ws.rs.core.Response; - -import org.jboss.resteasy.client.ClientResponse; -import org.jboss.resteasy.client.core.BaseClientResponse; -import com.google.common.base.Strings; - -public class ClientUtility { - public static void checkResult(ClientResponse response) { - checkResult(response, null); - } - - public static void checkResult(ClientResponse response, URI uri) { - Response.Status responseStatus = response.getResponseStatus(); - int statusCode = response.getStatus(); - - if (responseStatus == Response.Status.UNAUTHORIZED) { - throw new RuntimeException("Incorrect username/password"); - } else if (responseStatus == Response.Status.SERVICE_UNAVAILABLE) { - throw new RuntimeException("Service is currently unavailable. " + - "Please check outage notification or try again later."); - } else if (responseStatus == Response.Status.MOVED_PERMANENTLY - || statusCode == 302 ) { - // if server returns a redirect (most likely due to http to https - // redirect), we don't want to bury this information in a xml - // marshalling exception. - String movedTo = response.getHeaderString("Location"); - - String message; - if (!Strings.isNullOrEmpty(movedTo)) { - String baseUrl = getBaseURL(movedTo); - message = "Server returned a redirect to:" + baseUrl + - ". You must change your url option or config file."; - } else { - message = "Server returned a redirect. You must change your url option or config file."; - } - throw new RuntimeException(message); - } else if (statusCode >= 399) { - String annotString = ""; - String uriString = ""; - String entity = ""; - if (response instanceof BaseClientResponse) { - BaseClientResponse resp = (BaseClientResponse) response; - annotString = - ", annotations: " - + Arrays.asList(resp.getAnnotations()) - .toString(); - } - if (uri != null) { - uriString = ", uri: " + uri; - } - try { - entity = ": " + response.getEntity(String.class); - } finally { - // ignore - } - String msg = - "operation returned " - + statusCode - + " (" - + Response.Status.fromStatusCode(statusCode) + ")" - + entity + uriString - + annotString; - throw new RuntimeException(msg); - } - } - - public static String getBaseURL(String movedTo) { - try { - URL url = new URI(movedTo).toURL(); - int pathIndex = movedTo.lastIndexOf(url.getPath()); - return movedTo.substring(0, pathIndex) + "/"; - } - catch (MalformedURLException | URISyntaxException e) { - return movedTo; - } - } - - public static void checkResultAndReleaseConnection( - ClientResponse clientResponse) { - checkResult(clientResponse, null); - clientResponse.releaseConnection(); - } -} diff --git a/zanata-rest-client/src/main/java/org/zanata/rest/client/CopyTransClient.java b/zanata-rest-client/src/main/java/org/zanata/rest/client/CopyTransClient.java new file mode 100644 index 00000000..c356d179 --- /dev/null +++ b/zanata-rest-client/src/main/java/org/zanata/rest/client/CopyTransClient.java @@ -0,0 +1,72 @@ +/* + * Copyright 2014, Red Hat, Inc. and individual contributors + * as indicated by the @author tags. See the copyright.txt file in the + * distribution for a full listing of individual contributors. + * + * This is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this software; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA, or see the FSF site: http://www.fsf.org. + */ + +package org.zanata.rest.client; + +import java.net.URI; + +import org.zanata.rest.dto.CopyTransStatus; +import org.zanata.rest.service.CopyTransResource; +import com.sun.jersey.api.client.Client; +import com.sun.jersey.api.client.WebResource; + +/** + * @author Patrick Huang pahuang@redhat.com + */ +public class CopyTransClient implements CopyTransResource { + private final RestClientFactory factory; + private final URI baseUri; + + CopyTransClient(RestClientFactory factory) { + this.factory = factory; + baseUri = factory.getBaseUri(); + } + + @Override + public CopyTransStatus startCopyTrans(String projectSlug, + String iterationSlug, String docId) { + Client client = factory.getClient(); + CacheResponseFilter filter = new CacheResponseFilter(); + client.addFilter(filter); + webResource(client, projectSlug, iterationSlug, docId) + .post(); + client.removeFilter(filter); + return filter.getEntity(CopyTransStatus.class); + } + + private WebResource webResource(Client client, String projectSlug, + String iterationSlug, + String docId) { + return client.resource(baseUri) + .path(CopyTransResource.SERVICE_PATH) + .path("/proj").path(projectSlug) + .path("iter").path(iterationSlug) + .path("doc").path(docId); + } + + @Override + public CopyTransStatus getCopyTransStatus(String projectSlug, + String iterationSlug, String docId) { + return webResource(factory.getClient(), projectSlug, iterationSlug, docId + ).get(CopyTransStatus.class); + } +} diff --git a/zanata-rest-client/src/main/java/org/zanata/rest/client/FileResourceClient.java b/zanata-rest-client/src/main/java/org/zanata/rest/client/FileResourceClient.java new file mode 100644 index 00000000..4ffb7ab3 --- /dev/null +++ b/zanata-rest-client/src/main/java/org/zanata/rest/client/FileResourceClient.java @@ -0,0 +1,163 @@ +/* + * Copyright 2014, Red Hat, Inc. and individual contributors + * as indicated by the @author tags. See the copyright.txt file in the + * distribution for a full listing of individual contributors. + * + * This is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this software; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA, or see the FSF site: http://www.fsf.org. + */ + +package org.zanata.rest.client; + +import java.net.URI; +import java.util.Arrays; +import java.util.List; +import java.util.Map; + +import javax.ws.rs.core.MediaType; + +import com.sun.jersey.api.client.GenericType; +import org.zanata.common.DocumentType; +import org.zanata.rest.DocumentFileUploadForm; +import org.zanata.rest.StringSet; +import org.zanata.rest.dto.ChunkUploadResponse; +import org.zanata.rest.service.FileResource; + +import com.sun.jersey.api.client.Client; +import com.sun.jersey.api.client.ClientResponse; +import com.sun.jersey.api.client.WebResource; +import com.sun.jersey.multipart.FormDataMultiPart; + +/** + * @author Patrick Huang pahuang@redhat.com + */ +public class FileResourceClient { + private final RestClientFactory factory; + + private final URI baseUri; + + FileResourceClient(RestClientFactory restClientFactory) { + this.factory = restClientFactory; + baseUri = restClientFactory.getBaseUri(); + + } + + public List acceptedFileTypes() { + List types = factory.getClient() + .resource(baseUri) + .path(FileResource.SERVICE_PATH + + FileResource.ACCEPTED_TYPE_LIST_RESOURCE) + .get(new GenericType>() { + }); + return types; + } + + public ChunkUploadResponse uploadSourceFile( + String projectSlug, + String iterationSlug, String docId, + DocumentFileUploadForm documentFileUploadForm) { + CacheResponseFilter filter = new CacheResponseFilter(); + Client client = factory.getClient(); + client.addFilter(filter); + WebResource.Builder builder = client + .resource(baseUri) + .path("file").path("source").path(projectSlug) + .path(iterationSlug) + .queryParam("docId", docId) + .type(MediaType.MULTIPART_FORM_DATA_TYPE); + FormDataMultiPart form = + prepareFormDataMultiPart(documentFileUploadForm); + + builder.post(form); + ChunkUploadResponse chunkUploadResponse = + filter.getEntity(ChunkUploadResponse.class); + client.removeFilter(filter); + return chunkUploadResponse; + } + + private FormDataMultiPart prepareFormDataMultiPart( + DocumentFileUploadForm documentFileUploadForm) { + FormDataMultiPart form = + new FormDataMultiPart() + .field("file", documentFileUploadForm + .getFileStream(), + MediaType.APPLICATION_OCTET_STREAM_TYPE); + addBodyPartIfPresent(form, "adapterParams", + documentFileUploadForm.getAdapterParams()); + addBodyPartIfPresent(form, "type", documentFileUploadForm.getFileType()); + addBodyPartIfPresent(form, "first", documentFileUploadForm.getFirst()); + addBodyPartIfPresent(form, "hash", documentFileUploadForm.getHash()); + addBodyPartIfPresent(form, "last", documentFileUploadForm.getLast()); + addBodyPartIfPresent(form, "size", documentFileUploadForm.getSize()); + addBodyPartIfPresent(form, "uploadId", + documentFileUploadForm.getUploadId()); + return form; + } + + public ChunkUploadResponse uploadTranslationFile( + String projectSlug, + String iterationSlug, String locale, String docId, + String mergeType, + DocumentFileUploadForm documentFileUploadForm) { + CacheResponseFilter filter = new CacheResponseFilter(); + Client client = factory.getClient(); + client.addFilter(filter); + WebResource.Builder builder = client.resource(baseUri) + .path(FileResource.SERVICE_PATH) + .path("translation") + .path(projectSlug) + .path(iterationSlug) + .path(locale) + .queryParam("docId", docId) + .queryParam("merge", mergeType) + .type(MediaType.MULTIPART_FORM_DATA_TYPE); + FormDataMultiPart form = + prepareFormDataMultiPart(documentFileUploadForm); + + builder.post(form); + ChunkUploadResponse chunkUploadResponse = + filter.getEntity(ChunkUploadResponse.class); + client.removeFilter(filter); + return chunkUploadResponse; + } + + public ClientResponse downloadSourceFile(String projectSlug, + String iterationSlug, + String fileType, String docId) { + WebResource webResource = factory.getClient().resource(baseUri) + .path(FileResource.SERVICE_PATH).path("source") + .path(projectSlug).path(iterationSlug).path(fileType); + return webResource.queryParam("docId", docId).get(ClientResponse.class); + } + + public ClientResponse downloadTranslationFile(String projectSlug, + String iterationSlug, String locale, String fileExtension, + String docId) { + WebResource webResource = factory.getClient().resource(baseUri) + .path(FileResource.SERVICE_PATH).path("translation") + .path(projectSlug).path(iterationSlug).path(locale) + .path(fileExtension); + return webResource.queryParam("docId", docId).get(ClientResponse.class); + } + + private static FormDataMultiPart addBodyPartIfPresent( + FormDataMultiPart form, String field, T value) { + if (value != null) { + return form.field(field, value.toString()); + } + return form; + } +} diff --git a/zanata-rest-client/src/main/java/org/zanata/rest/client/GlossaryClient.java b/zanata-rest-client/src/main/java/org/zanata/rest/client/GlossaryClient.java new file mode 100644 index 00000000..93233325 --- /dev/null +++ b/zanata-rest-client/src/main/java/org/zanata/rest/client/GlossaryClient.java @@ -0,0 +1,62 @@ +/* + * Copyright 2014, Red Hat, Inc. and individual contributors + * as indicated by the @author tags. See the copyright.txt file in the + * distribution for a full listing of individual contributors. + * + * This is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this software; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA, or see the FSF site: http://www.fsf.org. + */ + +package org.zanata.rest.client; + +import java.net.URI; + +import org.zanata.common.LocaleId; +import org.zanata.rest.dto.Glossary; +import org.zanata.rest.service.GlossaryResource; +import com.sun.jersey.api.client.WebResource; + +/** + * @author Patrick Huang pahuang@redhat.com + */ +public class GlossaryClient { + private final RestClientFactory factory; + private final URI baseUri; + + GlossaryClient(RestClientFactory factory) { + this.factory = factory; + baseUri = factory.getBaseUri(); + } + + public void put(Glossary glossary) { + webResource().put(glossary); + } + + public void delete(LocaleId locale) { + webResource().path(locale.getId()) + .delete(); + + } + + public void deleteAll() { + webResource().delete(); + } + + private WebResource webResource() { + return factory.getClient().resource(baseUri) + .path(GlossaryResource.SERVICE_PATH); + } +} diff --git a/zanata-rest-client/src/main/java/org/zanata/rest/client/ITranslationResourcesFactory.java b/zanata-rest-client/src/main/java/org/zanata/rest/client/ITranslationResourcesFactory.java deleted file mode 100644 index bfa6617e..00000000 --- a/zanata-rest-client/src/main/java/org/zanata/rest/client/ITranslationResourcesFactory.java +++ /dev/null @@ -1,10 +0,0 @@ -package org.zanata.rest.client; - -import java.net.URI; - -public interface ITranslationResourcesFactory { - ITranslatedDocResource getTranslatedDocResource(String projectSlug, - String versionSlug); - - URI getResourceURI(String projectSlug, String versionSlug); -} diff --git a/zanata-rest-client/src/main/java/org/zanata/rest/client/ProjectClient.java b/zanata-rest-client/src/main/java/org/zanata/rest/client/ProjectClient.java new file mode 100644 index 00000000..d1ae8e44 --- /dev/null +++ b/zanata-rest-client/src/main/java/org/zanata/rest/client/ProjectClient.java @@ -0,0 +1,56 @@ +/* + * Copyright 2014, Red Hat, Inc. and individual contributors + * as indicated by the @author tags. See the copyright.txt file in the + * distribution for a full listing of individual contributors. + * + * This is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this software; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA, or see the FSF site: http://www.fsf.org. + */ + +package org.zanata.rest.client; + +import org.zanata.rest.dto.Project; + +import com.sun.jersey.api.client.WebResource; + +/** + * @author Patrick Huang pahuang@redhat.com + */ +public class ProjectClient { + private final RestClientFactory factory; + private final String projectSlug; + + ProjectClient(RestClientFactory factory, String projectSlug) { + this.factory = factory; + this.projectSlug = projectSlug; + } + + public Project get() { + return webResource() + .get(Project.class); + } + + private WebResource webResource() { + return factory.getClient() + .resource(factory.getBaseUri()) + .path("projects").path("p").path(projectSlug); + } + + public void put(Project project) { + webResource().put(project); + } +} + diff --git a/zanata-rest-client/src/main/java/org/zanata/rest/client/ProjectIterationClient.java b/zanata-rest-client/src/main/java/org/zanata/rest/client/ProjectIterationClient.java new file mode 100644 index 00000000..f8c4eff5 --- /dev/null +++ b/zanata-rest-client/src/main/java/org/zanata/rest/client/ProjectIterationClient.java @@ -0,0 +1,66 @@ +/* + * Copyright 2014, Red Hat, Inc. and individual contributors + * as indicated by the @author tags. See the copyright.txt file in the + * distribution for a full listing of individual contributors. + * + * This is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this software; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA, or see the FSF site: http://www.fsf.org. + */ + +package org.zanata.rest.client; + +import java.net.URI; + +import org.zanata.rest.dto.ProjectIteration; + +import com.sun.jersey.api.client.WebResource; + +/** + * @author Patrick Huang + * pahuang@redhat.com + */ +public class ProjectIterationClient { + private final RestClientFactory factory; + private final String projectSlug; + private final String versionSlug; + private URI baseUri; + + ProjectIterationClient(RestClientFactory factory, String projectSlug, String versionSlug) { + this.factory = factory; + this.projectSlug = projectSlug; + this.versionSlug = versionSlug; + baseUri = factory.getBaseUri(); + } + + public ProjectIteration get() { + return webResource() + .get(ProjectIteration.class); + } + + private WebResource webResource() { + return factory.getClient().resource(baseUri) + .path("projects").path("p").path(projectSlug) + .path("iterations").path("i").path(versionSlug); + } + + public void put(ProjectIteration projectVersion) { + webResource().put(projectVersion); + } + + public String sampleConfiguration() { + return webResource().path("config").get(String.class); + } +} + diff --git a/zanata-rest-client/src/main/java/org/zanata/rest/client/ProjectIterationLocalesClient.java b/zanata-rest-client/src/main/java/org/zanata/rest/client/ProjectIterationLocalesClient.java new file mode 100644 index 00000000..dba4d105 --- /dev/null +++ b/zanata-rest-client/src/main/java/org/zanata/rest/client/ProjectIterationLocalesClient.java @@ -0,0 +1,57 @@ +/* + * Copyright 2014, Red Hat, Inc. and individual contributors + * as indicated by the @author tags. See the copyright.txt file in the + * distribution for a full listing of individual contributors. + * + * This is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this software; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA, or see the FSF site: http://www.fsf.org. + */ + +package org.zanata.rest.client; + +import java.util.List; + +import org.zanata.rest.dto.LocaleDetails; + +import com.sun.jersey.api.client.GenericType; + +/** + * REST client for project iteration locales. + * + * @author Patrick Huang pahuang@redhat.com + */ +public class ProjectIterationLocalesClient { + private final RestClientFactory restClientFactory; + private final String projectSlug; + private final String versionSlug; + + public ProjectIterationLocalesClient(RestClientFactory restClientFactory, + String projectSlug, String versionSlug) { + this.restClientFactory = restClientFactory; + this.projectSlug = projectSlug; + this.versionSlug = versionSlug; + } + + public List getLocales() { + return restClientFactory.getClient() + .resource(restClientFactory.getBaseUri()) + .path("projects").path("p").path(projectSlug) + .path("iterations").path("i").path(versionSlug) + .path("locales") + .get(new GenericType>() { + }); + } +} diff --git a/zanata-rest-client/src/main/java/org/zanata/rest/client/ProjectsClient.java b/zanata-rest-client/src/main/java/org/zanata/rest/client/ProjectsClient.java new file mode 100644 index 00000000..24eca02b --- /dev/null +++ b/zanata-rest-client/src/main/java/org/zanata/rest/client/ProjectsClient.java @@ -0,0 +1,45 @@ +/* + * Copyright 2014, Red Hat, Inc. and individual contributors + * as indicated by the @author tags. See the copyright.txt file in the + * distribution for a full listing of individual contributors. + * + * This is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this software; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA, or see the FSF site: http://www.fsf.org. + */ + +package org.zanata.rest.client; + +import org.zanata.rest.dto.Project; +import org.zanata.rest.service.ProjectsResource; +import com.sun.jersey.api.client.GenericType; + +/** + * @author Patrick Huang pahuang@redhat.com + */ +public class ProjectsClient { + private final RestClientFactory factory; + + ProjectsClient(RestClientFactory factory) { + this.factory = factory; + } + + public Project[] getProjects() { + return factory.getClient().resource(factory.getBaseUri()) + .path(ProjectsResource.SERVICE_PATH) + .get(new GenericType() { + }); + } +} diff --git a/zanata-rest-client/src/main/java/org/zanata/rest/client/RestClientFactory.java b/zanata-rest-client/src/main/java/org/zanata/rest/client/RestClientFactory.java new file mode 100644 index 00000000..faac5150 --- /dev/null +++ b/zanata-rest-client/src/main/java/org/zanata/rest/client/RestClientFactory.java @@ -0,0 +1,280 @@ +/* + * Copyright 2014, Red Hat, Inc. and individual contributors + * as indicated by the @author tags. See the copyright.txt file in the + * distribution for a full listing of individual contributors. + * + * This is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this software; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA, or see the FSF site: http://www.fsf.org. + */ + +package org.zanata.rest.client; + +import java.net.MalformedURLException; +import java.net.URI; +import java.net.URISyntaxException; +import java.net.URL; +import java.security.SecureRandom; +import java.security.cert.CertificateException; +import java.security.cert.X509Certificate; + +import javax.net.ssl.HostnameVerifier; +import javax.net.ssl.HttpsURLConnection; +import javax.net.ssl.SSLContext; +import javax.net.ssl.SSLSession; +import javax.net.ssl.TrustManager; +import javax.net.ssl.X509TrustManager; + +import org.apache.maven.artifact.versioning.DefaultArtifactVersion; +import org.codehaus.jackson.jaxrs.JacksonJsonProvider; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.zanata.rest.RestConstant; +import org.zanata.rest.dto.VersionInfo; + +import com.google.common.base.Throwables; +import com.sun.jersey.api.client.Client; +import com.sun.jersey.api.client.config.ClientConfig; +import com.sun.jersey.api.client.config.DefaultClientConfig; +import com.sun.jersey.client.urlconnection.HTTPSProperties; +import com.sun.jersey.multipart.impl.MultiPartWriter; + +/** + * @author Patrick Huang pahuang@redhat.com + */ +public class RestClientFactory { + private static final Logger log = + LoggerFactory.getLogger(RestClientFactory.class); + private String serverVersion; + private String clientVersion; + private VersionInfo clientApiVersion; + + private Client client; + private URI baseURI; + + // for use by InitCommand + protected RestClientFactory() { + } + + public RestClientFactory(URI base, String username, String apiKey, + VersionInfo clientApiVersion, boolean logHttp, + boolean sslCertDisabled) { + baseURI = base; + this.clientApiVersion = clientApiVersion; + clientVersion = clientApiVersion.getVersionNo(); + DefaultClientConfig clientConfig = + new DefaultClientConfig(MultiPartWriter.class); + + sslConfiguration(sslCertDisabled, clientConfig); + clientConfig.getClasses().add(JacksonJsonProvider.class); + + client = Client.create(clientConfig); + client.addFilter( + new ApiKeyHeaderFilter(username, apiKey, clientVersion)); + client.addFilter(new AcceptTypeFilter()); + client.addFilter(new TraceDebugFilter(logHttp)); + } + + private static void sslConfiguration(boolean sslCertDisabled, + ClientConfig clientConfig) { + if (!sslCertDisabled) { + return; + } + try { + final SSLContext sslContext = SSLContext.getInstance("TLS"); + + // Create a trust manager that does not validate certificate chains + // against our server + final TrustManager[] trustAllCerts; + trustAllCerts = + new TrustManager[] { new AcceptAllX509TrustManager() }; + sslContext.init(null, trustAllCerts, new SecureRandom()); + HttpsURLConnection + .setDefaultSSLSocketFactory(sslContext + .getSocketFactory()); + clientConfig.getProperties().put( + HTTPSProperties.PROPERTY_HTTPS_PROPERTIES, + new HTTPSProperties( + new HostnameVerifier() { + @Override + public boolean verify(String s, + SSLSession sslSession) { + // whatever your matching policy states + return true; + } + }, sslContext + )); + } catch (Exception e) { + log.warn("error creating SSL client", e); + Throwables.propagate(e); + } + } + + public VersionInfo getServerVersionInfo() { + return client.resource(getBaseUri()).path("version") + .get(VersionInfo.class); + } + + public void performVersionCheck() { + clientVersion = clientApiVersion.getVersionNo(); + String clientScm = clientApiVersion.getScmDescribe(); + + VersionInfo serverVersionInfo = getServerVersionInfo(); + serverVersion = serverVersionInfo.getVersionNo(); + String serverScm = serverVersionInfo.getScmDescribe(); + log.info("client API version: {}, server API version: {}", + clientVersion, serverVersion); + warnMismatchAPIVersion(clientScm, serverScm); + } + + private void warnMismatchAPIVersion(String clientScm, String serverScm) { + if (!serverVersion.equals(clientVersion)) { + log.warn("client API version is {}, but server API version is {}", + clientVersion, serverVersion); + } else if (serverVersion.contains(RestConstant.SNAPSHOT_VERSION) + && !serverScm.equalsIgnoreCase(clientScm)) { + log.warn( + "client API SCM id is {}, but server API SCM id is {}", + clientScm, serverScm); + } + } + + private URL getBaseUrl() { + try { + return new URL(fixBase(baseURI).toString() + getUrlPrefix()); + } catch (MalformedURLException e) { + throw new RuntimeException(e); + } + } + + protected URI getBaseUri() { + try { + return getBaseUrl().toURI(); + } catch (URISyntaxException e) { + throw new RuntimeException(e); + } + } + + private static URI fixBase(URI base) { + if (base != null) { + String baseString = base.toString(); + if (!baseString.endsWith("/")) { + try { + URI result = new URI(baseString + "/"); + log.warn("Appending '/' to base URL '{}': using '{}'", + baseString, result); + return result; + } catch (URISyntaxException e) { + throw new RuntimeException(e); + } + } + } + return base; + } + + protected String getUrlPrefix() { + return "rest/"; + } + + protected Client getClient() { + return client; + } + + /** + * Compares a given version identifier with the server version. + * + * @param version + * The version to against which to compare the server version. + * @return A positive integer if the server version is greater than the + * given version. A negative integer if the server version is less + * than the given version. 0 if both versions are the same. + */ + public int compareToServerVersion(String version) { + DefaultArtifactVersion srvVersion = + new DefaultArtifactVersion(serverVersion); + DefaultArtifactVersion providedVersion = + new DefaultArtifactVersion(version); + + return srvVersion.compareTo(providedVersion); + } + + public AccountClient getAccountClient() { + return new AccountClient(this); + } + + public AsyncProcessClient getAsyncProcessClient() { + return new AsyncProcessClient(this); + } + + public CopyTransClient getCopyTransClient() { + return new CopyTransClient(this); + } + + public FileResourceClient getFileResourceClient() { + return new FileResourceClient(this); + } + + public GlossaryClient getGlossaryClient() { + return new GlossaryClient(this); + } + + public ProjectClient getProjectClient(String projectSlug) { + return new ProjectClient(this, projectSlug); + } + + public ProjectIterationClient getProjectIterationClient(String projectSlug, + String versionSlug) { + return new ProjectIterationClient(this, projectSlug, versionSlug); + } + + public ProjectsClient getProjectsClient() { + return new ProjectsClient(this); + } + + public SourceDocResourceClient getSourceDocResourceClient( + String projectSlug, String versionSlug) { + return new SourceDocResourceClient(this, projectSlug, versionSlug); + } + + public StatisticsResourceClient getStatisticsClient() { + return new StatisticsResourceClient(this); + } + + public TransDocResourceClient getTransDocResourceClient(String projectSlug, + String versionSlug) { + return new TransDocResourceClient(this, projectSlug, versionSlug); + } + + public ProjectIterationLocalesClient getProjectLocalesClient( + String projectSlug, String versionSlug) { + return new ProjectIterationLocalesClient(this, projectSlug, versionSlug); + } + + private static class AcceptAllX509TrustManager implements X509TrustManager { + public X509Certificate[] getAcceptedIssuers() { + return null; + } + + public void + checkClientTrusted(X509Certificate[] certs, String authType) + throws CertificateException { + } + + public void + checkServerTrusted(X509Certificate[] certs, String authType) + throws CertificateException { + } + } +} diff --git a/zanata-rest-client/src/main/java/org/zanata/rest/client/SourceDocResourceClient.java b/zanata-rest-client/src/main/java/org/zanata/rest/client/SourceDocResourceClient.java new file mode 100644 index 00000000..e6553fb9 --- /dev/null +++ b/zanata-rest-client/src/main/java/org/zanata/rest/client/SourceDocResourceClient.java @@ -0,0 +1,110 @@ +/* + * Copyright 2014, Red Hat, Inc. and individual contributors + * as indicated by the @author tags. See the copyright.txt file in the + * distribution for a full listing of individual contributors. + * + * This is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this software; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA, or see the FSF site: http://www.fsf.org. + */ + +package org.zanata.rest.client; + +import java.net.URI; +import java.util.List; +import java.util.Set; + +import org.zanata.rest.dto.resource.Resource; +import org.zanata.rest.dto.resource.ResourceMeta; +import com.sun.jersey.api.client.Client; +import com.sun.jersey.api.client.GenericType; + +import com.sun.jersey.api.client.WebResource; + +/** + * This "implements" caller methods to endpoints in SourceDocResource. + * + * N.B.(as of 11/11/2014 commit 8dbf5ec) post is not used. putResource(with + * copyTrans) is only used by PublicanPushCommand. putResource is not used. + * getResourceMeta is not used. putResourceMeta is not used. + * + * @author Patrick Huang pahuang@redhat.com + */ +public class SourceDocResourceClient { + private final RestClientFactory factory; + private final String project; + private final String projectVersion; + private final URI baseUri; + + SourceDocResourceClient(RestClientFactory factory, String project, + String projectVersion) { + this.factory = factory; + this.project = project; + this.projectVersion = projectVersion; + baseUri = factory.getBaseUri(); + } + + public List getResourceMeta(Set extensions) { + Client client = factory.getClient(); + WebResource webResource = getBaseServiceResource(client) + .queryParams(ClientUtil.asMultivaluedMap( + "ext", extensions)); + return webResource + .get(new GenericType>() { + }); + } + + private WebResource getBaseServiceResource(Client client) { + return client.resource(baseUri) + .path("projects").path("p") + .path(project) + .path("iterations").path("i") + .path(projectVersion) + .path("r"); + } + + public Resource getResource(String idNoSlash, Set extensions) { + Client client = factory.getClient(); + WebResource webResource = + getBaseServiceResource(client) + .path(idNoSlash) + .queryParams(ClientUtil.asMultivaluedMap( + "ext", extensions)); + return webResource.get(Resource.class); + } + + public String putResource(String idNoSlash, Resource resource, + Set extensions, boolean copyTrans) { + Client client = factory.getClient(); + CacheResponseFilter filter = new CacheResponseFilter(); + client.addFilter(filter); + WebResource webResource = getBaseServiceResource(client) + .path(idNoSlash) + .queryParams(ClientUtil.asMultivaluedMap( + "ext", extensions)) + .queryParam("copyTrans", String.valueOf(copyTrans)); + + webResource.put(resource); + client.removeFilter(filter); + return filter.getEntity(String.class); + } + + public String deleteResource(String idNoSlash) { + Client client = factory.getClient(); + WebResource webResource = getBaseServiceResource(client); + return webResource.path(idNoSlash).delete(String.class); + } + +} diff --git a/zanata-rest-client/src/main/java/org/zanata/rest/client/StatisticsResourceClient.java b/zanata-rest-client/src/main/java/org/zanata/rest/client/StatisticsResourceClient.java new file mode 100644 index 00000000..19db011d --- /dev/null +++ b/zanata-rest-client/src/main/java/org/zanata/rest/client/StatisticsResourceClient.java @@ -0,0 +1,110 @@ +/* + * Copyright 2014, Red Hat, Inc. and individual contributors + * as indicated by the @author tags. See the copyright.txt file in the + * distribution for a full listing of individual contributors. + * + * This is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this software; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA, or see the FSF site: http://www.fsf.org. + */ + +package org.zanata.rest.client; + +import java.net.URI; +import java.util.ArrayList; +import java.util.List; +import javax.ws.rs.DefaultValue; + +import org.zanata.rest.dto.stats.ContainerTranslationStatistics; +import org.zanata.rest.dto.stats.contribution.ContributionStatistics; +import org.zanata.rest.service.StatisticsResource; +import com.google.common.collect.Lists; +import com.sun.jersey.api.client.WebResource; + +import static org.zanata.rest.client.ClientUtil.asMultivaluedMap; + +/** + * @author Patrick Huang pahuang@redhat.com + */ +public class StatisticsResourceClient implements StatisticsResource { + private final RestClientFactory factory; + private final URI baseUri; + + StatisticsResourceClient(RestClientFactory factory) { + this.factory = factory; + baseUri = factory.getBaseUri(); + } + + @Override + public ContainerTranslationStatistics getStatistics(String projectSlug, + String iterationSlug, + @DefaultValue("false") boolean includeDetails, + @DefaultValue("false") boolean includeWordStats, String[] locales) { + WebResource webResource = + factory.getClient().resource(baseUri).path("stats") + .path("proj") + .path(projectSlug) + .path("iter") + .path(iterationSlug) + .queryParam("detail", String.valueOf(includeDetails)) + .queryParam("word", String.valueOf(includeWordStats)) + .queryParams(asMultivaluedMap("locale", + toLocaleList(locales))); + return webResource.get(ContainerTranslationStatistics.class); + } + + private static List toLocaleList(String[] locales) { + List localesList; + if (locales == null) { + localesList = Lists.newArrayList(); + } else { + localesList = Lists.newArrayList(locales); + } + return localesList; + } + + @Override + public ContainerTranslationStatistics getStatistics(String projectSlug, + String iterationSlug, String docId, + @DefaultValue("false") boolean includeWordStats, String[] locales) { + WebResource webResource = + factory.getClient().resource(baseUri).path("stats") + .path("proj") + .path(projectSlug) + .path("iter") + .path(iterationSlug) + .path("doc") + .path(docId) + .queryParam("word", String.valueOf(includeWordStats)) + .queryParams(asMultivaluedMap("locale", + toLocaleList(locales))); + return webResource.get(ContainerTranslationStatistics.class); + } + + @Override + public ContributionStatistics getContributionStatistics(String projectSlug, + String versionSlug, String username, String dateRange) { + WebResource webResource = + factory.getClient().resource(baseUri).path("stats") + .path("project") + .path(projectSlug) + .path("version") + .path(versionSlug) + .path("contributor") + .path(username) + .path(dateRange); + return webResource.get(ContributionStatistics.class); + } +} diff --git a/zanata-rest-client/src/main/java/org/zanata/rest/client/TraceDebugFilter.java b/zanata-rest-client/src/main/java/org/zanata/rest/client/TraceDebugFilter.java new file mode 100644 index 00000000..3ea3e3f0 --- /dev/null +++ b/zanata-rest-client/src/main/java/org/zanata/rest/client/TraceDebugFilter.java @@ -0,0 +1,143 @@ +/* + * Copyright 2014, Red Hat, Inc. and individual contributors + * as indicated by the @author tags. See the copyright.txt file in the + * distribution for a full listing of individual contributors. + * + * This is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this software; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA, or see the FSF site: http://www.fsf.org. + */ +package org.zanata.rest.client; + +import java.io.ByteArrayInputStream; +import java.util.ArrayList; +import java.util.List; + +import javax.ws.rs.ext.Provider; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.zanata.rest.RestConstant; + +import com.google.common.base.Charsets; +import com.sun.jersey.api.client.ClientHandlerException; +import com.sun.jersey.api.client.ClientRequest; +import com.sun.jersey.api.client.ClientResponse; +import com.sun.jersey.api.client.filter.ClientFilter; + +/** + * Performs logging of Requests on the client side. This interceptor + * logs at the level TRACE, unless the option logHttp is set, in which case it + * will log as INFO. + * + * @author Patrick Huang + * pahuang@redhat.com + * + */ +@Provider +public class TraceDebugFilter extends ClientFilter { + + private static final Logger log = LoggerFactory + .getLogger(TraceDebugFilter.class); + + private boolean logHttp; + + @Override + public ClientResponse handle(ClientRequest cr) + throws ClientHandlerException { + if (!logHttp && !log.isTraceEnabled()) { + return getNext().handle(cr); + } + log(">> REST Request: " + cr.getMethod() + " => " + + cr.getURI()); + + // Log before sending a request + for (String key : cr.getHeaders().keySet()) { + String headerVal = + cr.getHeaders().get(key).toString(); + if (key.equals(RestConstant.HEADER_API_KEY)) { + headerVal = + this.maskHeaderValues( + cr.getHeaders() + .get(key)); + } + + log(">> Header: " + key + " = " + headerVal); + } + log(">> body: " + cr.getEntity()); + + + + ClientResponse response = getNext().handle(cr); + + // log after a response has been received + log("<< REST Response: " + response.getStatus() + + ":" + response.getClientResponseStatus()); + for (String key : response.getHeaders().keySet()) { + log("<< Header: " + key + " = " + + response.getHeaders().get(key)); + } + response.bufferEntity(); + log(">> Body: " + getPayloadAsString(response)); + return response; + } + + // this is jersey implementation specific + private String getPayloadAsString(ClientResponse response) { + ByteArrayInputStream entityInputStream = null; + try { + entityInputStream = + (ByteArrayInputStream) response.getEntityInputStream(); + int available = entityInputStream.available(); + byte[] data = new byte[available]; + entityInputStream.read(data); + return new String(data, 0, available, Charsets.UTF_8); + } catch (Exception e) { + log.warn("can't read response payload"); + return "[error reading response]"; + } finally { + if (entityInputStream != null) { + entityInputStream.reset(); + } + } + + } + + public TraceDebugFilter(boolean logHttp) { + this.logHttp = logHttp; + } + + private void log(String msg) { + if (logHttp) { + log.info(msg); + } else { + log.trace(msg); + } + } + + /** + * Masks a list of header values so they are not displayed as clear text in + * the logs. + */ + private String maskHeaderValues(List headerValues) { + List maskedList = new ArrayList(headerValues.size()); + + for (Object actualValue : headerValues) { + // mask all characters with stars + maskedList.add(actualValue.toString().replaceAll(".", "*")); + } + + return maskedList.toString(); + } +} diff --git a/zanata-rest-client/src/main/java/org/zanata/rest/client/TraceDebugInterceptor.java b/zanata-rest-client/src/main/java/org/zanata/rest/client/TraceDebugInterceptor.java deleted file mode 100644 index ca9e00bf..00000000 --- a/zanata-rest-client/src/main/java/org/zanata/rest/client/TraceDebugInterceptor.java +++ /dev/null @@ -1,122 +0,0 @@ -/* - * Copyright 2010, Red Hat, Inc. and individual contributors - * as indicated by the @author tags. See the copyright.txt file in the - * distribution for a full listing of individual contributors. - * - * This is free software; you can redistribute it and/or modify it - * under the terms of the GNU Lesser General Public License as - * published by the Free Software Foundation; either version 2.1 of - * the License, or (at your option) any later version. - * - * This software is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this software; if not, write to the Free - * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA - * 02110-1301 USA, or see the FSF site: http://www.fsf.org. - */ -package org.zanata.rest.client; - -import java.util.ArrayList; -import java.util.List; - -import javax.ws.rs.ext.Provider; - -import org.jboss.resteasy.annotations.interception.ClientInterceptor; -import org.jboss.resteasy.client.ClientResponse; -import org.jboss.resteasy.spi.interception.ClientExecutionContext; -import org.jboss.resteasy.spi.interception.ClientExecutionInterceptor; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.zanata.rest.RestConstant; - -/** - * Performs logging of Resteasy Requests on the client side. This interceptor - * logs at the level TRACE, unless the option logHttp is set, in which case it - * will log as INFO. - * - * @author Carlos Munoz camunoz@redhat.com - * - */ -@Provider -@ClientInterceptor -public class TraceDebugInterceptor implements ClientExecutionInterceptor { - - private static final Logger log = LoggerFactory - .getLogger(TraceDebugInterceptor.class); - - private boolean logHttp; - - public TraceDebugInterceptor() { - this(true); - } - - public TraceDebugInterceptor(boolean logHttp) { - this.logHttp = logHttp; - } - - private void log(String msg) { - if (logHttp) { - log.info(msg); - } else { - log.trace(msg); - } - } - - @SuppressWarnings("rawtypes") - @Override - public ClientResponse execute(ClientExecutionContext ctx) throws Exception { - if (!logHttp && !log.isTraceEnabled()) { - return ctx.proceed(); - } - - log(">> REST Request: " + ctx.getRequest().getHttpMethod() + " => " - + ctx.getRequest().getUri()); - - // Log before sending a request - for (String key : ctx.getRequest().getHeaders().keySet()) { - String headerVal = - ctx.getRequest().getHeaders().get(key).toString(); - if (key.equals(RestConstant.HEADER_API_KEY)) { - headerVal = - this.maskHeaderValues(ctx.getRequest().getHeaders() - .get(key)); - } - - log(">> Header: " + key + " = " + headerVal); - } - log(">> Body: " + ctx.getRequest().getBody()); - - ClientResponse result = ctx.proceed(); - - // log after a response has been received - log("<< REST Response: " + result.getResponseStatus().getStatusCode() - + ":" + result.getResponseStatus()); - for (String key : result.getHeaders().keySet()) { - log("<< Header: " + key + " = " + result.getHeaders().get(key)); - } - - return result; - } - - /** - * Masks a list of header values so they are not displayed as clear text in - * the logs. - */ - private String maskHeaderValues(List headerValues) { - List maskedList = new ArrayList(headerValues.size()); - - for (String actualValue : headerValues) { - maskedList.add(actualValue.replaceAll(".", "*")); // mask all - // characters with - // stars - } - - return maskedList.toString(); - } - -} diff --git a/zanata-rest-client/src/main/java/org/zanata/rest/client/TransDocResourceClient.java b/zanata-rest-client/src/main/java/org/zanata/rest/client/TransDocResourceClient.java new file mode 100644 index 00000000..dd9fe2f2 --- /dev/null +++ b/zanata-rest-client/src/main/java/org/zanata/rest/client/TransDocResourceClient.java @@ -0,0 +1,84 @@ +/* + * Copyright 2014, Red Hat, Inc. and individual contributors + * as indicated by the @author tags. See the copyright.txt file in the + * distribution for a full listing of individual contributors. + * + * This is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this software; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA, or see the FSF site: http://www.fsf.org. + */ + +package org.zanata.rest.client; + +import java.net.URI; +import java.util.Set; +import javax.ws.rs.HeaderParam; +import javax.ws.rs.PathParam; +import javax.ws.rs.QueryParam; +import javax.ws.rs.core.HttpHeaders; + +import org.zanata.common.LocaleId; +import org.zanata.rest.dto.resource.TranslationsResource; +import com.sun.jersey.api.client.Client; +import com.sun.jersey.api.client.ClientResponse; +import com.sun.jersey.api.client.WebResource; + +import static org.zanata.rest.client.ClientUtil.asMultivaluedMap; + +/** + * This "implements" caller methods to endpoints in TranslatedDocResource. + * + * @author Patrick Huang pahuang@redhat.com + */ +public class TransDocResourceClient { + private final RestClientFactory factory; + private final String project; + private final String projectVersion; + private final URI baseUri; + + TransDocResourceClient(RestClientFactory factory, String project, + String projectVersion) { + this.factory = factory; + this.project = project; + this.projectVersion = projectVersion; + baseUri = factory.getBaseUri(); + } + + public ClientResponse getTranslations( + @PathParam("id") String idNoSlash, + @PathParam("locale") LocaleId locale, + @QueryParam("ext") Set extensions, + @QueryParam("skeletons") boolean createSkeletons, + @HeaderParam(HttpHeaders.IF_NONE_MATCH) String eTag) { + Client client = factory.getClient(); + return getBaseServiceResource(client) + .path(idNoSlash) + .path("translations").path(locale.getId()) + .queryParams(asMultivaluedMap("ext", extensions)) + .queryParam("skeletons", String.valueOf(createSkeletons)) + .header(HttpHeaders.IF_NONE_MATCH, eTag) + .get(ClientResponse.class); + } + + private WebResource getBaseServiceResource(Client client) { + return client.resource(baseUri) + .path("projects").path("p") + .path(project) + .path("iterations").path("i") + .path(projectVersion) + .path("r"); + } + +} diff --git a/zanata-rest-client/src/main/java/org/zanata/rest/client/ZanataProxyFactory.java b/zanata-rest-client/src/main/java/org/zanata/rest/client/ZanataProxyFactory.java deleted file mode 100644 index e0925347..00000000 --- a/zanata-rest-client/src/main/java/org/zanata/rest/client/ZanataProxyFactory.java +++ /dev/null @@ -1,464 +0,0 @@ -package org.zanata.rest.client; - -import java.io.IOException; -import java.lang.reflect.Method; -import java.net.ConnectException; -import java.net.InetSocketAddress; -import java.net.MalformedURLException; -import java.net.Socket; -import java.net.URI; -import java.net.URISyntaxException; -import java.net.URL; -import java.net.UnknownHostException; -import java.security.SecureRandom; -import java.security.cert.CertificateException; -import java.security.cert.X509Certificate; - -import javax.net.ssl.SSLContext; -import javax.net.ssl.SSLSocket; -import javax.net.ssl.TrustManager; -import javax.net.ssl.X509TrustManager; - -import org.apache.commons.beanutils.PropertyUtils; -import org.apache.commons.lang.SystemUtils; -import org.apache.http.HttpHost; -import org.apache.http.client.HttpClient; -import org.apache.http.conn.ClientConnectionManager; -import org.apache.http.conn.ConnectTimeoutException; -import org.apache.http.conn.scheme.Scheme; -import org.apache.http.conn.ssl.SSLSocketFactory; -import org.apache.http.conn.ssl.X509HostnameVerifier; -import org.apache.http.impl.client.DefaultHttpClient; -import org.apache.http.params.HttpParams; -import org.apache.http.protocol.HttpContext; -import org.apache.maven.artifact.versioning.DefaultArtifactVersion; -import org.jboss.resteasy.client.ClientExecutor; -import org.jboss.resteasy.client.ClientRequestFactory; -import org.jboss.resteasy.client.ClientResponse; -import org.jboss.resteasy.client.core.ClientInterceptorRepository; -import org.jboss.resteasy.client.core.executors.ApacheHttpClient4Executor; -import org.jboss.resteasy.client.exception.ResteasyIOException; -import org.jboss.resteasy.plugins.providers.RegisterBuiltin; -import org.jboss.resteasy.spi.ResteasyProviderFactory; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.zanata.rest.RestConstant; -import org.zanata.rest.dto.VersionInfo; -import com.google.common.base.Throwables; - -// TODO fix deprecation warnings -public class ZanataProxyFactory implements ITranslationResourcesFactory { - private static final Logger log = LoggerFactory - .getLogger(ZanataProxyFactory.class); - - static { - ResteasyProviderFactory instance = - ResteasyProviderFactory.getInstance(); - RegisterBuiltin.register(instance); - - if (!SystemUtils.isJavaVersionAtLeast(1.7f)) { - log.warn("Please upgrade to Java 1.7 or later, or you may have trouble connecting to some secure hosts."); - } - } - - private VersionInfo clientApiVersion; - - private String clientVersion; - private String serverVersion; - - private ClientRequestFactory crf; - - /** - * see org.zanata.client.commands.init.InitCommand.MockZanataProxyFactory - */ - protected ZanataProxyFactory() { - } - - /** - * This will by default pass a null ClientExecutor to the - * ClientRequestFactory which in turn create default client executor. If - * sslCertDisabled is true, we will create our customized ClientExecutor. - * - * @param base - * base url - * @param username - * username - * @param apiKey - * api key - * @param clientApiVersion - * client API version - * @param logHttp - * whether to log http output - * @param sslCertDisabled - * whether to disable SSL certificate verification - */ - public ZanataProxyFactory(URI base, String username, String apiKey, - VersionInfo clientApiVersion, boolean logHttp, - boolean sslCertDisabled) { - this(base, username, apiKey, clientApiVersion, logHttp, - sslCertDisabled, true); - } - - /** - * Same as - * org.zanata.rest.client.ZanataProxyFactory#ZanataProxyFactory(java. - * net.URI, java.lang.String, java.lang.String, - * org.zanata.rest.dto.VersionInfo, boolean, boolean) except it allows to - * configure whether to do an eager REST version check to the server. - * - * @param eagerVersionCheck - * whether or not to perform an eager REST version call - */ - public ZanataProxyFactory(URI base, String username, String apiKey, - VersionInfo clientApiVersion, boolean logHttp, - boolean sslCertDisabled, boolean eagerVersionCheck) { - this.clientApiVersion = clientApiVersion; - ClientExecutor clientExecutor = createClientExecutor(sslCertDisabled); - - crf = new ClientRequestFactory(clientExecutor, null, fixBase(base)); - // This is not doing anything. - // There is no easy way to handle redirect with resteasy at the moment. - // See https://issues.jboss.org/browse/RESTEASY-1075 - // See org.zanata.rest.client.ClientUtility.checkResult(org.jboss.resteasy.client.ClientResponse, java.net.URI)() - crf.setFollowRedirects(true); - registerPrefixInterceptor(new TraceDebugInterceptor(logHttp)); - registerPrefixInterceptor(new ApiKeyHeaderDecorator(username, apiKey, - clientApiVersion.getVersionNo())); - if (eagerVersionCheck) { - performVersionCheck(); - } - } - - - public void performVersionCheck() { - clientVersion = clientApiVersion.getVersionNo(); - String clientScm = clientApiVersion.getScmDescribe(); - - VersionInfo serverVersionInfo = getServerVersionInfo(); - serverVersion = serverVersionInfo.getVersionNo(); - String serverScm = serverVersionInfo.getScmDescribe(); - log.info("client API version: {}, server API version: {}", - clientVersion, serverVersion); - warnMismatchAPIVersion(clientScm, serverScm); - } - - public VersionInfo getServerVersionInfo() { - IVersionResource iversion = createIVersionResource(); - ClientResponse versionResp; - try { - versionResp = iversion.get(); - } catch (ResteasyIOException e) { - Throwable rootCause = Throwables.getRootCause(e); - if (rootCause instanceof ConnectException) { - throw new RuntimeException("Can not connect to the server [" + crf.getBase() + "]. Please check server is up."); - } else { - throw e; - } - } - ClientUtility.checkResult(versionResp); - return versionResp.getEntity(); - } - - private void warnMismatchAPIVersion(String clientScm, String serverScm) { - if (!serverVersion.equals(clientVersion)) { - log.warn("client API version is {}, but server API version is {}", - clientVersion, serverVersion); - } else if (serverVersion.contains(RestConstant.SNAPSHOT_VERSION) - && !serverScm.equalsIgnoreCase(clientScm)) { - log.warn( - "client API SCM id is {}, but server API SCM id is {}", - clientScm, serverScm); - } - } - - private static ClientExecutor createClientExecutor(boolean sslCertDisabled) { - try { - final SSLContext sslContext = SSLContext.getInstance("TLS"); - - // Create a trust manager that does not validate certificate chains - // against our server - final TrustManager[] trustAllCerts; - if (sslCertDisabled) { - trustAllCerts = - new TrustManager[]{ new AcceptAllX509TrustManager() }; - } else { - trustAllCerts = null; - } - - sslContext.init(null, trustAllCerts, new SecureRandom()); - - // NB: This factory is a workaround to enable SNI with - // httpcomponents-client 4.2; not needed for 4.3 - SSLSocketFactory factory; - if (sslCertDisabled) { - // avoid triggering the problem described here: - // https://stackoverflow.com/questions/7615645/ssl-handshake-alert-unrecognized-name-error-since-upgrade-to-java-1-7-0 - factory = new SSLSocketFactory(sslContext); - } else { - factory = new SSLSocketFactory(sslContext) { - @Override - public Socket connectSocket(Socket socket, - InetSocketAddress remoteAddress, - InetSocketAddress localAddress, - HttpParams params) - throws IOException, UnknownHostException, - ConnectTimeoutException { - if (socket instanceof SSLSocket) { - try { - PropertyUtils.setProperty(socket, "host", - remoteAddress.getHostName()); - } catch (Exception ex) { - log.warn( - "Unable to enable SNI; you may have trouble connecting to some secure hosts. Please ensure that you are running Java 1.7 or later."); - } - } - return super.connectSocket(socket, remoteAddress, - localAddress, params); - } - }; - } - - HttpClient client = new DefaultHttpClient(); - - if (sslCertDisabled) { - X509HostnameVerifier hostnameVerifier = org.apache.http.conn.ssl.SSLSocketFactory.ALLOW_ALL_HOSTNAME_VERIFIER; - factory.setHostnameVerifier(hostnameVerifier); - } - - ClientConnectionManager manager = client.getConnectionManager(); - manager.getSchemeRegistry().register( - new Scheme("https", 443, factory)); - return new ApacheHttpClient4Executor(client); - - } catch (Exception e) { - log.warn("error creating SSL client", e); - } - return null; - } - - /** - * Returns the Base url to be used for rest Requests. - */ - private URL getBaseUrl() { - try { - return new URL(fixBase(crf.getBase()).toString() + getUrlPrefix()); - } catch (MalformedURLException e) { - throw new RuntimeException(e); - } - } - - private URI getBaseUri() { - try { - return getBaseUrl().toURI(); - } catch (URISyntaxException e) { - throw new RuntimeException(e); - } - } - - protected String getUrlPrefix() { - return "rest/"; - } - - public T createProxy(Class clazz, URI baseUri) { - log.debug("{} proxy uri: {}", clazz.getSimpleName(), baseUri); - T proxy = crf.createProxy(clazz, baseUri); - // CacheFactory.makeCacheable(proxy); - return proxy; - } - - /** - * Returns a client proxy, provided all information is on the proxied - * interface. (i.e. The interface is marked with a {@link javax.ws.rs.Path} - * annotation.) - * - * @param clazz - * Client interface to proxy. - * @return Client proxy for the class. - * @see {@link ZanataProxyFactory#createProxy(Class, java.net.URI)} - */ - public T createProxy(Class clazz) { - return createProxy(clazz, getBaseUri()); - } - - private static URI fixBase(URI base) { - if (base != null) { - String baseString = base.toString(); - if (!baseString.endsWith("/")) { - try { - URI result = new URI(baseString + "/"); - log.warn("Appending '/' to base URL '{}': using '{}'", - baseString, result); - return result; - } catch (URISyntaxException e) { - throw new RuntimeException(e); - } - } - } - return base; - } - - public IGlossaryResource getGlossaryResource() { - return createProxy(IGlossaryResource.class); - } - - public IAccountResource getAccount(String username) { - return createProxy(IAccountResource.class, getAccountURI(username)); - } - - public URI getAccountURI(String username) { - try { - URL url = new URL(getBaseUrl(), "accounts/u/" + username); - return url.toURI(); - } catch (MalformedURLException e) { - throw new RuntimeException(e); - } catch (URISyntaxException e) { - throw new RuntimeException(e); - } - } - - public IProjectResource getProject(String proj) { - return createProxy(IProjectResource.class, getProjectURI(proj)); - } - - public URI getProjectURI(String proj) { - try { - URL url = new URL(getBaseUrl(), "projects/p/" + proj); - return url.toURI(); - } catch (MalformedURLException e) { - throw new RuntimeException(e); - } catch (URISyntaxException e) { - throw new RuntimeException(e); - } - } - - public IProjectIterationResource getProjectIteration(String proj, - String iter) { - return createProxy(IProjectIterationResource.class, - getProjectIterationURI(proj, iter)); - } - - public URI getProjectIterationURI(String proj, String iter) { - try { - URL url = - new URL(getBaseUrl(), "projects/p/" + proj - + "/iterations/i/" + iter); - return url.toURI(); - } catch (MalformedURLException e) { - throw new RuntimeException(e); - } catch (URISyntaxException e) { - throw new RuntimeException(e); - } - } - - // NB IProjectsResource is not currently used in Java - public IProjectsResource getProjectsResource() { - return createProxy(IProjectsResource.class); - } - - @Override - public ITranslatedDocResource getTranslatedDocResource(String projectSlug, - String versionSlug) { - return createProxy(ITranslatedDocResource.class, - getResourceURI(projectSlug, versionSlug)); - } - - public ISourceDocResource getSourceDocResource(String projectSlug, - String versionSlug) { - return createProxy(ISourceDocResource.class, - getResourceURI(projectSlug, versionSlug)); - } - - public IFileResource getFileResource() { - return createProxy(IFileResource.class); - } - - public IStatisticsResource getStatisticsResource() { - return createProxy(IStatisticsResource.class); - } - - public ICopyTransResource getCopyTransResource() { - return createProxy(ICopyTransResource.class); - } - - public IAsynchronousProcessResource getAsynchronousProcessResource() { - return createProxy(IAsynchronousProcessResource.class); - } - - @Override - public URI getResourceURI(String projectSlug, String versionSlug) { - String spec = - "projects/p/" + projectSlug + "/iterations/i/" + versionSlug - + "/r"; - try { - return new URL(getBaseUrl(), spec).toURI(); - } catch (MalformedURLException e) { - throw new RuntimeException(e); - } catch (URISyntaxException e) { - String msg = - "URI Syntax error. Please make sure your project (project ID) and version are correct."; - log.error(msg); - log.error("part of your url: {}", spec); - throw new RuntimeException(msg); - } - } - - /** - * @see org.jboss.resteasy.client.core.ClientInterceptorRepositoryImpl#registerInterceptor(Object) - * @param interceptor - */ - public void registerPrefixInterceptor(Object interceptor) { - ClientInterceptorRepository repo = getPrefixInterceptors(); - repo.registerInterceptor(interceptor); - } - - /** - * Workaround for signature incompatibility between RESTEasy 2.x and 3.x - * @return - */ - private ClientInterceptorRepository getPrefixInterceptors() { - try { - Method m = crf.getClass().getMethod("getPrefixInterceptors"); - return (ClientInterceptorRepository) m.invoke(crf); - } catch (Exception e) { - throw new RuntimeException(e); - } - } - - protected IVersionResource createIVersionResource() { - return createProxy(IVersionResource.class, getBaseUri()); - } - - /** - * Compares a given version identifier with the server version. - * - * @param version - * The version to against which to compare the server version. - * @return A positive integer if the server version is greater than the - * given version. A negative integer if the server version is less - * than the given version. 0 if both versions are the same. - */ - public int compareToServerVersion(String version) { - DefaultArtifactVersion srvVersion = - new DefaultArtifactVersion(serverVersion); - DefaultArtifactVersion providedVersion = - new DefaultArtifactVersion(version); - - return srvVersion.compareTo(providedVersion); - } - - private static class AcceptAllX509TrustManager implements X509TrustManager { - public X509Certificate[] getAcceptedIssuers() { - return null; - } - - public void - checkClientTrusted(X509Certificate[] certs, String authType) - throws CertificateException { - } - - public void - checkServerTrusted(X509Certificate[] certs, String authType) - throws CertificateException { - } - } -} diff --git a/zanata-rest-client/src/test/java/org/zanata/rest/client/AccountClientTest.java b/zanata-rest-client/src/test/java/org/zanata/rest/client/AccountClientTest.java new file mode 100644 index 00000000..b726c5f4 --- /dev/null +++ b/zanata-rest-client/src/test/java/org/zanata/rest/client/AccountClientTest.java @@ -0,0 +1,61 @@ +/* + * Copyright 2014, Red Hat, Inc. and individual contributors + * as indicated by the @author tags. See the copyright.txt file in the + * distribution for a full listing of individual contributors. + * + * This is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this software; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA, or see the FSF site: http://www.fsf.org. + */ + +package org.zanata.rest.client; + +import org.hamcrest.Matchers; +import org.junit.Before; +import org.junit.ClassRule; +import org.junit.Test; +import org.zanata.rest.dto.Account; +import org.zanata.rest.service.StubbingServerRule; + +import static org.junit.Assert.*; + +public class AccountClientTest { + @ClassRule + public static StubbingServerRule + stubbingServerRule = new StubbingServerRule(); + private AccountClient client; + + @Before + public void setUp() throws Exception { + client = new AccountClient(MockServerTestUtil + .createClientFactory(stubbingServerRule.getServerBaseUri())); + + } + + @Test + public void testGet() throws Exception { + Account account = client.get("admin"); + + assertThat(account.getEmail(), Matchers.equalTo("admin@zanata.org")); + } + + @Test + public void testPut() throws Exception { + client.put("admin", new Account("a@b.c", "d", "e", "f")); + + MockServerTestUtil.verifyServerRespondSuccessStatus(); + } +} + + diff --git a/zanata-rest-client/src/test/java/org/zanata/rest/client/ApiKeyHeaderDecoratorTest.java b/zanata-rest-client/src/test/java/org/zanata/rest/client/ApiKeyHeaderDecoratorTest.java deleted file mode 100644 index 49248b3f..00000000 --- a/zanata-rest-client/src/test/java/org/zanata/rest/client/ApiKeyHeaderDecoratorTest.java +++ /dev/null @@ -1,69 +0,0 @@ -/* - * Copyright 2011, Red Hat, Inc. and individual contributors - * as indicated by the @author tags. See the copyright.txt file in the - * distribution for a full listing of individual contributors. - * - * This is free software; you can redistribute it and/or modify it - * under the terms of the GNU Lesser General Public License as - * published by the Free Software Foundation; either version 2.1 of - * the License, or (at your option) any later version. - * - * This software is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this software; if not, write to the Free - * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA - * 02110-1301 USA, or see the FSF site: http://www.fsf.org. - */ - -package org.zanata.rest.client; - -import javax.ws.rs.core.MultivaluedMap; - -import org.jboss.resteasy.client.ClientRequest; -import org.jboss.resteasy.client.ClientResponse; -import org.jboss.resteasy.spi.interception.ClientExecutionContext; -import org.junit.Test; -import org.zanata.rest.RestConstant; - -import static org.junit.Assert.assertEquals; - -/** - * @author Sean Flanigan sflaniga@redhat.com - * - */ -public class ApiKeyHeaderDecoratorTest { - @Test - public void testHeaders() throws Exception { - String username = "username"; - String apiKey = "apiKey"; - String ver = "ver"; - ApiKeyHeaderDecorator decorator = - new ApiKeyHeaderDecorator(username, apiKey, ver); - - final ClientRequest mockRequest = - new ClientRequest("http://uri.example.com/"); - ClientExecutionContext mockCtx = new ClientExecutionContext() { - - @SuppressWarnings("rawtypes") - @Override - public ClientResponse proceed() throws Exception { - return null; - } - - @Override - public ClientRequest getRequest() { - return mockRequest; - } - }; - decorator.execute(mockCtx); - MultivaluedMap headers = mockRequest.getHeaders(); - assertEquals(username, headers.getFirst(RestConstant.HEADER_USERNAME)); - assertEquals(apiKey, headers.getFirst(RestConstant.HEADER_API_KEY)); - assertEquals(ver, headers.getFirst(RestConstant.HEADER_VERSION_NO)); - } -} diff --git a/zanata-rest-client/src/test/java/org/zanata/rest/client/ApiKeyHeaderFilterTest.java b/zanata-rest-client/src/test/java/org/zanata/rest/client/ApiKeyHeaderFilterTest.java new file mode 100644 index 00000000..8a3fc60a --- /dev/null +++ b/zanata-rest-client/src/test/java/org/zanata/rest/client/ApiKeyHeaderFilterTest.java @@ -0,0 +1,83 @@ +/* + * Copyright 2011, Red Hat, Inc. and individual contributors + * as indicated by the @author tags. See the copyright.txt file in the + * distribution for a full listing of individual contributors. + * + * This is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this software; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA, or see the FSF site: http://www.fsf.org. + */ + +package org.zanata.rest.client; + +import static org.hamcrest.MatcherAssert.assertThat; +import static org.mockito.Mockito.when; + +import javax.ws.rs.core.MultivaluedHashMap; + +import org.hamcrest.Matchers; +import org.junit.Before; +import org.junit.Test; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; +import org.zanata.rest.RestConstant; +import com.sun.jersey.api.client.ClientRequest; +import com.sun.jersey.api.client.ClientResponse; + +/** + * @author Sean Flanigan sflaniga@redhat.com + * + */ +public class ApiKeyHeaderFilterTest { + @Mock + private ClientRequest mockRequest; + @Mock + private ClientResponse response; + + @Before + public void setUp() { + MockitoAnnotations.initMocks(this); + } + + @Test + public void testHeaders() throws Exception { + String username = "username"; + String apiKey = "apiKey"; + String ver = "ver"; + ApiKeyHeaderFilter filter = + new ApiKeyHeaderFilter(username, apiKey, ver) { + @Override + protected ClientResponse handleNext( + ClientRequest cr) { + return response; + } + }; + + MultivaluedHashMap headerMap = + new MultivaluedHashMap<>(); + when(mockRequest.getHeaders()).thenReturn(headerMap); + // + + filter.handle(mockRequest); + + assertThat(headerMap.getFirst(RestConstant.HEADER_USERNAME).toString(), + Matchers.equalTo(username)); + assertThat(headerMap.getFirst(RestConstant.HEADER_API_KEY).toString(), + Matchers.equalTo(apiKey)); + assertThat(headerMap.getFirst(RestConstant.HEADER_VERSION_NO) + .toString(), Matchers.equalTo(ver)); + } +} + diff --git a/zanata-rest-client/src/test/java/org/zanata/rest/client/AsyncProcessClientTest.java b/zanata-rest-client/src/test/java/org/zanata/rest/client/AsyncProcessClientTest.java new file mode 100644 index 00000000..bf9730be --- /dev/null +++ b/zanata-rest-client/src/test/java/org/zanata/rest/client/AsyncProcessClientTest.java @@ -0,0 +1,85 @@ +/* + * Copyright 2014, Red Hat, Inc. and individual contributors + * as indicated by the @author tags. See the copyright.txt file in the + * distribution for a full listing of individual contributors. + * + * This is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this software; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA, or see the FSF site: http://www.fsf.org. + */ + +package org.zanata.rest.client; + +import org.hamcrest.Matchers; +import org.junit.Before; +import org.junit.ClassRule; +import org.junit.Test; +import org.zanata.common.LocaleId; +import org.zanata.rest.dto.ProcessStatus; +import org.zanata.rest.dto.resource.Resource; +import org.zanata.rest.dto.resource.TranslationsResource; +import org.zanata.rest.service.StubbingServerRule; + +import com.google.common.collect.Sets; + +import static org.junit.Assert.*; + +public class AsyncProcessClientTest { + @ClassRule + public static StubbingServerRule + stubbingServerRule = new StubbingServerRule(); + private AsyncProcessClient client; + + @Before + public void setUp() throws Exception { + client = new AsyncProcessClient(MockServerTestUtil + .createClientFactory(stubbingServerRule.getServerBaseUri())); + } + + @Test + public void testStartSourceDocCreationOrUpdate() throws Exception { + ProcessStatus processStatus = + client.startSourceDocCreationOrUpdate("message", + "about-fedora", + "master", + new Resource("message"), Sets.newHashSet("gettext"), + false); + + assertThat(processStatus.getStatusCode(), Matchers.equalTo( + ProcessStatus.ProcessStatusCode.Running)); + } + + @Test + public void testStartTranslatedDocCreationOrUpdate() throws Exception { + ProcessStatus processStatus = + client.startTranslatedDocCreationOrUpdate("message", + "about-fedora", + "master", LocaleId.DE, + new TranslationsResource(), Sets.newHashSet("gettext"), + "auto", false); + + assertThat(processStatus.getStatusCode(), Matchers.equalTo( + ProcessStatus.ProcessStatusCode.Running)); + } + + @Test + public void testGetProcessStatus() throws Exception { + ProcessStatus processStatus = client.getProcessStatus("a"); + + assertThat(processStatus.getStatusCode(), Matchers.equalTo( + ProcessStatus.ProcessStatusCode.Finished)); + } +} + + diff --git a/zanata-rest-client/src/test/java/org/zanata/rest/client/CopyTransClientTest.java b/zanata-rest-client/src/test/java/org/zanata/rest/client/CopyTransClientTest.java new file mode 100644 index 00000000..183d3a50 --- /dev/null +++ b/zanata-rest-client/src/test/java/org/zanata/rest/client/CopyTransClientTest.java @@ -0,0 +1,60 @@ +/* + * Copyright 2014, Red Hat, Inc. and individual contributors + * as indicated by the @author tags. See the copyright.txt file in the + * distribution for a full listing of individual contributors. + * + * This is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this software; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA, or see the FSF site: http://www.fsf.org. + */ + +package org.zanata.rest.client; + +import org.hamcrest.Matchers; +import org.junit.Before; +import org.junit.ClassRule; +import org.junit.Test; +import org.zanata.rest.dto.CopyTransStatus; +import org.zanata.rest.service.StubbingServerRule; + +import static org.junit.Assert.*; + +public class CopyTransClientTest { + @ClassRule + public static StubbingServerRule + stubbingServerRule = new StubbingServerRule(); + private CopyTransClient client; + + @Before + public void setUp() throws Exception { + client = new CopyTransClient(MockServerTestUtil + .createClientFactory(stubbingServerRule.getServerBaseUri())); + } + + @Test + public void testStartCopyTrans() throws Exception { + CopyTransStatus copyTransStatus = + client.startCopyTrans("about-fedora", "master", "Authors"); + assertThat(copyTransStatus.isInProgress(), Matchers.is(true)); + } + + @Test + public void testGetCopyTransStatus() throws Exception { + CopyTransStatus copyTransStatus = + client.getCopyTransStatus("about-fedora", "master", "Authors"); + assertThat(copyTransStatus.isInProgress(), Matchers.is(false)); + } +} + + diff --git a/zanata-rest-client/src/test/java/org/zanata/rest/client/FileResourceClientTest.java b/zanata-rest-client/src/test/java/org/zanata/rest/client/FileResourceClientTest.java new file mode 100644 index 00000000..c780db6f --- /dev/null +++ b/zanata-rest-client/src/test/java/org/zanata/rest/client/FileResourceClientTest.java @@ -0,0 +1,184 @@ +/* + * Copyright 2014, Red Hat, Inc. and individual contributors + * as indicated by the @author tags. See the copyright.txt file in the + * distribution for a full listing of individual contributors. + * + * This is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this software; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA, or see the FSF site: http://www.fsf.org. + */ + +package org.zanata.rest.client; + +import java.io.File; +import java.io.FileInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.net.URISyntaxException; +import java.security.DigestInputStream; +import java.security.MessageDigest; +import java.security.NoSuchAlgorithmException; +import java.util.ArrayList; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; + +import org.apache.commons.codec.binary.Hex; +import org.hamcrest.Matchers; +import org.junit.Before; +import org.junit.ClassRule; +import org.junit.Test; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.xml.sax.InputSource; +import org.zanata.adapter.po.PoReader2; +import org.zanata.common.DocumentType; +import org.zanata.common.LocaleId; +import org.zanata.rest.DocumentFileUploadForm; +import org.zanata.rest.StringSet; +import org.zanata.rest.dto.ChunkUploadResponse; +import org.zanata.rest.dto.resource.Resource; +import org.zanata.rest.dto.resource.TranslationsResource; +import org.zanata.rest.service.StubbingServerRule; + +import static org.hamcrest.collection.IsMapContaining.hasEntry; +import static org.junit.Assert.*; + +public class FileResourceClientTest { + private static final Logger log = + LoggerFactory.getLogger(FileResourceClientTest.class); + private FileResourceClient client; + + @ClassRule + public static StubbingServerRule + stubbingServerRule = new StubbingServerRule(); + + @Before + public void setUp() throws URISyntaxException { + RestClientFactory restClientFactory = MockServerTestUtil + .createClientFactory(stubbingServerRule.getServerBaseUri()); + client = new FileResourceClient(restClientFactory); + } + + @Test + public void testServerAcceptedType() { + List serverAcceptedTypes = client + .acceptedFileTypes(); + + Set allExtension = new HashSet(); + for (DocumentType docType : serverAcceptedTypes) { + allExtension.addAll(docType.getSourceExtensions()); + } + assertThat(allExtension, Matchers.containsInAnyOrder("dtd", "pot", + "txt", "idml", "html", "htm", "odt", "odp", "odg", "ods", + "srt", "sbt", "sub", "vtt", "properties", "xml")); + } + + @Test + public + void testSourceFileUpload() throws Exception { +// client = clientTalkingToRealServer(); + DocumentFileUploadForm uploadForm = new DocumentFileUploadForm(); + File source = loadFileFromClasspath("test-odt.odt"); + FileInputStream fileInputStream = new FileInputStream(source); + + uploadForm.setFileStream(fileInputStream); + uploadForm.setFileType("odt"); + uploadForm.setHash(calculateFileHash(source)); + uploadForm.setFirst(true); + uploadForm.setLast(true); + uploadForm.setSize(source.length()); + ChunkUploadResponse uploadResponse = client + .uploadSourceFile("about-fedora", "master", + "test.odt", + uploadForm); + log.info("response: {}", uploadResponse); + assertThat(uploadResponse.getAcceptedChunks(), Matchers.equalTo(1)); + } + + private static File loadFileFromClasspath(String file) { + return new File(Thread.currentThread().getContextClassLoader() + .getResource(file).getFile()); + } + + @Test + public + void testTranslationFileUpload() throws Exception { +// client = clientTalkingToRealServer(); + DocumentFileUploadForm uploadForm = new DocumentFileUploadForm(); + File source = loadFileFromClasspath("zh-CN/test-odt.odt"); + FileInputStream fileInputStream = new FileInputStream(source); + + uploadForm.setFileStream(fileInputStream); + uploadForm.setFileType("odt"); + uploadForm.setHash(calculateFileHash(source)); + uploadForm.setFirst(true); + uploadForm.setLast(true); + uploadForm.setSize(source.length()); + ChunkUploadResponse uploadResponse = client + .uploadTranslationFile("about-fedora", "master", + "zh", + "test.odt", "auto", + uploadForm); + log.info("response: {}", uploadResponse); + assertThat(uploadResponse.getAcceptedChunks(), Matchers.equalTo(1)); + } + + private String calculateFileHash(File srcFile) { + try { + MessageDigest md = MessageDigest.getInstance("MD5"); + InputStream fileStream = new FileInputStream(srcFile); + try { + fileStream = new DigestInputStream(fileStream, md); + byte[] buffer = new byte[256]; + while (fileStream.read(buffer) > 0) { + // continue + } + } finally { + fileStream.close(); + } + return new String(Hex.encodeHex(md.digest())); + } catch (NoSuchAlgorithmException | IOException e) { + throw new RuntimeException(e); + } + } + + @Test + public void testDownloadSourceFile() throws IOException { + InputStream inputStream = + client.downloadSourceFile("about-fedora", "master", "pot", + "About-Fedora").getEntity(InputStream.class); + PoReader2 reader = new PoReader2(); + Resource resource = + reader.extractTemplate(new InputSource(inputStream), + LocaleId.EN_US, "About-Fedora"); + assertThat(resource.getTextFlows(), Matchers.hasSize(1)); + } + + @Test + public void testDownloadTranslationFile() { + InputStream inputStream = + client.downloadTranslationFile("about-fedora", "master", "es", + "po", "About-Fedora").getEntity(InputStream.class); + PoReader2 reader = new PoReader2(); + TranslationsResource translationsResource = + reader.extractTarget(new InputSource(inputStream)); + assertThat(translationsResource.getTextFlowTargets(), + Matchers.hasSize(1)); + } + +} + + diff --git a/zanata-rest-client/src/test/java/org/zanata/rest/client/GlossaryClientTest.java b/zanata-rest-client/src/test/java/org/zanata/rest/client/GlossaryClientTest.java new file mode 100644 index 00000000..a7767ecc --- /dev/null +++ b/zanata-rest-client/src/test/java/org/zanata/rest/client/GlossaryClientTest.java @@ -0,0 +1,63 @@ +/* + * Copyright 2014, Red Hat, Inc. and individual contributors + * as indicated by the @author tags. See the copyright.txt file in the + * distribution for a full listing of individual contributors. + * + * This is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this software; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA, or see the FSF site: http://www.fsf.org. + */ + +package org.zanata.rest.client; + +import org.junit.Before; +import org.junit.ClassRule; +import org.junit.Test; +import org.zanata.common.LocaleId; +import org.zanata.rest.dto.Glossary; +import org.zanata.rest.service.StubbingServerRule; + +public class GlossaryClientTest { + @ClassRule + public static StubbingServerRule + stubbingServerRule = new StubbingServerRule(); + private GlossaryClient client; + + @Before + public void setUp() throws Exception { + client = new GlossaryClient(MockServerTestUtil + .createClientFactory(stubbingServerRule.getServerBaseUri())); + } + + @Test + public void testPut() throws Exception { + client.put(new Glossary()); + + MockServerTestUtil.verifyServerRespondSuccessStatus(); + } + + @Test + public void testDelete() throws Exception { + client.delete(LocaleId.DE); + MockServerTestUtil.verifyServerRespondSuccessStatus(); + } + + @Test + public void testDeleteAll() throws Exception { + client.deleteAll(); + MockServerTestUtil.verifyServerRespondSuccessStatus(); + } +} + + diff --git a/zanata-rest-client/src/test/java/org/zanata/rest/client/MockServerTestUtil.java b/zanata-rest-client/src/test/java/org/zanata/rest/client/MockServerTestUtil.java new file mode 100644 index 00000000..fc8d9cbd --- /dev/null +++ b/zanata-rest-client/src/test/java/org/zanata/rest/client/MockServerTestUtil.java @@ -0,0 +1,64 @@ +/* + * Copyright 2014, Red Hat, Inc. and individual contributors + * as indicated by the @author tags. See the copyright.txt file in the + * distribution for a full listing of individual contributors. + * + * This is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this software; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA, or see the FSF site: http://www.fsf.org. + */ + +package org.zanata.rest.client; + +import java.net.URI; +import java.net.URISyntaxException; + +import org.hamcrest.Matchers; +import org.zanata.rest.dto.VersionInfo; + +import static org.junit.Assert.assertThat; + +/** + * @author Patrick Huang + * pahuang@redhat.com + */ +public class MockServerTestUtil { + + public static RestClientFactory createClientFactory(URI serverBaseUri) { + return new RestClientFactory(serverBaseUri, + "admin", + "b6d7044e9ee3b2447c28fb7c50d86d98", new VersionInfo( + "3.6.0-SNAPSHOT", "unknown", "unknown"), true, true) { + @Override + protected String getUrlPrefix() { + return ""; + } + }; + } + + // If you ever want to test against real server, switch to use this one + public static RestClientFactory clientTalkingToRealServer() + throws URISyntaxException { + return new RestClientFactory(new URI("http://localhost:8080/zanata/"), + "admin", + "b6d7044e9ee3b2447c28fb7c50d86d98", new VersionInfo( + "3.6.0-SNAPSHOT", "unknown", "unknown"), true, + true); + } + + static void verifyServerRespondSuccessStatus() { + assertThat("server return successfuly status code", true, Matchers + .is(true)); + } +} diff --git a/zanata-rest-client/src/test/java/org/zanata/rest/client/ProjectClientTest.java b/zanata-rest-client/src/test/java/org/zanata/rest/client/ProjectClientTest.java new file mode 100644 index 00000000..0a52d9b0 --- /dev/null +++ b/zanata-rest-client/src/test/java/org/zanata/rest/client/ProjectClientTest.java @@ -0,0 +1,62 @@ +/* + * Copyright 2014, Red Hat, Inc. and individual contributors + * as indicated by the @author tags. See the copyright.txt file in the + * distribution for a full listing of individual contributors. + * + * This is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this software; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA, or see the FSF site: http://www.fsf.org. + */ + +package org.zanata.rest.client; + +import org.hamcrest.Matchers; +import org.junit.Before; +import org.junit.ClassRule; +import org.junit.Test; +import org.zanata.rest.dto.Project; +import org.zanata.rest.service.StubbingServerRule; + +import static org.junit.Assert.*; + +public class ProjectClientTest { + @ClassRule + public static StubbingServerRule + stubbingServerRule = new StubbingServerRule(); + private ProjectClient client; + + @Before + public void setUp() throws Exception { + client = new ProjectClient(MockServerTestUtil + .createClientFactory(stubbingServerRule.getServerBaseUri()), + "about-fedora"); + } + + @Test + public void testGet() throws Exception { + Project project = client.get(); + + assertThat(project.getId(), Matchers.equalTo("about-fedora")); + } + + @Test + public void testPut() throws Exception { + client.put(new Project("a", "b", "gettext")); + + MockServerTestUtil.verifyServerRespondSuccessStatus(); + } + +} + + diff --git a/zanata-rest-client/src/test/java/org/zanata/rest/client/ProjectIterationClientTest.java b/zanata-rest-client/src/test/java/org/zanata/rest/client/ProjectIterationClientTest.java new file mode 100644 index 00000000..3538161d --- /dev/null +++ b/zanata-rest-client/src/test/java/org/zanata/rest/client/ProjectIterationClientTest.java @@ -0,0 +1,67 @@ +/* + * Copyright 2014, Red Hat, Inc. and individual contributors + * as indicated by the @author tags. See the copyright.txt file in the + * distribution for a full listing of individual contributors. + * + * This is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this software; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA, or see the FSF site: http://www.fsf.org. + */ + +package org.zanata.rest.client; + +import org.hamcrest.Matchers; +import org.junit.Before; +import org.junit.ClassRule; +import org.junit.Test; +import org.zanata.rest.dto.ProjectIteration; +import org.zanata.rest.service.StubbingServerRule; + +import static org.junit.Assert.*; + +public class ProjectIterationClientTest { + @ClassRule + public static StubbingServerRule + stubbingServerRule = new StubbingServerRule(); + private ProjectIterationClient client; + + @Before + public void setUp() throws Exception { + client = new ProjectIterationClient(MockServerTestUtil + .createClientFactory(stubbingServerRule.getServerBaseUri()), + "about-fedora", "master"); + } + + @Test + public void testGet() throws Exception { + ProjectIteration projectIteration = client.get(); + assertThat(projectIteration.getId(), Matchers.equalTo("master")); + } + + @Test + public void testPut() throws Exception { + client.put(new ProjectIteration("1.1")); + + MockServerTestUtil.verifyServerRespondSuccessStatus(); + } + + @Test + public void testSampleConfig() { + String config = client.sampleConfiguration(); + + assertThat(config, Matchers.containsString("")); + } +} + + diff --git a/zanata-rest-client/src/test/java/org/zanata/rest/client/ProjectsClientTest.java b/zanata-rest-client/src/test/java/org/zanata/rest/client/ProjectsClientTest.java new file mode 100644 index 00000000..806dba35 --- /dev/null +++ b/zanata-rest-client/src/test/java/org/zanata/rest/client/ProjectsClientTest.java @@ -0,0 +1,55 @@ +/* + * Copyright 2014, Red Hat, Inc. and individual contributors + * as indicated by the @author tags. See the copyright.txt file in the + * distribution for a full listing of individual contributors. + * + * This is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this software; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA, or see the FSF site: http://www.fsf.org. + */ + +package org.zanata.rest.client; + +import org.hamcrest.Matchers; +import org.junit.Before; +import org.junit.ClassRule; +import org.junit.Test; +import org.zanata.rest.dto.Project; +import org.zanata.rest.service.StubbingServerRule; + +import static org.junit.Assert.*; + +public class ProjectsClientTest { + @ClassRule + public static StubbingServerRule + stubbingServerRule = new StubbingServerRule(); + private ProjectsClient client; + + @Before + public void setUp() { + client = new ProjectsClient(MockServerTestUtil + .createClientFactory(stubbingServerRule.getServerBaseUri())); + } + + @Test + public void canGetProjects() { + Project[] projects = client.getProjects(); + + assertThat(projects, Matchers.arrayWithSize(1)); + assertThat(projects[0].getId(), Matchers.equalTo("about-fedora")); + } + +} + + diff --git a/zanata-rest-client/src/test/java/org/zanata/rest/client/ClientUtilityTest.java b/zanata-rest-client/src/test/java/org/zanata/rest/client/RestClientFactoryTest.java similarity index 62% rename from zanata-rest-client/src/test/java/org/zanata/rest/client/ClientUtilityTest.java rename to zanata-rest-client/src/test/java/org/zanata/rest/client/RestClientFactoryTest.java index a64245e1..62c4bdf7 100644 --- a/zanata-rest-client/src/test/java/org/zanata/rest/client/ClientUtilityTest.java +++ b/zanata-rest-client/src/test/java/org/zanata/rest/client/RestClientFactoryTest.java @@ -21,23 +21,28 @@ package org.zanata.rest.client; -import static org.hamcrest.CoreMatchers.equalTo; -import static org.hamcrest.MatcherAssert.assertThat; +import org.hamcrest.MatcherAssert; +import org.hamcrest.Matchers; +import org.junit.Rule; +import org.junit.Test; +import org.zanata.rest.dto.VersionInfo; +import org.zanata.rest.service.StubbingServerRule; -import java.net.MalformedURLException; -import java.net.URISyntaxException; +public class RestClientFactoryTest { -import org.junit.Test; + @Rule + public StubbingServerRule stubbingServerRule = new StubbingServerRule(); -public class ClientUtilityTest { @Test - public void test() throws URISyntaxException, MalformedURLException { + public void testGetVersion() { + VersionInfo serverVersionInfo = MockServerTestUtil.createClientFactory( + stubbingServerRule.getServerBaseUri()) + .getServerVersionInfo(); - assertThat(ClientUtility.getBaseURL( - "https://zanata.org/rest/version/?a=b"), equalTo( - "https://zanata.org/")); - assertThat(ClientUtility.getBaseURL("https://zanata.org"), - equalTo("https://zanata.org/")); + MatcherAssert.assertThat(serverVersionInfo.getVersionNo(), + Matchers.equalTo("3.6.0-SNAPSHOT")); } } + + diff --git a/zanata-rest-client/src/test/java/org/zanata/rest/client/SourceDocResourceClientTest.java b/zanata-rest-client/src/test/java/org/zanata/rest/client/SourceDocResourceClientTest.java new file mode 100644 index 00000000..eb4e1619 --- /dev/null +++ b/zanata-rest-client/src/test/java/org/zanata/rest/client/SourceDocResourceClientTest.java @@ -0,0 +1,83 @@ +/* + * Copyright 2014, Red Hat, Inc. and individual contributors + * as indicated by the @author tags. See the copyright.txt file in the + * distribution for a full listing of individual contributors. + * + * This is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this software; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA, or see the FSF site: http://www.fsf.org. + */ + +package org.zanata.rest.client; + +import static org.junit.Assert.assertThat; + +import java.net.URISyntaxException; +import java.util.List; + +import org.hamcrest.Matchers; +import org.junit.Before; +import org.junit.ClassRule; +import org.junit.Test; +import org.zanata.rest.dto.resource.Resource; +import org.zanata.rest.dto.resource.ResourceMeta; +import org.zanata.rest.service.StubbingServerRule; +import com.google.common.collect.Sets; + +public class SourceDocResourceClientTest { + @ClassRule + public static StubbingServerRule + stubbingServerRule = new StubbingServerRule(); + + private SourceDocResourceClient client; + + @Before + public void setUp() throws URISyntaxException { + client = + new SourceDocResourceClient( + MockServerTestUtil.createClientFactory( + stubbingServerRule.getServerBaseUri()), "about-fedora", + "master"); + } + + @Test + public void testGetResourceMeta() { + List resourceMeta = client.getResourceMeta(null); + + assertThat(resourceMeta, Matchers.hasSize(2)); + } + + @Test + public void testGetResource() { + Resource resource = client.getResource("test", + Sets.newHashSet("gettext", "comment")); + + assertThat(resource.getName(), Matchers.equalTo("test")); + } + + @Test + public void testPutResource() { + String result = client.putResource("test", new Resource("newName"), + Sets.newHashSet("gettext"), true); + assertThat(result, Matchers.equalTo("newName")); + } + + @Test + public void testDeleteResource() { + String result = client.deleteResource("test"); + assertThat(result, Matchers.isEmptyOrNullString()); + } +} + + diff --git a/zanata-rest-client/src/test/java/org/zanata/rest/client/StatisticsResourceClientTest.java b/zanata-rest-client/src/test/java/org/zanata/rest/client/StatisticsResourceClientTest.java new file mode 100644 index 00000000..78a64608 --- /dev/null +++ b/zanata-rest-client/src/test/java/org/zanata/rest/client/StatisticsResourceClientTest.java @@ -0,0 +1,82 @@ +/* + * Copyright 2014, Red Hat, Inc. and individual contributors + * as indicated by the @author tags. See the copyright.txt file in the + * distribution for a full listing of individual contributors. + * + * This is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this software; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA, or see the FSF site: http://www.fsf.org. + */ + +package org.zanata.rest.client; + +import static org.junit.Assert.assertThat; + +import java.net.URISyntaxException; + +import org.hamcrest.Matchers; +import org.junit.Before; +import org.junit.ClassRule; +import org.junit.Test; +import org.zanata.rest.dto.stats.ContainerTranslationStatistics; +import org.zanata.rest.dto.stats.contribution.ContributionStatistics; +import org.zanata.rest.service.StubbingServerRule; + +public class StatisticsResourceClientTest { + @ClassRule + public static StubbingServerRule + stubbingServerRule = new StubbingServerRule(); + + private StatisticsResourceClient client; + + @Before + public void setUp() throws URISyntaxException { + client = + new StatisticsResourceClient( + MockServerTestUtil.createClientFactory( + stubbingServerRule.getServerBaseUri())); + } + + @Test + public void testGetIterationStatistics() { + String versionSlug = "master"; + ContainerTranslationStatistics statistics = + client.getStatistics("pahuang-test", versionSlug, true, true, + new String[] { "de-DE", "de", "zh-CN" }); + + assertThat(statistics.getId(), Matchers.equalTo(versionSlug)); + assertThat(statistics.getStats(), Matchers.hasSize(3)); + } + + @Test + public void testGetDocStatistics() { + String docId = "About-Fedora"; + ContainerTranslationStatistics statistics = + client.getStatistics("about-fedora", "master", docId, + true, new String[] { "de-DE", "zh-CN" }); + assertThat(statistics.getId(), Matchers.equalTo(docId)); + assertThat(statistics.getStats(), Matchers.hasSize(2)); + } + + @Test + public void testGetContributorStatistics() { + ContributionStatistics statistics = + client.getContributionStatistics("about-fedora", "master", + "pahuang", "2014-10-01..2014-11-10"); + assertThat(statistics, Matchers.hasKey("pahuang")); + } + +} + + diff --git a/zanata-rest-client/src/test/java/org/zanata/rest/client/TransDocResourceClientTest.java b/zanata-rest-client/src/test/java/org/zanata/rest/client/TransDocResourceClientTest.java new file mode 100644 index 00000000..2ad03c99 --- /dev/null +++ b/zanata-rest-client/src/test/java/org/zanata/rest/client/TransDocResourceClientTest.java @@ -0,0 +1,62 @@ +/* + * Copyright 2014, Red Hat, Inc. and individual contributors + * as indicated by the @author tags. See the copyright.txt file in the + * distribution for a full listing of individual contributors. + * + * This is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this software; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA, or see the FSF site: http://www.fsf.org. + */ + +package org.zanata.rest.client; + +import org.hamcrest.Matchers; +import org.junit.Before; +import org.junit.ClassRule; +import org.junit.Test; +import org.zanata.common.LocaleId; +import org.zanata.rest.dto.resource.TranslationsResource; +import org.zanata.rest.service.StubbingServerRule; + +import com.google.common.collect.Sets; + +import static org.junit.Assert.*; + +public class TransDocResourceClientTest { + @ClassRule + public static StubbingServerRule + stubbingServerRule = new StubbingServerRule(); + + private TransDocResourceClient client; + + @Before + public void setUp() { + client = new TransDocResourceClient( + MockServerTestUtil.createClientFactory(stubbingServerRule.getServerBaseUri()), "about-fedora", + "master"); + } + + @Test + public void testGetTranslations() { + TranslationsResource translations = + client.getTranslations("test", LocaleId.DE, + Sets.newHashSet("gettext", "comment"), true, "abc") + .getEntity(TranslationsResource.class); + + assertThat(translations.getTextFlowTargets(), Matchers.hasSize(1)); + } + +} + + diff --git a/zanata-rest-client/src/test/resources/test-odt.odt b/zanata-rest-client/src/test/resources/test-odt.odt new file mode 100644 index 00000000..ba7e10ec Binary files /dev/null and b/zanata-rest-client/src/test/resources/test-odt.odt differ diff --git a/zanata-rest-client/src/test/resources/zh-CN/test-odt.odt b/zanata-rest-client/src/test/resources/zh-CN/test-odt.odt new file mode 100644 index 00000000..5c5d4ad8 Binary files /dev/null and b/zanata-rest-client/src/test/resources/zh-CN/test-odt.odt differ