Commit 35e5fef5 authored by Mathias Vehus's avatar Mathias Vehus
Browse files

Started solving task4

parent 55cd0c73
package inf226.inchat;
import com.lambdaworks.crypto.SCryptUtil;
import inf226.util.Triple;
import inf226.util.immutable.List;
import inf226.util.Pair;
......@@ -16,12 +17,12 @@ public final class Account {
* and a list of channels which the user can post to.
*/
public final Stored<User> user;
public final List<Pair<String,Stored<Channel>>> channels;
public final List<Triple<String,Stored<Channel>,Role>> channels;
public final String hashedPassword;
public Account(final Stored<User> user,
final List<Pair<String,Stored<Channel>>> channels,
final List<Triple<String,Stored<Channel>,Role>> channels,
final String hashedPassword) {
this.user = user;
this.channels = channels;
......@@ -45,15 +46,9 @@ public final class Account {
*
* @return A new account object with the channel added.
*/
public Account joinChannel(final String alias,
final Stored<Channel> channel) {
Pair<String,Stored<Channel>> entry
= new Pair<String,Stored<Channel>>(alias,channel);
return new Account
(user,
List.cons(entry,
channels),
hashedPassword);
public Account joinChannel(final String alias, final Stored<Channel> channel, final Role role) {
Triple<String,Stored<Channel>,Role> entry = new Triple<String,Stored<Channel>,Role>(alias,channel, role);
return new Account (user,List.cons(entry,channels),hashedPassword);
}
......
......@@ -37,7 +37,7 @@ public final class AccountStorage
connection.createStatement()
.executeUpdate("CREATE TABLE IF NOT EXISTS Account (id TEXT PRIMARY KEY, version TEXT, user TEXT, password TEXT, FOREIGN KEY(user) REFERENCES User(id) ON DELETE CASCADE)");
connection.createStatement()
.executeUpdate("CREATE TABLE IF NOT EXISTS AccountChannel (account TEXT, channel TEXT, alias TEXT, ordinal INTEGER, PRIMARY KEY(account,channel), FOREIGN KEY(account) REFERENCES Account(id) ON DELETE CASCADE, FOREIGN KEY(channel) REFERENCES Channel(id) ON DELETE CASCADE)");
.executeUpdate("CREATE TABLE IF NOT EXISTS AccountChannel (account TEXT, channel TEXT, alias TEXT, ordinal INTEGER, role TEXT, PRIMARY KEY(account,channel), FOREIGN KEY(account) REFERENCES Account(id) ON DELETE CASCADE, FOREIGN KEY(channel) REFERENCES Channel(id) ON DELETE CASCADE)");
}
@Override
......@@ -59,12 +59,14 @@ public final class AccountStorage
account.channels.forEach(element -> {
String alias = element.first;
Stored<Channel> channel = element.second;
Role role = element.third;
try {
PreparedStatement statement1 = connection.prepareStatement("INSERT INTO AccountChannel VALUES(?,?,?,?");
PreparedStatement statement1 = connection.prepareStatement("INSERT INTO AccountChannel VALUES(?,?,?,?,?");
statement1.setObject(1, stored.identity);
statement1.setObject(2, channel.identity);
statement1.setString(3, alias);
statement1.setString(4, ordinal.get().toString());
statement1.setString(5, role.toString());
//Execute statement
statement1.executeUpdate();
......@@ -104,13 +106,14 @@ public final class AccountStorage
new_account.channels.forEach(element -> {
String alias = element.first;
Stored<Channel> channel = element.second;
Role role = element.third;
try {
PreparedStatement statement1 = connection.prepareStatement("INSERT INTO AccountChannel VALUES(?,?,?,?)");
PreparedStatement statement1 = connection.prepareStatement("INSERT INTO AccountChannel VALUES(?,?,?,?,?)");
statement1.setObject(1, account.identity);
statement1.setObject(2, channel.identity);
statement1.setString(3, alias);
statement1.setString(4, ordinal.get().toString());
statement1.setString(5,role.toString());
// Execute statement
statement1.executeUpdate();
......@@ -148,7 +151,7 @@ public final class AccountStorage
PreparedStatement accountStmt = connection.prepareStatement("SELECT version,user,password FROM Account WHERE id =?");
accountStmt.setString(1, id.toString());
PreparedStatement channelStmt = connection.prepareStatement("SELECT channel,alias,ordinal FROM AccountChannel WHERE account = ? ORDER BY ordinal DESC");
PreparedStatement channelStmt = connection.prepareStatement("SELECT channel,alias,ordinal,role FROM AccountChannel WHERE account = ? ORDER BY ordinal DESC");
channelStmt.setString(1, id.toString());
......@@ -163,14 +166,15 @@ public final class AccountStorage
accountResult.getString("password");
final Stored<User> user = userStore.get(userid);
// Get all the channels associated with this account
final List.Builder<Pair<String,Stored<Channel>>> channels = List.builder();
final List.Builder<Triple<String,Stored<Channel>,Role>> channels = List.builder();
while(channelResult.next()) {
final UUID channelId =
UUID.fromString(channelResult.getString("channel"));
final String alias = channelResult.getString("alias");
final Role role = Role.valueOf(channelResult.getString("role"));
channels.accept(
new Pair<String,Stored<Channel>>(
alias,channelStore.get(channelId)));
new Triple<String,Stored<Channel>,Role>(
alias,channelStore.get(channelId), role));
}
return (new Stored<Account>(new Account(user,channels.getList(),password),id,version));
} else {
......
......@@ -9,10 +9,8 @@ import java.io.IOException;
import org.eclipse.jetty.server.Server;
import org.eclipse.jetty.server.Request;
import org.eclipse.jetty.server.handler.AbstractHandler;
import java.util.TreeMap;
import java.util.ArrayList;
import java.util.Map;
import java.util.UUID;
import java.util.*;
import java.util.function.Consumer;
import java.lang.IllegalArgumentException;
import java.time.format.DateTimeFormatter;
......@@ -181,8 +179,12 @@ public class Handler extends AbstractHandler
Stored<Channel.Event> event = inchat.getEvent(messageId).get();
channel = inchat.editMessage(channel, event, message);
}
// TODO: Handle requests to change user roles on channel.
if (request.getParameter("setpermission") != null){
String targetedUserName = request.getParameter("username");
String newRole = request.getParameter("role");
System.out.println(targetedUserName + " has now got role " + newRole);
channel = inchat.setRole(account, channel, targetedUserName, newRole.toUpperCase()).get();
}
}
......@@ -282,7 +284,7 @@ public class Handler extends AbstractHandler
final UUID channelId
= UUID.fromString(idparam.get());
Stored<Channel> channel
= inchat.joinChannel(account,channelId).get();
= inchat.joinChannel(account,channelId, Role.PARTICIPANT).get();
response.setStatus(HttpServletResponse.SC_MOVED_TEMPORARILY);
response.setHeader("Location","/channel/" + channel.value.name);
baseRequest.setHandled(true);
......
......@@ -2,6 +2,7 @@ package inf226.inchat;
import inf226.storage.*;
import inf226.util.Maybe;
import inf226.util.Triple;
import inf226.util.Util;
import java.util.TreeMap;
......@@ -53,7 +54,7 @@ public class InChat {
*/
@FunctionalInterface
private interface Operation<T,E extends Throwable> {
void run(final Consumer<T> result) throws E,DeletedException;
void run(final Consumer<T> result) throws E, DeletedException, IllegalAccessException, UpdatedException, Maybe.NothingException;
}
/**
* Execute an operation atomically in SQL.
......@@ -66,9 +67,7 @@ public class InChat {
op.run(result);
connection.commit();
return result.getMaybe();
}catch (SQLException e) {
System.err.println(e.toString());
}catch (DeletedException e) {
} catch (DeletedException | IllegalAccessException | SQLException | UpdatedException | Maybe.NothingException e) {
System.err.println(e.toString());
}
try {
......@@ -138,7 +137,7 @@ public class InChat {
return atomic(result -> {
Stored<Channel> channel
= channelStore.save(new Channel(name,List.empty()));
joinChannel(account, channel.identity);
joinChannel(account, channel.identity, Role.OWNER);
result.accept(channel);
});
}
......@@ -146,22 +145,12 @@ public class InChat {
/**
* Join a channel.
*/
public Maybe<Stored<Channel>> joinChannel(Stored<Account> account,
UUID channelID) {
public Maybe<Stored<Channel>> joinChannel(Stored<Account> account, UUID channelID, Role role) {
return atomic(result -> {
Stored<Channel> channel = channelStore.get(channelID);
Util.updateSingle(account,
accountStore,
a -> a.value.joinChannel(channel.value.name,channel));
Stored<Channel.Event> joinEvent
= channelStore.eventStore.save(
Channel.Event.createJoinEvent(channelID,
Instant.now(),
account.value.user.value.userName.toString()));
result.accept(
Util.updateSingle(channel,
channelStore,
c -> c.value.postEvent(joinEvent)));
Util.updateSingle(account,accountStore,a -> a.value.joinChannel(channel.value.name,channel, role));
Stored<Channel.Event> joinEvent = channelStore.eventStore.save(Channel.Event.createJoinEvent(channelID, Instant.now(),account.value.user.value.userName.toString()));
result.accept(Util.updateSingle(channel,channelStore, c -> c.value.postEvent(joinEvent)));
});
}
......@@ -228,6 +217,25 @@ public class InChat {
result.accept(channelStore.noChangeUpdate(channel.identity));
}).defaultValue(channel);
}
public Maybe<Stored<Channel>> setRole(Stored<Account> account, Stored<Channel> channel, String targetUserName, String targetRoleString){
return atomic(result -> {
Role targetRole = Role.valueOf(targetRoleString.toUpperCase());
Role role = Util.lookupTriple(account.value.channels, channel.value.name).get();
if (!role.equals(Role.OWNER)) {
throw new IllegalAccessException("Non-owner tried to change a role");
}
Stored<Account> targetAccount = accountStore.lookup(new UserName(targetUserName).toString());
List<Triple<String, Stored<Channel>, Role>> newChannels = Util.replaceTriple(targetAccount.value.channels, channel, targetRole);
if (newChannels.equals(account.value.channels)) {
newChannels.add(new Triple<>(channel.value.name, channel, targetRole));
}
accountStore.update(targetAccount, new Account(account.value.user, newChannels, account.value.hashedPassword));
result.accept(channel);
});
}
}
package inf226.inchat;
public enum Role {
OWNER,
MODERATOR,
PARTICIPANT,
OBSERVER,
BANNED
}
package inf226.util;
import java.util.Objects;
public class Triple<A,B,C> {
public final A first;
public final B second;
public final C third;
public Triple(A first, B second, C third) {
this.first = first;
this.second = second;
this.third = third;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Triple<?, ?, ?> triple1 = (Triple<?, ?, ?>) o;
return Objects.equals(first, triple1.first) && Objects.equals(second, triple1.second) && Objects.equals(third, triple1.third);
}
@Override
public int hashCode() {
return Objects.hash(first, second, third);
}
}
......@@ -12,7 +12,7 @@ public class Util {
catch (Maybe.NothingException e) { /* Intensionally left blank */ }
}
public static<A,B> Maybe<B> lookup(List<Pair<A,B>> list, A key) {
public static<A,B,C> Maybe<B> lookup(List<Triple<A,B,C>> list, A key) {
final Maybe.Builder<B> result
= new Maybe.Builder<B>();
list.forEach(pair -> {
......@@ -22,12 +22,28 @@ public class Util {
return result.getMaybe();
}
public static <A,B,C> Maybe<C> lookupTriple(List<Triple<A,B,C>> list, A key){
final Maybe.Builder<C> result
= new Maybe.Builder<C>();
list.forEach(triple -> {
if(triple.first.equals(key))
result.accept(triple.third);
});
return result.getMaybe();
}
public static<A,B,C> List<Triple<A,B,C>> replaceTriple(List<Triple<A,B,C>> list, B key, C replacement){
return list.map(triple -> {
if (triple.second.equals(key)){
return new Triple<>(triple.first,triple.second,replacement);
}
else
return triple;
});
}
public static<A,Q, E extends Exception>
Stored<A> updateSingle(Stored<A> stored,
Storage<A,E> storage,
Function<Stored<A>,A> update)
throws E, DeletedException {
public static<A,Q, E extends Exception> Stored<A> updateSingle(Stored<A> stored, Storage<A,E> storage, Function<Stored<A>,A> update) throws E, DeletedException {
boolean updated = true;
while(true) {
try {
......@@ -38,8 +54,7 @@ public class Util {
}
}
public static<A,Q, E extends Exception> void deleteSingle(Stored<A> stored, Storage<A,E> storage)
throws E {
public static<A,Q, E extends Exception> void deleteSingle(Stored<A> stored, Storage<A,E> storage) throws E {
while(true) {
try {
storage.delete(stored);
......
......@@ -36,7 +36,7 @@ public class InchatTest{
Stored<Session> bobSession = inchat.login("Bob","worse").get();
Stored<Channel> channel = inchat.createChannel(aliceSession.value.account,"Awesome").get();
inchat.postMessage(aliceSession.value.account,channel, "Test message.").get();
inchat.joinChannel(bobSession.value.account,channel.identity).get();
inchat.joinChannel(bobSession.value.account,channel.identity, Role.PARTICIPANT).get();
connection.close();
}
}
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment