first commit
This commit is contained in:
1
libs/web/tracker/infrastructure/src/index.ts
Normal file
1
libs/web/tracker/infrastructure/src/index.ts
Normal file
@@ -0,0 +1 @@
|
||||
export * from './lib/tracker-infrastructure.providers';
|
||||
@@ -0,0 +1,108 @@
|
||||
import { inject, Injectable } from '@angular/core';
|
||||
import {
|
||||
Activity,
|
||||
ActivityGateway,
|
||||
CommandResult,
|
||||
Comment,
|
||||
CommentsGateway,
|
||||
CreateReleaseInput,
|
||||
Release,
|
||||
ReleasesGateway,
|
||||
UserDirectoryEntry,
|
||||
UsersGateway,
|
||||
} from '@task-stream/web/tracker/domain';
|
||||
import { map, Observable } from 'rxjs';
|
||||
import {
|
||||
ActivityDocument,
|
||||
AddCommentDocument,
|
||||
ArchiveReleaseDocument,
|
||||
CreateReleaseDocument,
|
||||
DeleteCommentDocument,
|
||||
EditCommentDocument,
|
||||
IssueCommentsDocument,
|
||||
PublishReleaseDocument,
|
||||
ReleasesDocument,
|
||||
SearchUsersDocument,
|
||||
} from '../graphql/generated';
|
||||
import { GraphqlOperations } from './graphql-operations';
|
||||
import { toActivity, toCommandResult, toComment, toRelease } from './mappers';
|
||||
|
||||
@Injectable()
|
||||
export class GraphqlCommentsGateway extends CommentsGateway {
|
||||
private readonly gql = inject(GraphqlOperations);
|
||||
|
||||
list(issueId: string): Observable<Comment[]> {
|
||||
return this.gql
|
||||
.query(IssueCommentsDocument, { idOrKey: issueId })
|
||||
.pipe(map((d) => d.issue.comments.items.map(toComment)));
|
||||
}
|
||||
|
||||
add(issueId: string, body: string): Observable<CommandResult> {
|
||||
return this.gql
|
||||
.mutate(AddCommentDocument, { issueId, body })
|
||||
.pipe(map((d) => toCommandResult(d.addComment)));
|
||||
}
|
||||
|
||||
edit(commentId: string, body: string): Observable<CommandResult> {
|
||||
return this.gql
|
||||
.mutate(EditCommentDocument, { commentId, body })
|
||||
.pipe(map((d) => toCommandResult(d.editComment)));
|
||||
}
|
||||
|
||||
remove(commentId: string): Observable<CommandResult> {
|
||||
return this.gql
|
||||
.mutate(DeleteCommentDocument, { commentId })
|
||||
.pipe(map((d) => toCommandResult(d.deleteComment)));
|
||||
}
|
||||
}
|
||||
|
||||
@Injectable()
|
||||
export class GraphqlReleasesGateway extends ReleasesGateway {
|
||||
private readonly gql = inject(GraphqlOperations);
|
||||
|
||||
list(projectId: string): Observable<Release[]> {
|
||||
return this.gql
|
||||
.query(ReleasesDocument, { idOrKey: projectId })
|
||||
.pipe(map((d) => d.project.releases.map(toRelease)));
|
||||
}
|
||||
|
||||
create(input: CreateReleaseInput): Observable<CommandResult> {
|
||||
return this.gql
|
||||
.mutate(CreateReleaseDocument, { input })
|
||||
.pipe(map((d) => toCommandResult(d.createRelease)));
|
||||
}
|
||||
|
||||
publish(releaseId: string, force: boolean): Observable<CommandResult> {
|
||||
return this.gql
|
||||
.mutate(PublishReleaseDocument, { releaseId, force })
|
||||
.pipe(map((d) => toCommandResult(d.publishRelease)));
|
||||
}
|
||||
|
||||
archive(releaseId: string): Observable<CommandResult> {
|
||||
return this.gql
|
||||
.mutate(ArchiveReleaseDocument, { releaseId })
|
||||
.pipe(map((d) => toCommandResult(d.archiveRelease)));
|
||||
}
|
||||
}
|
||||
|
||||
@Injectable()
|
||||
export class GraphqlActivityGateway extends ActivityGateway {
|
||||
private readonly gql = inject(GraphqlOperations);
|
||||
|
||||
list(projectId: string, issueId?: string): Observable<Activity[]> {
|
||||
return this.gql
|
||||
.query(ActivityDocument, { projectId, issueId: issueId ?? null })
|
||||
.pipe(map((d) => d.activity.map(toActivity)));
|
||||
}
|
||||
}
|
||||
|
||||
@Injectable()
|
||||
export class GraphqlUsersGateway extends UsersGateway {
|
||||
private readonly gql = inject(GraphqlOperations);
|
||||
|
||||
search(term: string): Observable<UserDirectoryEntry[]> {
|
||||
return this.gql
|
||||
.query(SearchUsersDocument, { search: term })
|
||||
.pipe(map((d) => d.users));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,112 @@
|
||||
import { inject, Injectable } from '@angular/core';
|
||||
import {
|
||||
BoardColumn,
|
||||
BoardFilter,
|
||||
ChangeStatusInput,
|
||||
CommandResult,
|
||||
CreateIssueInput,
|
||||
Issue,
|
||||
IssuesGateway,
|
||||
IssueSummary,
|
||||
LinkIssuesInput,
|
||||
UpdateIssueInput,
|
||||
} from '@task-stream/web/tracker/domain';
|
||||
import { map, Observable } from 'rxjs';
|
||||
import {
|
||||
AssignIssueDocument,
|
||||
BoardDocument,
|
||||
ChangeIssueStatusDocument,
|
||||
CreateIssueDocument,
|
||||
IssueDocument,
|
||||
LinkIssuesDocument,
|
||||
SearchIssuesDocument,
|
||||
SetIssueReleaseDocument,
|
||||
UnlinkIssuesDocument,
|
||||
UpdateIssueDocument,
|
||||
} from '../graphql/generated';
|
||||
import { GraphqlOperations } from './graphql-operations';
|
||||
import { toCommandResult, toIssue, toIssueSummary } from './mappers';
|
||||
|
||||
@Injectable()
|
||||
export class GraphqlIssuesGateway extends IssuesGateway {
|
||||
private readonly gql = inject(GraphqlOperations);
|
||||
|
||||
board(filter: BoardFilter): Observable<BoardColumn[]> {
|
||||
return this.gql
|
||||
.query(BoardDocument, {
|
||||
idOrKey: filter.projectId,
|
||||
releaseId: filter.releaseId ?? null,
|
||||
assigneeId: filter.assigneeId ?? null,
|
||||
area: filter.area ?? null,
|
||||
})
|
||||
.pipe(
|
||||
map((d) =>
|
||||
d.project.board.map((column) => ({
|
||||
status: column.status as BoardColumn['status'],
|
||||
total: column.total,
|
||||
issues: column.issues.map(toIssueSummary),
|
||||
})),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
issue(idOrKey: string): Observable<Issue> {
|
||||
return this.gql
|
||||
.query(IssueDocument, { idOrKey })
|
||||
.pipe(map((d) => toIssue(d.issue)));
|
||||
}
|
||||
|
||||
search(projectId: string, text: string): Observable<IssueSummary[]> {
|
||||
return this.gql
|
||||
.query(SearchIssuesDocument, { filter: { projectId, search: text } })
|
||||
.pipe(map((d) => d.issues.items.map(toIssueSummary)));
|
||||
}
|
||||
|
||||
create(input: CreateIssueInput): Observable<CommandResult> {
|
||||
return this.gql
|
||||
.mutate(CreateIssueDocument, { input: input as never })
|
||||
.pipe(map((d) => toCommandResult(d.createIssue)));
|
||||
}
|
||||
|
||||
update(input: UpdateIssueInput): Observable<CommandResult> {
|
||||
return this.gql
|
||||
.mutate(UpdateIssueDocument, { input: input as never })
|
||||
.pipe(map((d) => toCommandResult(d.updateIssue)));
|
||||
}
|
||||
|
||||
changeStatus(input: ChangeStatusInput): Observable<CommandResult> {
|
||||
return this.gql
|
||||
.mutate(ChangeIssueStatusDocument, { input: input as never })
|
||||
.pipe(map((d) => toCommandResult(d.changeIssueStatus)));
|
||||
}
|
||||
|
||||
assign(
|
||||
issueId: string,
|
||||
assigneeId: string | null,
|
||||
): Observable<CommandResult> {
|
||||
return this.gql
|
||||
.mutate(AssignIssueDocument, { issueId, assigneeId })
|
||||
.pipe(map((d) => toCommandResult(d.assignIssue)));
|
||||
}
|
||||
|
||||
link(input: LinkIssuesInput): Observable<CommandResult> {
|
||||
return this.gql
|
||||
.mutate(LinkIssuesDocument, { input: input as never })
|
||||
.pipe(map((d) => toCommandResult(d.linkIssues)));
|
||||
}
|
||||
|
||||
unlink(input: LinkIssuesInput): Observable<CommandResult> {
|
||||
return this.gql
|
||||
.mutate(UnlinkIssuesDocument, { input: input as never })
|
||||
.pipe(map((d) => toCommandResult(d.unlinkIssues)));
|
||||
}
|
||||
|
||||
setRelease(
|
||||
issueId: string,
|
||||
releaseId: string | null,
|
||||
): Observable<CommandResult> {
|
||||
return this.gql
|
||||
.mutate(SetIssueReleaseDocument, { issueId, releaseId })
|
||||
.pipe(map((d) => toCommandResult(d.setIssueRelease)));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,46 @@
|
||||
import { inject, Injectable } from '@angular/core';
|
||||
import type { TypedDocumentNode } from '@graphql-typed-document-node/core';
|
||||
import { Apollo } from 'apollo-angular';
|
||||
import { filter, map, Observable } from 'rxjs';
|
||||
|
||||
type Vars = Record<string, unknown>;
|
||||
|
||||
/** Thin typed wrapper: one-shot queries/mutations and subscriptions as plain data streams. */
|
||||
@Injectable({ providedIn: 'root' })
|
||||
export class GraphqlOperations {
|
||||
private readonly apollo = inject(Apollo);
|
||||
|
||||
query<TData, TVars extends Vars>(
|
||||
document: TypedDocumentNode<TData, TVars>,
|
||||
variables?: TVars,
|
||||
): Observable<TData> {
|
||||
return this.apollo
|
||||
.query<TData, TVars>({ query: document, variables: variables as TVars })
|
||||
.pipe(map((result) => result.data as TData));
|
||||
}
|
||||
|
||||
mutate<TData, TVars extends Vars>(
|
||||
document: TypedDocumentNode<TData, TVars>,
|
||||
variables: TVars,
|
||||
): Observable<TData> {
|
||||
return this.apollo
|
||||
.mutate<TData, TVars>({ mutation: document, variables })
|
||||
.pipe(map((result) => result.data as TData));
|
||||
}
|
||||
|
||||
subscribe<TData, TVars extends Vars>(
|
||||
document: TypedDocumentNode<TData, TVars>,
|
||||
variables?: TVars,
|
||||
): Observable<TData> {
|
||||
return this.apollo
|
||||
.subscribe<
|
||||
TData,
|
||||
TVars
|
||||
>({ query: document, variables: variables as TVars })
|
||||
.pipe(
|
||||
map((result) => result.data),
|
||||
filter((data): data is NonNullable<typeof data> => !!data),
|
||||
map((data) => data as TData),
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,68 @@
|
||||
import { inject, Injectable } from '@angular/core';
|
||||
import {
|
||||
CommandResult,
|
||||
CreateProjectInput,
|
||||
MemberInput,
|
||||
Project,
|
||||
ProjectsGateway,
|
||||
ProjectStats,
|
||||
} from '@task-stream/web/tracker/domain';
|
||||
import { map, Observable } from 'rxjs';
|
||||
import {
|
||||
AddProjectMemberDocument,
|
||||
ChangeProjectMemberRoleDocument,
|
||||
CreateProjectDocument,
|
||||
MyProjectsDocument,
|
||||
ProjectDocument,
|
||||
ProjectStatsDocument,
|
||||
RemoveProjectMemberDocument,
|
||||
} from '../graphql/generated';
|
||||
import { GraphqlOperations } from './graphql-operations';
|
||||
import { toCommandResult, toProject, toStats } from './mappers';
|
||||
|
||||
@Injectable()
|
||||
export class GraphqlProjectsGateway extends ProjectsGateway {
|
||||
private readonly gql = inject(GraphqlOperations);
|
||||
|
||||
myProjects(): Observable<Project[]> {
|
||||
return this.gql
|
||||
.query(MyProjectsDocument)
|
||||
.pipe(map((d) => d.myProjects.map(toProject)));
|
||||
}
|
||||
|
||||
project(idOrKey: string): Observable<Project> {
|
||||
return this.gql
|
||||
.query(ProjectDocument, { idOrKey })
|
||||
.pipe(map((d) => toProject(d.project)));
|
||||
}
|
||||
|
||||
stats(projectId: string): Observable<ProjectStats> {
|
||||
return this.gql
|
||||
.query(ProjectStatsDocument, { idOrKey: projectId })
|
||||
.pipe(map((d) => toStats(d.project.stats)));
|
||||
}
|
||||
|
||||
create(input: CreateProjectInput): Observable<CommandResult> {
|
||||
return this.gql
|
||||
.mutate(CreateProjectDocument, { input })
|
||||
.pipe(map((d) => toCommandResult(d.createProject)));
|
||||
}
|
||||
|
||||
addMember(input: MemberInput): Observable<CommandResult> {
|
||||
return this.gql
|
||||
.mutate(AddProjectMemberDocument, { input: input as never })
|
||||
.pipe(map((d) => toCommandResult(d.addProjectMember)));
|
||||
}
|
||||
|
||||
changeMemberRole(input: MemberInput): Observable<CommandResult> {
|
||||
return this.gql
|
||||
.mutate(ChangeProjectMemberRoleDocument, { input: input as never })
|
||||
.pipe(map((d) => toCommandResult(d.changeProjectMemberRole)));
|
||||
}
|
||||
|
||||
removeMember(projectId: string, userId: string): Observable<CommandResult> {
|
||||
return this.gql
|
||||
.mutate(RemoveProjectMemberDocument, { projectId, userId })
|
||||
.pipe(map((d) => toCommandResult(d.removeProjectMember)));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,77 @@
|
||||
import { inject, Injectable } from '@angular/core';
|
||||
import {
|
||||
Activity,
|
||||
CommentChanged,
|
||||
IssueChanged,
|
||||
Project,
|
||||
Release,
|
||||
TrackerRealtimeGateway,
|
||||
} from '@task-stream/web/tracker/domain';
|
||||
import { map, Observable } from 'rxjs';
|
||||
import {
|
||||
ActivityAddedDocument,
|
||||
CommentChangedDocument,
|
||||
IssueChangedDocument,
|
||||
MyProjectsChangedDocument,
|
||||
ProjectChangedDocument,
|
||||
ReleaseChangedDocument,
|
||||
} from '../graphql/generated';
|
||||
import { GraphqlOperations } from './graphql-operations';
|
||||
import {
|
||||
toActivity,
|
||||
toComment,
|
||||
toIssueSummary,
|
||||
toProject,
|
||||
toRelease,
|
||||
} from './mappers';
|
||||
|
||||
/** GraphQL subscriptions (graphql-ws). Each stream lives while it is subscribed. */
|
||||
@Injectable()
|
||||
export class GraphqlRealtimeGateway extends TrackerRealtimeGateway {
|
||||
private readonly gql = inject(GraphqlOperations);
|
||||
|
||||
issueChanges(projectId: string): Observable<IssueChanged> {
|
||||
return this.gql.subscribe(IssueChangedDocument, { projectId }).pipe(
|
||||
map(({ issueChanged }) => ({
|
||||
...issueChanged,
|
||||
issue: toIssueSummary(issueChanged.issue),
|
||||
})),
|
||||
);
|
||||
}
|
||||
|
||||
commentChanges(issueId: string): Observable<CommentChanged> {
|
||||
return this.gql.subscribe(CommentChangedDocument, { issueId }).pipe(
|
||||
map(({ commentChanged }) => ({
|
||||
action: commentChanged.action,
|
||||
commentId: commentChanged.commentId,
|
||||
comment: commentChanged.comment
|
||||
? toComment(commentChanged.comment)
|
||||
: null,
|
||||
})),
|
||||
);
|
||||
}
|
||||
|
||||
activity(projectId: string): Observable<Activity> {
|
||||
return this.gql
|
||||
.subscribe(ActivityAddedDocument, { projectId })
|
||||
.pipe(map((d) => toActivity(d.activityAdded)));
|
||||
}
|
||||
|
||||
releaseChanges(projectId: string): Observable<Release> {
|
||||
return this.gql
|
||||
.subscribe(ReleaseChangedDocument, { projectId })
|
||||
.pipe(map((d) => toRelease(d.releaseChanged.release)));
|
||||
}
|
||||
|
||||
projectChanges(projectId: string): Observable<Project> {
|
||||
return this.gql
|
||||
.subscribe(ProjectChangedDocument, { projectId })
|
||||
.pipe(map((d) => toProject(d.projectChanged.project)));
|
||||
}
|
||||
|
||||
myProjectsChanges(): Observable<Project> {
|
||||
return this.gql
|
||||
.subscribe(MyProjectsChangedDocument)
|
||||
.pipe(map((d) => toProject(d.myProjectsChanged.project)));
|
||||
}
|
||||
}
|
||||
55
libs/web/tracker/infrastructure/src/lib/adapters/mappers.ts
Normal file
55
libs/web/tracker/infrastructure/src/lib/adapters/mappers.ts
Normal file
@@ -0,0 +1,55 @@
|
||||
import type {
|
||||
Activity,
|
||||
Comment,
|
||||
CommandResult,
|
||||
Issue,
|
||||
IssueSummary,
|
||||
Project,
|
||||
ProjectStats,
|
||||
Release,
|
||||
} from '@task-stream/web/tracker/domain';
|
||||
import type {
|
||||
ActivityFieldsFragment,
|
||||
CommandResultFieldsFragment,
|
||||
CommentFieldsFragment,
|
||||
IssueFieldsFragment,
|
||||
IssueSummaryFieldsFragment,
|
||||
ProjectFieldsFragment,
|
||||
ProjectStatsQuery,
|
||||
ReleaseFieldsFragment,
|
||||
} from '../graphql/generated';
|
||||
|
||||
/*
|
||||
* GraphQL → domain. The generated types already have the domain's shape;
|
||||
* enums arrive as string unions with the same values as the domain enums,
|
||||
* so mapping is a typed re-labelling kept in one place.
|
||||
*/
|
||||
|
||||
const as = <T>(value: unknown): T => value as T;
|
||||
|
||||
export const toProject = (p: ProjectFieldsFragment): Project => as<Project>(p);
|
||||
export const toIssueSummary = (i: IssueSummaryFieldsFragment): IssueSummary =>
|
||||
as<IssueSummary>(i);
|
||||
export const toIssue = (i: IssueFieldsFragment): Issue => as<Issue>(i);
|
||||
export const toComment = (c: CommentFieldsFragment): Comment => c;
|
||||
export const toActivity = (a: ActivityFieldsFragment): Activity => a;
|
||||
export const toCommandResult = (
|
||||
r: CommandResultFieldsFragment,
|
||||
): CommandResult => r;
|
||||
|
||||
export function toRelease(
|
||||
r: ReleaseFieldsFragment & {
|
||||
progress?: { total: number; finished: number; percent: number };
|
||||
},
|
||||
): Release {
|
||||
return as<Release>({
|
||||
...r,
|
||||
progress: r.progress ?? { total: 0, finished: 0, percent: 0 },
|
||||
});
|
||||
}
|
||||
|
||||
export function toStats(
|
||||
stats: NonNullable<ProjectStatsQuery['project']>['stats'],
|
||||
): ProjectStats {
|
||||
return as<ProjectStats>(stats);
|
||||
}
|
||||
@@ -0,0 +1,102 @@
|
||||
fragment UserRefFields on UserRef {
|
||||
id
|
||||
name
|
||||
}
|
||||
|
||||
fragment ProjectFields on Project {
|
||||
id
|
||||
key
|
||||
name
|
||||
description
|
||||
updatedAt
|
||||
version
|
||||
owner {
|
||||
...UserRefFields
|
||||
}
|
||||
members {
|
||||
role
|
||||
user {
|
||||
...UserRefFields
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fragment IssueSummaryFields on Issue {
|
||||
id
|
||||
projectId
|
||||
key
|
||||
type
|
||||
area
|
||||
title
|
||||
status
|
||||
priority
|
||||
labels
|
||||
commentCount
|
||||
updatedAt
|
||||
version
|
||||
assignee {
|
||||
...UserRefFields
|
||||
}
|
||||
release {
|
||||
id
|
||||
name
|
||||
}
|
||||
}
|
||||
|
||||
fragment IssueFields on Issue {
|
||||
...IssueSummaryFields
|
||||
description
|
||||
figmaUrl
|
||||
storyPoints
|
||||
dueDate
|
||||
createdAt
|
||||
allowedTransitions
|
||||
reporter {
|
||||
...UserRefFields
|
||||
}
|
||||
links {
|
||||
type
|
||||
direction
|
||||
issueId
|
||||
issueKey
|
||||
}
|
||||
}
|
||||
|
||||
fragment CommentFields on Comment {
|
||||
id
|
||||
issueId
|
||||
body
|
||||
createdAt
|
||||
editedAt
|
||||
author {
|
||||
...UserRefFields
|
||||
}
|
||||
}
|
||||
|
||||
fragment ReleaseFields on Release {
|
||||
id
|
||||
projectId
|
||||
name
|
||||
description
|
||||
targetDate
|
||||
status
|
||||
releasedAt
|
||||
}
|
||||
|
||||
fragment ActivityFields on Activity {
|
||||
id
|
||||
type
|
||||
issueId
|
||||
issueKey
|
||||
summary
|
||||
occurredAt
|
||||
actor {
|
||||
...UserRefFields
|
||||
}
|
||||
}
|
||||
|
||||
fragment CommandResultFields on CommandResult {
|
||||
id
|
||||
key
|
||||
version
|
||||
}
|
||||
4412
libs/web/tracker/infrastructure/src/lib/graphql/generated.ts
Normal file
4412
libs/web/tracker/infrastructure/src/lib/graphql/generated.ts
Normal file
File diff suppressed because it is too large
Load Diff
107
libs/web/tracker/infrastructure/src/lib/graphql/issues.graphql
Normal file
107
libs/web/tracker/infrastructure/src/lib/graphql/issues.graphql
Normal file
@@ -0,0 +1,107 @@
|
||||
query Board(
|
||||
$idOrKey: String!
|
||||
$releaseId: ID
|
||||
$assigneeId: ID
|
||||
$area: IssueArea
|
||||
) {
|
||||
project(idOrKey: $idOrKey) {
|
||||
id
|
||||
board(
|
||||
releaseId: $releaseId
|
||||
assigneeId: $assigneeId
|
||||
area: $area
|
||||
perColumn: 100
|
||||
) {
|
||||
status
|
||||
total
|
||||
issues {
|
||||
...IssueSummaryFields
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
query Issue($idOrKey: String!) {
|
||||
issue(idOrKey: $idOrKey) {
|
||||
...IssueFields
|
||||
}
|
||||
}
|
||||
|
||||
query SearchIssues($filter: IssueFilterInput!) {
|
||||
issues(filter: $filter, limit: 20) {
|
||||
items {
|
||||
...IssueSummaryFields
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
mutation CreateIssue($input: CreateIssueInput!) {
|
||||
createIssue(input: $input) {
|
||||
...CommandResultFields
|
||||
}
|
||||
}
|
||||
|
||||
mutation UpdateIssue($input: UpdateIssueInput!) {
|
||||
updateIssue(input: $input) {
|
||||
...CommandResultFields
|
||||
}
|
||||
}
|
||||
|
||||
mutation ChangeIssueStatus($input: ChangeIssueStatusInput!) {
|
||||
changeIssueStatus(input: $input) {
|
||||
...CommandResultFields
|
||||
}
|
||||
}
|
||||
|
||||
mutation AssignIssue($issueId: ID!, $assigneeId: ID) {
|
||||
assignIssue(issueId: $issueId, assigneeId: $assigneeId) {
|
||||
...CommandResultFields
|
||||
}
|
||||
}
|
||||
|
||||
mutation LinkIssues($input: LinkIssuesInput!) {
|
||||
linkIssues(input: $input) {
|
||||
...CommandResultFields
|
||||
}
|
||||
}
|
||||
|
||||
mutation UnlinkIssues($input: LinkIssuesInput!) {
|
||||
unlinkIssues(input: $input) {
|
||||
...CommandResultFields
|
||||
}
|
||||
}
|
||||
|
||||
mutation SetIssueRelease($issueId: ID!, $releaseId: ID) {
|
||||
setIssueRelease(issueId: $issueId, releaseId: $releaseId) {
|
||||
...CommandResultFields
|
||||
}
|
||||
}
|
||||
|
||||
query IssueComments($idOrKey: String!) {
|
||||
issue(idOrKey: $idOrKey) {
|
||||
id
|
||||
comments(limit: 200) {
|
||||
items {
|
||||
...CommentFields
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
mutation AddComment($issueId: ID!, $body: String!) {
|
||||
addComment(issueId: $issueId, body: $body) {
|
||||
...CommandResultFields
|
||||
}
|
||||
}
|
||||
|
||||
mutation EditComment($commentId: ID!, $body: String!) {
|
||||
editComment(commentId: $commentId, body: $body) {
|
||||
...CommandResultFields
|
||||
}
|
||||
}
|
||||
|
||||
mutation DeleteComment($commentId: ID!) {
|
||||
deleteComment(commentId: $commentId) {
|
||||
...CommandResultFields
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,75 @@
|
||||
query MyProjects {
|
||||
myProjects {
|
||||
...ProjectFields
|
||||
}
|
||||
}
|
||||
|
||||
query Project($idOrKey: String!) {
|
||||
project(idOrKey: $idOrKey) {
|
||||
...ProjectFields
|
||||
}
|
||||
}
|
||||
|
||||
query ProjectStats($idOrKey: String!) {
|
||||
project(idOrKey: $idOrKey) {
|
||||
id
|
||||
stats {
|
||||
total
|
||||
open
|
||||
byStatus {
|
||||
key
|
||||
count
|
||||
}
|
||||
byType {
|
||||
key
|
||||
count
|
||||
}
|
||||
byPriority {
|
||||
key
|
||||
count
|
||||
}
|
||||
byArea {
|
||||
key
|
||||
count
|
||||
}
|
||||
byAssignee {
|
||||
count
|
||||
user {
|
||||
...UserRefFields
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
mutation CreateProject($input: CreateProjectInput!) {
|
||||
createProject(input: $input) {
|
||||
...CommandResultFields
|
||||
}
|
||||
}
|
||||
|
||||
mutation AddProjectMember($input: ProjectMemberInput!) {
|
||||
addProjectMember(input: $input) {
|
||||
...CommandResultFields
|
||||
}
|
||||
}
|
||||
|
||||
mutation ChangeProjectMemberRole($input: ProjectMemberInput!) {
|
||||
changeProjectMemberRole(input: $input) {
|
||||
...CommandResultFields
|
||||
}
|
||||
}
|
||||
|
||||
mutation RemoveProjectMember($projectId: ID!, $userId: ID!) {
|
||||
removeProjectMember(projectId: $projectId, userId: $userId) {
|
||||
...CommandResultFields
|
||||
}
|
||||
}
|
||||
|
||||
query SearchUsers($search: String!) {
|
||||
users(search: $search, limit: 20) {
|
||||
id
|
||||
name
|
||||
email
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
query Releases($idOrKey: String!) {
|
||||
project(idOrKey: $idOrKey) {
|
||||
id
|
||||
releases {
|
||||
...ReleaseFields
|
||||
progress {
|
||||
total
|
||||
finished
|
||||
percent
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
mutation CreateRelease($input: CreateReleaseInput!) {
|
||||
createRelease(input: $input) {
|
||||
...CommandResultFields
|
||||
}
|
||||
}
|
||||
|
||||
mutation PublishRelease($releaseId: ID!, $force: Boolean!) {
|
||||
publishRelease(releaseId: $releaseId, force: $force) {
|
||||
...CommandResultFields
|
||||
}
|
||||
}
|
||||
|
||||
mutation ArchiveRelease($releaseId: ID!) {
|
||||
archiveRelease(releaseId: $releaseId) {
|
||||
...CommandResultFields
|
||||
}
|
||||
}
|
||||
|
||||
query Activity($projectId: ID!, $issueId: ID) {
|
||||
activity(projectId: $projectId, issueId: $issueId, limit: 100) {
|
||||
...ActivityFields
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,50 @@
|
||||
subscription IssueChanged($projectId: ID!) {
|
||||
issueChanged(projectId: $projectId) {
|
||||
action
|
||||
version
|
||||
actorId
|
||||
issue {
|
||||
...IssueSummaryFields
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
subscription CommentChanged($issueId: ID!) {
|
||||
commentChanged(issueId: $issueId) {
|
||||
action
|
||||
commentId
|
||||
comment {
|
||||
...CommentFields
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
subscription ActivityAdded($projectId: ID!) {
|
||||
activityAdded(projectId: $projectId) {
|
||||
...ActivityFields
|
||||
}
|
||||
}
|
||||
|
||||
subscription ReleaseChanged($projectId: ID!) {
|
||||
releaseChanged(projectId: $projectId) {
|
||||
release {
|
||||
...ReleaseFields
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
subscription ProjectChanged($projectId: ID!) {
|
||||
projectChanged(projectId: $projectId) {
|
||||
project {
|
||||
...ProjectFields
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
subscription MyProjectsChanged {
|
||||
myProjectsChanged {
|
||||
project {
|
||||
...ProjectFields
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
import {
|
||||
makeEnvironmentProviders,
|
||||
type EnvironmentProviders,
|
||||
} from '@angular/core';
|
||||
import {
|
||||
ActivityGateway,
|
||||
CommentsGateway,
|
||||
IssuesGateway,
|
||||
ProjectsGateway,
|
||||
ReleasesGateway,
|
||||
TrackerRealtimeGateway,
|
||||
UsersGateway,
|
||||
} from '@task-stream/web/tracker/domain';
|
||||
import {
|
||||
GraphqlActivityGateway,
|
||||
GraphqlCommentsGateway,
|
||||
GraphqlReleasesGateway,
|
||||
GraphqlUsersGateway,
|
||||
} from './adapters/graphql-collaboration.gateways';
|
||||
import { GraphqlIssuesGateway } from './adapters/graphql-issues.gateway';
|
||||
import { GraphqlProjectsGateway } from './adapters/graphql-projects.gateway';
|
||||
import { GraphqlRealtimeGateway } from './adapters/graphql-realtime.gateway';
|
||||
|
||||
/** Ports → GraphQL adapters. */
|
||||
export function provideTrackerInfrastructure(): EnvironmentProviders {
|
||||
return makeEnvironmentProviders([
|
||||
{ provide: ProjectsGateway, useClass: GraphqlProjectsGateway },
|
||||
{ provide: IssuesGateway, useClass: GraphqlIssuesGateway },
|
||||
{ provide: CommentsGateway, useClass: GraphqlCommentsGateway },
|
||||
{ provide: ReleasesGateway, useClass: GraphqlReleasesGateway },
|
||||
{ provide: ActivityGateway, useClass: GraphqlActivityGateway },
|
||||
{ provide: UsersGateway, useClass: GraphqlUsersGateway },
|
||||
{ provide: TrackerRealtimeGateway, useClass: GraphqlRealtimeGateway },
|
||||
]);
|
||||
}
|
||||
Reference in New Issue
Block a user