Skip to content

Commit

Permalink
Enhance expense jar category with name
Browse files Browse the repository at this point in the history
  • Loading branch information
semotpan committed Aug 15, 2024
1 parent f931e9a commit fc26aca
Show file tree
Hide file tree
Showing 11 changed files with 40 additions and 24 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -86,7 +86,7 @@ private JarCategoriesCommand toCommand(JarCategoryModificationResource resource)
var categoryToAdds = resource.getCategories()
.stream()
.filter(Objects::nonNull) // avoid null categoryToAdd
.map(categoryToAdd -> new JarCategoryToAddOrRemove(categoryToAdd.getCategoryId(), categoryToAdd.getToAdd()))
.map(categoryToAdd -> new JarCategoryToAddOrRemove(categoryToAdd.getCategoryId(), categoryToAdd.getCategoryName(), categoryToAdd.getToAdd()))
.toList();
return new JarCategoriesCommand(categoryToAdds);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -86,10 +86,10 @@ private Either<Failure, List<JarExpenseCategory>> validate(UUID planId, UUID jar
private List<JarExpenseCategory> filterToCreate(Jar jar, JarCategoriesCommand command) {
return command.categories().stream()
.filter(JarCategoryToAddOrRemove::toAdd)
.map(JarCategoryToAddOrRemove::categoryId)
.filter(category -> !existsByJarIdAndCategoryId(jar.getId(), category))
.filter(category -> !existsByJarIdAndCategoryId(jar.getId(), category.categoryId()))
.map(category -> JarExpenseCategory.builder()
.categoryId(new CategoryIdentifier(category))
.categoryId(new CategoryIdentifier(category.categoryId()))
.categoryName(category.categoryName())
.jar(jar)
.build())
.toList();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ record JarCategoriesCommand(List<JarCategoryToAddOrRemove> categories) {
public static final String CATEGORIES_JAR_FIELD = "categories";
}

record JarCategoryToAddOrRemove(UUID categoryId, Boolean toAdd) {
record JarCategoryToAddOrRemove(UUID categoryId, String categoryName, Boolean toAdd) {

/**
* Gets whether the category is checked or not.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@
import io.vavr.control.Validation;

import java.util.List;
import java.util.Objects;
import java.util.stream.Collectors;

import static io.myfinbox.shared.Failure.FieldViolation;
Expand All @@ -13,6 +12,7 @@
import static io.vavr.API.Invalid;
import static io.vavr.API.Valid;
import static java.util.Objects.isNull;
import static org.apache.commons.lang3.StringUtils.isBlank;

final class CategoriesJarCommandValidator {

Expand All @@ -26,13 +26,12 @@ Validation<FieldViolation, List<JarCategoryToAddOrRemove>> validate(List<JarCate
}

var anyNull = categories.stream()
.map(JarCategoryToAddOrRemove::categoryId)
.anyMatch(Objects::isNull);
.anyMatch(category -> isNull(category.categoryId()) || isBlank(category.categoryName()));

if (anyNull) {
return Invalid(Failure.FieldViolation.builder()
.field(CATEGORIES_JAR_FIELD)
.message("Null categoryId not allowed.")
.message("Null categoryId or blank categoryName not allowed.")
.rejectedValue(categories)
.build());
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
import java.util.ArrayList;
import java.util.List;

import static io.myfinbox.shared.Guards.notNull;
import static io.myfinbox.shared.Guards.*;
import static jakarta.persistence.CascadeType.ALL;
import static jakarta.persistence.GenerationType.SEQUENCE;
import static lombok.AccessLevel.PACKAGE;
Expand All @@ -21,8 +21,8 @@
public class JarExpenseCategory {

@Id
@GeneratedValue(strategy = SEQUENCE, generator = "jer_seq_id")
@SequenceGenerator(name = "jer_seq_id", sequenceName = "jer_seq_id", allocationSize = 1)
@GeneratedValue(strategy = SEQUENCE, generator = "sjec_seq_id")
@SequenceGenerator(name = "sjec_seq_id", sequenceName = "sjec_seq_id", allocationSize = 1)
// https://vladmihalcea.com/migrate-hilo-hibernate-pooled/
private Long id;

Expand All @@ -32,6 +32,9 @@ public class JarExpenseCategory {
@AttributeOverride(name = "id", column = @Column(name = "category_id"))
private final CategoryIdentifier categoryId;

@Column(name = "category_name", nullable = false)
private String categoryName;

@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "jar_id", referencedColumnName = "id", nullable = false)
private final Jar jar;
Expand All @@ -40,9 +43,10 @@ public class JarExpenseCategory {
private List<ExpenseRecord> expenseRecords = new ArrayList<>();

@Builder
public JarExpenseCategory(Jar jar, CategoryIdentifier categoryId) {
public JarExpenseCategory(Jar jar, CategoryIdentifier categoryId, String categoryName) {
this.jar = notNull(jar, "jar cannot be null.");
this.categoryId = notNull(categoryId, "categoryId cannot be null.");
this.categoryName = notBlank(categoryName, "categoryName cannot be null.");
this.creationTimestamp = Instant.now();
}
}
Original file line number Diff line number Diff line change
@@ -1,15 +1,16 @@
CREATE SEQUENCE IF NOT EXISTS sjec_seq_id START 1 INCREMENT 1;

CREATE TABLE IF NOT EXISTS spending_jar_expense_category
(
id BIGSERIAL PRIMARY KEY,
jar_id UUID NOT NULL,
category_id UUID NOT NULL,
creation_timestamp TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
id BIGINT PRIMARY KEY DEFAULT nextval('sjec_seq_id'),
jar_id UUID NOT NULL,
category_id UUID NOT NULL,
category_name VARCHAR(100) NOT NULL,
creation_timestamp TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
FOREIGN KEY (jar_id) REFERENCES spending_jars (id)
)
;

CREATE UNIQUE INDEX IF NOT EXISTS unique_spending_jar_expense_category_id_idx ON spending_jar_expense_category (jar_id, category_id);

CREATE INDEX IF NOT EXISTS search_expense_category_jar_id_idx ON spending_jar_expense_category (jar_id);

CREATE SEQUENCE IF NOT EXISTS sjec_seq_id START 1 INCREMENT 1;
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
CREATE SEQUENCE IF NOT EXISTS jer_seq_id START 1 INCREMENT 1;

CREATE TABLE IF NOT EXISTS jar_expense_record
(
id BIGSERIAL PRIMARY KEY,
id BIGINT PRIMARY KEY DEFAULT nextval('jer_seq_id'),
expense_id UUID NOT NULL,
creation_timestamp TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
category_id UUID NOT NULL,
Expand All @@ -14,5 +16,3 @@ CREATE TABLE IF NOT EXISTS jar_expense_record
;

CREATE INDEX IF NOT EXISTS search_jar_expense_category_jar_id_idx ON jar_expense_record (category_id, expense_date, jar_expense_category_id);

CREATE SEQUENCE IF NOT EXISTS jer_seq_id START 1 INCREMENT 1;
4 changes: 4 additions & 0 deletions server/src/main/resources/openapi.yml
Original file line number Diff line number Diff line change
Expand Up @@ -287,6 +287,10 @@ components:
type: string
format: uuid
description: Unique identifier for the expense category.
categoryName:
type: string
example: Clothing
description: The expense category name.
toAdd:
type: boolean
default: true
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ class DataSamples {
static expenseDate = "2024-03-23"
static String jarDescription = "Necessities spending: Rent, Food, Bills etc."
static String planDescription = "My basic plan for tracking expenses"
static String categoryName = 'Clothing'

static AMOUNT = [
amount : amount,
Expand Down Expand Up @@ -125,6 +126,7 @@ class DataSamples {

static JAR_CATEGORY_TO_ADD_OR_REMOVE = [
categoryId: jarCategoryId,
categoryName: categoryName,
toAdd : true
]

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,9 @@ class AddOrRemoveJarCategoryServiceSpec extends Specification {
categories | failMessage
null | 'At least one category must be provided.'
[] | 'At least one category must be provided.'
[newSampleJarCategoryToAddAsMap(categoryId: null)] | 'Null categoryId not allowed.'
[newSampleJarCategoryToAddAsMap(categoryId: null)] | 'Null categoryId or blank categoryName not allowed.'
[newSampleJarCategoryToAddAsMap(categoryName: null)] | 'Null categoryId or blank categoryName not allowed.'
[newSampleJarCategoryToAddAsMap(categoryName: '')] | 'Null categoryId or blank categoryName not allowed.'
[newSampleJarCategoryToAddAsMap(), newSampleJarCategoryToAddAsMap()] | 'Duplicate category ids provided.'
}

Expand Down Expand Up @@ -85,7 +87,7 @@ class AddOrRemoveJarCategoryServiceSpec extends Specification {
given: 'a new command'
def command = newSampleJarCategoriesCommand(categories: [newSampleJarCategoryToAddAsMap()])

1 * jars.findByIdAndPlanId(_ as JarIdentifier, _ as PlanIdentifier) >>Optional.empty()
1 * jars.findByIdAndPlanId(_ as JarIdentifier, _ as PlanIdentifier) >> Optional.empty()

when: 'attempting to add or remove categories for null jar id'
def either = service.addOrRemove(UUID.randomUUID(), UUID.randomUUID(), command)
Expand Down Expand Up @@ -116,6 +118,7 @@ class AddOrRemoveJarCategoryServiceSpec extends Specification {
and: 'the added categories are present'
assert either.get().size() == 1
assert either.get().getFirst().getCategoryId() == new CategoryIdentifier(UUID.fromString(jarCategoryId))
assert either.get().getFirst().getCategoryName() == categoryName
assert either.get().getFirst().getJar() == newSampleJar()
assert either.get().getFirst().getCreationTimestamp() != null

Expand Down
Original file line number Diff line number Diff line change
@@ -1,12 +1,15 @@
INSERT INTO server.spending_jar_expense_category(id,
jar_id,
category_id,
category_name,
creation_timestamp)
VALUES (1,
'e2709aa2-7907-4f78-98b6-0f36a0c1b5ca',
'e2709aa2-7907-4f78-98b6-0f36a0c1b5ca',
'Clothing',
'2024-03-23T10:00:04.224870Z'),
(2,
'e2709aa2-7907-4f78-98b6-0f36a0c1b5ca',
'ee0a4cdc-84f0-4f81-8aea-224dad4915e7',
'Clothing',
'2024-03-23T10:00:04.224870Z');

0 comments on commit fc26aca

Please sign in to comment.