Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Staff channel list #16

Merged
merged 7 commits into from Nov 24, 2020
Merged
Show file tree
Hide file tree
Changes from 5 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
6 changes: 6 additions & 0 deletions .gitignore
@@ -1,3 +1,9 @@
# Emacs files

*~

# other files

HELP.md
target/
!.mvn/wrapper/maven-wrapper.jar
Expand Down
2 changes: 2 additions & 0 deletions javascript/src/main/App.js
Expand Up @@ -11,6 +11,7 @@ import PrivateRoute from "main/components/Auth/PrivateRoute";
import Admin from "main/pages/Admin/Admin";
import SlackUsers from "./pages/Admin/SlackUsers";
import AuthorizedRoute from "main/components/Nav/AuthorizedRoute";
import ChannelList from "main/pages/Channels/ChannelList";

function App() {
return (
Expand All @@ -20,6 +21,7 @@ function App() {
<Switch>
<AuthorizedRoute path="/admin" exact component={Admin} authorizedRoles={["admin"]} />
<AuthorizedRoute path="/admin/slackUsers" exact component={SlackUsers} authorizedRoles={["admin"]} />
<AuthorizedRoute path="/member/channels" exact component={ChannelList} authorizedRoles={["admin","member"]} />
<PrivateRoute path="/profile" component={Profile} />
<Route path="/about" component={About} />
<Route path="/" exact component={Home} />
Expand Down
21 changes: 21 additions & 0 deletions javascript/src/main/components/Channels/ChannelTable.js
@@ -0,0 +1,21 @@
import React from "react";
import BootstrapTable from 'react-bootstrap-table-next';

export default ({ channels }) => {

const columns = [{
dataField: 'name',
text: 'Name'
},{
dataField: 'purpose.value',
text: 'Purpose'
},{
dataField: 'topic.value',
text: 'Topic'
}
];

return (
<BootstrapTable keyField='id' data={channels} columns={columns} />
);
}
8 changes: 7 additions & 1 deletion javascript/src/main/components/Nav/AppNavbar.js
Expand Up @@ -15,19 +15,25 @@ function AppNavbar() {
fetchWithToken
);
const isAdmin = roleInfo && roleInfo.role.toLowerCase() === "admin";
const isMember = roleInfo && roleInfo.role.toLowerCase() === "member";

return (
<Navbar bg="dark" variant="dark">
<LinkContainer to={""}>
<Navbar.Brand data-testid="brand">Mapache Search</Navbar.Brand>
</LinkContainer>
<Nav>
{isAdmin &&
{ isAdmin &&
<NavDropdown title="Admin">
<NavDropdown.Item href="/admin">Maintain Admins</NavDropdown.Item>
<NavDropdown.Item href="/admin/slackUsers">Slack Users</NavDropdown.Item>
</NavDropdown>
}
{ (isMember || isAdmin) &&
<NavDropdown title="Channels">
<NavDropdown.Item href="/member/channels">List Channels</NavDropdown.Item>
</NavDropdown>
}
<LinkContainer to={"/about"}>
<Nav.Link>About</Nav.Link>
</LinkContainer>
Expand Down
18 changes: 18 additions & 0 deletions javascript/src/main/pages/Channels/ChannelList.js
@@ -0,0 +1,18 @@
import React from "react";
import { Jumbotron } from "react-bootstrap";
import {useAuth0} from "@auth0/auth0-react";
import { Redirect } from "react-router-dom";
import ChannelTable from "main/components/Channels/ChannelTable"
import useSWR from "swr";
import {fetchWithToken} from "../../utils/fetch";

const ChannelList = () => {
const { getAccessTokenSilently: getToken } = useAuth0();
const { data: channels } = useSWR(["/api/members/channels", getToken], fetchWithToken);

return (
<ChannelTable channels={channels || []} />
);
};

export default ChannelList;
61 changes: 61 additions & 0 deletions src/main/java/edu/ucsb/mapache/controllers/ChannelsController.java
@@ -0,0 +1,61 @@
package edu.ucsb.mapache.controllers;

import java.util.HashMap;
import java.util.Map;
import java.util.Optional;
import javax.validation.Valid;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import edu.ucsb.mapache.advice.AuthControllerAdvice;
import edu.ucsb.mapache.documents.Channel;
import edu.ucsb.mapache.documents.SlackUser;
import edu.ucsb.mapache.entities.Admin;
import edu.ucsb.mapache.entities.AppUser;
import edu.ucsb.mapache.repositories.AppUserRepository;
import edu.ucsb.mapache.repositories.ChannelRepository;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PutMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestHeader;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
@RequestMapping("/api/members")
public class ChannelsController {
private final Logger logger = LoggerFactory.getLogger(ChannelsController.class);

@Autowired
ChannelRepository channelRepository;

@Autowired
private AuthControllerAdvice authControllerAdvice;

private ObjectMapper mapper = new ObjectMapper();

private ResponseEntity<String> getUnauthorizedResponse(String roleRequired) throws JsonProcessingException {
Map<String, String> response = new HashMap<String, String>();
response.put("error", String.format("Unauthorized; only %s may access this resource.", roleRequired));
String body = mapper.writeValueAsString(response);
return new ResponseEntity<String>(body, HttpStatus.UNAUTHORIZED);
}

@GetMapping("/channels")
public ResponseEntity<String> getChannels(@RequestHeader("Authorization") String authorization)
throws JsonProcessingException {
if (!authControllerAdvice.getIsMember(authorization))
return getUnauthorizedResponse("member");

Iterable<Channel> channels = channelRepository.findAll();
String body = mapper.writeValueAsString(channels);

return ResponseEntity.ok().body(body);
}
}
181 changes: 181 additions & 0 deletions src/main/java/edu/ucsb/mapache/documents/Channel.java
@@ -0,0 +1,181 @@
package edu.ucsb.mapache.documents;

import java.util.List;
import java.util.Objects;
import org.apache.commons.lang3.builder.EqualsBuilder;
import org.springframework.data.mongodb.core.mapping.Document;

@Document(collection = "channels")
public class Channel {
private String id;
private String name;
private String creator;
private Boolean is_archived;
private Boolean is_general;
private List<String> members;
private ChannelTopic topic;
private ChannelPurpose purpose;

public Channel() {
}

public Channel(String id, String name, String creator, Boolean is_archived, Boolean is_general, List<String> members, ChannelTopic topic, ChannelPurpose purpose) {
this.id = id;
this.name = name;
this.creator = creator;
this.is_archived = is_archived;
this.is_general = is_general;
this.members = members;
this.topic = topic;
this.purpose = purpose;
}

public String getId() {
return this.id;
}

public void setId(String id) {
this.id = id;
}

public String getName() {
return this.name;
}

public void setName(String name) {
this.name = name;
}

public String getCreator() {
return this.creator;
}

public void setCreator(String creator) {
this.creator = creator;
}

public Boolean isIs_archived() {
return this.is_archived;
}

public Boolean getIs_archived() {
return this.is_archived;
}

public void setIs_archived(Boolean is_archived) {
this.is_archived = is_archived;
}

public Boolean isIs_general() {
return this.is_general;
}

public Boolean getIs_general() {
return this.is_general;
}

public void setIs_general(Boolean is_general) {
this.is_general = is_general;
}

public List<String> getMembers() {
return this.members;
}

public void setMembers(List<String> members) {
this.members = members;
}

public ChannelTopic getTopic() {
return this.topic;
}

public void setTopic(ChannelTopic topic) {
this.topic = topic;
}

public ChannelPurpose getPurpose() {
return this.purpose;
}

public void setPurpose(ChannelPurpose purpose) {
this.purpose = purpose;
}


@Override
public boolean equals(Object o) {
if (o == this)
return true;
if (!(o instanceof Channel)) {
return false;
}
Channel channel = (Channel) o;
return Objects.equals(id, channel.id) && Objects.equals(name, channel.name) && Objects.equals(creator, channel.creator) && Objects.equals(is_archived, channel.is_archived) && Objects.equals(is_general, channel.is_general) && Objects.equals(members, channel.members) && Objects.equals(topic, channel.topic) && Objects.equals(purpose, channel.purpose);
pconrad marked this conversation as resolved.
Show resolved Hide resolved
}

@Override
public int hashCode() {
return Objects.hash(id, name, creator, is_archived, is_general, members, topic, purpose);
}

@Override
public String toString() {
return "{" +
" id='" + getId() + "'" +
", name='" + getName() + "'" +
", creator='" + getCreator() + "'" +
", is_archived='" + isIs_archived() + "'" +
", is_general='" + isIs_general() + "'" +
", members='" + getMembers() + "'" +
", topic='" + getTopic() + "'" +
", purpose='" + getPurpose() + "'" +
"}";
}


}

/*

Example of the JSON for a Channel document

{
"_id": {
"$oid": "5fb068cd0a8dccb8dbab0a82"
},
"id": "C016GMB0H5L",
"name": "section-6pm",
"created": {
"$numberInt": "1594143066"
},
"creator": "U017218J9B3",
"is_archived": false,
"is_general": false,
"members": [
"U017218J9B3",
"U0185QQSJBY",
"U018CH1NLPL",
"U018R1AULF3",
"U018XEKMRTM",
"U0192BC785N",
"U019B1Q0YHW"
],
"topic": {
"value": "",
"creator": "",
"last_set": {
"$numberInt": "0"
}
},
"purpose": {
"value": "",
"creator": "",
"last_set": {
"$numberInt": "0"
}
}
}


*/