Skip to content

Commit

Permalink
Readme (#33)
Browse files Browse the repository at this point in the history
* Usage section

* Better usage section

* Example generated objects

* Link to Jersey, Retrofit files

* Flesh out jersey and retrofit sections

* Generated objects builder pattern & jackson example

* Mention jackson

* Mention union visitors

* Bintray shield

* License badge

* mention unknown values for enums

* describe intended usages of aliases

* Contributing document mentions Intellij and checkstyle

* CR

* Update readme.md
  • Loading branch information
iamdanfox authored Jul 9, 2018
1 parent 16c77cf commit 37e1224
Show file tree
Hide file tree
Showing 4 changed files with 138 additions and 11 deletions.
19 changes: 19 additions & 0 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
# Contributing

The team welcomes contributions! To make changes:

- Fork the repo and make a branch
- Write your code (ideally with tests) and make sure the CircleCI build passes
- Open a PR (optionally linking to a github issue)

## Local development

We recommend using [Intellij IDEA Community Edition](https://www.jetbrains.com/idea/) for Java projects. You'll need Java 8 on your machine.

1. Fork the repository
1. Generate the IDE configuration: `./gradlew idea`
1. Import projects into Intellij: `open *.ipr`

Tips:

- run `./gradlew checkstyleMain checkstyleTest` locally to make sure your code conforms to the code-style.
Original file line number Diff line number Diff line change
Expand Up @@ -49,19 +49,21 @@ public static void main(String[] args) {
static CliConfiguration parseCliConfiguration(String[] args) {
CommandLineParser parser = new BasicParser();
Options options = new Options();
options.addOption(new Option(CliConfiguration.OBJECTS_OPTION, "Generate java objects"));
options.addOption(new Option(CliConfiguration.JERSEY_OPTION, "Generate jersey services"));
options.addOption(new Option(CliConfiguration.RETROFIT_OPTION, "Generate retrofit services"));
options.addOption(new Option(
CliConfiguration.RETROFIT_COMPLETABLE_FUTURES,
"Generate retrofit services which return completableFutures"));
options.addOption(new Option(CliConfiguration.OBJECTS_OPTION,
"Generate POJOs for Conjure type definitions"));
options.addOption(new Option(CliConfiguration.JERSEY_OPTION,
"Generate jax-rs annotated interfaces for client or server-usage"));
options.addOption(new Option(CliConfiguration.RETROFIT_OPTION,
"Generate retrofit interfaces for streaming/async clients"));
options.addOption(new Option(CliConfiguration.RETROFIT_COMPLETABLE_FUTURES,
"Generate retrofit services which return Java8 CompletableFuture instead of OkHttp Call"));

try {
CommandLine cmd = parser.parse(options, args, false);
String[] parsedArgs = cmd.getArgs();

Preconditions.checkArgument(parsedArgs.length == 3 && GENERATE_COMMAND.equals(args[0]),
"Usage: conjure-java %s <target> <output> [...options]", GENERATE_COMMAND);
"Usage: conjure-java %s <input> <output> [...options]", GENERATE_COMMAND);

return CliConfiguration.of(parsedArgs[1], parsedArgs[2], cmd.getOptions());
} catch (ParseException e) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -94,7 +94,7 @@ public void throwsWhenMissingArguments() {
String[] args = {};
assertThatThrownBy(() -> ConjureJavaCli.parseCliConfiguration(args))
.isInstanceOf(IllegalArgumentException.class)
.hasMessage("Usage: conjure-java generate <target> <output> [...options]");
.hasMessage("Usage: conjure-java generate <input> <output> [...options]");
}

@Test
Expand Down
112 changes: 109 additions & 3 deletions readme.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,109 @@
Conjure-Java
=======
The java implementation of Conjure generator for java.
# Conjure-Java ![Bintray](https://img.shields.io/bintray/v/palantir/releases/conjure-java.svg) [![License](https://img.shields.io/badge/License-Apache%202.0-lightgrey.svg)](https://opensource.org/licenses/Apache-2.0)




_CLI to generate Java POJOs and interfaces from [Conjure API definitions](https://github.com/palantir/conjure)._

## Usage

The recommended way to use conjure-java is via a build tool like [gradle-conjure](https://github.com/palantir/gradle-conjure). However, if you don't want to use gradle-conjure, there is also an executable which conforms to [RFC 002](https://github.com/palantir/conjure/blob/develop/rfc/002-contract-for-conjure-generators.md), published on [bintray](https://bintray.com/palantir/releases/conjure-java).

Usage: conjure-java generate <input> <output> [...options]

--objects [boolean] Generate POJOs for Conjure type definitions
--jersey [boolean] Generate jax-rs annotated interfaces for client or server-usage
--retrofit [boolean] Generate retrofit interfaces for streaming/async clients

## Example generated objects

Conjure-java objects are always immutable and thread-safe. Fields are never null; instead, Java 8 Optionals are used. JSON serialization is handled using [Jackson](https://github.com/FasterXML/jackson) annotations.

- **Conjure object: [ManyFieldExample](./conjure-java-core/src/integrationInput/java/com/palantir/product/ManyFieldExample.java)**

Objects can only be instantiated using the builder pattern, or by deserializing from JSON.

```java
ManyFieldExample example = ManyFieldExample.builder()
.string("foo")
.integer(123)
.optionalItem("bar")
.addAllItems(iterable)
.build();

// or using Jackson (via com.palantir.remoting3:jackson-support)
ObjectMapper mapper = ObjectMappers.newServerObjectMapper();
ManyFieldExample fromJson = mapper.readValue("{\"string\":\"foo\", ...}", ManyFieldExample.class);
```

- **Conjure union: [UnionTypeExample](./conjure-java-core/src/integrationInput/java/com/palantir/product/UnionTypeExample.java)**

Union types can be one of a few variants. To interact with a union value, users should use the `.accept` method and define a Visitor that handles each of the possible variants, including the possibility of an unknown variant.

```java
Foo output = unionTypeExample.accept(new Visitor<Foo>() {

public Foo visitStringExample(StringExample value) {
// your logic here!
}

public Foo visitSet(Set<String> value) {}

// ...

public Foo visitUnknown(String unknownType) {}

});
```

Visitors may seem clunky in Java, but they have the upside of compile-time assurance that you've handled all the possible variants. If you upgrade an API dependency and the API author added a new variant, the Java compiler will force you to explicitly deal with this new variant. We intentionally avoid `switch` statements and `instanceof` checks for this exact reason.
- **Conjure enum: [EnumExample](./conjure-java-core/src/integrationInput/java/com/palantir/product/EnumExample.java)**
Enums are subtly different from regular Java enums because they tolerate deserializing unknown values. This is important because it ensures server authors can add new variants to an enum without causing runtime errors for consumers that use older API jars.
```java
EnumExample one = EnumExample.ONE;
System.out.println(one); // prints: 'ONE'
EnumExample fromJson = mapper.readValue("\"XYZ\"", EnumExample.class);
System.out.println(fromJson); // prints: 'XYZ'
```
- **Conjure alias: [StringAliasExample](./conjure-java-core/src/integrationInput/java/com/palantir/product/StringAliasExample.java)**
Aliases have exactly the same JSON representation as their inner type, so they are useful for making error-prone function signatures more bulletproof:
```diff
-doSomething(String, String, String);
+doSomething(ProductId, UserId, EmailAddress);
```
## Example Jersey interfaces
Conjure-java generates Java interfaces with [JAX-RS](http://jax-rs-spec.java.net/) annotations, so they can be used on the client side or on the server-side.
- Example jersey interface: [EteService](./conjure-java-core/src/integrationInput/java/com/palantir/product/EteService.java)
For **client-side usage**, use [http-remoting](https://github.com/palantir/http-remoting#jaxrs-clients) which configures Feign with sensible defaults.
For **server-side usage**, you need a [Jersey](https://jersey.github.io/)-compatible server. We recommend Dropwizard (which is based on Jetty), but Grizzly, Tomcat, etc should also work fine! Use [http-remoting's jersey-servers](https://github.com/palantir/http-remoting#jersey-servers) to configure Jackson and Exception mappers appropriately.


## Example Retrofit interfaces

As an alternative to the JAX-RS interfaces above, conjure-java can generate equivalent interfaces with [Retrofit2](http://square.github.io/retrofit/) annotations. These clients are useful if you want to stream binary data or make non-blocking async calls:

- Example retrofit2 interface: [EteServiceRetrofit](./conjure-java-core/src/integrationInput/java/com/palantir/product/EteServiceRetrofit.java)

```java
@GET("./base/binary")
@Streaming
Call<ResponseBody> binary(@Header("Authorization") AuthHeader authHeader);
```

You can also supply the `--retrofitCompletableFutures` flag if you prefer Java 8 CompletableFutures instead of OkHttp's Call.
## Contributing
For instructions on how to set up your local development environment, check out the [Contributing document][./CONTRIBUTING.md].

0 comments on commit 37e1224

Please sign in to comment.