From 0caa803d276892cd6c16f411442fb29eae7f3ca2 Mon Sep 17 00:00:00 2001 From: Jordan Knott Date: Wed, 12 Aug 2020 20:54:14 -0500 Subject: [PATCH] feat: add notification UI showPopup was also refactored to be better --- frontend/package.json | 3 + frontend/src/App/TopNavbar.tsx | 31 +- frontend/src/App/index.tsx | 46 + frontend/src/Profile/index.tsx | 8 + frontend/src/Projects/Project/Board/index.tsx | 6 +- .../src/Projects/Project/Details/index.tsx | 5 +- frontend/src/Teams/index.tsx | 2 +- .../components/NotifcationPopup/index.tsx | 111 +++ .../src/shared/components/PopupMenu/Styles.ts | 33 +- .../src/shared/components/PopupMenu/index.tsx | 132 ++- .../src/shared/components/TopNavbar/Styles.ts | 8 +- .../src/shared/components/TopNavbar/index.tsx | 40 +- frontend/src/shared/generated/graphql.tsx | 165 +++- frontend/src/shared/graphql/topNavbar.ts | 43 + frontend/yarn.lock | 31 + go.mod | 10 +- go.sum | 253 ++++- internal/commands/commands.go | 2 +- internal/commands/web.go | 4 + internal/commands/worker.go | 83 ++ internal/db/models.go | 16 + internal/db/notification.sql.go | 177 ++++ internal/db/querier.go | 6 + internal/db/query/notification.sql | 28 + internal/graph/generated.go | 891 +++++++++++++++++- internal/graph/graph.go | 55 +- internal/graph/models_gen.go | 129 +++ internal/graph/schema.graphqls | 41 +- internal/graph/schema.resolvers.go | 75 ++ internal/graph/schema/notification.gql | 36 + internal/graph/schema/task.gql | 4 +- internal/notification/logger.go | 53 ++ internal/notification/notification.go | 78 ++ .../0051_add-notification-tables.up.sql | 15 + 34 files changed, 2516 insertions(+), 104 deletions(-) create mode 100644 frontend/src/shared/components/NotifcationPopup/index.tsx create mode 100644 frontend/src/shared/graphql/topNavbar.ts create mode 100644 internal/commands/worker.go create mode 100644 internal/db/notification.sql.go create mode 100644 internal/db/query/notification.sql create mode 100644 internal/graph/schema/notification.gql create mode 100644 internal/notification/logger.go create mode 100644 internal/notification/notification.go create mode 100644 migrations/0051_add-notification-tables.up.sql diff --git a/frontend/package.json b/frontend/package.json index 6e05ebd..8d8338c 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -28,6 +28,7 @@ "@types/react-router": "^5.1.4", "@types/react-router-dom": "^5.1.3", "@types/react-select": "^3.0.13", + "@types/react-timeago": "^4.1.1", "@types/styled-components": "^5.0.0", "apollo-cache-inmemory": "^1.6.5", "apollo-client": "^2.6.8", @@ -60,6 +61,8 @@ "react-scripts": "3.4.0", "react-select": "^3.1.0", "rich-markdown-editor": "^10.6.5", + "react-timeago": "^4.4.0", + "react-toastify": "^6.0.8", "styled-components": "^5.0.1", "typescript": "~3.7.2" }, diff --git a/frontend/src/App/TopNavbar.tsx b/frontend/src/App/TopNavbar.tsx index b4cf041..949faa2 100644 --- a/frontend/src/App/TopNavbar.tsx +++ b/frontend/src/App/TopNavbar.tsx @@ -7,7 +7,7 @@ import { useHistory } from 'react-router'; import { PermissionLevel, PermissionObjectType, useCurrentUser } from 'App/context'; import { RoleCode, - useMeQuery, + useTopNavbarQuery, useDeleteProjectMutation, useGetProjectsQuery, GetProjectsDocument, @@ -19,6 +19,7 @@ import { Link } from 'react-router-dom'; import MiniProfile from 'shared/components/MiniProfile'; import cache from 'App/cache'; import NOOP from 'shared/utils/noop'; +import NotificationPopup, { NotificationItem } from 'shared/components/NotifcationPopup'; const TeamContainer = styled.div` display: flex; @@ -197,7 +198,7 @@ export const ProjectPopup: React.FC = ({ history, name, proje { - setTab(1, 300); + setTab(1, { width: 300 }); }} /> @@ -250,7 +251,7 @@ const GlobalTopNavbar: React.FC = ({ onRemoveFromBoard, }) => { const { user, setUserRoles, setUser } = useCurrentUser(); - const { data } = useMeQuery({ + const { loading, data } = useTopNavbarQuery({ onCompleted: response => { if (user && user.roles) { setUserRoles({ @@ -300,13 +301,31 @@ const GlobalTopNavbar: React.FC = ({ }} /> , - 195, + { width: 195 }, ); }; const onOpenSettings = ($target: React.RefObject) => { if (popupContent) { - showPopup($target, popupContent, 185); + showPopup($target, popupContent, { width: 185 }); + } + }; + + const onNotificationClick = ($target: React.RefObject) => { + if (data) { + showPopup( + $target, + + {data.notifications.map(notification => ( + + ))} + , + { width: 415, borders: false, diamondColor: '#7367f0' }, + ); } }; @@ -366,7 +385,7 @@ const GlobalTopNavbar: React.FC = ({ onInviteUser={onInviteUser} onChangeRole={onChangeRole} onChangeProjectOwner={onChangeProjectOwner} - onNotificationClick={NOOP} + onNotificationClick={onNotificationClick} onSetTab={onSetTab} onRemoveFromBoard={onRemoveFromBoard} onDashboardClick={() => { diff --git a/frontend/src/App/index.tsx b/frontend/src/App/index.tsx index 0e4ef7f..4c2ef58 100644 --- a/frontend/src/App/index.tsx +++ b/frontend/src/App/index.tsx @@ -3,6 +3,7 @@ import jwtDecode from 'jwt-decode'; import { createBrowserHistory } from 'history'; import { Router } from 'react-router'; import { PopupProvider } from 'shared/components/PopupMenu'; +import { ToastContainer } from 'react-toastify'; import { setAccessToken } from 'shared/utils/accessToken'; import styled, { ThemeProvider } from 'styled-components'; import NormalizeStyles from './NormalizeStyles'; @@ -11,6 +12,39 @@ import theme from './ThemeStyles'; import Routes from './Routes'; import { UserContext, CurrentUserRaw, CurrentUserRoles, PermissionLevel, PermissionObjectType } from './context'; +import 'react-toastify/dist/ReactToastify.css'; + +const StyledContainer = styled(ToastContainer).attrs({ + // custom props +})` + .Toastify__toast-container { + } + .Toastify__toast { + padding: 5px; + margin-left: 5px; + margin-right: 5px; + border-radius: 10px; + background: #7367f0; + color: #fff; + } + .Toastify__toast--error { + background: rgba(${props => props.theme.colors.danger}); + } + .Toastify__toast--warning { + background: rgba(${props => props.theme.colors.warning}); + } + .Toastify__toast--success { + background: rgba(${props => props.theme.colors.success}); + } + .Toastify__toast-body { + } + .Toastify__progress-bar { + } + .Toastify__close-button { + display: none; + } +`; + const history = createBrowserHistory(); type RefreshTokenResponse = { accessToken: string; @@ -72,6 +106,18 @@ const App = () => { )} + diff --git a/frontend/src/Profile/index.tsx b/frontend/src/Profile/index.tsx index 2974f1f..26cb9f9 100644 --- a/frontend/src/Profile/index.tsx +++ b/frontend/src/Profile/index.tsx @@ -7,6 +7,13 @@ import { useMeQuery, useClearProfileAvatarMutation, useUpdateUserPasswordMutatio import axios from 'axios'; import { useCurrentUser } from 'App/context'; import NOOP from 'shared/utils/noop'; +import { toast } from 'react-toastify'; + +const MainContent = styled.div` + padding: 0 0 50px 80px; + height: 100%; + background: #262c49; +`; const Projects = () => { const $fileUpload = useRef(null); @@ -59,6 +66,7 @@ const Projects = () => { }} onResetPassword={(password, done) => { updateUserPassword({ variables: { userID: user.id, password } }); + toast('Password was changed!'); done(); }} onProfileAvatarRemove={() => { diff --git a/frontend/src/Projects/Project/Board/index.tsx b/frontend/src/Projects/Project/Board/index.tsx index a3c9d5d..4bf375a 100644 --- a/frontend/src/Projects/Project/Board/index.tsx +++ b/frontend/src/Projects/Project/Board/index.tsx @@ -460,7 +460,7 @@ const ProjectBoard: React.FC = ({ projectID, onCardLabelClick }} /> , - 185, + { width: 185 }, ); }} > @@ -479,7 +479,7 @@ const ProjectBoard: React.FC = ({ projectID, onCardLabelClick }} /> , - 185, + { width: 185 }, ); }} > @@ -499,7 +499,7 @@ const ProjectBoard: React.FC = ({ projectID, onCardLabelClick labels={labelsRef} members={membersRef} />, - 200, + { width: 200 }, ); }} > diff --git a/frontend/src/Projects/Project/Details/index.tsx b/frontend/src/Projects/Project/Details/index.tsx index 2da521b..9a69a4f 100644 --- a/frontend/src/Projects/Project/Details/index.tsx +++ b/frontend/src/Projects/Project/Details/index.tsx @@ -290,10 +290,10 @@ const Details: React.FC = ({ }, }); if (loading) { - return
loading
; + return null; } if (!data) { - return
loading
; + return null; } return ( <> @@ -501,6 +501,7 @@ const Details: React.FC = ({ onCancel={NOOP} /> , + { showDiamond: false, targetPadding: '0' }, ); }} /> diff --git a/frontend/src/Teams/index.tsx b/frontend/src/Teams/index.tsx index 4aa76d2..012fcc7 100644 --- a/frontend/src/Teams/index.tsx +++ b/frontend/src/Teams/index.tsx @@ -57,7 +57,7 @@ export const TeamPopup: React.FC = ({ history, name, teamID }) = { - setTab(1, 340); + setTab(1, { width: 340 }); }} /> diff --git a/frontend/src/shared/components/NotifcationPopup/index.tsx b/frontend/src/shared/components/NotifcationPopup/index.tsx new file mode 100644 index 0000000..2b2090f --- /dev/null +++ b/frontend/src/shared/components/NotifcationPopup/index.tsx @@ -0,0 +1,111 @@ +import React from 'react'; +import styled from 'styled-components'; +import TimeAgo from 'react-timeago'; + +import { Popup } from 'shared/components/PopupMenu'; + +const ItemWrapper = styled.div` + cursor: pointer; + border-bottom: 1px solid #414561; + padding-left: 1rem; + padding-right: 1rem; + padding-top: 1rem; + padding-bottom: 1rem; + justify-content: space-between; + display: flex; + + &:hover { + background: #10163a; + } +`; +const ItemWrapperContent = styled.div` + display: flex; + align-items: flex-start; +`; + +const ItemIconContainer = styled.span` + position: relative; + display: inline-flex; + align-items: center; +`; + +const ItemTextContainer = styled.div` + margin-left: 0.5rem; + margin-right: 0.5rem; +`; + +const ItemTextTitle = styled.span` + font-weight: 500; + display: block; + color: rgba(${props => props.theme.colors.primary}); + font-size: 14px; +`; +const ItemTextDesc = styled.span` + font-size: 12px; +`; + +const ItemTimeAgo = styled.span` + margin-top: 0.25rem; + white-space: nowrap; + font-size: 11px; +`; + +type NotificationItemProps = { + title: string; + description: string; + createdAt: string; +}; + +export const NotificationItem: React.FC = ({ title, description, createdAt }) => { + return ( + + + + + {title} + {description} + + + + + ); +}; + +const NotificationHeader = styled.div` + padding: 0.75rem; + text-align: center; + border-top-left-radius: 6px; + border-top-right-radius: 6px; + background: rgba(${props => props.theme.colors.primary}); +`; + +const NotificationHeaderTitle = styled.span` + font-size: 14px; + color: rgba(${props => props.theme.colors.text.secondary}); +`; + +const NotificationFooter = styled.div` + cursor: pointer; + padding: 0.5rem; + text-align: center; + color: rgba(${props => props.theme.colors.primary}); + &:hover { + background: #10163a; + } + border-bottom-left-radius: 6px; + border-bottom-right-radius: 6px; +`; + +const NotificationPopup: React.FC = ({ children }) => { + return ( + + + Notifications + +
    {children}
+ View All +
+ ); +}; + +export default NotificationPopup; diff --git a/frontend/src/shared/components/PopupMenu/Styles.ts b/frontend/src/shared/components/PopupMenu/Styles.ts index 8ec9505..d781dda 100644 --- a/frontend/src/shared/components/PopupMenu/Styles.ts +++ b/frontend/src/shared/components/PopupMenu/Styles.ts @@ -5,6 +5,7 @@ import ControlledInput from 'shared/components/ControlledInput'; export const Container = styled.div<{ invertY: boolean; invert: boolean; + targetPadding: string; top: number; left: number; ref: any; @@ -15,7 +16,7 @@ export const Container = styled.div<{ display: block; position: absolute; width: ${props => props.width}px; - padding-top: 10px; + padding-top: ${props => props.targetPadding}; height: auto; z-index: 40000; ${props => @@ -28,14 +29,18 @@ export const Container = styled.div<{ css` top: auto; padding-top: 0; - padding-bottom: 10px; + padding-bottom: ${props.targetPadding}; bottom: ${props.top}px; `} `; -export const Wrapper = styled.div` - padding: 5px; - padding-top: 8px; +export const Wrapper = styled.div<{ padding: boolean; borders: boolean }>` + ${props => + props.padding && + css` + padding: 5px; + padding-top: 8px; + `} border-radius: 5px; box-shadow: 0 5px 25px 0 rgba(0, 0, 0, 0.1); position: relative; @@ -43,8 +48,12 @@ export const Wrapper = styled.div` color: #c2c6dc; background: #262c49; - border: 1px solid rgba(0, 0, 0, 0.1); - border-color: #414561; + ${props => + props.borders && + css` + border: 1px solid rgba(0, 0, 0, 0.1); + border-color: #414561; + `} `; export const Header = styled.div` @@ -326,7 +335,7 @@ export const PreviousButton = styled.div` cursor: pointer; `; -export const ContainerDiamond = styled.div<{ invert: boolean; invertY: boolean }>` +export const ContainerDiamond = styled.div<{ borders: boolean; color: string; invert: boolean; invertY: boolean }>` ${props => (props.invert ? 'right: 10px; ' : 'left: 15px;')} position: absolute; width: 10px; @@ -347,6 +356,10 @@ export const ContainerDiamond = styled.div<{ invert: boolean; invertY: boolean } transform: rotate(45deg) translate(-7px); z-index: 10; - background: #262c49; - border-color: #414561; + background: ${props => props.color}; + ${props => + props.borders && + css` + border-color: #414561; + `} `; diff --git a/frontend/src/shared/components/PopupMenu/index.tsx b/frontend/src/shared/components/PopupMenu/index.tsx index dabd49c..8dce11f 100644 --- a/frontend/src/shared/components/PopupMenu/index.tsx +++ b/frontend/src/shared/components/PopupMenu/index.tsx @@ -15,9 +15,37 @@ import { Wrapper, } from './Styles'; +function getPopupOptions(options?: PopupOptions) { + const popupOptions = { + borders: true, + diamondColor: '#262c49', + targetPadding: '10px', + showDiamond: true, + width: 316, + }; + if (options) { + if (options.borders) { + popupOptions.borders = options.borders; + } + if (options.width) { + popupOptions.width = options.width; + } + if (options.targetPadding) { + popupOptions.targetPadding = options.targetPadding; + } + if (typeof options.showDiamond !== 'undefined' && options.showDiamond !== null) { + popupOptions.showDiamond = options.showDiamond; + } + if (options.diamondColor) { + popupOptions.diamondColor = options.diamondColor; + } + } + return popupOptions; +} + type PopupContextState = { - show: (target: RefObject, content: JSX.Element, width?: string | number) => void; - setTab: (newTab: number, width?: number | string) => void; + show: (target: RefObject, content: JSX.Element, options?: PopupOptions) => void; + setTab: (newTab: number, options?: PopupOptions) => void; getCurrentTab: () => number; hide: () => void; }; @@ -26,23 +54,44 @@ type PopupProps = { title: string | null; onClose?: () => void; tab: number; + padding?: boolean; + borders?: boolean; + diamondColor?: string; }; type PopupContainerProps = { top: number; left: number; invert: boolean; + targetPadding: string; invertY: boolean; onClose: () => void; width?: string | number; }; -const PopupContainer: React.FC = ({ width, top, left, onClose, children, invert, invertY }) => { +const PopupContainer: React.FC = ({ + width, + top, + left, + onClose, + children, + invert, + invertY, + targetPadding, +}) => { const $containerRef = useRef(null); const [currentTop, setCurrentTop] = useState(top); useOnOutsideClick($containerRef, true, onClose, null); return ( - + {children} ); @@ -73,13 +122,28 @@ type PopupState = { currentTab: number; previousTab: number; content: JSX.Element | null; - width?: string | number; + options: PopupOptionsInternal | null; }; const { Provider, Consumer } = PopupContext; const canUseDOM = !!(typeof window !== 'undefined' && window.document && window.document.createElement); +type PopupOptionsInternal = { + width: number; + borders: boolean; + targetPadding: string; + diamondColor: string; + showDiamond: boolean; +}; + +type PopupOptions = { + targetPadding?: string | null; + showDiamond?: boolean | null; + width?: number | null; + borders?: boolean | null; + diamondColor?: string | null; +}; const defaultState = { isOpen: false, left: 0, @@ -89,11 +153,12 @@ const defaultState = { currentTab: 0, previousTab: 0, content: null, + options: null, }; export const PopupProvider: React.FC = ({ children }) => { const [currentState, setState] = useState(defaultState); - const show = (target: RefObject, content: JSX.Element, width?: number | string) => { + const show = (target: RefObject, content: JSX.Element, options?: PopupOptions) => { if (target && target.current) { const bounds = target.current.getBoundingClientRect(); let top = bounds.top + bounds.height; @@ -102,6 +167,7 @@ export const PopupProvider: React.FC = ({ children }) => { top = window.innerHeight - bounds.top; invertY = true; } + const popupOptions = getPopupOptions(options); if (bounds.left + 304 + 30 > window.innerWidth) { setState({ isOpen: true, @@ -112,7 +178,7 @@ export const PopupProvider: React.FC = ({ children }) => { currentTab: 0, previousTab: 0, content, - width: width ?? 316, + options: popupOptions, }); } else { setState({ @@ -124,7 +190,7 @@ export const PopupProvider: React.FC = ({ children }) => { currentTab: 0, previousTab: 0, content, - width: width ?? 316, + options: popupOptions, }); } } @@ -139,20 +205,21 @@ export const PopupProvider: React.FC = ({ children }) => { currentTab: 0, previousTab: 0, content: null, + options: null, }); }; const portalTarget = canUseDOM ? document.body : null; // appease flow - const setTab = (newTab: number, width?: number | string) => { - const newWidth = width ?? currentState.width; - setState((prevState: PopupState) => { - return { - ...prevState, - previousTab: currentState.currentTab, - currentTab: newTab, - width: newWidth, - }; - }); + const setTab = (newTab: number, options?: PopupOptions) => { + setState((prevState: PopupState) => + produce(prevState, draftState => { + draftState.previousTab = currentState.currentTab; + draftState.currentTab = newTab; + if (options) { + draftState.options = getPopupOptions(options); + } + }), + ); }; const getCurrentTab = () => { @@ -163,17 +230,26 @@ export const PopupProvider: React.FC = ({ children }) => { {portalTarget && currentState.isOpen && + currentState.options && createPortal( setState(defaultState)} - width={currentState.width ?? 316} + width={currentState.options.width} > {currentState.content} - + {currentState.options.showDiamond && ( + + )} , portalTarget, )} @@ -197,8 +273,16 @@ const PopupMenu: React.FC = ({ width, title, top, left, onClose, noHeader useOnOutsideClick($containerRef, true, onClose, null); return ( - - + + {onPrevious && ( @@ -222,7 +306,7 @@ const PopupMenu: React.FC = ({ width, title, top, left, onClose, noHeader ); }; -export const Popup: React.FC = ({ title, onClose, tab, children }) => { +export const Popup: React.FC = ({ borders = true, padding = true, title, onClose, tab, children }) => { const { getCurrentTab, setTab } = usePopup(); if (getCurrentTab() !== tab) { return null; @@ -230,7 +314,7 @@ export const Popup: React.FC = ({ title, onClose, tab, children }) = return ( <> - + {tab > 0 && ( { diff --git a/frontend/src/shared/components/TopNavbar/Styles.ts b/frontend/src/shared/components/TopNavbar/Styles.ts index c940503..29af821 100644 --- a/frontend/src/shared/components/TopNavbar/Styles.ts +++ b/frontend/src/shared/components/TopNavbar/Styles.ts @@ -5,6 +5,7 @@ import Button from 'shared/components/Button'; import { Taskcafe } from 'shared/icons'; import { NavLink, Link } from 'react-router-dom'; import TaskAssignee from 'shared/components/TaskAssignee'; +import { useRef } from 'react'; export const ProjectMember = styled(TaskAssignee)<{ zIndex: number }>` z-index: ${props => props.zIndex}; @@ -65,7 +66,7 @@ export const ProfileNameWrapper = styled.div` line-height: 1.25; `; -export const IconContainer = styled.div<{ disabled?: boolean }>` +export const IconContainerWrapper = styled.div<{ disabled?: boolean }>` margin-right: 20px; cursor: pointer; ${props => @@ -86,7 +87,10 @@ export const ProfileNameSecondary = styled.small` color: #c2c6dc; `; -export const ProfileIcon = styled.div<{ bgColor: string | null; backgroundURL: string | null }>` +export const ProfileIcon = styled.div<{ + bgColor: string | null; + backgroundURL: string | null; +}>` width: 40px; height: 40px; border-radius: 9999px; diff --git a/frontend/src/shared/components/TopNavbar/index.tsx b/frontend/src/shared/components/TopNavbar/index.tsx index 1c7ba0e..00ac13d 100644 --- a/frontend/src/shared/components/TopNavbar/index.tsx +++ b/frontend/src/shared/components/TopNavbar/index.tsx @@ -1,19 +1,17 @@ import React, { useRef, useState, useEffect } from 'react'; import { Home, Star, Bell, AngleDown, BarChart, CheckCircle } from 'shared/icons'; -import { Link } from 'react-router-dom'; import styled from 'styled-components'; import ProfileIcon from 'shared/components/ProfileIcon'; -import TaskAssignee from 'shared/components/TaskAssignee'; -import { usePopup, Popup } from 'shared/components/PopupMenu'; +import { usePopup } from 'shared/components/PopupMenu'; import { RoleCode } from 'shared/generated/graphql'; -import MiniProfile from 'shared/components/MiniProfile'; +import NOOP from 'shared/utils/noop'; import { TaskcafeLogo, TaskcafeTitle, ProjectFinder, LogoContainer, NavSeparator, - IconContainer, + IconContainerWrapper, ProjectNameTextarea, InviteButton, GlobalActions, @@ -33,6 +31,28 @@ import { ProjectMembers, } from './Styles'; +type IconContainerProps = { + disabled?: boolean; + onClick?: ($target: React.RefObject) => void; +}; + +const IconContainer: React.FC = ({ onClick, disabled = false, children }) => { + const $container = useRef(null); + return ( + { + if (onClick) { + onClick($container); + } + }} + > + {children} + + ); +}; + const HomeDashboard = styled(Home)``; type ProjectHeadingProps = { @@ -145,7 +165,7 @@ type NavBarProps = { onFavorite?: () => void; onProfileClick: ($target: React.RefObject) => void; onSaveName?: (name: string) => void; - onNotificationClick: () => void; + onNotificationClick: ($target: React.RefObject) => void; canEditProjectName?: boolean; canInviteUser?: boolean; onInviteUser?: ($target: React.RefObject) => void; @@ -257,16 +277,16 @@ const NavBar: React.FC = ({ Projects - + onDashboardClick()}> - + - + - + diff --git a/frontend/src/shared/generated/graphql.tsx b/frontend/src/shared/generated/graphql.tsx index 8f0982f..ef18598 100644 --- a/frontend/src/shared/generated/graphql.tsx +++ b/frontend/src/shared/generated/graphql.tsx @@ -213,22 +213,18 @@ export enum ObjectType { export type Query = { __typename?: 'Query'; - organizations: Array; - users: Array; - findUser: UserAccount; findProject: Project; findTask: Task; - projects: Array; findTeam: Team; - teams: Array; + findUser: UserAccount; labelColors: Array; - taskGroups: Array; me: MePayload; -}; - - -export type QueryFindUserArgs = { - input: FindUser; + notifications: Array; + organizations: Array; + projects: Array; + taskGroups: Array; + teams: Array; + users: Array; }; @@ -242,13 +238,18 @@ export type QueryFindTaskArgs = { }; -export type QueryProjectsArgs = { - input?: Maybe; +export type QueryFindTeamArgs = { + input: FindTeam; }; -export type QueryFindTeamArgs = { - input: FindTeam; +export type QueryFindUserArgs = { + input: FindUser; +}; + + +export type QueryProjectsArgs = { + input?: Maybe; }; export type Mutation = { @@ -577,6 +578,42 @@ export type FindTeam = { teamID: Scalars['UUID']; }; +export enum EntityType { + Task = 'TASK' +} + +export enum ActorType { + User = 'USER' +} + +export enum ActionType { + TaskMemberAdded = 'TASK_MEMBER_ADDED' +} + +export type NotificationActor = { + __typename?: 'NotificationActor'; + id: Scalars['UUID']; + type: ActorType; + name: Scalars['String']; +}; + +export type NotificationEntity = { + __typename?: 'NotificationEntity'; + id: Scalars['UUID']; + type: EntityType; + name: Scalars['String']; +}; + +export type Notification = { + __typename?: 'Notification'; + id: Scalars['ID']; + entity: NotificationEntity; + actionType: ActionType; + actor: NotificationActor; + read: Scalars['Boolean']; + createdAt: Scalars['Time']; +}; + export type NewProject = { userID: Scalars['UUID']; teamID: Scalars['UUID']; @@ -1755,6 +1792,40 @@ export type ToggleTaskLabelMutation = ( ) } ); +export type TopNavbarQueryVariables = {}; + + +export type TopNavbarQuery = ( + { __typename?: 'Query' } + & { notifications: Array<( + { __typename?: 'Notification' } + & Pick + & { entity: ( + { __typename?: 'NotificationEntity' } + & Pick + ), actor: ( + { __typename?: 'NotificationActor' } + & Pick + ) } + )>, me: ( + { __typename?: 'MePayload' } + & { user: ( + { __typename?: 'UserAccount' } + & Pick + & { profileIcon: ( + { __typename?: 'ProfileIcon' } + & Pick + ) } + ), teamRoles: Array<( + { __typename?: 'TeamRole' } + & Pick + )>, projectRoles: Array<( + { __typename?: 'ProjectRole' } + & Pick + )> } + ) } +); + export type UnassignTaskMutationVariables = { taskID: Scalars['UUID']; userID: Scalars['UUID']; @@ -3613,6 +3684,70 @@ export function useToggleTaskLabelMutation(baseOptions?: ApolloReactHooks.Mutati export type ToggleTaskLabelMutationHookResult = ReturnType; export type ToggleTaskLabelMutationResult = ApolloReactCommon.MutationResult; export type ToggleTaskLabelMutationOptions = ApolloReactCommon.BaseMutationOptions; +export const TopNavbarDocument = gql` + query topNavbar { + notifications { + createdAt + read + id + entity { + id + type + name + } + actor { + id + type + name + } + actionType + } + me { + user { + id + fullName + profileIcon { + initials + bgColor + url + } + } + teamRoles { + teamID + roleCode + } + projectRoles { + projectID + roleCode + } + } +} + `; + +/** + * __useTopNavbarQuery__ + * + * To run a query within a React component, call `useTopNavbarQuery` and pass it any options that fit your needs. + * When your component renders, `useTopNavbarQuery` returns an object from Apollo Client that contains loading, error, and data properties + * you can use to render your UI. + * + * @param baseOptions options that will be passed into the query, supported options are listed on: https://www.apollographql.com/docs/react/api/react-hooks/#options; + * + * @example + * const { data, loading, error } = useTopNavbarQuery({ + * variables: { + * }, + * }); + */ +export function useTopNavbarQuery(baseOptions?: ApolloReactHooks.QueryHookOptions) { + return ApolloReactHooks.useQuery(TopNavbarDocument, baseOptions); + } +export function useTopNavbarLazyQuery(baseOptions?: ApolloReactHooks.LazyQueryHookOptions) { + return ApolloReactHooks.useLazyQuery(TopNavbarDocument, baseOptions); + } +export type TopNavbarQueryHookResult = ReturnType; +export type TopNavbarLazyQueryHookResult = ReturnType; +export type TopNavbarQueryResult = ApolloReactCommon.QueryResult; export const UnassignTaskDocument = gql` mutation unassignTask($taskID: UUID!, $userID: UUID!) { unassignTask(input: {taskID: $taskID, userID: $userID}) { diff --git a/frontend/src/shared/graphql/topNavbar.ts b/frontend/src/shared/graphql/topNavbar.ts new file mode 100644 index 0000000..2177afc --- /dev/null +++ b/frontend/src/shared/graphql/topNavbar.ts @@ -0,0 +1,43 @@ +import gql from 'graphql-tag'; + +export const TOP_NAVBAR_QUERY = gql` + query topNavbar { + notifications { + createdAt + read + id + entity { + id + type + name + } + actor { + id + type + name + } + actionType + } + me { + user { + id + fullName + profileIcon { + initials + bgColor + url + } + } + teamRoles { + teamID + roleCode + } + projectRoles { + projectID + roleCode + } + } + } +`; + +export default TOP_NAVBAR_QUERY; diff --git a/frontend/yarn.lock b/frontend/yarn.lock index c652f68..2aa6ef8 100644 --- a/frontend/yarn.lock +++ b/frontend/yarn.lock @@ -3290,6 +3290,13 @@ dependencies: "@types/react" "*" +"@types/react-timeago@^4.1.1": + version "4.1.1" + resolved "https://registry.yarnpkg.com/@types/react-timeago/-/react-timeago-4.1.1.tgz#14c24cb8c1299379d9c2941ca3373f849d8bc78a" + integrity sha512-Rokr09qNqkKXpKwrDVYzGz/mGlmgX5ZdxAJToUehMyRHXEwtXUUNaAZur5RoZ5JtukBwXKzDevS72Axtjm+yKg== + dependencies: + "@types/react" "*" + "@types/react-transition-group@*": version "4.4.0" resolved "https://registry.yarnpkg.com/@types/react-transition-group/-/react-transition-group-4.4.0.tgz#882839db465df1320e4753e6e9f70ca7e9b4d46d" @@ -13986,6 +13993,20 @@ react-textarea-autosize@^7.1.0: "@babel/runtime" "^7.1.2" prop-types "^15.6.0" +react-timeago@^4.4.0: + version "4.4.0" + resolved "https://registry.yarnpkg.com/react-timeago/-/react-timeago-4.4.0.tgz#4520dd9ba63551afc4d709819f52b14b9343ba2b" + integrity sha512-Zj8RchTqZEH27LAANemzMR2RpotbP2aMd+UIajfYMZ9KW4dMcViUVKzC7YmqfiqlFfz8B0bjDw2xUBjmcxDngA== + +react-toastify@^6.0.8: + version "6.0.8" + resolved "https://registry.yarnpkg.com/react-toastify/-/react-toastify-6.0.8.tgz#84625d81d0fd01902a7f4c6f317eb074cb3bba67" + integrity sha512-NSqCNwv+C4IfR+c92PFZiNyeBwOJvigrP2bcRi2f6Hg3WqcHhEHOknbSQOs9QDFuqUjmK3SOrdvScQ3z63ifXg== + dependencies: + classnames "^2.2.6" + prop-types "^15.7.2" + react-transition-group "^4.4.1" + react-transition-group@^4.3.0: version "4.3.0" resolved "https://registry.yarnpkg.com/react-transition-group/-/react-transition-group-4.3.0.tgz#fea832e386cf8796c58b61874a3319704f5ce683" @@ -13996,6 +14017,16 @@ react-transition-group@^4.3.0: loose-envify "^1.4.0" prop-types "^15.6.2" +react-transition-group@^4.4.1: + version "4.4.1" + resolved "https://registry.yarnpkg.com/react-transition-group/-/react-transition-group-4.4.1.tgz#63868f9325a38ea5ee9535d828327f85773345c9" + integrity sha512-Djqr7OQ2aPUiYurhPalTrVy9ddmFCCzwhqQmtN+J3+3DzLO209Fdr70QrN8Z3DsglWql6iY1lDWAfpFiBtuKGw== + dependencies: + "@babel/runtime" "^7.5.5" + dom-helpers "^5.0.1" + loose-envify "^1.4.0" + prop-types "^15.6.2" + react@^16.12.0, react@^16.8.3: version "16.12.0" resolved "https://registry.yarnpkg.com/react/-/react-16.12.0.tgz#0c0a9c6a142429e3614834d5a778e18aa78a0b83" diff --git a/go.mod b/go.mod index 1dbc979..6650609 100644 --- a/go.mod +++ b/go.mod @@ -4,10 +4,10 @@ go 1.13 require ( github.com/99designs/gqlgen v0.11.3 + github.com/RichardKnop/machinery v1.9.1 github.com/dgrijalva/jwt-go v3.2.0+incompatible github.com/go-chi/chi v3.3.2+incompatible github.com/golang-migrate/migrate/v4 v4.11.0 - github.com/golang/protobuf v1.3.4 // indirect github.com/google/uuid v1.1.1 github.com/jmoiron/sqlx v1.2.0 github.com/lib/pq v1.3.0 @@ -20,11 +20,5 @@ require ( github.com/spf13/pflag v1.0.5 // indirect github.com/spf13/viper v1.4.0 github.com/vektah/gqlparser/v2 v2.0.1 - golang.org/x/crypto v0.0.0-20200302210943-78000ba7a073 - golang.org/x/net v0.0.0-20200301022130-244492dfa37a // indirect - golang.org/x/sys v0.0.0-20200302150141-5c8b2ff67527 // indirect - golang.org/x/text v0.3.3 // indirect - golang.org/x/time v0.0.0-20191024005414-555d28b269f0 // indirect - golang.org/x/tools v0.0.0-20200303165918-5bcca83a7881 // indirect - google.golang.org/genproto v0.0.0-20200303153909-beee998c1893 // indirect + golang.org/x/crypto v0.0.0-20200709230013-948cd5f35899 ) diff --git a/go.sum b/go.sum index c077f39..02f4119 100644 --- a/go.sum +++ b/go.sum @@ -10,18 +10,35 @@ cloud.google.com/go v0.50.0/go.mod h1:r9sluTvynVuxRIOHXQEHMFffphuXHOMZMycpNR5e6T cloud.google.com/go v0.52.0/go.mod h1:pXajvRH/6o3+F9jDHZWQ5PbGhn+o8w9qiu/CffaVdO4= cloud.google.com/go v0.53.0 h1:MZQCQQaRwOrAcuKjiHWHrgKykt4fZyuwF2dtiG3fGW8= cloud.google.com/go v0.53.0/go.mod h1:fp/UouUEsRkN6ryDKNW/Upv/JBKnv6WDthjR6+vze6M= +cloud.google.com/go v0.54.0/go.mod h1:1rq2OEkV3YMf6n/9ZvGWI3GWw0VoqH/1x2nd8Is/bPc= +cloud.google.com/go v0.56.0/go.mod h1:jr7tqZxxKOVYizybht9+26Z/gUq7tiRzu+ACVAMbKVk= +cloud.google.com/go v0.57.0/go.mod h1:oXiQ6Rzq3RAkkY7N6t3TcE6jE+CIBBbA36lwQ1JyzZs= +cloud.google.com/go v0.60.0/go.mod h1:yw2G51M9IfRboUH61Us8GqCeF1PzPblB823Mn2q2eAU= +cloud.google.com/go v0.61.0 h1:NLQf5e1OMspfNT1RAHOB3ublr1TW3YTXO8OiWwVjK2U= +cloud.google.com/go v0.61.0/go.mod h1:XukKJg4Y7QsUu0Hxg3qQKUWR4VuWivmyMK2+rUyxAqw= cloud.google.com/go/bigquery v1.0.1/go.mod h1:i/xbL2UlR5RvWAURpBYZTtm/cXjCha9lbfbpx4poX+o= cloud.google.com/go/bigquery v1.3.0/go.mod h1:PjpwJnslEMmckchkHFfq+HTD2DmtT67aNFKH1/VBDHE= cloud.google.com/go/bigquery v1.4.0 h1:xE3CPsOgttP4ACBePh79zTKALtXwn/Edhcr16R5hMWU= cloud.google.com/go/bigquery v1.4.0/go.mod h1:S8dzgnTigyfTmLBfrtrhyYhwRxG72rYxvftPBK2Dvzc= +cloud.google.com/go/bigquery v1.5.0/go.mod h1:snEHRnqQbz117VIFhE8bmtwIDY80NLUZUMb4Nv6dBIg= +cloud.google.com/go/bigquery v1.7.0/go.mod h1://okPTzCYNXSlb24MZs83e2Do+h+VXtc4gLoIoXIAPc= +cloud.google.com/go/bigquery v1.8.0/go.mod h1:J5hqkt3O0uAFnINi6JXValWIb1v0goeZM77hZzJN/fQ= cloud.google.com/go/datastore v1.0.0 h1:Kt+gOPPp2LEPWp8CSfxhsM8ik9CcyE/gYu+0r+RnZvM= cloud.google.com/go/datastore v1.0.0/go.mod h1:LXYbyblFSglQ5pkeyhO+Qmw7ukd3C+pD7TKLgZqpHYE= +cloud.google.com/go/datastore v1.1.0/go.mod h1:umbIZjpQpHh4hmRpGhH4tLFup+FVzqBi1b3c64qFpCk= cloud.google.com/go/pubsub v1.0.1/go.mod h1:R0Gpsv3s54REJCy4fxDixWD93lHJMoZTyQ2kNxGRt3I= cloud.google.com/go/pubsub v1.1.0/go.mod h1:EwwdRX2sKPjnvnqCa270oGRyludottCI76h+R3AArQw= +cloud.google.com/go/pubsub v1.2.0/go.mod h1:jhfEVHT8odbXTkndysNHCcx0awwzvfOlguIAii9o8iA= +cloud.google.com/go/pubsub v1.3.1/go.mod h1:i+ucay31+CNRpDW4Lu78I4xXG+O1r/MAHgjpRVR+TSU= +cloud.google.com/go/pubsub v1.5.0 h1:9cH52jizPUVSSrSe+J16RC9wB0QI7i/cfuCm5UUCcIk= +cloud.google.com/go/pubsub v1.5.0/go.mod h1:ZEwJccE3z93Z2HWvstpri00jOg7oO4UZDtKhwDwqF0w= cloud.google.com/go/spanner v1.2.0/go.mod h1:LfwGAsK42Yz8IeLsd/oagGFBqTXt3xVWtm8/KD2vrEI= cloud.google.com/go/storage v1.0.0/go.mod h1:IhtSnM/ZTZV8YYJWCY8RULGVqBDmpoyjwiyrjsg+URw= cloud.google.com/go/storage v1.5.0 h1:RPUcBvDeYgQFMfQu1eBMq6piD1SXmLH+vK3qjewZPus= cloud.google.com/go/storage v1.5.0/go.mod h1:tpKbwo567HUNpVclU5sGELwQWBDZ8gh0ZeosJ0Rtdos= +cloud.google.com/go/storage v1.6.0/go.mod h1:N7U0C8pVQ/+NIKOBQyamJIeKQKkZ+mxpohlUTyfDhBk= +cloud.google.com/go/storage v1.8.0/go.mod h1:Wv1Oy7z6Yz3DshWRJFhqM/UCfaWIRTdp0RXyy7KQOVs= +cloud.google.com/go/storage v1.10.0/go.mod h1:FLPqc6j+Ki4BU591ie1oL6qBQGu2Bl/tZ9ullr3+Kg0= dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU= github.com/99designs/gqlgen v0.11.3 h1:oFSxl1DFS9X///uHV3y6CEfpcXWrDUxVblR4Xib2bs4= github.com/99designs/gqlgen v0.11.3/go.mod h1:RgX5GRRdDWNkh4pBrdzNpNPFVsdoUFY2+adM6nb1N+4= @@ -31,10 +48,19 @@ github.com/BurntSushi/toml v0.3.1 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo= github.com/ClickHouse/clickhouse-go v1.3.12/go.mod h1:EaI/sW7Azgz9UATzd5ZdZHRUhHgv5+JMS9NSr2smCJI= +github.com/DataDog/sketches-go v0.0.0-20190923095040-43f19ad77ff7 h1:qELHH0AWCvf98Yf+CNIJx9vOZOfHFDDzgDRYsnNk/vs= +github.com/DataDog/sketches-go v0.0.0-20190923095040-43f19ad77ff7/go.mod h1:Q5DbzQ+3AkgGwymQO7aZFNP7ns2lZKGtvRBzRXfdi60= github.com/Microsoft/go-winio v0.4.11/go.mod h1:VhR8bwka0BXejwEJY73c50VrPtXAaKcyvVC4A4RozmA= github.com/Microsoft/go-winio v0.4.14 h1:+hMXMk01us9KgxGb7ftKQt2Xpf5hH/yky+TDA+qxleU= github.com/Microsoft/go-winio v0.4.14/go.mod h1:qXqCSQ3Xa7+6tgxaGTIe4Kpcdsi+P8jBhyzoq1bpyYA= +github.com/OneOfOne/xxhash v1.2.2 h1:KMrpdQIwFcEqXDklaen+P1axHaj9BSKzvpUUfnHldSE= github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU= +github.com/RichardKnop/logging v0.0.0-20190827224416-1a693bdd4fae h1:DcFpTQBYQ9Ct2d6sC7ol0/ynxc2pO1cpGUM+f4t5adg= +github.com/RichardKnop/logging v0.0.0-20190827224416-1a693bdd4fae/go.mod h1:rJJ84PyA/Wlmw1hO+xTzV2wsSUon6J5ktg0g8BF2PuU= +github.com/RichardKnop/machinery v1.9.1 h1:Q4WInk0OWGMbXDH3Q8dm8uadN5Wcyquc+7IcM4p9ECs= +github.com/RichardKnop/machinery v1.9.1/go.mod h1:ww7OQ0FxPS39PqWsJyqKCatZTQ/o8hsiKGAa7m0BtYY= +github.com/RichardKnop/redsync v1.2.0 h1:gK35hR3zZkQigHKm8wOGb9MpJ9BsrW6MzxezwjTcHP0= +github.com/RichardKnop/redsync v1.2.0/go.mod h1:9b8nBGAX3bE2uCfJGSnsDvF23mKyHTZzmvmj5FH3Tp0= github.com/Shopify/sarama v1.19.0/go.mod h1:FVkBWblsNy7DGZRfXLU0O9RCGt5g3g3yEuWXgklEdEo= github.com/Shopify/toxiproxy v2.1.4+incompatible/go.mod h1:OXgGpZ6Cli1/URJOF1DMxUHB2q5Ap20/P/eIdh4G0pI= github.com/agnivade/levenshtein v1.0.1/go.mod h1:CURSv5d9Uaml+FovSIICkLbAUZ9S4RqaHDIsdSBg7lM= @@ -49,18 +75,26 @@ github.com/arbovm/levenshtein v0.0.0-20160628152529-48b4e1c0c4d0 h1:jfIu9sQUG6Ig github.com/arbovm/levenshtein v0.0.0-20160628152529-48b4e1c0c4d0/go.mod h1:t2tdKJDJF9BV14lnkjHmOQgcvEKgtqs5a1N3LNdJhGE= github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5doyWs3UAsr3K4I6qtAmlQcZDesFNEHPZAzj8= github.com/aws/aws-sdk-go v1.17.7/go.mod h1:KmX6BPdI08NWTb3/sm4ZGu5ShLoqVDhKgpiN924inxo= +github.com/aws/aws-sdk-go v1.33.6 h1:YLoUeMSx05kHwhS+HLDSpdYYpPzJMyp6hn1cWsJ6a+U= +github.com/aws/aws-sdk-go v1.33.6/go.mod h1:5zCpMtNQVjRREroY7sYe8lOMRSxkhG6MZveU8YkpAk0= +github.com/benbjohnson/clock v1.0.3 h1:vkLuvpK4fmtSCuo60+yC63p7y0BmQ8gm5ZXGuBCJyXg= +github.com/benbjohnson/clock v1.0.3/go.mod h1:bGMdMPoPVvcYyt1gHDf4J2KE153Yf9BuiUKYMaxlTDM= github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q= github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8= github.com/bitly/go-hostpool v0.0.0-20171023180738-a3a6125de932/go.mod h1:NOuUCSz6Q9T7+igc/hlvDOUdtWKryOrtFyIVABv/p7k= github.com/bkaradzic/go-lz4 v1.0.0/go.mod h1:0YdlkowM3VswSROI7qDxhRvJ3sLhlFrRRwjwegp5jy4= github.com/bmizerany/assert v0.0.0-20160611221934-b7ed37b82869/go.mod h1:Ekp36dRnpXw/yCqJaO+ZrUyxD+3VXMFFr56k5XYrpB4= +github.com/bradfitz/gomemcache v0.0.0-20190913173617-a41fca850d0b h1:L/QXpzIa3pOvUGt1D1lA5KjYhPBAN/3iWdP7xeFS9F0= +github.com/bradfitz/gomemcache v0.0.0-20190913173617-a41fca850d0b/go.mod h1:H0wQNHz2YrLsuXOZozoeDmnHXkNCRmMW0gwFWDfEZDA= github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= +github.com/cespare/xxhash v1.1.0 h1:a6HrQnmkObjyL+Gs60czilIUGqrzKutQD6XZog3p+ko= github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc= github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI= github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI= github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU= github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= github.com/cloudflare/golz4 v0.0.0-20150217214814-ef862a3cdc58/go.mod h1:EOBUe0h4xcZ5GoxqC5SDxFQ8gwyZPKQoEzownBlhI80= +github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc= github.com/cockroachdb/apd v1.1.0/go.mod h1:8Sl8LxpKi29FqWXR16WEFZRNSz3SoPzUzeMeY4+DwBQ= github.com/cockroachdb/cockroach-go v0.0.0-20190925194419-606b3d062051/go.mod h1:XGLbWH/ujMcbPbhZq52Nv6UrCghb1yGn//133kEsvDk= github.com/containerd/containerd v1.3.3 h1:LoIzb5y9x5l8VKAlyrbusNPXqBY0+kviRloxFUMFwKc= @@ -82,6 +116,8 @@ github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSs github.com/denisenkom/go-mssqldb v0.0.0-20190515213511-eb9f6a1743f3/go.mod h1:zAg7JM8CkOJ43xKXIj7eRO9kmWm/TW578qo+oDO6tuM= github.com/dgrijalva/jwt-go v3.2.0+incompatible h1:7qlOGliEKZXTDg6OTjfoBKDXWrumCAMpl/TFQ4/5kLM= github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ= +github.com/dgryski/go-rendezvous v0.0.0-20200624174652-8d2f3be8b2d9 h1:h2Ul3Ym2iVZWMQGYmulVUJ4LSkBm1erp9mUkPwtMoLg= +github.com/dgryski/go-rendezvous v0.0.0-20200624174652-8d2f3be8b2d9/go.mod h1:cuUVRXasLTGF7a8hSLbxyZXjz+1KgoB3wDUb6vlszIc= github.com/dgryski/go-sip13 v0.0.0-20181026042036-e10d5fee7954/go.mod h1:vAd38F8PWV+bWy6jNmig1y/TA+kYO4g3RSRF0IAv0no= github.com/dgryski/trifles v0.0.0-20190318185328-a8d75aae118c h1:TUuUh0Xgj97tLMNtWtNvI9mIV6isjEb9lBMNv+77IGM= github.com/dgryski/trifles v0.0.0-20190318185328-a8d75aae118c/go.mod h1:if7Fbed8SFyPtHLHbg49SI7NAdJiC5WIA09pe59rfAA= @@ -101,7 +137,9 @@ github.com/eapache/go-resiliency v1.1.0/go.mod h1:kFI+JgMyC7bLPUVY133qvEBtVayf5m github.com/eapache/go-xerial-snappy v0.0.0-20180814174437-776d5712da21/go.mod h1:+020luEh2TKB4/GOp8oxxtq0Daoen/Cii55CzbTV6DU= github.com/eapache/queue v1.1.0/go.mod h1:6eCeP0CKFpHLu8blIFXhExK/dRa7WDZfr6jVFPTqq+I= github.com/edsrzf/mmap-go v0.0.0-20170320065105-0bce6a688712/go.mod h1:YO35OhQPt3KJa3ryjFM5Bs14WD66h8eGKpfaBNrHW5M= +github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= +github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98= github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= github.com/fsnotify/fsnotify v1.4.7 h1:IXs+QLmnXW2CcXuY+8Mzv/fWEsPGWxqefPtCP5CnV9I= github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= @@ -111,16 +149,45 @@ github.com/go-chi/chi v3.3.2+incompatible h1:uQNcQN3NsV1j4ANsPh42P4ew4t6rnRbJb8f github.com/go-chi/chi v3.3.2+incompatible/go.mod h1:eB3wogJHnLi3x/kFX2A+IbTBlXxmMeXJVKy9tTv1XzQ= github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU= github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= +github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE= github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk= +github.com/go-redis/redis v6.15.8+incompatible h1:BKZuG6mCnRj5AOaWJXoCgf6rqTYnYJLe4en2hxT7r9o= +github.com/go-redis/redis v6.15.8+incompatible/go.mod h1:NAIEuMOZ/fxfXJIrKDQDz8wamY7mA7PouImQ2Jvg6kA= +github.com/go-redis/redis/v8 v8.0.0-beta.6 h1:QeXAkG9L5cWJA+eJTBvhkftE7dwpJ0gbMYeBE2NxXS4= +github.com/go-redis/redis/v8 v8.0.0-beta.6/go.mod h1:g79Vpae8JMzg5qjk8BiwU9tK+HmU3iDVyS4UAJLFycI= github.com/go-sql-driver/mysql v1.4.0 h1:7LxgVwFb2hIQtMm87NdgAVfXjnt4OePseqT1tKx+opk= github.com/go-sql-driver/mysql v1.4.0/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG5ZlKdlhCg5w= github.com/go-sql-driver/mysql v1.5.0 h1:ozyZYNQW3x3HtqT1jira07DN2PArx2v7/mN66gGcHOs= github.com/go-sql-driver/mysql v1.5.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg= github.com/go-stack/stack v1.8.0 h1:5SgMzNM5HxrEjV0ww2lTmX6E2Izsfxas4+YHWRs3Lsk= github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= +github.com/gobuffalo/attrs v0.0.0-20190224210810-a9411de4debd/go.mod h1:4duuawTqi2wkkpB4ePgWMaai6/Kc6WEz83bhFwpHzj0= +github.com/gobuffalo/depgen v0.0.0-20190329151759-d478694a28d3/go.mod h1:3STtPUQYuzV0gBVOY3vy6CfMm/ljR4pABfrTeHNLHUY= +github.com/gobuffalo/depgen v0.1.0/go.mod h1:+ifsuy7fhi15RWncXQQKjWS9JPkdah5sZvtHc2RXGlg= +github.com/gobuffalo/envy v1.6.15/go.mod h1:n7DRkBerg/aorDM8kbduw5dN3oXGswK5liaSCx4T5NI= +github.com/gobuffalo/envy v1.7.0/go.mod h1:n7DRkBerg/aorDM8kbduw5dN3oXGswK5liaSCx4T5NI= +github.com/gobuffalo/flect v0.1.0/go.mod h1:d2ehjJqGOH/Kjqcoz+F7jHTBbmDb38yXA598Hb50EGs= +github.com/gobuffalo/flect v0.1.1/go.mod h1:8JCgGVbRjJhVgD6399mQr4fx5rRfGKVzFjbj6RE/9UI= +github.com/gobuffalo/flect v0.1.3/go.mod h1:8JCgGVbRjJhVgD6399mQr4fx5rRfGKVzFjbj6RE/9UI= +github.com/gobuffalo/genny v0.0.0-20190329151137-27723ad26ef9/go.mod h1:rWs4Z12d1Zbf19rlsn0nurr75KqhYp52EAGGxTbBhNk= +github.com/gobuffalo/genny v0.0.0-20190403191548-3ca520ef0d9e/go.mod h1:80lIj3kVJWwOrXWWMRzzdhW3DsrdjILVil/SFKBzF28= +github.com/gobuffalo/genny v0.1.0/go.mod h1:XidbUqzak3lHdS//TPu2OgiFB+51Ur5f7CSnXZ/JDvo= +github.com/gobuffalo/genny v0.1.1/go.mod h1:5TExbEyY48pfunL4QSXxlDOmdsD44RRq4mVZ0Ex28Xk= +github.com/gobuffalo/gitgen v0.0.0-20190315122116-cc086187d211/go.mod h1:vEHJk/E9DmhejeLeNt7UVvlSGv3ziL+djtTr3yyzcOw= +github.com/gobuffalo/gogen v0.0.0-20190315121717-8f38393713f5/go.mod h1:V9QVDIxsgKNZs6L2IYiGR8datgMhB577vzTDqypH360= +github.com/gobuffalo/gogen v0.1.0/go.mod h1:8NTelM5qd8RZ15VjQTFkAW6qOMx5wBbW4dSCS3BY8gg= +github.com/gobuffalo/gogen v0.1.1/go.mod h1:y8iBtmHmGc4qa3urIyo1shvOD8JftTtfcKi+71xfDNE= github.com/gobuffalo/here v0.6.0/go.mod h1:wAG085dHOYqUpf+Ap+WOdrPTp5IYcDAs/x7PLa8Y5fM= +github.com/gobuffalo/logger v0.0.0-20190315122211-86e12af44bc2/go.mod h1:QdxcLw541hSGtBnhUc4gaNIXRjiDppFGaDqzbrBd3v8= +github.com/gobuffalo/mapi v1.0.1/go.mod h1:4VAGh89y6rVOvm5A8fKFxYG+wIW6LO1FMTG9hnKStFc= +github.com/gobuffalo/mapi v1.0.2/go.mod h1:4VAGh89y6rVOvm5A8fKFxYG+wIW6LO1FMTG9hnKStFc= +github.com/gobuffalo/packd v0.0.0-20190315124812-a385830c7fc0/go.mod h1:M2Juc+hhDXf/PnmBANFCqx4DM3wRbgDvnVWeG2RIxq4= +github.com/gobuffalo/packd v0.1.0/go.mod h1:M2Juc+hhDXf/PnmBANFCqx4DM3wRbgDvnVWeG2RIxq4= +github.com/gobuffalo/packr/v2 v2.0.9/go.mod h1:emmyGweYTm6Kdper+iywB6YK5YzuKchGtJQZ0Odn4pQ= +github.com/gobuffalo/packr/v2 v2.2.0/go.mod h1:CaAwI0GPIAv+5wKLtv8Afwl+Cm78K/I/VCm/3ptBN+0= +github.com/gobuffalo/syncx v0.0.0-20190224160051-33c29581e754/go.mod h1:HhnNqWY95UYwwW3uSASeV7vtgYkT2t16hJgV3AEPUpw= github.com/gocql/gocql v0.0.0-20190301043612-f6df8288f9b4/go.mod h1:4Fw1eo5iaEhDUs8XyuhSVCVy52Jq3L+/3GJgYkwc+/0= github.com/gogo/protobuf v1.0.0/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= @@ -141,16 +208,29 @@ github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfb github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= github.com/golang/mock v1.3.1/go.mod h1:sBzyDLLjw3U8JLTeZvSv8jJB+tU5PVekmnlKIyFUx0Y= github.com/golang/mock v1.4.0/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= +github.com/golang/mock v1.4.1/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= +github.com/golang/mock v1.4.3/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw= github.com/golang/protobuf v1.3.4 h1:87PNWwrRvUSnqS4dlcBU/ftvOIBep4sYuBLlh6rX2wk= github.com/golang/protobuf v1.3.4/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw= +github.com/golang/protobuf v1.3.5/go.mod h1:6O5/vntMXwX2lRkT1hjjk0nAC1IDOTvTlVgjlRvqsdk= +github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8= +github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA= +github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs= +github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w= +github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0= +github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8= +github.com/golang/protobuf v1.4.2 h1:+Z5KGCizgyZCbGh1KZqA0fcLLkwbsjIzS4aV2v7wJX0= +github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= github.com/golang/snappy v0.0.0-20170215233205-553a64147049/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= github.com/golang/snappy v0.0.0-20180518054509-2e65f85255db/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= github.com/golang/snappy v0.0.1 h1:Qgr9rKW7uDUkrbSmQeiDsGa8SjGyCOGtuasMWwvp2P4= github.com/golang/snappy v0.0.1/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= +github.com/gomodule/redigo v1.8.2 h1:H5XSIre1MB5NbPYFp+i1NBbb5qN1W8Y8YAQoAYbkm8k= +github.com/gomodule/redigo v1.8.2/go.mod h1:P9dn9mFrCBvWhGE1wpxx6fgq7BAeLBk+UUUzlpkBYO0= github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= @@ -158,14 +238,24 @@ github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMyw github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= github.com/google/go-cmp v0.4.0 h1:xsAVV57WRhGj6kEIi8ReJzQlHHqcBYCElAvkovg3B/4= github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.4.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.0 h1:/QaMHBdZ26BB3SSst0Iwl10Epc+xhTquomWX0oZEB6w= +github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-github v17.0.0+incompatible/go.mod h1:zLgOLi98H3fifZn+44m+umXrS52loVEgC2AApnigrVQ= github.com/google/go-querystring v1.0.0/go.mod h1:odCYkC5MyYFN7vkCjXpyrEuKhc/BUO6wN/zVPAxq5ck= +github.com/google/gofuzz v1.0.0 h1:A8PeW59pxE9IoFRqBp37U+mSNaQoZ46F1f0f863XSXw= +github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= github.com/google/martian v2.1.0+incompatible h1:/CP5g8u/VJHijgedC/Legn3BAbAaWPgecwXBIDzw5no= github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs= +github.com/google/martian/v3 v3.0.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0= github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= github.com/google/pprof v0.0.0-20190515194954-54271f7e092f/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= github.com/google/pprof v0.0.0-20191218002539-d4f498aebedc/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= github.com/google/pprof v0.0.0-20200212024743-f11f1df84d12/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= +github.com/google/pprof v0.0.0-20200229191704-1ebb73c60ed3/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= +github.com/google/pprof v0.0.0-20200430221834-fc25d7d30c6d/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= +github.com/google/pprof v0.0.0-20200507031123-427632fa3b1c/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= +github.com/google/pprof v0.0.0-20200708004538-1a94d8640e99/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= github.com/google/uuid v1.1.1 h1:Gkbcsh/GbpXz7lPftLA3P6TYMwjCLYm83jiFQZF/3gY= github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= @@ -229,17 +319,27 @@ github.com/jackc/puddle v0.0.0-20190413234325-e4ced69a3a2b/go.mod h1:m4B5Dj62Y0f github.com/jackc/puddle v0.0.0-20190608224051-11cab39313c9/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk= github.com/jmespath/go-jmespath v0.0.0-20180206201540-c2b33e8439af h1:pmfjZENx5imkbgOkpRUYLnmbU7UEFbjtDA2hxJ1ichM= github.com/jmespath/go-jmespath v0.0.0-20180206201540-c2b33e8439af/go.mod h1:Nht3zPeWKUH0NzdCt2Blrr5ys8VGpn0CEB0cQHVjt7k= +github.com/jmespath/go-jmespath v0.3.0 h1:OS12ieG61fsCg5+qLJ+SsW9NicxNkg3b25OyT2yCeUc= +github.com/jmespath/go-jmespath v0.3.0/go.mod h1:9QtRXoHjLGCJ5IBSaohpXITPlowMeeYCZ7fLUTSywik= github.com/jmoiron/sqlx v1.2.0 h1:41Ip0zITnmWNR/vHV+S4m+VoUivnWY5E4OJfLZjCJMA= github.com/jmoiron/sqlx v1.2.0/go.mod h1:1FEQNm3xlJgrMD+FBdI9+xvCksHtbpVBBw5dYhBSsks= +github.com/joho/godotenv v1.3.0/go.mod h1:7hK45KPybAkOC6peb+G5yklZfMxEjkZhHbwpqxOKXbg= github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo= github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU= github.com/jstemmer/go-junit-report v0.9.1 h1:6QPYqodiu3GuPL+7mfx+NwDdp2eTkp9IfEUpgAwUN0o= github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/XSXhF0NWZEnDohbsk= github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w= github.com/kardianos/osext v0.0.0-20190222173326-2bc1f35cddc0/go.mod h1:1NbS8ALrpOvjt0rHPNLyCIeMtbizbir8U//inJ+zuB8= +github.com/karrick/godirwalk v1.8.0/go.mod h1:H5KPZjojv4lE+QYImBI8xVtrBRgYrIVsaRPx4tDPEn4= +github.com/karrick/godirwalk v1.10.3/go.mod h1:RoGL9dQei4vP9ilrpETWE8CLOZ1kiN0LhBygSwrAsHA= +github.com/kelseyhightower/envconfig v1.4.0 h1:Im6hONhd3pLkfDFsbRgu68RDNkGF1r3dvMUtDTo2cv8= +github.com/kelseyhightower/envconfig v1.4.0/go.mod h1:cccZRl6mQpaq41TPp5QxidR+Sa3axMbJDNb//FQX6Gg= github.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvWXihfKN4Q= github.com/kisielk/errcheck v1.2.0/go.mod h1:/BMXB+zMLi60iA8Vv6Ksmxu/1UDYcXs4uQLJ+jE2L00= github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= +github.com/klauspost/compress v1.9.5/go.mod h1:RyIbtBH6LamlWaDj8nUwkbUhJ87Yi3uG0guNDohfE1A= +github.com/klauspost/compress v1.10.10 h1:a/y8CglcM7gLGYmlbP/stPE5sR3hbhFRUjCBfd/0B3I= +github.com/klauspost/compress v1.10.10/go.mod h1:aoV0uJVorq1K+umq18yTdKaF57EivdYsUV+/s2qKfXs= github.com/konsorten/go-windows-terminal-sequences v1.0.1 h1:mweAR1A6xJ3oS2pRaGiHgQ4OO8tzTaLawm8vnODuwDk= github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= github.com/konsorten/go-windows-terminal-sequences v1.0.2 h1:DB17ag19krx9CFsz4o3enTrPXyIXCl+2iCXH/aMAp9s= @@ -262,7 +362,9 @@ github.com/magefile/mage v1.9.0 h1:t3AU2wNwehMCW97vuqQLtw6puppWXHO+O2MHo5a50XE= github.com/magefile/mage v1.9.0/go.mod h1:z5UZb/iS3GoOSn0JgWuiw7dxlurVYTu+/jHXqQg881A= github.com/magiconair/properties v1.8.0 h1:LLgXmsheXeRoUOBOjtwPQCWIYqM/LU1ayDtDePerRcY= github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ= +github.com/markbates/oncer v0.0.0-20181203154359-bf2de49a0be2/go.mod h1:Ld9puTsIW75CHf65OeIOkyKbteujpZVXDpWK6YGZbxE= github.com/markbates/pkger v0.15.1/go.mod h1:0JoVlrol20BSywW79rN3kdFFsE5xYM+rSCQDXbLhiuI= +github.com/markbates/safe v1.0.1/go.mod h1:nAqgmRi7cY2nqMc92/bSEeQA+R4OheNU2T1kNSCBdG0= github.com/matryer/moq v0.0.0-20200106131100-75d0ddfc0007 h1:reVOUXwnhsYv/8UqjvhrMOu5CNT9UapHFLbQ2JcXsmg= github.com/matryer/moq v0.0.0-20200106131100-75d0ddfc0007/go.mod h1:9ELz6aaclSIGnZBoaSLZ3NAl1VTufbOrXBPvtcy6WiQ= github.com/mattn/go-colorable v0.1.1/go.mod h1:FuOcm+DKB9mbwrcAfNl7/TZVBZ6rcnceauSikq3lYCQ= @@ -281,6 +383,7 @@ github.com/mitchellh/mapstructure v0.0.0-20180203102830-a4e142e9c047 h1:zCoDWFD5 github.com/mitchellh/mapstructure v0.0.0-20180203102830-a4e142e9c047/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= github.com/mitchellh/mapstructure v1.1.2 h1:fmNYVwqnSfB9mZU6OS2O6GsXM+wcskZDuKQzvN1EDeE= github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= +github.com/montanaflynn/stats v0.0.0-20171201202039-1bf9dbcd8cbe/go.mod h1:wL8QJuTMNUDYhXwkmfOly8iTdp5TEcJFWZD2D7SIkUc= github.com/morikuni/aec v1.0.0 h1:nP9CBfwrvYnBRgY6qfDQkygYDmYwOilePFkwzv4dU8A= github.com/morikuni/aec v1.0.0/go.mod h1:BbKIizmSmc5MMPqRYbxO4ZU0S0+P200+tUnFx7PXmsc= github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= @@ -290,15 +393,23 @@ github.com/neo4j/neo4j-go-driver v1.7.4/go.mod h1:aPO0vVr+WnhEJne+FgFjfsjzAnssPF github.com/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn4U= github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= github.com/onsi/ginkgo v1.7.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= +github.com/onsi/ginkgo v1.10.1 h1:q/mM8GF/n0shIN8SaAZ0V+jnLPzen6WIVZdiwrRlMlo= +github.com/onsi/ginkgo v1.10.1/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= github.com/onsi/gomega v1.4.3/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY= +github.com/onsi/gomega v1.7.0 h1:XPnZz8VVBHjVsy1vzJmRwIcSwiUO+JFfrv/xGiigmME= +github.com/onsi/gomega v1.7.0/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY= github.com/opencontainers/go-digest v1.0.0-rc1 h1:WzifXhOVOEOuFYOJAW6aQqW0TooG2iki3E3Ii+WN7gQ= github.com/opencontainers/go-digest v1.0.0-rc1/go.mod h1:cMLVZDEM3+U2I4VmLI6N8jQYUd2OVphdqWwCJHrFt2s= github.com/opencontainers/image-spec v1.0.1 h1:JMemWkRwHx4Zj+fVxWoMCFm/8sYGGrUVojFA6h/TRcI= github.com/opencontainers/image-spec v1.0.1/go.mod h1:BtxoFyWECRxE4U/7sNtV5W15zMzWCbyJoFRP3s7yZA0= github.com/opentracing/basictracer-go v1.0.0/go.mod h1:QfBfYuafItcjQuMwinw9GhYKwFXS9KnPs5lxoYwgW74= github.com/opentracing/opentracing-go v1.0.2/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFStlNbqXla1AfSYxPUl2o= +github.com/opentracing/opentracing-go v1.1.1-0.20190913142402-a7454ce5950e/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFStlNbqXla1AfSYxPUl2o= +github.com/opentracing/opentracing-go v1.2.0 h1:uEJPy/1a5RIPAJ0Ov+OIO8OxWu77jEv+1B0VhjKrZUs= +github.com/opentracing/opentracing-go v1.2.0/go.mod h1:GxEUsuufX4nBwe+T+Wl9TAgYrxe9dPLANfrWvHYVTgc= github.com/openzipkin/zipkin-go v0.1.6/go.mod h1:QgAqvLzwWbR/WpD4A3cGpPtJrZXNIiJc5AZX7/PBEpw= github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic= +github.com/pelletier/go-toml v1.4.0/go.mod h1:PN7xzY2wHTK0K9p34ErDQMlFxa51Fk0OUruD3k1mMwo= github.com/pelletier/go-toml v1.8.0 h1:Keo9qb7iRJs2voHvunFtuuYFsbWeOBh8/P9v/kVMFtw= github.com/pelletier/go-toml v1.8.0/go.mod h1:D6yutnOGMveHEPV7VQOuvI/gXY61bv+9bAOTRnLElKs= github.com/pierrec/lz4 v2.0.5+incompatible/go.mod h1:pdkljMzZIN41W+lC3N2tnIh5sFi+IEE17M5jbnwPHcY= @@ -326,6 +437,8 @@ github.com/prometheus/tsdb v0.7.1/go.mod h1:qhTCs0VvXwvX/y3TZrWD7rabWM+ijKTux40T github.com/rcrowley/go-metrics v0.0.0-20181016184325-3113b8401b8a/go.mod h1:bCqnVzQkZxMG4s8nGwiZ5l3QUCyqpo9Y+/ZMZ9VjZe4= github.com/remyoudompheng/bigfft v0.0.0-20190728182440-6a916e37a237/go.mod h1:qqbHyh8v60DhA7CoWK5oRCqLrMHRGoxYCSS9EjAz6Eo= github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg= +github.com/rogpeppe/go-internal v1.1.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= +github.com/rogpeppe/go-internal v1.2.2/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= github.com/rs/cors v1.6.0/go.mod h1:gFx+x8UowdsKA9AchylcLynDq+nNFfI8FkUZdN/jGCU= github.com/rs/xid v1.2.1/go.mod h1:+uKXf+4Djp6Md1KODXJxgGQPKngRmWyn10oCKFzNHOQ= @@ -344,15 +457,18 @@ github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeV github.com/shurcooL/vfsgen v0.0.0-20180121065927-ffb13db8def0 h1:JJV9CsgM9EC9w2iVkwuz+sMx8yRFe89PJRUrv6hPCIA= github.com/shurcooL/vfsgen v0.0.0-20180121065927-ffb13db8def0/go.mod h1:TrYk7fJVaAttu97ZZKrO9UbRa8izdowaMIZcxYMbVaw= github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo= +github.com/sirupsen/logrus v1.4.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo= github.com/sirupsen/logrus v1.4.1/go.mod h1:ni0Sbl8bgC9z8RoU9G6nDWqqs/fq4eDPysMBDgk/93Q= github.com/sirupsen/logrus v1.4.2 h1:SPIRibHv4MatM3XXNO2BJeFLZwZ2LvZgfQ5+UNI2im4= github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE= github.com/soheilhy/cmux v0.1.4/go.mod h1:IM3LyeVVIOuxMH7sFAkER9+bJ4dT7Ms6E4xg4kGIyLM= +github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72 h1:qLC7fQah7D6K1B0ujays3HV9gkFtllcxhzImRR7ArPQ= github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA= github.com/spf13/afero v1.1.2 h1:m8/z1t7/fwjysjQRYbP0RD+bUIF/8tJwPdEZsI83ACI= github.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ= github.com/spf13/cast v1.3.0 h1:oget//CVOEoFewqQxwr0Ej5yjygnqGkvggSE/gB35Q8= github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE= +github.com/spf13/cobra v0.0.3/go.mod h1:1l0Ry5zgKvJasoi3XT1TypsSe7PqH0Sj9dhYf7v3XqQ= github.com/spf13/cobra v1.0.0 h1:6m/oheQuQ13N9ks4hubMG6BnvwOeaJrqSPLahSnczz8= github.com/spf13/cobra v1.0.0/go.mod h1:/6GTrnGXV9HjY+aR4k0oJ5tcvakLuG6EuKReYlHNrgE= github.com/spf13/jwalterweatherman v1.0.0 h1:XHEdyB+EcvlqZamSM4ZOMGlc93t6AcsBEu9Gc1vn7yk= @@ -363,6 +479,8 @@ github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= github.com/spf13/viper v1.4.0 h1:yXHLWeravcrgGyFSyCgdYpXQ9dR9c/WED3pg1RhxqEU= github.com/spf13/viper v1.4.0/go.mod h1:PTJ7Z/lr49W6bUbkmS1V3by4uWynFiR9p7+dSq/yZzE= +github.com/streadway/amqp v1.0.0 h1:kuuDrUJFZL1QYL9hUNuCxNObNzB0bV/ZG5jV3RWAQgo= +github.com/streadway/amqp v1.0.0/go.mod h1:AZpEONHx3DKn8O/DFsRAY58/XVQiIPMTMB1SddzLXVw= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.2.0/go.mod h1:qt09Ya8vawLte6SNmTgCsAVtYtaKzEcn8ATUoHMkEqE= @@ -371,9 +489,17 @@ github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXf github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= github.com/stretchr/testify v1.4.0 h1:2E4SXV/wtOkTonXsotYi4li6zVWxYlZuYNCXe9XRJyk= github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= +github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= +github.com/stretchr/testify v1.6.1 h1:hDPOHmpOpP40lSULcqw7IrRb/u7w6RpDC9399XyoNd0= +github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stvp/tempredis v0.0.0-20181119212430-b82af8480203 h1:QVqDTf3h2WHt08YuiTGPZLls0Wq99X9bWd0Q5ZSBesM= +github.com/stvp/tempredis v0.0.0-20181119212430-b82af8480203/go.mod h1:oqN97ltKNihBbwlX8dLpwxCl3+HnXKV/R0e+sRLd9C8= github.com/tidwall/pretty v0.0.0-20180105212114-65a9db5fad51/go.mod h1:XNkn88O1ChpSDQmQeStsy+sBenx6DDtFZJxhVysOjyk= +github.com/tidwall/pretty v1.0.0 h1:HsD+QiTn7sK6flMKIvNmpqz1qrpP3Ps6jOKIKMooyg4= +github.com/tidwall/pretty v1.0.0/go.mod h1:XNkn88O1ChpSDQmQeStsy+sBenx6DDtFZJxhVysOjyk= github.com/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U= github.com/ugorji/go v1.1.4/go.mod h1:uQMGLiO92mf5W77hV/PUCpI3pbzQx3CRekS0kk+RGrc= +github.com/urfave/cli v1.22.4/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0= github.com/urfave/cli/v2 v2.1.1 h1:Qt8FeAtxE/vfdrLmR3rxR6JRE0RoVmbXu8+6kZtYU4k= github.com/urfave/cli/v2 v2.1.1/go.mod h1:SE9GqnLQmjVa0iPEY0f1w3ygNIYcIJ0OKPMoW2caLfQ= github.com/vektah/dataloaden v0.2.1-0.20190515034641-a19b9a6e7c9e h1:+w0Zm/9gaWpEAyDlU1eKOuk5twTjAjuevXqcJJw8hrg= @@ -383,20 +509,31 @@ github.com/vektah/gqlparser/v2 v2.0.1/go.mod h1:SyUiHgLATUR8BiYURfTirrTcGpcE+4Xk github.com/xanzy/go-gitlab v0.15.0/go.mod h1:8zdQa/ri1dfn8eS3Ir1SyfvOKlw7WBJ8DVThkpGiXrs= github.com/xdg/scram v0.0.0-20180814205039-7eeb5667e42c h1:u40Z8hqBAAQyv+vATcGgV0YCnDjqSL7/q/JyPhhJSPk= github.com/xdg/scram v0.0.0-20180814205039-7eeb5667e42c/go.mod h1:lB8K/P019DLNhemzwFU4jHLhdvlE6uDZjXFejJXr49I= +github.com/xdg/stringprep v0.0.0-20180714160509-73f8eece6fdc/go.mod h1:Jhud4/sHMO4oL310DaZAKk9ZaJ08SJfe+sJh0HrGL1Y= github.com/xdg/stringprep v1.0.0 h1:d9X0esnoa3dFsV0FG35rAT0RIhYFlPq7MiP+DW89La0= github.com/xdg/stringprep v1.0.0/go.mod h1:Jhud4/sHMO4oL310DaZAKk9ZaJ08SJfe+sJh0HrGL1Y= github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU= github.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77/go.mod h1:aYKd//L2LvnjZzWKhF00oedf4jCCReLcmhLdhm1A27Q= +github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/zenazn/goji v0.9.0/go.mod h1:7S9M489iMyHBNxwZnk9/EHS098H4/F6TATF2mIxtB1Q= gitlab.com/nyarla/go-crypt v0.0.0-20160106005555-d9a5dc2b789b/go.mod h1:T3BPAOm2cqquPa0MKWeNkmOM5RQsRhkrwMWonFMN7fE= go.etcd.io/bbolt v1.3.2/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU= go.mongodb.org/mongo-driver v1.1.0/go.mod h1:u7ryQJ+DOzQmeO7zB6MHyr8jkEQvC8vH7qLUO4lqsUM= +go.mongodb.org/mongo-driver v1.3.5 h1:S0ZOruh4YGHjD7JoN7mIsTrNjnQbOjrmgrx6l6pZN7I= +go.mongodb.org/mongo-driver v1.3.5/go.mod h1:Ual6Gkco7ZGQw8wE1t4tLnvBsf6yVSM60qW6TgOeJ5c= go.opencensus.io v0.20.1/go.mod h1:6WKK9ahsWS3RSO+PY9ZHZUfv2irvY6gN279GOPZjmmk= go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU= go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8= go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= go.opencensus.io v0.22.3 h1:8sGtKOrtQqkN1bp2AtX+misvLIlOmsEsNd+9NIcPEm8= go.opencensus.io v0.22.3/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= +go.opencensus.io v0.22.4 h1:LYy1Hy3MJdrCdMwwzxA/dRok4ejH+RwNGbuoD9fCjto= +go.opencensus.io v0.22.4/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= +go.opentelemetry.io/otel v0.7.0/go.mod h1:aZMyHG5TqDOXEgH2tyLiXSUKly1jT3yqE9PmrzIeCdo= +go.opentelemetry.io/otel v0.8.0 h1:he/8j/EBlKjENVtDvFalawIUcQ+1E3uHRsvJZWLIa7M= +go.opentelemetry.io/otel v0.8.0/go.mod h1:ckxzUEfk7tAkTwEMVdkllBM+YOfE/K9iwg6zYntFYSg= go.uber.org/atomic v1.3.2/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0= @@ -406,13 +543,18 @@ golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnf golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20190325154230-a5d413f7728c/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20190411191339-88737f569e3a/go.mod h1:WFFai1msRO1wXaEeE5yQxYXgSfI8pQAWXbQop6sCtWE= +golang.org/x/crypto v0.0.0-20190422162423-af44ce270edf/go.mod h1:WFFai1msRO1wXaEeE5yQxYXgSfI8pQAWXbQop6sCtWE= golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20190530122614-20be4c3c3ed5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20190820162420-60c769a6c586/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550 h1:ObdrDkeb4kJdCP557AjRjq69pTHfNouLtWZG7j9rPN8= golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20200302210943-78000ba7a073 h1:xMPOj6Pz6UipU1wXLkrtqpHbR0AVFnyPEQq/wRWz9lM= golang.org/x/crypto v0.0.0-20200302210943-78000ba7a073/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/crypto v0.0.0-20200709230013-948cd5f35899 h1:DZhuSZLsGlFL4CmhA8BcRA0mnthyA/nZ00AqCUo7vHg= +golang.org/x/crypto v0.0.0-20200709230013-948cd5f35899/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8= @@ -423,6 +565,9 @@ golang.org/x/exp v0.0.0-20191227195350-da58074b4299/go.mod h1:2RIsYlXP63K8oxa1u0 golang.org/x/exp v0.0.0-20200119233911-0405dc783f0a/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= golang.org/x/exp v0.0.0-20200207192155-f17229e696bd/go.mod h1:J/WKrq2StrnmMY6+EHIKF9dgMWnmCNThgcyBT1FY9mM= golang.org/x/exp v0.0.0-20200213203834-85f925bdd4d0/go.mod h1:IX6Eufr4L0ErOUlzqX/aFlHqsiKZRbV42Kb69e9VsTE= +golang.org/x/exp v0.0.0-20200224162631-6cc2880d07d6/go.mod h1:3jZMyOhIsHpP37uCMkUooju7aAi5cS1Q23tOzKc+0MU= +golang.org/x/exp v0.0.0-20200513190911-00229845015e h1:rMqLP+9XLy+LdbCXHjJHAmTfXCr93W7oruWA6Hq1Alc= +golang.org/x/exp v0.0.0-20200513190911-00229845015e/go.mod h1:4M0jN8W1tt0AVLNr8HDosyJCDCDuyL9N9+3m7wDWgKw= golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js= golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= @@ -434,6 +579,8 @@ golang.org/x/lint v0.0.0-20190909230951-414d861bb4ac/go.mod h1:6SW0HCj/g11FgYtHl golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= golang.org/x/lint v0.0.0-20191125180803-fdd1cda4f05f/go.mod h1:5qLYkcX4OjUUV8bRuDixDT3tpyyb+LUpUlRWLxfhWrs= golang.org/x/lint v0.0.0-20200130185559-910be7a94367/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= +golang.org/x/lint v0.0.0-20200302205851-738671d3881b h1:Wh+f8QHJXR411sJR8/vRBTZ7YapZaRvUcLFFJhusH0k= +golang.org/x/lint v0.0.0-20200302205851-738671d3881b/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= golang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU15maQ/Ox0txvL9dWGYEHz965HBQE= golang.org/x/mobile v0.0.0-20190719004257-d2bd2a29d028/go.mod h1:E/iHnbuqvinMTCcRqshq8CkpyQDoeVncDDYHnLhea+o= golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc= @@ -442,6 +589,8 @@ golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzB golang.org/x/mod v0.1.1-0.20191107180719-034126e5016b/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= golang.org/x/mod v0.2.0 h1:KU7oHjnv3XNWfa5COkzUifxZmxp1TyI7ImMXqFxLwvQ= golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/mod v0.3.0 h1:RM4zey1++hCTbCVQfnWeKs9/IEsaBLA8vTkd0WVtmH4= +golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= @@ -457,15 +606,28 @@ golang.org/x/net v0.0.0-20190501004415-9ce7a6920f09/go.mod h1:t9HGtf8HONx5eT2rtn golang.org/x/net v0.0.0-20190503192946-f4e77d36d62c/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190522155817-f3200d17e092/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks= golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks= +golang.org/x/net v0.0.0-20190613194153-d28f0bde5980/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20190620200207-3b0461eec859 h1:R/3boaszxrf1GEUWTVDzSKVwLmSJpwZ1yqXm8j0v2QI= golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20190628185345-da137c7871d7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20190724013045-ca1201d0de80/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20190813141303-74dc4d7220e7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20190923162816-aa69164e4478/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20191209160850-c0dbc17a3553/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20200114155413-6afb5195e5aa/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20200202094626-16171245cfb2/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200222125558-5a598a2470a0/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20200301022130-244492dfa37a h1:GuSPYbZzB5/dcLNCwLQLsg3obCJtX9IJhpXkvY7kzk0= golang.org/x/net v0.0.0-20200301022130-244492dfa37a/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200324143707-d3edc9973b7e/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= +golang.org/x/net v0.0.0-20200501053045-e0ff5e5a1de5/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= +golang.org/x/net v0.0.0-20200506145744-7e3656a0809f/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= +golang.org/x/net v0.0.0-20200513185701-a91f0712d120/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= +golang.org/x/net v0.0.0-20200520182314-0ba52f642ac2/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= +golang.org/x/net v0.0.0-20200625001655-4c5254603344/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= +golang.org/x/net v0.0.0-20200707034311-ab3426394381 h1:VXak5I6aEWmAXeQjA+QSZzlgNrpq9mjcfDemuexIKsU= +golang.org/x/net v0.0.0-20200707034311-ab3426394381/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20181106182150-f42d05182288/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= @@ -477,9 +639,13 @@ golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJ golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20190412183630-56d357773e84/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e h1:vcxGaoTs7kV8m5Np9uUNQin4BrLOthgV7252N8V+FwY= golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20200317015054-43a5402ce75a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208 h1:qwRHBd0NqMbJxfbotnDhm2ByMI1Shq4Y6oRJo21SGJA= +golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= @@ -491,14 +657,17 @@ golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5h golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190403152447-81d4e9dc473e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190419153524-e8e3143a4f4a/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190531175056-4c3a928424d2/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190726091711-fc99dfbffb4e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190813064441-fde4db37ae7a/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191001151750-bb3f8db39f24/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191010194322-b09406accb47/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191228213918-04cbcbbfeed8/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200113162924-86b910548bc1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -506,9 +675,20 @@ golang.org/x/sys v0.0.0-20200116001909-b77594299b42 h1:vEOn+mP2zCOVzKckCZy6YsCtD golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200122134326-e047566fdf82/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200124204421-9fbb57f87de9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200202164722-d101bd2416d5/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200212091648-12a6c2dcc1e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200302150141-5c8b2ff67527 h1:uYVVQ9WP/Ds2ROhcaGPeIdVq0RIXVLwsHlnvJ+cT1So= golang.org/x/sys v0.0.0-20200302150141-5c8b2ff67527/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200331124033-c3d80250170d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200501052902-10377860bb8e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200511232937-7e40ca221e25/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200515095857-1151b9dac4a9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200523222454-059865788121/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200615200032-f1bc736245b1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200625212154-ddb9806d33ae h1:Ih9Yo4hSPImZOpfGuA4bR/ORKTAbhZo2AbWNRCnevdo= +golang.org/x/sys v0.0.0-20200625212154-ddb9806d33ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.0 h1:g61tztE5qeGQ89tm6NTjjM9VPIm088od1l6aSorWRWg= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= @@ -520,6 +700,8 @@ golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxb golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20191024005414-555d28b269f0 h1:/5xXl8Y5W96D+TtHSlonuFqGHIWVuyCkGJLwGh9JJFs= golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +golang.org/x/time v0.0.0-20200416051211-89c76fbcd5d1 h1:NusfzzA6yGQ+ua51ck7E3omNUX/JuqbFSaRGqU8CcLI= +golang.org/x/time v0.0.0-20200416051211-89c76fbcd5d1/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/tools v0.0.0-20180221164845-07fd8470d635/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20180828015842-6cd1fcedba52/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= @@ -530,11 +712,15 @@ golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3 golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= golang.org/x/tools v0.0.0-20190312151545-0bb0c0a6e846/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= golang.org/x/tools v0.0.0-20190312170243-e65039ee4138/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +golang.org/x/tools v0.0.0-20190329151228-23e29df326fe/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +golang.org/x/tools v0.0.0-20190416151739-9c9e1878f421/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +golang.org/x/tools v0.0.0-20190420181800-aa740d480789/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= golang.org/x/tools v0.0.0-20190425150028-36563e24a262/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= golang.org/x/tools v0.0.0-20190425163242-31fd60d6bfdc/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= golang.org/x/tools v0.0.0-20190506145303-2d16b83fe98c/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= golang.org/x/tools v0.0.0-20190515012406-7d7faa4812bd/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= +golang.org/x/tools v0.0.0-20190531172133-b3315ee88b7d/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= golang.org/x/tools v0.0.0-20190606124116-d0a3d012864b/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= golang.org/x/tools v0.0.0-20190624222133-a101b041ded4/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= @@ -547,6 +733,7 @@ golang.org/x/tools v0.0.0-20191113191852-77e3bb0ad9e7/go.mod h1:b+2E5dAYhXwXZwtn golang.org/x/tools v0.0.0-20191115202509-3a792d9c32b2/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191125144606-a911d9008d1f/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191130070609-6e064ea0cf2d/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191216173652-a0e659d51361/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= golang.org/x/tools v0.0.0-20191227053925-7b8e75db28f4/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= golang.org/x/tools v0.0.0-20200114235610-7ae403b6b589 h1:rjUrONFu4kLchcZTfp3/96bR8bW8dIa8uz3cR5n0cgM= @@ -555,11 +742,24 @@ golang.org/x/tools v0.0.0-20200117161641-43d50277825c/go.mod h1:TB2adYChydJhpapK golang.org/x/tools v0.0.0-20200122220014-bf1340f18c4a/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= golang.org/x/tools v0.0.0-20200128002243-345141a36859/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= golang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200204074204-1cc6d1ef6c74/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= golang.org/x/tools v0.0.0-20200207183749-b753a1ba74fa/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= golang.org/x/tools v0.0.0-20200212150539-ea181f53ac56/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= golang.org/x/tools v0.0.0-20200213224642-88e652f7a869/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20200303165918-5bcca83a7881 h1:6bcQ/hWOMu5dXxMPcdxhx5uOoQBkeleqvbGdt4lh8hg= -golang.org/x/tools v0.0.0-20200303165918-5bcca83a7881/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200224181240-023911ca70b2/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200227222343-706bc42d1f0d/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200304193943-95d2e580d8eb/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw= +golang.org/x/tools v0.0.0-20200312045724-11d5b4c81c7d/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw= +golang.org/x/tools v0.0.0-20200331025713-a30bf2db82d4/go.mod h1:Sl4aGygMT6LrqrWclx+PTx3U+LnKx/seiNR+3G19Ar8= +golang.org/x/tools v0.0.0-20200501065659-ab2804fb9c9d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= +golang.org/x/tools v0.0.0-20200512131952-2bc93b1c0c88/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= +golang.org/x/tools v0.0.0-20200515010526-7d3b6ebf133d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= +golang.org/x/tools v0.0.0-20200618134242-20370b0cb4b2/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= +golang.org/x/tools v0.0.0-20200626171337-aa94e735be7f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= +golang.org/x/tools v0.0.0-20200706234117-b22de6825cf7/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= +golang.org/x/tools v0.0.0-20200713011307-fd294ab11aed/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= +golang.org/x/tools v0.0.0-20200715235423-130c9f19d3fe h1:q+DqTBqwpg3C8p5kTZ0WSLOmLxH57dKhWAieWmuFv7M= +golang.org/x/tools v0.0.0-20200715235423-130c9f19d3fe/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= golang.org/x/xerrors v0.0.0-20190410155217-1f06c39b4373/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20190513163551-3ee3066db522/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= @@ -575,6 +775,14 @@ google.golang.org/api v0.13.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsb google.golang.org/api v0.14.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= google.golang.org/api v0.15.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= google.golang.org/api v0.17.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= +google.golang.org/api v0.18.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= +google.golang.org/api v0.19.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= +google.golang.org/api v0.20.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= +google.golang.org/api v0.22.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= +google.golang.org/api v0.24.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0MncE= +google.golang.org/api v0.28.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0MncE= +google.golang.org/api v0.29.0 h1:BaiDisFir8O4IJxvAabCGGkQ6yCJegNQqSVoYUNAnbk= +google.golang.org/api v0.29.0/go.mod h1:Lcubydp8VUV7KeIHD9z2Bys/sm/vGKnG1UHuDBSrHWM= google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= google.golang.org/appengine v1.3.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= @@ -582,6 +790,8 @@ google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7 google.golang.org/appengine v1.6.1/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww/cMBSeb0= google.golang.org/appengine v1.6.5 h1:tycE03LOZYQNhDpS27tcQdAzLCVMaj7QT2SXxebnpCM= google.golang.org/appengine v1.6.5/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= +google.golang.org/appengine v1.6.6 h1:lMO5rYAqUxkmaj76jAkRUvt5JZgFymx/+Q5Mzfivuhc= +google.golang.org/appengine v1.6.6/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= google.golang.org/genproto v0.0.0-20190404172233-64821d5d2107/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= @@ -591,6 +801,7 @@ google.golang.org/genproto v0.0.0-20190502173448-54afdca5d873/go.mod h1:VzzqZJRn google.golang.org/genproto v0.0.0-20190801165951-fa694d86fc64/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= google.golang.org/genproto v0.0.0-20190911173649-1774047e7e51/go.mod h1:IbNlFCBrqXvoKpeg0TB2l7cyZUmoaFKYIwrEpbDKLA8= +google.golang.org/genproto v0.0.0-20191009194640-548a555dbc03/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= google.golang.org/genproto v0.0.0-20191108220845-16a3f7862a1a/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= google.golang.org/genproto v0.0.0-20191115194625-c23dd37a84c9/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= google.golang.org/genproto v0.0.0-20191216164720-4f79533eabd1/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= @@ -598,19 +809,49 @@ google.golang.org/genproto v0.0.0-20191230161307-f3c370f40bfb/go.mod h1:n3cpQtvx google.golang.org/genproto v0.0.0-20200115191322-ca5a22157cba/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= google.golang.org/genproto v0.0.0-20200122232147-0452cf42e150/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= google.golang.org/genproto v0.0.0-20200128133413-58ce757ed39b/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= +google.golang.org/genproto v0.0.0-20200204135345-fa8e72b47b90/go.mod h1:GmwEX6Z4W5gMy59cAlVYjN9JhxgbQH6Gn+gFDQe2lzA= google.golang.org/genproto v0.0.0-20200212174721-66ed5ce911ce/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= -google.golang.org/genproto v0.0.0-20200303153909-beee998c1893 h1:OTjq5CN+5TpMIvzqxSFCjbBX3jNKjX0XOPi4SdBxQU8= -google.golang.org/genproto v0.0.0-20200303153909-beee998c1893/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200224152610-e50cd9704f63/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200228133532-8c2c7df3a383/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200305110556-506484158171/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200312145019-da6875a35672/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200331122359-1ee6d9798940/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200430143042-b979b6f78d84/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200511104702-f5ebc3bea380/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200515170657-fc4c6c6a6587/go.mod h1:YsZOwe1myG/8QRHRsmBRE1LrgQY60beZKjly0O1fX9U= +google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo= +google.golang.org/genproto v0.0.0-20200618031413-b414f8b61790/go.mod h1:jDfRM7FcilCzHH/e9qn6dsT145K34l5v+OpcnNgKAAA= +google.golang.org/genproto v0.0.0-20200626011028-ee7919e894b5/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20200707001353-8e8330bf89df/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20200711021454-869866162049/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20200715011427-11fb19a81f2c h1:6DWnZZ6EY/59QRRQttZKiktVL23UuQYs7uy75MhhLRM= +google.golang.org/genproto v0.0.0-20200715011427-11fb19a81f2c/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= google.golang.org/grpc v1.17.0/go.mod h1:6QZJwpn2B+Zp71q/5VxRsJ6NXXVCE5NRUHRo+f3cWCs= google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38= google.golang.org/grpc v1.21.0/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM= google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM= google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= +google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY= google.golang.org/grpc v1.26.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= google.golang.org/grpc v1.27.1 h1:zvIju4sqAGvwKspUQOhwnpcqSbzi7/H6QomNNjTL4sk= google.golang.org/grpc v1.27.1/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= +google.golang.org/grpc v1.28.0/go.mod h1:rpkK4SK4GF4Ach/+MFLZUBavHOvF2JJB5uozKKal+60= +google.golang.org/grpc v1.29.1/go.mod h1:itym6AZVZYACWQqET3MqgPpjcuV5QH3BxFS3IjizoKk= +google.golang.org/grpc v1.30.0 h1:M5a8xTlYTxwMn5ZFkwhRabsygDY5G8TYLyQDBxJNAxE= +google.golang.org/grpc v1.30.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= +google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= +google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= +google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= +google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE= +google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo= +google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= +google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= +google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= +google.golang.org/protobuf v1.24.0/go.mod h1:r/3tXBNzIEhYS9I1OUVjXDlt8tc493IdKGjtUeSXeh4= +google.golang.org/protobuf v1.25.0 h1:Ejskq+SyPohKW+1uil0JJMtmHCgJPJ/qWTxr8qp+R4c= +google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c= gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= @@ -632,6 +873,8 @@ gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.7/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.3.0 h1:clyUAQHOM3G0M3f5vQj7LuJrETvjVot3Z5el9nffUtU= gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo= +gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gotest.tools/v3 v3.0.2 h1:kG1BFyqVHuQoVQiR1bWGnfz/fmHvvuiSPIV7rvl360E= gotest.tools/v3 v3.0.2/go.mod h1:3SzNCllyD9/Y+b5r9JIKQ474KzkZyqLqEfYqMsX94Bk= honnef.co/go/tools v0.0.0-20180728063816-88497007e858/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= @@ -640,6 +883,8 @@ honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWh honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg= +honnef.co/go/tools v0.0.1-2020.1.3/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= +honnef.co/go/tools v0.0.1-2020.1.4/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= modernc.org/b v1.0.0/go.mod h1:uZWcZfRj1BpYzfN9JTerzlNUnnPsV9O2ZA8JsRcubNg= modernc.org/db v1.0.0/go.mod h1:kYD/cO29L/29RM0hXYl4i3+Q5VojL31kTUVpVJDw0s8= modernc.org/file v1.0.0/go.mod h1:uqEokAEn1u6e+J45e54dsEA/pw4o7zLrA2GwyntZzjw= diff --git a/internal/commands/commands.go b/internal/commands/commands.go index dbdcd13..9c45896 100644 --- a/internal/commands/commands.go +++ b/internal/commands/commands.go @@ -67,6 +67,6 @@ func initConfig() { // Execute the root cobra command func Execute() { rootCmd.SetVersionTemplate(versionTemplate) - rootCmd.AddCommand(newWebCmd(), newMigrateCmd(), newTokenCmd()) + rootCmd.AddCommand(newWebCmd(), newMigrateCmd(), newTokenCmd(), newWorkerCmd()) rootCmd.Execute() } diff --git a/internal/commands/web.go b/internal/commands/web.go index 9385480..0adc14b 100644 --- a/internal/commands/web.go +++ b/internal/commands/web.go @@ -53,6 +53,7 @@ func newWebCmd() *cobra.Command { db.SetMaxIdleConns(25) db.SetConnMaxLifetime(5 * time.Minute) defer db.Close() + if viper.GetBool("migrate") { log.Info("running auto schema migrations") if err = runMigration(db); err != nil { @@ -74,6 +75,9 @@ func newWebCmd() *cobra.Command { viper.SetDefault("database.name", "taskcafe") viper.SetDefault("database.user", "taskcafe") viper.SetDefault("database.password", "taskcafe_test") + + viper.SetDefault("queue.broker", "amqp://guest:guest@localhost:5672/") + viper.SetDefault("queue.store", "memcache://localhost:11211") return cc } diff --git a/internal/commands/worker.go b/internal/commands/worker.go new file mode 100644 index 0000000..7855fb0 --- /dev/null +++ b/internal/commands/worker.go @@ -0,0 +1,83 @@ +package commands + +import ( + "fmt" + "github.com/spf13/cobra" + "github.com/spf13/viper" + "time" + + "github.com/RichardKnop/machinery/v1" + "github.com/RichardKnop/machinery/v1/config" + queueLog "github.com/RichardKnop/machinery/v1/log" + "github.com/jmoiron/sqlx" + repo "github.com/jordanknott/taskcafe/internal/db" + "github.com/jordanknott/taskcafe/internal/notification" + log "github.com/sirupsen/logrus" +) + +func newWorkerCmd() *cobra.Command { + cc := &cobra.Command{ + Use: "worker", + Short: "Run the task queue worker", + Long: "Run the task queue worker", + RunE: func(cmd *cobra.Command, args []string) error { + Formatter := new(log.TextFormatter) + Formatter.TimestampFormat = "02-01-2006 15:04:05" + Formatter.FullTimestamp = true + log.SetFormatter(Formatter) + log.SetLevel(log.InfoLevel) + + connection := fmt.Sprintf("user=%s password=%s host=%s dbname=%s sslmode=disable", + viper.GetString("database.user"), + viper.GetString("database.password"), + viper.GetString("database.host"), + viper.GetString("database.name"), + ) + db, err := sqlx.Connect("postgres", connection) + if err != nil { + log.Panic(err) + } + db.SetMaxOpenConns(25) + db.SetMaxIdleConns(25) + db.SetConnMaxLifetime(5 * time.Minute) + defer db.Close() + + var cnf = &config.Config{ + Broker: viper.GetString("queue.broker"), + DefaultQueue: "machinery_tasks", + ResultBackend: viper.GetString("queue.store"), + AMQP: &config.AMQPConfig{ + Exchange: "machinery_exchange", + ExchangeType: "direct", + BindingKey: "machinery_task", + }, + } + + log.Info("starting task queue server instance") + server, err := machinery.NewServer(cnf) + if err != nil { + // do something with the error + } + queueLog.Set(¬ification.MachineryLogger{}) + repo := *repo.NewRepository(db) + notification.RegisterTasks(server, repo) + + worker := server.NewWorker("taskcafe_worker", 10) + log.Info("starting task queue worker") + err = worker.Launch() + if err != nil { + log.WithError(err).Error("error while launching ") + return err + } + return nil + }, + } + viper.SetDefault("database.host", "127.0.0.1") + viper.SetDefault("database.name", "taskcafe") + viper.SetDefault("database.user", "taskcafe") + viper.SetDefault("database.password", "taskcafe_test") + + viper.SetDefault("queue.broker", "amqp://guest:guest@localhost:5672/") + viper.SetDefault("queue.store", "memcache://localhost:11211") + return cc +} diff --git a/internal/db/models.go b/internal/db/models.go index bad118a..231db64 100644 --- a/internal/db/models.go +++ b/internal/db/models.go @@ -16,6 +16,22 @@ type LabelColor struct { Name string `json:"name"` } +type Notification struct { + NotificationID uuid.UUID `json:"notification_id"` + NotificationObjectID uuid.UUID `json:"notification_object_id"` + NotifierID uuid.UUID `json:"notifier_id"` + Read bool `json:"read"` +} + +type NotificationObject struct { + NotificationObjectID uuid.UUID `json:"notification_object_id"` + EntityID uuid.UUID `json:"entity_id"` + ActionType int32 `json:"action_type"` + ActorID uuid.UUID `json:"actor_id"` + EntityType int32 `json:"entity_type"` + CreatedOn time.Time `json:"created_on"` +} + type Organization struct { OrganizationID uuid.UUID `json:"organization_id"` CreatedAt time.Time `json:"created_at"` diff --git a/internal/db/notification.sql.go b/internal/db/notification.sql.go new file mode 100644 index 0000000..4403ec5 --- /dev/null +++ b/internal/db/notification.sql.go @@ -0,0 +1,177 @@ +// Code generated by sqlc. DO NOT EDIT. +// source: notification.sql + +package db + +import ( + "context" + "time" + + "github.com/google/uuid" +) + +const createNotification = `-- name: CreateNotification :one +INSERT INTO notification(notification_object_id, notifier_id) + VALUES ($1, $2) RETURNING notification_id, notification_object_id, notifier_id, read +` + +type CreateNotificationParams struct { + NotificationObjectID uuid.UUID `json:"notification_object_id"` + NotifierID uuid.UUID `json:"notifier_id"` +} + +func (q *Queries) CreateNotification(ctx context.Context, arg CreateNotificationParams) (Notification, error) { + row := q.db.QueryRowContext(ctx, createNotification, arg.NotificationObjectID, arg.NotifierID) + var i Notification + err := row.Scan( + &i.NotificationID, + &i.NotificationObjectID, + &i.NotifierID, + &i.Read, + ) + return i, err +} + +const createNotificationObject = `-- name: CreateNotificationObject :one +INSERT INTO notification_object(entity_type, action_type, entity_id, created_on, actor_id) + VALUES ($1, $2, $3, $4, $5) RETURNING notification_object_id, entity_id, action_type, actor_id, entity_type, created_on +` + +type CreateNotificationObjectParams struct { + EntityType int32 `json:"entity_type"` + ActionType int32 `json:"action_type"` + EntityID uuid.UUID `json:"entity_id"` + CreatedOn time.Time `json:"created_on"` + ActorID uuid.UUID `json:"actor_id"` +} + +func (q *Queries) CreateNotificationObject(ctx context.Context, arg CreateNotificationObjectParams) (NotificationObject, error) { + row := q.db.QueryRowContext(ctx, createNotificationObject, + arg.EntityType, + arg.ActionType, + arg.EntityID, + arg.CreatedOn, + arg.ActorID, + ) + var i NotificationObject + err := row.Scan( + &i.NotificationObjectID, + &i.EntityID, + &i.ActionType, + &i.ActorID, + &i.EntityType, + &i.CreatedOn, + ) + return i, err +} + +const getAllNotificationsForUserID = `-- name: GetAllNotificationsForUserID :many +SELECT n.notification_id, n.notification_object_id, n.notifier_id, n.read FROM notification as n +INNER JOIN notification_object as no ON no.notification_object_id = n.notification_object_id +WHERE n.notifier_id = $1 ORDER BY no.created_on DESC +` + +func (q *Queries) GetAllNotificationsForUserID(ctx context.Context, notifierID uuid.UUID) ([]Notification, error) { + rows, err := q.db.QueryContext(ctx, getAllNotificationsForUserID, notifierID) + if err != nil { + return nil, err + } + defer rows.Close() + var items []Notification + for rows.Next() { + var i Notification + if err := rows.Scan( + &i.NotificationID, + &i.NotificationObjectID, + &i.NotifierID, + &i.Read, + ); err != nil { + return nil, err + } + items = append(items, i) + } + if err := rows.Close(); err != nil { + return nil, err + } + if err := rows.Err(); err != nil { + return nil, err + } + return items, nil +} + +const getEntityForNotificationID = `-- name: GetEntityForNotificationID :one +SELECT no.created_on, no.entity_id, no.entity_type, no.action_type, no.actor_id FROM notification as n + INNER JOIN notification_object as no ON no.notification_object_id = n.notification_object_id +WHERE n.notification_id = $1 +` + +type GetEntityForNotificationIDRow struct { + CreatedOn time.Time `json:"created_on"` + EntityID uuid.UUID `json:"entity_id"` + EntityType int32 `json:"entity_type"` + ActionType int32 `json:"action_type"` + ActorID uuid.UUID `json:"actor_id"` +} + +func (q *Queries) GetEntityForNotificationID(ctx context.Context, notificationID uuid.UUID) (GetEntityForNotificationIDRow, error) { + row := q.db.QueryRowContext(ctx, getEntityForNotificationID, notificationID) + var i GetEntityForNotificationIDRow + err := row.Scan( + &i.CreatedOn, + &i.EntityID, + &i.EntityType, + &i.ActionType, + &i.ActorID, + ) + return i, err +} + +const getEntityIDForNotificationID = `-- name: GetEntityIDForNotificationID :one +SELECT no.entity_id FROM notification as n + INNER JOIN notification_object as no ON no.notification_object_id = n.notification_object_id +WHERE n.notification_id = $1 +` + +func (q *Queries) GetEntityIDForNotificationID(ctx context.Context, notificationID uuid.UUID) (uuid.UUID, error) { + row := q.db.QueryRowContext(ctx, getEntityIDForNotificationID, notificationID) + var entity_id uuid.UUID + err := row.Scan(&entity_id) + return entity_id, err +} + +const getNotificationForNotificationID = `-- name: GetNotificationForNotificationID :one +SELECT n.notification_id, n.notification_object_id, n.notifier_id, n.read, no.notification_object_id, no.entity_id, no.action_type, no.actor_id, no.entity_type, no.created_on FROM notification as n + INNER JOIN notification_object as no ON no.notification_object_id = n.notification_object_id +WHERE n.notification_id = $1 +` + +type GetNotificationForNotificationIDRow struct { + NotificationID uuid.UUID `json:"notification_id"` + NotificationObjectID uuid.UUID `json:"notification_object_id"` + NotifierID uuid.UUID `json:"notifier_id"` + Read bool `json:"read"` + NotificationObjectID_2 uuid.UUID `json:"notification_object_id_2"` + EntityID uuid.UUID `json:"entity_id"` + ActionType int32 `json:"action_type"` + ActorID uuid.UUID `json:"actor_id"` + EntityType int32 `json:"entity_type"` + CreatedOn time.Time `json:"created_on"` +} + +func (q *Queries) GetNotificationForNotificationID(ctx context.Context, notificationID uuid.UUID) (GetNotificationForNotificationIDRow, error) { + row := q.db.QueryRowContext(ctx, getNotificationForNotificationID, notificationID) + var i GetNotificationForNotificationIDRow + err := row.Scan( + &i.NotificationID, + &i.NotificationObjectID, + &i.NotifierID, + &i.Read, + &i.NotificationObjectID_2, + &i.EntityID, + &i.ActionType, + &i.ActorID, + &i.EntityType, + &i.CreatedOn, + ) + return i, err +} diff --git a/internal/db/querier.go b/internal/db/querier.go index 8b66f6a..a2a81d9 100644 --- a/internal/db/querier.go +++ b/internal/db/querier.go @@ -10,6 +10,8 @@ import ( type Querier interface { CreateLabelColor(ctx context.Context, arg CreateLabelColorParams) (LabelColor, error) + CreateNotification(ctx context.Context, arg CreateNotificationParams) (Notification, error) + CreateNotificationObject(ctx context.Context, arg CreateNotificationObjectParams) (NotificationObject, error) CreateOrganization(ctx context.Context, arg CreateOrganizationParams) (Organization, error) CreateProject(ctx context.Context, arg CreateProjectParams) (Project, error) CreateProjectLabel(ctx context.Context, arg CreateProjectLabelParams) (ProjectLabel, error) @@ -42,6 +44,7 @@ type Querier interface { DeleteTeamByID(ctx context.Context, teamID uuid.UUID) error DeleteTeamMember(ctx context.Context, arg DeleteTeamMemberParams) error DeleteUserAccountByID(ctx context.Context, userID uuid.UUID) error + GetAllNotificationsForUserID(ctx context.Context, notifierID uuid.UUID) ([]Notification, error) GetAllOrganizations(ctx context.Context) ([]Organization, error) GetAllProjects(ctx context.Context) ([]Project, error) GetAllProjectsForTeam(ctx context.Context, teamID uuid.UUID) ([]Project, error) @@ -51,10 +54,13 @@ type Querier interface { GetAllUserAccounts(ctx context.Context) ([]UserAccount, error) GetAllVisibleProjectsForUserID(ctx context.Context, userID uuid.UUID) ([]Project, error) GetAssignedMembersForTask(ctx context.Context, taskID uuid.UUID) ([]TaskAssigned, error) + GetEntityForNotificationID(ctx context.Context, notificationID uuid.UUID) (GetEntityForNotificationIDRow, error) + GetEntityIDForNotificationID(ctx context.Context, notificationID uuid.UUID) (uuid.UUID, error) GetLabelColorByID(ctx context.Context, labelColorID uuid.UUID) (LabelColor, error) GetLabelColors(ctx context.Context) ([]LabelColor, error) GetMemberProjectIDsForUserID(ctx context.Context, userID uuid.UUID) ([]uuid.UUID, error) GetMemberTeamIDsForUserID(ctx context.Context, userID uuid.UUID) ([]uuid.UUID, error) + GetNotificationForNotificationID(ctx context.Context, notificationID uuid.UUID) (GetNotificationForNotificationIDRow, error) GetProjectByID(ctx context.Context, projectID uuid.UUID) (Project, error) GetProjectIDForTask(ctx context.Context, taskID uuid.UUID) (uuid.UUID, error) GetProjectLabelByID(ctx context.Context, projectLabelID uuid.UUID) (ProjectLabel, error) diff --git a/internal/db/query/notification.sql b/internal/db/query/notification.sql new file mode 100644 index 0000000..c8c86ad --- /dev/null +++ b/internal/db/query/notification.sql @@ -0,0 +1,28 @@ +-- name: GetAllNotificationsForUserID :many +SELECT n.* FROM notification as n +INNER JOIN notification_object as no ON no.notification_object_id = n.notification_object_id +WHERE n.notifier_id = $1 ORDER BY no.created_on DESC; + +-- name: GetNotificationForNotificationID :one +SELECT n.*, no.* FROM notification as n + INNER JOIN notification_object as no ON no.notification_object_id = n.notification_object_id +WHERE n.notification_id = $1; + +-- name: CreateNotificationObject :one +INSERT INTO notification_object(entity_type, action_type, entity_id, created_on, actor_id) + VALUES ($1, $2, $3, $4, $5) RETURNING *; + +-- name: GetEntityIDForNotificationID :one +SELECT no.entity_id FROM notification as n + INNER JOIN notification_object as no ON no.notification_object_id = n.notification_object_id +WHERE n.notification_id = $1; + +-- name: GetEntityForNotificationID :one +SELECT no.created_on, no.entity_id, no.entity_type, no.action_type, no.actor_id FROM notification as n + INNER JOIN notification_object as no ON no.notification_object_id = n.notification_object_id +WHERE n.notification_id = $1; + +-- name: CreateNotification :one +INSERT INTO notification(notification_object_id, notifier_id) + VALUES ($1, $2) RETURNING *; + diff --git a/internal/graph/generated.go b/internal/graph/generated.go index 48806b7..f1689d5 100644 --- a/internal/graph/generated.go +++ b/internal/graph/generated.go @@ -40,6 +40,7 @@ type Config struct { type ResolverRoot interface { LabelColor() LabelColorResolver Mutation() MutationResolver + Notification() NotificationResolver Organization() OrganizationResolver Project() ProjectResolver ProjectLabel() ProjectLabelResolver @@ -201,6 +202,27 @@ type ComplexityRoot struct { UpdateUserRole func(childComplexity int, input UpdateUserRole) int } + Notification struct { + ActionType func(childComplexity int) int + Actor func(childComplexity int) int + CreatedAt func(childComplexity int) int + Entity func(childComplexity int) int + ID func(childComplexity int) int + Read func(childComplexity int) int + } + + NotificationActor struct { + ID func(childComplexity int) int + Name func(childComplexity int) int + Type func(childComplexity int) int + } + + NotificationEntity struct { + ID func(childComplexity int) int + Name func(childComplexity int) int + Type func(childComplexity int) int + } + Organization struct { ID func(childComplexity int) int Name func(childComplexity int) int @@ -251,6 +273,7 @@ type ComplexityRoot struct { FindUser func(childComplexity int, input FindUser) int LabelColors func(childComplexity int) int Me func(childComplexity int) int + Notifications func(childComplexity int) int Organizations func(childComplexity int) int Projects func(childComplexity int, input *ProjectsFilter) int TaskGroups func(childComplexity int) int @@ -440,6 +463,14 @@ type MutationResolver interface { UpdateUserPassword(ctx context.Context, input UpdateUserPassword) (*UpdateUserPasswordPayload, error) UpdateUserRole(ctx context.Context, input UpdateUserRole) (*UpdateUserRolePayload, error) } +type NotificationResolver interface { + ID(ctx context.Context, obj *db.Notification) (uuid.UUID, error) + Entity(ctx context.Context, obj *db.Notification) (*NotificationEntity, error) + ActionType(ctx context.Context, obj *db.Notification) (ActionType, error) + Actor(ctx context.Context, obj *db.Notification) (*NotificationActor, error) + + CreatedAt(ctx context.Context, obj *db.Notification) (*time.Time, error) +} type OrganizationResolver interface { ID(ctx context.Context, obj *db.Organization) (uuid.UUID, error) } @@ -469,6 +500,7 @@ type QueryResolver interface { LabelColors(ctx context.Context) ([]db.LabelColor, error) TaskGroups(ctx context.Context) ([]db.TaskGroup, error) Me(ctx context.Context) (*MePayload, error) + Notifications(ctx context.Context) ([]db.Notification, error) } type RefreshTokenResolver interface { ID(ctx context.Context, obj *db.RefreshToken) (uuid.UUID, error) @@ -1408,6 +1440,90 @@ func (e *executableSchema) Complexity(typeName, field string, childComplexity in return e.complexity.Mutation.UpdateUserRole(childComplexity, args["input"].(UpdateUserRole)), true + case "Notification.actionType": + if e.complexity.Notification.ActionType == nil { + break + } + + return e.complexity.Notification.ActionType(childComplexity), true + + case "Notification.actor": + if e.complexity.Notification.Actor == nil { + break + } + + return e.complexity.Notification.Actor(childComplexity), true + + case "Notification.createdAt": + if e.complexity.Notification.CreatedAt == nil { + break + } + + return e.complexity.Notification.CreatedAt(childComplexity), true + + case "Notification.entity": + if e.complexity.Notification.Entity == nil { + break + } + + return e.complexity.Notification.Entity(childComplexity), true + + case "Notification.id": + if e.complexity.Notification.ID == nil { + break + } + + return e.complexity.Notification.ID(childComplexity), true + + case "Notification.read": + if e.complexity.Notification.Read == nil { + break + } + + return e.complexity.Notification.Read(childComplexity), true + + case "NotificationActor.id": + if e.complexity.NotificationActor.ID == nil { + break + } + + return e.complexity.NotificationActor.ID(childComplexity), true + + case "NotificationActor.name": + if e.complexity.NotificationActor.Name == nil { + break + } + + return e.complexity.NotificationActor.Name(childComplexity), true + + case "NotificationActor.type": + if e.complexity.NotificationActor.Type == nil { + break + } + + return e.complexity.NotificationActor.Type(childComplexity), true + + case "NotificationEntity.id": + if e.complexity.NotificationEntity.ID == nil { + break + } + + return e.complexity.NotificationEntity.ID(childComplexity), true + + case "NotificationEntity.name": + if e.complexity.NotificationEntity.Name == nil { + break + } + + return e.complexity.NotificationEntity.Name(childComplexity), true + + case "NotificationEntity.type": + if e.complexity.NotificationEntity.Type == nil { + break + } + + return e.complexity.NotificationEntity.Type(childComplexity), true + case "Organization.id": if e.complexity.Organization.ID == nil { break @@ -1624,6 +1740,13 @@ func (e *executableSchema) Complexity(typeName, field string, childComplexity in return e.complexity.Query.Me(childComplexity), true + case "Query.notifications": + if e.complexity.Query.Notifications == nil { + break + } + + return e.complexity.Query.Notifications(childComplexity), true + case "Query.organizations": if e.complexity.Query.Organizations == nil { break @@ -2456,6 +2579,43 @@ input FindTeam { teamID: UUID! } +extend type Query { + notifications: [Notification!]! +} + +enum EntityType { + TASK +} + +enum ActorType { + USER +} + +enum ActionType { + TASK_MEMBER_ADDED +} + +type NotificationActor { + id: UUID! + type: ActorType! + name: String! +} + +type NotificationEntity { + id: UUID! + type: EntityType! + name: String! +} + +type Notification { + id: ID! + entity: NotificationEntity! + actionType: ActionType! + actor: NotificationActor! + read: Boolean! + createdAt: Time! +} + extend type Mutation { createProject(input: NewProject!): Project! @hasRole(roles: [ADMIN], level: TEAM, type: TEAM) deleteProject(input: DeleteProject!): @@ -2582,9 +2742,9 @@ extend type Mutation { Task! @hasRole(roles: [ADMIN], level: PROJECT, type: PROJECT) assignTask(input: AssignTaskInput): - Task! @hasRole(roles: [ADMIN], level: PROJECT, type: PROJECT) + Task! @hasRole(roles: [ADMIN], level: PROJECT, type: TASK) unassignTask(input: UnassignTaskInput): - Task! @hasRole(roles: [ADMIN], level: PROJECT, type: PROJECT) + Task! @hasRole(roles: [ADMIN], level: PROJECT, type: TASK) } input NewTask { @@ -6547,7 +6707,7 @@ func (ec *executionContext) _Mutation_assignTask(ctx context.Context, field grap if err != nil { return nil, err } - typeArg, err := ec.unmarshalNObjectType2githubᚗcomᚋjordanknottᚋtaskcafeᚋinternalᚋgraphᚐObjectType(ctx, "PROJECT") + typeArg, err := ec.unmarshalNObjectType2githubᚗcomᚋjordanknottᚋtaskcafeᚋinternalᚋgraphᚐObjectType(ctx, "TASK") if err != nil { return nil, err } @@ -6620,7 +6780,7 @@ func (ec *executionContext) _Mutation_unassignTask(ctx context.Context, field gr if err != nil { return nil, err } - typeArg, err := ec.unmarshalNObjectType2githubᚗcomᚋjordanknottᚋtaskcafeᚋinternalᚋgraphᚐObjectType(ctx, "PROJECT") + typeArg, err := ec.unmarshalNObjectType2githubᚗcomᚋjordanknottᚋtaskcafeᚋinternalᚋgraphᚐObjectType(ctx, "TASK") if err != nil { return nil, err } @@ -8566,6 +8726,414 @@ func (ec *executionContext) _Mutation_updateUserRole(ctx context.Context, field return ec.marshalNUpdateUserRolePayload2ᚖgithubᚗcomᚋjordanknottᚋtaskcafeᚋinternalᚋgraphᚐUpdateUserRolePayload(ctx, field.Selections, res) } +func (ec *executionContext) _Notification_id(ctx context.Context, field graphql.CollectedField, obj *db.Notification) (ret graphql.Marshaler) { + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = graphql.Null + } + }() + fc := &graphql.FieldContext{ + Object: "Notification", + Field: field, + Args: nil, + IsMethod: true, + } + + ctx = graphql.WithFieldContext(ctx, fc) + resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { + ctx = rctx // use context from middleware stack in children + return ec.resolvers.Notification().ID(rctx, obj) + }) + if err != nil { + ec.Error(ctx, err) + return graphql.Null + } + if resTmp == nil { + if !graphql.HasFieldError(ctx, fc) { + ec.Errorf(ctx, "must not be null") + } + return graphql.Null + } + res := resTmp.(uuid.UUID) + fc.Result = res + return ec.marshalNID2githubᚗcomᚋgoogleᚋuuidᚐUUID(ctx, field.Selections, res) +} + +func (ec *executionContext) _Notification_entity(ctx context.Context, field graphql.CollectedField, obj *db.Notification) (ret graphql.Marshaler) { + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = graphql.Null + } + }() + fc := &graphql.FieldContext{ + Object: "Notification", + Field: field, + Args: nil, + IsMethod: true, + } + + ctx = graphql.WithFieldContext(ctx, fc) + resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { + ctx = rctx // use context from middleware stack in children + return ec.resolvers.Notification().Entity(rctx, obj) + }) + if err != nil { + ec.Error(ctx, err) + return graphql.Null + } + if resTmp == nil { + if !graphql.HasFieldError(ctx, fc) { + ec.Errorf(ctx, "must not be null") + } + return graphql.Null + } + res := resTmp.(*NotificationEntity) + fc.Result = res + return ec.marshalNNotificationEntity2ᚖgithubᚗcomᚋjordanknottᚋtaskcafeᚋinternalᚋgraphᚐNotificationEntity(ctx, field.Selections, res) +} + +func (ec *executionContext) _Notification_actionType(ctx context.Context, field graphql.CollectedField, obj *db.Notification) (ret graphql.Marshaler) { + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = graphql.Null + } + }() + fc := &graphql.FieldContext{ + Object: "Notification", + Field: field, + Args: nil, + IsMethod: true, + } + + ctx = graphql.WithFieldContext(ctx, fc) + resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { + ctx = rctx // use context from middleware stack in children + return ec.resolvers.Notification().ActionType(rctx, obj) + }) + if err != nil { + ec.Error(ctx, err) + return graphql.Null + } + if resTmp == nil { + if !graphql.HasFieldError(ctx, fc) { + ec.Errorf(ctx, "must not be null") + } + return graphql.Null + } + res := resTmp.(ActionType) + fc.Result = res + return ec.marshalNActionType2githubᚗcomᚋjordanknottᚋtaskcafeᚋinternalᚋgraphᚐActionType(ctx, field.Selections, res) +} + +func (ec *executionContext) _Notification_actor(ctx context.Context, field graphql.CollectedField, obj *db.Notification) (ret graphql.Marshaler) { + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = graphql.Null + } + }() + fc := &graphql.FieldContext{ + Object: "Notification", + Field: field, + Args: nil, + IsMethod: true, + } + + ctx = graphql.WithFieldContext(ctx, fc) + resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { + ctx = rctx // use context from middleware stack in children + return ec.resolvers.Notification().Actor(rctx, obj) + }) + if err != nil { + ec.Error(ctx, err) + return graphql.Null + } + if resTmp == nil { + if !graphql.HasFieldError(ctx, fc) { + ec.Errorf(ctx, "must not be null") + } + return graphql.Null + } + res := resTmp.(*NotificationActor) + fc.Result = res + return ec.marshalNNotificationActor2ᚖgithubᚗcomᚋjordanknottᚋtaskcafeᚋinternalᚋgraphᚐNotificationActor(ctx, field.Selections, res) +} + +func (ec *executionContext) _Notification_read(ctx context.Context, field graphql.CollectedField, obj *db.Notification) (ret graphql.Marshaler) { + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = graphql.Null + } + }() + fc := &graphql.FieldContext{ + Object: "Notification", + Field: field, + Args: nil, + IsMethod: false, + } + + ctx = graphql.WithFieldContext(ctx, fc) + resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { + ctx = rctx // use context from middleware stack in children + return obj.Read, nil + }) + if err != nil { + ec.Error(ctx, err) + return graphql.Null + } + if resTmp == nil { + if !graphql.HasFieldError(ctx, fc) { + ec.Errorf(ctx, "must not be null") + } + return graphql.Null + } + res := resTmp.(bool) + fc.Result = res + return ec.marshalNBoolean2bool(ctx, field.Selections, res) +} + +func (ec *executionContext) _Notification_createdAt(ctx context.Context, field graphql.CollectedField, obj *db.Notification) (ret graphql.Marshaler) { + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = graphql.Null + } + }() + fc := &graphql.FieldContext{ + Object: "Notification", + Field: field, + Args: nil, + IsMethod: true, + } + + ctx = graphql.WithFieldContext(ctx, fc) + resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { + ctx = rctx // use context from middleware stack in children + return ec.resolvers.Notification().CreatedAt(rctx, obj) + }) + if err != nil { + ec.Error(ctx, err) + return graphql.Null + } + if resTmp == nil { + if !graphql.HasFieldError(ctx, fc) { + ec.Errorf(ctx, "must not be null") + } + return graphql.Null + } + res := resTmp.(*time.Time) + fc.Result = res + return ec.marshalNTime2ᚖtimeᚐTime(ctx, field.Selections, res) +} + +func (ec *executionContext) _NotificationActor_id(ctx context.Context, field graphql.CollectedField, obj *NotificationActor) (ret graphql.Marshaler) { + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = graphql.Null + } + }() + fc := &graphql.FieldContext{ + Object: "NotificationActor", + Field: field, + Args: nil, + IsMethod: false, + } + + ctx = graphql.WithFieldContext(ctx, fc) + resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { + ctx = rctx // use context from middleware stack in children + return obj.ID, nil + }) + if err != nil { + ec.Error(ctx, err) + return graphql.Null + } + if resTmp == nil { + if !graphql.HasFieldError(ctx, fc) { + ec.Errorf(ctx, "must not be null") + } + return graphql.Null + } + res := resTmp.(uuid.UUID) + fc.Result = res + return ec.marshalNUUID2githubᚗcomᚋgoogleᚋuuidᚐUUID(ctx, field.Selections, res) +} + +func (ec *executionContext) _NotificationActor_type(ctx context.Context, field graphql.CollectedField, obj *NotificationActor) (ret graphql.Marshaler) { + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = graphql.Null + } + }() + fc := &graphql.FieldContext{ + Object: "NotificationActor", + Field: field, + Args: nil, + IsMethod: false, + } + + ctx = graphql.WithFieldContext(ctx, fc) + resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { + ctx = rctx // use context from middleware stack in children + return obj.Type, nil + }) + if err != nil { + ec.Error(ctx, err) + return graphql.Null + } + if resTmp == nil { + if !graphql.HasFieldError(ctx, fc) { + ec.Errorf(ctx, "must not be null") + } + return graphql.Null + } + res := resTmp.(ActorType) + fc.Result = res + return ec.marshalNActorType2githubᚗcomᚋjordanknottᚋtaskcafeᚋinternalᚋgraphᚐActorType(ctx, field.Selections, res) +} + +func (ec *executionContext) _NotificationActor_name(ctx context.Context, field graphql.CollectedField, obj *NotificationActor) (ret graphql.Marshaler) { + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = graphql.Null + } + }() + fc := &graphql.FieldContext{ + Object: "NotificationActor", + Field: field, + Args: nil, + IsMethod: false, + } + + ctx = graphql.WithFieldContext(ctx, fc) + resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { + ctx = rctx // use context from middleware stack in children + return obj.Name, nil + }) + if err != nil { + ec.Error(ctx, err) + return graphql.Null + } + if resTmp == nil { + if !graphql.HasFieldError(ctx, fc) { + ec.Errorf(ctx, "must not be null") + } + return graphql.Null + } + res := resTmp.(string) + fc.Result = res + return ec.marshalNString2string(ctx, field.Selections, res) +} + +func (ec *executionContext) _NotificationEntity_id(ctx context.Context, field graphql.CollectedField, obj *NotificationEntity) (ret graphql.Marshaler) { + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = graphql.Null + } + }() + fc := &graphql.FieldContext{ + Object: "NotificationEntity", + Field: field, + Args: nil, + IsMethod: false, + } + + ctx = graphql.WithFieldContext(ctx, fc) + resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { + ctx = rctx // use context from middleware stack in children + return obj.ID, nil + }) + if err != nil { + ec.Error(ctx, err) + return graphql.Null + } + if resTmp == nil { + if !graphql.HasFieldError(ctx, fc) { + ec.Errorf(ctx, "must not be null") + } + return graphql.Null + } + res := resTmp.(uuid.UUID) + fc.Result = res + return ec.marshalNUUID2githubᚗcomᚋgoogleᚋuuidᚐUUID(ctx, field.Selections, res) +} + +func (ec *executionContext) _NotificationEntity_type(ctx context.Context, field graphql.CollectedField, obj *NotificationEntity) (ret graphql.Marshaler) { + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = graphql.Null + } + }() + fc := &graphql.FieldContext{ + Object: "NotificationEntity", + Field: field, + Args: nil, + IsMethod: false, + } + + ctx = graphql.WithFieldContext(ctx, fc) + resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { + ctx = rctx // use context from middleware stack in children + return obj.Type, nil + }) + if err != nil { + ec.Error(ctx, err) + return graphql.Null + } + if resTmp == nil { + if !graphql.HasFieldError(ctx, fc) { + ec.Errorf(ctx, "must not be null") + } + return graphql.Null + } + res := resTmp.(EntityType) + fc.Result = res + return ec.marshalNEntityType2githubᚗcomᚋjordanknottᚋtaskcafeᚋinternalᚋgraphᚐEntityType(ctx, field.Selections, res) +} + +func (ec *executionContext) _NotificationEntity_name(ctx context.Context, field graphql.CollectedField, obj *NotificationEntity) (ret graphql.Marshaler) { + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = graphql.Null + } + }() + fc := &graphql.FieldContext{ + Object: "NotificationEntity", + Field: field, + Args: nil, + IsMethod: false, + } + + ctx = graphql.WithFieldContext(ctx, fc) + resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { + ctx = rctx // use context from middleware stack in children + return obj.Name, nil + }) + if err != nil { + ec.Error(ctx, err) + return graphql.Null + } + if resTmp == nil { + if !graphql.HasFieldError(ctx, fc) { + ec.Errorf(ctx, "must not be null") + } + return graphql.Null + } + res := resTmp.(string) + fc.Result = res + return ec.marshalNString2string(ctx, field.Selections, res) +} + func (ec *executionContext) _Organization_id(ctx context.Context, field graphql.CollectedField, obj *db.Organization) (ret graphql.Marshaler) { defer func() { if r := recover(); r != nil { @@ -9711,6 +10279,40 @@ func (ec *executionContext) _Query_me(ctx context.Context, field graphql.Collect return ec.marshalNMePayload2ᚖgithubᚗcomᚋjordanknottᚋtaskcafeᚋinternalᚋgraphᚐMePayload(ctx, field.Selections, res) } +func (ec *executionContext) _Query_notifications(ctx context.Context, field graphql.CollectedField) (ret graphql.Marshaler) { + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = graphql.Null + } + }() + fc := &graphql.FieldContext{ + Object: "Query", + Field: field, + Args: nil, + IsMethod: true, + } + + ctx = graphql.WithFieldContext(ctx, fc) + resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { + ctx = rctx // use context from middleware stack in children + return ec.resolvers.Query().Notifications(rctx) + }) + if err != nil { + ec.Error(ctx, err) + return graphql.Null + } + if resTmp == nil { + if !graphql.HasFieldError(ctx, fc) { + ec.Errorf(ctx, "must not be null") + } + return graphql.Null + } + res := resTmp.([]db.Notification) + fc.Result = res + return ec.marshalNNotification2ᚕgithubᚗcomᚋjordanknottᚋtaskcafeᚋinternalᚋdbᚐNotificationᚄ(ctx, field.Selections, res) +} + func (ec *executionContext) _Query___type(ctx context.Context, field graphql.CollectedField) (ret graphql.Marshaler) { defer func() { if r := recover(); r != nil { @@ -15341,6 +15943,177 @@ func (ec *executionContext) _Mutation(ctx context.Context, sel ast.SelectionSet) return out } +var notificationImplementors = []string{"Notification"} + +func (ec *executionContext) _Notification(ctx context.Context, sel ast.SelectionSet, obj *db.Notification) graphql.Marshaler { + fields := graphql.CollectFields(ec.OperationContext, sel, notificationImplementors) + + out := graphql.NewFieldSet(fields) + var invalids uint32 + for i, field := range fields { + switch field.Name { + case "__typename": + out.Values[i] = graphql.MarshalString("Notification") + case "id": + field := field + out.Concurrently(i, func() (res graphql.Marshaler) { + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + } + }() + res = ec._Notification_id(ctx, field, obj) + if res == graphql.Null { + atomic.AddUint32(&invalids, 1) + } + return res + }) + case "entity": + field := field + out.Concurrently(i, func() (res graphql.Marshaler) { + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + } + }() + res = ec._Notification_entity(ctx, field, obj) + if res == graphql.Null { + atomic.AddUint32(&invalids, 1) + } + return res + }) + case "actionType": + field := field + out.Concurrently(i, func() (res graphql.Marshaler) { + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + } + }() + res = ec._Notification_actionType(ctx, field, obj) + if res == graphql.Null { + atomic.AddUint32(&invalids, 1) + } + return res + }) + case "actor": + field := field + out.Concurrently(i, func() (res graphql.Marshaler) { + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + } + }() + res = ec._Notification_actor(ctx, field, obj) + if res == graphql.Null { + atomic.AddUint32(&invalids, 1) + } + return res + }) + case "read": + out.Values[i] = ec._Notification_read(ctx, field, obj) + if out.Values[i] == graphql.Null { + atomic.AddUint32(&invalids, 1) + } + case "createdAt": + field := field + out.Concurrently(i, func() (res graphql.Marshaler) { + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + } + }() + res = ec._Notification_createdAt(ctx, field, obj) + if res == graphql.Null { + atomic.AddUint32(&invalids, 1) + } + return res + }) + default: + panic("unknown field " + strconv.Quote(field.Name)) + } + } + out.Dispatch() + if invalids > 0 { + return graphql.Null + } + return out +} + +var notificationActorImplementors = []string{"NotificationActor"} + +func (ec *executionContext) _NotificationActor(ctx context.Context, sel ast.SelectionSet, obj *NotificationActor) graphql.Marshaler { + fields := graphql.CollectFields(ec.OperationContext, sel, notificationActorImplementors) + + out := graphql.NewFieldSet(fields) + var invalids uint32 + for i, field := range fields { + switch field.Name { + case "__typename": + out.Values[i] = graphql.MarshalString("NotificationActor") + case "id": + out.Values[i] = ec._NotificationActor_id(ctx, field, obj) + if out.Values[i] == graphql.Null { + invalids++ + } + case "type": + out.Values[i] = ec._NotificationActor_type(ctx, field, obj) + if out.Values[i] == graphql.Null { + invalids++ + } + case "name": + out.Values[i] = ec._NotificationActor_name(ctx, field, obj) + if out.Values[i] == graphql.Null { + invalids++ + } + default: + panic("unknown field " + strconv.Quote(field.Name)) + } + } + out.Dispatch() + if invalids > 0 { + return graphql.Null + } + return out +} + +var notificationEntityImplementors = []string{"NotificationEntity"} + +func (ec *executionContext) _NotificationEntity(ctx context.Context, sel ast.SelectionSet, obj *NotificationEntity) graphql.Marshaler { + fields := graphql.CollectFields(ec.OperationContext, sel, notificationEntityImplementors) + + out := graphql.NewFieldSet(fields) + var invalids uint32 + for i, field := range fields { + switch field.Name { + case "__typename": + out.Values[i] = graphql.MarshalString("NotificationEntity") + case "id": + out.Values[i] = ec._NotificationEntity_id(ctx, field, obj) + if out.Values[i] == graphql.Null { + invalids++ + } + case "type": + out.Values[i] = ec._NotificationEntity_type(ctx, field, obj) + if out.Values[i] == graphql.Null { + invalids++ + } + case "name": + out.Values[i] = ec._NotificationEntity_name(ctx, field, obj) + if out.Values[i] == graphql.Null { + invalids++ + } + default: + panic("unknown field " + strconv.Quote(field.Name)) + } + } + out.Dispatch() + if invalids > 0 { + return graphql.Null + } + return out +} + var organizationImplementors = []string{"Organization"} func (ec *executionContext) _Organization(ctx context.Context, sel ast.SelectionSet, obj *db.Organization) graphql.Marshaler { @@ -15843,6 +16616,20 @@ func (ec *executionContext) _Query(ctx context.Context, sel ast.SelectionSet) gr } return res }) + case "notifications": + field := field + out.Concurrently(i, func() (res graphql.Marshaler) { + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + } + }() + res = ec._Query_notifications(ctx, field) + if res == graphql.Null { + atomic.AddUint32(&invalids, 1) + } + return res + }) case "__type": out.Values[i] = ec._Query___type(ctx, field) case "__schema": @@ -17107,6 +17894,24 @@ func (ec *executionContext) marshalNActionLevel2githubᚗcomᚋjordanknottᚋtas return v } +func (ec *executionContext) unmarshalNActionType2githubᚗcomᚋjordanknottᚋtaskcafeᚋinternalᚋgraphᚐActionType(ctx context.Context, v interface{}) (ActionType, error) { + var res ActionType + return res, res.UnmarshalGQL(v) +} + +func (ec *executionContext) marshalNActionType2githubᚗcomᚋjordanknottᚋtaskcafeᚋinternalᚋgraphᚐActionType(ctx context.Context, sel ast.SelectionSet, v ActionType) graphql.Marshaler { + return v +} + +func (ec *executionContext) unmarshalNActorType2githubᚗcomᚋjordanknottᚋtaskcafeᚋinternalᚋgraphᚐActorType(ctx context.Context, v interface{}) (ActorType, error) { + var res ActorType + return res, res.UnmarshalGQL(v) +} + +func (ec *executionContext) marshalNActorType2githubᚗcomᚋjordanknottᚋtaskcafeᚋinternalᚋgraphᚐActorType(ctx context.Context, sel ast.SelectionSet, v ActorType) graphql.Marshaler { + return v +} + func (ec *executionContext) unmarshalNBoolean2bool(ctx context.Context, v interface{}) (bool, error) { return graphql.UnmarshalBoolean(v) } @@ -17331,6 +18136,15 @@ func (ec *executionContext) marshalNDeleteUserAccountPayload2ᚖgithubᚗcomᚋj return ec._DeleteUserAccountPayload(ctx, sel, v) } +func (ec *executionContext) unmarshalNEntityType2githubᚗcomᚋjordanknottᚋtaskcafeᚋinternalᚋgraphᚐEntityType(ctx context.Context, v interface{}) (EntityType, error) { + var res EntityType + return res, res.UnmarshalGQL(v) +} + +func (ec *executionContext) marshalNEntityType2githubᚗcomᚋjordanknottᚋtaskcafeᚋinternalᚋgraphᚐEntityType(ctx context.Context, sel ast.SelectionSet, v EntityType) graphql.Marshaler { + return v +} + func (ec *executionContext) unmarshalNFindProject2githubᚗcomᚋjordanknottᚋtaskcafeᚋinternalᚋgraphᚐFindProject(ctx context.Context, v interface{}) (FindProject, error) { return ec.unmarshalInputFindProject(ctx, v) } @@ -17559,6 +18373,75 @@ func (ec *executionContext) unmarshalNNewUserAccount2githubᚗcomᚋjordanknott return ec.unmarshalInputNewUserAccount(ctx, v) } +func (ec *executionContext) marshalNNotification2githubᚗcomᚋjordanknottᚋtaskcafeᚋinternalᚋdbᚐNotification(ctx context.Context, sel ast.SelectionSet, v db.Notification) graphql.Marshaler { + return ec._Notification(ctx, sel, &v) +} + +func (ec *executionContext) marshalNNotification2ᚕgithubᚗcomᚋjordanknottᚋtaskcafeᚋinternalᚋdbᚐNotificationᚄ(ctx context.Context, sel ast.SelectionSet, v []db.Notification) graphql.Marshaler { + ret := make(graphql.Array, len(v)) + var wg sync.WaitGroup + isLen1 := len(v) == 1 + if !isLen1 { + wg.Add(len(v)) + } + for i := range v { + i := i + fc := &graphql.FieldContext{ + Index: &i, + Result: &v[i], + } + ctx := graphql.WithFieldContext(ctx, fc) + f := func(i int) { + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = nil + } + }() + if !isLen1 { + defer wg.Done() + } + ret[i] = ec.marshalNNotification2githubᚗcomᚋjordanknottᚋtaskcafeᚋinternalᚋdbᚐNotification(ctx, sel, v[i]) + } + if isLen1 { + f(i) + } else { + go f(i) + } + + } + wg.Wait() + return ret +} + +func (ec *executionContext) marshalNNotificationActor2githubᚗcomᚋjordanknottᚋtaskcafeᚋinternalᚋgraphᚐNotificationActor(ctx context.Context, sel ast.SelectionSet, v NotificationActor) graphql.Marshaler { + return ec._NotificationActor(ctx, sel, &v) +} + +func (ec *executionContext) marshalNNotificationActor2ᚖgithubᚗcomᚋjordanknottᚋtaskcafeᚋinternalᚋgraphᚐNotificationActor(ctx context.Context, sel ast.SelectionSet, v *NotificationActor) graphql.Marshaler { + if v == nil { + if !graphql.HasFieldError(ctx, graphql.GetFieldContext(ctx)) { + ec.Errorf(ctx, "must not be null") + } + return graphql.Null + } + return ec._NotificationActor(ctx, sel, v) +} + +func (ec *executionContext) marshalNNotificationEntity2githubᚗcomᚋjordanknottᚋtaskcafeᚋinternalᚋgraphᚐNotificationEntity(ctx context.Context, sel ast.SelectionSet, v NotificationEntity) graphql.Marshaler { + return ec._NotificationEntity(ctx, sel, &v) +} + +func (ec *executionContext) marshalNNotificationEntity2ᚖgithubᚗcomᚋjordanknottᚋtaskcafeᚋinternalᚋgraphᚐNotificationEntity(ctx context.Context, sel ast.SelectionSet, v *NotificationEntity) graphql.Marshaler { + if v == nil { + if !graphql.HasFieldError(ctx, graphql.GetFieldContext(ctx)) { + ec.Errorf(ctx, "must not be null") + } + return graphql.Null + } + return ec._NotificationEntity(ctx, sel, v) +} + func (ec *executionContext) unmarshalNObjectType2githubᚗcomᚋjordanknottᚋtaskcafeᚋinternalᚋgraphᚐObjectType(ctx context.Context, v interface{}) (ObjectType, error) { var res ObjectType return res, res.UnmarshalGQL(v) diff --git a/internal/graph/graph.go b/internal/graph/graph.go index c0e5839..f1a4909 100644 --- a/internal/graph/graph.go +++ b/internal/graph/graph.go @@ -41,19 +41,34 @@ func NewHandler(repo db.Repository) http.Handler { var subjectID uuid.UUID in := graphql.GetResolverContext(ctx).Args["input"] - if typeArg == ObjectTypeProject || typeArg == ObjectTypeTeam { - val := reflect.ValueOf(in) // could be any underlying type - fieldName := "ProjectID" - if typeArg == ObjectTypeTeam { - fieldName = "TeamID" - } - subjectID, ok = val.FieldByName(fieldName).Interface().(uuid.UUID) - if !ok { - return nil, errors.New("error while casting subject uuid") - } + val := reflect.ValueOf(in) // could be any underlying type + if val.Kind() == reflect.Ptr { + val = reflect.Indirect(val) + } + var fieldName string + switch typeArg { + case ObjectTypeTeam: + fieldName = "TeamID" + case ObjectTypeTask: + fieldName = "TaskID" + default: + fieldName = "ProjectID" + } + log.WithFields(log.Fields{"typeArg": typeArg, "fieldName": fieldName}).Info("getting field by name") + subjectID, ok = val.FieldByName(fieldName).Interface().(uuid.UUID) + if !ok { + return nil, errors.New("error while casting subject uuid") } + var err error if level == ActionLevelProject { + if typeArg == ObjectTypeTask { + log.WithFields(log.Fields{"subjectID": subjectID}).Info("fetching project ID using task ID") + subjectID, err = repo.GetProjectIDForTask(ctx, subjectID) + if err != nil { + return nil, err + } + } roles, err := GetProjectRoles(ctx, repo, subjectID) if err != nil { return nil, err @@ -151,3 +166,23 @@ func ConvertToRoleCode(r string) RoleCode { } return RoleCodeObserver } + +// GetEntityType converts integer to EntityType enum +func GetEntityType(entityType int32) EntityType { + switch entityType { + case 1: + return EntityTypeTask + default: + panic("Not a valid entity type!") + } +} + +// GetActionType converts integer to ActionType enum +func GetActionType(actionType int32) ActionType { + switch actionType { + case 1: + return ActionTypeTaskMemberAdded + default: + panic("Not a valid entity type!") + } +} diff --git a/internal/graph/models_gen.go b/internal/graph/models_gen.go index 974e237..5e12ef2 100644 --- a/internal/graph/models_gen.go +++ b/internal/graph/models_gen.go @@ -245,6 +245,18 @@ type NewUserAccount struct { RoleCode string `json:"roleCode"` } +type NotificationActor struct { + ID uuid.UUID `json:"id"` + Type ActorType `json:"type"` + Name string `json:"name"` +} + +type NotificationEntity struct { + ID uuid.UUID `json:"id"` + Type EntityType `json:"type"` + Name string `json:"name"` +} + type OwnedList struct { Teams []db.Team `json:"teams"` Projects []db.Project `json:"projects"` @@ -470,6 +482,123 @@ func (e ActionLevel) MarshalGQL(w io.Writer) { fmt.Fprint(w, strconv.Quote(e.String())) } +type ActionType string + +const ( + ActionTypeTaskMemberAdded ActionType = "TASK_MEMBER_ADDED" +) + +var AllActionType = []ActionType{ + ActionTypeTaskMemberAdded, +} + +func (e ActionType) IsValid() bool { + switch e { + case ActionTypeTaskMemberAdded: + return true + } + return false +} + +func (e ActionType) String() string { + return string(e) +} + +func (e *ActionType) UnmarshalGQL(v interface{}) error { + str, ok := v.(string) + if !ok { + return fmt.Errorf("enums must be strings") + } + + *e = ActionType(str) + if !e.IsValid() { + return fmt.Errorf("%s is not a valid ActionType", str) + } + return nil +} + +func (e ActionType) MarshalGQL(w io.Writer) { + fmt.Fprint(w, strconv.Quote(e.String())) +} + +type ActorType string + +const ( + ActorTypeUser ActorType = "USER" +) + +var AllActorType = []ActorType{ + ActorTypeUser, +} + +func (e ActorType) IsValid() bool { + switch e { + case ActorTypeUser: + return true + } + return false +} + +func (e ActorType) String() string { + return string(e) +} + +func (e *ActorType) UnmarshalGQL(v interface{}) error { + str, ok := v.(string) + if !ok { + return fmt.Errorf("enums must be strings") + } + + *e = ActorType(str) + if !e.IsValid() { + return fmt.Errorf("%s is not a valid ActorType", str) + } + return nil +} + +func (e ActorType) MarshalGQL(w io.Writer) { + fmt.Fprint(w, strconv.Quote(e.String())) +} + +type EntityType string + +const ( + EntityTypeTask EntityType = "TASK" +) + +var AllEntityType = []EntityType{ + EntityTypeTask, +} + +func (e EntityType) IsValid() bool { + switch e { + case EntityTypeTask: + return true + } + return false +} + +func (e EntityType) String() string { + return string(e) +} + +func (e *EntityType) UnmarshalGQL(v interface{}) error { + str, ok := v.(string) + if !ok { + return fmt.Errorf("enums must be strings") + } + + *e = EntityType(str) + if !e.IsValid() { + return fmt.Errorf("%s is not a valid EntityType", str) + } + return nil +} + +func (e EntityType) MarshalGQL(w io.Writer) { + fmt.Fprint(w, strconv.Quote(e.String())) +} + type ObjectType string const ( diff --git a/internal/graph/schema.graphqls b/internal/graph/schema.graphqls index 873f881..5047388 100644 --- a/internal/graph/schema.graphqls +++ b/internal/graph/schema.graphqls @@ -229,6 +229,43 @@ input FindTeam { teamID: UUID! } +extend type Query { + notifications: [Notification!]! +} + +enum EntityType { + TASK +} + +enum ActorType { + USER +} + +enum ActionType { + TASK_MEMBER_ADDED +} + +type NotificationActor { + id: UUID! + type: ActorType! + name: String! +} + +type NotificationEntity { + id: UUID! + type: EntityType! + name: String! +} + +type Notification { + id: ID! + entity: NotificationEntity! + actionType: ActionType! + actor: NotificationActor! + read: Boolean! + createdAt: Time! +} + extend type Mutation { createProject(input: NewProject!): Project! @hasRole(roles: [ADMIN], level: TEAM, type: TEAM) deleteProject(input: DeleteProject!): @@ -355,9 +392,9 @@ extend type Mutation { Task! @hasRole(roles: [ADMIN], level: PROJECT, type: PROJECT) assignTask(input: AssignTaskInput): - Task! @hasRole(roles: [ADMIN], level: PROJECT, type: PROJECT) + Task! @hasRole(roles: [ADMIN], level: PROJECT, type: TASK) unassignTask(input: UnassignTaskInput): - Task! @hasRole(roles: [ADMIN], level: PROJECT, type: PROJECT) + Task! @hasRole(roles: [ADMIN], level: PROJECT, type: TASK) } input NewTask { diff --git a/internal/graph/schema.resolvers.go b/internal/graph/schema.resolvers.go index ff3b5dc..cb2f2d3 100644 --- a/internal/graph/schema.resolvers.go +++ b/internal/graph/schema.resolvers.go @@ -269,6 +269,7 @@ func (r *mutationResolver) AssignTask(ctx context.Context, input *AssignTaskInpu if err != nil { return &db.Task{}, err } + // r.NotificationQueue.TaskMemberWasAdded(assignedTask.TaskID, userID, assignedTask.UserID) task, err := r.Repository.GetTaskByID(ctx, input.TaskID) return &task, err } @@ -720,6 +721,61 @@ func (r *mutationResolver) UpdateUserRole(ctx context.Context, input UpdateUserR return &UpdateUserRolePayload{User: &user}, nil } +func (r *notificationResolver) ID(ctx context.Context, obj *db.Notification) (uuid.UUID, error) { + return obj.NotificationID, nil +} + +func (r *notificationResolver) Entity(ctx context.Context, obj *db.Notification) (*NotificationEntity, error) { + log.WithFields(log.Fields{"notificationID": obj.NotificationID}).Info("fetching entity for notification") + entity, err := r.Repository.GetEntityForNotificationID(ctx, obj.NotificationID) + log.WithFields(log.Fields{"entityID": entity.EntityID}).Info("fetched entity") + if err != nil { + return &NotificationEntity{}, err + } + entityType := GetEntityType(entity.EntityType) + switch entityType { + case EntityTypeTask: + task, err := r.Repository.GetTaskByID(ctx, entity.EntityID) + if err != nil { + return &NotificationEntity{}, err + } + return &NotificationEntity{Type: entityType, ID: entity.EntityID, Name: task.Name}, err + + default: + panic(fmt.Errorf("not implemented")) + } +} + +func (r *notificationResolver) ActionType(ctx context.Context, obj *db.Notification) (ActionType, error) { + entity, err := r.Repository.GetEntityForNotificationID(ctx, obj.NotificationID) + if err != nil { + return ActionTypeTaskMemberAdded, err + } + actionType := GetActionType(entity.ActionType) + return actionType, nil +} + +func (r *notificationResolver) Actor(ctx context.Context, obj *db.Notification) (*NotificationActor, error) { + entity, err := r.Repository.GetEntityForNotificationID(ctx, obj.NotificationID) + if err != nil { + return &NotificationActor{}, err + } + log.WithFields(log.Fields{"entityID": entity.ActorID}).Info("fetching actor") + user, err := r.Repository.GetUserAccountByID(ctx, entity.ActorID) + if err != nil { + return &NotificationActor{}, err + } + return &NotificationActor{ID: entity.ActorID, Name: user.FullName, Type: ActorTypeUser}, nil +} + +func (r *notificationResolver) CreatedAt(ctx context.Context, obj *db.Notification) (*time.Time, error) { + entity, err := r.Repository.GetEntityForNotificationID(ctx, obj.NotificationID) + if err != nil { + return &time.Time{}, err + } + return &entity.CreatedOn, nil +} + func (r *organizationResolver) ID(ctx context.Context, obj *db.Organization) (uuid.UUID, error) { return obj.OrganizationID, nil } @@ -1011,6 +1067,21 @@ func (r *queryResolver) Me(ctx context.Context) (*MePayload, error) { return &MePayload{User: &user, TeamRoles: teamRoles, ProjectRoles: projectRoles}, err } +func (r *queryResolver) Notifications(ctx context.Context) ([]db.Notification, error) { + userID, ok := GetUserID(ctx) + log.WithFields(log.Fields{"userID": userID}).Info("fetching notifications") + if !ok { + return []db.Notification{}, errors.New("user id is missing") + } + notifications, err := r.Repository.GetAllNotificationsForUserID(ctx, userID) + if err == sql.ErrNoRows { + return []db.Notification{}, nil + } else if err != nil { + return []db.Notification{}, err + } + return notifications, nil +} + func (r *refreshTokenResolver) ID(ctx context.Context, obj *db.RefreshToken) (uuid.UUID, error) { return obj.TokenID, nil } @@ -1268,6 +1339,9 @@ func (r *Resolver) LabelColor() LabelColorResolver { return &labelColorResolver{ // Mutation returns MutationResolver implementation. func (r *Resolver) Mutation() MutationResolver { return &mutationResolver{r} } +// Notification returns NotificationResolver implementation. +func (r *Resolver) Notification() NotificationResolver { return ¬ificationResolver{r} } + // Organization returns OrganizationResolver implementation. func (r *Resolver) Organization() OrganizationResolver { return &organizationResolver{r} } @@ -1308,6 +1382,7 @@ func (r *Resolver) UserAccount() UserAccountResolver { return &userAccountResolv type labelColorResolver struct{ *Resolver } type mutationResolver struct{ *Resolver } +type notificationResolver struct{ *Resolver } type organizationResolver struct{ *Resolver } type projectResolver struct{ *Resolver } type projectLabelResolver struct{ *Resolver } diff --git a/internal/graph/schema/notification.gql b/internal/graph/schema/notification.gql new file mode 100644 index 0000000..3cdc748 --- /dev/null +++ b/internal/graph/schema/notification.gql @@ -0,0 +1,36 @@ +extend type Query { + notifications: [Notification!]! +} + +enum EntityType { + TASK +} + +enum ActorType { + USER +} + +enum ActionType { + TASK_MEMBER_ADDED +} + +type NotificationActor { + id: UUID! + type: ActorType! + name: String! +} + +type NotificationEntity { + id: UUID! + type: EntityType! + name: String! +} + +type Notification { + id: ID! + entity: NotificationEntity! + actionType: ActionType! + actor: NotificationActor! + read: Boolean! + createdAt: Time! +} diff --git a/internal/graph/schema/task.gql b/internal/graph/schema/task.gql index 3e7e99b..0f63b8b 100644 --- a/internal/graph/schema/task.gql +++ b/internal/graph/schema/task.gql @@ -16,9 +16,9 @@ extend type Mutation { Task! @hasRole(roles: [ADMIN], level: PROJECT, type: PROJECT) assignTask(input: AssignTaskInput): - Task! @hasRole(roles: [ADMIN], level: PROJECT, type: PROJECT) + Task! @hasRole(roles: [ADMIN], level: PROJECT, type: TASK) unassignTask(input: UnassignTaskInput): - Task! @hasRole(roles: [ADMIN], level: PROJECT, type: PROJECT) + Task! @hasRole(roles: [ADMIN], level: PROJECT, type: TASK) } input NewTask { diff --git a/internal/notification/logger.go b/internal/notification/logger.go new file mode 100644 index 0000000..25b8c6a --- /dev/null +++ b/internal/notification/logger.go @@ -0,0 +1,53 @@ +package notification + +import ( + log "github.com/sirupsen/logrus" +) + +// MachineryLogger is a customer logger for machinery worker +type MachineryLogger struct{} + +// Print sends to logrus.Info +func (m *MachineryLogger) Print(args ...interface{}) { + log.Info(args...) +} + +// Printf sends to logrus.Infof +func (m *MachineryLogger) Printf(format string, args ...interface{}) { + log.Infof(format, args...) +} + +// Println sends to logrus.Info +func (m *MachineryLogger) Println(args ...interface{}) { + log.Info(args...) +} + +// Fatal sends to logrus.Fatal +func (m *MachineryLogger) Fatal(args ...interface{}) { + log.Fatal(args...) +} + +// Fatalf sends to logrus.Fatalf +func (m *MachineryLogger) Fatalf(format string, args ...interface{}) { + log.Fatalf(format, args...) +} + +// Fatalln sends to logrus.Fatal +func (m *MachineryLogger) Fatalln(args ...interface{}) { + log.Fatal(args...) +} + +// Panic sends to logrus.Panic +func (m *MachineryLogger) Panic(args ...interface{}) { + log.Panic(args...) +} + +// Panicf sends to logrus.Panic +func (m *MachineryLogger) Panicf(format string, args ...interface{}) { + log.Panic(args...) +} + +// Panicln sends to logrus.Panic +func (m *MachineryLogger) Panicln(args ...interface{}) { + log.Panic(args...) +} diff --git a/internal/notification/notification.go b/internal/notification/notification.go new file mode 100644 index 0000000..497ab15 --- /dev/null +++ b/internal/notification/notification.go @@ -0,0 +1,78 @@ +package notification + +import ( + "context" + "time" + + "github.com/RichardKnop/machinery/v1" + "github.com/RichardKnop/machinery/v1/tasks" + + "github.com/google/uuid" + "github.com/jordanknott/taskcafe/internal/db" + log "github.com/sirupsen/logrus" +) + +func RegisterTasks(server *machinery.Server, repo db.Repository) { + tasks := NotificationTasks{repo} + server.RegisterTasks(map[string]interface{}{ + "taskMemberWasAdded": tasks.TaskMemberWasAdded, + }) +} + +type NotificationTasks struct { + Repository db.Repository +} + +func (m *NotificationTasks) TaskMemberWasAdded(taskID, notifierID, notifiedID string) (bool, error) { + tid := uuid.MustParse(taskID) + notifier := uuid.MustParse(notifierID) + notified := uuid.MustParse(notifiedID) + if notifier == notified { + return true, nil + } + ctx := context.Background() + now := time.Now().UTC() + notificationObject, err := m.Repository.CreateNotificationObject(ctx, db.CreateNotificationObjectParams{EntityType: 1, EntityID: tid, ActionType: 1, ActorID: notifier, CreatedOn: now}) + if err != nil { + return false, err + } + notification, err := m.Repository.CreateNotification(ctx, db.CreateNotificationParams{NotificationObjectID: notificationObject.NotificationObjectID, NotifierID: notified}) + if err != nil { + return false, err + } + log.WithFields(log.Fields{"notificationID": notification.NotificationID}).Info("created new notification") + return true, nil +} + +type NotificationQueue struct { + Server *machinery.Server +} + +func (n *NotificationQueue) TaskMemberWasAdded(taskID, notifier, notified uuid.UUID) error { + task := tasks.Signature{ + Name: "taskMemberWasAdded", + Args: []tasks.Arg{ + { + Name: "taskID", + Type: "string", + Value: taskID.String(), + }, + { + Name: "notifierID", + Type: "string", + Value: notifier.String(), + }, + { + Name: "notifiedID", + Type: "string", + Value: notified.String(), + }, + }, + } + + _, err := n.Server.SendTask(&task) + if err != nil { + return err + } + return nil +} diff --git a/migrations/0051_add-notification-tables.up.sql b/migrations/0051_add-notification-tables.up.sql new file mode 100644 index 0000000..04dd4a3 --- /dev/null +++ b/migrations/0051_add-notification-tables.up.sql @@ -0,0 +1,15 @@ +CREATE TABLE notification_object ( + notification_object_id uuid PRIMARY KEY DEFAULT uuid_generate_v4(), + entity_id uuid NOT NULL, + action_type int NOT NULL, + actor_id uuid NOT NULL, + entity_type int NOT NULL, + created_on timestamptz NOT NULL +); + +CREATE TABLE notification ( + notification_id uuid PRIMARY KEY DEFAULT uuid_generate_v4(), + notification_object_id uuid REFERENCES notification_object(notification_object_id) ON DELETE CASCADE, + notifier_id uuid NOT NULL REFERENCES user_account(user_id) ON DELETE CASCADE, + read boolean NOT NULL DEFAULT false +);