Merge branch 'develop' into stable
This commit is contained in:
@@ -98,9 +98,9 @@
|
||||
<mainClass>com.inteligr8.buxfer.CLI</mainClass>
|
||||
</manifest>
|
||||
</archive>
|
||||
<descriptorRefs>
|
||||
<descriptorRef>jar-with-dependencies</descriptorRef>
|
||||
</descriptorRefs>
|
||||
<descriptors>
|
||||
<descriptor>${basedir}/src/assembly/resources/assembly.xml</descriptor>
|
||||
</descriptors>
|
||||
</configuration>
|
||||
</execution>
|
||||
</executions>
|
||||
|
27
buxfer-cli/src/assembly/resources/assembly.xml
Normal file
27
buxfer-cli/src/assembly/resources/assembly.xml
Normal file
@@ -0,0 +1,27 @@
|
||||
<assembly xmlns="http://maven.apache.org/ASSEMBLY/2.0.0"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/ASSEMBLY/2.0.0 http://maven.apache.org/xsd/assembly-2.0.0.xsd">
|
||||
<id>full</id>
|
||||
|
||||
<formats>
|
||||
<format>jar</format>
|
||||
</formats>
|
||||
|
||||
<includeBaseDirectory>false</includeBaseDirectory>
|
||||
|
||||
<dependencySets>
|
||||
<dependencySet>
|
||||
<outputDirectory>/</outputDirectory>
|
||||
<useProjectArtifact>true</useProjectArtifact>
|
||||
<unpack>true</unpack>
|
||||
<scope>runtime</scope>
|
||||
</dependencySet>
|
||||
</dependencySets>
|
||||
|
||||
<containerDescriptorHandlers>
|
||||
<containerDescriptorHandler>
|
||||
<handlerName>metaInf-services</handlerName>
|
||||
</containerDescriptorHandler>
|
||||
</containerDescriptorHandlers>
|
||||
|
||||
</assembly>
|
@@ -78,6 +78,7 @@ public class InvestNormalizeCLI {
|
||||
"Investment / Security / " + ptx.getSecuritySymbol(), null);
|
||||
return 1;
|
||||
} catch (IllegalArgumentException iae) {
|
||||
logger.trace("failed to parse: " + iae.getMessage(), iae);
|
||||
// try another
|
||||
}
|
||||
}
|
||||
|
@@ -1,5 +1,6 @@
|
||||
package com.inteligr8.buxfer;
|
||||
|
||||
import java.text.NumberFormat;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
@@ -50,14 +51,14 @@ public class SofiInvestTransaction implements ParsedTransaction {
|
||||
this.throttle();
|
||||
|
||||
this.securities = this.estimateSecurites();
|
||||
this.perSecurityPrice = this.estimatePrice(securities);
|
||||
this.perSecurityPrice = this.estimatePrice(this.securities);
|
||||
|
||||
String action = Type.Buy.equals(type) ? "Bought" : "Sold";
|
||||
this.description = new StringBuilder()
|
||||
.append(action).append(' ')
|
||||
.append(this.securities == null ? "NaN" : NumberFormatFactory.getSecuritiesFormatter().format(this.securities)).append(' ')
|
||||
.append(this.getRange(NumberFormatFactory.getSecuritiesFormatter(), this.securities)).append(' ')
|
||||
.append(this.symbol).append(" @ ")
|
||||
.append(this.perSecurityPrice == null ? "NaN" : NumberFormatFactory.getPriceFormatter().format(this.perSecurityPrice)).toString();
|
||||
.append(this.getRange(NumberFormatFactory.getPriceFormatter(), this.perSecurityPrice)).toString();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -159,5 +160,15 @@ public class SofiInvestTransaction implements ParsedTransaction {
|
||||
else if (this.perSecurityPrice.isPoint()) return this.perSecurityPrice.getFrom().doubleValue();
|
||||
else return this.perSecurityPrice.getMidpoint();
|
||||
}
|
||||
|
||||
private String getRange(NumberFormat formatter, NumericRange<? extends Number> range) {
|
||||
if (range == null) {
|
||||
return "NaN";
|
||||
} else if (range.isPoint()) {
|
||||
return formatter.format(range.getFrom().doubleValue());
|
||||
} else {
|
||||
return formatter.format(range.getFrom().doubleValue()) + "-" + formatter.format(range.getTo().doubleValue());
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
@@ -1,35 +0,0 @@
|
||||
package com.inteligr8.buxfer;
|
||||
|
||||
import org.apache.http.HttpResponse;
|
||||
import org.apache.http.client.HttpClient;
|
||||
import org.apache.http.client.methods.HttpUriRequest;
|
||||
import org.apache.http.client.methods.RequestBuilder;
|
||||
import org.apache.http.impl.client.DefaultRedirectStrategy;
|
||||
import org.apache.http.impl.client.HttpClientBuilder;
|
||||
|
||||
import com.inteligr8.rs.ClientConfiguration;
|
||||
|
||||
public abstract class ConditionalIT {
|
||||
|
||||
public abstract ClientConfiguration getConfiguration();
|
||||
|
||||
public boolean hostExists() {
|
||||
String uri = this.getConfiguration().getBaseUrl();
|
||||
|
||||
HttpUriRequest request = RequestBuilder.get()
|
||||
.setUri(uri)
|
||||
.build();
|
||||
|
||||
HttpClient client = HttpClientBuilder.create()
|
||||
.setRedirectStrategy(DefaultRedirectStrategy.INSTANCE)
|
||||
.build();
|
||||
|
||||
try {
|
||||
HttpResponse response = client.execute(request);
|
||||
return response.getStatusLine().getStatusCode() < 300;
|
||||
} catch (Exception e) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@@ -1,176 +0,0 @@
|
||||
package com.inteligr8.buxfer;
|
||||
|
||||
import java.time.LocalDate;
|
||||
import java.util.Collection;
|
||||
|
||||
import org.junit.jupiter.api.Assertions;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.condition.EnabledIf;
|
||||
|
||||
import com.inteligr8.buxfer.api.CommandApi;
|
||||
import com.inteligr8.buxfer.model.Account;
|
||||
import com.inteligr8.buxfer.model.ArrayResponse;
|
||||
import com.inteligr8.buxfer.model.BaseResponse;
|
||||
import com.inteligr8.buxfer.model.BaseResponse.Status;
|
||||
import com.inteligr8.buxfer.model.Budget;
|
||||
import com.inteligr8.buxfer.model.Item;
|
||||
import com.inteligr8.buxfer.model.NamedItem;
|
||||
import com.inteligr8.buxfer.model.Reminder;
|
||||
import com.inteligr8.buxfer.model.Tag;
|
||||
import com.inteligr8.buxfer.model.Transaction;
|
||||
import com.inteligr8.buxfer.model.TransactionsResponse;
|
||||
|
||||
public abstract class ConnectionClientIT extends ConditionalIT {
|
||||
|
||||
public abstract BuxferPublicRestApi getClient();
|
||||
|
||||
private CommandApi api;
|
||||
|
||||
public boolean fullTest() {
|
||||
return false;
|
||||
//return this.hostExists();
|
||||
}
|
||||
|
||||
@BeforeEach
|
||||
public void getApi() {
|
||||
this.api = this.getClient().getCommandApi();
|
||||
}
|
||||
|
||||
@Test
|
||||
@EnabledIf("hostExists")
|
||||
public void testTransactions() {
|
||||
TransactionsResponse response = this.api.getTransactions(
|
||||
LocalDate.of(2021, 12, 1),
|
||||
LocalDate.of(2021, 12, 31),
|
||||
null,
|
||||
1
|
||||
).getResponse();
|
||||
this.assertArrayNotEmpty(response);
|
||||
Assertions.assertTrue(response.getTotalItems() > 0L);
|
||||
this.assertItems(response.getItems());
|
||||
}
|
||||
|
||||
@Test
|
||||
@EnabledIf("hostExists")
|
||||
public void testAccounts() {
|
||||
ArrayResponse<? extends Item> response = this.api.getAccounts().getResponse();
|
||||
this.assertArrayNotEmpty(response);
|
||||
this.assertItems(response.getItems());
|
||||
}
|
||||
|
||||
@Test
|
||||
@EnabledIf("hostExists")
|
||||
public void testTags() {
|
||||
ArrayResponse<? extends Item> response = this.api.getTags().getResponse();
|
||||
this.assertArrayNotEmpty(response);
|
||||
this.assertItems(response.getItems());
|
||||
}
|
||||
|
||||
@Test
|
||||
@EnabledIf("hostExists")
|
||||
public void testBudgets() {
|
||||
ArrayResponse<? extends Item> response = this.api.getBudgets().getResponse();
|
||||
this.assertArrayNotEmpty(response);
|
||||
this.assertItems(response.getItems());
|
||||
}
|
||||
|
||||
@Test
|
||||
@EnabledIf("hostExists")
|
||||
public void testReminders() {
|
||||
ArrayResponse<? extends Item> response = this.api.getReminders().getResponse();
|
||||
this.assertArrayNotEmpty(response);
|
||||
this.assertItems(response.getItems());
|
||||
}
|
||||
|
||||
@Test
|
||||
@EnabledIf("fullTest")
|
||||
public void testGroups() {
|
||||
ArrayResponse<? extends Item> response = this.api.getGroups().getResponse();
|
||||
this.assertArrayEmpty(response);
|
||||
}
|
||||
|
||||
@Test
|
||||
@EnabledIf("fullTest")
|
||||
public void testContacts() {
|
||||
ArrayResponse<? extends Item> response = this.api.getContacts().getResponse();
|
||||
this.assertArrayEmpty(response);
|
||||
}
|
||||
|
||||
private void assertOk(BaseResponse response) {
|
||||
Assertions.assertNotNull(response);
|
||||
Assertions.assertEquals(Status.OK, response.getStatus());
|
||||
Assertions.assertNull(response.getError());
|
||||
}
|
||||
|
||||
private void assertArrayEmpty(ArrayResponse<?> response) {
|
||||
this.assertOk(response);
|
||||
Assertions.assertNotNull(response.getItems());
|
||||
Assertions.assertTrue(response.getItems().isEmpty());
|
||||
}
|
||||
|
||||
private void assertArrayNotEmpty(ArrayResponse<?> response) {
|
||||
this.assertOk(response);
|
||||
Assertions.assertNotNull(response.getItems());
|
||||
Assertions.assertFalse(response.getItems().isEmpty());
|
||||
}
|
||||
|
||||
private void assertItem(Item item) {
|
||||
Assertions.assertTrue(item.getId() > 0);
|
||||
}
|
||||
|
||||
private void assertNamedItem(NamedItem item) {
|
||||
this.assertItem(item);
|
||||
Assertions.assertNotNull(item.getName());
|
||||
Assertions.assertNotEquals(0, item.getName().length());
|
||||
}
|
||||
|
||||
private void assertSpecificItem(Item item) {
|
||||
if (item instanceof Transaction) {
|
||||
this.assertTransaction((Transaction)item);
|
||||
} else if (item instanceof Budget) {
|
||||
this.assertBudget((Budget)item);
|
||||
} else if (item instanceof Reminder) {
|
||||
this.assertReminder((Reminder)item);
|
||||
} else if (item instanceof Tag) {
|
||||
this.assertTag((Tag)item);
|
||||
} else if (item instanceof Account) {
|
||||
this.assertAccount((Account)item);
|
||||
}
|
||||
}
|
||||
|
||||
private void assertTransaction(Transaction item) {
|
||||
this.assertItem(item);
|
||||
Assertions.assertNotNull(item.getType());
|
||||
Assertions.assertNotNull(item.getAmount());
|
||||
Assertions.assertNotNull(item.getDate());
|
||||
}
|
||||
|
||||
private void assertAccount(Account item) {
|
||||
this.assertNamedItem(item);
|
||||
Assertions.assertNotNull(item.getBank());
|
||||
Assertions.assertNotEquals(0, item.getBank().length());
|
||||
Assertions.assertNotNull(item.getBalance());
|
||||
}
|
||||
|
||||
private void assertTag(Tag item) {
|
||||
this.assertNamedItem(item);
|
||||
}
|
||||
|
||||
private void assertBudget(Budget item) {
|
||||
this.assertNamedItem(item);
|
||||
}
|
||||
|
||||
private void assertReminder(Reminder item) {
|
||||
this.assertNamedItem(item);
|
||||
Assertions.assertNotNull(item.getStartDate());
|
||||
Assertions.assertTrue(item.getStartDate().isAfter(LocalDate.of(2010, 1, 1)));
|
||||
}
|
||||
|
||||
private void assertItems(Collection<? extends Item> items) {
|
||||
for (Item item : items) {
|
||||
this.assertSpecificItem(item);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@@ -1,6 +1,7 @@
|
||||
package com.inteligr8.buxfer;
|
||||
|
||||
import com.inteligr8.buxfer.api.CommandApi;
|
||||
import com.inteligr8.buxfer.api.SecurityApi;
|
||||
|
||||
/**
|
||||
* This interface consolidates the JAX-RS APIs available in the Buxfer Public
|
||||
@@ -10,6 +11,8 @@ import com.inteligr8.buxfer.api.CommandApi;
|
||||
*/
|
||||
public interface BuxferPublicRestApi {
|
||||
|
||||
SecurityApi getSecurityApi();
|
||||
|
||||
CommandApi getCommandApi();
|
||||
|
||||
}
|
||||
|
@@ -1,7 +1,10 @@
|
||||
package com.inteligr8.buxfer.model;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonIgnore;
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
import com.fasterxml.jackson.annotation.JsonRootName;
|
||||
|
||||
@JsonRootName("response")
|
||||
public class BaseResponse {
|
||||
|
||||
public enum Status {
|
||||
@@ -13,7 +16,10 @@ public class BaseResponse {
|
||||
private Status status;
|
||||
|
||||
@JsonProperty(value = "error_description", required = false)
|
||||
private String error;
|
||||
private String errorDesc;
|
||||
|
||||
@JsonProperty(required = false)
|
||||
private Error error;
|
||||
|
||||
|
||||
|
||||
@@ -25,12 +31,29 @@ public class BaseResponse {
|
||||
this.status = status;
|
||||
}
|
||||
|
||||
public String getError() {
|
||||
public String getErrorDescription() {
|
||||
return this.errorDesc;
|
||||
}
|
||||
|
||||
public void setErrorDescription(String errorDesc) {
|
||||
this.errorDesc = errorDesc;
|
||||
}
|
||||
|
||||
public Error getError() {
|
||||
return this.error;
|
||||
}
|
||||
|
||||
public void setError(String error) {
|
||||
public void setError(Error error) {
|
||||
this.error = error;
|
||||
}
|
||||
|
||||
@JsonIgnore
|
||||
public String getErrorDescriptionOrMessage() {
|
||||
if (this.error != null && this.error.getMessage() != null) {
|
||||
return this.error.getMessage();
|
||||
} else {
|
||||
return this.errorDesc;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
@@ -0,0 +1,31 @@
|
||||
package com.inteligr8.buxfer.model;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
|
||||
public class Error {
|
||||
|
||||
@JsonProperty
|
||||
private String type;
|
||||
|
||||
@JsonProperty
|
||||
private String message;
|
||||
|
||||
|
||||
|
||||
public String getType() {
|
||||
return this.type;
|
||||
}
|
||||
|
||||
public void setType(String type) {
|
||||
this.type = type;
|
||||
}
|
||||
|
||||
public String getMessage() {
|
||||
return this.message;
|
||||
}
|
||||
|
||||
public void setMessage(String message) {
|
||||
this.message = message;
|
||||
}
|
||||
|
||||
}
|
@@ -9,15 +9,19 @@ import javax.ws.rs.client.Entity;
|
||||
import javax.ws.rs.core.Form;
|
||||
import javax.ws.rs.core.GenericType;
|
||||
import javax.ws.rs.core.MediaType;
|
||||
import javax.ws.rs.core.Response.Status;
|
||||
import javax.ws.rs.core.UriBuilder;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import com.inteligr8.buxfer.model.Response;
|
||||
import com.inteligr8.buxfer.model.TokenResponse;
|
||||
import com.inteligr8.rs.AuthorizationFilter;
|
||||
|
||||
public class BuxferAuthorizationFilter implements AuthorizationFilter {
|
||||
|
||||
private final Logger logger = LoggerFactory.getLogger(this.getClass());
|
||||
|
||||
private String email;
|
||||
private String password;
|
||||
private String token;
|
||||
@@ -43,11 +47,13 @@ public class BuxferAuthorizationFilter implements AuthorizationFilter {
|
||||
|
||||
protected void requestToken(ClientRequestContext requestContext) {
|
||||
UriBuilder loginUri = UriBuilder.fromUri(requestContext.getUri())
|
||||
.replacePath("/api/login");
|
||||
.replacePath("/api/login")
|
||||
.replaceQuery(null);
|
||||
|
||||
Form form = new Form();
|
||||
form.param("email", this.email);
|
||||
form.param("password", this.password);
|
||||
Entity<Form> formEntity = Entity.form(form);
|
||||
|
||||
GenericType<Response<TokenResponse>> responseType = new GenericType<Response<TokenResponse>>() {};
|
||||
|
||||
@@ -56,16 +62,16 @@ public class BuxferAuthorizationFilter implements AuthorizationFilter {
|
||||
.target(loginUri)
|
||||
.request()
|
||||
.accept(MediaType.APPLICATION_JSON)
|
||||
.post(Entity.form(form), responseType)
|
||||
.getResponse();
|
||||
.post(formEntity, responseType).getResponse();
|
||||
|
||||
if (!com.inteligr8.buxfer.model.BaseResponse.Status.OK.equals(response.getStatus()))
|
||||
throw new WebApplicationException(response.getError(), Status.UNAUTHORIZED.getStatusCode());
|
||||
throw new NotAuthorizedException(response.getErrorDescriptionOrMessage(), response);
|
||||
this.token = response.getToken();
|
||||
this.logger.debug("received access token: {} = > {}", this.email, this.token);
|
||||
} catch (NotAuthorizedException nae) {
|
||||
throw nae;
|
||||
} catch (WebApplicationException wae) {
|
||||
throw new NotAuthorizedException("Indirect due to non-authorization failure: " + wae.getMessage(), wae);
|
||||
throw new NotAuthorizedException("Indirect due to non-authorization failure: [" + wae.getResponse().getStatus() + "]", wae);
|
||||
}
|
||||
}
|
||||
|
||||
|
@@ -1,6 +1,7 @@
|
||||
package com.inteligr8.buxfer;
|
||||
|
||||
import com.inteligr8.buxfer.api.CommandApi;
|
||||
import com.inteligr8.buxfer.api.SecurityApi;
|
||||
import com.inteligr8.rs.Client;
|
||||
|
||||
/**
|
||||
@@ -21,6 +22,10 @@ public class BuxferPublicRestApiImpl implements BuxferPublicRestApi {
|
||||
return this.client.getApi(apiClass);
|
||||
}
|
||||
|
||||
public SecurityApi getSecurityApi() {
|
||||
return this.client.getApi(SecurityApi.class);
|
||||
}
|
||||
|
||||
public CommandApi getCommandApi() {
|
||||
return this.client.getApi(CommandApi.class);
|
||||
}
|
||||
|
11
buxfer-public-rest-client/src/test/vscode/test.http
Normal file
11
buxfer-public-rest-client/src/test/vscode/test.http
Normal file
@@ -0,0 +1,11 @@
|
||||
|
||||
# Authentication
|
||||
# @name auth
|
||||
POST https://www.buxfer.com/api/login
|
||||
Content-type: application/x-www-form-urlencoded
|
||||
|
||||
email=bmlong137@gmail.com
|
||||
&password=mnM9zh,_n/-w
|
||||
|
||||
@token = {{auth.response.body.response.token}}
|
||||
|
Reference in New Issue
Block a user