Browse Source
Therefore task and project module had to be created and partially implementedfeature/document
39 changed files with 642 additions and 61 deletions
@ -0,0 +1,12 @@
@@ -0,0 +1,12 @@
|
||||
/* © SRSoftware 2025 */ |
||||
package de.srsoftware.umbrella.core.api; |
||||
|
||||
import de.srsoftware.umbrella.core.exceptions.UmbrellaException; |
||||
import de.srsoftware.umbrella.core.model.Project; |
||||
import java.util.Collection; |
||||
|
||||
public interface ProjectService { |
||||
public Collection<Project> listProjects(long companyId,boolean includeClosed) throws UmbrellaException; |
||||
|
||||
CompanyService companyService(); |
||||
} |
||||
@ -0,0 +1,56 @@
@@ -0,0 +1,56 @@
|
||||
/* © SRSoftware 2025 */ |
||||
package de.srsoftware.umbrella.core.model; |
||||
|
||||
import static de.srsoftware.umbrella.core.Constants.*; |
||||
|
||||
import de.srsoftware.tools.Mappable; |
||||
import java.sql.ResultSet; |
||||
import java.sql.SQLException; |
||||
import java.util.Map; |
||||
|
||||
public record Project(long id, String name, String description, Status status, long companyId, boolean showClosed) implements Mappable { |
||||
public enum Status{ |
||||
Open(10), |
||||
Started(20), |
||||
Pending(40), |
||||
Complete(60), |
||||
Cancelled(100); |
||||
|
||||
private int code; |
||||
|
||||
Status(int code){ |
||||
this.code = code; |
||||
} |
||||
|
||||
public int code(){ |
||||
return code; |
||||
} |
||||
|
||||
public static Status of(int code){ |
||||
return switch (code){ |
||||
case 10 -> Open; |
||||
case 20 -> Started; |
||||
case 40 -> Pending; |
||||
case 60 -> Complete; |
||||
case 100 -> Cancelled; |
||||
default -> throw new IllegalArgumentException(); |
||||
}; |
||||
} |
||||
} |
||||
|
||||
public static Project of(ResultSet rs) throws SQLException { |
||||
return new Project(rs.getLong(ID),rs.getString(NAME),rs.getString(DESCRIPTION),Status.of(rs.getInt(STATUS)),rs.getLong(COMPANY_ID),rs.getBoolean(SHOW_CLOSED)); |
||||
} |
||||
|
||||
@Override |
||||
public Map<String, Object> toMap() { |
||||
return Map.of( |
||||
ID,id, |
||||
NAME,name, |
||||
DESCRIPTION,description, |
||||
STATUS,Map.of(STATUS_CODE,status.code(), NAME,status.name()), |
||||
COMPANY_ID,companyId, |
||||
SHOW_CLOSED,showClosed |
||||
); |
||||
} |
||||
} |
||||
@ -0,0 +1,11 @@
@@ -0,0 +1,11 @@
|
||||
<script> |
||||
import { t } from '../translations.svelte.js'; |
||||
|
||||
let { item, onclick } = $props(); |
||||
</script> |
||||
|
||||
<fieldset {onclick}> |
||||
<legend>{item.code} | {item.name}</legend> |
||||
<div>{@html item.description.rendered}</div> |
||||
<span>{item.unit_price/100} {item.currency} / {item.unit}</span> |
||||
</fieldset> |
||||
@ -1,7 +1,39 @@
@@ -1,7 +1,39 @@
|
||||
<script> |
||||
import { t } from '../../translations.svelte.js'; |
||||
import { onMount } from 'svelte'; |
||||
import EstimatedTime from '../../Components/Item.svelte'; |
||||
let { company_id, onSelect = (item) => {} } = $props(); |
||||
|
||||
let items = $state(null); |
||||
let error = $state(null); |
||||
|
||||
async function loadItems(){ |
||||
const url = `${location.protocol}//${location.host.replace('5173','8080')}/api/task/estimated_times`; |
||||
let data = { company_id: company_id }; |
||||
const resp = await fetch(url,{ |
||||
credentials:'include', |
||||
method: 'POST', |
||||
body: JSON.stringify(data) |
||||
}); |
||||
if (resp.ok){ |
||||
items = await resp.json(); |
||||
} else { |
||||
error = await resp.body(); |
||||
} |
||||
} |
||||
|
||||
onMount(loadItems); |
||||
|
||||
</script> |
||||
|
||||
<div> |
||||
<h1>Estimated Times</h1> |
||||
<h1>{t('task.estimated_times')}</h1> |
||||
{#if error} |
||||
<span class="error">{error}</span> |
||||
{/if} |
||||
{#if items} |
||||
{#each items as item,id} |
||||
<EstimatedTime item={item} onclick={() => onSelect(item)} /> |
||||
{/each} |
||||
{/if} |
||||
</div> |
||||
@ -1,7 +1,12 @@
@@ -1,7 +1,12 @@
|
||||
/* © SRSoftware 2025 */ |
||||
package de.srsoftware.umbrella.items; |
||||
|
||||
public class Constants { |
||||
private Constants(){} |
||||
|
||||
public static final String CODE = "code"; |
||||
public static final String CONFIG_DATABASE = "umbrella.modules.items.database"; |
||||
public static final String TABLE_ITEMS = "items"; |
||||
public static final String TAX = "tax"; |
||||
public static final String UNIT = "unit"; |
||||
public static final String UNIT_PRICE = "unit_price"; |
||||
} |
||||
|
||||
@ -1,4 +1,38 @@
@@ -1,4 +1,38 @@
|
||||
/* © SRSoftware 2025 */ |
||||
package de.srsoftware.umbrella.items; |
||||
|
||||
public class Item { |
||||
import static de.srsoftware.umbrella.core.Constants.*; |
||||
import static de.srsoftware.umbrella.core.Util.markdown; |
||||
import static de.srsoftware.umbrella.items.Constants.*; |
||||
|
||||
import de.srsoftware.tools.Mappable; |
||||
import java.sql.ResultSet; |
||||
import java.sql.SQLException; |
||||
import java.util.Map; |
||||
|
||||
public record Item(long id, long companyId, String code, String name, String description, String unit, long unitPrice, long tax) implements Mappable { |
||||
public static Item of(ResultSet rs) throws SQLException { |
||||
var id = rs.getLong(ID); |
||||
var companyId = rs.getLong(COMPANY_ID); |
||||
var code = rs.getString(CODE); |
||||
var name = rs.getString(NAME); |
||||
var desc = rs.getString(DESCRIPTION); |
||||
var unit = rs.getString(UNIT); |
||||
var unitPrice = rs.getLong(UNIT_PRICE); |
||||
var tax = rs.getInt(TAX); |
||||
|
||||
return new Item(id,companyId,code,name,desc,unit,unitPrice,tax); |
||||
} |
||||
|
||||
@Override |
||||
public Map<String, Object> toMap() { |
||||
return Map.of( |
||||
ID,id, |
||||
COMPANY_ID,companyId, |
||||
CODE,code,NAME,name, |
||||
DESCRIPTION,Map.of(SOURCE,description,RENDERED,markdown(description)), |
||||
UNIT,unit, |
||||
UNIT_PRICE,unitPrice, |
||||
TAX,tax); |
||||
} |
||||
} |
||||
|
||||
@ -1,7 +1,9 @@
@@ -1,7 +1,9 @@
|
||||
/* © SRSoftware 2025 */ |
||||
package de.srsoftware.umbrella.items; |
||||
|
||||
import de.srsoftware.umbrella.core.exceptions.UmbrellaException; |
||||
import java.util.Collection; |
||||
|
||||
public interface ItemDb { |
||||
Collection<Item> list(); |
||||
Collection<Item> list(long companyId) throws UmbrellaException; |
||||
} |
||||
|
||||
@ -1,11 +1,36 @@
@@ -1,11 +1,36 @@
|
||||
/* © SRSoftware 2025 */ |
||||
package de.srsoftware.umbrella.items; |
||||
|
||||
import static de.srsoftware.tools.jdbc.Condition.equal; |
||||
import static de.srsoftware.tools.jdbc.Query.select; |
||||
import static de.srsoftware.umbrella.core.Constants.COMPANY_ID; |
||||
import static de.srsoftware.umbrella.core.ResponseCode.HTTP_SERVER_ERROR; |
||||
import static de.srsoftware.umbrella.items.Constants.TABLE_ITEMS; |
||||
|
||||
import de.srsoftware.umbrella.core.exceptions.UmbrellaException; |
||||
import java.sql.Connection; |
||||
import java.sql.SQLException; |
||||
import java.util.Collection; |
||||
import java.util.HashSet; |
||||
|
||||
public class SqliteDb implements ItemDb{ |
||||
|
||||
private final Connection db; |
||||
|
||||
public SqliteDb(Connection connection) { |
||||
db = connection; |
||||
} |
||||
|
||||
@Override |
||||
public Collection<Item> list(long companyId) throws UmbrellaException { |
||||
try { |
||||
var items = new HashSet<Item>(); |
||||
var rs = select("*").from(TABLE_ITEMS).where(COMPANY_ID, equal(companyId)).exec(db); |
||||
while (rs.next()) items.add(Item.of(rs)); |
||||
rs.close(); |
||||
return items; |
||||
} catch (SQLException e) { |
||||
throw new UmbrellaException(HTTP_SERVER_ERROR,"Failed to load items from database"); |
||||
} |
||||
} |
||||
} |
||||
|
||||
@ -0,0 +1,5 @@
@@ -0,0 +1,5 @@
|
||||
description = "Umbrella : Projects" |
||||
|
||||
dependencies{ |
||||
implementation(project(":core")) |
||||
} |
||||
@ -0,0 +1,10 @@
@@ -0,0 +1,10 @@
|
||||
/* © SRSoftware 2025 */ |
||||
package de.srsoftware.umbrella.project; |
||||
|
||||
public class Constants { |
||||
private Constants(){} |
||||
public static final String CONFIG_DATABASE = "umbrella.modules.project.database"; |
||||
public static final String TABLE_PROJECTS = "projects"; |
||||
|
||||
|
||||
} |
||||
@ -0,0 +1,10 @@
@@ -0,0 +1,10 @@
|
||||
/* © SRSoftware 2025 */ |
||||
package de.srsoftware.umbrella.project; |
||||
|
||||
import de.srsoftware.umbrella.core.exceptions.UmbrellaException; |
||||
import de.srsoftware.umbrella.core.model.Project; |
||||
import java.util.Collection; |
||||
|
||||
public interface ProjectDb { |
||||
Collection<Project> list(long companyId, boolean includeClosed) throws UmbrellaException; |
||||
} |
||||
@ -0,0 +1,79 @@
@@ -0,0 +1,79 @@
|
||||
/* © SRSoftware 2025 */ |
||||
package de.srsoftware.umbrella.project; |
||||
|
||||
import static de.srsoftware.umbrella.core.ConnectionProvider.connect; |
||||
import static de.srsoftware.umbrella.core.Constants.*; |
||||
import static de.srsoftware.umbrella.core.Paths.LIST; |
||||
import static de.srsoftware.umbrella.core.exceptions.UmbrellaException.forbidden; |
||||
import static de.srsoftware.umbrella.core.exceptions.UmbrellaException.missingFieldException; |
||||
import static de.srsoftware.umbrella.project.Constants.CONFIG_DATABASE; |
||||
|
||||
import com.sun.net.httpserver.HttpExchange; |
||||
import de.srsoftware.configuration.Configuration; |
||||
import de.srsoftware.tools.Path; |
||||
import de.srsoftware.tools.SessionToken; |
||||
import de.srsoftware.umbrella.core.BaseHandler; |
||||
import de.srsoftware.umbrella.core.api.CompanyService; |
||||
import de.srsoftware.umbrella.core.api.ProjectService; |
||||
import de.srsoftware.umbrella.core.api.UserService; |
||||
import de.srsoftware.umbrella.core.exceptions.UmbrellaException; |
||||
import de.srsoftware.umbrella.core.model.Project; |
||||
import de.srsoftware.umbrella.core.model.Token; |
||||
import de.srsoftware.umbrella.core.model.UmbrellaUser; |
||||
import java.io.IOException; |
||||
import java.util.Collection; |
||||
import java.util.HashMap; |
||||
import java.util.Optional; |
||||
|
||||
public class ProjectModule extends BaseHandler implements ProjectService { |
||||
|
||||
private final ProjectDb projectDb; |
||||
private final CompanyService companies; |
||||
private final UserService users; |
||||
|
||||
public ProjectModule(Configuration config, CompanyService companyService) throws UmbrellaException { |
||||
var dbFile = config.get(CONFIG_DATABASE).orElseThrow(() -> missingFieldException(CONFIG_DATABASE)); |
||||
projectDb = new SqliteDb(connect(dbFile)); |
||||
companies = companyService; |
||||
users = companies.userService(); |
||||
} |
||||
|
||||
@Override |
||||
public CompanyService companyService() { |
||||
return companies; |
||||
} |
||||
|
||||
@Override |
||||
public boolean doPost(Path path, HttpExchange ex) throws IOException { |
||||
addCors(ex); |
||||
try { |
||||
Optional<Token> token = SessionToken.from(ex).map(Token::of); |
||||
var user = users.loadUser(token); |
||||
if (user.isEmpty()) return unauthorized(ex); |
||||
var head = path.pop(); |
||||
return switch (head) { |
||||
case LIST -> listItems(ex,user.get()); |
||||
default -> super.doGet(path,ex); |
||||
}; |
||||
} catch (UmbrellaException e){ |
||||
return send(ex,e); |
||||
} |
||||
} |
||||
|
||||
public Collection<Project> listProjects(long companyId, boolean includeClosed) throws UmbrellaException { |
||||
return projectDb.list(companyId, includeClosed); |
||||
} |
||||
|
||||
private boolean listItems(HttpExchange ex, UmbrellaUser user) throws IOException, UmbrellaException { |
||||
var json = json(ex); |
||||
if (!(json.has(COMPANY_ID) && json.get(COMPANY_ID) instanceof Number cid)) throw missingFieldException(COMPANY_ID); |
||||
var companyId = cid.longValue(); |
||||
var company = companies.get(companyId); |
||||
if (!companies.membership(companyId,user.id())) throw forbidden("You are mot a member of company {0}",company.name()); |
||||
var items = listProjects(companyId,false) |
||||
.stream() |
||||
.map(Project::toMap) |
||||
.map(HashMap::new); |
||||
return sendContent(ex,items); |
||||
} |
||||
} |
||||
@ -0,0 +1,41 @@
@@ -0,0 +1,41 @@
|
||||
/* © SRSoftware 2025 */ |
||||
package de.srsoftware.umbrella.project; |
||||
|
||||
import static de.srsoftware.tools.jdbc.Condition.equal; |
||||
import static de.srsoftware.tools.jdbc.Condition.lessThan; |
||||
import static de.srsoftware.tools.jdbc.Query.select; |
||||
import static de.srsoftware.umbrella.core.Constants.COMPANY_ID; |
||||
import static de.srsoftware.umbrella.core.Constants.STATUS; |
||||
import static de.srsoftware.umbrella.core.ResponseCode.HTTP_SERVER_ERROR; |
||||
import static de.srsoftware.umbrella.project.Constants.TABLE_PROJECTS; |
||||
|
||||
import de.srsoftware.umbrella.core.exceptions.UmbrellaException; |
||||
import de.srsoftware.umbrella.core.model.Project; |
||||
import java.sql.Connection; |
||||
import java.sql.SQLException; |
||||
import java.util.Collection; |
||||
import java.util.HashSet; |
||||
|
||||
public class SqliteDb implements ProjectDb { |
||||
|
||||
private final Connection db; |
||||
|
||||
public SqliteDb(Connection connection) { |
||||
db = connection; |
||||
} |
||||
|
||||
@Override |
||||
public Collection<Project> list(long companyId, boolean includeClosed) throws UmbrellaException { |
||||
try { |
||||
var items = new HashSet<Project>(); |
||||
var query = select("*").from(TABLE_PROJECTS).where(COMPANY_ID, equal(companyId)); |
||||
if (!includeClosed) query = query.where(STATUS,lessThan(Project.Status.Complete.code())); |
||||
var rs = query.exec(db); |
||||
while (rs.next()) items.add(Project.of(rs)); |
||||
rs.close(); |
||||
return items; |
||||
} catch (SQLException e) { |
||||
throw new UmbrellaException(HTTP_SERVER_ERROR,"Failed to load items from database"); |
||||
} |
||||
} |
||||
} |
||||
@ -0,0 +1,6 @@
@@ -0,0 +1,6 @@
|
||||
description = "Umbrella : Tasks" |
||||
|
||||
dependencies{ |
||||
implementation(project(":core")) |
||||
implementation(project(":project")) |
||||
} |
||||
@ -0,0 +1,19 @@
@@ -0,0 +1,19 @@
|
||||
/* © SRSoftware 2025 */ |
||||
package de.srsoftware.umbrella.task; |
||||
|
||||
public class Constants { |
||||
private Constants(){} |
||||
|
||||
public static final String CONFIG_DATABASE = "umbrella.modules.task.database"; |
||||
public static final String CHILDREN = "children"; |
||||
public static final String DUE_DATE = "due_date"; |
||||
public static final String ESTIMATED_TIMES = "estimated_times"; |
||||
public static final String ESTIMATED_TIME = "estimated_time"; |
||||
public static final String EST_TIME = "est_time"; |
||||
public static final String NO_INDEX = "no_index"; |
||||
public static final String PARENT_TASK_ID = "parent_task_id"; |
||||
public static final String PROJECT_ID = "project_id"; |
||||
public static final String START_DATE = "start_date"; |
||||
public static final String TABLE_TASKS = "tasks"; |
||||
public static final String FIELD_TASKS = "tasks"; |
||||
} |
||||
@ -0,0 +1,38 @@
@@ -0,0 +1,38 @@
|
||||
/* © SRSoftware 2025 */ |
||||
package de.srsoftware.umbrella.task; |
||||
|
||||
|
||||
import static de.srsoftware.tools.jdbc.Condition.in; |
||||
import static de.srsoftware.tools.jdbc.Query.select; |
||||
import static de.srsoftware.umbrella.core.ResponseCode.HTTP_SERVER_ERROR; |
||||
import static de.srsoftware.umbrella.task.Constants.PROJECT_ID; |
||||
import static de.srsoftware.umbrella.task.Constants.TABLE_TASKS; |
||||
|
||||
import de.srsoftware.umbrella.core.exceptions.UmbrellaException; |
||||
import java.sql.Connection; |
||||
import java.sql.SQLException; |
||||
import java.util.Collection; |
||||
import java.util.HashSet; |
||||
import java.util.List; |
||||
|
||||
|
||||
public class SqliteDb implements TaskDb { |
||||
|
||||
private final Connection db; |
||||
|
||||
public SqliteDb(Connection connection) { |
||||
db = connection; |
||||
} |
||||
|
||||
public Collection<Task> listTasks(List<Long> projectIds) throws UmbrellaException { |
||||
try { |
||||
var rs = select("*").from(TABLE_TASKS).where(PROJECT_ID, in(projectIds.toArray())).exec(db); |
||||
var list = new HashSet<Task>(); |
||||
while (rs.next()) list.add(Task.of(rs)); |
||||
rs.close(); |
||||
return list; |
||||
} catch (SQLException e) { |
||||
throw new UmbrellaException(HTTP_SERVER_ERROR,"Failed to load tasks for project ids"); |
||||
} |
||||
} |
||||
} |
||||
@ -0,0 +1,54 @@
@@ -0,0 +1,54 @@
|
||||
/* © SRSoftware 2025 */ |
||||
package de.srsoftware.umbrella.task; |
||||
|
||||
import static de.srsoftware.tools.Optionals.nullIfEmpty; |
||||
import static de.srsoftware.umbrella.core.Constants.*; |
||||
import static de.srsoftware.umbrella.core.Constants.SHOW_CLOSED; |
||||
import static de.srsoftware.umbrella.core.Constants.STATUS; |
||||
import static de.srsoftware.umbrella.task.Constants.*; |
||||
|
||||
import de.srsoftware.tools.Mappable; |
||||
import java.sql.ResultSet; |
||||
import java.sql.SQLException; |
||||
import java.time.LocalDate; |
||||
import java.util.HashMap; |
||||
import java.util.Map; |
||||
|
||||
public record Task(long id, long projectId, Long parentTaskId, String name, String description, int status, Double estimatedTime, LocalDate start, LocalDate dueDate,boolean showClosed, boolean noIndex) implements Mappable { |
||||
public static Task of(ResultSet rs) throws SQLException { |
||||
var estTime = rs.getDouble(EST_TIME); |
||||
var parentTaskId = rs.getLong(PARENT_TASK_ID); |
||||
var startDate = nullIfEmpty(rs.getString(START_DATE)); |
||||
var dueDate = nullIfEmpty(rs.getString(DUE_DATE)); |
||||
return new Task( |
||||
rs.getLong(ID), |
||||
rs.getLong(PROJECT_ID), |
||||
parentTaskId == 0d ? null : parentTaskId, |
||||
rs.getString(NAME), |
||||
rs.getString(DESCRIPTION), |
||||
rs.getInt(STATUS), |
||||
estTime == 0d ? null : estTime, |
||||
startDate != null ? LocalDate.parse(startDate) : null, |
||||
dueDate != null ? LocalDate.parse(dueDate) : null, |
||||
rs.getBoolean(SHOW_CLOSED), |
||||
rs.getBoolean(NO_INDEX) |
||||
); |
||||
} |
||||
|
||||
@Override |
||||
public Map<String, Object> toMap() { |
||||
var map = new HashMap<String,Object>(); |
||||
map.put(ID, id); |
||||
map.put(PROJECT_ID, projectId); |
||||
map.put(PARENT_TASK_ID, parentTaskId); |
||||
map.put(NAME, name); |
||||
map.put(DESCRIPTION, description); |
||||
map.put(STATUS, status); |
||||
map.put(ESTIMATED_TIME, estimatedTime); |
||||
map.put(START_DATE,start); |
||||
map.put(DUE_DATE,dueDate); |
||||
map.put(SHOW_CLOSED,showClosed); |
||||
map.put(NO_INDEX,noIndex); |
||||
return map; |
||||
} |
||||
} |
||||
@ -0,0 +1,8 @@
@@ -0,0 +1,8 @@
|
||||
/* © SRSoftware 2025 */ |
||||
package de.srsoftware.umbrella.task; |
||||
|
||||
|
||||
|
||||
public interface TaskDb { |
||||
|
||||
} |
||||
@ -0,0 +1,94 @@
@@ -0,0 +1,94 @@
|
||||
/* © SRSoftware 2025 */ |
||||
package de.srsoftware.umbrella.task; |
||||
|
||||
import static de.srsoftware.tools.Optionals.is0; |
||||
import static de.srsoftware.umbrella.core.ConnectionProvider.connect; |
||||
import static de.srsoftware.umbrella.core.Constants.COMPANY_ID; |
||||
import static de.srsoftware.umbrella.core.exceptions.UmbrellaException.forbidden; |
||||
import static de.srsoftware.umbrella.core.exceptions.UmbrellaException.missingFieldException; |
||||
import static de.srsoftware.umbrella.task.Constants.*; |
||||
import static java.util.Objects.isNull; |
||||
import static java.util.stream.Collectors.toMap; |
||||
|
||||
import com.sun.net.httpserver.HttpExchange; |
||||
import de.srsoftware.configuration.Configuration; |
||||
import de.srsoftware.tools.Path; |
||||
import de.srsoftware.tools.SessionToken; |
||||
import de.srsoftware.umbrella.core.BaseHandler; |
||||
import de.srsoftware.umbrella.core.api.CompanyService; |
||||
import de.srsoftware.umbrella.core.api.ProjectService; |
||||
import de.srsoftware.umbrella.core.api.UserService; |
||||
import de.srsoftware.umbrella.core.exceptions.UmbrellaException; |
||||
import de.srsoftware.umbrella.core.model.Project; |
||||
import de.srsoftware.umbrella.core.model.Token; |
||||
import de.srsoftware.umbrella.core.model.UmbrellaUser; |
||||
import java.io.IOException; |
||||
import java.util.*; |
||||
import java.util.stream.Collectors; |
||||
|
||||
public class TaskModule extends BaseHandler { |
||||
|
||||
private final SqliteDb taskDb; |
||||
private final ProjectService projects; |
||||
private final UserService users; |
||||
private final CompanyService companies; |
||||
|
||||
public TaskModule(Configuration config, ProjectService projectService) throws UmbrellaException { |
||||
var dbFile = config.get(CONFIG_DATABASE).orElseThrow(() -> missingFieldException(CONFIG_DATABASE)); |
||||
taskDb = new SqliteDb(connect(dbFile)); |
||||
projects = projectService; |
||||
companies = projectService.companyService(); |
||||
users = companies.userService(); |
||||
} |
||||
|
||||
@Override |
||||
public boolean doPost(Path path, HttpExchange ex) throws IOException { |
||||
addCors(ex); |
||||
try { |
||||
Optional<Token> token = SessionToken.from(ex).map(Token::of); |
||||
var user = users.loadUser(token); |
||||
if (user.isEmpty()) return unauthorized(ex); |
||||
var head = path.pop(); |
||||
return switch (head) { |
||||
case ESTIMATED_TIMES -> estimatedTimes(user.get(),ex); |
||||
default -> super.doGet(path,ex); |
||||
}; |
||||
} catch (UmbrellaException e){ |
||||
return send(ex,e); |
||||
} |
||||
} |
||||
|
||||
private boolean estimatedTimes(UmbrellaUser user, HttpExchange ex) throws IOException, UmbrellaException { |
||||
var json = json(ex); |
||||
if (!(json.has(COMPANY_ID) && json.get(COMPANY_ID) instanceof Number cid)) throw missingFieldException(COMPANY_ID); |
||||
var companyId = cid.longValue(); |
||||
var company = companies.get(companyId); |
||||
if (!companies.membership(companyId,user.id())) throw forbidden("You are mot a member of company {0}",company.name()); |
||||
var projects = this.projects.listProjects(companyId,false); |
||||
var taskList = taskDb.listTasks(projects.stream().map(Project::id).toList()); |
||||
var map = taskList.stream().collect(toMap(Task::id, t -> t)); |
||||
var tree = new HashMap<Long,Map<String,Object>>(); |
||||
taskList.stream().filter(task -> !is0(task.estimatedTime())).forEach(task -> placeInTree(task,tree,map)); |
||||
var result = new ArrayList<Map<String,Object>>(); |
||||
projects.forEach(project -> { |
||||
var projectMap = new HashMap<>(project.toMap()); |
||||
var children = tree.values().stream().filter(root -> project.id() == (Long)root.get(PROJECT_ID)).toList(); |
||||
projectMap.put(FIELD_TASKS,children); |
||||
result.add(projectMap); |
||||
}); |
||||
return sendContent(ex,result); |
||||
} |
||||
|
||||
private Map<String,Object> placeInTree(Task task, HashMap<Long, Map<String,Object>> tree, Map<Long, Task> map) { |
||||
var taskMap = task.toMap(); |
||||
if (task.parentTaskId() != null){ |
||||
Task parent = map.get(task.parentTaskId()); |
||||
var trunk = placeInTree(parent,tree,map); |
||||
ArrayList<Object> children = (ArrayList<Object>) trunk.computeIfAbsent(CHILDREN, k -> new ArrayList<Object>()); |
||||
children.add(taskMap); |
||||
return taskMap; |
||||
} |
||||
tree.put(task.id(),taskMap); |
||||
return taskMap; |
||||
} |
||||
} |
||||
Loading…
Reference in new issue