Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Exposing jclouds overrides option through client builder, sys-props, and env-vars. #123

Merged
merged 6 commits into from
Jan 19, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
99 changes: 67 additions & 32 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@

java client, based on jclouds, to interact with Bitbucket's REST API.


## On jclouds, apis and endpoints
Being built on top of `jclouds` means things are broken up into [Apis](https://github.com/cdancy/bitbucket-rest/tree/master/src/main/java/com/cdancy/bitbucket/rest/features).
`Apis` are just Interfaces that are analagous to a resource provided by the server-side program (e.g. /api/branches, /api/pullrequest, /api/commits, etc..).
Expand Down Expand Up @@ -44,68 +43,104 @@ Can be sourced from jcenter like so:

javadocs can be found via [github pages here](http://cdancy.github.io/bitbucket-rest/docs/javadoc/)

## Property based setup
## Examples on how to build a _BitbucketClient_

When using `Basic` (e.g. username and password) authentication:

BitbucketClient client = BitbucketClient.builder()
.endPoint("http://127.0.0.1:7990") // Optional and can be sourced from system/env. Falls back to http://127.0.0.1:7990
.credentials("admin:password") // Optional and can be sourced from system/env and can be Base64 encoded.
.build();

Version version = client.api().systemApi().version();

When using `Bearer` (e.g. token) authentication:

BitbucketClient client = BitbucketClient.builder()
.endPoint("http://127.0.0.1:7990") // Optional and can be sourced from system/env. Falls back to http://127.0.0.1:7990
.token("123456789abcdef") // Optional and can be sourced from system/env.
.build();

Version version = client.api().systemApi().version();

When using `Anonymous` authentication or sourcing from system/environment (as described above):

BitbucketClient client = BitbucketClient.builder()
.endPoint("http://127.0.0.1:7990") // Optional and can be sourced from system/env. Falls back to http://127.0.0.1:7990
.build();

Version version = client.api().systemApi().version();

## On `System Property` and `Environment Variable` setup

Client's do NOT need to supply the endPoint or authentication as part of instantiating the
_BitbucketClient_ object. Instead one can supply them through system properties, environment
variables, or a combination of the 2. _System Properties_ will be searched first and if not
found we will attempt to query the _Environment Variables_. If neither turns up anything
_BitbucketClient_ object. Instead one can supply them through `System Properties`, `Environment
Variables`, or a combination of the 2. `System Properties` will be searched first and if not
found we will attempt to query the `Environment Variables`. If neither turns up anything
than anonymous access is assumed.

Setting the `endpoint` can be done with any of the following (searched in order):
Setting the `endpoint` can be done like so (searched in order):

- `bitbucket.rest.endpoint`
- `bitbucketRestEndpoint`
- `BITBUCKET_REST_ENDPOINT`
`System.setProperty("bitbucket.rest.endpoint", "http://my-bitbucket-instance:12345")`
`export BITBUCKET_REST_ENDPOINT=http://my-bitbucket-instance:12345`

Setting the `credentials` can be done with any of the following (searched in order):
Setting the `credentials`, which represents `Basic` authentication and is optionally Base64 encoded, can be done like so (searched in order):

- `bitbucket.rest.credentials`
- `bitbucketRestCredentials`
- `BITBUCKET_REST_CREDENTIALS`
`System.setProperty("bitbucket.rest.credentials", "username:password")`
`export BITBUCKET_REST_CREDENTIALS=username:password`

Setting the `token` can be done with any of the following (searched in order):
Setting the `token`, which represents `Bearer` authentication, can be done like so (searched in order):

- `bitbucket.rest.token`
- `bitbucketRestToken`
- `BITBUCKET_REST_TOKEN`
`System.setProperty("bitbucket.rest.token", "abcdefg1234567")`
`export BITBUCKET_REST_TOKEN=abcdefg1234567`

## Authentication
## On Overrides

Authentication/Credentials for `bitbucket-rest` can take 1 of 3 forms:
Because we are built on top of jclouds we can take advantage of overriding various internal _HTTP_ properties by
passing in a `Properties` object or, and in following with the spirit of this library, configuring them
through `System Properties` of `Environment Variables`. The properties a given client can configure can be
found [HERE](https://github.com/jclouds/jclouds/blob/master/core/src/main/java/org/jclouds/Constants.java).

- Colon delimited username and password: __admin:password__
- Base64 encoded username and password: __YWRtaW46cGFzc3dvcmQ=__
- Personal access token: __9DfK3AF9Jeke1O0dkKX5kDswps43FEDlf5Frkspma21M__

## Examples on how to build a _BitbucketClient_
When configuring through a `Properties` object you must pass in the keys exactly as they are named within jclouds:

When using `Basic` (e.g. username and password) authentication:
Properties props = new Properties();
props.setProperty("jclouds.so-timeout", "60000");
props.setProperty("jclouds.connection-timeout", "120000");

BitbucketClient client = BitbucketClient.builder()
.endPoint("http://127.0.0.1:7990") // Optional and can be sourced from system/env. Falls back to http://127.0.0.1:7990
.credentials("admin:password") // Optional and can be sourced from system/env.
.overrides(props)
.build();

Version version = client.api().systemApi().version();

When using `Bearer` (e.g. token) authentication:
When configuring through `System Properties` you must prepend the jclouds name with `bitbucket.rest.`:

System.setProperty("bitbucket.rest.jclouds.so-timeout", "60000");
System.setProperty("bitbucket.rest.jclouds.connection-timeout", "120000");

BitbucketClient client = BitbucketClient.builder()
.endPoint("http://127.0.0.1:7990") // Optional and can be sourced from system/env. Falls back to http://127.0.0.1:7990
.token("123456789abcdef") // Optional and can be sourced from system/env.
.build();

Version version = client.api().systemApi().version();

When using `Anonymous` authentication or sourcing from system/environment (as described above):
When configuring through `Environment Variables` you must CAPITALIZE all characters,
replace any `.` with `_`, and prepend the jclouds name with `BITBUCKET_REST_`:

export BITBUCKET_REST_JCLOUDS_SO-TIMEOUT=60000
export BITBUCKET_REST_JCLOUDS_CONNECTION-TIMEOUT=120000

BitbucketClient client = BitbucketClient.builder()
.endPoint("http://127.0.0.1:7990") // Optional and can be sourced from system/env. Falls back to http://127.0.0.1:7990
.build();

Version version = client.api().systemApi().version();

It should be noted that when using this feature a merge happens behind the scenes between all
possible ways one can pass in _overrides_. Meaning if you pass in a `Properties` object, and
there are `System Properties` and `Environment Variables` set, then all 3 will be merged into
a single `Properties` object which in turn will be passed along to _jclouds_. When it comes to
precedence passed in `Properties` take precedence over `System Properties` which in turn
take precedence over `Environment Variables`.

## Understanding Error objects

When something pops server-side `bitbucket` will hand us back a list of [Error](https://github.com/cdancy/bitbucket-rest/blob/master/src/main/java/com/cdancy/bitbucket/rest/error/Error.java) objects. Instead of failing and/or throwing an exception at runtime we attach this List of `Error` objects
Expand Down
107 changes: 72 additions & 35 deletions src/main/java/com/cdancy/bitbucket/rest/BitbucketClient.java
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
import com.cdancy.bitbucket.rest.config.BitbucketAuthenticationModule;
import com.google.common.collect.Lists;
import java.util.Objects;
import java.util.Properties;
import org.jclouds.ContextBuilder;
import org.jclouds.javax.annotation.Nullable;

Expand All @@ -29,15 +30,14 @@ public final class BitbucketClient {
private final String endPoint;
private final BitbucketAuthentication credentials;
private final BitbucketApi bitbucketApi;
private final Properties overrides;

/**
* Create a BitbucketClient inferring endpoint and authentication from
* environment and system properties.
*/
public BitbucketClient() {
this.endPoint = BitbucketUtils.inferEndpoint();
this.credentials = BitbucketUtils.inferCredentials();
this.bitbucketApi = createApi(this.endPoint(), credentials);
this(null, null, null);
}

/**
Expand All @@ -46,13 +46,8 @@ public BitbucketClient() {
*
* @param endPoint URL of Bitbucket instance
*/
@Deprecated //@Nullable annotation will be removed in 2.x releases
public BitbucketClient(@Nullable final String endPoint) {
this.endPoint = endPoint != null
? endPoint
: BitbucketUtils.inferEndpoint();
this.credentials = BitbucketUtils.inferCredentials();
this.bitbucketApi = createApi(this.endPoint(), credentials);
this(endPoint, null, null);
}

/**
Expand All @@ -63,54 +58,84 @@ public BitbucketClient(@Nullable final String endPoint) {
*/
@Deprecated //Constructor will be deleted in favor of 'String, BitbucketCredentials' version in 2.x releases
public BitbucketClient(@Nullable final String endPoint, @Nullable final String basicCredentials) {
this.endPoint = endPoint != null
? endPoint
: BitbucketUtils.inferEndpoint();
this.credentials = basicCredentials != null
? BitbucketAuthentication.builder().credentials(basicCredentials).build()
: BitbucketUtils.inferCredentials();
this.bitbucketApi = createApi(this.endPoint(), credentials);
this(endPoint,
basicCredentials != null
? BitbucketAuthentication.builder().credentials(basicCredentials).build()
: null,
null);
}

/**
* Create an BitbucketClient using the passed in endpoint and BitbucketCredentials instance.
* Create an BitbucketClient. If any of the passed in variables are null we
* will query System Properties and Environment Variables, in order, to
* search for values that may be set in a devops/CI fashion. The only
* difference is the `overrides` which gets merged, but takes precedence,
* with those System Properties and Environment Variables found.
*
* @param endPoint URL of Bitbucket instance
* @param credentials Credentials used to connect to Bitbucket instance.
* @param endPoint URL of Bitbucket instance.
* @param authentication authentication used to connect to Bitbucket instance.
* @param overrides jclouds Properties to override defaults when creating a new BitbucketApi.
*/
public BitbucketClient(final String endPoint, final BitbucketAuthentication credentials) {
this.endPoint = endPoint;
this.credentials = credentials;
this.bitbucketApi = createApi(endPoint, Objects.requireNonNull(credentials));
public BitbucketClient(@Nullable final String endPoint,
@Nullable final BitbucketAuthentication authentication,
@Nullable final Properties overrides) {
this.endPoint = endPoint != null
? endPoint
: BitbucketUtils.inferEndpoint();
this.credentials = authentication != null
? authentication
: BitbucketUtils.inferAuthentication();
this.overrides = mergeOverrides(overrides);
this.bitbucketApi = createApi(this.endPoint, Objects.requireNonNull(this.credentials), this.overrides);
}

private BitbucketApi createApi(final String endPoint, final BitbucketAuthentication authentication) {
private BitbucketApi createApi(final String endPoint, final BitbucketAuthentication authentication, final Properties overrides) {
return ContextBuilder
.newBuilder(new BitbucketApiMetadata.Builder().build())
.endpoint(endPoint)
.modules(Lists.newArrayList(new BitbucketAuthenticationModule(authentication)))
.overrides(overrides)
.buildApi(BitbucketApi.class);
}

/**
* Query System Properties and Environment Variables for overrides and merge
* the potentially passed in overrides with those.
*
* @param possibleOverrides Optional passed in overrides.
* @return Properties object.
*/
private Properties mergeOverrides(final Properties possibleOverrides) {
final Properties inferOverrides = BitbucketUtils.inferOverrides();
if (possibleOverrides != null) {
inferOverrides.putAll(possibleOverrides);
}
return inferOverrides;
}

public String endPoint() {
return this.endPoint;
}

@Deprecated
public String credentials() {
return authValue();
return this.authValue();
}

public Properties overrides() {
return this.overrides;
}

public String authValue() {
return credentials.authValue();
return this.credentials.authValue();
}

public AuthenticationType authType() {
return credentials.authType();
return this.credentials.authType();
}

public BitbucketApi api() {
return bitbucketApi;
return this.bitbucketApi;
}

public static Builder builder() {
Expand All @@ -121,6 +146,7 @@ public static class Builder {

private String endPoint;
private BitbucketAuthentication.Builder authBuilder;
private Properties overrides;

/**
* Define the base endpoint to connect to.
Expand Down Expand Up @@ -148,7 +174,7 @@ public Builder credentials(final String optionallyBase64EncodedCredentials) {

/**
* Optional token to use for authentication.
*
*
* @param token authentication token.
* @return this Builder.
*/
Expand All @@ -158,22 +184,33 @@ public Builder token(final String token) {
return this;
}

/**
* Optional jclouds Properties to override. What can be overridden can
* be found here:
*
* <p>https://github.com/jclouds/jclouds/blob/master/core/src/main/java/org/jclouds/Constants.java
*
* @param overrides optional jclouds Properties to override.
* @return this Builder.
*/
public Builder overrides(final Properties overrides) {
this.overrides = overrides;
return this;
}

/**
* Build an instance of BitbucketClient.
*
* @return BitbucketClient
*/
public BitbucketClient build() {

// 1.) Use passed-in endpoint or attempt to infer one from system/environment.
final String foundEndpoint = (endPoint != null) ? endPoint : BitbucketUtils.inferEndpoint();

// 2.) Use passed-in credentials or attempt to infer them from system/environment.
// 1.) If user passed in some auth use/build that.
final BitbucketAuthentication authentication = authBuilder != null
? authBuilder.build()
: BitbucketUtils.inferCredentials();
: null;

return new BitbucketClient(foundEndpoint, authentication);
return new BitbucketClient(endPoint, authentication, overrides);
}
}
}
29 changes: 15 additions & 14 deletions src/main/java/com/cdancy/bitbucket/rest/BitbucketConstants.java
Original file line number Diff line number Diff line change
Expand Up @@ -17,27 +17,28 @@

package com.cdancy.bitbucket.rest;

import java.util.Arrays;
import java.util.Collections;
import java.util.List;

/**
* Various constants that can be used in a global context.
*/
public class BitbucketConstants {

public static final List<String> ENDPOINT_PROPERTIES = Collections
.unmodifiableList(Arrays
.asList("bitbucket.rest.endpoint", "bitbucketRestEndpoint", "BITBUCKET_REST_ENDPOINT"));
public static final List<String> CREDENTIALS_PROPERTIES = Collections
.unmodifiableList(Arrays
.asList("bitbucket.rest.credentials", "bitbucketRestCredentials", "BITBUCKET_REST_CREDENTIALS"));
public static final List<String> TOKEN_PROPERTIES = Collections
.unmodifiableList(Arrays
.asList("bitbucket.rest.token", "bitbucketRestToken", "BITBUCKET_REST_TOKEN"));
public static final String ENDPOINT_SYSTEM_PROPERTY = "bitbucket.rest.endpoint";
public static final String ENDPOINT_ENVIRONMENT_VARIABLE = ENDPOINT_SYSTEM_PROPERTY.replaceAll("\\.", "_").toUpperCase();

public static final String CREDENTIALS_SYSTEM_PROPERTY = "bitbucket.rest.credentials";
public static final String CREDENTIALS_ENVIRONMENT_VARIABLE = CREDENTIALS_SYSTEM_PROPERTY.replaceAll("\\.", "_").toUpperCase();

public static final String TOKEN_SYSTEM_PROPERTY = "bitbucket.rest.token";
public static final String TOKEN_ENVIRONMENT_VARIABLE = TOKEN_SYSTEM_PROPERTY.replaceAll("\\.", "_").toUpperCase();

public static final String DEFAULT_ENDPOINT = "http://127.0.0.1:7990";


public static final String JCLOUDS_PROPERTY_ID = "jclouds.";
public static final String BITBUCKET_REST_PROPERTY_ID = "bitbucket.rest." + JCLOUDS_PROPERTY_ID;

public static final String JCLOUDS_VARIABLE_ID = "JCLOUDS_";
public static final String BITBUCKET_REST_VARIABLE_ID = "BITBUCKET_REST_" + JCLOUDS_VARIABLE_ID;

protected BitbucketConstants() {
throw new UnsupportedOperationException("Purposefully not implemented");
}
Expand Down
Loading