feat: batch commit delete/move (#1016)
* feat: batch delete/move commits * fix: ts linter issue
This commit is contained in:
committed by
GitHub
parent
1a0a72f73b
commit
05f11a26da
@@ -16,6 +16,7 @@
|
||||
"prettier:check": "prettier --check .",
|
||||
"prettier:fix": "prettier --write .",
|
||||
"circleci:check": "circleci config validate ./.circleci/config.yml",
|
||||
"dev:docker": "docker-compose -f ./docker-compose-deps.yml",
|
||||
"dev:docker:up": "docker-compose -f ./docker-compose-deps.yml up -d",
|
||||
"dev:docker:down": "docker-compose -f ./docker-compose-deps.yml down",
|
||||
"dev": "yarn workspaces foreach -piv -j unlimited run dev",
|
||||
|
||||
@@ -2,6 +2,7 @@ query StreamWithBranch($streamId: String!, $branchName: String!, $cursor: String
|
||||
stream(id: $streamId) {
|
||||
id
|
||||
name
|
||||
role
|
||||
branch(name: $branchName) {
|
||||
id
|
||||
name
|
||||
|
||||
@@ -0,0 +1,26 @@
|
||||
import { gql } from '@apollo/client/core'
|
||||
|
||||
export const streamBranchesSelectorQuery = gql`
|
||||
query StreamBranchesSelector($streamId: String!) {
|
||||
stream(id: $streamId) {
|
||||
id
|
||||
branches(limit: 100) {
|
||||
items {
|
||||
name
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
`
|
||||
|
||||
export const moveCommitsMutation = gql`
|
||||
mutation MoveCommits($input: CommitsMoveInput!) {
|
||||
commitsMove(input: $input)
|
||||
}
|
||||
`
|
||||
|
||||
export const deleteCommitsMutation = gql`
|
||||
mutation DeleteCommits($input: CommitsDeleteInput!) {
|
||||
commitsDelete(input: $input)
|
||||
}
|
||||
`
|
||||
@@ -166,7 +166,7 @@ export type BranchCommitsArgs = {
|
||||
export type BranchCollection = {
|
||||
__typename?: 'BranchCollection';
|
||||
cursor?: Maybe<Scalars['String']>;
|
||||
items?: Maybe<Array<Maybe<Branch>>>;
|
||||
items?: Maybe<Array<Branch>>;
|
||||
totalCount: Scalars['Int'];
|
||||
};
|
||||
|
||||
@@ -346,8 +346,6 @@ export type CommitCreateInput = {
|
||||
message?: InputMaybe<Scalars['String']>;
|
||||
objectId: Scalars['String'];
|
||||
parents?: InputMaybe<Array<InputMaybe<Scalars['String']>>>;
|
||||
/** **DEPRECATED** Use the `parents` field. */
|
||||
previousCommitIds?: InputMaybe<Array<InputMaybe<Scalars['String']>>>;
|
||||
sourceApplication?: InputMaybe<Scalars['String']>;
|
||||
streamId: Scalars['String'];
|
||||
totalChildrenCount?: InputMaybe<Scalars['Int']>;
|
||||
@@ -388,6 +386,15 @@ export type CommitUpdateInput = {
|
||||
streamId: Scalars['String'];
|
||||
};
|
||||
|
||||
export type CommitsDeleteInput = {
|
||||
commitIds: Array<Scalars['String']>;
|
||||
};
|
||||
|
||||
export type CommitsMoveInput = {
|
||||
commitIds: Array<Scalars['String']>;
|
||||
targetBranch: Scalars['String'];
|
||||
};
|
||||
|
||||
export enum DiscoverableStreamsSortType {
|
||||
CreatedDate = 'CREATED_DATE',
|
||||
FavoritesCount = 'FAVORITES_COUNT'
|
||||
@@ -467,6 +474,10 @@ export type Mutation = {
|
||||
commitDelete: Scalars['Boolean'];
|
||||
commitReceive: Scalars['Boolean'];
|
||||
commitUpdate: Scalars['Boolean'];
|
||||
/** Delete a batch of commits */
|
||||
commitsDelete: Scalars['Boolean'];
|
||||
/** Move a batch of commits to a new branch */
|
||||
commitsMove: Scalars['Boolean'];
|
||||
/** Delete a pending invite */
|
||||
inviteDelete: Scalars['Boolean'];
|
||||
/** Re-send a pending invite */
|
||||
@@ -621,6 +632,16 @@ export type MutationCommitUpdateArgs = {
|
||||
};
|
||||
|
||||
|
||||
export type MutationCommitsDeleteArgs = {
|
||||
input: CommitsDeleteInput;
|
||||
};
|
||||
|
||||
|
||||
export type MutationCommitsMoveArgs = {
|
||||
input: CommitsMoveInput;
|
||||
};
|
||||
|
||||
|
||||
export type MutationInviteDeleteArgs = {
|
||||
inviteId: Scalars['String'];
|
||||
};
|
||||
@@ -1678,7 +1699,7 @@ export type StreamWithBranchQueryVariables = Exact<{
|
||||
}>;
|
||||
|
||||
|
||||
export type StreamWithBranchQuery = { __typename?: 'Query', stream?: { __typename?: 'Stream', id: string, name: string, branch?: { __typename?: 'Branch', id: string, name: string, description?: string | null, commits?: { __typename?: 'CommitCollection', totalCount: number, cursor?: string | null, items?: Array<{ __typename?: 'Commit', id: string, authorName?: string | null, authorId?: string | null, authorAvatar?: string | null, sourceApplication?: string | null, message?: string | null, referencedObject: string, createdAt?: string | null, commentCount: number } | null> | null } | null } | null } | null };
|
||||
export type StreamWithBranchQuery = { __typename?: 'Query', stream?: { __typename?: 'Stream', id: string, name: string, role?: string | null, branch?: { __typename?: 'Branch', id: string, name: string, description?: string | null, commits?: { __typename?: 'CommitCollection', totalCount: number, cursor?: string | null, items?: Array<{ __typename?: 'Commit', id: string, authorName?: string | null, authorId?: string | null, authorAvatar?: string | null, sourceApplication?: string | null, message?: string | null, referencedObject: string, createdAt?: string | null, commentCount: number } | null> | null } | null } | null } | null };
|
||||
|
||||
export type BranchCreatedSubscriptionVariables = Exact<{
|
||||
streamId: Scalars['String'];
|
||||
@@ -1697,6 +1718,27 @@ export type StreamCommitQueryQueryVariables = Exact<{
|
||||
|
||||
export type StreamCommitQueryQuery = { __typename?: 'Query', stream?: { __typename?: 'Stream', id: string, name: string, role?: string | null, commit?: { __typename?: 'Commit', id: string, message?: string | null, referencedObject: string, authorName?: string | null, authorId?: string | null, authorAvatar?: string | null, createdAt?: string | null, branchName?: string | null, sourceApplication?: string | null } | null } | null };
|
||||
|
||||
export type StreamBranchesSelectorQueryVariables = Exact<{
|
||||
streamId: Scalars['String'];
|
||||
}>;
|
||||
|
||||
|
||||
export type StreamBranchesSelectorQuery = { __typename?: 'Query', stream?: { __typename?: 'Stream', id: string, branches?: { __typename?: 'BranchCollection', items?: Array<{ __typename?: 'Branch', name: string }> | null } | null } | null };
|
||||
|
||||
export type MoveCommitsMutationVariables = Exact<{
|
||||
input: CommitsMoveInput;
|
||||
}>;
|
||||
|
||||
|
||||
export type MoveCommitsMutation = { __typename?: 'Mutation', commitsMove: boolean };
|
||||
|
||||
export type DeleteCommitsMutationVariables = Exact<{
|
||||
input: CommitsDeleteInput;
|
||||
}>;
|
||||
|
||||
|
||||
export type DeleteCommitsMutation = { __typename?: 'Mutation', commitsDelete: boolean };
|
||||
|
||||
export type BasicStreamAccessRequestFieldsFragment = { __typename?: 'StreamAccessRequest', id: string, streamId: string, createdAt: string };
|
||||
|
||||
export type FullStreamAccessRequestFieldsFragment = { __typename?: 'StreamAccessRequest', id: string, streamId: string, createdAt: string, requester: { __typename?: 'LimitedUser', id: string, name?: string | null, bio?: string | null, company?: string | null, avatar?: string | null, verified?: boolean | null } };
|
||||
@@ -2214,6 +2256,7 @@ export const StreamWithBranch = gql`
|
||||
stream(id: $streamId) {
|
||||
id
|
||||
name
|
||||
role
|
||||
branch(name: $branchName) {
|
||||
id
|
||||
name
|
||||
@@ -2262,6 +2305,28 @@ export const StreamCommitQuery = gql`
|
||||
}
|
||||
}
|
||||
`;
|
||||
export const StreamBranchesSelector = gql`
|
||||
query StreamBranchesSelector($streamId: String!) {
|
||||
stream(id: $streamId) {
|
||||
id
|
||||
branches(limit: 100) {
|
||||
items {
|
||||
name
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
`;
|
||||
export const MoveCommits = gql`
|
||||
mutation MoveCommits($input: CommitsMoveInput!) {
|
||||
commitsMove(input: $input)
|
||||
}
|
||||
`;
|
||||
export const DeleteCommits = gql`
|
||||
mutation DeleteCommits($input: CommitsDeleteInput!) {
|
||||
commitsDelete(input: $input)
|
||||
}
|
||||
`;
|
||||
export const StreamInvite = gql`
|
||||
query StreamInvite($streamId: String!, $token: String) {
|
||||
streamInvite(streamId: $streamId, token: $token) {
|
||||
@@ -2765,9 +2830,12 @@ export const CommonUserFieldsFragmentDoc = {"kind":"Document","definitions":[{"k
|
||||
export const GetStreamAccessRequestDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"GetStreamAccessRequest"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"streamId"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"streamAccessRequest"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"streamId"},"value":{"kind":"Variable","name":{"kind":"Name","value":"streamId"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"BasicStreamAccessRequestFields"}}]}}]}},...BasicStreamAccessRequestFieldsFragmentDoc.definitions]} as unknown as DocumentNode<GetStreamAccessRequestQuery, GetStreamAccessRequestQueryVariables>;
|
||||
export const CreateStreamAccessRequestDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"mutation","name":{"kind":"Name","value":"CreateStreamAccessRequest"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"streamId"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"streamAccessRequestCreate"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"streamId"},"value":{"kind":"Variable","name":{"kind":"Name","value":"streamId"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"BasicStreamAccessRequestFields"}}]}}]}},...BasicStreamAccessRequestFieldsFragmentDoc.definitions]} as unknown as DocumentNode<CreateStreamAccessRequestMutation, CreateStreamAccessRequestMutationVariables>;
|
||||
export const UseStreamAccessRequestDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"mutation","name":{"kind":"Name","value":"UseStreamAccessRequest"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"requestId"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"accept"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"Boolean"}}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"role"}},"type":{"kind":"NamedType","name":{"kind":"Name","value":"StreamRole"}},"defaultValue":{"kind":"EnumValue","value":"STREAM_CONTRIBUTOR"}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"streamAccessRequestUse"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"requestId"},"value":{"kind":"Variable","name":{"kind":"Name","value":"requestId"}}},{"kind":"Argument","name":{"kind":"Name","value":"accept"},"value":{"kind":"Variable","name":{"kind":"Name","value":"accept"}}},{"kind":"Argument","name":{"kind":"Name","value":"role"},"value":{"kind":"Variable","name":{"kind":"Name","value":"role"}}}]}]}}]} as unknown as DocumentNode<UseStreamAccessRequestMutation, UseStreamAccessRequestMutationVariables>;
|
||||
export const StreamWithBranchDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"StreamWithBranch"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"streamId"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"branchName"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"cursor"}},"type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"stream"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"id"},"value":{"kind":"Variable","name":{"kind":"Name","value":"streamId"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"branch"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"name"},"value":{"kind":"Variable","name":{"kind":"Name","value":"branchName"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"description"}},{"kind":"Field","name":{"kind":"Name","value":"commits"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"cursor"},"value":{"kind":"Variable","name":{"kind":"Name","value":"cursor"}}},{"kind":"Argument","name":{"kind":"Name","value":"limit"},"value":{"kind":"IntValue","value":"4"}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"totalCount"}},{"kind":"Field","name":{"kind":"Name","value":"cursor"}},{"kind":"Field","name":{"kind":"Name","value":"items"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"authorName"}},{"kind":"Field","name":{"kind":"Name","value":"authorId"}},{"kind":"Field","name":{"kind":"Name","value":"authorAvatar"}},{"kind":"Field","name":{"kind":"Name","value":"sourceApplication"}},{"kind":"Field","name":{"kind":"Name","value":"message"}},{"kind":"Field","name":{"kind":"Name","value":"referencedObject"}},{"kind":"Field","name":{"kind":"Name","value":"createdAt"}},{"kind":"Field","name":{"kind":"Name","value":"commentCount"}}]}}]}}]}}]}}]}}]} as unknown as DocumentNode<StreamWithBranchQuery, StreamWithBranchQueryVariables>;
|
||||
export const StreamWithBranchDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"StreamWithBranch"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"streamId"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"branchName"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"cursor"}},"type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"stream"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"id"},"value":{"kind":"Variable","name":{"kind":"Name","value":"streamId"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"role"}},{"kind":"Field","name":{"kind":"Name","value":"branch"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"name"},"value":{"kind":"Variable","name":{"kind":"Name","value":"branchName"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"description"}},{"kind":"Field","name":{"kind":"Name","value":"commits"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"cursor"},"value":{"kind":"Variable","name":{"kind":"Name","value":"cursor"}}},{"kind":"Argument","name":{"kind":"Name","value":"limit"},"value":{"kind":"IntValue","value":"4"}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"totalCount"}},{"kind":"Field","name":{"kind":"Name","value":"cursor"}},{"kind":"Field","name":{"kind":"Name","value":"items"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"authorName"}},{"kind":"Field","name":{"kind":"Name","value":"authorId"}},{"kind":"Field","name":{"kind":"Name","value":"authorAvatar"}},{"kind":"Field","name":{"kind":"Name","value":"sourceApplication"}},{"kind":"Field","name":{"kind":"Name","value":"message"}},{"kind":"Field","name":{"kind":"Name","value":"referencedObject"}},{"kind":"Field","name":{"kind":"Name","value":"createdAt"}},{"kind":"Field","name":{"kind":"Name","value":"commentCount"}}]}}]}}]}}]}}]}}]} as unknown as DocumentNode<StreamWithBranchQuery, StreamWithBranchQueryVariables>;
|
||||
export const BranchCreatedDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"subscription","name":{"kind":"Name","value":"BranchCreated"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"streamId"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"branchCreated"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"streamId"},"value":{"kind":"Variable","name":{"kind":"Name","value":"streamId"}}}]}]}}]} as unknown as DocumentNode<BranchCreatedSubscription, BranchCreatedSubscriptionVariables>;
|
||||
export const StreamCommitQueryDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"StreamCommitQuery"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"streamId"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"id"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"stream"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"id"},"value":{"kind":"Variable","name":{"kind":"Name","value":"streamId"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"role"}},{"kind":"Field","name":{"kind":"Name","value":"commit"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"id"},"value":{"kind":"Variable","name":{"kind":"Name","value":"id"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"message"}},{"kind":"Field","name":{"kind":"Name","value":"referencedObject"}},{"kind":"Field","name":{"kind":"Name","value":"authorName"}},{"kind":"Field","name":{"kind":"Name","value":"authorId"}},{"kind":"Field","name":{"kind":"Name","value":"authorAvatar"}},{"kind":"Field","name":{"kind":"Name","value":"createdAt"}},{"kind":"Field","name":{"kind":"Name","value":"branchName"}},{"kind":"Field","name":{"kind":"Name","value":"sourceApplication"}}]}}]}}]}}]} as unknown as DocumentNode<StreamCommitQueryQuery, StreamCommitQueryQueryVariables>;
|
||||
export const StreamBranchesSelectorDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"StreamBranchesSelector"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"streamId"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"stream"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"id"},"value":{"kind":"Variable","name":{"kind":"Name","value":"streamId"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"branches"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"limit"},"value":{"kind":"IntValue","value":"100"}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"items"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"name"}}]}}]}}]}}]}}]} as unknown as DocumentNode<StreamBranchesSelectorQuery, StreamBranchesSelectorQueryVariables>;
|
||||
export const MoveCommitsDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"mutation","name":{"kind":"Name","value":"MoveCommits"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"input"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"CommitsMoveInput"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"commitsMove"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"input"},"value":{"kind":"Variable","name":{"kind":"Name","value":"input"}}}]}]}}]} as unknown as DocumentNode<MoveCommitsMutation, MoveCommitsMutationVariables>;
|
||||
export const DeleteCommitsDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"mutation","name":{"kind":"Name","value":"DeleteCommits"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"input"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"CommitsDeleteInput"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"commitsDelete"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"input"},"value":{"kind":"Variable","name":{"kind":"Name","value":"input"}}}]}]}}]} as unknown as DocumentNode<DeleteCommitsMutation, DeleteCommitsMutationVariables>;
|
||||
export const StreamInviteDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"StreamInvite"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"streamId"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"token"}},"type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"streamInvite"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"streamId"},"value":{"kind":"Variable","name":{"kind":"Name","value":"streamId"}}},{"kind":"Argument","name":{"kind":"Name","value":"token"},"value":{"kind":"Variable","name":{"kind":"Name","value":"token"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"UsersOwnInviteFields"}}]}}]}},...UsersOwnInviteFieldsFragmentDoc.definitions,...LimitedUserFieldsFragmentDoc.definitions]} as unknown as DocumentNode<StreamInviteQuery, StreamInviteQueryVariables>;
|
||||
export const UserStreamInvitesDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"UserStreamInvites"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"streamInvites"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"UsersOwnInviteFields"}}]}}]}},...UsersOwnInviteFieldsFragmentDoc.definitions,...LimitedUserFieldsFragmentDoc.definitions]} as unknown as DocumentNode<UserStreamInvitesQuery, UserStreamInvitesQueryVariables>;
|
||||
export const UseStreamInviteDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"mutation","name":{"kind":"Name","value":"UseStreamInvite"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"accept"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"Boolean"}}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"streamId"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"token"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"streamInviteUse"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"accept"},"value":{"kind":"Variable","name":{"kind":"Name","value":"accept"}}},{"kind":"Argument","name":{"kind":"Name","value":"streamId"},"value":{"kind":"Variable","name":{"kind":"Name","value":"streamId"}}},{"kind":"Argument","name":{"kind":"Name","value":"token"},"value":{"kind":"Variable","name":{"kind":"Name","value":"token"}}}]}]}}]} as unknown as DocumentNode<UseStreamInviteMutation, UseStreamInviteMutationVariables>;
|
||||
|
||||
@@ -3,9 +3,7 @@
|
||||
<v-card
|
||||
:class="`rounded-lg overflow-hidden`"
|
||||
:elevation="hover ? 10 : 1"
|
||||
:style="`transition: all 0.2s ease-in-out; ${
|
||||
highlight ? 'outline: 0.2rem solid #047EFB;' : ''
|
||||
}`"
|
||||
:style="`${highlighted ? 'outline: 0.2rem solid #047EFB;' : ''}`"
|
||||
>
|
||||
<router-link :to="`/streams/${streamId}/commits/${commit.id}`">
|
||||
<preview-image
|
||||
@@ -15,14 +13,22 @@
|
||||
></preview-image>
|
||||
</router-link>
|
||||
<v-toolbar class="transparent elevation-0" dense>
|
||||
<v-toolbar-title>
|
||||
<router-link
|
||||
class="text-decoration-none"
|
||||
:to="`/streams/${streamId}/commits/${commit.id}`"
|
||||
>
|
||||
<v-icon small>mdi-source-commit</v-icon>
|
||||
{{ commit.message }}
|
||||
</router-link>
|
||||
<v-toolbar-title class="d-flex" style="overflow: visible; width: 100%">
|
||||
<v-checkbox
|
||||
v-if="allowSelect"
|
||||
v-model="selectedState"
|
||||
dense
|
||||
@change="onSelect"
|
||||
/>
|
||||
<div style="overflow: hidden; text-overflow: ellipsis; white-space: nowrap">
|
||||
<router-link
|
||||
class="text-decoration-none"
|
||||
:to="`/streams/${streamId}/commits/${commit.id}`"
|
||||
>
|
||||
<v-icon v-if="!allowSelect" small>mdi-source-commit</v-icon>
|
||||
{{ commit.message }}
|
||||
</router-link>
|
||||
</div>
|
||||
</v-toolbar-title>
|
||||
</v-toolbar>
|
||||
<div class="mx-1 mb-2">
|
||||
@@ -90,13 +96,37 @@ export default {
|
||||
commit: { type: Object, default: () => null },
|
||||
previewHeight: { type: Number, default: () => 180 },
|
||||
showStreamAndBranch: { type: Boolean, default: true },
|
||||
highlight: { type: Boolean, default: false }
|
||||
highlight: { type: Boolean, default: false },
|
||||
allowSelect: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
selected: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
highlighted() {
|
||||
return this.highlight || this.selected
|
||||
},
|
||||
streamId() {
|
||||
return (
|
||||
this.commit.streamId ?? this.$route.params.streamId ?? this.$route.query.stream
|
||||
)
|
||||
},
|
||||
selectedState: {
|
||||
get() {
|
||||
return this.selected
|
||||
},
|
||||
set(val) {
|
||||
this.$emit('update:selected', !!val)
|
||||
}
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
onSelect() {
|
||||
this.$emit('select', { value: this.selected })
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,70 @@
|
||||
<template>
|
||||
<v-dialog
|
||||
v-model="dialogModel"
|
||||
:fullscreen="$vuetify.breakpoint.xsOnly"
|
||||
:max-width="maxWidth"
|
||||
>
|
||||
<v-card>
|
||||
<!-- Header -->
|
||||
<v-toolbar color="primary" dark flat>
|
||||
<v-app-bar-nav-icon style="pointer-events: none">
|
||||
<slot name="icon">
|
||||
<v-icon>mdi-information</v-icon>
|
||||
</slot>
|
||||
</v-app-bar-nav-icon>
|
||||
<v-toolbar-title>
|
||||
<slot name="title">Title</slot>
|
||||
</v-toolbar-title>
|
||||
<v-spacer></v-spacer>
|
||||
<v-btn icon @click="close"><v-icon>mdi-close</v-icon></v-btn>
|
||||
</v-toolbar>
|
||||
|
||||
<!-- Body -->
|
||||
<v-card-text class="pt-5">
|
||||
<slot name="content">
|
||||
Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor
|
||||
incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis
|
||||
nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat.
|
||||
Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore
|
||||
eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt
|
||||
in culpa qui officia deserunt mollit anim id est laborum.
|
||||
</slot>
|
||||
</v-card-text>
|
||||
|
||||
<!-- Actions -->
|
||||
<v-card-actions class="px-6 pb-5 pt-0">
|
||||
<slot name="actions">
|
||||
<v-spacer></v-spacer>
|
||||
<v-btn text @click="close">Cancel</v-btn>
|
||||
</slot>
|
||||
</v-card-actions>
|
||||
</v-card>
|
||||
</v-dialog>
|
||||
</template>
|
||||
<script lang="ts">
|
||||
import { computed, defineComponent } from 'vue'
|
||||
|
||||
export default defineComponent({
|
||||
name: 'BaseDialog',
|
||||
props: {
|
||||
show: {
|
||||
type: Boolean,
|
||||
required: true
|
||||
},
|
||||
maxWidth: {
|
||||
type: Number,
|
||||
default: 500
|
||||
}
|
||||
},
|
||||
setup(props, { emit }) {
|
||||
const dialogModel = computed({
|
||||
get: () => props.show,
|
||||
set: (newVal) => emit('update:show', newVal)
|
||||
})
|
||||
|
||||
const close = () => (dialogModel.value = false)
|
||||
|
||||
return { dialogModel, close }
|
||||
}
|
||||
})
|
||||
</script>
|
||||
@@ -0,0 +1,53 @@
|
||||
<template>
|
||||
<v-select
|
||||
v-model="model"
|
||||
filled
|
||||
rounded
|
||||
dense
|
||||
hide-details
|
||||
:items="items"
|
||||
prepend-icon="mdi-source-branch"
|
||||
:disabled="loading"
|
||||
/>
|
||||
</template>
|
||||
<script lang="ts">
|
||||
import { StreamBranchesSelectorDocument } from '@/graphql/generated/graphql'
|
||||
import { Nullable } from '@/helpers/typeHelpers'
|
||||
import { useQuery } from '@vue/apollo-composable'
|
||||
import { computed, defineComponent, PropType } from 'vue'
|
||||
|
||||
export default defineComponent({
|
||||
name: 'BranchSelect',
|
||||
props: {
|
||||
streamId: {
|
||||
type: String,
|
||||
required: true
|
||||
},
|
||||
value: {
|
||||
type: String as PropType<Nullable<string>>,
|
||||
default: null
|
||||
},
|
||||
excludedNames: {
|
||||
type: Array as PropType<string[]>,
|
||||
default: () => []
|
||||
}
|
||||
},
|
||||
setup(props, { emit }) {
|
||||
const { result, loading } = useQuery(StreamBranchesSelectorDocument, () => ({
|
||||
streamId: props.streamId
|
||||
}))
|
||||
|
||||
const items = computed(() =>
|
||||
(result.value?.stream?.branches?.items || [])
|
||||
.filter((i) => !props.excludedNames.includes(i.name))
|
||||
.map((i) => i.name)
|
||||
)
|
||||
const model = computed({
|
||||
get: () => props.value,
|
||||
set: (newVal) => emit('input', newVal)
|
||||
})
|
||||
|
||||
return { items, loading, model }
|
||||
}
|
||||
})
|
||||
</script>
|
||||
@@ -0,0 +1,74 @@
|
||||
<template>
|
||||
<div>
|
||||
<prioritized-portal to="toolbar" identity="commits-multi-select" :priority="2">
|
||||
<div class="font-weight-bold">{{ count }} commits selected</div>
|
||||
</prioritized-portal>
|
||||
|
||||
<prioritized-portal to="actions" identity="commits-multi-select" :priority="2">
|
||||
<div class="d-flex align-center">
|
||||
<v-btn small @click="clear">Clear selection</v-btn>
|
||||
<v-btn small class="ml-2" color="primary" @click="initMove">Move To</v-btn>
|
||||
<v-btn small class="mx-2" color="red" @click="initDelete">Delete</v-btn>
|
||||
</div>
|
||||
</prioritized-portal>
|
||||
|
||||
<commits-batch-actions-dialog
|
||||
:show.sync="showDialog"
|
||||
:stream-id="streamId"
|
||||
:branch-name="branchName"
|
||||
:selected-commit-ids="selectedCommitIds"
|
||||
:type="dialogType"
|
||||
@finish="onFinish"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
<script lang="ts">
|
||||
import PrioritizedPortal from '@/main/components/common/utility/PrioritizedPortal.vue'
|
||||
import CommitsBatchActionsDialog from '@/main/dialogs/commit/CommitsBatchActionsDialog.vue'
|
||||
import { BatchActionType } from '@/main/lib/stream/composables/commitMultiActions'
|
||||
import { computed, defineComponent, PropType, ref } from 'vue'
|
||||
|
||||
export default defineComponent({
|
||||
name: 'CommitMultiSelectToolbar',
|
||||
components: {
|
||||
PrioritizedPortal,
|
||||
CommitsBatchActionsDialog
|
||||
},
|
||||
props: {
|
||||
streamId: {
|
||||
type: String,
|
||||
required: true
|
||||
},
|
||||
selectedCommitIds: {
|
||||
type: Array as PropType<string[]>,
|
||||
required: true
|
||||
},
|
||||
branchName: {
|
||||
type: String,
|
||||
required: true
|
||||
}
|
||||
},
|
||||
setup(props, { emit }) {
|
||||
const showDialog = ref(false)
|
||||
const dialogType = ref(BatchActionType.Move)
|
||||
|
||||
const count = computed(() => props.selectedCommitIds?.length || 0)
|
||||
|
||||
const clear = () => emit('clear')
|
||||
const initMove = () => {
|
||||
showDialog.value = true
|
||||
dialogType.value = BatchActionType.Move
|
||||
}
|
||||
const initDelete = () => {
|
||||
showDialog.value = true
|
||||
dialogType.value = BatchActionType.Delete
|
||||
}
|
||||
const onFinish = () => {
|
||||
clear()
|
||||
emit('finish')
|
||||
}
|
||||
|
||||
return { clear, count, showDialog, initMove, initDelete, onFinish, dialogType }
|
||||
}
|
||||
})
|
||||
</script>
|
||||
@@ -85,6 +85,7 @@ import { gql } from '@apollo/client/core'
|
||||
import isNull from 'lodash/isNull'
|
||||
import isUndefined from 'lodash/isUndefined'
|
||||
import clone from 'lodash/clone'
|
||||
import { StreamEvents } from '@/main/lib/core/helpers/eventHubHelper'
|
||||
|
||||
export default {
|
||||
props: {
|
||||
@@ -190,7 +191,7 @@ export default {
|
||||
this.$eventHub.$emit('notification', { text: 'Branch deleted' })
|
||||
this.$router.push(`/streams/` + this.$route.params.streamId)
|
||||
this.$emit('close')
|
||||
this.$eventHub.$emit('branch-refresh')
|
||||
this.$eventHub.$emit(StreamEvents.RefetchBranches)
|
||||
},
|
||||
async updateBranch() {
|
||||
if (!this.$refs.form.validate()) return
|
||||
@@ -227,7 +228,7 @@ export default {
|
||||
}
|
||||
|
||||
this.loading = false
|
||||
this.$eventHub.$emit('branch-refresh')
|
||||
this.$eventHub.$emit(StreamEvents.RefetchBranches)
|
||||
this.$eventHub.$emit('notification', {
|
||||
text: 'Branch updated',
|
||||
action: {
|
||||
|
||||
@@ -0,0 +1,200 @@
|
||||
<template>
|
||||
<base-dialog :show.sync="realShow">
|
||||
<template #title>
|
||||
{{ titleText }}
|
||||
</template>
|
||||
<template #content>
|
||||
<template v-if="type === BatchActionType.Delete">
|
||||
Deleting commits is an irrevocable action! If you are sure about wanting to
|
||||
delete the selected commits, please click on the button below!
|
||||
</template>
|
||||
<template v-else-if="type === BatchActionType.Move">
|
||||
<div class="mb-4">
|
||||
Please select the target branch to move all of the selected commits to:
|
||||
</div>
|
||||
<branch-select
|
||||
v-model="targetBranch"
|
||||
:stream-id="streamId"
|
||||
:excluded-names="[branchName]"
|
||||
/>
|
||||
</template>
|
||||
</template>
|
||||
<template #actions>
|
||||
<v-spacer></v-spacer>
|
||||
<v-btn @click="close">Cancel</v-btn>
|
||||
<template v-if="type === BatchActionType.Delete">
|
||||
<v-btn color="red" :disabled="deleteDisabled" @click="deleteCommits">
|
||||
Delete
|
||||
</v-btn>
|
||||
</template>
|
||||
<template v-else-if="type === BatchActionType.Move">
|
||||
<v-btn color="primary" :disabled="moveDisabled" @click="moveCommits">
|
||||
Move
|
||||
</v-btn>
|
||||
</template>
|
||||
</template>
|
||||
</base-dialog>
|
||||
</template>
|
||||
<script lang="ts">
|
||||
import BaseDialog from '@/main/components/common/layout/BaseDialog.vue'
|
||||
import BranchSelect from '@/main/components/stream/branch/BranchSelect.vue'
|
||||
import { BatchActionType } from '@/main/lib/stream/composables/commitMultiActions'
|
||||
import { useApolloClient } from '@vue/apollo-composable'
|
||||
import { computed, defineComponent, PropType, ref } from 'vue'
|
||||
import {
|
||||
MoveCommitsDocument,
|
||||
DeleteCommitsDocument,
|
||||
MoveCommitsMutation,
|
||||
MoveCommitsMutationVariables,
|
||||
DeleteCommitsMutation,
|
||||
DeleteCommitsMutationVariables
|
||||
} from '@/graphql/generated/graphql'
|
||||
import { Nullable } from '@/helpers/typeHelpers'
|
||||
import {
|
||||
convertThrowIntoFetchResult,
|
||||
getFirstErrorMessage
|
||||
} from '@/main/lib/common/apollo/helpers/apolloOperationHelper'
|
||||
import { useGlobalToast } from '@/main/lib/core/composables/notifications'
|
||||
import { DocumentNode } from 'graphql'
|
||||
|
||||
export default defineComponent({
|
||||
name: 'CommitsBatchActionsDialog',
|
||||
components: {
|
||||
BaseDialog,
|
||||
BranchSelect
|
||||
},
|
||||
props: {
|
||||
streamId: {
|
||||
type: String,
|
||||
required: true
|
||||
},
|
||||
branchName: {
|
||||
type: String,
|
||||
required: true
|
||||
},
|
||||
selectedCommitIds: {
|
||||
type: Array as PropType<string[]>,
|
||||
required: true
|
||||
},
|
||||
show: {
|
||||
type: Boolean,
|
||||
required: true
|
||||
},
|
||||
type: {
|
||||
type: String as PropType<BatchActionType>,
|
||||
default: BatchActionType.Delete
|
||||
}
|
||||
},
|
||||
setup(props, { emit }) {
|
||||
const apollo = useApolloClient().client
|
||||
const { triggerNotification } = useGlobalToast()
|
||||
|
||||
const targetBranch = ref(null as Nullable<string>)
|
||||
const loading = ref(false)
|
||||
|
||||
const realShow = computed({
|
||||
get: () => props.show,
|
||||
set: (newShow) => emit('update:show', newShow)
|
||||
})
|
||||
const count = computed(() => props.selectedCommitIds.length)
|
||||
const titleText = computed(() => {
|
||||
switch (props.type) {
|
||||
case BatchActionType.Delete:
|
||||
return `Delete ${count.value} commits`
|
||||
case BatchActionType.Move:
|
||||
return `Move ${count.value} commits`
|
||||
default:
|
||||
return ''
|
||||
}
|
||||
})
|
||||
const moveDisabled = computed(() => !targetBranch.value || loading.value)
|
||||
const deleteDisabled = computed(() => loading.value)
|
||||
|
||||
const close = () => (realShow.value = false)
|
||||
|
||||
const invokeAction = async <
|
||||
D = Record<string, unknown>,
|
||||
V = Record<string, unknown>
|
||||
>(params: {
|
||||
shouldQuit: () => boolean
|
||||
getResult: (data: D | undefined) => boolean | null | undefined
|
||||
document: DocumentNode
|
||||
variables: V
|
||||
successMessage: string
|
||||
}) => {
|
||||
const { shouldQuit, document, variables, getResult, successMessage } = params
|
||||
if (shouldQuit()) return
|
||||
|
||||
loading.value = true
|
||||
const { data, errors } = await apollo
|
||||
.mutate({
|
||||
mutation: document,
|
||||
variables
|
||||
})
|
||||
.catch(convertThrowIntoFetchResult)
|
||||
|
||||
const result = getResult(data)
|
||||
if (result) {
|
||||
triggerNotification({
|
||||
text: successMessage,
|
||||
type: 'success'
|
||||
})
|
||||
|
||||
// finished
|
||||
close()
|
||||
emit('finish')
|
||||
} else {
|
||||
const msg = getFirstErrorMessage(errors)
|
||||
triggerNotification({
|
||||
text: msg,
|
||||
type: 'error'
|
||||
})
|
||||
}
|
||||
|
||||
loading.value = false
|
||||
}
|
||||
|
||||
const moveCommits = async () => {
|
||||
await invokeAction<MoveCommitsMutation, MoveCommitsMutationVariables>({
|
||||
shouldQuit: () => moveDisabled.value,
|
||||
getResult: (data) => data?.commitsMove,
|
||||
document: MoveCommitsDocument,
|
||||
variables: {
|
||||
input: {
|
||||
commitIds: props.selectedCommitIds.slice(),
|
||||
targetBranch: targetBranch.value!
|
||||
}
|
||||
},
|
||||
successMessage: 'Selected commits successfully moved'
|
||||
})
|
||||
}
|
||||
|
||||
const deleteCommits = async () => {
|
||||
await invokeAction<DeleteCommitsMutation, DeleteCommitsMutationVariables>({
|
||||
shouldQuit: () => deleteDisabled.value,
|
||||
getResult: (data) => data?.commitsDelete,
|
||||
document: DeleteCommitsDocument,
|
||||
variables: {
|
||||
input: {
|
||||
commitIds: props.selectedCommitIds.slice()
|
||||
}
|
||||
},
|
||||
successMessage: 'Selected commits successfully deleted'
|
||||
})
|
||||
}
|
||||
|
||||
return {
|
||||
realShow,
|
||||
count,
|
||||
titleText,
|
||||
BatchActionType,
|
||||
close,
|
||||
moveCommits,
|
||||
deleteCommits,
|
||||
targetBranch,
|
||||
moveDisabled,
|
||||
deleteDisabled
|
||||
}
|
||||
}
|
||||
})
|
||||
</script>
|
||||
@@ -25,12 +25,12 @@ export function useMixpanel(): OverridedMixpanel {
|
||||
}
|
||||
|
||||
/**
|
||||
* Composable that resolves whether the user is logged in through an Apollo query
|
||||
* Composable that resolves user auth information through an Apollo query
|
||||
*/
|
||||
export function useIsLoggedIn() {
|
||||
const { result } = useQuery(IsLoggedInDocument)
|
||||
const isLoggedIn = computed(() => !!result.value?.user?.id)
|
||||
const userId = computed(() => result.value?.user?.id)
|
||||
const isLoggedIn = computed(() => !!userId.value)
|
||||
return { isLoggedIn, userId }
|
||||
}
|
||||
|
||||
|
||||
@@ -29,12 +29,17 @@ export const GlobalEvents = Object.freeze({
|
||||
*/
|
||||
export const StreamEvents = Object.freeze({
|
||||
/**
|
||||
* For triggering a refetch of stream data
|
||||
* For triggering a refetch of main stream data
|
||||
*/
|
||||
Refetch: 'stream:refetch',
|
||||
|
||||
/**
|
||||
* For triggering a refetch of stream collaborator data
|
||||
*/
|
||||
RefetchCollaborators: 'stream:refetch:collaborators'
|
||||
RefetchCollaborators: 'stream:refetch:collaborators',
|
||||
|
||||
/**
|
||||
* For triggering a refetch of stream branch data
|
||||
*/
|
||||
RefetchBranches: 'stream:refetch:branches'
|
||||
})
|
||||
|
||||
@@ -0,0 +1,52 @@
|
||||
import { reduce } from 'lodash'
|
||||
import { ref, computed } from 'vue'
|
||||
|
||||
export enum BatchActionType {
|
||||
Move = 'move',
|
||||
Delete = 'delete'
|
||||
}
|
||||
|
||||
/**
|
||||
* Composable for setting up commit multi-select & actions like delete, move etc.
|
||||
*/
|
||||
export function useCommitMultiActions() {
|
||||
const selectedCommitsState = ref({} as Record<string, boolean>)
|
||||
const selectedCommitIds = computed(() =>
|
||||
reduce(
|
||||
selectedCommitsState.value,
|
||||
(res, val, key) => {
|
||||
if (val) {
|
||||
res.push(key)
|
||||
}
|
||||
|
||||
return res
|
||||
},
|
||||
[] as string[]
|
||||
)
|
||||
)
|
||||
|
||||
const clearSelectedCommits = () => {
|
||||
selectedCommitsState.value = {}
|
||||
}
|
||||
|
||||
const hasSelectedCommits = computed(() => selectedCommitIds.value.length > 0)
|
||||
|
||||
return {
|
||||
/**
|
||||
* Selected commit IDs (read-only)
|
||||
*/
|
||||
selectedCommitIds,
|
||||
/**
|
||||
* Object with selected commit keys and bool values
|
||||
*/
|
||||
selectedCommitsState,
|
||||
/**
|
||||
* Whether there are any selected commit ids
|
||||
*/
|
||||
hasSelectedCommits,
|
||||
/**
|
||||
* Clear selected commits
|
||||
*/
|
||||
clearSelectedCommits
|
||||
}
|
||||
}
|
||||
@@ -226,6 +226,7 @@ import {
|
||||
STANDARD_PORTAL_KEYS,
|
||||
buildPortalStateMixin
|
||||
} from '@/main/utils/portalStateManager'
|
||||
import { StreamEvents } from '@/main/lib/core/helpers/eventHubHelper'
|
||||
|
||||
export default {
|
||||
components: {
|
||||
@@ -301,7 +302,7 @@ export default {
|
||||
},
|
||||
mounted() {
|
||||
this.branchMenuOpen = this.$route.name.toLowerCase().includes('branch')
|
||||
this.$eventHub.$on('branch-refresh', async () => {
|
||||
this.$eventHub.$on(StreamEvents.RefetchBranches, async () => {
|
||||
await this.refetchBranches()
|
||||
})
|
||||
this.$eventHub.$on('show-new-branch-dialog', () => {
|
||||
|
||||
@@ -1,15 +1,16 @@
|
||||
<template>
|
||||
<div>
|
||||
<portal v-if="canRenderToolbarPortal" to="toolbar">
|
||||
<!-- Toolbar -->
|
||||
<prioritized-portal to="toolbar" identity="commits" :priority="0">
|
||||
<div class="font-weight-bold">
|
||||
Your Latest Commits
|
||||
<span v-if="user" class="caption">({{ user.commits.totalCount }})</span>
|
||||
</div>
|
||||
</portal>
|
||||
</prioritized-portal>
|
||||
|
||||
<v-row v-if="user && user.commits.totalCount !== 0">
|
||||
<v-col
|
||||
v-for="commit in user.commits.items.filter((c) => c.branchName !== 'globals')"
|
||||
v-for="commit in commitItems"
|
||||
:key="commit.id"
|
||||
cols="12"
|
||||
sm="6"
|
||||
@@ -61,21 +62,18 @@
|
||||
</template>
|
||||
<script>
|
||||
import { gql } from '@apollo/client/core'
|
||||
import {
|
||||
STANDARD_PORTAL_KEYS,
|
||||
buildPortalStateMixin
|
||||
} from '@/main/utils/portalStateManager'
|
||||
import { useQuery } from '@vue/apollo-composable'
|
||||
import { computed } from 'vue'
|
||||
import { computed, defineComponent } from 'vue'
|
||||
import PrioritizedPortal from '@/main/components/common/utility/PrioritizedPortal.vue'
|
||||
|
||||
export default {
|
||||
export default defineComponent({
|
||||
name: 'TheCommits',
|
||||
components: {
|
||||
InfiniteLoading: () => import('vue-infinite-loading'),
|
||||
CommitPreviewCard: () => import('@/main/components/common/CommitPreviewCard'),
|
||||
NoDataPlaceholder: () => import('@/main/components/common/NoDataPlaceholder')
|
||||
NoDataPlaceholder: () => import('@/main/components/common/NoDataPlaceholder'),
|
||||
PrioritizedPortal
|
||||
},
|
||||
mixins: [buildPortalStateMixin([STANDARD_PORTAL_KEYS.Toolbar], 'commits', 0)],
|
||||
setup() {
|
||||
const { result, fetchMore: userFetchMore } = useQuery(gql`
|
||||
query ($cursor: String) {
|
||||
@@ -101,9 +99,13 @@ export default {
|
||||
}
|
||||
`)
|
||||
const user = computed(() => result.value?.user)
|
||||
const commitItems = computed(() =>
|
||||
(user.value?.commits.items || []).filter((c) => c.branchName !== 'globals')
|
||||
)
|
||||
|
||||
return {
|
||||
user,
|
||||
commitItems,
|
||||
userFetchMore
|
||||
}
|
||||
},
|
||||
@@ -123,5 +125,5 @@ export default {
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
</script>
|
||||
|
||||
@@ -1,7 +1,15 @@
|
||||
<template>
|
||||
<div>
|
||||
<commit-multi-select-toolbar
|
||||
v-if="hasSelectedCommits"
|
||||
:selected-commit-ids="selectedCommitIds"
|
||||
:stream-id="streamId"
|
||||
:branch-name="branchName"
|
||||
@clear="clearSelectedCommits"
|
||||
@finish="onBatchCommitActionFinish"
|
||||
/>
|
||||
<branch-toolbar
|
||||
v-if="canRenderToolbarPortal && stream && stream.branch"
|
||||
v-else-if="canRenderToolbarPortal && stream && stream.branch"
|
||||
:stream="stream"
|
||||
@edit-branch="branchEditDialog = true"
|
||||
/>
|
||||
@@ -25,7 +33,7 @@
|
||||
</v-row>
|
||||
<v-row v-if="!listMode">
|
||||
<v-col
|
||||
v-for="(commit, index) in allCommits"
|
||||
v-for="commit in allCommits"
|
||||
:key="commit.id + 'card'"
|
||||
cols="12"
|
||||
sm="6"
|
||||
@@ -35,7 +43,8 @@
|
||||
<commit-preview-card
|
||||
:commit="commit"
|
||||
:show-stream-and-branch="false"
|
||||
:highlight="index === 0"
|
||||
:allow-select="isStreamOwner || isCommitOwner(commit)"
|
||||
:selected.sync="selectedCommitsState[commit.id]"
|
||||
/>
|
||||
</v-col>
|
||||
</v-row>
|
||||
@@ -100,14 +109,16 @@
|
||||
<script>
|
||||
import { gql } from '@apollo/client/core'
|
||||
import branchQuery from '@/graphql/branch.gql'
|
||||
import {
|
||||
STANDARD_PORTAL_KEYS,
|
||||
buildPortalStateMixin
|
||||
} from '@/main/utils/portalStateManager'
|
||||
import { STANDARD_PORTAL_KEYS, usePortalState } from '@/main/utils/portalStateManager'
|
||||
import { useQuery } from '@vue/apollo-composable'
|
||||
import { computed } from 'vue'
|
||||
import { useRoute } from '@/main/lib/core/composables/router'
|
||||
import { AppLocalStorage } from '@/utils/localStorage'
|
||||
import { useCommitMultiActions } from '@/main/lib/stream/composables/commitMultiActions'
|
||||
import CommitMultiSelectToolbar from '@/main/components/stream/commit/CommitMultiSelectToolbar.vue'
|
||||
import { Roles } from '@/helpers/mainConstants'
|
||||
import { useEventHub, useIsLoggedIn } from '@/main/lib/core/composables/core'
|
||||
import { StreamEvents } from '@/main/lib/core/helpers/eventHubHelper'
|
||||
|
||||
export default {
|
||||
name: 'TheBranch',
|
||||
@@ -118,11 +129,31 @@ export default {
|
||||
ListItemCommit: () => import('@/main/components/stream/ListItemCommit'),
|
||||
BranchEditDialog: () => import('@/main/dialogs/BranchEditDialog'),
|
||||
BranchToolbar: () => import('@/main/toolbars/BranchToolbar'),
|
||||
CommitPreviewCard: () => import('@/main/components/common/CommitPreviewCard')
|
||||
CommitPreviewCard: () => import('@/main/components/common/CommitPreviewCard'),
|
||||
CommitMultiSelectToolbar
|
||||
},
|
||||
mixins: [buildPortalStateMixin([STANDARD_PORTAL_KEYS.Toolbar], 'stream-branch', 1)],
|
||||
setup() {
|
||||
const eventHub = useEventHub()
|
||||
|
||||
const route = useRoute()
|
||||
const streamId = computed(() => route.params.streamId)
|
||||
const branchName = computed(() => (route.params.branchName || '').toLowerCase())
|
||||
|
||||
const { canRenderToolbarPortal } = usePortalState(
|
||||
[STANDARD_PORTAL_KEYS.Toolbar],
|
||||
'stream-branch',
|
||||
1
|
||||
)
|
||||
|
||||
const { userId } = useIsLoggedIn()
|
||||
|
||||
const {
|
||||
selectedCommitIds,
|
||||
hasSelectedCommits,
|
||||
clearSelectedCommits,
|
||||
selectedCommitsState
|
||||
} = useCommitMultiActions()
|
||||
|
||||
const {
|
||||
result,
|
||||
fetchMore: streamFetchMore,
|
||||
@@ -131,19 +162,41 @@ export default {
|
||||
} = useQuery(
|
||||
branchQuery,
|
||||
() => ({
|
||||
streamId: route.params.streamId,
|
||||
branchName: (route.params.branchName || '').toLowerCase(),
|
||||
streamId: streamId.value,
|
||||
branchName: branchName.value,
|
||||
cursor: null
|
||||
}),
|
||||
{ fetchPolicy: 'network-only' }
|
||||
)
|
||||
const stream = computed(() => result.value?.stream)
|
||||
|
||||
const isStreamOwner = computed(() => stream.value.role === Roles.Stream.Owner)
|
||||
const isCommitOwner = (commit) => userId.value && commit.authorId === userId.value
|
||||
|
||||
const onBatchCommitActionFinish = () => {
|
||||
// refetch the main branch query
|
||||
streamRefetch()
|
||||
|
||||
// refetch stream & branches
|
||||
eventHub.$emit(StreamEvents.Refetch)
|
||||
eventHub.$emit(StreamEvents.RefetchBranches)
|
||||
}
|
||||
|
||||
return {
|
||||
stream,
|
||||
streamFetchMore,
|
||||
streamRefetch,
|
||||
streamLoading
|
||||
streamLoading,
|
||||
streamId,
|
||||
branchName,
|
||||
selectedCommitIds,
|
||||
hasSelectedCommits,
|
||||
clearSelectedCommits,
|
||||
selectedCommitsState,
|
||||
canRenderToolbarPortal,
|
||||
isStreamOwner,
|
||||
isCommitOwner,
|
||||
onBatchCommitActionFinish
|
||||
}
|
||||
},
|
||||
data() {
|
||||
@@ -210,9 +263,6 @@ export default {
|
||||
loggedInUserId() {
|
||||
return AppLocalStorage.get('uuid')
|
||||
},
|
||||
streamId() {
|
||||
return this.$route.params.streamId
|
||||
},
|
||||
latestCommitObjectUrl() {
|
||||
if ((this.stream?.branch?.commits?.items || []).length > 0)
|
||||
return `${window.location.origin}/streams/${this.stream.id}/objects/${this.stream.branch.commits.items[0].referencedObject}`
|
||||
|
||||
@@ -52,7 +52,7 @@ type CommitCollectionUserNode {
|
||||
type BranchCollection {
|
||||
totalCount: Int!
|
||||
cursor: String
|
||||
items: [Branch]
|
||||
items: [Branch!]
|
||||
}
|
||||
|
||||
type CommitCollection {
|
||||
@@ -90,6 +90,20 @@ extend type Mutation {
|
||||
commitDelete(commit: CommitDeleteInput!): Boolean!
|
||||
@hasRole(role: "server:user")
|
||||
@hasScope(scope: "streams:write")
|
||||
|
||||
"""
|
||||
Move a batch of commits to a new branch
|
||||
"""
|
||||
commitsMove(input: CommitsMoveInput!): Boolean!
|
||||
@hasRole(role: "server:user")
|
||||
@hasScope(scope: "streams:write")
|
||||
|
||||
"""
|
||||
Delete a batch of commits
|
||||
"""
|
||||
commitsDelete(input: CommitsDeleteInput!): Boolean!
|
||||
@hasRole(role: "server:user")
|
||||
@hasScope(scope: "streams:write")
|
||||
}
|
||||
|
||||
extend type Subscription {
|
||||
@@ -161,7 +175,7 @@ input CommitCreateInput {
|
||||
"""
|
||||
**DEPRECATED** Use the `parents` field.
|
||||
"""
|
||||
previousCommitIds: [String]
|
||||
previousCommitIds: [String] @deprecated
|
||||
parents: [String]
|
||||
}
|
||||
|
||||
@@ -186,3 +200,12 @@ input CommitDeleteInput {
|
||||
streamId: String!
|
||||
id: String!
|
||||
}
|
||||
|
||||
input CommitsMoveInput {
|
||||
targetBranch: String!
|
||||
commitIds: [String!]!
|
||||
}
|
||||
|
||||
input CommitsDeleteInput {
|
||||
commitIds: [String!]!
|
||||
}
|
||||
|
||||
@@ -96,6 +96,8 @@ function buildTableHelper<T extends string, C extends string>(
|
||||
*
|
||||
* Streams.with({...}) - configure helper, e.g. disable table name being prefixed to col names:
|
||||
* Streams.with({withoutTablePrefix: true}).col.id
|
||||
*
|
||||
* Streams.withoutTablePrefix.col.id - Shorthand for accessing columns without the table prefix
|
||||
*/
|
||||
|
||||
export const Streams = buildTableHelper('streams', [
|
||||
@@ -240,4 +242,35 @@ export const UserNotificationPreferences = buildTableHelper(
|
||||
['userId', 'preferences']
|
||||
)
|
||||
|
||||
export const Commits = buildTableHelper('commits', [
|
||||
'id',
|
||||
'referencedObject',
|
||||
'author',
|
||||
'message',
|
||||
'createdAt',
|
||||
'sourceApplication',
|
||||
'totalChildrenCount',
|
||||
'parents'
|
||||
])
|
||||
|
||||
export const StreamCommits = buildTableHelper('stream_commits', [
|
||||
'streamId',
|
||||
'commitId'
|
||||
])
|
||||
|
||||
export const BranchCommits = buildTableHelper('branch_commits', [
|
||||
'branchId',
|
||||
'commitId'
|
||||
])
|
||||
|
||||
export const Branches = buildTableHelper('branches', [
|
||||
'id',
|
||||
'streamId',
|
||||
'authorId',
|
||||
'name',
|
||||
'description',
|
||||
'createdAt',
|
||||
'updatedAt'
|
||||
])
|
||||
|
||||
export { knex }
|
||||
|
||||
@@ -0,0 +1,11 @@
|
||||
import { BaseError } from '@/modules/shared/errors'
|
||||
|
||||
export class CommitInvalidAccessError extends BaseError {
|
||||
static defaultMessage = 'User does not have access to the specified commit'
|
||||
static code = 'COMMIT_INVALID_ACCESS_ERROR'
|
||||
}
|
||||
|
||||
export class CommitBatchUpdateError extends BaseError {
|
||||
static defaultMessage = 'An issue occurred while batch updating commits'
|
||||
static code = 'COMMIT_BATCH_UPDATE_ERROR'
|
||||
}
|
||||
@@ -165,7 +165,7 @@ export type BranchCommitsArgs = {
|
||||
export type BranchCollection = {
|
||||
__typename?: 'BranchCollection';
|
||||
cursor?: Maybe<Scalars['String']>;
|
||||
items?: Maybe<Array<Maybe<Branch>>>;
|
||||
items?: Maybe<Array<Branch>>;
|
||||
totalCount: Scalars['Int'];
|
||||
};
|
||||
|
||||
@@ -340,7 +340,10 @@ export type CommitCreateInput = {
|
||||
message?: InputMaybe<Scalars['String']>;
|
||||
objectId: Scalars['String'];
|
||||
parents?: InputMaybe<Array<InputMaybe<Scalars['String']>>>;
|
||||
/** **DEPRECATED** Use the `parents` field. */
|
||||
/**
|
||||
* **DEPRECATED** Use the `parents` field.
|
||||
* @deprecated Field no longer supported
|
||||
*/
|
||||
previousCommitIds?: InputMaybe<Array<InputMaybe<Scalars['String']>>>;
|
||||
sourceApplication?: InputMaybe<Scalars['String']>;
|
||||
streamId: Scalars['String'];
|
||||
@@ -367,6 +370,15 @@ export type CommitUpdateInput = {
|
||||
streamId: Scalars['String'];
|
||||
};
|
||||
|
||||
export type CommitsDeleteInput = {
|
||||
commitIds: Array<Scalars['String']>;
|
||||
};
|
||||
|
||||
export type CommitsMoveInput = {
|
||||
commitIds: Array<Scalars['String']>;
|
||||
targetBranch: Scalars['String'];
|
||||
};
|
||||
|
||||
export enum DiscoverableStreamsSortType {
|
||||
CreatedDate = 'CREATED_DATE',
|
||||
FavoritesCount = 'FAVORITES_COUNT'
|
||||
@@ -446,6 +458,10 @@ export type Mutation = {
|
||||
commitDelete: Scalars['Boolean'];
|
||||
commitReceive: Scalars['Boolean'];
|
||||
commitUpdate: Scalars['Boolean'];
|
||||
/** Delete a batch of commits */
|
||||
commitsDelete: Scalars['Boolean'];
|
||||
/** Move a batch of commits to a new branch */
|
||||
commitsMove: Scalars['Boolean'];
|
||||
/** Delete a pending invite */
|
||||
inviteDelete: Scalars['Boolean'];
|
||||
/** Re-send a pending invite */
|
||||
@@ -599,6 +615,16 @@ export type MutationCommitUpdateArgs = {
|
||||
};
|
||||
|
||||
|
||||
export type MutationCommitsDeleteArgs = {
|
||||
input: CommitsDeleteInput;
|
||||
};
|
||||
|
||||
|
||||
export type MutationCommitsMoveArgs = {
|
||||
input: CommitsMoveInput;
|
||||
};
|
||||
|
||||
|
||||
export type MutationInviteDeleteArgs = {
|
||||
inviteId: Scalars['String'];
|
||||
};
|
||||
@@ -1701,7 +1727,7 @@ export type ResolversTypes = {
|
||||
BlobMetadataCollection: ResolverTypeWrapper<BlobMetadataCollection>;
|
||||
Boolean: ResolverTypeWrapper<Scalars['Boolean']>;
|
||||
Branch: ResolverTypeWrapper<Omit<Branch, 'author'> & { author?: Maybe<ResolversTypes['User']> }>;
|
||||
BranchCollection: ResolverTypeWrapper<Omit<BranchCollection, 'items'> & { items?: Maybe<Array<Maybe<ResolversTypes['Branch']>>> }>;
|
||||
BranchCollection: ResolverTypeWrapper<Omit<BranchCollection, 'items'> & { items?: Maybe<Array<ResolversTypes['Branch']>> }>;
|
||||
BranchCreateInput: BranchCreateInput;
|
||||
BranchDeleteInput: BranchDeleteInput;
|
||||
BranchUpdateInput: BranchUpdateInput;
|
||||
@@ -1719,6 +1745,8 @@ export type ResolversTypes = {
|
||||
CommitDeleteInput: CommitDeleteInput;
|
||||
CommitReceivedInput: CommitReceivedInput;
|
||||
CommitUpdateInput: CommitUpdateInput;
|
||||
CommitsDeleteInput: CommitsDeleteInput;
|
||||
CommitsMoveInput: CommitsMoveInput;
|
||||
DateTime: ResolverTypeWrapper<Scalars['DateTime']>;
|
||||
DiscoverableStreamsSortType: DiscoverableStreamsSortType;
|
||||
DiscoverableStreamsSortingInput: DiscoverableStreamsSortingInput;
|
||||
@@ -1794,7 +1822,7 @@ export type ResolversParentTypes = {
|
||||
BlobMetadataCollection: BlobMetadataCollection;
|
||||
Boolean: Scalars['Boolean'];
|
||||
Branch: Omit<Branch, 'author'> & { author?: Maybe<ResolversParentTypes['User']> };
|
||||
BranchCollection: Omit<BranchCollection, 'items'> & { items?: Maybe<Array<Maybe<ResolversParentTypes['Branch']>>> };
|
||||
BranchCollection: Omit<BranchCollection, 'items'> & { items?: Maybe<Array<ResolversParentTypes['Branch']>> };
|
||||
BranchCreateInput: BranchCreateInput;
|
||||
BranchDeleteInput: BranchDeleteInput;
|
||||
BranchUpdateInput: BranchUpdateInput;
|
||||
@@ -1812,6 +1840,8 @@ export type ResolversParentTypes = {
|
||||
CommitDeleteInput: CommitDeleteInput;
|
||||
CommitReceivedInput: CommitReceivedInput;
|
||||
CommitUpdateInput: CommitUpdateInput;
|
||||
CommitsDeleteInput: CommitsDeleteInput;
|
||||
CommitsMoveInput: CommitsMoveInput;
|
||||
DateTime: Scalars['DateTime'];
|
||||
DiscoverableStreamsSortingInput: DiscoverableStreamsSortingInput;
|
||||
EmailAddress: Scalars['EmailAddress'];
|
||||
@@ -1993,7 +2023,7 @@ export type BranchResolvers<ContextType = GraphQLContext, ParentType extends Res
|
||||
|
||||
export type BranchCollectionResolvers<ContextType = GraphQLContext, ParentType extends ResolversParentTypes['BranchCollection'] = ResolversParentTypes['BranchCollection']> = {
|
||||
cursor?: Resolver<Maybe<ResolversTypes['String']>, ParentType, ContextType>;
|
||||
items?: Resolver<Maybe<Array<Maybe<ResolversTypes['Branch']>>>, ParentType, ContextType>;
|
||||
items?: Resolver<Maybe<Array<ResolversTypes['Branch']>>, ParentType, ContextType>;
|
||||
totalCount?: Resolver<ResolversTypes['Int'], ParentType, ContextType>;
|
||||
__isTypeOf?: IsTypeOfResolverFn<ParentType, ContextType>;
|
||||
};
|
||||
@@ -2140,6 +2170,8 @@ export type MutationResolvers<ContextType = GraphQLContext, ParentType extends R
|
||||
commitDelete?: Resolver<ResolversTypes['Boolean'], ParentType, ContextType, RequireFields<MutationCommitDeleteArgs, 'commit'>>;
|
||||
commitReceive?: Resolver<ResolversTypes['Boolean'], ParentType, ContextType, RequireFields<MutationCommitReceiveArgs, 'input'>>;
|
||||
commitUpdate?: Resolver<ResolversTypes['Boolean'], ParentType, ContextType, RequireFields<MutationCommitUpdateArgs, 'commit'>>;
|
||||
commitsDelete?: Resolver<ResolversTypes['Boolean'], ParentType, ContextType, RequireFields<MutationCommitsDeleteArgs, 'input'>>;
|
||||
commitsMove?: Resolver<ResolversTypes['Boolean'], ParentType, ContextType, RequireFields<MutationCommitsMoveArgs, 'input'>>;
|
||||
inviteDelete?: Resolver<ResolversTypes['Boolean'], ParentType, ContextType, RequireFields<MutationInviteDeleteArgs, 'inviteId'>>;
|
||||
inviteResend?: Resolver<ResolversTypes['Boolean'], ParentType, ContextType, RequireFields<MutationInviteResendArgs, 'inviteId'>>;
|
||||
objectCreate?: Resolver<Array<Maybe<ResolversTypes['String']>>, ParentType, ContextType, RequireFields<MutationObjectCreateArgs, 'objectInput'>>;
|
||||
|
||||
@@ -27,12 +27,17 @@ const { getStream } = require('../../services/streams')
|
||||
const { getUser } = require('../../services/users')
|
||||
|
||||
const { respectsLimits } = require('../../services/ratelimits')
|
||||
const {
|
||||
batchMoveCommits,
|
||||
batchDeleteCommits
|
||||
} = require('@/modules/core/services/commit/batchCommitActions')
|
||||
|
||||
// subscription events
|
||||
const COMMIT_CREATED = 'COMMIT_CREATED'
|
||||
const COMMIT_UPDATED = 'COMMIT_UPDATED'
|
||||
const COMMIT_DELETED = 'COMMIT_DELETED'
|
||||
|
||||
/** @type {import('@/modules/core/graph/generated/graphql').Resolvers} */
|
||||
module.exports = {
|
||||
Query: {},
|
||||
Stream: {
|
||||
@@ -245,6 +250,16 @@ module.exports = {
|
||||
}
|
||||
|
||||
return deleted
|
||||
},
|
||||
|
||||
async commitsMove(_, args, ctx) {
|
||||
await batchMoveCommits(args.input, ctx.userId)
|
||||
return true
|
||||
},
|
||||
|
||||
async commitsDelete(_, args, ctx) {
|
||||
await batchDeleteCommits(args.input, ctx.userId)
|
||||
return true
|
||||
}
|
||||
},
|
||||
Subscription: {
|
||||
|
||||
@@ -74,3 +74,34 @@ export type ServerInfo = ServerConfigRecord & {
|
||||
*/
|
||||
version: string
|
||||
}
|
||||
|
||||
export type CommitRecord = {
|
||||
id: string
|
||||
referencedObject: string
|
||||
author: string
|
||||
message: string
|
||||
createdAt: Date
|
||||
sourceApplication: Nullable<string>
|
||||
totalChildrenCount: Nullable<number>
|
||||
parents: Nullable<string>
|
||||
}
|
||||
|
||||
export type BranchCommitRecord = {
|
||||
branchId: string
|
||||
commitId: string
|
||||
}
|
||||
|
||||
export type StreamCommitRecord = {
|
||||
streamId: string
|
||||
commitId: string
|
||||
}
|
||||
|
||||
export type BranchRecord = {
|
||||
id: string
|
||||
streamId: string
|
||||
authorId: string
|
||||
name: string
|
||||
description: Nullable<string>
|
||||
createdAt: Date
|
||||
updatedAt: Date
|
||||
}
|
||||
|
||||
@@ -0,0 +1,18 @@
|
||||
import { Knex } from 'knex'
|
||||
|
||||
const TABLE_NAME = 'branches'
|
||||
|
||||
export async function up(knex: Knex): Promise<void> {
|
||||
// delete all invalid branches (null name - shouldnt even exist)
|
||||
await knex.table(TABLE_NAME).whereNull('name').del()
|
||||
|
||||
await knex.schema.alterTable(TABLE_NAME, (table) => {
|
||||
table.string('name', 512).notNullable().alter()
|
||||
})
|
||||
}
|
||||
|
||||
export async function down(knex: Knex): Promise<void> {
|
||||
await knex.schema.alterTable(TABLE_NAME, (table) => {
|
||||
table.string('name', 512).nullable().alter()
|
||||
})
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
import { Branches, knex } from '@/modules/core/dbSchema'
|
||||
import { BranchRecord } from '@/modules/core/helpers/types'
|
||||
|
||||
export async function getStreamBranchByName(streamId: string, name: string) {
|
||||
if (!streamId || !name) return null
|
||||
|
||||
const q = Branches.knex<BranchRecord>()
|
||||
.where(Branches.col.streamId, streamId)
|
||||
.andWhere(knex.raw('LOWER(name) = ?', [name.toLowerCase()]))
|
||||
|
||||
return await q.first()
|
||||
}
|
||||
@@ -0,0 +1,71 @@
|
||||
import {
|
||||
BranchCommits,
|
||||
Branches,
|
||||
Commits,
|
||||
StreamCommits
|
||||
} from '@/modules/core/dbSchema'
|
||||
import { BranchCommitRecord, CommitRecord } from '@/modules/core/helpers/types'
|
||||
import { uniqBy } from 'lodash'
|
||||
|
||||
const CommitWithStreamBranchMetadataFields = [
|
||||
...Commits.cols,
|
||||
StreamCommits.col.streamId,
|
||||
BranchCommits.col.branchId,
|
||||
`${Branches.col.name} as branchName`
|
||||
]
|
||||
|
||||
export type CommitWithStreamBranchMetadata = CommitRecord & {
|
||||
streamId: string
|
||||
branchId: string
|
||||
branchName: string
|
||||
}
|
||||
|
||||
/**
|
||||
* Get commits with their stream and branch IDs
|
||||
*/
|
||||
export async function getCommits(commitIds: string[]) {
|
||||
const q = Commits.knex()
|
||||
.select<CommitWithStreamBranchMetadata[]>(CommitWithStreamBranchMetadataFields)
|
||||
.whereIn(Commits.col.id, commitIds)
|
||||
.leftJoin(StreamCommits.name, StreamCommits.col.commitId, Commits.col.id)
|
||||
.leftJoin(BranchCommits.name, BranchCommits.col.commitId, Commits.col.id)
|
||||
.innerJoin(Branches.name, Branches.col.id, BranchCommits.col.branchId)
|
||||
|
||||
const rows = await q
|
||||
|
||||
// in case the join tables have multiple values for each commit
|
||||
// (shouldnt happen, but the schema allows for it)
|
||||
const uniqueRows = uniqBy(rows, (r) => r.id)
|
||||
|
||||
return uniqueRows
|
||||
}
|
||||
|
||||
/**
|
||||
* Move all commits to the specified branch
|
||||
* Note: Make sure to validate beforehand that the branch ID belongs to the
|
||||
* same stream etc. THIS DOESN'T DO ANY VALIDATION!
|
||||
* @returns The amount of commits that were moved
|
||||
*/
|
||||
export async function moveCommitsToBranch(commitIds: string[], branchId: string) {
|
||||
if (!commitIds?.length) return
|
||||
|
||||
// delete old branch commits
|
||||
await BranchCommits.knex().whereIn(BranchCommits.col.commitId, commitIds).del()
|
||||
|
||||
// insert new ones
|
||||
const inserts = await BranchCommits.knex().insert(
|
||||
commitIds.map(
|
||||
(cId): BranchCommitRecord => ({
|
||||
branchId,
|
||||
commitId: cId
|
||||
})
|
||||
),
|
||||
'*'
|
||||
)
|
||||
|
||||
return inserts.length
|
||||
}
|
||||
|
||||
export async function deleteCommits(commitIds: string[]) {
|
||||
return await Commits.knex().whereIn(Commits.col.id, commitIds).del()
|
||||
}
|
||||
@@ -20,7 +20,7 @@ import {
|
||||
QueryDiscoverableStreamsArgs,
|
||||
SortDirection
|
||||
} from '@/modules/core/graph/generated/graphql'
|
||||
import { Nullable } from '@/modules/shared/helpers/typeHelper'
|
||||
import { Nullable, Optional } from '@/modules/shared/helpers/typeHelper'
|
||||
import { decodeCursor, encodeCursor } from '@/modules/shared/helpers/graphqlHelper'
|
||||
import dayjs from 'dayjs'
|
||||
import { UserWithOptionalRole } from '@/modules/core/repositories/users'
|
||||
@@ -42,25 +42,16 @@ export const STREAM_WITH_OPTIONAL_ROLE_COLUMNS = [...Streams.cols, StreamAcl.col
|
||||
export const generateId = () => cryptoRandomString({ length: 10 })
|
||||
|
||||
/**
|
||||
* Get multiple streams
|
||||
* @param {string[]} streamIds
|
||||
* Get multiple streams. If userId is specified, the role will be resolved as well.
|
||||
*/
|
||||
export async function getStreams(streamIds: string[]) {
|
||||
if (!streamIds?.length) throw new InvalidArgumentError('Invalid stream IDs')
|
||||
const q = Streams.knex<StreamRecord[]>().whereIn(Streams.col.id, streamIds)
|
||||
return await q
|
||||
}
|
||||
export async function getStreams(
|
||||
streamIds: string[],
|
||||
options: Partial<{ userId: string }> = {}
|
||||
) {
|
||||
const { userId } = options
|
||||
if (!streamIds?.length) throw new InvalidArgumentError('Empty stream IDs')
|
||||
|
||||
/**
|
||||
* Get a single stream. If userId is specified, the role will be resolved as well.
|
||||
*/
|
||||
export async function getStream(params: { streamId: string; userId?: string }) {
|
||||
const { streamId, userId } = params
|
||||
if (!streamId) throw new InvalidArgumentError('Invalid stream ID')
|
||||
|
||||
const q = Streams.knex<StreamWithOptionalRole[]>().where({
|
||||
[Streams.col.id]: streamId
|
||||
})
|
||||
const q = Streams.knex<StreamWithOptionalRole[]>().whereIn(Streams.col.id, streamIds)
|
||||
|
||||
if (userId) {
|
||||
q.select([
|
||||
@@ -74,11 +65,21 @@ export async function getStream(params: { streamId: string; userId?: string }) {
|
||||
userId
|
||||
)
|
||||
})
|
||||
q.groupBy(Streams.col.id) //
|
||||
q.groupBy(Streams.col.id)
|
||||
}
|
||||
|
||||
const res = await q.first()
|
||||
return res
|
||||
return await q
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a single stream. If userId is specified, the role will be resolved as well.
|
||||
*/
|
||||
export async function getStream(params: { streamId: string; userId?: string }) {
|
||||
const { streamId, userId } = params
|
||||
if (!streamId) throw new InvalidArgumentError('Invalid stream ID')
|
||||
|
||||
const streams = await getStreams([streamId], { userId })
|
||||
return <Optional<StreamWithOptionalRole>>streams[0]
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
'use strict'
|
||||
const crs = require('crypto-random-string')
|
||||
const knex = require('@/db/knex')
|
||||
const { getStreamBranchByName } = require('@/modules/core/repositories/branches')
|
||||
|
||||
const Streams = () => knex('streams')
|
||||
const Branches = () => knex('branches')
|
||||
@@ -66,12 +67,7 @@ module.exports = {
|
||||
},
|
||||
|
||||
async getBranchByNameAndStreamId({ streamId, name }) {
|
||||
const query = Branches()
|
||||
.select('*')
|
||||
.where({ streamId })
|
||||
.andWhere(knex.raw('LOWER(name) = ?', [name.toLowerCase()]))
|
||||
.first()
|
||||
return await query
|
||||
return await getStreamBranchByName(streamId, name)
|
||||
},
|
||||
|
||||
async deleteBranchById({ id, streamId }) {
|
||||
|
||||
@@ -0,0 +1,135 @@
|
||||
import {
|
||||
CommitInvalidAccessError,
|
||||
CommitBatchUpdateError
|
||||
} from '@/modules/core/errors/commit'
|
||||
import {
|
||||
CommitsDeleteInput,
|
||||
CommitsMoveInput
|
||||
} from '@/modules/core/graph/generated/graphql'
|
||||
import { Roles } from '@/modules/core/helpers/mainConstants'
|
||||
import { getStreamBranchByName } from '@/modules/core/repositories/branches'
|
||||
import {
|
||||
deleteCommits,
|
||||
getCommits,
|
||||
moveCommitsToBranch
|
||||
} from '@/modules/core/repositories/commits'
|
||||
import { getStreams } from '@/modules/core/repositories/streams'
|
||||
import { ensureError } from '@/modules/shared/helpers/errorHelper'
|
||||
import { difference, groupBy, keyBy } from 'lodash'
|
||||
|
||||
type CommitBatchInput = CommitsMoveInput | CommitsDeleteInput
|
||||
|
||||
/**
|
||||
* Do base validation that's going to be appropriate for all batch actions and return
|
||||
* the DB entities that were tested
|
||||
*/
|
||||
async function validateBatchBaseRules(params: CommitBatchInput, userId: string) {
|
||||
const { commitIds } = params
|
||||
|
||||
if (!userId) {
|
||||
throw new CommitInvalidAccessError(
|
||||
'User must be authenticate to operate with commits'
|
||||
)
|
||||
}
|
||||
if (!commitIds?.length) {
|
||||
throw new CommitBatchUpdateError('No commits specified')
|
||||
}
|
||||
|
||||
const commits = await getCommits(commitIds)
|
||||
const foundCommitIds = commits.map((c) => c.id)
|
||||
if (
|
||||
commitIds.length !== foundCommitIds.length ||
|
||||
difference(commitIds, foundCommitIds).length > 0
|
||||
) {
|
||||
throw new CommitBatchUpdateError('At least one of the commits does not exist')
|
||||
}
|
||||
|
||||
const streamGroups = groupBy(commits, (c) => c.streamId)
|
||||
const streamIds = Object.keys(streamGroups)
|
||||
const streams = await getStreams(streamIds, { userId })
|
||||
|
||||
if (
|
||||
streamIds.length !== streams.length ||
|
||||
difference(
|
||||
streamIds,
|
||||
streams.map((s) => s.id)
|
||||
).length > 0
|
||||
) {
|
||||
throw new CommitBatchUpdateError("At least one commit stream wasn't found")
|
||||
}
|
||||
|
||||
const streamsById = keyBy(streams, (s) => s.id)
|
||||
const commitsWithStreams = commits.map((c) => ({
|
||||
commit: c,
|
||||
stream: streamsById[c.streamId]
|
||||
}))
|
||||
|
||||
for (const { commit, stream } of commitsWithStreams) {
|
||||
if (stream.role !== Roles.Stream.Owner && commit.author !== userId) {
|
||||
throw new CommitInvalidAccessError(
|
||||
'To operate on these commits you must either own them or their streams'
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
return { commitsWithStreams, commits, streams }
|
||||
}
|
||||
|
||||
/**
|
||||
* Validate batch move params
|
||||
*/
|
||||
async function validateCommitsMove(params: CommitsMoveInput, userId: string) {
|
||||
const { targetBranch } = params
|
||||
const { streams } = await validateBatchBaseRules(params, userId)
|
||||
|
||||
if (streams.length > 1) {
|
||||
throw new CommitBatchUpdateError('Commits belong to different streams')
|
||||
}
|
||||
|
||||
const stream = streams[0]
|
||||
const branch = await getStreamBranchByName(stream.id, targetBranch)
|
||||
if (!branch) {
|
||||
throw new CommitBatchUpdateError('Invalid target branch')
|
||||
}
|
||||
|
||||
return { stream, branch }
|
||||
}
|
||||
|
||||
/**
|
||||
* Validate batch delete params
|
||||
*/
|
||||
async function validateCommitsDelete(params: CommitsDeleteInput, userId: string) {
|
||||
await validateBatchBaseRules(params, userId)
|
||||
}
|
||||
|
||||
/**
|
||||
* Move a batch of commits belonging to the same stream to another branch
|
||||
*/
|
||||
export async function batchMoveCommits(params: CommitsMoveInput, userId: string) {
|
||||
const { commitIds } = params
|
||||
|
||||
const { branch } = await validateCommitsMove(params, userId)
|
||||
|
||||
try {
|
||||
await moveCommitsToBranch(commitIds, branch.id)
|
||||
} catch (e) {
|
||||
const err = ensureError(e)
|
||||
throw new CommitBatchUpdateError('Batch commit move failed', { cause: err })
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete a batch of commits
|
||||
*/
|
||||
export async function batchDeleteCommits(params: CommitsDeleteInput, userId: string) {
|
||||
const { commitIds } = params
|
||||
|
||||
await validateCommitsDelete(params, userId)
|
||||
|
||||
try {
|
||||
await deleteCommits(commitIds)
|
||||
} catch (e) {
|
||||
const err = ensureError(e)
|
||||
throw new CommitBatchUpdateError('Batch commit delete failed', { cause: err })
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,287 @@
|
||||
import { Commits, Streams, Users } from '@/modules/core/dbSchema'
|
||||
import { Roles } from '@/modules/core/helpers/mainConstants'
|
||||
import { getCommits } from '@/modules/core/repositories/commits'
|
||||
import { createBranch } from '@/modules/core/services/branches'
|
||||
import { addOrUpdateStreamCollaborator } from '@/modules/core/services/streams/streamAccessService'
|
||||
import { BasicTestUser, createTestUsers } from '@/test/authHelper'
|
||||
import { deleteCommits, moveCommits } from '@/test/graphql/commits'
|
||||
import { truncateTables } from '@/test/hooks'
|
||||
import {
|
||||
buildAuthenticatedApolloServer,
|
||||
buildUnauthenticatedApolloServer
|
||||
} from '@/test/serverHelper'
|
||||
import { BasicTestCommit, createTestCommits } from '@/test/speckle-helpers/commitHelper'
|
||||
import { BasicTestStream, createTestStreams } from '@/test/speckle-helpers/streamHelper'
|
||||
import { ApolloServer } from 'apollo-server-express'
|
||||
import { expect } from 'chai'
|
||||
import { times } from 'lodash'
|
||||
|
||||
enum BatchActionType {
|
||||
Move,
|
||||
Delete
|
||||
}
|
||||
|
||||
const cleanup = async () => {
|
||||
await truncateTables([Streams.name, Users.name, Commits.name])
|
||||
}
|
||||
|
||||
describe('Batch commits', () => {
|
||||
const userCommmitCount = 10
|
||||
|
||||
const secondBranchName = 'second'
|
||||
|
||||
const me: BasicTestUser = {
|
||||
name: 'batch commit dude',
|
||||
email: 'batchcommitguy@gmail.com',
|
||||
id: ''
|
||||
}
|
||||
|
||||
const otherGuy: BasicTestUser = {
|
||||
name: 'other batch commit guy',
|
||||
email: 'otherbatchcommitguy@gmail.com',
|
||||
id: ''
|
||||
}
|
||||
|
||||
const myStream: BasicTestStream = {
|
||||
name: 'my first test stream',
|
||||
isPublic: false,
|
||||
ownerId: '',
|
||||
id: ''
|
||||
}
|
||||
|
||||
const otherStream: BasicTestStream = {
|
||||
name: 'other guys first test stream',
|
||||
isPublic: false,
|
||||
ownerId: '',
|
||||
id: ''
|
||||
}
|
||||
|
||||
let myCommits: BasicTestCommit[]
|
||||
|
||||
let otherCommits: BasicTestCommit[]
|
||||
|
||||
before(async () => {
|
||||
await cleanup()
|
||||
await createTestUsers([me, otherGuy])
|
||||
await createTestStreams([
|
||||
[myStream, me],
|
||||
[otherStream, otherGuy]
|
||||
])
|
||||
|
||||
await Promise.all([
|
||||
// create another branch for each stream
|
||||
createBranch({
|
||||
name: secondBranchName,
|
||||
description: '',
|
||||
streamId: myStream.id,
|
||||
authorId: me.id
|
||||
}),
|
||||
createBranch({
|
||||
name: secondBranchName,
|
||||
description: '',
|
||||
streamId: otherStream.id,
|
||||
authorId: otherGuy.id
|
||||
}),
|
||||
// add users as contributors to each others streams
|
||||
addOrUpdateStreamCollaborator(
|
||||
otherStream.id,
|
||||
me.id,
|
||||
Roles.Stream.Contributor,
|
||||
otherGuy.id
|
||||
)
|
||||
])
|
||||
|
||||
myCommits = times(
|
||||
userCommmitCount,
|
||||
(i): BasicTestCommit => ({
|
||||
id: '',
|
||||
objectId: '',
|
||||
streamId: i % 2 === 0 ? myStream.id : otherStream.id,
|
||||
authorId: me.id
|
||||
})
|
||||
)
|
||||
otherCommits = times(
|
||||
userCommmitCount,
|
||||
(): BasicTestCommit => ({
|
||||
id: '',
|
||||
objectId: '',
|
||||
streamId: otherStream.id,
|
||||
authorId: otherGuy.id
|
||||
})
|
||||
)
|
||||
|
||||
await createTestCommits([...myCommits, ...otherCommits])
|
||||
})
|
||||
|
||||
const batchActionDataSet = [
|
||||
{ display: 'move', type: BatchActionType.Move },
|
||||
{ display: 'delete', type: BatchActionType.Delete }
|
||||
]
|
||||
|
||||
const buildBatchActionInvoker =
|
||||
(apollo: ApolloServer) => (type: BatchActionType, commitIds: string[]) => {
|
||||
if (type === BatchActionType.Delete) {
|
||||
return deleteCommits(apollo, { input: { commitIds } })
|
||||
} else if (type === BatchActionType.Move) {
|
||||
return moveCommits(apollo, {
|
||||
input: { commitIds, targetBranch: secondBranchName }
|
||||
})
|
||||
} else {
|
||||
throw new Error('Unexpected batch action type')
|
||||
}
|
||||
}
|
||||
|
||||
type BatchActionInvoker = ReturnType<typeof buildBatchActionInvoker>
|
||||
|
||||
describe('when authenticated', () => {
|
||||
let apollo: ApolloServer
|
||||
let invokeBatchAction: BatchActionInvoker
|
||||
|
||||
before(async () => {
|
||||
apollo = buildAuthenticatedApolloServer(me.id)
|
||||
invokeBatchAction = buildBatchActionInvoker(apollo)
|
||||
})
|
||||
|
||||
batchActionDataSet.forEach(({ display, type }) => {
|
||||
it(`can't batch ${display} commits if not commit or stream author`, async () => {
|
||||
const result = await invokeBatchAction(
|
||||
type,
|
||||
otherCommits.map((c) => c.id)
|
||||
)
|
||||
|
||||
expect(result).to.haveGraphQLErrors('you must either own them or their streams')
|
||||
})
|
||||
|
||||
it(`can't batch ${display} an empty commit array`, async () => {
|
||||
const result = await invokeBatchAction(type, [])
|
||||
|
||||
expect(result).to.haveGraphQLErrors('No commits specified')
|
||||
})
|
||||
|
||||
it(`can't batch ${display} commits if at least one is nonexistant`, async () => {
|
||||
const result = await invokeBatchAction(type, [
|
||||
...myCommits.map((c) => c.id),
|
||||
'aaaaaaaa'
|
||||
])
|
||||
|
||||
expect(result).to.haveGraphQLErrors('one of the commits does not exist')
|
||||
})
|
||||
})
|
||||
|
||||
describe('and deleting commits', async () => {
|
||||
const deletableCommitCount = 5
|
||||
|
||||
let myDeletableCommits: BasicTestCommit[]
|
||||
|
||||
beforeEach(async () => {
|
||||
myDeletableCommits = times(
|
||||
deletableCommitCount,
|
||||
(i): BasicTestCommit => ({
|
||||
id: '',
|
||||
objectId: '',
|
||||
streamId: i % 2 === 0 ? myStream.id : otherStream.id,
|
||||
authorId: me.id
|
||||
})
|
||||
)
|
||||
|
||||
await createTestCommits(myDeletableCommits)
|
||||
})
|
||||
|
||||
const invokeDelete = (commitIds: string[]) =>
|
||||
deleteCommits(apollo, { input: { commitIds } })
|
||||
|
||||
const validateDeleted = async (commitIds: string[]) => {
|
||||
const commits = await getCommits(commitIds)
|
||||
expect(commits).to.be.empty
|
||||
}
|
||||
|
||||
it('can do it for commits of multiple streams', async () => {
|
||||
const commitIds = myDeletableCommits.map((c) => c.id)
|
||||
const result = await invokeDelete(commitIds)
|
||||
|
||||
expect(result).to.not.haveGraphQLErrors()
|
||||
await validateDeleted(commitIds)
|
||||
})
|
||||
})
|
||||
|
||||
describe('and moving commits', async () => {
|
||||
const movableCommitCount = 5
|
||||
|
||||
let myMovableCommits: BasicTestCommit[]
|
||||
|
||||
before(async () => {
|
||||
myMovableCommits = times(
|
||||
movableCommitCount,
|
||||
(i): BasicTestCommit => ({
|
||||
id: '',
|
||||
objectId: '',
|
||||
streamId: i % 2 === 0 ? myStream.id : otherStream.id,
|
||||
authorId: me.id
|
||||
})
|
||||
)
|
||||
|
||||
await createTestCommits(myMovableCommits)
|
||||
})
|
||||
|
||||
const invokeMove = (commitIds: string[], targetBranch = secondBranchName) =>
|
||||
moveCommits(apollo, { input: { commitIds, targetBranch } })
|
||||
const validateMoved = async (
|
||||
commitIds: string[],
|
||||
targetBranch = secondBranchName
|
||||
) => {
|
||||
const commits = await getCommits(commitIds)
|
||||
const areAllMoved =
|
||||
commits.length === commitIds.length &&
|
||||
commits.every((c) => c.branchName === targetBranch)
|
||||
expect(areAllMoved).to.be.true
|
||||
}
|
||||
|
||||
it("can't do it for commits belonging to multiple streams", async () => {
|
||||
const commitIds = myMovableCommits.map((c) => c.id)
|
||||
const result = await invokeMove(commitIds)
|
||||
|
||||
expect(result).to.haveGraphQLErrors('commits belong to different streams')
|
||||
})
|
||||
|
||||
it("can't do it when specifying a nonexistant target branch", async () => {
|
||||
const commitIds = myMovableCommits
|
||||
.filter((c) => c.streamId === myStream.id)
|
||||
.map((c) => c.id)
|
||||
const result = await invokeMove(commitIds, 'some-nonexistant-stream')
|
||||
|
||||
expect(result).to.haveGraphQLErrors('invalid target branch')
|
||||
})
|
||||
|
||||
it('can do it with commits belonging to the same stream', async () => {
|
||||
const commitIds = myMovableCommits
|
||||
.filter((c) => c.streamId === myStream.id)
|
||||
.map((c) => c.id)
|
||||
const result = await invokeMove(commitIds)
|
||||
|
||||
expect(result).to.not.haveGraphQLErrors()
|
||||
await validateMoved(commitIds)
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
describe('when not authenticated', () => {
|
||||
let apollo: ApolloServer
|
||||
let invokeBatchAction: BatchActionInvoker
|
||||
|
||||
before(async () => {
|
||||
apollo = buildUnauthenticatedApolloServer()
|
||||
invokeBatchAction = buildBatchActionInvoker(apollo)
|
||||
})
|
||||
|
||||
batchActionDataSet.forEach(({ display, type }) => {
|
||||
it(`can't batch ${display} commits`, async () => {
|
||||
const result = await invokeBatchAction(
|
||||
type,
|
||||
myCommits.map((c) => c.id)
|
||||
)
|
||||
|
||||
expect(result).to.haveGraphQLErrors('you do not have the required privileges')
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
@@ -20,6 +20,7 @@ const {
|
||||
getCommitsByUserId,
|
||||
getCommitsTotalCountByUserId
|
||||
} = require('../services/commits')
|
||||
const { times } = require('lodash')
|
||||
|
||||
describe('Commits @core-commits', () => {
|
||||
const user = {
|
||||
@@ -48,54 +49,92 @@ describe('Commits @core-commits', () => {
|
||||
baz: 'qux5'
|
||||
}
|
||||
|
||||
const generateObject = async (streamId = stream.id, object = testObject) =>
|
||||
await createObject(streamId, object)
|
||||
const generateStream = async (streamBase = stream, ownerId = user.id) =>
|
||||
await createStream({ ...streamBase, ownerId })
|
||||
|
||||
let commitId1, commitId2, commitId3
|
||||
|
||||
before(async () => {
|
||||
await beforeEachContext()
|
||||
|
||||
user.id = await createUser(user)
|
||||
stream.id = await createStream({ ...stream, ownerId: user.id })
|
||||
|
||||
testObject.id = await createObject(stream.id, testObject)
|
||||
testObject2.id = await createObject(stream.id, testObject2)
|
||||
testObject3.id = await createObject(stream.id, testObject3)
|
||||
})
|
||||
const testObjectId = await createObject(stream.id, testObject)
|
||||
const testObject2Id = await createObject(stream.id, testObject2)
|
||||
const testObject3Id = await createObject(stream.id, testObject3)
|
||||
|
||||
let commitId1, commitId2, commitId3
|
||||
|
||||
it('Should create a commit by branch name', async () => {
|
||||
commitId1 = await createCommitByBranchName({
|
||||
streamId: stream.id,
|
||||
branchName: 'main',
|
||||
message: 'first commit',
|
||||
sourceApplication: 'tests',
|
||||
objectId: testObject.id,
|
||||
objectId: testObjectId,
|
||||
authorId: user.id
|
||||
})
|
||||
expect(commitId1).to.be.a.string
|
||||
})
|
||||
|
||||
it('Should create a commit with a previous commit id', async () => {
|
||||
commitId2 = await createCommitByBranchName({
|
||||
streamId: stream.id,
|
||||
branchName: 'main',
|
||||
message: 'second commit',
|
||||
sourceApplication: 'tests',
|
||||
objectId: testObject2.id,
|
||||
objectId: testObject2Id,
|
||||
authorId: user.id,
|
||||
parents: [commitId1]
|
||||
})
|
||||
expect(commitId2).to.be.a.string
|
||||
|
||||
commitId3 = await createCommitByBranchName({
|
||||
streamId: stream.id,
|
||||
branchName: 'main',
|
||||
message: 'third commit',
|
||||
sourceApplication: 'tests',
|
||||
objectId: testObject3.id,
|
||||
objectId: testObject3Id,
|
||||
authorId: user.id,
|
||||
parents: [commitId1, commitId2]
|
||||
})
|
||||
})
|
||||
|
||||
it('Should create a commit by branch name', async () => {
|
||||
const objectId = await generateObject()
|
||||
const id = await createCommitByBranchName({
|
||||
streamId: stream.id,
|
||||
branchName: 'main',
|
||||
message: 'first commit',
|
||||
sourceApplication: 'tests',
|
||||
objectId,
|
||||
authorId: user.id
|
||||
})
|
||||
expect(id).to.be.a.string
|
||||
})
|
||||
|
||||
it('Should create a commit with a previous commit id', async () => {
|
||||
const objectId = await generateObject()
|
||||
const objectId2 = await generateObject()
|
||||
|
||||
const id = await createCommitByBranchName({
|
||||
streamId: stream.id,
|
||||
branchName: 'main',
|
||||
message: 'second commit',
|
||||
sourceApplication: 'tests',
|
||||
objectId,
|
||||
authorId: user.id,
|
||||
parents: [commitId1]
|
||||
})
|
||||
expect(id).to.be.a.string
|
||||
|
||||
const id2 = await createCommitByBranchName({
|
||||
streamId: stream.id,
|
||||
branchName: 'main',
|
||||
message: 'third commit',
|
||||
sourceApplication: 'tests',
|
||||
objectId: objectId2,
|
||||
authorId: user.id,
|
||||
parents: [commitId1, commitId2]
|
||||
})
|
||||
|
||||
expect(commitId3).to.be.a.string
|
||||
expect(id2).to.be.a.string
|
||||
})
|
||||
|
||||
it('Should update a commit', async () => {
|
||||
@@ -104,12 +143,13 @@ describe('Commits @core-commits', () => {
|
||||
})
|
||||
|
||||
it('Should delete a commit', async () => {
|
||||
const objectId = await generateObject()
|
||||
const tempCommit = await createCommitByBranchName({
|
||||
streamId: stream.id,
|
||||
branchName: 'main',
|
||||
message: 'temp commit',
|
||||
sourceApplication: 'tests',
|
||||
objectId: testObject.id,
|
||||
objectId,
|
||||
authorId: user.id
|
||||
})
|
||||
|
||||
@@ -123,12 +163,14 @@ describe('Commits @core-commits', () => {
|
||||
expect(cm.authorId).to.equal(user.id)
|
||||
})
|
||||
|
||||
it('Should get the commits from a branch', async () => {
|
||||
it('Should get the commits and their total count from a branch', async () => {
|
||||
const streamId = await generateStream()
|
||||
|
||||
for (let i = 0; i < 10; i++) {
|
||||
const t = { qux: i }
|
||||
t.id = await createObject(stream.id, t)
|
||||
t.id = await createObject(streamId, t)
|
||||
await createCommitByBranchName({
|
||||
streamId: stream.id,
|
||||
streamId,
|
||||
branchName: 'main',
|
||||
message: `commit # ${i + 3}`,
|
||||
sourceApplication: 'tests',
|
||||
@@ -138,7 +180,7 @@ describe('Commits @core-commits', () => {
|
||||
}
|
||||
|
||||
const { commits, cursor } = await getCommitsByBranchName({
|
||||
streamId: stream.id,
|
||||
streamId,
|
||||
branchName: 'main',
|
||||
limit: 2
|
||||
})
|
||||
@@ -146,30 +188,29 @@ describe('Commits @core-commits', () => {
|
||||
expect(commits.length).to.equal(2)
|
||||
|
||||
const { commits: commits2 } = await getCommitsByBranchName({
|
||||
streamId: stream.id,
|
||||
streamId,
|
||||
branchName: 'main',
|
||||
limit: 5,
|
||||
cursor
|
||||
})
|
||||
expect(commits2.length).to.equal(5)
|
||||
})
|
||||
|
||||
it('Should get the commit count from a branch', async () => {
|
||||
const c = await getCommitsTotalCountByBranchName({
|
||||
streamId: stream.id,
|
||||
streamId,
|
||||
branchName: 'main'
|
||||
})
|
||||
expect(c).to.equal(13)
|
||||
expect(c).to.equal(10)
|
||||
})
|
||||
|
||||
it('Should get the commits from a stream', async () => {
|
||||
await createBranch({ name: 'dim/dev', streamId: stream.id, authorId: user.id })
|
||||
it('Should get the commits and their total count from a stream', async () => {
|
||||
const streamId = await generateStream()
|
||||
await createBranch({ name: 'dim/dev', streamId, authorId: user.id })
|
||||
|
||||
for (let i = 0; i < 10; i++) {
|
||||
for (let i = 0; i < 15; i++) {
|
||||
const t = { thud: i }
|
||||
t.id = await createObject(stream.id, t)
|
||||
t.id = await createObject(streamId, t)
|
||||
await createCommitByBranchName({
|
||||
streamId: stream.id,
|
||||
streamId,
|
||||
branchName: 'dim/dev',
|
||||
message: `pushed something # ${i + 3}`,
|
||||
sourceApplication: 'tests',
|
||||
@@ -179,60 +220,106 @@ describe('Commits @core-commits', () => {
|
||||
}
|
||||
|
||||
const { commits, cursor } = await getCommitsByStreamId({
|
||||
streamId: stream.id,
|
||||
streamId,
|
||||
limit: 10
|
||||
})
|
||||
const { commits: commits2 } = await getCommitsByStreamId({
|
||||
streamId: stream.id,
|
||||
streamId,
|
||||
limit: 20,
|
||||
cursor
|
||||
})
|
||||
|
||||
expect(commits.length).to.equal(10)
|
||||
expect(commits2.length).to.equal(13)
|
||||
expect(commits2.length).to.equal(5)
|
||||
|
||||
const c = await getCommitsTotalCountByStreamId({ streamId })
|
||||
expect(c).to.equal(15)
|
||||
})
|
||||
|
||||
it('Should get the commit count of a stream', async () => {
|
||||
const c = await getCommitsTotalCountByStreamId({ streamId: stream.id })
|
||||
expect(c).to.equal(23)
|
||||
})
|
||||
describe('when reading user commits', async () => {
|
||||
const otherUser = {
|
||||
name: 'Dimitrie Other',
|
||||
email: 'otthhherrdidimitrie4342@gmail.com',
|
||||
password: 'sn3aky-1337-b1m'
|
||||
}
|
||||
|
||||
it('Should get the commits of a user', async () => {
|
||||
const { commits, cursor } = await getCommitsByUserId({ userId: user.id, limit: 3 })
|
||||
const otherStream = {
|
||||
name: 'Other Test Stream References',
|
||||
description: 'Whatever goes in here usually...'
|
||||
}
|
||||
|
||||
const { commits: commits2 } = await getCommitsByUserId({
|
||||
userId: user.id,
|
||||
limit: 100,
|
||||
cursor
|
||||
const mainCommitCount = 16
|
||||
|
||||
before(async () => {
|
||||
otherUser.id = await createUser(otherUser)
|
||||
otherStream.id = await generateStream(otherStream, otherUser.id)
|
||||
|
||||
// create objects
|
||||
const objectIds = await Promise.all(
|
||||
times(mainCommitCount, () => generateObject(otherStream.id))
|
||||
)
|
||||
|
||||
// create commits
|
||||
await Promise.all(
|
||||
objectIds.map((oid) =>
|
||||
createCommitByBranchName({
|
||||
streamId: otherStream.id,
|
||||
branchName: 'main',
|
||||
message: 'first commit',
|
||||
sourceApplication: 'tests',
|
||||
objectId: oid,
|
||||
authorId: otherUser.id
|
||||
})
|
||||
)
|
||||
)
|
||||
})
|
||||
|
||||
expect(commits.length).to.equal(3)
|
||||
expect(commits2.length).to.equal(20)
|
||||
})
|
||||
it('Should get the commits of a user', async () => {
|
||||
const { commits, cursor } = await getCommitsByUserId({
|
||||
userId: otherUser.id,
|
||||
limit: 3
|
||||
})
|
||||
|
||||
it('Should get the public commits of an user only', async () => {
|
||||
const privateStreamId = await createStream({
|
||||
name: 'private',
|
||||
isPublic: false,
|
||||
ownerId: user.id
|
||||
})
|
||||
const objectId = await createObject(privateStreamId, testObject)
|
||||
await createCommitByBranchName({
|
||||
streamId: privateStreamId,
|
||||
branchName: 'main',
|
||||
message: 'first commit',
|
||||
sourceApplication: 'tests',
|
||||
objectId,
|
||||
authorId: user.id
|
||||
const { commits: commits2 } = await getCommitsByUserId({
|
||||
userId: otherUser.id,
|
||||
limit: 100,
|
||||
cursor
|
||||
})
|
||||
|
||||
expect(commits.length).to.equal(3)
|
||||
expect(commits2.length).to.equal(mainCommitCount - 3)
|
||||
})
|
||||
|
||||
const { commits } = await getCommitsByUserId({ userId: user.id, limit: 1000 })
|
||||
expect(commits.length).to.equal(23)
|
||||
})
|
||||
it('Should get the public commits of an user only', async () => {
|
||||
const privateStreamId = await createStream({
|
||||
name: 'private',
|
||||
isPublic: false,
|
||||
ownerId: otherUser.id
|
||||
})
|
||||
const objectId = await createObject(privateStreamId, testObject)
|
||||
const commitId = await createCommitByBranchName({
|
||||
streamId: privateStreamId,
|
||||
branchName: 'main',
|
||||
message: 'first commit',
|
||||
sourceApplication: 'tests',
|
||||
objectId,
|
||||
authorId: otherUser.id
|
||||
})
|
||||
|
||||
it('Should get the commit count of an user', async () => {
|
||||
const c = await getCommitsTotalCountByUserId({ userId: user.id })
|
||||
expect(c).to.equal(24)
|
||||
const { commits } = await getCommitsByUserId({
|
||||
userId: otherUser.id,
|
||||
limit: 1000
|
||||
})
|
||||
expect(commits.length).to.equal(mainCommitCount)
|
||||
|
||||
// clean up
|
||||
await deleteCommit({ id: commitId })
|
||||
})
|
||||
|
||||
it('Should get the commit count of an user', async () => {
|
||||
const c = await getCommitsTotalCountByUserId({ userId: otherUser.id })
|
||||
expect(c).to.equal(mainCommitCount)
|
||||
})
|
||||
})
|
||||
|
||||
it('Commits should have source, total count, branch name and parents fields', async () => {
|
||||
|
||||
@@ -299,7 +299,7 @@ describe('Streams @core-streams', () => {
|
||||
streamId: updatableStream.id,
|
||||
name: 'dim/lol'
|
||||
})
|
||||
await deleteBranchById({ id: b.id, streamId: updatableStream.id })
|
||||
await deleteBranchById({ id: b!.id, streamId: updatableStream.id })
|
||||
|
||||
const su2 = await getStream({ streamId: updatableStream.id })
|
||||
expect(su2?.updatedAt).to.be.ok
|
||||
|
||||
@@ -0,0 +1,87 @@
|
||||
import {
|
||||
DeleteCommitsMutation,
|
||||
DeleteCommitsMutationVariables,
|
||||
MoveCommitsMutation,
|
||||
MoveCommitsMutationVariables,
|
||||
ReadStreamBranchCommitsQuery,
|
||||
ReadStreamBranchCommitsQueryVariables
|
||||
} from '@/test/graphql/generated/graphql'
|
||||
import { executeOperation } from '@/test/graphqlHelper'
|
||||
import { ApolloServer, gql } from 'apollo-server-express'
|
||||
|
||||
const readStreamBranchCommitsQuery = gql`
|
||||
query ReadStreamBranchCommits(
|
||||
$streamId: String!
|
||||
$branchName: String!
|
||||
$cursor: String
|
||||
$limit: Int! = 10
|
||||
) {
|
||||
stream(id: $streamId) {
|
||||
id
|
||||
name
|
||||
role
|
||||
branch(name: $branchName) {
|
||||
id
|
||||
name
|
||||
description
|
||||
commits(cursor: $cursor, limit: $limit) {
|
||||
totalCount
|
||||
cursor
|
||||
items {
|
||||
id
|
||||
authorName
|
||||
authorId
|
||||
authorAvatar
|
||||
sourceApplication
|
||||
message
|
||||
referencedObject
|
||||
createdAt
|
||||
commentCount
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
`
|
||||
|
||||
const moveCommitsMutation = gql`
|
||||
mutation MoveCommits($input: CommitsMoveInput!) {
|
||||
commitsMove(input: $input)
|
||||
}
|
||||
`
|
||||
|
||||
const deleteCommitsMutation = gql`
|
||||
mutation DeleteCommits($input: CommitsDeleteInput!) {
|
||||
commitsDelete(input: $input)
|
||||
}
|
||||
`
|
||||
|
||||
export const readStreamBranchCommits = (
|
||||
apollo: ApolloServer,
|
||||
variables: ReadStreamBranchCommitsQueryVariables
|
||||
) =>
|
||||
executeOperation<ReadStreamBranchCommitsQuery, ReadStreamBranchCommitsQueryVariables>(
|
||||
apollo,
|
||||
readStreamBranchCommitsQuery,
|
||||
variables
|
||||
)
|
||||
|
||||
export const moveCommits = (
|
||||
apollo: ApolloServer,
|
||||
variables: MoveCommitsMutationVariables
|
||||
) =>
|
||||
executeOperation<MoveCommitsMutation, MoveCommitsMutationVariables>(
|
||||
apollo,
|
||||
moveCommitsMutation,
|
||||
variables
|
||||
)
|
||||
|
||||
export const deleteCommits = (
|
||||
apollo: ApolloServer,
|
||||
variables: DeleteCommitsMutationVariables
|
||||
) =>
|
||||
executeOperation<DeleteCommitsMutation, DeleteCommitsMutationVariables>(
|
||||
apollo,
|
||||
deleteCommitsMutation,
|
||||
variables
|
||||
)
|
||||
@@ -159,7 +159,7 @@ export type BranchCommitsArgs = {
|
||||
export type BranchCollection = {
|
||||
__typename?: 'BranchCollection';
|
||||
cursor?: Maybe<Scalars['String']>;
|
||||
items?: Maybe<Array<Maybe<Branch>>>;
|
||||
items?: Maybe<Array<Branch>>;
|
||||
totalCount: Scalars['Int'];
|
||||
};
|
||||
|
||||
@@ -334,7 +334,10 @@ export type CommitCreateInput = {
|
||||
message?: InputMaybe<Scalars['String']>;
|
||||
objectId: Scalars['String'];
|
||||
parents?: InputMaybe<Array<InputMaybe<Scalars['String']>>>;
|
||||
/** **DEPRECATED** Use the `parents` field. */
|
||||
/**
|
||||
* **DEPRECATED** Use the `parents` field.
|
||||
* @deprecated Field no longer supported
|
||||
*/
|
||||
previousCommitIds?: InputMaybe<Array<InputMaybe<Scalars['String']>>>;
|
||||
sourceApplication?: InputMaybe<Scalars['String']>;
|
||||
streamId: Scalars['String'];
|
||||
@@ -361,6 +364,15 @@ export type CommitUpdateInput = {
|
||||
streamId: Scalars['String'];
|
||||
};
|
||||
|
||||
export type CommitsDeleteInput = {
|
||||
commitIds: Array<Scalars['String']>;
|
||||
};
|
||||
|
||||
export type CommitsMoveInput = {
|
||||
commitIds: Array<Scalars['String']>;
|
||||
targetBranch: Scalars['String'];
|
||||
};
|
||||
|
||||
export enum DiscoverableStreamsSortType {
|
||||
CreatedDate = 'CREATED_DATE',
|
||||
FavoritesCount = 'FAVORITES_COUNT'
|
||||
@@ -440,6 +452,10 @@ export type Mutation = {
|
||||
commitDelete: Scalars['Boolean'];
|
||||
commitReceive: Scalars['Boolean'];
|
||||
commitUpdate: Scalars['Boolean'];
|
||||
/** Delete a batch of commits */
|
||||
commitsDelete: Scalars['Boolean'];
|
||||
/** Move a batch of commits to a new branch */
|
||||
commitsMove: Scalars['Boolean'];
|
||||
/** Delete a pending invite */
|
||||
inviteDelete: Scalars['Boolean'];
|
||||
/** Re-send a pending invite */
|
||||
@@ -593,6 +609,16 @@ export type MutationCommitUpdateArgs = {
|
||||
};
|
||||
|
||||
|
||||
export type MutationCommitsDeleteArgs = {
|
||||
input: CommitsDeleteInput;
|
||||
};
|
||||
|
||||
|
||||
export type MutationCommitsMoveArgs = {
|
||||
input: CommitsMoveInput;
|
||||
};
|
||||
|
||||
|
||||
export type MutationInviteDeleteArgs = {
|
||||
inviteId: Scalars['String'];
|
||||
};
|
||||
@@ -1675,6 +1701,30 @@ export type GetCommentsQueryVariables = Exact<{
|
||||
|
||||
export type GetCommentsQuery = { __typename?: 'Query', comments?: { __typename?: 'CommentCollection', totalCount: number, cursor?: string | null, items: Array<{ __typename?: 'Comment', id: string, text: { __typename?: 'SmartTextEditorValue', doc?: Record<string, unknown> | null, attachments?: Array<{ __typename?: 'BlobMetadata', id: string, fileName: string, streamId: string }> | null }, replies?: { __typename?: 'CommentCollection', items: Array<{ __typename?: 'Comment', id: string, text: { __typename?: 'SmartTextEditorValue', doc?: Record<string, unknown> | null, attachments?: Array<{ __typename?: 'BlobMetadata', id: string, fileName: string, streamId: string }> | null } }> } | null }> } | null };
|
||||
|
||||
export type ReadStreamBranchCommitsQueryVariables = Exact<{
|
||||
streamId: Scalars['String'];
|
||||
branchName: Scalars['String'];
|
||||
cursor?: InputMaybe<Scalars['String']>;
|
||||
limit?: Scalars['Int'];
|
||||
}>;
|
||||
|
||||
|
||||
export type ReadStreamBranchCommitsQuery = { __typename?: 'Query', stream?: { __typename?: 'Stream', id: string, name: string, role?: string | null, branch?: { __typename?: 'Branch', id: string, name: string, description?: string | null, commits?: { __typename?: 'CommitCollection', totalCount: number, cursor?: string | null, items?: Array<{ __typename?: 'Commit', id: string, authorName?: string | null, authorId?: string | null, authorAvatar?: string | null, sourceApplication?: string | null, message?: string | null, referencedObject: string, createdAt?: string | null, commentCount: number } | null> | null } | null } | null } | null };
|
||||
|
||||
export type MoveCommitsMutationVariables = Exact<{
|
||||
input: CommitsMoveInput;
|
||||
}>;
|
||||
|
||||
|
||||
export type MoveCommitsMutation = { __typename?: 'Mutation', commitsMove: boolean };
|
||||
|
||||
export type DeleteCommitsMutationVariables = Exact<{
|
||||
input: CommitsDeleteInput;
|
||||
}>;
|
||||
|
||||
|
||||
export type DeleteCommitsMutation = { __typename?: 'Mutation', commitsDelete: boolean };
|
||||
|
||||
export type CreateServerInviteMutationVariables = Exact<{
|
||||
input: ServerInviteCreateInput;
|
||||
}>;
|
||||
|
||||
@@ -13,8 +13,8 @@ type TypedGraphqlResponse<R = Record<string, any>> = GraphQLResponse & {
|
||||
* a properly typed response
|
||||
*/
|
||||
export async function executeOperation<
|
||||
R = Record<string, any>,
|
||||
V = Record<string, unknown>
|
||||
R extends Record<string, any> = Record<string, any>,
|
||||
V extends Record<string, any> = Record<string, any>
|
||||
>(
|
||||
apollo: ApolloServer,
|
||||
query: DocumentNode,
|
||||
|
||||
@@ -0,0 +1,61 @@
|
||||
import { createCommitByBranchName } from '@/modules/core/services/commits'
|
||||
import { createObject } from '@/modules/core/services/objects'
|
||||
|
||||
export type BasicTestCommit = {
|
||||
/**
|
||||
* Can be left empty, will be filled on creation
|
||||
*/
|
||||
id: string
|
||||
/**
|
||||
* Can be left empty, will be filled on creation
|
||||
*/
|
||||
objectId: string
|
||||
streamId: string
|
||||
authorId: string
|
||||
/**
|
||||
* Defaults to 'main'
|
||||
*/
|
||||
branchName?: string
|
||||
/**
|
||||
* Auto-generated, if empty
|
||||
*/
|
||||
message?: string
|
||||
|
||||
/**
|
||||
* Empty array by default
|
||||
*/
|
||||
parents?: string[]
|
||||
}
|
||||
|
||||
/**
|
||||
* Ensure all commits have objectId set
|
||||
*/
|
||||
async function ensureObjects(commits: BasicTestCommit[]) {
|
||||
const commitsWithoutObjects = commits.filter((c) => !c.objectId)
|
||||
await Promise.all(
|
||||
commitsWithoutObjects.map((c) =>
|
||||
createObject(c.streamId, { foo: 'bar' }).then((oid) => (c.objectId = oid))
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Create test commits
|
||||
*/
|
||||
export async function createTestCommits(commits: BasicTestCommit[]) {
|
||||
await ensureObjects(commits)
|
||||
await Promise.all(
|
||||
commits.map((c) =>
|
||||
createCommitByBranchName({
|
||||
streamId: c.streamId,
|
||||
branchName: 'main',
|
||||
message: c.message || 'this message is auto generated',
|
||||
sourceApplication: 'tests',
|
||||
objectId: c.objectId,
|
||||
authorId: c.authorId,
|
||||
totalChildrenCount: 0,
|
||||
parents: c.parents || []
|
||||
}).then((cid) => (c.id = cid))
|
||||
)
|
||||
)
|
||||
}
|
||||
Reference in New Issue
Block a user