feat: redesign project invite popup
This commit is contained in:
parent
737d2b640f
commit
262f9cbdda
@ -16,7 +16,7 @@ import {
|
|||||||
} from 'react-router-dom';
|
} from 'react-router-dom';
|
||||||
import {
|
import {
|
||||||
useUpdateProjectMemberRoleMutation,
|
useUpdateProjectMemberRoleMutation,
|
||||||
useInviteProjectMemberMutation,
|
useInviteProjectMembersMutation,
|
||||||
useDeleteProjectMemberMutation,
|
useDeleteProjectMemberMutation,
|
||||||
useToggleTaskLabelMutation,
|
useToggleTaskLabelMutation,
|
||||||
useUpdateProjectNameMutation,
|
useUpdateProjectNameMutation,
|
||||||
@ -84,9 +84,10 @@ type InviteUserData = {
|
|||||||
suerID?: string;
|
suerID?: string;
|
||||||
};
|
};
|
||||||
type UserManagementPopupProps = {
|
type UserManagementPopupProps = {
|
||||||
|
projectID: string;
|
||||||
users: Array<User>;
|
users: Array<User>;
|
||||||
projectMembers: Array<TaskUser>;
|
projectMembers: Array<TaskUser>;
|
||||||
onInviteProjectMember: (data: InviteUserData) => void;
|
onInviteProjectMembers: (data: Array<InviteUserData>) => void;
|
||||||
};
|
};
|
||||||
|
|
||||||
const VisibiltyPrivateIcon = styled(Lock)`
|
const VisibiltyPrivateIcon = styled(Lock)`
|
||||||
@ -131,14 +132,14 @@ type MemberFilterOptions = {
|
|||||||
organization?: boolean;
|
organization?: boolean;
|
||||||
};
|
};
|
||||||
|
|
||||||
const fetchMembers = async (client: any, options: MemberFilterOptions, input: string, cb: any) => {
|
const fetchMembers = async (client: any, projectID: string, options: MemberFilterOptions, input: string, cb: any) => {
|
||||||
if (input && input.trim().length < 3) {
|
if (input && input.trim().length < 3) {
|
||||||
return [];
|
return [];
|
||||||
}
|
}
|
||||||
const res = await client.query({
|
const res = await client.query({
|
||||||
query: gql`
|
query: gql`
|
||||||
query {
|
query {
|
||||||
searchMembers(input: {SearchFilter:"${input}"}) {
|
searchMembers(input: {SearchFilter:"${input}", projectID:"${projectID}"}) {
|
||||||
similarity
|
similarity
|
||||||
confirmed
|
confirmed
|
||||||
joined
|
joined
|
||||||
@ -165,7 +166,7 @@ const fetchMembers = async (client: any, options: MemberFilterOptions, input: st
|
|||||||
emails.push(m.user.email);
|
emails.push(m.user.email);
|
||||||
return {
|
return {
|
||||||
label: m.user.fullName,
|
label: m.user.fullName,
|
||||||
value: { id: m.id, type: 0, profileIcon: m.user.profileIcon },
|
value: { id: m.user.id, type: 0, profileIcon: m.user.profileIcon },
|
||||||
};
|
};
|
||||||
}),
|
}),
|
||||||
];
|
];
|
||||||
@ -214,6 +215,7 @@ const OptionContent = styled.div`
|
|||||||
`;
|
`;
|
||||||
|
|
||||||
const UserOption: React.FC<UserOptionProps> = ({ isDisabled, isFocused, innerProps, label, data }) => {
|
const UserOption: React.FC<UserOptionProps> = ({ isDisabled, isFocused, innerProps, label, data }) => {
|
||||||
|
console.log(data);
|
||||||
return !isDisabled ? (
|
return !isDisabled ? (
|
||||||
<OptionWrapper {...innerProps} isFocused={isFocused}>
|
<OptionWrapper {...innerProps} isFocused={isFocused}>
|
||||||
<TaskAssignee
|
<TaskAssignee
|
||||||
@ -280,16 +282,24 @@ const InviteContainer = styled.div`
|
|||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
`;
|
`;
|
||||||
|
|
||||||
const UserManagementPopup: React.FC<UserManagementPopupProps> = ({ users, projectMembers, onInviteProjectMember }) => {
|
const UserManagementPopup: React.FC<UserManagementPopupProps> = ({
|
||||||
|
projectID,
|
||||||
|
users,
|
||||||
|
projectMembers,
|
||||||
|
onInviteProjectMembers,
|
||||||
|
}) => {
|
||||||
const client = useApolloClient();
|
const client = useApolloClient();
|
||||||
const [invitedUsers, setInvitedUsers] = useState<Array<any> | null>(null);
|
const [invitedUsers, setInvitedUsers] = useState<Array<any> | null>(null);
|
||||||
return (
|
return (
|
||||||
<Popup tab={0} title="Invite a user">
|
<Popup tab={0} title="Invite a user">
|
||||||
<InviteContainer>
|
<InviteContainer>
|
||||||
<AsyncSelect
|
<AsyncSelect
|
||||||
|
getOptionValue={option => option.value.id}
|
||||||
placeholder="Email address or username"
|
placeholder="Email address or username"
|
||||||
noOptionsMessage={() => null}
|
noOptionsMessage={() => null}
|
||||||
onChange={(e: any) => setInvitedUsers(e ? e.value : null)}
|
onChange={(e: any) => {
|
||||||
|
setInvitedUsers(e);
|
||||||
|
}}
|
||||||
isMulti
|
isMulti
|
||||||
autoFocus
|
autoFocus
|
||||||
cacheOptions
|
cacheOptions
|
||||||
@ -301,13 +311,25 @@ const UserManagementPopup: React.FC<UserManagementPopupProps> = ({ users, projec
|
|||||||
IndicatorSeparator: null,
|
IndicatorSeparator: null,
|
||||||
DropdownIndicator: null,
|
DropdownIndicator: null,
|
||||||
}}
|
}}
|
||||||
loadOptions={(i, cb) => fetchMembers(client, {}, i, cb)}
|
loadOptions={(i, cb) => fetchMembers(client, projectID, {}, i, cb)}
|
||||||
/>
|
/>
|
||||||
</InviteContainer>
|
</InviteContainer>
|
||||||
<InviteButton
|
<InviteButton
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
// FUCK, gotta rewrite invite member to be MULTIPLE. SHIT!
|
if (invitedUsers) {
|
||||||
// onInviteProjectMember();
|
onInviteProjectMembers(
|
||||||
|
invitedUsers.map(user => {
|
||||||
|
if (user.value.type === 0) {
|
||||||
|
return {
|
||||||
|
userID: user.value.id,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
return {
|
||||||
|
email: user.value.id,
|
||||||
|
};
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
}
|
||||||
}}
|
}}
|
||||||
disabled={invitedUsers === null}
|
disabled={invitedUsers === null}
|
||||||
hoverVariant="none"
|
hoverVariant="none"
|
||||||
@ -398,14 +420,17 @@ const Project = () => {
|
|||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
const [inviteProjectMember] = useInviteProjectMemberMutation({
|
const [inviteProjectMembers] = useInviteProjectMembersMutation({
|
||||||
update: (client, response) => {
|
update: (client, response) => {
|
||||||
updateApolloCache<FindProjectQuery>(
|
updateApolloCache<FindProjectQuery>(
|
||||||
client,
|
client,
|
||||||
FindProjectDocument,
|
FindProjectDocument,
|
||||||
cache =>
|
cache =>
|
||||||
produce(cache, draftCache => {
|
produce(cache, draftCache => {
|
||||||
draftCache.findProject.members.push({ ...response.data.inviteProjectMember.member });
|
draftCache.findProject.members = [
|
||||||
|
...cache.findProject.members,
|
||||||
|
...response.data.inviteProjectMembers.members,
|
||||||
|
];
|
||||||
}),
|
}),
|
||||||
{ projectID },
|
{ projectID },
|
||||||
);
|
);
|
||||||
@ -472,8 +497,10 @@ const Project = () => {
|
|||||||
showPopup(
|
showPopup(
|
||||||
$target,
|
$target,
|
||||||
<UserManagementPopup
|
<UserManagementPopup
|
||||||
onInviteProjectMember={userID => {
|
projectID={projectID}
|
||||||
// /inviteProjectMember({ variables: { userID, projectID } });
|
onInviteProjectMembers={members => {
|
||||||
|
inviteProjectMembers({ variables: { projectID, members } });
|
||||||
|
hidePopup();
|
||||||
}}
|
}}
|
||||||
users={data.users}
|
users={data.users}
|
||||||
projectMembers={data.findProject.members}
|
projectMembers={data.findProject.members}
|
||||||
|
@ -289,7 +289,7 @@ export type Mutation = {
|
|||||||
deleteTeamMember: DeleteTeamMemberPayload;
|
deleteTeamMember: DeleteTeamMemberPayload;
|
||||||
deleteUserAccount: DeleteUserAccountPayload;
|
deleteUserAccount: DeleteUserAccountPayload;
|
||||||
duplicateTaskGroup: DuplicateTaskGroupPayload;
|
duplicateTaskGroup: DuplicateTaskGroupPayload;
|
||||||
inviteProjectMember: InviteProjectMemberPayload;
|
inviteProjectMembers: InviteProjectMembersPayload;
|
||||||
logoutUser: Scalars['Boolean'];
|
logoutUser: Scalars['Boolean'];
|
||||||
removeTaskLabel: Task;
|
removeTaskLabel: Task;
|
||||||
setTaskChecklistItemComplete: TaskChecklistItem;
|
setTaskChecklistItemComplete: TaskChecklistItem;
|
||||||
@ -439,8 +439,8 @@ export type MutationDuplicateTaskGroupArgs = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
export type MutationInviteProjectMemberArgs = {
|
export type MutationInviteProjectMembersArgs = {
|
||||||
input: InviteProjectMember;
|
input: InviteProjectMembers;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
@ -694,16 +694,21 @@ export type UpdateProjectLabelColor = {
|
|||||||
labelColorID: Scalars['UUID'];
|
labelColorID: Scalars['UUID'];
|
||||||
};
|
};
|
||||||
|
|
||||||
export type InviteProjectMember = {
|
export type MemberInvite = {
|
||||||
projectID: Scalars['UUID'];
|
|
||||||
userID?: Maybe<Scalars['UUID']>;
|
userID?: Maybe<Scalars['UUID']>;
|
||||||
email?: Maybe<Scalars['String']>;
|
email?: Maybe<Scalars['String']>;
|
||||||
};
|
};
|
||||||
|
|
||||||
export type InviteProjectMemberPayload = {
|
export type InviteProjectMembers = {
|
||||||
__typename?: 'InviteProjectMemberPayload';
|
projectID: Scalars['UUID'];
|
||||||
|
members: Array<MemberInvite>;
|
||||||
|
};
|
||||||
|
|
||||||
|
export type InviteProjectMembersPayload = {
|
||||||
|
__typename?: 'InviteProjectMembersPayload';
|
||||||
ok: Scalars['Boolean'];
|
ok: Scalars['Boolean'];
|
||||||
member: Member;
|
projectID: Scalars['UUID'];
|
||||||
|
members: Array<Member>;
|
||||||
};
|
};
|
||||||
|
|
||||||
export type DeleteProjectMember = {
|
export type DeleteProjectMember = {
|
||||||
@ -1446,19 +1451,18 @@ export type DeleteProjectMemberMutation = (
|
|||||||
) }
|
) }
|
||||||
);
|
);
|
||||||
|
|
||||||
export type InviteProjectMemberMutationVariables = {
|
export type InviteProjectMembersMutationVariables = {
|
||||||
projectID: Scalars['UUID'];
|
projectID: Scalars['UUID'];
|
||||||
userID?: Maybe<Scalars['UUID']>;
|
members: Array<MemberInvite>;
|
||||||
email?: Maybe<Scalars['String']>;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
export type InviteProjectMemberMutation = (
|
export type InviteProjectMembersMutation = (
|
||||||
{ __typename?: 'Mutation' }
|
{ __typename?: 'Mutation' }
|
||||||
& { inviteProjectMember: (
|
& { inviteProjectMembers: (
|
||||||
{ __typename?: 'InviteProjectMemberPayload' }
|
{ __typename?: 'InviteProjectMembersPayload' }
|
||||||
& Pick<InviteProjectMemberPayload, 'ok'>
|
& Pick<InviteProjectMembersPayload, 'ok'>
|
||||||
& { member: (
|
& { members: Array<(
|
||||||
{ __typename?: 'Member' }
|
{ __typename?: 'Member' }
|
||||||
& Pick<Member, 'id' | 'fullName' | 'username'>
|
& Pick<Member, 'id' | 'fullName' | 'username'>
|
||||||
& { profileIcon: (
|
& { profileIcon: (
|
||||||
@ -1468,7 +1472,7 @@ export type InviteProjectMemberMutation = (
|
|||||||
{ __typename?: 'Role' }
|
{ __typename?: 'Role' }
|
||||||
& Pick<Role, 'code' | 'name'>
|
& Pick<Role, 'code' | 'name'>
|
||||||
) }
|
) }
|
||||||
) }
|
)> }
|
||||||
) }
|
) }
|
||||||
);
|
);
|
||||||
|
|
||||||
@ -2978,11 +2982,11 @@ export function useDeleteProjectMemberMutation(baseOptions?: ApolloReactHooks.Mu
|
|||||||
export type DeleteProjectMemberMutationHookResult = ReturnType<typeof useDeleteProjectMemberMutation>;
|
export type DeleteProjectMemberMutationHookResult = ReturnType<typeof useDeleteProjectMemberMutation>;
|
||||||
export type DeleteProjectMemberMutationResult = ApolloReactCommon.MutationResult<DeleteProjectMemberMutation>;
|
export type DeleteProjectMemberMutationResult = ApolloReactCommon.MutationResult<DeleteProjectMemberMutation>;
|
||||||
export type DeleteProjectMemberMutationOptions = ApolloReactCommon.BaseMutationOptions<DeleteProjectMemberMutation, DeleteProjectMemberMutationVariables>;
|
export type DeleteProjectMemberMutationOptions = ApolloReactCommon.BaseMutationOptions<DeleteProjectMemberMutation, DeleteProjectMemberMutationVariables>;
|
||||||
export const InviteProjectMemberDocument = gql`
|
export const InviteProjectMembersDocument = gql`
|
||||||
mutation inviteProjectMember($projectID: UUID!, $userID: UUID, $email: String) {
|
mutation inviteProjectMembers($projectID: UUID!, $members: [MemberInvite!]!) {
|
||||||
inviteProjectMember(input: {projectID: $projectID, userID: $userID, email: $email}) {
|
inviteProjectMembers(input: {projectID: $projectID, members: $members}) {
|
||||||
ok
|
ok
|
||||||
member {
|
members {
|
||||||
id
|
id
|
||||||
fullName
|
fullName
|
||||||
profileIcon {
|
profileIcon {
|
||||||
@ -2999,33 +3003,32 @@ export const InviteProjectMemberDocument = gql`
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
`;
|
`;
|
||||||
export type InviteProjectMemberMutationFn = ApolloReactCommon.MutationFunction<InviteProjectMemberMutation, InviteProjectMemberMutationVariables>;
|
export type InviteProjectMembersMutationFn = ApolloReactCommon.MutationFunction<InviteProjectMembersMutation, InviteProjectMembersMutationVariables>;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* __useInviteProjectMemberMutation__
|
* __useInviteProjectMembersMutation__
|
||||||
*
|
*
|
||||||
* To run a mutation, you first call `useInviteProjectMemberMutation` within a React component and pass it any options that fit your needs.
|
* To run a mutation, you first call `useInviteProjectMembersMutation` within a React component and pass it any options that fit your needs.
|
||||||
* When your component renders, `useInviteProjectMemberMutation` returns a tuple that includes:
|
* When your component renders, `useInviteProjectMembersMutation` returns a tuple that includes:
|
||||||
* - A mutate function that you can call at any time to execute the mutation
|
* - A mutate function that you can call at any time to execute the mutation
|
||||||
* - An object with fields that represent the current status of the mutation's execution
|
* - An object with fields that represent the current status of the mutation's execution
|
||||||
*
|
*
|
||||||
* @param baseOptions options that will be passed into the mutation, supported options are listed on: https://www.apollographql.com/docs/react/api/react-hooks/#options-2;
|
* @param baseOptions options that will be passed into the mutation, supported options are listed on: https://www.apollographql.com/docs/react/api/react-hooks/#options-2;
|
||||||
*
|
*
|
||||||
* @example
|
* @example
|
||||||
* const [inviteProjectMemberMutation, { data, loading, error }] = useInviteProjectMemberMutation({
|
* const [inviteProjectMembersMutation, { data, loading, error }] = useInviteProjectMembersMutation({
|
||||||
* variables: {
|
* variables: {
|
||||||
* projectID: // value for 'projectID'
|
* projectID: // value for 'projectID'
|
||||||
* userID: // value for 'userID'
|
* members: // value for 'members'
|
||||||
* email: // value for 'email'
|
|
||||||
* },
|
* },
|
||||||
* });
|
* });
|
||||||
*/
|
*/
|
||||||
export function useInviteProjectMemberMutation(baseOptions?: ApolloReactHooks.MutationHookOptions<InviteProjectMemberMutation, InviteProjectMemberMutationVariables>) {
|
export function useInviteProjectMembersMutation(baseOptions?: ApolloReactHooks.MutationHookOptions<InviteProjectMembersMutation, InviteProjectMembersMutationVariables>) {
|
||||||
return ApolloReactHooks.useMutation<InviteProjectMemberMutation, InviteProjectMemberMutationVariables>(InviteProjectMemberDocument, baseOptions);
|
return ApolloReactHooks.useMutation<InviteProjectMembersMutation, InviteProjectMembersMutationVariables>(InviteProjectMembersDocument, baseOptions);
|
||||||
}
|
}
|
||||||
export type InviteProjectMemberMutationHookResult = ReturnType<typeof useInviteProjectMemberMutation>;
|
export type InviteProjectMembersMutationHookResult = ReturnType<typeof useInviteProjectMembersMutation>;
|
||||||
export type InviteProjectMemberMutationResult = ApolloReactCommon.MutationResult<InviteProjectMemberMutation>;
|
export type InviteProjectMembersMutationResult = ApolloReactCommon.MutationResult<InviteProjectMembersMutation>;
|
||||||
export type InviteProjectMemberMutationOptions = ApolloReactCommon.BaseMutationOptions<InviteProjectMemberMutation, InviteProjectMemberMutationVariables>;
|
export type InviteProjectMembersMutationOptions = ApolloReactCommon.BaseMutationOptions<InviteProjectMembersMutation, InviteProjectMembersMutationVariables>;
|
||||||
export const UpdateProjectMemberRoleDocument = gql`
|
export const UpdateProjectMemberRoleDocument = gql`
|
||||||
mutation updateProjectMemberRole($projectID: UUID!, $userID: UUID!, $roleCode: RoleCode!) {
|
mutation updateProjectMemberRole($projectID: UUID!, $userID: UUID!, $roleCode: RoleCode!) {
|
||||||
updateProjectMemberRole(input: {projectID: $projectID, userID: $userID, roleCode: $roleCode}) {
|
updateProjectMemberRole(input: {projectID: $projectID, userID: $userID, roleCode: $roleCode}) {
|
||||||
|
@ -1,25 +0,0 @@
|
|||||||
import gql from 'graphql-tag';
|
|
||||||
|
|
||||||
export const INVITE_PROJECT_MEMBER_MUTATION = gql`
|
|
||||||
mutation inviteProjectMember($projectID: UUID!, $userID: UUID, $email: String) {
|
|
||||||
inviteProjectMember(input: { projectID: $projectID, userID: $userID, email: $email }) {
|
|
||||||
ok
|
|
||||||
member {
|
|
||||||
id
|
|
||||||
fullName
|
|
||||||
profileIcon {
|
|
||||||
url
|
|
||||||
initials
|
|
||||||
bgColor
|
|
||||||
}
|
|
||||||
username
|
|
||||||
role {
|
|
||||||
code
|
|
||||||
name
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
`;
|
|
||||||
|
|
||||||
export default INVITE_PROJECT_MEMBER_MUTATION;
|
|
25
frontend/src/shared/graphql/project/inviteProjectMembers.ts
Normal file
25
frontend/src/shared/graphql/project/inviteProjectMembers.ts
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
import gql from 'graphql-tag';
|
||||||
|
|
||||||
|
export const INVITE_PROJECT_MEMBERS_MUTATION = gql`
|
||||||
|
mutation inviteProjectMembers($projectID: UUID!, $members: [MemberInvite!]!) {
|
||||||
|
inviteProjectMembers(input: { projectID: $projectID, members: $members }) {
|
||||||
|
ok
|
||||||
|
members {
|
||||||
|
id
|
||||||
|
fullName
|
||||||
|
profileIcon {
|
||||||
|
url
|
||||||
|
initials
|
||||||
|
bgColor
|
||||||
|
}
|
||||||
|
username
|
||||||
|
role {
|
||||||
|
code
|
||||||
|
name
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
|
||||||
|
export default INVITE_PROJECT_MEMBERS_MUTATION;
|
@ -78,8 +78,11 @@ func newWebCmd() *cobra.Command {
|
|||||||
return http.ListenAndServe(viper.GetString("server.hostname"), r)
|
return http.ListenAndServe(viper.GetString("server.hostname"), r)
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
cc.Flags().Bool("migrate", false, "if true, auto run's schema migrations before starting the web server")
|
cc.Flags().Bool("migrate", false, "if true, auto run's schema migrations before starting the web server")
|
||||||
|
|
||||||
viper.BindPFlag("migrate", cc.Flags().Lookup("migrate"))
|
viper.BindPFlag("migrate", cc.Flags().Lookup("migrate"))
|
||||||
|
|
||||||
viper.SetDefault("migrate", false)
|
viper.SetDefault("migrate", false)
|
||||||
return cc
|
return cc
|
||||||
}
|
}
|
||||||
|
@ -61,7 +61,7 @@ type Querier interface {
|
|||||||
GetEntityIDForNotificationID(ctx context.Context, notificationID uuid.UUID) (uuid.UUID, error)
|
GetEntityIDForNotificationID(ctx context.Context, notificationID uuid.UUID) (uuid.UUID, error)
|
||||||
GetLabelColorByID(ctx context.Context, labelColorID uuid.UUID) (LabelColor, error)
|
GetLabelColorByID(ctx context.Context, labelColorID uuid.UUID) (LabelColor, error)
|
||||||
GetLabelColors(ctx context.Context) ([]LabelColor, error)
|
GetLabelColors(ctx context.Context) ([]LabelColor, error)
|
||||||
GetMemberData(ctx context.Context) ([]GetMemberDataRow, error)
|
GetMemberData(ctx context.Context, projectID uuid.UUID) ([]UserAccount, error)
|
||||||
GetMemberProjectIDsForUserID(ctx context.Context, userID uuid.UUID) ([]uuid.UUID, error)
|
GetMemberProjectIDsForUserID(ctx context.Context, userID uuid.UUID) ([]uuid.UUID, error)
|
||||||
GetMemberTeamIDsForUserID(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)
|
GetNotificationForNotificationID(ctx context.Context, notificationID uuid.UUID) (GetNotificationForNotificationIDRow, error)
|
||||||
|
@ -16,7 +16,9 @@ UPDATE user_account SET profile_avatar_url = $2 WHERE user_id = $1
|
|||||||
RETURNING *;
|
RETURNING *;
|
||||||
|
|
||||||
-- name: GetMemberData :many
|
-- name: GetMemberData :many
|
||||||
SELECT username, full_name, email, user_id FROM user_account;
|
SELECT * FROM user_account
|
||||||
|
WHERE username != 'system'
|
||||||
|
AND user_id NOT IN (SELECT user_id FROM project_member WHERE project_id = $1);
|
||||||
|
|
||||||
-- name: UpdateUserAccountInfo :one
|
-- name: UpdateUserAccountInfo :one
|
||||||
UPDATE user_account SET bio = $2, full_name = $3, initials = $4, email = $5
|
UPDATE user_account SET bio = $2, full_name = $3, initials = $4, email = $5
|
||||||
|
@ -102,30 +102,32 @@ func (q *Queries) GetAllUserAccounts(ctx context.Context) ([]UserAccount, error)
|
|||||||
}
|
}
|
||||||
|
|
||||||
const getMemberData = `-- name: GetMemberData :many
|
const getMemberData = `-- name: GetMemberData :many
|
||||||
SELECT username, full_name, email, user_id FROM user_account
|
SELECT user_id, created_at, email, username, password_hash, profile_bg_color, full_name, initials, profile_avatar_url, role_code, bio FROM user_account
|
||||||
|
WHERE username != 'system'
|
||||||
|
AND user_id NOT IN (SELECT user_id FROM project_member WHERE project_id = $1)
|
||||||
`
|
`
|
||||||
|
|
||||||
type GetMemberDataRow struct {
|
func (q *Queries) GetMemberData(ctx context.Context, projectID uuid.UUID) ([]UserAccount, error) {
|
||||||
Username string `json:"username"`
|
rows, err := q.db.QueryContext(ctx, getMemberData, projectID)
|
||||||
FullName string `json:"full_name"`
|
|
||||||
Email string `json:"email"`
|
|
||||||
UserID uuid.UUID `json:"user_id"`
|
|
||||||
}
|
|
||||||
|
|
||||||
func (q *Queries) GetMemberData(ctx context.Context) ([]GetMemberDataRow, error) {
|
|
||||||
rows, err := q.db.QueryContext(ctx, getMemberData)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
defer rows.Close()
|
defer rows.Close()
|
||||||
var items []GetMemberDataRow
|
var items []UserAccount
|
||||||
for rows.Next() {
|
for rows.Next() {
|
||||||
var i GetMemberDataRow
|
var i UserAccount
|
||||||
if err := rows.Scan(
|
if err := rows.Scan(
|
||||||
&i.Username,
|
|
||||||
&i.FullName,
|
|
||||||
&i.Email,
|
|
||||||
&i.UserID,
|
&i.UserID,
|
||||||
|
&i.CreatedAt,
|
||||||
|
&i.Email,
|
||||||
|
&i.Username,
|
||||||
|
&i.PasswordHash,
|
||||||
|
&i.ProfileBgColor,
|
||||||
|
&i.FullName,
|
||||||
|
&i.Initials,
|
||||||
|
&i.ProfileAvatarUrl,
|
||||||
|
&i.RoleCode,
|
||||||
|
&i.Bio,
|
||||||
); err != nil {
|
); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
@ -127,9 +127,10 @@ type ComplexityRoot struct {
|
|||||||
TaskGroup func(childComplexity int) int
|
TaskGroup func(childComplexity int) int
|
||||||
}
|
}
|
||||||
|
|
||||||
InviteProjectMemberPayload struct {
|
InviteProjectMembersPayload struct {
|
||||||
Member func(childComplexity int) int
|
Members func(childComplexity int) int
|
||||||
Ok func(childComplexity int) int
|
Ok func(childComplexity int) int
|
||||||
|
ProjectID func(childComplexity int) int
|
||||||
}
|
}
|
||||||
|
|
||||||
LabelColor struct {
|
LabelColor struct {
|
||||||
@ -194,7 +195,7 @@ type ComplexityRoot struct {
|
|||||||
DeleteTeamMember func(childComplexity int, input DeleteTeamMember) int
|
DeleteTeamMember func(childComplexity int, input DeleteTeamMember) int
|
||||||
DeleteUserAccount func(childComplexity int, input DeleteUserAccount) int
|
DeleteUserAccount func(childComplexity int, input DeleteUserAccount) int
|
||||||
DuplicateTaskGroup func(childComplexity int, input DuplicateTaskGroup) int
|
DuplicateTaskGroup func(childComplexity int, input DuplicateTaskGroup) int
|
||||||
InviteProjectMember func(childComplexity int, input InviteProjectMember) int
|
InviteProjectMembers func(childComplexity int, input InviteProjectMembers) int
|
||||||
LogoutUser func(childComplexity int, input LogoutUser) int
|
LogoutUser func(childComplexity int, input LogoutUser) int
|
||||||
RemoveTaskLabel func(childComplexity int, input *RemoveTaskLabelInput) int
|
RemoveTaskLabel func(childComplexity int, input *RemoveTaskLabelInput) int
|
||||||
SetTaskChecklistItemComplete func(childComplexity int, input SetTaskChecklistItemComplete) int
|
SetTaskChecklistItemComplete func(childComplexity int, input SetTaskChecklistItemComplete) int
|
||||||
@ -454,7 +455,7 @@ type MutationResolver interface {
|
|||||||
UpdateProjectLabel(ctx context.Context, input UpdateProjectLabel) (*db.ProjectLabel, error)
|
UpdateProjectLabel(ctx context.Context, input UpdateProjectLabel) (*db.ProjectLabel, error)
|
||||||
UpdateProjectLabelName(ctx context.Context, input UpdateProjectLabelName) (*db.ProjectLabel, error)
|
UpdateProjectLabelName(ctx context.Context, input UpdateProjectLabelName) (*db.ProjectLabel, error)
|
||||||
UpdateProjectLabelColor(ctx context.Context, input UpdateProjectLabelColor) (*db.ProjectLabel, error)
|
UpdateProjectLabelColor(ctx context.Context, input UpdateProjectLabelColor) (*db.ProjectLabel, error)
|
||||||
InviteProjectMember(ctx context.Context, input InviteProjectMember) (*InviteProjectMemberPayload, error)
|
InviteProjectMembers(ctx context.Context, input InviteProjectMembers) (*InviteProjectMembersPayload, error)
|
||||||
DeleteProjectMember(ctx context.Context, input DeleteProjectMember) (*DeleteProjectMemberPayload, error)
|
DeleteProjectMember(ctx context.Context, input DeleteProjectMember) (*DeleteProjectMemberPayload, error)
|
||||||
UpdateProjectMemberRole(ctx context.Context, input UpdateProjectMemberRole) (*UpdateProjectMemberRolePayload, error)
|
UpdateProjectMemberRole(ctx context.Context, input UpdateProjectMemberRole) (*UpdateProjectMemberRolePayload, error)
|
||||||
CreateTask(ctx context.Context, input NewTask) (*db.Task, error)
|
CreateTask(ctx context.Context, input NewTask) (*db.Task, error)
|
||||||
@ -801,19 +802,26 @@ func (e *executableSchema) Complexity(typeName, field string, childComplexity in
|
|||||||
|
|
||||||
return e.complexity.DuplicateTaskGroupPayload.TaskGroup(childComplexity), true
|
return e.complexity.DuplicateTaskGroupPayload.TaskGroup(childComplexity), true
|
||||||
|
|
||||||
case "InviteProjectMemberPayload.member":
|
case "InviteProjectMembersPayload.members":
|
||||||
if e.complexity.InviteProjectMemberPayload.Member == nil {
|
if e.complexity.InviteProjectMembersPayload.Members == nil {
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
|
|
||||||
return e.complexity.InviteProjectMemberPayload.Member(childComplexity), true
|
return e.complexity.InviteProjectMembersPayload.Members(childComplexity), true
|
||||||
|
|
||||||
case "InviteProjectMemberPayload.ok":
|
case "InviteProjectMembersPayload.ok":
|
||||||
if e.complexity.InviteProjectMemberPayload.Ok == nil {
|
if e.complexity.InviteProjectMembersPayload.Ok == nil {
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
|
|
||||||
return e.complexity.InviteProjectMemberPayload.Ok(childComplexity), true
|
return e.complexity.InviteProjectMembersPayload.Ok(childComplexity), true
|
||||||
|
|
||||||
|
case "InviteProjectMembersPayload.projectID":
|
||||||
|
if e.complexity.InviteProjectMembersPayload.ProjectID == nil {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
return e.complexity.InviteProjectMembersPayload.ProjectID(childComplexity), true
|
||||||
|
|
||||||
case "LabelColor.colorHex":
|
case "LabelColor.colorHex":
|
||||||
if e.complexity.LabelColor.ColorHex == nil {
|
if e.complexity.LabelColor.ColorHex == nil {
|
||||||
@ -1257,17 +1265,17 @@ func (e *executableSchema) Complexity(typeName, field string, childComplexity in
|
|||||||
|
|
||||||
return e.complexity.Mutation.DuplicateTaskGroup(childComplexity, args["input"].(DuplicateTaskGroup)), true
|
return e.complexity.Mutation.DuplicateTaskGroup(childComplexity, args["input"].(DuplicateTaskGroup)), true
|
||||||
|
|
||||||
case "Mutation.inviteProjectMember":
|
case "Mutation.inviteProjectMembers":
|
||||||
if e.complexity.Mutation.InviteProjectMember == nil {
|
if e.complexity.Mutation.InviteProjectMembers == nil {
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
|
|
||||||
args, err := ec.field_Mutation_inviteProjectMember_args(context.TODO(), rawArgs)
|
args, err := ec.field_Mutation_inviteProjectMembers_args(context.TODO(), rawArgs)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return 0, false
|
return 0, false
|
||||||
}
|
}
|
||||||
|
|
||||||
return e.complexity.Mutation.InviteProjectMember(childComplexity, args["input"].(InviteProjectMember)), true
|
return e.complexity.Mutation.InviteProjectMembers(childComplexity, args["input"].(InviteProjectMembers)), true
|
||||||
|
|
||||||
case "Mutation.logoutUser":
|
case "Mutation.logoutUser":
|
||||||
if e.complexity.Mutation.LogoutUser == nil {
|
if e.complexity.Mutation.LogoutUser == nil {
|
||||||
@ -2869,24 +2877,28 @@ input UpdateProjectLabelColor {
|
|||||||
}
|
}
|
||||||
|
|
||||||
extend type Mutation {
|
extend type Mutation {
|
||||||
# TODO: rename to inviteProjectMember
|
inviteProjectMembers(input: InviteProjectMembers!):
|
||||||
inviteProjectMember(input: InviteProjectMember!):
|
InviteProjectMembersPayload! @hasRole(roles: [ADMIN], level: PROJECT, type: PROJECT)
|
||||||
InviteProjectMemberPayload! @hasRole(roles: [ADMIN], level: PROJECT, type: PROJECT)
|
|
||||||
deleteProjectMember(input: DeleteProjectMember!):
|
deleteProjectMember(input: DeleteProjectMember!):
|
||||||
DeleteProjectMemberPayload! @hasRole(roles: [ADMIN], level: PROJECT, type: PROJECT)
|
DeleteProjectMemberPayload! @hasRole(roles: [ADMIN], level: PROJECT, type: PROJECT)
|
||||||
updateProjectMemberRole(input: UpdateProjectMemberRole!):
|
updateProjectMemberRole(input: UpdateProjectMemberRole!):
|
||||||
UpdateProjectMemberRolePayload! @hasRole(roles: [ADMIN], level: PROJECT, type: PROJECT)
|
UpdateProjectMemberRolePayload! @hasRole(roles: [ADMIN], level: PROJECT, type: PROJECT)
|
||||||
}
|
}
|
||||||
|
|
||||||
input InviteProjectMember {
|
input MemberInvite {
|
||||||
projectID: UUID!
|
|
||||||
userID: UUID
|
userID: UUID
|
||||||
email: String
|
email: String
|
||||||
}
|
}
|
||||||
|
|
||||||
type InviteProjectMemberPayload {
|
input InviteProjectMembers {
|
||||||
|
projectID: UUID!
|
||||||
|
members: [MemberInvite!]!
|
||||||
|
}
|
||||||
|
|
||||||
|
type InviteProjectMembersPayload {
|
||||||
ok: Boolean!
|
ok: Boolean!
|
||||||
member: Member!
|
projectID: UUID!
|
||||||
|
members: [Member!]!
|
||||||
}
|
}
|
||||||
|
|
||||||
input DeleteProjectMember {
|
input DeleteProjectMember {
|
||||||
@ -3715,12 +3727,12 @@ func (ec *executionContext) field_Mutation_duplicateTaskGroup_args(ctx context.C
|
|||||||
return args, nil
|
return args, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (ec *executionContext) field_Mutation_inviteProjectMember_args(ctx context.Context, rawArgs map[string]interface{}) (map[string]interface{}, error) {
|
func (ec *executionContext) field_Mutation_inviteProjectMembers_args(ctx context.Context, rawArgs map[string]interface{}) (map[string]interface{}, error) {
|
||||||
var err error
|
var err error
|
||||||
args := map[string]interface{}{}
|
args := map[string]interface{}{}
|
||||||
var arg0 InviteProjectMember
|
var arg0 InviteProjectMembers
|
||||||
if tmp, ok := rawArgs["input"]; ok {
|
if tmp, ok := rawArgs["input"]; ok {
|
||||||
arg0, err = ec.unmarshalNInviteProjectMember2githubᚗcomᚋjordanknottᚋtaskcafeᚋinternalᚋgraphᚐInviteProjectMember(ctx, tmp)
|
arg0, err = ec.unmarshalNInviteProjectMembers2githubᚗcomᚋjordanknottᚋtaskcafeᚋinternalᚋgraphᚐInviteProjectMembers(ctx, tmp)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@ -5179,7 +5191,7 @@ func (ec *executionContext) _DuplicateTaskGroupPayload_taskGroup(ctx context.Con
|
|||||||
return ec.marshalNTaskGroup2ᚖgithubᚗcomᚋjordanknottᚋtaskcafeᚋinternalᚋdbᚐTaskGroup(ctx, field.Selections, res)
|
return ec.marshalNTaskGroup2ᚖgithubᚗcomᚋjordanknottᚋtaskcafeᚋinternalᚋdbᚐTaskGroup(ctx, field.Selections, res)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (ec *executionContext) _InviteProjectMemberPayload_ok(ctx context.Context, field graphql.CollectedField, obj *InviteProjectMemberPayload) (ret graphql.Marshaler) {
|
func (ec *executionContext) _InviteProjectMembersPayload_ok(ctx context.Context, field graphql.CollectedField, obj *InviteProjectMembersPayload) (ret graphql.Marshaler) {
|
||||||
defer func() {
|
defer func() {
|
||||||
if r := recover(); r != nil {
|
if r := recover(); r != nil {
|
||||||
ec.Error(ctx, ec.Recover(ctx, r))
|
ec.Error(ctx, ec.Recover(ctx, r))
|
||||||
@ -5187,7 +5199,7 @@ func (ec *executionContext) _InviteProjectMemberPayload_ok(ctx context.Context,
|
|||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
fc := &graphql.FieldContext{
|
fc := &graphql.FieldContext{
|
||||||
Object: "InviteProjectMemberPayload",
|
Object: "InviteProjectMembersPayload",
|
||||||
Field: field,
|
Field: field,
|
||||||
Args: nil,
|
Args: nil,
|
||||||
IsMethod: false,
|
IsMethod: false,
|
||||||
@ -5213,7 +5225,7 @@ func (ec *executionContext) _InviteProjectMemberPayload_ok(ctx context.Context,
|
|||||||
return ec.marshalNBoolean2bool(ctx, field.Selections, res)
|
return ec.marshalNBoolean2bool(ctx, field.Selections, res)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (ec *executionContext) _InviteProjectMemberPayload_member(ctx context.Context, field graphql.CollectedField, obj *InviteProjectMemberPayload) (ret graphql.Marshaler) {
|
func (ec *executionContext) _InviteProjectMembersPayload_projectID(ctx context.Context, field graphql.CollectedField, obj *InviteProjectMembersPayload) (ret graphql.Marshaler) {
|
||||||
defer func() {
|
defer func() {
|
||||||
if r := recover(); r != nil {
|
if r := recover(); r != nil {
|
||||||
ec.Error(ctx, ec.Recover(ctx, r))
|
ec.Error(ctx, ec.Recover(ctx, r))
|
||||||
@ -5221,7 +5233,7 @@ func (ec *executionContext) _InviteProjectMemberPayload_member(ctx context.Conte
|
|||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
fc := &graphql.FieldContext{
|
fc := &graphql.FieldContext{
|
||||||
Object: "InviteProjectMemberPayload",
|
Object: "InviteProjectMembersPayload",
|
||||||
Field: field,
|
Field: field,
|
||||||
Args: nil,
|
Args: nil,
|
||||||
IsMethod: false,
|
IsMethod: false,
|
||||||
@ -5230,7 +5242,7 @@ func (ec *executionContext) _InviteProjectMemberPayload_member(ctx context.Conte
|
|||||||
ctx = graphql.WithFieldContext(ctx, fc)
|
ctx = graphql.WithFieldContext(ctx, fc)
|
||||||
resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) {
|
resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) {
|
||||||
ctx = rctx // use context from middleware stack in children
|
ctx = rctx // use context from middleware stack in children
|
||||||
return obj.Member, nil
|
return obj.ProjectID, nil
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
ec.Error(ctx, err)
|
ec.Error(ctx, err)
|
||||||
@ -5242,9 +5254,43 @@ func (ec *executionContext) _InviteProjectMemberPayload_member(ctx context.Conte
|
|||||||
}
|
}
|
||||||
return graphql.Null
|
return graphql.Null
|
||||||
}
|
}
|
||||||
res := resTmp.(*Member)
|
res := resTmp.(uuid.UUID)
|
||||||
fc.Result = res
|
fc.Result = res
|
||||||
return ec.marshalNMember2ᚖgithubᚗcomᚋjordanknottᚋtaskcafeᚋinternalᚋgraphᚐMember(ctx, field.Selections, res)
|
return ec.marshalNUUID2githubᚗcomᚋgoogleᚋuuidᚐUUID(ctx, field.Selections, res)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ec *executionContext) _InviteProjectMembersPayload_members(ctx context.Context, field graphql.CollectedField, obj *InviteProjectMembersPayload) (ret graphql.Marshaler) {
|
||||||
|
defer func() {
|
||||||
|
if r := recover(); r != nil {
|
||||||
|
ec.Error(ctx, ec.Recover(ctx, r))
|
||||||
|
ret = graphql.Null
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
fc := &graphql.FieldContext{
|
||||||
|
Object: "InviteProjectMembersPayload",
|
||||||
|
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.Members, 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.([]Member)
|
||||||
|
fc.Result = res
|
||||||
|
return ec.marshalNMember2ᚕgithubᚗcomᚋjordanknottᚋtaskcafeᚋinternalᚋgraphᚐMemberᚄ(ctx, field.Selections, res)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (ec *executionContext) _LabelColor_id(ctx context.Context, field graphql.CollectedField, obj *db.LabelColor) (ret graphql.Marshaler) {
|
func (ec *executionContext) _LabelColor_id(ctx context.Context, field graphql.CollectedField, obj *db.LabelColor) (ret graphql.Marshaler) {
|
||||||
@ -6545,7 +6591,7 @@ func (ec *executionContext) _Mutation_updateProjectLabelColor(ctx context.Contex
|
|||||||
return ec.marshalNProjectLabel2ᚖgithubᚗcomᚋjordanknottᚋtaskcafeᚋinternalᚋdbᚐProjectLabel(ctx, field.Selections, res)
|
return ec.marshalNProjectLabel2ᚖgithubᚗcomᚋjordanknottᚋtaskcafeᚋinternalᚋdbᚐProjectLabel(ctx, field.Selections, res)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (ec *executionContext) _Mutation_inviteProjectMember(ctx context.Context, field graphql.CollectedField) (ret graphql.Marshaler) {
|
func (ec *executionContext) _Mutation_inviteProjectMembers(ctx context.Context, field graphql.CollectedField) (ret graphql.Marshaler) {
|
||||||
defer func() {
|
defer func() {
|
||||||
if r := recover(); r != nil {
|
if r := recover(); r != nil {
|
||||||
ec.Error(ctx, ec.Recover(ctx, r))
|
ec.Error(ctx, ec.Recover(ctx, r))
|
||||||
@ -6561,7 +6607,7 @@ func (ec *executionContext) _Mutation_inviteProjectMember(ctx context.Context, f
|
|||||||
|
|
||||||
ctx = graphql.WithFieldContext(ctx, fc)
|
ctx = graphql.WithFieldContext(ctx, fc)
|
||||||
rawArgs := field.ArgumentMap(ec.Variables)
|
rawArgs := field.ArgumentMap(ec.Variables)
|
||||||
args, err := ec.field_Mutation_inviteProjectMember_args(ctx, rawArgs)
|
args, err := ec.field_Mutation_inviteProjectMembers_args(ctx, rawArgs)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
ec.Error(ctx, err)
|
ec.Error(ctx, err)
|
||||||
return graphql.Null
|
return graphql.Null
|
||||||
@ -6570,7 +6616,7 @@ func (ec *executionContext) _Mutation_inviteProjectMember(ctx context.Context, f
|
|||||||
resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) {
|
resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) {
|
||||||
directive0 := func(rctx context.Context) (interface{}, error) {
|
directive0 := func(rctx context.Context) (interface{}, error) {
|
||||||
ctx = rctx // use context from middleware stack in children
|
ctx = rctx // use context from middleware stack in children
|
||||||
return ec.resolvers.Mutation().InviteProjectMember(rctx, args["input"].(InviteProjectMember))
|
return ec.resolvers.Mutation().InviteProjectMembers(rctx, args["input"].(InviteProjectMembers))
|
||||||
}
|
}
|
||||||
directive1 := func(ctx context.Context) (interface{}, error) {
|
directive1 := func(ctx context.Context) (interface{}, error) {
|
||||||
roles, err := ec.unmarshalNRoleLevel2ᚕgithubᚗcomᚋjordanknottᚋtaskcafeᚋinternalᚋgraphᚐRoleLevelᚄ(ctx, []interface{}{"ADMIN"})
|
roles, err := ec.unmarshalNRoleLevel2ᚕgithubᚗcomᚋjordanknottᚋtaskcafeᚋinternalᚋgraphᚐRoleLevelᚄ(ctx, []interface{}{"ADMIN"})
|
||||||
@ -6598,10 +6644,10 @@ func (ec *executionContext) _Mutation_inviteProjectMember(ctx context.Context, f
|
|||||||
if tmp == nil {
|
if tmp == nil {
|
||||||
return nil, nil
|
return nil, nil
|
||||||
}
|
}
|
||||||
if data, ok := tmp.(*InviteProjectMemberPayload); ok {
|
if data, ok := tmp.(*InviteProjectMembersPayload); ok {
|
||||||
return data, nil
|
return data, nil
|
||||||
}
|
}
|
||||||
return nil, fmt.Errorf(`unexpected type %T from directive, should be *github.com/jordanknott/taskcafe/internal/graph.InviteProjectMemberPayload`, tmp)
|
return nil, fmt.Errorf(`unexpected type %T from directive, should be *github.com/jordanknott/taskcafe/internal/graph.InviteProjectMembersPayload`, tmp)
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
ec.Error(ctx, err)
|
ec.Error(ctx, err)
|
||||||
@ -6613,9 +6659,9 @@ func (ec *executionContext) _Mutation_inviteProjectMember(ctx context.Context, f
|
|||||||
}
|
}
|
||||||
return graphql.Null
|
return graphql.Null
|
||||||
}
|
}
|
||||||
res := resTmp.(*InviteProjectMemberPayload)
|
res := resTmp.(*InviteProjectMembersPayload)
|
||||||
fc.Result = res
|
fc.Result = res
|
||||||
return ec.marshalNInviteProjectMemberPayload2ᚖgithubᚗcomᚋjordanknottᚋtaskcafeᚋinternalᚋgraphᚐInviteProjectMemberPayload(ctx, field.Selections, res)
|
return ec.marshalNInviteProjectMembersPayload2ᚖgithubᚗcomᚋjordanknottᚋtaskcafeᚋinternalᚋgraphᚐInviteProjectMembersPayload(ctx, field.Selections, res)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (ec *executionContext) _Mutation_deleteProjectMember(ctx context.Context, field graphql.CollectedField) (ret graphql.Marshaler) {
|
func (ec *executionContext) _Mutation_deleteProjectMember(ctx context.Context, field graphql.CollectedField) (ret graphql.Marshaler) {
|
||||||
@ -15403,8 +15449,8 @@ func (ec *executionContext) unmarshalInputFindUser(ctx context.Context, obj inte
|
|||||||
return it, nil
|
return it, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (ec *executionContext) unmarshalInputInviteProjectMember(ctx context.Context, obj interface{}) (InviteProjectMember, error) {
|
func (ec *executionContext) unmarshalInputInviteProjectMembers(ctx context.Context, obj interface{}) (InviteProjectMembers, error) {
|
||||||
var it InviteProjectMember
|
var it InviteProjectMembers
|
||||||
var asMap = obj.(map[string]interface{})
|
var asMap = obj.(map[string]interface{})
|
||||||
|
|
||||||
for k, v := range asMap {
|
for k, v := range asMap {
|
||||||
@ -15415,15 +15461,9 @@ func (ec *executionContext) unmarshalInputInviteProjectMember(ctx context.Contex
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return it, err
|
return it, err
|
||||||
}
|
}
|
||||||
case "userID":
|
case "members":
|
||||||
var err error
|
var err error
|
||||||
it.UserID, err = ec.unmarshalOUUID2ᚖgithubᚗcomᚋgoogleᚋuuidᚐUUID(ctx, v)
|
it.Members, err = ec.unmarshalNMemberInvite2ᚕgithubᚗcomᚋjordanknottᚋtaskcafeᚋinternalᚋgraphᚐMemberInviteᚄ(ctx, v)
|
||||||
if err != nil {
|
|
||||||
return it, err
|
|
||||||
}
|
|
||||||
case "email":
|
|
||||||
var err error
|
|
||||||
it.Email, err = ec.unmarshalOString2ᚖstring(ctx, v)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return it, err
|
return it, err
|
||||||
}
|
}
|
||||||
@ -15451,6 +15491,30 @@ func (ec *executionContext) unmarshalInputLogoutUser(ctx context.Context, obj in
|
|||||||
return it, nil
|
return it, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (ec *executionContext) unmarshalInputMemberInvite(ctx context.Context, obj interface{}) (MemberInvite, error) {
|
||||||
|
var it MemberInvite
|
||||||
|
var asMap = obj.(map[string]interface{})
|
||||||
|
|
||||||
|
for k, v := range asMap {
|
||||||
|
switch k {
|
||||||
|
case "userID":
|
||||||
|
var err error
|
||||||
|
it.UserID, err = ec.unmarshalOUUID2ᚖgithubᚗcomᚋgoogleᚋuuidᚐUUID(ctx, v)
|
||||||
|
if err != nil {
|
||||||
|
return it, err
|
||||||
|
}
|
||||||
|
case "email":
|
||||||
|
var err error
|
||||||
|
it.Email, err = ec.unmarshalOString2ᚖstring(ctx, v)
|
||||||
|
if err != nil {
|
||||||
|
return it, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return it, nil
|
||||||
|
}
|
||||||
|
|
||||||
func (ec *executionContext) unmarshalInputMemberSearchFilter(ctx context.Context, obj interface{}) (MemberSearchFilter, error) {
|
func (ec *executionContext) unmarshalInputMemberSearchFilter(ctx context.Context, obj interface{}) (MemberSearchFilter, error) {
|
||||||
var it MemberSearchFilter
|
var it MemberSearchFilter
|
||||||
var asMap = obj.(map[string]interface{})
|
var asMap = obj.(map[string]interface{})
|
||||||
@ -16797,24 +16861,29 @@ func (ec *executionContext) _DuplicateTaskGroupPayload(ctx context.Context, sel
|
|||||||
return out
|
return out
|
||||||
}
|
}
|
||||||
|
|
||||||
var inviteProjectMemberPayloadImplementors = []string{"InviteProjectMemberPayload"}
|
var inviteProjectMembersPayloadImplementors = []string{"InviteProjectMembersPayload"}
|
||||||
|
|
||||||
func (ec *executionContext) _InviteProjectMemberPayload(ctx context.Context, sel ast.SelectionSet, obj *InviteProjectMemberPayload) graphql.Marshaler {
|
func (ec *executionContext) _InviteProjectMembersPayload(ctx context.Context, sel ast.SelectionSet, obj *InviteProjectMembersPayload) graphql.Marshaler {
|
||||||
fields := graphql.CollectFields(ec.OperationContext, sel, inviteProjectMemberPayloadImplementors)
|
fields := graphql.CollectFields(ec.OperationContext, sel, inviteProjectMembersPayloadImplementors)
|
||||||
|
|
||||||
out := graphql.NewFieldSet(fields)
|
out := graphql.NewFieldSet(fields)
|
||||||
var invalids uint32
|
var invalids uint32
|
||||||
for i, field := range fields {
|
for i, field := range fields {
|
||||||
switch field.Name {
|
switch field.Name {
|
||||||
case "__typename":
|
case "__typename":
|
||||||
out.Values[i] = graphql.MarshalString("InviteProjectMemberPayload")
|
out.Values[i] = graphql.MarshalString("InviteProjectMembersPayload")
|
||||||
case "ok":
|
case "ok":
|
||||||
out.Values[i] = ec._InviteProjectMemberPayload_ok(ctx, field, obj)
|
out.Values[i] = ec._InviteProjectMembersPayload_ok(ctx, field, obj)
|
||||||
if out.Values[i] == graphql.Null {
|
if out.Values[i] == graphql.Null {
|
||||||
invalids++
|
invalids++
|
||||||
}
|
}
|
||||||
case "member":
|
case "projectID":
|
||||||
out.Values[i] = ec._InviteProjectMemberPayload_member(ctx, field, obj)
|
out.Values[i] = ec._InviteProjectMembersPayload_projectID(ctx, field, obj)
|
||||||
|
if out.Values[i] == graphql.Null {
|
||||||
|
invalids++
|
||||||
|
}
|
||||||
|
case "members":
|
||||||
|
out.Values[i] = ec._InviteProjectMembersPayload_members(ctx, field, obj)
|
||||||
if out.Values[i] == graphql.Null {
|
if out.Values[i] == graphql.Null {
|
||||||
invalids++
|
invalids++
|
||||||
}
|
}
|
||||||
@ -17108,8 +17177,8 @@ func (ec *executionContext) _Mutation(ctx context.Context, sel ast.SelectionSet)
|
|||||||
if out.Values[i] == graphql.Null {
|
if out.Values[i] == graphql.Null {
|
||||||
invalids++
|
invalids++
|
||||||
}
|
}
|
||||||
case "inviteProjectMember":
|
case "inviteProjectMembers":
|
||||||
out.Values[i] = ec._Mutation_inviteProjectMember(ctx, field)
|
out.Values[i] = ec._Mutation_inviteProjectMembers(ctx, field)
|
||||||
if out.Values[i] == graphql.Null {
|
if out.Values[i] == graphql.Null {
|
||||||
invalids++
|
invalids++
|
||||||
}
|
}
|
||||||
@ -19692,22 +19761,22 @@ func (ec *executionContext) marshalNInt2int(ctx context.Context, sel ast.Selecti
|
|||||||
return res
|
return res
|
||||||
}
|
}
|
||||||
|
|
||||||
func (ec *executionContext) unmarshalNInviteProjectMember2githubᚗcomᚋjordanknottᚋtaskcafeᚋinternalᚋgraphᚐInviteProjectMember(ctx context.Context, v interface{}) (InviteProjectMember, error) {
|
func (ec *executionContext) unmarshalNInviteProjectMembers2githubᚗcomᚋjordanknottᚋtaskcafeᚋinternalᚋgraphᚐInviteProjectMembers(ctx context.Context, v interface{}) (InviteProjectMembers, error) {
|
||||||
return ec.unmarshalInputInviteProjectMember(ctx, v)
|
return ec.unmarshalInputInviteProjectMembers(ctx, v)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (ec *executionContext) marshalNInviteProjectMemberPayload2githubᚗcomᚋjordanknottᚋtaskcafeᚋinternalᚋgraphᚐInviteProjectMemberPayload(ctx context.Context, sel ast.SelectionSet, v InviteProjectMemberPayload) graphql.Marshaler {
|
func (ec *executionContext) marshalNInviteProjectMembersPayload2githubᚗcomᚋjordanknottᚋtaskcafeᚋinternalᚋgraphᚐInviteProjectMembersPayload(ctx context.Context, sel ast.SelectionSet, v InviteProjectMembersPayload) graphql.Marshaler {
|
||||||
return ec._InviteProjectMemberPayload(ctx, sel, &v)
|
return ec._InviteProjectMembersPayload(ctx, sel, &v)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (ec *executionContext) marshalNInviteProjectMemberPayload2ᚖgithubᚗcomᚋjordanknottᚋtaskcafeᚋinternalᚋgraphᚐInviteProjectMemberPayload(ctx context.Context, sel ast.SelectionSet, v *InviteProjectMemberPayload) graphql.Marshaler {
|
func (ec *executionContext) marshalNInviteProjectMembersPayload2ᚖgithubᚗcomᚋjordanknottᚋtaskcafeᚋinternalᚋgraphᚐInviteProjectMembersPayload(ctx context.Context, sel ast.SelectionSet, v *InviteProjectMembersPayload) graphql.Marshaler {
|
||||||
if v == nil {
|
if v == nil {
|
||||||
if !graphql.HasFieldError(ctx, graphql.GetFieldContext(ctx)) {
|
if !graphql.HasFieldError(ctx, graphql.GetFieldContext(ctx)) {
|
||||||
ec.Errorf(ctx, "must not be null")
|
ec.Errorf(ctx, "must not be null")
|
||||||
}
|
}
|
||||||
return graphql.Null
|
return graphql.Null
|
||||||
}
|
}
|
||||||
return ec._InviteProjectMemberPayload(ctx, sel, v)
|
return ec._InviteProjectMembersPayload(ctx, sel, v)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (ec *executionContext) marshalNLabelColor2githubᚗcomᚋjordanknottᚋtaskcafeᚋinternalᚋdbᚐLabelColor(ctx context.Context, sel ast.SelectionSet, v db.LabelColor) graphql.Marshaler {
|
func (ec *executionContext) marshalNLabelColor2githubᚗcomᚋjordanknottᚋtaskcafeᚋinternalᚋdbᚐLabelColor(ctx context.Context, sel ast.SelectionSet, v db.LabelColor) graphql.Marshaler {
|
||||||
@ -19830,6 +19899,30 @@ func (ec *executionContext) marshalNMember2ᚖgithubᚗcomᚋjordanknottᚋtaskc
|
|||||||
return ec._Member(ctx, sel, v)
|
return ec._Member(ctx, sel, v)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (ec *executionContext) unmarshalNMemberInvite2githubᚗcomᚋjordanknottᚋtaskcafeᚋinternalᚋgraphᚐMemberInvite(ctx context.Context, v interface{}) (MemberInvite, error) {
|
||||||
|
return ec.unmarshalInputMemberInvite(ctx, v)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ec *executionContext) unmarshalNMemberInvite2ᚕgithubᚗcomᚋjordanknottᚋtaskcafeᚋinternalᚋgraphᚐMemberInviteᚄ(ctx context.Context, v interface{}) ([]MemberInvite, error) {
|
||||||
|
var vSlice []interface{}
|
||||||
|
if v != nil {
|
||||||
|
if tmp1, ok := v.([]interface{}); ok {
|
||||||
|
vSlice = tmp1
|
||||||
|
} else {
|
||||||
|
vSlice = []interface{}{v}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
var err error
|
||||||
|
res := make([]MemberInvite, len(vSlice))
|
||||||
|
for i := range vSlice {
|
||||||
|
res[i], err = ec.unmarshalNMemberInvite2githubᚗcomᚋjordanknottᚋtaskcafeᚋinternalᚋgraphᚐMemberInvite(ctx, vSlice[i])
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return res, nil
|
||||||
|
}
|
||||||
|
|
||||||
func (ec *executionContext) marshalNMemberList2githubᚗcomᚋjordanknottᚋtaskcafeᚋinternalᚋgraphᚐMemberList(ctx context.Context, sel ast.SelectionSet, v MemberList) graphql.Marshaler {
|
func (ec *executionContext) marshalNMemberList2githubᚗcomᚋjordanknottᚋtaskcafeᚋinternalᚋgraphᚐMemberList(ctx context.Context, sel ast.SelectionSet, v MemberList) graphql.Marshaler {
|
||||||
return ec._MemberList(ctx, sel, &v)
|
return ec._MemberList(ctx, sel, &v)
|
||||||
}
|
}
|
||||||
|
@ -19,6 +19,7 @@ import (
|
|||||||
"github.com/google/uuid"
|
"github.com/google/uuid"
|
||||||
"github.com/jordanknott/taskcafe/internal/auth"
|
"github.com/jordanknott/taskcafe/internal/auth"
|
||||||
"github.com/jordanknott/taskcafe/internal/db"
|
"github.com/jordanknott/taskcafe/internal/db"
|
||||||
|
"github.com/jordanknott/taskcafe/internal/logger"
|
||||||
"github.com/jordanknott/taskcafe/internal/utils"
|
"github.com/jordanknott/taskcafe/internal/utils"
|
||||||
log "github.com/sirupsen/logrus"
|
log "github.com/sirupsen/logrus"
|
||||||
"github.com/vektah/gqlparser/v2/gqlerror"
|
"github.com/vektah/gqlparser/v2/gqlerror"
|
||||||
@ -63,10 +64,10 @@ func NewHandler(repo db.Repository) http.Handler {
|
|||||||
default:
|
default:
|
||||||
fieldName = "ProjectID"
|
fieldName = "ProjectID"
|
||||||
}
|
}
|
||||||
log.WithFields(log.Fields{"typeArg": typeArg, "fieldName": fieldName}).Info("getting field by name")
|
logger.New(ctx).WithFields(log.Fields{"typeArg": typeArg, "fieldName": fieldName}).Info("getting field by name")
|
||||||
subjectField := val.FieldByName(fieldName)
|
subjectField := val.FieldByName(fieldName)
|
||||||
if !subjectField.IsValid() {
|
if !subjectField.IsValid() {
|
||||||
log.Error("subject field name does not exist on input type")
|
logger.New(ctx).Error("subject field name does not exist on input type")
|
||||||
return nil, errors.New("subject field name does not exist on input type")
|
return nil, errors.New("subject field name does not exist on input type")
|
||||||
}
|
}
|
||||||
if fieldName == "TeamID" && subjectField.IsNil() {
|
if fieldName == "TeamID" && subjectField.IsNil() {
|
||||||
@ -76,13 +77,13 @@ func NewHandler(repo db.Repository) http.Handler {
|
|||||||
}
|
}
|
||||||
subjectID, ok = subjectField.Interface().(uuid.UUID)
|
subjectID, ok = subjectField.Interface().(uuid.UUID)
|
||||||
if !ok {
|
if !ok {
|
||||||
log.Error("error while casting subject UUID")
|
logger.New(ctx).Error("error while casting subject UUID")
|
||||||
return nil, errors.New("error while casting subject uuid")
|
return nil, errors.New("error while casting subject uuid")
|
||||||
}
|
}
|
||||||
|
|
||||||
var err error
|
var err error
|
||||||
if level == ActionLevelProject {
|
if level == ActionLevelProject {
|
||||||
log.WithFields(log.Fields{"subjectID": subjectID}).Info("fetching subject ID by typeArg")
|
logger.New(ctx).WithFields(log.Fields{"subjectID": subjectID}).Info("fetching subject ID by typeArg")
|
||||||
if typeArg == ObjectTypeTask {
|
if typeArg == ObjectTypeTask {
|
||||||
subjectID, err = repo.GetProjectIDForTask(ctx, subjectID)
|
subjectID, err = repo.GetProjectIDForTask(ctx, subjectID)
|
||||||
}
|
}
|
||||||
@ -96,7 +97,7 @@ func NewHandler(repo db.Repository) http.Handler {
|
|||||||
subjectID, err = repo.GetProjectIDForTaskChecklistItem(ctx, subjectID)
|
subjectID, err = repo.GetProjectIDForTaskChecklistItem(ctx, subjectID)
|
||||||
}
|
}
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.WithError(err).Error("error while getting subject ID")
|
logger.New(ctx).WithError(err).Error("error while getting subject ID")
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
projectRoles, err := GetProjectRoles(ctx, repo, subjectID)
|
projectRoles, err := GetProjectRoles(ctx, repo, subjectID)
|
||||||
@ -109,13 +110,13 @@ func NewHandler(repo db.Repository) http.Handler {
|
|||||||
},
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
log.WithError(err).Error("error while getting project roles")
|
logger.New(ctx).WithError(err).Error("error while getting project roles")
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
for _, validRole := range roles {
|
for _, validRole := range roles {
|
||||||
log.WithFields(log.Fields{"validRole": validRole}).Info("checking role")
|
logger.New(ctx).WithFields(log.Fields{"validRole": validRole}).Info("checking role")
|
||||||
if CompareRoleLevel(projectRoles.TeamRole, validRole) || CompareRoleLevel(projectRoles.ProjectRole, validRole) {
|
if CompareRoleLevel(projectRoles.TeamRole, validRole) || CompareRoleLevel(projectRoles.ProjectRole, validRole) {
|
||||||
log.WithFields(log.Fields{"teamRole": projectRoles.TeamRole, "projectRole": projectRoles.ProjectRole}).Info("is team or project role")
|
logger.New(ctx).WithFields(log.Fields{"teamRole": projectRoles.TeamRole, "projectRole": projectRoles.ProjectRole}).Info("is team or project role")
|
||||||
return next(ctx)
|
return next(ctx)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -132,7 +133,7 @@ func NewHandler(repo db.Repository) http.Handler {
|
|||||||
}
|
}
|
||||||
role, err := repo.GetTeamRoleForUserID(ctx, db.GetTeamRoleForUserIDParams{UserID: userID, TeamID: subjectID})
|
role, err := repo.GetTeamRoleForUserID(ctx, db.GetTeamRoleForUserIDParams{UserID: userID, TeamID: subjectID})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.WithError(err).Error("error while getting team roles for user ID")
|
logger.New(ctx).WithError(err).Error("error while getting team roles for user ID")
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
for _, validRole := range roles {
|
for _, validRole := range roles {
|
||||||
|
@ -177,15 +177,15 @@ type FindUser struct {
|
|||||||
UserID uuid.UUID `json:"userID"`
|
UserID uuid.UUID `json:"userID"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type InviteProjectMember struct {
|
type InviteProjectMembers struct {
|
||||||
ProjectID uuid.UUID `json:"projectID"`
|
ProjectID uuid.UUID `json:"projectID"`
|
||||||
UserID *uuid.UUID `json:"userID"`
|
Members []MemberInvite `json:"members"`
|
||||||
Email *string `json:"email"`
|
|
||||||
}
|
}
|
||||||
|
|
||||||
type InviteProjectMemberPayload struct {
|
type InviteProjectMembersPayload struct {
|
||||||
Ok bool `json:"ok"`
|
Ok bool `json:"ok"`
|
||||||
Member *Member `json:"member"`
|
ProjectID uuid.UUID `json:"projectID"`
|
||||||
|
Members []Member `json:"members"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type LogoutUser struct {
|
type LogoutUser struct {
|
||||||
@ -208,6 +208,11 @@ type Member struct {
|
|||||||
Member *MemberList `json:"member"`
|
Member *MemberList `json:"member"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type MemberInvite struct {
|
||||||
|
UserID *uuid.UUID `json:"userID"`
|
||||||
|
Email *string `json:"email"`
|
||||||
|
}
|
||||||
|
|
||||||
type MemberList struct {
|
type MemberList struct {
|
||||||
Teams []db.Team `json:"teams"`
|
Teams []db.Team `json:"teams"`
|
||||||
Projects []db.Project `json:"projects"`
|
Projects []db.Project `json:"projects"`
|
||||||
|
@ -338,24 +338,28 @@ input UpdateProjectLabelColor {
|
|||||||
}
|
}
|
||||||
|
|
||||||
extend type Mutation {
|
extend type Mutation {
|
||||||
# TODO: rename to inviteProjectMember
|
inviteProjectMembers(input: InviteProjectMembers!):
|
||||||
inviteProjectMember(input: InviteProjectMember!):
|
InviteProjectMembersPayload! @hasRole(roles: [ADMIN], level: PROJECT, type: PROJECT)
|
||||||
InviteProjectMemberPayload! @hasRole(roles: [ADMIN], level: PROJECT, type: PROJECT)
|
|
||||||
deleteProjectMember(input: DeleteProjectMember!):
|
deleteProjectMember(input: DeleteProjectMember!):
|
||||||
DeleteProjectMemberPayload! @hasRole(roles: [ADMIN], level: PROJECT, type: PROJECT)
|
DeleteProjectMemberPayload! @hasRole(roles: [ADMIN], level: PROJECT, type: PROJECT)
|
||||||
updateProjectMemberRole(input: UpdateProjectMemberRole!):
|
updateProjectMemberRole(input: UpdateProjectMemberRole!):
|
||||||
UpdateProjectMemberRolePayload! @hasRole(roles: [ADMIN], level: PROJECT, type: PROJECT)
|
UpdateProjectMemberRolePayload! @hasRole(roles: [ADMIN], level: PROJECT, type: PROJECT)
|
||||||
}
|
}
|
||||||
|
|
||||||
input InviteProjectMember {
|
input MemberInvite {
|
||||||
projectID: UUID!
|
|
||||||
userID: UUID
|
userID: UUID
|
||||||
email: String
|
email: String
|
||||||
}
|
}
|
||||||
|
|
||||||
type InviteProjectMemberPayload {
|
input InviteProjectMembers {
|
||||||
|
projectID: UUID!
|
||||||
|
members: [MemberInvite!]!
|
||||||
|
}
|
||||||
|
|
||||||
|
type InviteProjectMembersPayload {
|
||||||
ok: Boolean!
|
ok: Boolean!
|
||||||
member: Member!
|
projectID: UUID!
|
||||||
|
members: [Member!]!
|
||||||
}
|
}
|
||||||
|
|
||||||
input DeleteProjectMember {
|
input DeleteProjectMember {
|
||||||
|
@ -13,6 +13,7 @@ import (
|
|||||||
"github.com/google/uuid"
|
"github.com/google/uuid"
|
||||||
"github.com/jordanknott/taskcafe/internal/auth"
|
"github.com/jordanknott/taskcafe/internal/auth"
|
||||||
"github.com/jordanknott/taskcafe/internal/db"
|
"github.com/jordanknott/taskcafe/internal/db"
|
||||||
|
"github.com/jordanknott/taskcafe/internal/logger"
|
||||||
"github.com/lithammer/fuzzysearch/fuzzy"
|
"github.com/lithammer/fuzzysearch/fuzzy"
|
||||||
log "github.com/sirupsen/logrus"
|
log "github.com/sirupsen/logrus"
|
||||||
"github.com/vektah/gqlparser/v2/gqlerror"
|
"github.com/vektah/gqlparser/v2/gqlerror"
|
||||||
@ -29,7 +30,7 @@ func (r *mutationResolver) CreateProject(ctx context.Context, input NewProject)
|
|||||||
return &db.Project{}, errors.New("user id is missing")
|
return &db.Project{}, errors.New("user id is missing")
|
||||||
}
|
}
|
||||||
createdAt := time.Now().UTC()
|
createdAt := time.Now().UTC()
|
||||||
log.WithFields(log.Fields{"name": input.Name, "teamID": input.TeamID}).Info("creating new project")
|
logger.New(ctx).WithFields(log.Fields{"name": input.Name, "teamID": input.TeamID}).Info("creating new project")
|
||||||
var project db.Project
|
var project db.Project
|
||||||
var err error
|
var err error
|
||||||
if input.TeamID == nil {
|
if input.TeamID == nil {
|
||||||
@ -38,10 +39,10 @@ func (r *mutationResolver) CreateProject(ctx context.Context, input NewProject)
|
|||||||
Name: input.Name,
|
Name: input.Name,
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.WithError(err).Error("error while creating project")
|
logger.New(ctx).WithError(err).Error("error while creating project")
|
||||||
return &db.Project{}, err
|
return &db.Project{}, err
|
||||||
}
|
}
|
||||||
log.WithFields(log.Fields{"userID": userID, "projectID": project.ProjectID}).Info("creating personal project link")
|
logger.New(ctx).WithField("projectID", project.ProjectID).Info("creating personal project link")
|
||||||
} else {
|
} else {
|
||||||
project, err = r.Repository.CreateTeamProject(ctx, db.CreateTeamProjectParams{
|
project, err = r.Repository.CreateTeamProject(ctx, db.CreateTeamProjectParams{
|
||||||
CreatedAt: createdAt,
|
CreatedAt: createdAt,
|
||||||
@ -49,13 +50,13 @@ func (r *mutationResolver) CreateProject(ctx context.Context, input NewProject)
|
|||||||
TeamID: *input.TeamID,
|
TeamID: *input.TeamID,
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.WithError(err).Error("error while creating project")
|
logger.New(ctx).WithError(err).Error("error while creating project")
|
||||||
return &db.Project{}, err
|
return &db.Project{}, err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
_, err = r.Repository.CreateProjectMember(ctx, db.CreateProjectMemberParams{ProjectID: project.ProjectID, UserID: userID, AddedAt: createdAt, RoleCode: "admin"})
|
_, err = r.Repository.CreateProjectMember(ctx, db.CreateProjectMemberParams{ProjectID: project.ProjectID, UserID: userID, AddedAt: createdAt, RoleCode: "admin"})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.WithError(err).Error("error while creating initial project member")
|
logger.New(ctx).WithError(err).Error("error while creating initial project member")
|
||||||
return &db.Project{}, err
|
return &db.Project{}, err
|
||||||
}
|
}
|
||||||
return &project, nil
|
return &project, nil
|
||||||
@ -124,31 +125,33 @@ func (r *mutationResolver) UpdateProjectLabelColor(ctx context.Context, input Up
|
|||||||
return &label, err
|
return &label, err
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *mutationResolver) InviteProjectMember(ctx context.Context, input InviteProjectMember) (*InviteProjectMemberPayload, error) {
|
func (r *mutationResolver) InviteProjectMembers(ctx context.Context, input InviteProjectMembers) (*InviteProjectMembersPayload, error) {
|
||||||
if input.Email != nil && input.UserID != nil {
|
members := []Member{}
|
||||||
return &InviteProjectMemberPayload{Ok: false}, &gqlerror.Error{
|
for _, invitedMember := range input.Members {
|
||||||
|
if invitedMember.Email != nil && invitedMember.UserID != nil {
|
||||||
|
return &InviteProjectMembersPayload{Ok: false}, &gqlerror.Error{
|
||||||
Message: "Both email and userID can not be used to invite a project member",
|
Message: "Both email and userID can not be used to invite a project member",
|
||||||
Extensions: map[string]interface{}{
|
Extensions: map[string]interface{}{
|
||||||
"code": "403",
|
"code": "403",
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
} else if input.Email == nil && input.UserID == nil {
|
} else if invitedMember.Email == nil && invitedMember.UserID == nil {
|
||||||
return &InviteProjectMemberPayload{Ok: false}, &gqlerror.Error{
|
return &InviteProjectMembersPayload{Ok: false}, &gqlerror.Error{
|
||||||
Message: "Either email or userID must be set to invite a project member",
|
Message: "Either email or userID must be set to invite a project member",
|
||||||
Extensions: map[string]interface{}{
|
Extensions: map[string]interface{}{
|
||||||
"code": "403",
|
"code": "403",
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if input.UserID != nil {
|
if invitedMember.UserID != nil {
|
||||||
addedAt := time.Now().UTC()
|
addedAt := time.Now().UTC()
|
||||||
_, err := r.Repository.CreateProjectMember(ctx, db.CreateProjectMemberParams{ProjectID: input.ProjectID, UserID: *input.UserID, AddedAt: addedAt, RoleCode: "member"})
|
_, err := r.Repository.CreateProjectMember(ctx, db.CreateProjectMemberParams{ProjectID: input.ProjectID, UserID: *invitedMember.UserID, AddedAt: addedAt, RoleCode: "member"})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return &InviteProjectMemberPayload{Ok: false}, err
|
return &InviteProjectMembersPayload{Ok: false}, err
|
||||||
}
|
}
|
||||||
user, err := r.Repository.GetUserAccountByID(ctx, *input.UserID)
|
user, err := r.Repository.GetUserAccountByID(ctx, *invitedMember.UserID)
|
||||||
if err != nil && err != sql.ErrNoRows {
|
if err != nil && err != sql.ErrNoRows {
|
||||||
return &InviteProjectMemberPayload{Ok: false}, err
|
return &InviteProjectMembersPayload{Ok: false}, err
|
||||||
|
|
||||||
}
|
}
|
||||||
var url *string
|
var url *string
|
||||||
@ -157,20 +160,20 @@ func (r *mutationResolver) InviteProjectMember(ctx context.Context, input Invite
|
|||||||
}
|
}
|
||||||
profileIcon := &ProfileIcon{url, &user.Initials, &user.ProfileBgColor}
|
profileIcon := &ProfileIcon{url, &user.Initials, &user.ProfileBgColor}
|
||||||
|
|
||||||
role, err := r.Repository.GetRoleForProjectMemberByUserID(ctx, db.GetRoleForProjectMemberByUserIDParams{UserID: *input.UserID, ProjectID: input.ProjectID})
|
role, err := r.Repository.GetRoleForProjectMemberByUserID(ctx, db.GetRoleForProjectMemberByUserIDParams{UserID: *invitedMember.UserID, ProjectID: input.ProjectID})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return &InviteProjectMemberPayload{Ok: false}, err
|
return &InviteProjectMembersPayload{Ok: false}, err
|
||||||
}
|
}
|
||||||
return &InviteProjectMemberPayload{Ok: true, Member: &Member{
|
members = append(members, Member{
|
||||||
ID: *input.UserID,
|
ID: *invitedMember.UserID,
|
||||||
FullName: user.FullName,
|
FullName: user.FullName,
|
||||||
Username: user.Username,
|
Username: user.Username,
|
||||||
ProfileIcon: profileIcon,
|
ProfileIcon: profileIcon,
|
||||||
Role: &db.Role{Code: role.Code, Name: role.Name},
|
Role: &db.Role{Code: role.Code, Name: role.Name},
|
||||||
}}, nil
|
})
|
||||||
}
|
}
|
||||||
// invite user
|
}
|
||||||
return &InviteProjectMemberPayload{Ok: false}, errors.New("not implemented")
|
return &InviteProjectMembersPayload{Ok: false, ProjectID: input.ProjectID, Members: members}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *mutationResolver) DeleteProjectMember(ctx context.Context, input DeleteProjectMember) (*DeleteProjectMemberPayload, error) {
|
func (r *mutationResolver) DeleteProjectMember(ctx context.Context, input DeleteProjectMember) (*DeleteProjectMemberPayload, error) {
|
||||||
@ -202,18 +205,18 @@ func (r *mutationResolver) DeleteProjectMember(ctx context.Context, input Delete
|
|||||||
func (r *mutationResolver) UpdateProjectMemberRole(ctx context.Context, input UpdateProjectMemberRole) (*UpdateProjectMemberRolePayload, error) {
|
func (r *mutationResolver) UpdateProjectMemberRole(ctx context.Context, input UpdateProjectMemberRole) (*UpdateProjectMemberRolePayload, error) {
|
||||||
user, err := r.Repository.GetUserAccountByID(ctx, input.UserID)
|
user, err := r.Repository.GetUserAccountByID(ctx, input.UserID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.WithError(err).Error("get user account")
|
logger.New(ctx).WithError(err).Error("get user account")
|
||||||
return &UpdateProjectMemberRolePayload{Ok: false}, err
|
return &UpdateProjectMemberRolePayload{Ok: false}, err
|
||||||
}
|
}
|
||||||
_, err = r.Repository.UpdateProjectMemberRole(ctx, db.UpdateProjectMemberRoleParams{ProjectID: input.ProjectID,
|
_, err = r.Repository.UpdateProjectMemberRole(ctx, db.UpdateProjectMemberRoleParams{ProjectID: input.ProjectID,
|
||||||
UserID: input.UserID, RoleCode: input.RoleCode.String()})
|
UserID: input.UserID, RoleCode: input.RoleCode.String()})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.WithError(err).Error("update project member role")
|
logger.New(ctx).WithError(err).Error("update project member role")
|
||||||
return &UpdateProjectMemberRolePayload{Ok: false}, err
|
return &UpdateProjectMemberRolePayload{Ok: false}, err
|
||||||
}
|
}
|
||||||
role, err := r.Repository.GetRoleForProjectMemberByUserID(ctx, db.GetRoleForProjectMemberByUserIDParams{UserID: user.UserID, ProjectID: input.ProjectID})
|
role, err := r.Repository.GetRoleForProjectMemberByUserID(ctx, db.GetRoleForProjectMemberByUserIDParams{UserID: user.UserID, ProjectID: input.ProjectID})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.WithError(err).Error("get role for project member")
|
logger.New(ctx).WithError(err).Error("get role for project member")
|
||||||
return &UpdateProjectMemberRolePayload{Ok: false}, err
|
return &UpdateProjectMemberRolePayload{Ok: false}, err
|
||||||
}
|
}
|
||||||
var url *string
|
var url *string
|
||||||
@ -232,17 +235,17 @@ func (r *mutationResolver) UpdateProjectMemberRole(ctx context.Context, input Up
|
|||||||
|
|
||||||
func (r *mutationResolver) CreateTask(ctx context.Context, input NewTask) (*db.Task, error) {
|
func (r *mutationResolver) CreateTask(ctx context.Context, input NewTask) (*db.Task, error) {
|
||||||
createdAt := time.Now().UTC()
|
createdAt := time.Now().UTC()
|
||||||
log.WithFields(log.Fields{"positon": input.Position, "taskGroupID": input.TaskGroupID}).Info("creating task")
|
logger.New(ctx).WithFields(log.Fields{"positon": input.Position, "taskGroupID": input.TaskGroupID}).Info("creating task")
|
||||||
task, err := r.Repository.CreateTask(ctx, db.CreateTaskParams{input.TaskGroupID, createdAt, input.Name, input.Position})
|
task, err := r.Repository.CreateTask(ctx, db.CreateTaskParams{input.TaskGroupID, createdAt, input.Name, input.Position})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.WithError(err).Error("issue while creating task")
|
logger.New(ctx).WithError(err).Error("issue while creating task")
|
||||||
return &db.Task{}, err
|
return &db.Task{}, err
|
||||||
}
|
}
|
||||||
return &task, nil
|
return &task, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *mutationResolver) DeleteTask(ctx context.Context, input DeleteTaskInput) (*DeleteTaskPayload, error) {
|
func (r *mutationResolver) DeleteTask(ctx context.Context, input DeleteTaskInput) (*DeleteTaskPayload, error) {
|
||||||
log.WithFields(log.Fields{
|
logger.New(ctx).WithFields(log.Fields{
|
||||||
"taskID": input.TaskID,
|
"taskID": input.TaskID,
|
||||||
}).Info("deleting task")
|
}).Info("deleting task")
|
||||||
err := r.Repository.DeleteTaskByID(ctx, input.TaskID)
|
err := r.Repository.DeleteTaskByID(ctx, input.TaskID)
|
||||||
@ -299,8 +302,8 @@ func (r *mutationResolver) UpdateTaskDueDate(ctx context.Context, input UpdateTa
|
|||||||
func (r *mutationResolver) AssignTask(ctx context.Context, input *AssignTaskInput) (*db.Task, error) {
|
func (r *mutationResolver) AssignTask(ctx context.Context, input *AssignTaskInput) (*db.Task, error) {
|
||||||
assignedDate := time.Now().UTC()
|
assignedDate := time.Now().UTC()
|
||||||
assignedTask, err := r.Repository.CreateTaskAssigned(ctx, db.CreateTaskAssignedParams{input.TaskID, input.UserID, assignedDate})
|
assignedTask, err := r.Repository.CreateTaskAssigned(ctx, db.CreateTaskAssignedParams{input.TaskID, input.UserID, assignedDate})
|
||||||
log.WithFields(log.Fields{
|
logger.New(ctx).WithFields(log.Fields{
|
||||||
"userID": assignedTask.UserID,
|
"assignedUserID": assignedTask.UserID,
|
||||||
"taskID": assignedTask.TaskID,
|
"taskID": assignedTask.TaskID,
|
||||||
"assignedTaskID": assignedTask.TaskAssignedID,
|
"assignedTaskID": assignedTask.TaskAssignedID,
|
||||||
}).Info("assigned task")
|
}).Info("assigned task")
|
||||||
@ -610,7 +613,7 @@ func (r *mutationResolver) ToggleTaskLabel(ctx context.Context, input ToggleTask
|
|||||||
createdAt := time.Now().UTC()
|
createdAt := time.Now().UTC()
|
||||||
|
|
||||||
if err == sql.ErrNoRows {
|
if err == sql.ErrNoRows {
|
||||||
log.WithFields(log.Fields{"err": err}).Warning("no rows")
|
logger.New(ctx).WithFields(log.Fields{"err": err}).Warning("no rows")
|
||||||
_, err := r.Repository.CreateTaskLabelForTask(ctx, db.CreateTaskLabelForTaskParams{
|
_, err := r.Repository.CreateTaskLabelForTask(ctx, db.CreateTaskLabelForTaskParams{
|
||||||
TaskID: input.TaskID,
|
TaskID: input.TaskID,
|
||||||
ProjectLabelID: input.ProjectLabelID,
|
ProjectLabelID: input.ProjectLabelID,
|
||||||
@ -643,17 +646,17 @@ func (r *mutationResolver) ToggleTaskLabel(ctx context.Context, input ToggleTask
|
|||||||
func (r *mutationResolver) DeleteTeam(ctx context.Context, input DeleteTeam) (*DeleteTeamPayload, error) {
|
func (r *mutationResolver) DeleteTeam(ctx context.Context, input DeleteTeam) (*DeleteTeamPayload, error) {
|
||||||
team, err := r.Repository.GetTeamByID(ctx, input.TeamID)
|
team, err := r.Repository.GetTeamByID(ctx, input.TeamID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Error(err)
|
logger.New(ctx).Error(err)
|
||||||
return &DeleteTeamPayload{Ok: false}, err
|
return &DeleteTeamPayload{Ok: false}, err
|
||||||
}
|
}
|
||||||
projects, err := r.Repository.GetAllProjectsForTeam(ctx, input.TeamID)
|
projects, err := r.Repository.GetAllProjectsForTeam(ctx, input.TeamID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Error(err)
|
logger.New(ctx).Error(err)
|
||||||
return &DeleteTeamPayload{Ok: false}, err
|
return &DeleteTeamPayload{Ok: false}, err
|
||||||
}
|
}
|
||||||
err = r.Repository.DeleteTeamByID(ctx, input.TeamID)
|
err = r.Repository.DeleteTeamByID(ctx, input.TeamID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Error(err)
|
logger.New(ctx).Error(err)
|
||||||
return &DeleteTeamPayload{Ok: false}, err
|
return &DeleteTeamPayload{Ok: false}, err
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -708,18 +711,18 @@ func (r *mutationResolver) CreateTeamMember(ctx context.Context, input CreateTea
|
|||||||
func (r *mutationResolver) UpdateTeamMemberRole(ctx context.Context, input UpdateTeamMemberRole) (*UpdateTeamMemberRolePayload, error) {
|
func (r *mutationResolver) UpdateTeamMemberRole(ctx context.Context, input UpdateTeamMemberRole) (*UpdateTeamMemberRolePayload, error) {
|
||||||
user, err := r.Repository.GetUserAccountByID(ctx, input.UserID)
|
user, err := r.Repository.GetUserAccountByID(ctx, input.UserID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.WithError(err).Error("get user account")
|
logger.New(ctx).WithError(err).Error("get user account")
|
||||||
return &UpdateTeamMemberRolePayload{Ok: false}, err
|
return &UpdateTeamMemberRolePayload{Ok: false}, err
|
||||||
}
|
}
|
||||||
_, err = r.Repository.UpdateTeamMemberRole(ctx, db.UpdateTeamMemberRoleParams{TeamID: input.TeamID,
|
_, err = r.Repository.UpdateTeamMemberRole(ctx, db.UpdateTeamMemberRoleParams{TeamID: input.TeamID,
|
||||||
UserID: input.UserID, RoleCode: input.RoleCode.String()})
|
UserID: input.UserID, RoleCode: input.RoleCode.String()})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.WithError(err).Error("update project member role")
|
logger.New(ctx).WithError(err).Error("update project member role")
|
||||||
return &UpdateTeamMemberRolePayload{Ok: false}, err
|
return &UpdateTeamMemberRolePayload{Ok: false}, err
|
||||||
}
|
}
|
||||||
role, err := r.Repository.GetRoleForTeamMember(ctx, db.GetRoleForTeamMemberParams{UserID: user.UserID, TeamID: input.TeamID})
|
role, err := r.Repository.GetRoleForTeamMember(ctx, db.GetRoleForTeamMemberParams{UserID: user.UserID, TeamID: input.TeamID})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.WithError(err).Error("get role for project member")
|
logger.New(ctx).WithError(err).Error("get role for project member")
|
||||||
return &UpdateTeamMemberRolePayload{Ok: false}, err
|
return &UpdateTeamMemberRolePayload{Ok: false}, err
|
||||||
}
|
}
|
||||||
var url *string
|
var url *string
|
||||||
@ -871,9 +874,9 @@ func (r *notificationResolver) ID(ctx context.Context, obj *db.Notification) (uu
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (r *notificationResolver) Entity(ctx context.Context, obj *db.Notification) (*NotificationEntity, error) {
|
func (r *notificationResolver) Entity(ctx context.Context, obj *db.Notification) (*NotificationEntity, error) {
|
||||||
log.WithFields(log.Fields{"notificationID": obj.NotificationID}).Info("fetching entity for notification")
|
logger.New(ctx).WithFields(log.Fields{"notificationID": obj.NotificationID}).Info("fetching entity for notification")
|
||||||
entity, err := r.Repository.GetEntityForNotificationID(ctx, obj.NotificationID)
|
entity, err := r.Repository.GetEntityForNotificationID(ctx, obj.NotificationID)
|
||||||
log.WithFields(log.Fields{"entityID": entity.EntityID}).Info("fetched entity")
|
logger.New(ctx).WithFields(log.Fields{"entityID": entity.EntityID}).Info("fetched entity")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return &NotificationEntity{}, err
|
return &NotificationEntity{}, err
|
||||||
}
|
}
|
||||||
@ -905,7 +908,7 @@ func (r *notificationResolver) Actor(ctx context.Context, obj *db.Notification)
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return &NotificationActor{}, err
|
return &NotificationActor{}, err
|
||||||
}
|
}
|
||||||
log.WithFields(log.Fields{"entityID": entity.ActorID}).Info("fetching actor")
|
logger.New(ctx).WithFields(log.Fields{"entityID": entity.ActorID}).Info("fetching actor")
|
||||||
user, err := r.Repository.GetUserAccountByID(ctx, entity.ActorID)
|
user, err := r.Repository.GetUserAccountByID(ctx, entity.ActorID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return &NotificationActor{}, err
|
return &NotificationActor{}, err
|
||||||
@ -935,7 +938,7 @@ func (r *projectResolver) Team(ctx context.Context, obj *db.Project) (*db.Team,
|
|||||||
if err == sql.ErrNoRows {
|
if err == sql.ErrNoRows {
|
||||||
return nil, nil
|
return nil, nil
|
||||||
}
|
}
|
||||||
log.WithFields(log.Fields{"teamID": obj.TeamID, "projectID": obj.ProjectID}).WithError(err).Error("issue while getting team for project")
|
logger.New(ctx).WithFields(log.Fields{"teamID": obj.TeamID, "projectID": obj.ProjectID}).WithError(err).Error("issue while getting team for project")
|
||||||
return &team, err
|
return &team, err
|
||||||
}
|
}
|
||||||
return &team, nil
|
return &team, nil
|
||||||
@ -949,14 +952,14 @@ func (r *projectResolver) Members(ctx context.Context, obj *db.Project) ([]Membe
|
|||||||
members := []Member{}
|
members := []Member{}
|
||||||
projectMembers, err := r.Repository.GetProjectMembersForProjectID(ctx, obj.ProjectID)
|
projectMembers, err := r.Repository.GetProjectMembersForProjectID(ctx, obj.ProjectID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.WithError(err).Error("get project members for project id")
|
logger.New(ctx).WithError(err).Error("get project members for project id")
|
||||||
return members, err
|
return members, err
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, projectMember := range projectMembers {
|
for _, projectMember := range projectMembers {
|
||||||
user, err := r.Repository.GetUserAccountByID(ctx, projectMember.UserID)
|
user, err := r.Repository.GetUserAccountByID(ctx, projectMember.UserID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.WithError(err).Error("get user account by ID")
|
logger.New(ctx).WithError(err).Error("get user account by ID")
|
||||||
return members, err
|
return members, err
|
||||||
}
|
}
|
||||||
var url *string
|
var url *string
|
||||||
@ -965,7 +968,7 @@ func (r *projectResolver) Members(ctx context.Context, obj *db.Project) ([]Membe
|
|||||||
}
|
}
|
||||||
role, err := r.Repository.GetRoleForProjectMemberByUserID(ctx, db.GetRoleForProjectMemberByUserIDParams{UserID: user.UserID, ProjectID: obj.ProjectID})
|
role, err := r.Repository.GetRoleForProjectMemberByUserID(ctx, db.GetRoleForProjectMemberByUserIDParams{UserID: user.UserID, ProjectID: obj.ProjectID})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.WithError(err).Error("get role for projet member by user ID")
|
logger.New(ctx).WithError(err).Error("get role for projet member by user ID")
|
||||||
return members, err
|
return members, err
|
||||||
}
|
}
|
||||||
profileIcon := &ProfileIcon{url, &user.Initials, &user.ProfileBgColor}
|
profileIcon := &ProfileIcon{url, &user.Initials, &user.ProfileBgColor}
|
||||||
@ -1023,11 +1026,7 @@ func (r *queryResolver) FindUser(ctx context.Context, input FindUser) (*db.UserA
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (r *queryResolver) FindProject(ctx context.Context, input FindProject) (*db.Project, error) {
|
func (r *queryResolver) FindProject(ctx context.Context, input FindProject) (*db.Project, error) {
|
||||||
userID, role, ok := GetUser(ctx)
|
logger.New(ctx).Info("finding project user")
|
||||||
log.WithFields(log.Fields{"userID": userID, "role": role}).Info("find project user")
|
|
||||||
if !ok {
|
|
||||||
return &db.Project{}, nil
|
|
||||||
}
|
|
||||||
project, err := r.Repository.GetProjectByID(ctx, input.ProjectID)
|
project, err := r.Repository.GetProjectByID(ctx, input.ProjectID)
|
||||||
if err == sql.ErrNoRows {
|
if err == sql.ErrNoRows {
|
||||||
return &db.Project{}, &gqlerror.Error{
|
return &db.Project{}, &gqlerror.Error{
|
||||||
@ -1048,10 +1047,10 @@ func (r *queryResolver) FindTask(ctx context.Context, input FindTask) (*db.Task,
|
|||||||
func (r *queryResolver) Projects(ctx context.Context, input *ProjectsFilter) ([]db.Project, error) {
|
func (r *queryResolver) Projects(ctx context.Context, input *ProjectsFilter) ([]db.Project, error) {
|
||||||
userID, orgRole, ok := GetUser(ctx)
|
userID, orgRole, ok := GetUser(ctx)
|
||||||
if !ok {
|
if !ok {
|
||||||
log.Info("user id was not found from middleware")
|
logger.New(ctx).Info("user id was not found from middleware")
|
||||||
return []db.Project{}, nil
|
return []db.Project{}, nil
|
||||||
}
|
}
|
||||||
log.WithFields(log.Fields{"userID": userID}).Info("fetching projects")
|
logger.New(ctx).Info("fetching projects")
|
||||||
|
|
||||||
if input != nil {
|
if input != nil {
|
||||||
return r.Repository.GetAllProjectsForTeam(ctx, *input.TeamID)
|
return r.Repository.GetAllProjectsForTeam(ctx, *input.TeamID)
|
||||||
@ -1067,37 +1066,36 @@ func (r *queryResolver) Projects(ctx context.Context, input *ProjectsFilter) ([]
|
|||||||
|
|
||||||
projects := make(map[string]db.Project)
|
projects := make(map[string]db.Project)
|
||||||
for _, team := range teams {
|
for _, team := range teams {
|
||||||
log.WithFields(log.Fields{"teamID": team.TeamID}).Info("found team")
|
logger.New(ctx).WithField("teamID", team.TeamID).Info("found team")
|
||||||
teamProjects, err := r.Repository.GetAllProjectsForTeam(ctx, team.TeamID)
|
teamProjects, err := r.Repository.GetAllProjectsForTeam(ctx, team.TeamID)
|
||||||
if err != sql.ErrNoRows && err != nil {
|
if err != sql.ErrNoRows && err != nil {
|
||||||
log.Info("issue getting team projects")
|
log.Info("issue getting team projects")
|
||||||
return []db.Project{}, nil
|
return []db.Project{}, nil
|
||||||
}
|
}
|
||||||
for _, project := range teamProjects {
|
for _, project := range teamProjects {
|
||||||
log.WithFields(log.Fields{"projectID": project.ProjectID.String()}).Info("adding team project")
|
logger.New(ctx).WithField("projectID", project.ProjectID).Info("adding team project")
|
||||||
projects[project.ProjectID.String()] = project
|
projects[project.ProjectID.String()] = project
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
visibleProjects, err := r.Repository.GetAllVisibleProjectsForUserID(ctx, userID)
|
visibleProjects, err := r.Repository.GetAllVisibleProjectsForUserID(ctx, userID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.WithField("userID", userID).Info("error getting visible projects for user")
|
logger.New(ctx).Info("error getting visible projects for user")
|
||||||
return []db.Project{}, nil
|
return []db.Project{}, nil
|
||||||
}
|
}
|
||||||
for _, project := range visibleProjects {
|
for _, project := range visibleProjects {
|
||||||
log.WithFields(log.Fields{"projectID": project.ProjectID.String()}).Info("found visible project")
|
logger.New(ctx).WithField("projectID", project.ProjectID).Info("found visible project")
|
||||||
if _, ok := projects[project.ProjectID.String()]; !ok {
|
if _, ok := projects[project.ProjectID.String()]; !ok {
|
||||||
log.WithFields(log.Fields{"projectID": project.ProjectID.String()}).Info("adding visible project")
|
logger.New(ctx).WithField("projectID", project.ProjectID).Info("adding visible project")
|
||||||
projects[project.ProjectID.String()] = project
|
projects[project.ProjectID.String()] = project
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
log.WithFields(log.Fields{"projectLength": len(projects)}).Info("making projects")
|
logger.New(ctx).WithField("projectLength", len(projects)).Info("making projects")
|
||||||
allProjects := make([]db.Project, 0, len(projects))
|
allProjects := make([]db.Project, 0, len(projects))
|
||||||
for _, project := range projects {
|
for _, project := range projects {
|
||||||
log.WithFields(log.Fields{"projectID": project.ProjectID.String()}).Info("add project to final list")
|
logger.New(ctx).WithField("projectID", project.ProjectID).Info("adding project to final list")
|
||||||
allProjects = append(allProjects, project)
|
allProjects = append(allProjects, project)
|
||||||
}
|
}
|
||||||
log.Info(allProjects)
|
|
||||||
return allProjects, nil
|
return allProjects, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1112,7 +1110,7 @@ func (r *queryResolver) FindTeam(ctx context.Context, input FindTeam) (*db.Team,
|
|||||||
func (r *queryResolver) Teams(ctx context.Context) ([]db.Team, error) {
|
func (r *queryResolver) Teams(ctx context.Context) ([]db.Team, error) {
|
||||||
userID, orgRole, ok := GetUser(ctx)
|
userID, orgRole, ok := GetUser(ctx)
|
||||||
if !ok {
|
if !ok {
|
||||||
log.Error("userID or orgRole does not exist!")
|
logger.New(ctx).Error("userID or org role does not exist")
|
||||||
return []db.Team{}, errors.New("internal error")
|
return []db.Team{}, errors.New("internal error")
|
||||||
}
|
}
|
||||||
if orgRole == "admin" {
|
if orgRole == "admin" {
|
||||||
@ -1123,7 +1121,7 @@ func (r *queryResolver) Teams(ctx context.Context) ([]db.Team, error) {
|
|||||||
teams := make(map[string]db.Team)
|
teams := make(map[string]db.Team)
|
||||||
adminTeams, err := r.Repository.GetTeamsForUserIDWhereAdmin(ctx, userID)
|
adminTeams, err := r.Repository.GetTeamsForUserIDWhereAdmin(ctx, userID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.WithError(err).Error("error while getting teams for user ID")
|
logger.New(ctx).WithError(err).Error("error while getting teams for user ID")
|
||||||
return []db.Team{}, err
|
return []db.Team{}, err
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1133,19 +1131,19 @@ func (r *queryResolver) Teams(ctx context.Context) ([]db.Team, error) {
|
|||||||
|
|
||||||
visibleProjects, err := r.Repository.GetAllVisibleProjectsForUserID(ctx, userID)
|
visibleProjects, err := r.Repository.GetAllVisibleProjectsForUserID(ctx, userID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.WithField("userID", userID).WithError(err).Error("error while getting visible projects for user ID")
|
logger.New(ctx).WithError(err).Error("error while getting visible projects for user ID")
|
||||||
return []db.Team{}, err
|
return []db.Team{}, err
|
||||||
}
|
}
|
||||||
for _, project := range visibleProjects {
|
for _, project := range visibleProjects {
|
||||||
log.WithFields(log.Fields{"projectID": project.ProjectID.String()}).Info("found visible project")
|
logger.New(ctx).WithField("projectID", project.ProjectID).Info("found visible project")
|
||||||
if _, ok := teams[project.ProjectID.String()]; !ok {
|
if _, ok := teams[project.ProjectID.String()]; !ok {
|
||||||
log.WithFields(log.Fields{"projectID": project.ProjectID.String()}).Info("adding visible project")
|
logger.New(ctx).WithField("projectID", project.ProjectID).Info("adding visible project")
|
||||||
team, err := r.Repository.GetTeamByID(ctx, project.TeamID)
|
team, err := r.Repository.GetTeamByID(ctx, project.TeamID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if err == sql.ErrNoRows {
|
if err == sql.ErrNoRows {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
log.WithField("teamID", project.TeamID).WithError(err).Error("error getting team by id")
|
logger.New(ctx).WithField("teamID", project.TeamID).WithError(err).Error("error getting team by id")
|
||||||
return []db.Team{}, err
|
return []db.Team{}, err
|
||||||
}
|
}
|
||||||
teams[project.TeamID.String()] = team
|
teams[project.TeamID.String()] = team
|
||||||
@ -1173,7 +1171,7 @@ func (r *queryResolver) Me(ctx context.Context) (*MePayload, error) {
|
|||||||
}
|
}
|
||||||
user, err := r.Repository.GetUserAccountByID(ctx, userID)
|
user, err := r.Repository.GetUserAccountByID(ctx, userID)
|
||||||
if err == sql.ErrNoRows {
|
if err == sql.ErrNoRows {
|
||||||
log.WithFields(log.Fields{"userID": userID}).Warning("can not find user for me query")
|
logger.New(ctx).Warning("can not find user for me query")
|
||||||
return &MePayload{}, nil
|
return &MePayload{}, nil
|
||||||
} else if err != nil {
|
} else if err != nil {
|
||||||
return &MePayload{}, err
|
return &MePayload{}, err
|
||||||
@ -1201,7 +1199,7 @@ func (r *queryResolver) Me(ctx context.Context) (*MePayload, error) {
|
|||||||
|
|
||||||
func (r *queryResolver) Notifications(ctx context.Context) ([]db.Notification, error) {
|
func (r *queryResolver) Notifications(ctx context.Context) ([]db.Notification, error) {
|
||||||
userID, ok := GetUserID(ctx)
|
userID, ok := GetUserID(ctx)
|
||||||
log.WithFields(log.Fields{"userID": userID}).Info("fetching notifications")
|
logger.New(ctx).Info("fetching notifications")
|
||||||
if !ok {
|
if !ok {
|
||||||
return []db.Notification{}, errors.New("user id is missing")
|
return []db.Notification{}, errors.New("user id is missing")
|
||||||
}
|
}
|
||||||
@ -1215,7 +1213,7 @@ func (r *queryResolver) Notifications(ctx context.Context) ([]db.Notification, e
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (r *queryResolver) SearchMembers(ctx context.Context, input MemberSearchFilter) ([]MemberSearchResult, error) {
|
func (r *queryResolver) SearchMembers(ctx context.Context, input MemberSearchFilter) ([]MemberSearchResult, error) {
|
||||||
availableMembers, err := r.Repository.GetMemberData(ctx)
|
availableMembers, err := r.Repository.GetMemberData(ctx, *input.ProjectID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return []MemberSearchResult{}, err
|
return []MemberSearchResult{}, err
|
||||||
}
|
}
|
||||||
@ -1233,7 +1231,7 @@ func (r *queryResolver) SearchMembers(ctx context.Context, input MemberSearchFil
|
|||||||
memberList := map[uuid.UUID]bool{}
|
memberList := map[uuid.UUID]bool{}
|
||||||
for _, rank := range rankedList {
|
for _, rank := range rankedList {
|
||||||
if _, ok := memberList[masterList[rank.Target]]; !ok {
|
if _, ok := memberList[masterList[rank.Target]]; !ok {
|
||||||
log.WithFields(log.Fields{"source": rank.Source, "target": rank.Target}).Info("searching")
|
logger.New(ctx).WithFields(log.Fields{"source": rank.Source, "target": rank.Target}).Info("searching")
|
||||||
userID := masterList[rank.Target]
|
userID := masterList[rank.Target]
|
||||||
user, err := r.Repository.GetUserAccountByID(ctx, userID)
|
user, err := r.Repository.GetUserAccountByID(ctx, userID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -1313,7 +1311,7 @@ func (r *taskResolver) Assigned(ctx context.Context, obj *db.Task) ([]Member, er
|
|||||||
if err == sql.ErrNoRows {
|
if err == sql.ErrNoRows {
|
||||||
role = db.Role{Code: "owner", Name: "Owner"}
|
role = db.Role{Code: "owner", Name: "Owner"}
|
||||||
} else {
|
} else {
|
||||||
log.WithFields(log.Fields{"userID": user.UserID}).WithError(err).Error("get role for project member")
|
logger.New(ctx).WithError(err).Error("get role for project member")
|
||||||
return taskMembers, err
|
return taskMembers, err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1407,14 +1405,14 @@ func (r *teamResolver) Members(ctx context.Context, obj *db.Team) ([]Member, err
|
|||||||
|
|
||||||
teamMembers, err := r.Repository.GetTeamMembersForTeamID(ctx, obj.TeamID)
|
teamMembers, err := r.Repository.GetTeamMembersForTeamID(ctx, obj.TeamID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.WithError(err).Error("get project members for project id")
|
logger.New(ctx).Error("get project members for project id")
|
||||||
return members, err
|
return members, err
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, teamMember := range teamMembers {
|
for _, teamMember := range teamMembers {
|
||||||
user, err := r.Repository.GetUserAccountByID(ctx, teamMember.UserID)
|
user, err := r.Repository.GetUserAccountByID(ctx, teamMember.UserID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.WithError(err).Error("get user account by ID")
|
logger.New(ctx).WithError(err).Error("get user account by ID")
|
||||||
return members, err
|
return members, err
|
||||||
}
|
}
|
||||||
var url *string
|
var url *string
|
||||||
@ -1423,7 +1421,7 @@ func (r *teamResolver) Members(ctx context.Context, obj *db.Team) ([]Member, err
|
|||||||
}
|
}
|
||||||
role, err := r.Repository.GetRoleForTeamMember(ctx, db.GetRoleForTeamMemberParams{UserID: user.UserID, TeamID: obj.TeamID})
|
role, err := r.Repository.GetRoleForTeamMember(ctx, db.GetRoleForTeamMemberParams{UserID: user.UserID, TeamID: obj.TeamID})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.WithError(err).Error("get role for projet member by user ID")
|
logger.New(ctx).WithError(err).Error("get role for projet member by user ID")
|
||||||
return members, err
|
return members, err
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1451,8 +1449,7 @@ func (r *userAccountResolver) ID(ctx context.Context, obj *db.UserAccount) (uuid
|
|||||||
func (r *userAccountResolver) Role(ctx context.Context, obj *db.UserAccount) (*db.Role, error) {
|
func (r *userAccountResolver) Role(ctx context.Context, obj *db.UserAccount) (*db.Role, error) {
|
||||||
role, err := r.Repository.GetRoleForUserID(ctx, obj.UserID)
|
role, err := r.Repository.GetRoleForUserID(ctx, obj.UserID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Info("beep!")
|
logger.New(ctx).WithError(err).Error("get role for user id")
|
||||||
log.WithError(err).Error("get role for user id")
|
|
||||||
return &db.Role{}, err
|
return &db.Role{}, err
|
||||||
}
|
}
|
||||||
return &db.Role{Code: role.Code, Name: role.Name}, nil
|
return &db.Role{Code: role.Code, Name: role.Name}, nil
|
||||||
|
@ -1,22 +1,26 @@
|
|||||||
extend type Mutation {
|
extend type Mutation {
|
||||||
# TODO: rename to inviteProjectMember
|
inviteProjectMembers(input: InviteProjectMembers!):
|
||||||
inviteProjectMember(input: InviteProjectMember!):
|
InviteProjectMembersPayload! @hasRole(roles: [ADMIN], level: PROJECT, type: PROJECT)
|
||||||
InviteProjectMemberPayload! @hasRole(roles: [ADMIN], level: PROJECT, type: PROJECT)
|
|
||||||
deleteProjectMember(input: DeleteProjectMember!):
|
deleteProjectMember(input: DeleteProjectMember!):
|
||||||
DeleteProjectMemberPayload! @hasRole(roles: [ADMIN], level: PROJECT, type: PROJECT)
|
DeleteProjectMemberPayload! @hasRole(roles: [ADMIN], level: PROJECT, type: PROJECT)
|
||||||
updateProjectMemberRole(input: UpdateProjectMemberRole!):
|
updateProjectMemberRole(input: UpdateProjectMemberRole!):
|
||||||
UpdateProjectMemberRolePayload! @hasRole(roles: [ADMIN], level: PROJECT, type: PROJECT)
|
UpdateProjectMemberRolePayload! @hasRole(roles: [ADMIN], level: PROJECT, type: PROJECT)
|
||||||
}
|
}
|
||||||
|
|
||||||
input InviteProjectMember {
|
input MemberInvite {
|
||||||
projectID: UUID!
|
|
||||||
userID: UUID
|
userID: UUID
|
||||||
email: String
|
email: String
|
||||||
}
|
}
|
||||||
|
|
||||||
type InviteProjectMemberPayload {
|
input InviteProjectMembers {
|
||||||
|
projectID: UUID!
|
||||||
|
members: [MemberInvite!]!
|
||||||
|
}
|
||||||
|
|
||||||
|
type InviteProjectMembersPayload {
|
||||||
ok: Boolean!
|
ok: Boolean!
|
||||||
member: Member!
|
projectID: UUID!
|
||||||
|
members: [Member!]!
|
||||||
}
|
}
|
||||||
|
|
||||||
input DeleteProjectMember {
|
input DeleteProjectMember {
|
||||||
|
@ -1,89 +1,21 @@
|
|||||||
package logger
|
package logger
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"context"
|
||||||
"net/http"
|
|
||||||
"time"
|
|
||||||
|
|
||||||
"github.com/go-chi/chi/middleware"
|
"github.com/google/uuid"
|
||||||
"github.com/sirupsen/logrus"
|
"github.com/jordanknott/taskcafe/internal/utils"
|
||||||
|
log "github.com/sirupsen/logrus"
|
||||||
)
|
)
|
||||||
|
|
||||||
// NewStructuredLogger creates a new logger for chi router
|
// New returns a log entry with the reqID and userID fields populated if they exist
|
||||||
func NewStructuredLogger(logger *logrus.Logger) func(next http.Handler) http.Handler {
|
func New(ctx context.Context) *log.Entry {
|
||||||
return middleware.RequestLogger(&StructuredLogger{logger})
|
entry := log.NewEntry(log.StandardLogger())
|
||||||
}
|
if reqID, ok := ctx.Value(utils.ReqIDKey).(uuid.UUID); ok {
|
||||||
|
entry = entry.WithField("reqID", reqID)
|
||||||
// StructuredLogger is a logger for chi router
|
|
||||||
type StructuredLogger struct {
|
|
||||||
Logger *logrus.Logger
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewLogEntry creates a new log entry for the given HTTP request
|
|
||||||
func (l *StructuredLogger) NewLogEntry(r *http.Request) middleware.LogEntry {
|
|
||||||
entry := &StructuredLoggerEntry{Logger: logrus.NewEntry(l.Logger)}
|
|
||||||
logFields := logrus.Fields{}
|
|
||||||
|
|
||||||
if reqID := middleware.GetReqID(r.Context()); reqID != "" {
|
|
||||||
logFields["req_id"] = reqID
|
|
||||||
}
|
}
|
||||||
|
if userID, ok := ctx.Value(utils.UserIDKey).(uuid.UUID); ok {
|
||||||
scheme := "http"
|
entry = entry.WithField("userID", userID)
|
||||||
if r.TLS != nil {
|
|
||||||
scheme = "https"
|
|
||||||
}
|
}
|
||||||
logFields["http_scheme"] = scheme
|
|
||||||
logFields["http_proto"] = r.Proto
|
|
||||||
logFields["http_method"] = r.Method
|
|
||||||
|
|
||||||
logFields["remote_addr"] = r.RemoteAddr
|
|
||||||
logFields["user_agent"] = r.UserAgent()
|
|
||||||
|
|
||||||
logFields["uri"] = fmt.Sprintf("%s://%s%s", scheme, r.Host, r.RequestURI)
|
|
||||||
|
|
||||||
entry.Logger = entry.Logger.WithFields(logFields)
|
|
||||||
|
|
||||||
return entry
|
return entry
|
||||||
}
|
}
|
||||||
|
|
||||||
// StructuredLoggerEntry is a log entry will all relevant information about a specific http request
|
|
||||||
type StructuredLoggerEntry struct {
|
|
||||||
Logger logrus.FieldLogger
|
|
||||||
}
|
|
||||||
|
|
||||||
// Write logs information about http request response body
|
|
||||||
func (l *StructuredLoggerEntry) Write(status, bytes int, elapsed time.Duration) {
|
|
||||||
l.Logger = l.Logger.WithFields(logrus.Fields{
|
|
||||||
"resp_status": status, "resp_bytes_length": bytes,
|
|
||||||
"resp_elapsed_ms": float64(elapsed.Nanoseconds()) / 1000000.0,
|
|
||||||
})
|
|
||||||
l.Logger.Debugln("request complete")
|
|
||||||
}
|
|
||||||
|
|
||||||
// Panic logs if the request panics
|
|
||||||
func (l *StructuredLoggerEntry) Panic(v interface{}, stack []byte) {
|
|
||||||
l.Logger = l.Logger.WithFields(logrus.Fields{
|
|
||||||
"stack": string(stack),
|
|
||||||
"panic": fmt.Sprintf("%+v", v),
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetLogEntry helper function for getting log entry for request
|
|
||||||
func GetLogEntry(r *http.Request) logrus.FieldLogger {
|
|
||||||
entry := middleware.GetLogEntry(r).(*StructuredLoggerEntry)
|
|
||||||
return entry.Logger
|
|
||||||
}
|
|
||||||
|
|
||||||
// LogEntrySetField sets a key's value
|
|
||||||
func LogEntrySetField(r *http.Request, key string, value interface{}) {
|
|
||||||
if entry, ok := r.Context().Value(middleware.LogEntryCtxKey).(*StructuredLoggerEntry); ok {
|
|
||||||
entry.Logger = entry.Logger.WithField(key, value)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// LogEntrySetFields sets the log entry's fields
|
|
||||||
func LogEntrySetFields(r *http.Request, fields map[string]interface{}) {
|
|
||||||
if entry, ok := r.Context().Value(middleware.LogEntryCtxKey).(*StructuredLoggerEntry); ok {
|
|
||||||
entry.Logger = entry.Logger.WithFields(fields)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
89
internal/logger/route_logger.go
Normal file
89
internal/logger/route_logger.go
Normal file
@ -0,0 +1,89 @@
|
|||||||
|
package logger
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"net/http"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/go-chi/chi/middleware"
|
||||||
|
"github.com/sirupsen/logrus"
|
||||||
|
)
|
||||||
|
|
||||||
|
// NewStructuredLogger creates a new logger for chi router
|
||||||
|
func NewStructuredLogger(logger *logrus.Logger) func(next http.Handler) http.Handler {
|
||||||
|
return middleware.RequestLogger(&StructuredLogger{logger})
|
||||||
|
}
|
||||||
|
|
||||||
|
// StructuredLogger is a logger for chi router
|
||||||
|
type StructuredLogger struct {
|
||||||
|
Logger *logrus.Logger
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewLogEntry creates a new log entry for the given HTTP request
|
||||||
|
func (l *StructuredLogger) NewLogEntry(r *http.Request) middleware.LogEntry {
|
||||||
|
entry := &StructuredLoggerEntry{Logger: logrus.NewEntry(l.Logger)}
|
||||||
|
logFields := logrus.Fields{}
|
||||||
|
|
||||||
|
if reqID := middleware.GetReqID(r.Context()); reqID != "" {
|
||||||
|
logFields["req_id"] = reqID
|
||||||
|
}
|
||||||
|
|
||||||
|
scheme := "http"
|
||||||
|
if r.TLS != nil {
|
||||||
|
scheme = "https"
|
||||||
|
}
|
||||||
|
logFields["http_scheme"] = scheme
|
||||||
|
logFields["http_proto"] = r.Proto
|
||||||
|
logFields["http_method"] = r.Method
|
||||||
|
|
||||||
|
logFields["remote_addr"] = r.RemoteAddr
|
||||||
|
logFields["user_agent"] = r.UserAgent()
|
||||||
|
|
||||||
|
logFields["uri"] = fmt.Sprintf("%s://%s%s", scheme, r.Host, r.RequestURI)
|
||||||
|
|
||||||
|
entry.Logger = entry.Logger.WithFields(logFields)
|
||||||
|
|
||||||
|
return entry
|
||||||
|
}
|
||||||
|
|
||||||
|
// StructuredLoggerEntry is a log entry will all relevant information about a specific http request
|
||||||
|
type StructuredLoggerEntry struct {
|
||||||
|
Logger logrus.FieldLogger
|
||||||
|
}
|
||||||
|
|
||||||
|
// Write logs information about http request response body
|
||||||
|
func (l *StructuredLoggerEntry) Write(status, bytes int, elapsed time.Duration) {
|
||||||
|
l.Logger = l.Logger.WithFields(logrus.Fields{
|
||||||
|
"resp_status": status, "resp_bytes_length": bytes,
|
||||||
|
"resp_elapsed_ms": float64(elapsed.Nanoseconds()) / 1000000.0,
|
||||||
|
})
|
||||||
|
l.Logger.Debugln("request complete")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Panic logs if the request panics
|
||||||
|
func (l *StructuredLoggerEntry) Panic(v interface{}, stack []byte) {
|
||||||
|
l.Logger = l.Logger.WithFields(logrus.Fields{
|
||||||
|
"stack": string(stack),
|
||||||
|
"panic": fmt.Sprintf("%+v", v),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetLogEntry helper function for getting log entry for request
|
||||||
|
func GetLogEntry(r *http.Request) logrus.FieldLogger {
|
||||||
|
entry := middleware.GetLogEntry(r).(*StructuredLoggerEntry)
|
||||||
|
return entry.Logger
|
||||||
|
}
|
||||||
|
|
||||||
|
// LogEntrySetField sets a key's value
|
||||||
|
func LogEntrySetField(r *http.Request, key string, value interface{}) {
|
||||||
|
if entry, ok := r.Context().Value(middleware.LogEntryCtxKey).(*StructuredLoggerEntry); ok {
|
||||||
|
entry.Logger = entry.Logger.WithField(key, value)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// LogEntrySetFields sets the log entry's fields
|
||||||
|
func LogEntrySetFields(r *http.Request, fields map[string]interface{}) {
|
||||||
|
if entry, ok := r.Context().Value(middleware.LogEntryCtxKey).(*StructuredLoggerEntry); ok {
|
||||||
|
entry.Logger = entry.Logger.WithFields(fields)
|
||||||
|
}
|
||||||
|
}
|
@ -19,6 +19,7 @@ type AuthenticationMiddleware struct {
|
|||||||
// Middleware returns the middleware handler
|
// Middleware returns the middleware handler
|
||||||
func (m *AuthenticationMiddleware) Middleware(next http.Handler) http.Handler {
|
func (m *AuthenticationMiddleware) Middleware(next http.Handler) http.Handler {
|
||||||
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
requestID := uuid.New()
|
||||||
bearerTokenRaw := r.Header.Get("Authorization")
|
bearerTokenRaw := r.Header.Get("Authorization")
|
||||||
splitToken := strings.Split(bearerTokenRaw, "Bearer")
|
splitToken := strings.Split(bearerTokenRaw, "Bearer")
|
||||||
if len(splitToken) != 2 {
|
if len(splitToken) != 2 {
|
||||||
@ -61,6 +62,7 @@ func (m *AuthenticationMiddleware) Middleware(next http.Handler) http.Handler {
|
|||||||
ctx := context.WithValue(r.Context(), utils.UserIDKey, userID)
|
ctx := context.WithValue(r.Context(), utils.UserIDKey, userID)
|
||||||
ctx = context.WithValue(ctx, utils.RestrictedModeKey, accessClaims.Restricted)
|
ctx = context.WithValue(ctx, utils.RestrictedModeKey, accessClaims.Restricted)
|
||||||
ctx = context.WithValue(ctx, utils.OrgRoleKey, accessClaims.OrgRole)
|
ctx = context.WithValue(ctx, utils.OrgRoleKey, accessClaims.OrgRole)
|
||||||
|
ctx = context.WithValue(ctx, utils.ReqIDKey, requestID)
|
||||||
|
|
||||||
next.ServeHTTP(w, r.WithContext(ctx))
|
next.ServeHTTP(w, r.WithContext(ctx))
|
||||||
})
|
})
|
||||||
|
@ -6,6 +6,8 @@ type ContextKey string
|
|||||||
const (
|
const (
|
||||||
// UserIDKey is the key for the user id of the authenticated user
|
// UserIDKey is the key for the user id of the authenticated user
|
||||||
UserIDKey ContextKey = "userID"
|
UserIDKey ContextKey = "userID"
|
||||||
|
// ReqIDKey is the unique ID key for current request
|
||||||
|
ReqIDKey ContextKey = "reqID"
|
||||||
//RestrictedModeKey is the key for whether the authenticated user only has access to install route
|
//RestrictedModeKey is the key for whether the authenticated user only has access to install route
|
||||||
RestrictedModeKey ContextKey = "restricted_mode"
|
RestrictedModeKey ContextKey = "restricted_mode"
|
||||||
// OrgRoleKey is the key for the organization role code of the authenticated user
|
// OrgRoleKey is the key for the organization role code of the authenticated user
|
||||||
|
Loading…
Reference in New Issue
Block a user