How to return result from dto? - spring-boot

I'm trying to map entities to dtos and return a result, but it return as null for some reason. What am I doing wrong?
#PutMapping("/contract")
fun getContract(#RequestParam itemId: Int, #RequestParam id: Int): InventoryItemDTO {
var item: InventoryItem = inventoryItemService.getInventoryItemById(itemId)
val i = convertToItemDTO(item)
var con = contractService.getContractById(id)
val c = convertToContractDTO(con)
item.contract = con
i.contractDTO = c
//inventoryItemService.saveInventoryItem(i)
return i
}
fun convertToItemDTO(item: InventoryItem): InventoryItemDTO {
val itemDTO = modelMapper.map(item, InventoryItemDTO::class.java)
itemDTO.contractDTO = convertToContractDTO(item.contract!!)
return itemDTO
}
fun convertToContractDTO(contract: Contract): ContractDTO {
return modelMapper.map(contract, ContractDTO::class.java)
}

Related

Kotlin MVVM, How to get the latest value from Entity in ViewModel?

I have created an app where I try to insert a record with the latest order number increased by one.
The main function is triggered from Activity, however, the whole process is in my ViewModel.
Issue no 1, After I insert a new record the order by number is not updated.
Issue no 2, When I insert first record the order by number is null, for that reason I am checking for null and setting the value to 0.
My goal here is to get the latest order_by number from Entity in my ViewModel, increased by 1 and add that new number to my new record using fun addTestData(..).
Entity:
#Entity(tableName = "word_table")
data class Word(
#ColumnInfo(name = "id") val id: Int,
#ColumnInfo(name = "word") val word: String,
#ColumnInfo(name = "order_by") val orderBy: Int
Dao:
#Query("SELECT order_by FROM word_table ORDER BY order_by DESC LIMIT 1")
suspend fun getHighestOrderId(): Int
Repository:
#Suppress("RedundantSuspendModifier")
#WorkerThread
suspend fun getHighestOrderId(): Int {
return wordDao.getHighestOrderId()
}
ViewModel:
private var _highestOrderId = MutableLiveData<Int>()
val highestOrderId: LiveData<Int> = _highestOrderId
fun getHighestOrderId() = viewModelScope.launch {
val highestOrderId = repository.getHighestOrderId()
_highestOrderId.postValue(highestOrderId)
}
fun addTestData(text: String) {
for (i in 0..1500) {
getHighestOrderId()
var highestNo = 0
val highestOrderId = highestOrderId.value
if (highestOrderId == null) {
highestNo = 0
} else {
highestNo = highestOrderId
}
val addNumber = highestNo + 1
val word2 = Word(0, text + "_" + addNumber,addNumber)
insertWord(word2)
}
}
Activity:
wordViewModel.addTestData(text)

How to crop image with known path based on user selection Android Studio (Ucrop or another library)

I've a question, how can i crop an image if i know the imagePath? I know the imagePath, because the user selects an image from the gallery that we provide.
private val getContent = registerForActivityResult(ActivityResultContracts.GetContent()){ uri ->
val inputUri = uri
val outputUri = File(filesDir,"croppedImage.jpg").toUri()
val listUri = listOf<Uri>(inputUri,outputUri)
cropImage.launch(listUri)
}
private val uCropContract = object: ActivityResultContract<List<Uri>,Uri>(){
override fun createIntent(context: Context, input: List<Uri>): Intent {
val inputUri = input[0]
val outputUri = input[1]
val uCrop = UCrop.of(inputUri, outputUri)
.withAspectRatio(5f,5f)
.withMaxResultSize(1080,1080)
return uCrop.getIntent(context)
}
override fun parseResult(resultCode: Int, intent: Intent?): Uri {
return UCrop.getOutput(intent!!)!!
}
}
private val cropImage = registerForActivityResult(uCropContract){ uri ->
binding.imvImageAdded.setImageURI(uri)
}

MockK no answer found for:

I'm trying to test a method in my Service Implementation with JUnit and MockK library.
PlanServiceFeatureImplTest.kt
#Test
fun `storeInstallmentPlan`(#MockK user: User) {
val debtId = 123L
val amount = BigDecimal.valueOf(1000)
val interval = Repeat.monthly
val firstPaymentDate = LocalDate.of(
2021, 11, 4
).atStartOfDay(Time.DEFAULT_TIME_ZONE).toOffsetDateTime()
val planDTO = InstallmentPlanDTO(
interval = interval,
firstPaymentDate = firstPaymentDate,
amount = amount,
installments = listOf()
)
val debt = Debt(
userId = 32L,
title = "debt1",
amount = amount,
category = DebtCategory.credit
)
val plan = InstallmentPlan(
id = 122L,
debtId = debtId,
interval = interval,
firstPaymentDate = firstPaymentDate,
amount = amount
)
val installment1 = Installment(
id = 34,
debtId = debtId,
recordId = 13
)
val installment2 = Installment(
id = 35,
debtId = debtId,
recordId = 14
)
val newStartDate = ZonedDateTime.parse("2021-10-05T00:00+02:00[Europe/Berlin]")
val newEndDate = ZonedDateTime.parse("2021-11-04T00:00+01:00[Europe/Berlin]")
val installments = listOf(
WalletRecord(
userId = debt.userId,
type = RecordType.debt_rate,
amount = plan.amount,
title = debt.title,
category = RecordCategory.debt_rate,
repeat = plan.interval,
startDate = newStartDate.toOffsetDateTime(),
endDate = newEndDate.toOffsetDateTime(),
)
)
val records = flowOf(
WalletRecord(
id = 43,
userId = debt.userId,
type = RecordType.debt_rate,
amount = plan.amount,
title = debt.title,
category = RecordCategory.debt_rate,
repeat = plan.interval,
startDate = newStartDate.toOffsetDateTime(),
endDate = newEndDate.toOffsetDateTime(),
)
)
every { user.tz } returns "Europe/Berlin"
coEvery { debtRepository.findById(debtId) } returns debt
coEvery { installmentPlanRepository.findByDebtId(debtId) } returns plan
coEvery {
installmentPlanRepository.save(
planDTO.copy(
amount = amount
).toEntity(
id = plan.id,
debtId = debtId
)
)
} returns InstallmentPlan(plan.id, debtId, interval, firstPaymentDate, amount )
coEvery { installmentRepository.findByDebtId(debtId) } returns flowOf(installment1, installment2)
coEvery {
installmentRepository.deleteAllById(listOf(installment1.id!!, installment2.id!!).asIterable())
} just Runs
coEvery { recordService.deleteAll(listOf(installment1.recordId, installment2.recordId)) } just Runs
coEvery { userService.findById(debt.userId) } returns user
coEvery { recordService.saveAll(installments) } returns records
coEvery {
installmentRepository.saveAll(
records.map {
Installment(
debtId = debtId,
recordId = it.id!!
)
}
).map { it.recordId }
} returns flowOf(43)
runBlocking { planService.storeInstallmentPlan(debtId, planDTO) }
}
PlanServiceFeatureImpl.kt
#Transactional
override suspend fun storeInstallmentPlan(debtId: Long, installmentPlanDTO: InstallmentPlanDTO): Flow<Long> {
val debt = debtRepository.findById(debtId) ?: throw NotFoundException("Could not find debt with id $debtId")
val installmentPlanId = installmentPlanRepository.findByDebtId(debtId)?.id
val minimumAmount =
BigDecimal.ONE.max(debt.amount.multiply(MINIMUM_INSTALLMENT_PERCENTAGE_OF_TOTAL_AMOUNT, MathContext.DECIMAL32))
.setScale(2, RoundingMode.HALF_UP)
.stripTrailingZeros()
val maximumAmount = debt.amount
val clampedAmount = maximumAmount.min(minimumAmount.max(installmentPlanDTO.amount))
val installmentPlan = installmentPlanDTO.copy(
amount = clampedAmount,
).toEntity(
id = installmentPlanId,
debtId = debtId,
)
installmentPlanRepository.save(installmentPlan)
// delete existing records associated with the installment plan
val existingInstallments = installmentRepository.findByDebtId(debtId).toList()
installmentRepository.deleteAllById(existingInstallments.map { it.id!! })
recordService.deleteAll(existingInstallments.map { it.recordId })
// calculate installments / records
/*
This calculation follows this invariant:
debt.amount = countOfFullInstallments * amount + lastInstallment
*/
val user = userService.findById(debt.userId)
?: throw NotFoundException("Could not find user that owns debt $debtId")
val zoneId = ZoneId.of(user.tz)
val firstPaymentDate = installmentPlan.firstPaymentDate.atZoneSameInstant(zoneId)
val countOfFullInstallments =
debt.amount.divide(
(if (installmentPlan.amount <= BigDecimal.ONE) BigDecimal.ONE else installmentPlan.amount),
MathContext.DECIMAL32
)
.setScale(0, RoundingMode.DOWN)
.intValueExact()
val lastInstallmentAmount = debt.amount - installmentPlan.amount * countOfFullInstallments.toBigDecimal()
val countOfInstallments = countOfFullInstallments + if (lastInstallmentAmount > BigDecimal.ZERO) 1 else 0
val installments = List(countOfInstallments) { i ->
val endDate =
addInterval(firstPaymentDate, installmentPlan.interval, i)
val startDate = addInterval(firstPaymentDate, installmentPlan.interval, i - 1)
.plusDays(1)
val recordAmount = if (i < countOfFullInstallments)
installmentPlan.amount
else lastInstallmentAmount
WalletRecord(
userId = debt.userId,
type = RecordType.debt_rate,
amount = recordAmount,
title = debt.title,
category = RecordCategory.debt_rate,
repeat = installmentPlan.interval,
startDate = startDate.toOffsetDateTime(),
endDate = endDate.toOffsetDateTime(),
)
}
val recordsFlow = recordService.saveAll(installments)
return installmentRepository.saveAll(recordsFlow.map {
Installment(
debtId = debtId,
recordId = it.id!!,
)
}).map { it.recordId }
}
I get this error:
no answer found for: InstallmentRepository(installmentRepository#4).saveAll(app.backend.plan.PlanServiceFeatureImpl$storeInstallmentPlan$suspendImpl$$inlined$map$1#31e1ec3)
io.mockk.MockKException: no answer found for: InstallmentRepository(installmentRepository#4).saveAll(app.backend.plan.PlanServiceFeatureImpl$storeInstallmentPlan$suspendImpl$$inlined$map$1#31e1ec3)
It is a lot of code, but since I don't know where the error comes from I provide the full code. In other cases with the error 'no answer found for' it provided me something like '[...]InstallmentRepository(...).saveAll([parameters here])' and not a path to the Unit under test.
Hope someone can help me with this.
You are trying to mock the following call:
installmentRepository.saveAll(recordsFlow.map {
Installment(
debtId = debtId,
recordId = it.id!!,
)
}).map { it.recordId }
But what you really need to mock is only saveAll(), not the map() after it as follows:
coEvery { installmentRepository.saveAll(
records.map {
Installment(
debtId = debtId,
recordId = it.id!!
)
}
)
} returns flowOf(Installment(debtId, 43))
If this does not work, try the following (with a less strict matching):
coEvery { installmentRepository.saveAll(ArgumentMatchers.any()) } returns flowOf(Installment(debtId, 43))

XUnit Test for ViewComponent returns null result?

I am trying to test my ViewComponent with XUnit.
When I debug through the component and set a break point right before it returns the Component View, the model is set.
Here is the simple model I am returning.
public class IntDashMakeRecAssgnmntsPoRespMngrVM
{
public IEnumerable<Audit> Audits { get; set; }
}
And I am trying to assert the Audits.Count() is greater than 0.
Here is my View Component:
public class IntDashMakeRecAssgnmntsPoRespMngrViewComponent : ViewComponent
{
private IAuditRepository _auditRepo;
private IExternalRepository _externalRepo;
public IntDashMakeRecAssgnmntsPoRespMngrViewComponent(IAuditRepository auditRepo,
IExternalRepository externalRepo)
{
_auditRepo = auditRepo;
_externalRepo = externalRepo;
}
public IViewComponentResult Invoke()
{
ClaimsPrincipal user = HttpContext.Request.HttpContext.User;
short staffId = short.Parse(user.Claims.Single(c => c.Type == "StaffId").Value);
// Get all Internal Audits that are not closed and not completed
var audits = _auditRepo.Audits
.Include(a => a.Findings).ThenInclude(f => f.Recommendations).ThenInclude(r => r.Assignments)
.Where(a => a.StatusID != 3 && a.StatusID != 11);
var external = _externalRepo.ExternalRecords;
audits = audits.Where(a => !external.Any(e => e.AuditID == a.AuditID));
if (User.IsInRole("PAG_SPEC") && !User.IsInRole("PAG_ADMIN_INT"))
{
audits = audits.Where(a =>
a.Assignments.Any(assn => assn.AssignmentAuditId == a.AuditID
&& assn.AssignmentRoleId == 2 && assn.AssignmentStaffId == staffId));
}
// Where audit has a recommendation without an assigned PO Authorizer
// OR without an assigned Responsible Manager (Rec Level).
List<Audit> auditsToAssign = new List<Audit>();
foreach (Audit audit in audits)
{
foreach (Finding finding in audit.Findings)
{
foreach (Recommendation rec in finding.Recommendations)
{
if (!rec.Assignments.Any(asgn => asgn.AssignmentRoleId == 15)
|| !rec.Assignments.Any(asgn => asgn.AssignmentRoleId == 26)
)
{
auditsToAssign.Add(rec.Finding.Audit);
break;
}
}
}
}
IntDashMakeRecAssgnmntsPoRespMngrVM intDashMakeRecAssgnmntsPoRespMngrVM =
new IntDashMakeRecAssgnmntsPoRespMngrVM
{
Audits = auditsToAssign
};
return View("/Views/InternalAudit/Components/Dashboard/IntDashMakeRecAssgnmntsPoRespMngr/Default.cshtml", intDashMakeRecAssgnmntsPoRespMngrVM);
}
}
When I get to this line in debugging and break to inspect, I have 1 Audit which I want:
return View("/Views/InternalAudit/Components/Dashboard/IntDashMakeRecAssgnmntsPoRespMngr/Default.cshtml", intDashMakeRecAssgnmntsPoRespMngrVM);
Now here is my Unit Test:
[Fact]
public void ReturnsAudit_1Finding_1Rec_1Asgn_PONeeded_RespMnrAssigned()
{
// Arrange
var audits = new Audit[]
{
new Audit { AuditID = 1 }
};
var findings = new Finding[]
{
new Finding{ Audit = audits[0], FindingId = 1 } // 1 Finding
};
var recommendations = new List<Recommendation>()
{
new Recommendation // 1 Rec
{
Finding = findings[0],
Assignments = new List<Assignment>()
{
// PO Authorizor
new Assignment { AssignmentRoleId = 15 }
// No Responsible Manager
}
}
};
audits[0].Findings = findings;
findings[0].Recommendations = recommendations;
Mock<IAuditRepository> mockAuditRepo = new Mock<IAuditRepository>();
mockAuditRepo.Setup(m => m.Audits).Returns(audits.AsQueryable());
Mock<IExternalRepository> mockExternalRepo = new Mock<IExternalRepository>();
mockExternalRepo.Setup(m => m.ExternalRecords).Returns(
new External[0].AsQueryable()
);
// Act
var component = new IntDashMakeRecAssgnmntsPoRespMngrViewComponent(
mockAuditRepo.Object, mockExternalRepo.Object);
component.ViewComponentContext = new ViewComponentContext();
component.ViewComponentContext.ViewContext.HttpContext = TestContext;
var result =
component.Invoke() as IntDashMakeRecAssgnmntsPoRespMngrVM;
int auditCount = (result).Audits.Count();
// Assert
Assert.Equal(1, auditCount);
}
Why is result null on this line?
var result =
component.Invoke() as IntDashMakeRecAssgnmntsPoRespMngrVM;
I also tried this first and it is still null:
ViewComponentResult result =
component.Invoke() as ViewComponentResult;
int auditCount =
((IntDashMakeRecAssgnmntsPoRespMngrVM)result.Model).Audits.Count();
I figured it out.
I wasn't casting the result to the right type.
I had this:
ViewComponentResult result =
component.Invoke() as ViewComponentResult;
int auditCount =
((IntDashMakeRecAssgnmntsPoRespMngrVM)result.Model).Audits.Count();
It should be this:
var result =
component.Invoke() as ViewViewComponentResult;
int auditCount =
((IntDashMakeRecAssgnmntsPoRespMngrVM)result.ViewData.Model).Audits.Count();
ViewViewComponentResult instead of ViewComponentResult.

Append to a Func in LINQ

I have a method with a projection
public IQueryable<EmpDTO> GetEmployee(Func<Employee, EmpDTO> projection = null)
{
if(projection == null)
projection = emp => new EmpDTO {
Id = emp.Id,
Name = emp.Name,
Salary = emp.Salary,
};
return entities.Employees.Where(e => e.Salary > 10000).Select(projection);
}
It can be extended as follows:
query = classInstance.GetEmployee(emp => new EmpDTO {
Id = emp.Id,
Name = emp.Name,
Salary = emp.Salary,
Address = emp.Address
});
How can I APPEND only the "Address" field to the Func without
rewriting the entire fields (repeated fields Id, Name, Salary)
Using Expression, you can build a new lambda to initialize the fields:
public IQueryable<EmpDTO> GetEmployee(Expression<Func<Employee, EmpDTO>> addProj = null) {
Expression<Func<Employee, EmpDTO>> projection = emp => new EmpDTO {
Id = emp.Id,
Name = emp.Name,
Salary = emp.Salary,
};
if (addProj != null) {
var pBody = ((MemberInitExpression)projection.Body);
var newBindings = new ReadOnlyCollection<MemberBinding>(pBody.Bindings.Concat(((MemberInitExpression)addProj.Body).Bindings).ToList());
var newBody = Expression.MemberInit(pBody.NewExpression, newBindings);
projection = (Expression<Func<Employee, EmpDTO>>) Expression.Lambda(newBody, projection.Parameters);
}
return entities.Employees.Where(e => e.Salary > 10000).Select(projection);
}
You could also build the entire lambda from scratch, but that seemed like more work to me. Plus you can encapsulate the combine init logic into an extension method:
public static Expression<Func<TIn, TOut>> Add<TIn, TOut>(this Expression<Func<TIn, TOut>> proj, Expression<Func<TIn, TOut>> addProj = null) {
if (addProj != null) {
var pBody = ((MemberInitExpression)proj.Body);
var newBindings = new ReadOnlyCollection<MemberBinding>(pBody.Bindings.Concat(((MemberInitExpression)addProj.Body).Bindings).ToList());
var newBody = Expression.MemberInit(pBody.NewExpression, newBindings);
proj = (Expression<Func<TIn, TOut>>)Expression.Lambda(newBody, proj.Parameters);
}
return proj;
}
which reduces the GetEmployee body to:
public IQueryable<EmpDTO> GetEmployee(Expression<Func<Employee, EmpDTO>> addProj = null) {
Expression<Func<Employee, EmpDTO>> projection = emp => new EmpDTO {
Id = emp.Id,
Name = emp.Name,
Salary = emp.Salary,
};
if (addProj != null)
projection = projection.Add(addProj);
return entities.Employees.Where(e => e.Salary > 10000).Select(projection);
}

Resources