Browse Source

working on Autocomplete field for member addition to projects

kanban
Stephan Richter 4 months ago
parent
commit
7eca9dd08e
  1. 1
      core/src/main/java/de/srsoftware/umbrella/core/api/ProjectService.java
  2. 47
      frontend/src/Components/Autocomplete.svelte
  3. 31
      frontend/src/Components/MemberEditor.svelte
  4. 12
      project/src/main/java/de/srsoftware/umbrella/project/ProjectModule.java
  5. 37
      user/src/main/java/de/srsoftware/umbrella/user/UserModule.java
  6. 6
      user/src/main/java/de/srsoftware/umbrella/user/api/UserDb.java
  7. 17
      user/src/main/java/de/srsoftware/umbrella/user/sqlite/SqliteDB.java

1
core/src/main/java/de/srsoftware/umbrella/core/api/ProjectService.java

@ -16,5 +16,4 @@ public interface ProjectService { @@ -16,5 +16,4 @@ public interface ProjectService {
loadMembers(List.of(project));
return project;
}
Map<Long, Map<String,Object>> mapProjects(Map<Long, Project> projects);
}

47
frontend/src/Components/Autocomplete.svelte

@ -0,0 +1,47 @@ @@ -0,0 +1,47 @@
<script>
import { t } from '../translations.svelte.js'
import { tick } from "svelte";
let { getOptionsFor = text => [], onSelect = text => [] } = $props();
let text = $state('')
let options = $state([]);
async function onkeyup(evt){
var select = evt.target;
var key = evt.key;
var ignore = ['Escape','Tab','ArrowUp','ArrowLeft','ArrowRight']
if (ignore.includes(key)) return;
if (key == 'ArrowDown'){
if (select.selectedIndex == 0) select.selectedIndex=1;
return;
}
if (key == 'Enter'){
text = select.value;
onSelect(text);
options=[];
return;
}
if (key == 'Backspace'){
text = text.substring(0,text.length-1)
} else if (key.length<2){
text += evt.key
}
options = await getOptionsFor(text);
await tick();
for (let o of select.getElementsByTagName('option')) o.selected = false;
}
</script>
<style>
select{
min-width: 200px;
}
</style>
<select size={options.length<2?2:options.length+1} {onkeyup} autofocus width="40">
<option>{text}</option>
{#each options as option,i}
<option>{option}</option>
{/each}
</select>

31
frontend/src/Components/MemberEditor.svelte

@ -1,6 +1,7 @@ @@ -1,6 +1,7 @@
<script>
import { onMount } from 'svelte';
import { t } from '../translations.svelte.js';
import Autocomplete from './Autocomplete.svelte';
import PermissionSelector from './PermissionSelector.svelte';
let { members, updatePermission = (uid,perm) => console.log({user:uid,perm:perm}) } = $props();
let error = $state(null);
@ -18,6 +19,32 @@ @@ -18,6 +19,32 @@
}
}
async function getOptionsFor(text){
const url = `${location.protocol}//${location.host.replace('5173','8080')}/api/user/search`;
var resp = await fetch(url,{
credentials: 'include',
method: 'POST',
body: text
});
if (resp.ok){
var json = await resp.json();
if (Array.isArray(json)) return json;
if (typeof json == 'object'){
let names = Object.values(json).map(user => user.name);
if (names.length > 10) names.length = 10;
return names;
}
console.warn('not an array:',json);
return [];
} else {
return [];
}
}
function onSelect(name){
console.log({selected_user:name})
}
onMount(loadPermissions);
</script>
@ -41,7 +68,9 @@ @@ -41,7 +68,9 @@
<tr>
<td></td>
<td>{t('add_member')}</td>
<td><input type="text" /></td>
<td>
<Autocomplete {getOptionsFor} {onSelect} />
</td>
</tr>
</tbody>
</table>

12
project/src/main/java/de/srsoftware/umbrella/project/ProjectModule.java

@ -4,6 +4,7 @@ package de.srsoftware.umbrella.project; @@ -4,6 +4,7 @@ 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.Util.mapValues;
import static de.srsoftware.umbrella.core.exceptions.UmbrellaException.*;
import static de.srsoftware.umbrella.core.model.Permission.EDIT;
import static de.srsoftware.umbrella.core.model.Permission.OWNER;
@ -14,6 +15,7 @@ import static java.net.HttpURLConnection.HTTP_OK; @@ -14,6 +15,7 @@ import static java.net.HttpURLConnection.HTTP_OK;
import com.sun.net.httpserver.HttpExchange;
import de.srsoftware.configuration.Configuration;
import de.srsoftware.tools.Mappable;
import de.srsoftware.tools.Path;
import de.srsoftware.tools.SessionToken;
import de.srsoftware.umbrella.core.BaseHandler;
@ -130,7 +132,7 @@ public class ProjectModule extends BaseHandler implements ProjectService { @@ -130,7 +132,7 @@ public class ProjectModule extends BaseHandler implements ProjectService {
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 = listCompanyProjects(companyId,false);
return sendContent(ex,mapProjects(projects));
return sendContent(ex,mapValues(projects));
}
@Override
@ -142,7 +144,7 @@ public class ProjectModule extends BaseHandler implements ProjectService { @@ -142,7 +144,7 @@ public class ProjectModule extends BaseHandler implements ProjectService {
private boolean listUserProjects(HttpExchange ex, UmbrellaUser user, boolean showClosed) throws IOException, UmbrellaException {
var projects = listUserProjects(user.id(),showClosed);
return sendContent(ex,mapProjects(projects));
return sendContent(ex,mapValues(projects));
}
@Override
@ -159,12 +161,6 @@ public class ProjectModule extends BaseHandler implements ProjectService { @@ -159,12 +161,6 @@ public class ProjectModule extends BaseHandler implements ProjectService {
return projectList;
}
@Override
public Map<Long, Map<String, Object>> mapProjects(Map<Long, Project> projects) {
var mapped = new HashMap<Long,Map<String,Object>>();
for (var entry : projects.entrySet()) mapped.put(entry.getKey(),entry.getValue().toMap());
return mapped;
}
private void patchMembers(Project project, JSONObject json) {
var members = project.members();

37
user/src/main/java/de/srsoftware/umbrella/user/UserModule.java

@ -7,12 +7,11 @@ import static de.srsoftware.tools.Strings.uuid; @@ -7,12 +7,11 @@ import static de.srsoftware.tools.Strings.uuid;
import static de.srsoftware.umbrella.core.ConnectionProvider.connect;
import static de.srsoftware.umbrella.core.Constants.*;
import static de.srsoftware.umbrella.core.Constants.CODE;
import static de.srsoftware.umbrella.core.Paths.LIST;
import static de.srsoftware.umbrella.core.Paths.LOGOUT;
import static de.srsoftware.umbrella.core.Constants.TOKEN;
import static de.srsoftware.umbrella.core.Paths.*;
import static de.srsoftware.umbrella.core.ResponseCode.*;
import static de.srsoftware.umbrella.core.ResponseCode.HTTP_SERVER_ERROR;
import static de.srsoftware.umbrella.core.Util.open;
import static de.srsoftware.umbrella.core.Util.request;
import static de.srsoftware.umbrella.core.Util.*;
import static de.srsoftware.umbrella.core.exceptions.UmbrellaException.*;
import static de.srsoftware.umbrella.user.Constants.*;
import static de.srsoftware.umbrella.user.Paths.*;
@ -257,6 +256,7 @@ public class UserModule extends BaseHandler implements UserService { @@ -257,6 +256,7 @@ public class UserModule extends BaseHandler implements UserService {
case OIDC -> postOIDC(ex, path);
case IMPERSONATE -> impersonate(ex, targetId);
case LOGIN -> postLogin(ex);
case SEARCH -> postSearch(ex);
case RESET_PW -> postResetPassword(ex);
case null, default -> super.doPost(path,ex);
};
@ -449,6 +449,17 @@ public class UserModule extends BaseHandler implements UserService { @@ -449,6 +449,17 @@ public class UserModule extends BaseHandler implements UserService {
}
}
private boolean patchService(HttpExchange ex, String serviceName, UmbrellaUser requestingUser) throws IOException, UmbrellaException {
if (!(requestingUser instanceof DbUser user && user.permissions().contains(MANAGE_LOGIN_SERVICES))) throw forbidden("You are not allowed to manage that service!");
var json = json(ex);
if (!json.has(NAME) || !(json.get(NAME) instanceof String name) || name.isBlank()) throw missingFieldException(NAME);
if (!json.has(URL) || !(json.get(URL) instanceof String url) || url.isBlank()) throw missingFieldException(URL);
if (!json.has(CLIENT_ID) || !(json.get(CLIENT_ID) instanceof String clientId) || clientId.isBlank()) throw missingFieldException(CLIENT_ID);
if (!json.has(CLIENT_SECRET) || !(json.get(CLIENT_SECRET) instanceof String secret) || secret.isBlank()) throw missingFieldException(CLIENT_SECRET);
var service = logins.save(new LoginService(name,url,clientId,secret, DEFAULT_FIELD));
return sendContent(ex,service.toMap());
}
@Override
public PostBox postBox() {
return messages;
@ -496,17 +507,6 @@ public class UserModule extends BaseHandler implements UserService { @@ -496,17 +507,6 @@ public class UserModule extends BaseHandler implements UserService {
return ok(ex);
}
private boolean patchService(HttpExchange ex, String serviceName, UmbrellaUser requestingUser) throws IOException, UmbrellaException {
if (!(requestingUser instanceof DbUser user && user.permissions().contains(MANAGE_LOGIN_SERVICES))) throw forbidden("You are not allowed to manage that service!");
var json = json(ex);
if (!json.has(NAME) || !(json.get(NAME) instanceof String name) || name.isBlank()) throw missingFieldException(NAME);
if (!json.has(URL) || !(json.get(URL) instanceof String url) || url.isBlank()) throw missingFieldException(URL);
if (!json.has(CLIENT_ID) || !(json.get(CLIENT_ID) instanceof String clientId) || clientId.isBlank()) throw missingFieldException(CLIENT_ID);
if (!json.has(CLIENT_SECRET) || !(json.get(CLIENT_SECRET) instanceof String secret) || secret.isBlank()) throw missingFieldException(CLIENT_SECRET);
var service = logins.save(new LoginService(name,url,clientId,secret, DEFAULT_FIELD));
return sendContent(ex,service.toMap());
}
private boolean postLogin(HttpExchange ex) throws IOException {
var json = json(ex);
if (!(json.has(USERNAME) && json.get(USERNAME) instanceof String username)) return sendContent(ex, HTTP_UNPROCESSABLE,"Username missing");
@ -522,7 +522,12 @@ public class UserModule extends BaseHandler implements UserService { @@ -522,7 +522,12 @@ public class UserModule extends BaseHandler implements UserService {
}
}
private boolean postSearch(HttpExchange ex) throws IOException {
var requestingUser = loadUser(ex);
if (!(requestingUser.isPresent() && requestingUser.get() instanceof DbUser dbUser)) return unauthorized(ex);
var key = body(ex);
return sendContent(ex,mapValues(users.search(key)));
}
static int score(String password){
if (password == null) return 0;

6
user/src/main/java/de/srsoftware/umbrella/user/api/UserDb.java

@ -17,6 +17,8 @@ public interface UserDb { @@ -17,6 +17,8 @@ public interface UserDb {
Boolean dropSession(Token token) throws UmbrellaException;
Session extend(Session session) throws UmbrellaException;
/**
* Get a session for the provided user.
* @param user
@ -24,8 +26,6 @@ public interface UserDb { @@ -24,8 +26,6 @@ public interface UserDb {
*/
Session getSession(UmbrellaUser user) throws UmbrellaException;
Session extend(Session session) throws UmbrellaException;
Map<Long, UmbrellaUser> list(Integer start, Integer limit, Collection<Long> ids) throws UmbrellaException;
Session load(Token token) throws UmbrellaException;
@ -39,4 +39,6 @@ public interface UserDb { @@ -39,4 +39,6 @@ public interface UserDb {
UmbrellaUser load(String key, Password password) throws UmbrellaException;
UmbrellaUser save(DbUser user) throws UmbrellaException;
Map<Long,DbUser> search(String key);
}

17
user/src/main/java/de/srsoftware/umbrella/user/sqlite/SqliteDB.java

@ -5,6 +5,7 @@ import static de.srsoftware.tools.jdbc.Condition.*; @@ -5,6 +5,7 @@ import static de.srsoftware.tools.jdbc.Condition.*;
import static de.srsoftware.tools.jdbc.Query.*;
import static de.srsoftware.tools.jdbc.Query.SelectQuery.ALL;
import static de.srsoftware.umbrella.core.Constants.*;
import static de.srsoftware.umbrella.core.ResponseCode.HTTP_SERVER_ERROR;
import static de.srsoftware.umbrella.user.Constants.*;
import static de.srsoftware.umbrella.user.model.DbUser.ADMIN_PERMISSIONS;
import static java.lang.System.Logger.Level.*;
@ -499,6 +500,22 @@ CREATE TABLE IF NOT EXISTS {0} ( @@ -499,6 +500,22 @@ CREATE TABLE IF NOT EXISTS {0} (
return user;
}
@Override
public Map<Long, DbUser> search(String key) {
try {
var rs = select(ALL).from(TABLE_USERS).where(LOGIN,like("%"+key+"%")).exec(db);
var users = new HashMap<Long,DbUser>();
while (rs.next()){
var user = toUser(rs);
users.put(user.id(),user);
}
rs.close();
return users;
} catch (SQLException e){
throw new UmbrellaException(HTTP_SERVER_ERROR,"Failed to search for user by key = {0}",key);
}
}
public Instant then(){
return LocalDateTime.now().plus(DEFAULT_SESSION_DURATION).toInstant(ZoneOffset.UTC);
}

Loading…
Cancel
Save