diff --git a/src/app.module.ts b/src/app.module.ts index bb8114b..b1c6898 100644 --- a/src/app.module.ts +++ b/src/app.module.ts @@ -25,6 +25,7 @@ import { CircleciPipelinesModule } from './circleciPipelines/circleciPipelines.m import { CircleciInsightsModule } from './circleciInsights/circleciInsights.module'; import { TestingStatesModule } from './testingStates/testingStates.module'; import { TestingRunsModule } from './testingRuns/testingRuns.module'; +import { TestingCasesModule } from './testingCases/testingCases.module'; import { TestingPerfsModule } from './testingPerfs/testingPerfs.module'; import { BambooRunsModule } from './bambooRuns/bambooRuns.module'; import { join } from 'path'; @@ -62,6 +63,7 @@ import { AuthenticationMiddleware } from './auth/authentication.middleware'; CircleciInsightsModule, TestingStatesModule, TestingRunsModule, + TestingCasesModule, TestingPerfsModule, BambooRunsModule, ], diff --git a/src/schema.graphql b/src/schema.graphql index e337fe7..b651264 100644 --- a/src/schema.graphql +++ b/src/schema.graphql @@ -2520,6 +2520,193 @@ type IssueNetwork { source: String } +type GithubMavenPoms { + """Access to the dataset as individual items, aggregations and more""" + data( + """Query used to filter down content""" + query: String + ): GithubMavenPomsData! + + """Access to configuration values and metadata""" + config: GithubMavenPomsConfig! +} + +type GithubMavenPomsData { + """Query received as a parameter""" + query: String! + + """Transformation of the received query in an Elasticsearch query""" + esQuery: String! + + """Total number of documents matching the query""" + count: Float! + + """Returns a paginated list of items""" + items( + orderBy: MavenPomsSortorder + + """Number if items to return """ + size: Int = 10 + + """Return items starting from""" + from: Int = 0 + ): DataMavenPomConnection! + + """Returns a single item by providing its ID""" + item( + """Return items starting from""" + id: String! + ): MavenPom! + + """Return aggregations (facets)""" + aggregations( + """ + Additional options as a stringified object (more details in the documentation) + """ + aggOptions: String + + """Type of aggregation (default: term)""" + aggType: String + + """ + Field to aggregate on, using the node as the root object (examples: states, author.login) + """ + field: String! + ): MavenPomsAggregationConnection! +} + +input MavenPomsSortorder { + direction: OrderDirection! + + """ + Order field (see config aggregations node for a sample of possible values) + """ + field: String +} + +type GithubMavenPomsConfig { + """Query received as a parameter""" + query: String! + + """Returns a paginated list of available aggregations""" + aggregations: ConfigAggregations! + + """Returns the table configuration for this datatype""" + table: ConfigTable! +} + +type MavenPomContentParent { + """Version""" + version: String + + """Artifact ID""" + artifactId: String + + """Group ID""" + groupId: String +} + +type MavenPomContent { + """Version""" + version: String + + """Artifact ID""" + artifactId: String + + """Name""" + name: String + + """Description""" + description: String + + """Parent detailed in the pom file""" + parent: MavenPomContentParent +} + +type MavenPom { + id: ID! + + """Identifies the primary key from the database.""" + databaseId: Float + + """The name of the repository.""" + name: String + + """Content of the POM file""" + pom: MavenPomContent + + """The repository name with owner.""" + nameWithOwner: String + + """The HTTP URL for this repository.""" + url: String! + + """The User owner of the repository.""" + owner: RepositoryOwner! + + """Identifies the date and time when the object was created.""" + createdAt: String + + """The Ref associated with the repository default branch.""" + defaultBranchRef: Ref + + """The description of the repository.""" + description: String + + """Indicates if the repository is unmaintained.""" + isArchived: Boolean + + """Returns whether or not this repository disabled.""" + isDisabled: Boolean + + """Identifies if the repository is a fork.""" + isFork: Boolean + + """Identifies if the repository contains a pom file""" + hasPom: Boolean + + """Identifies if the repository pom file has a parent""" + hasParent: Boolean + + """Identifies when the repository was last pushed to.""" + pushedAt: String + + """Link to the ref containing recent commits to master""" + lastCommitMainBranch: Commit + + """A list of applied repository-topic associations for this repository.""" + repositoryTopics: RepositoryTopicConnection + + """Identifies the date and time when the object was last updated.""" + updatedAt: String +} + +type DataMavenPomConnection { + nodes: [MavenPom!] + totalCount: Int! +} + +type MavenPomsAggregationConnection { + """The field that was aggregated on""" + field: String! + + """The ES Query executed to fetch the aggregation""" + esQuery: String! + + """ + When running a matrix aggregation, the field that was used for the comparison + """ + compareField: String + + """ + When running a matrix aggregation, the value of the field that was used for the comparison + """ + compareValue: String + + """A list of aggregation buckets""" + buckets: [AggregationBucket!]! +} + type JiraIssues { """Access to the dataset as individual items, aggregations and more""" data( @@ -2573,6 +2760,20 @@ type JiraIssuesData { """ field: String! ): JiraIssuesAggregationConnection! + + """Return a matrix aggregation per week and field""" + matrix( + """ + Additional options as a stringified object (more details in the documentation) + """ + aggOptions: String + + """Field to be used for the aggregations (for example: repository.name)""" + field: String! + + """Date field to be used for the aggregation (for example: ClosedAt)""" + dateField: String! + ): JiraMatrixConnection! } input JiraIssuesSortorder { @@ -2584,6 +2785,20 @@ input JiraIssuesSortorder { field: String } +type JiraMatrixConnection { + """Current Velocity data""" + field: String + + """A list of aggregation buckets""" + buckets: [JiraIssueActivityBucket!]! + + """Overall first week (first day of the week) in the aggregated buckets""" + fromWeekStart: String! + + """Overall last week (first day of the week) in the aggregated buckets""" + toWeekStart: String! +} + type JiraIssuesConfig { """Query received as a parameter""" query: String! @@ -2915,6 +3130,34 @@ type JiraIssueItemConnection { totalCount: Int! } +type JiraIssueBucketWeek { + """First day of the week""" + weekStart: String! + + """The number of documents that week""" + docCount: Float! + + """The number of points that week""" + sum: Float +} + +type JiraIssueActivityBucket { + """The Bucket key""" + key: String! + + """Representation of the key as a string (useful for dates)""" + keyAsString: String + + """The number of elements in the bucket""" + docCount: Float! + + """The number of points in the bucket""" + sum: Float + + """Weeks of activity""" + weeks: [JiraIssueBucketWeek!]! +} + type JiraIssuesAggregationConnection { """The field that was aggregated on""" field: String! @@ -3685,41 +3928,1082 @@ type StatesAggregationConnection { buckets: [AggregationBucket!]! } -type Query { - """Fetch data useful for UI configuration""" - config: Config! +type TestingRuns { + """Access to the dataset as individual items, aggregations and more""" + data( + """Query used to filter down content""" + query: String + ): TestingRunsData! - """Fetches the currently running API version""" - version: String! + """Access to configuration values and metadata""" + config: TestingRunsConfig! +} - """Fetch data (items, aggregatiosn) related to the dataset""" - githubPullrequests: GithubPullrequests! +type TestingRunsData { + """Query received as a parameter""" + query: String! - """Fetch data (items, aggregatiosn) related to the dataset""" - githubVulnerabilities: GithubVulnerabilities! + """Transformation of the received query in an Elasticsearch query""" + esQuery: String! - """Fetch data (items, aggregatiosn) related to the dataset""" - githubRepositories: GithubRepositories! + """Total number of documents matching the query""" + count: Float! - """Fetch data (items, aggregatiosn) related to the dataset""" - githubWatchers: GithubWatchers! + """Returns a paginated list of items""" + items( + orderBy: TestingRunsSortorder - """Fetch data (items, aggregatiosn) related to the dataset""" - githubLabels: GithubLabels! + """Number if items to return """ + size: Int = 10 - """Fetch data (items, aggregatiosn) related to the dataset""" - githubMilestones: GithubMilestones! + """Return items starting from""" + from: Int = 0 + ): TestingRunsConnection! - """Fetch data (items, aggregatiosn) related to the dataset""" - githubProjects: GithubProjects! + """Returns a single item by providing its ID""" + item( + """Return items starting from""" + id: String! + ): TestingRun! - """Fetch data (items, aggregatiosn) related to the dataset""" - githubReleases: GithubReleases! + """Return aggregations (facets)""" + aggregations( + """ + Additional options as a stringified object (more details in the documentation) + """ + aggOptions: String - """Fetch data (items, aggregatiosn) related to the dataset""" - githubIssues: GithubIssues! + """Type of aggregation (default: term)""" + aggType: String - """Fetch data (items, aggregatiosn) related to the dataset""" + """ + Field to aggregate on, using the node as the root object (examples: states, author.login) + """ + field: String! + ): TestingRunsAggregationConnection! + + """ + Return an aggregation by a provided timeframe (week, day, month) of the average failure rate + """ + failurerate( + """Specify the number of first level buckets to return""" + buckets: Float = 25 + + """Specify the date field to aggregate on""" + datefield: String = "createdAt" + + """Specify the field to aggregate by (such as plan.name.keyword)""" + field: String = "name.keyword" + + """Specify the interval for the data histogram (day, week, month, year)""" + interval: String = "week" + ): TestingRunsFailureRateConnection! +} + +input TestingRunsSortorder { + direction: OrderDirection! + + """ + Order field (see config aggregations node for a sample of possible values) + """ + field: String +} + +type TestingRunsConfig { + """Query received as a parameter""" + query: String! + + """Returns a paginated list of available aggregations""" + aggregations: ConfigAggregations! + + """Returns the table configuration for this datatype""" + table: ConfigTable! +} + +type TestingRun { + id: ID! + + """Date at which the state event was recorded (last)""" + createdAt: String! + + """Name of the element being tested""" + name: String! + + """Version of the element being tested""" + version: String! + + """Concatenated name and version, separated with a _""" + full: String! + + """List of dependencyes to the element being tested""" + dependencies: DependencyConnection! + + """State of the test execution (PASS, FAIL, ...)""" + state: String! + + """The HTTP URL for this PR.""" + url: String! + + """Total number of tests in the run""" + runTotal: Float! + + """Total number of successful tests in the run""" + runSuccess: Float! + + """Rate of successful tests in the run""" + runSuccessRate: Float! + + """Total number of failed tests in the run""" + runFailure: Float! + + """Rate of failed tests in the run""" + runFailureRate: Float! + + """Total duration of the run""" + runDuration: Float! +} + +type TestingRunsConnection { + nodes: [TestingRun!] + totalCount: Int! +} + +type FailureRateBucketDate { + """Start date of each bucket depending of the set interval""" + dateStart: String! + + """The number of documents that week""" + docCount: Float! + + """The failure rate (in %) over the bucket window""" + runFailureRate: Float! + + """The total number of tests that were executed""" + runTotal: Float! + + """The average number of tests that were executed per run""" + runTotalAvg: Float +} + +type FailureRateBucket { + """The Bucket key""" + key: String! + + """The number of elements in the bucket""" + docCount: Float! + + """Representation of the key as a string (useful for dates)""" + keyAsString: String + + """The failure rate (in %) over the entire period""" + runFailureRate: Float! + + """The total number of tests that were executed""" + runTotal: Float + + """The average number of tests that were executed per run""" + runTotalAvg: Float + + """Breakdown of the dataset per the defined interval""" + buckets: [FailureRateBucketDate!]! +} + +type TestingRunsFailureRateConnection { + """Field used to aggregate on""" + field: String + + """A list of aggregation buckets""" + buckets: [FailureRateBucket!]! + + """Overall first week (first day of the week) in the aggregated buckets""" + fromDateStart: String! + + """Overall last week (first day of the week) in the aggregated buckets""" + toDateStart: String! +} + +type TestingRunsAggregationConnection { + """The field that was aggregated on""" + field: String! + + """The ES Query executed to fetch the aggregation""" + esQuery: String! + + """ + When running a matrix aggregation, the field that was used for the comparison + """ + compareField: String + + """ + When running a matrix aggregation, the value of the field that was used for the comparison + """ + compareValue: String + + """A list of aggregation buckets""" + buckets: [AggregationBucket!]! +} + +type TestingCases { + """Access to the dataset as individual items, aggregations and more""" + data( + """Query used to filter down content""" + query: String + ): TestingCasesData! + + """Access to configuration values and metadata""" + config: TestingCasesConfig! +} + +type TestingCasesData { + """Query received as a parameter""" + query: String! + + """Transformation of the received query in an Elasticsearch query""" + esQuery: String! + + """Total number of documents matching the query""" + count: Float! + + """Returns a paginated list of items""" + items( + orderBy: TestingCasesSortorder + + """Number if items to return """ + size: Int = 10 + + """Return items starting from""" + from: Int = 0 + ): TestingCasesConnection! + + """Returns a single item by providing its ID""" + item( + """Return items starting from""" + id: String! + ): TestingCase! + + """Return aggregations (facets)""" + aggregations( + """ + Additional options as a stringified object (more details in the documentation) + """ + aggOptions: String + + """Type of aggregation (default: term)""" + aggType: String + + """ + Field to aggregate on, using the node as the root object (examples: states, author.login) + """ + field: String! + ): TestingCasesAggregationConnection! + + """ + Return an aggregation by a provided timeframe (week, day, month) of the average failure rate + """ + failurerate( + """Specify the number of first level buckets to return""" + buckets: Float = 25 + + """Specify the date field to aggregate on""" + datefield: String = "createdAt" + + """Specify the field to aggregate by (such as plan.name.keyword)""" + field: String = "name.keyword" + + """Specify the interval for the data histogram (day, week, month, year)""" + interval: String = "week" + ): TestingCasesFailureRateConnection! +} + +input TestingCasesSortorder { + direction: OrderDirection! + + """ + Order field (see config aggregations node for a sample of possible values) + """ + field: String +} + +type TestingCasesConfig { + """Query received as a parameter""" + query: String! + + """Returns a paginated list of available aggregations""" + aggregations: ConfigAggregations! + + """Returns the table configuration for this datatype""" + table: ConfigTable! +} + +type TestingCase { + id: ID! + + """Date at which the state event was recorded (last)""" + createdAt: String! + + """Name of the test case being executed""" + name: String! + + """Name of the project in which the test case is being executed""" + project: String! + + """Name of the test suite containing that the test case""" + suite: String! + + """Jahia version used during the test""" + jahia: String! + + """Jahia module used during the test""" + module: String! + + """Concatenated name and version, separated with a _""" + full: String! + + """List of dependencyes to the element being tested""" + dependencies: DependencyConnection! + + """State of the test execution (PASS, FAIL, ...)""" + state: String! + + """The HTTP URL for this PR.""" + url: String! + + """Total duration of the test case""" + duration: Float! +} + +type TestingCasesConnection { + nodes: [TestingCase!] + totalCount: Int! +} + +type FailureRateCasesBucketDate { + """Start date of each bucket depending of the set interval""" + dateStart: String! + + """The number of documents that week""" + docCount: Float! + + """The failure rate (in %) over the bucket window""" + caseFailureRate: Float! + + """The total number of tests that were executed""" + caseTotal: Float! + + """The average number of tests that were executed per case""" + caseTotalAvg: Float +} + +type FailureRateCasesBucket { + """The Bucket key""" + key: String! + + """The number of elements in the bucket""" + docCount: Float! + + """Representation of the key as a string (useful for dates)""" + keyAsString: String + + """The failure rate (in %) over the entire period""" + caseFailureRate: Float! + + """The total number of tests that were executed""" + caseTotal: Float + + """The average number of tests that were executed per case""" + caseTotalAvg: Float + + """Breakdown of the dataset per the defined interval""" + buckets: [FailureRateCasesBucketDate!]! +} + +type TestingCasesFailureRateConnection { + """Field used to aggregate on""" + field: String + + """A list of aggregation buckets""" + buckets: [FailureRateCasesBucket!]! + + """Overall first week (first day of the week) in the aggregated buckets""" + fromDateStart: String! + + """Overall last week (first day of the week) in the aggregated buckets""" + toDateStart: String! +} + +type TestingCasesAggregationConnection { + """The field that was aggregated on""" + field: String! + + """The ES Query executed to fetch the aggregation""" + esQuery: String! + + """ + When running a matrix aggregation, the field that was used for the comparison + """ + compareField: String + + """ + When running a matrix aggregation, the value of the field that was used for the comparison + """ + compareValue: String + + """A list of aggregation buckets""" + buckets: [AggregationBucket!]! +} + +type TestingPerfs { + """Access to the dataset as individual items, aggregations and more""" + data( + """Query used to filter down content""" + query: String + ): TestingPerfsData! + + """Access to configuration values and metadata""" + config: TestingPerfsConfig! +} + +type TestingPerfsData { + """Query received as a parameter""" + query: String! + + """Transformation of the received query in an Elasticsearch query""" + esQuery: String! + + """Total number of documents matching the query""" + count: Float! + + """Returns a paginated list of items""" + items( + """Should the respond include disabled (delete) documents?""" + includeDisabled: Boolean = false + orderBy: PerfsSortorder + + """Name of the profile to return, leave empty for all""" + profileName: String + + """Array of transactions keys to return, * for all""" + transactions: [String!] = ["Total"] + + """Number if items to return """ + size: Int = 100 + + """Return items starting from""" + from: Int = 0 + ): DataPerfConnection! + + """Returns a single item by providing its ID""" + item( + """Return a single profile (a single run within a run)""" + profileId: String + + """Return items starting from""" + id: String! + ): Perf + + """Return aggregations (facets)""" + aggregations( + """ + Additional options as a stringified object (more details in the documentation) + """ + aggOptions: String + + """Type of aggregation (default: term)""" + aggType: String + + """ + Field to aggregate on, using the node as the root object (examples: states, author.login) + """ + field: String! + ): PerfsAggregationConnection! + + """Returns a paginated list of items""" + average( + """Array of statistics keys to return""" + statsKeys: [String!] = ["sampleCount", "errorCount", "errorPct", "meanResTime", "medianResTime", "minResTime", "maxResTime", "pct1ResTime", "pct2ResTime", "pct3ResTime", "throughput"] + + """Query to fetch documents to be used to calculate the averages""" + averageQuery: String + + """Return a single profile (a single run within a run)""" + profileId: String + ): PerfAverage +} + +input PerfsSortorder { + direction: OrderDirection! + + """ + Order field (see config aggregations node for a sample of possible values) + """ + field: String +} + +type TestingPerfsConfig { + """Query received as a parameter""" + query: String! + + """Returns a paginated list of available aggregations""" + aggregations: ConfigAggregations! + + """Returns the table configuration for this datatype""" + table: ConfigTable! +} + +type Resource { + id: ID + + """Name of the resource""" + name: String! + + """Size of the resource""" + size: String + + """Name of the container that was used""" + image: String! + + """JSON.stringify() of the tfsettings file""" + tfsettings: String +} + +type ResourceEdge { + """The item at the end of the edge.""" + node: Resource! +} + +type ResourceConnection { + """A list of edges.""" + edges: [ResourceEdge!]! + + """Identifies the total count of items in the connection.""" + totalCount: Int! +} + +type Tag { + id: ID + + """Name of the tag""" + name: String! +} + +type TagEdge { + """The item at the end of the edge.""" + node: Tag! +} + +type TagConnection { + """A list of edges.""" + edges: [TagEdge!]! + + """Identifies the total count of items in the connection.""" + totalCount: Int! +} + +type Statistics { + """Name of the transaction""" + transaction: String + + """sampleCount result""" + sampleCount: Float + + """errorCount result""" + errorCount: Float + + """errorPct result""" + errorPct: Float + + """meanResTime result""" + meanResTime: Float + + """medianResTime result""" + medianResTime: Float + + """minResTime result""" + minResTime: Float + + """maxResTime result""" + maxResTime: Float + + """pct1ResTime result""" + pct1ResTime: Float + + """pct2ResTime result""" + pct2ResTime: Float + + """pct3ResTime result""" + pct3ResTime: Float + + """throughput result""" + throughput: Float + + """receivedKBytesPerSec result""" + receivedKBytesPerSec: Float + + """sentKBytesPerSec result""" + sentKBytesPerSec: Float +} + +type PerfRun { + id: ID + + """Name of the run""" + name: String! + + """Number of users used for the run""" + userCount: Float + + """Execution time for the run""" + duration: Float + + """Statistics coming from JMeter statistics.json file""" + statistics: [Statistics!] +} + +type PerfRunEdge { + """The item at the end of the edge.""" + node: PerfRun! +} + +type PerfRunConnection { + """A list of edges.""" + edges: [PerfRunEdge!]! + + """Identifies the total count of items in the connection.""" + totalCount: Int! +} + +type Platform { + """Name of the vendor""" + vendor: String! + + """Tenant/account within the vendor""" + tenant: String + + """Region in which the run was executed""" + region: String! +} + +type Perf { + id: ID + + """Full document source as a JSON string""" + _source: String + + """Name of the element being tested""" + name: String! + + """Description of the run""" + description: String + + """Who did the description""" + description_by: String + + """Date the descruption was added""" + description_date: String + + """Analysis of the run, written by a team member""" + analysis: String + + """Who did the analysis""" + analysis_by: String + + """Date the analysis was performed""" + analysis_date: String + + """Group set by a user to allow quick filtering""" + group: String + + """Who did set the group""" + group_by: String + + """When was the group set""" + group_date: String + + """Was the run verified and relevant ?""" + verified: Boolean + + """Who verified therun""" + verified_by: String + + """Date the run was verified""" + verified_date: String + + """List of dependencyes to the element being tested""" + repository: RepositoryConnection! + + """Date at which the performance run was started""" + startedAt: String! + + """Execution time for the run""" + duration: Float + + """Value of a single run metric (where relevant)""" + value: Float + + """Rampup used for the run""" + rampUp: Float + + """Platform used to run the tests""" + platform: Platform! + + """List of resources to the element being tested""" + resources: ResourceConnection! + + """List of tags associated with the run""" + tags: TagConnection! + + """Runs executed in the tests""" + runs: PerfRunConnection + + """Run corresponding to the selected profile (provided using profileid)""" + run: PerfRun! + + """The HTTP URL for this PR.""" + url: String + + """Is the run disabled (removed from the records)""" + disabled: Boolean + + """Who was the run disabled by""" + disabled_by: String + + """When was the run disabled""" + disabled_date: String +} + +type PerfRunMetric { + id: ID + + """Name of the element being tested""" + name: String! + + """Date at which the performance run was started""" + startedAt: String! + + """Value of a single run metric (where relevant)""" + value: Float +} + +type PerfAverageAverage { + id: ID + + """ + Object key in the statistics.json payload (sampleCount, errorCount, minResTime, ...) + """ + statisticsKey: String! + + """Name of the transaction""" + transaction: String! + + """Average value for that key in that transaction""" + value: Float! + + """List of runs used when calculating the average""" + runs: [PerfRunMetric!]! +} + +type PerfAverageValue { + id: ID + + """Name of the run""" + name: String! + + """Date at which the performance run was started""" + startedAt: String! + + """ + Object key in the statistics.json payload (sampleCount, errorCount, minResTime, ...) + """ + statisticsKey: String! + + """Name of the transaction""" + transaction: String! + + """Value for that key in that transaction""" + value: Float! +} + +type PerfAverage { + id: ID + + """List of runs used when calculating the average""" + runs: [Perf!]! + + """Computed average values per key and transaction""" + average: [PerfAverageAverage!]! + + """Indidual run metrics per key and per transaction""" + values: [PerfAverageValue!]! + + """List of available transactions""" + transactions: [String!] + + """List of available statistics keys""" + statisticsKeys: [String!]! +} + +type DataPerfConnection { + nodes: [Perf!] + totalCount: Int! +} + +type PerfsAggregationConnection { + """The field that was aggregated on""" + field: String! + + """The ES Query executed to fetch the aggregation""" + esQuery: String! + + """ + When running a matrix aggregation, the field that was used for the comparison + """ + compareField: String + + """ + When running a matrix aggregation, the value of the field that was used for the comparison + """ + compareValue: String + + """A list of aggregation buckets""" + buckets: [AggregationBucket!]! +} + +type BambooRuns { + """Access to the dataset as individual items, aggregations and more""" + data( + """Query used to filter down content""" + query: String + ): BambooRunsData! + + """Access to configuration values and metadata""" + config: BambooRunsConfig! +} + +type BambooRunsData { + """Query received as a parameter""" + query: String! + + """Transformation of the received query in an Elasticsearch query""" + esQuery: String! + + """Total number of documents matching the query""" + count: Float! + + """Returns a paginated list of items""" + items( + orderBy: RunsSortorder + + """Number if items to return """ + size: Int = 10 + + """Return items starting from""" + from: Int = 0 + ): DataRunConnection! + + """Returns a single item by providing its ID""" + item( + """Return items starting from""" + id: String! + ): State! + + """Return aggregations (facets)""" + aggregations( + """ + Additional options as a stringified object (more details in the documentation) + """ + aggOptions: String + + """Type of aggregation (default: term)""" + aggType: String + + """ + Field to aggregate on, using the node as the root object (examples: states, author.login) + """ + field: String! + ): RunsAggregationConnection! + + """ + Return an aggregation by a provided timeframe (week, day, month) of the average failure rate + """ + failurerate( + """Specify the date field to aggregate on""" + datefield: String = "startedAt" + + """Specify the number of first level buckets to return""" + buckets: Float = 25 + + """Specify the field to aggregate by (such as plan.name.keyword)""" + field: String = "plan.name.keyword" + + """Specify the interval for the data histogram (day, week, month, year)""" + interval: String = "week" + ): BambooRunsFailureRateConnection! +} + +input RunsSortorder { + direction: OrderDirection! + + """ + Order field (see config aggregations node for a sample of possible values) + """ + field: String +} + +type BambooRunsConfig { + """Query received as a parameter""" + query: String! + + """Returns a paginated list of available aggregations""" + aggregations: ConfigAggregations! + + """Returns the table configuration for this datatype""" + table: ConfigTable! +} + +type BambooProject { + id: ID! + + """Name of the project""" + name: String! +} + +type BambooPlan { + id: ID! + + """Name of the plan""" + name: String! + + """Short key of the plan""" + shortKey: String! + + """Bamboo project attached to the run""" + project: BambooProject! +} + +type BambooRun { + id: ID! + + """The key of the run""" + key: String! + + """Bamboo plan attached to the run""" + plan: BambooPlan! + + """Date at which the run was started""" + startedAt: String + + """Date at which the run was completed""" + completedAt: String + + """Name of the run""" + name: String! + + """Number of the run""" + number: Float! + + """Duration of the run in seconds""" + duration: Float! + + """Total number of tests in the run""" + runTotal: Float + + """Total number of successful tests in the run""" + runSuccess: Float + + """Total number of failed tests in the run""" + runFailure: Float + + """Total number of skipped tests in the run""" + runSkipped: Float + + """Total number of quarantined tests in the run""" + runQuarantined: Float + + """State of the run""" + state: String! + + """Was the run successful""" + successful: Boolean! +} + +type DataRunConnection { + nodes: [BambooRun!] + totalCount: Int! +} + +type RunsAggregationConnection { + """The field that was aggregated on""" + field: String! + + """The ES Query executed to fetch the aggregation""" + esQuery: String! + + """ + When running a matrix aggregation, the field that was used for the comparison + """ + compareField: String + + """ + When running a matrix aggregation, the value of the field that was used for the comparison + """ + compareValue: String + + """A list of aggregation buckets""" + buckets: [AggregationBucket!]! +} + +type BambooRunsFailureRateConnection { + """Field used to aggregate on""" + field: String + + """A list of aggregation buckets""" + buckets: [FailureRateBucket!]! + + """Overall first week (first day of the week) in the aggregated buckets""" + fromDateStart: String! + + """Overall last week (first day of the week) in the aggregated buckets""" + toDateStart: String! +} + +type Query { + """Fetch data useful for UI configuration""" + config: Config! + + """Fetches the currently running API version""" + version: String! + + """Fetch data (items, aggregatiosn) related to the dataset""" + githubPullrequests: GithubPullrequests! + + """Fetch data (items, aggregatiosn) related to the dataset""" + githubVulnerabilities: GithubVulnerabilities! + + """Fetch data (items, aggregatiosn) related to the dataset""" + githubRepositories: GithubRepositories! + + """Fetch data (items, aggregatiosn) related to the dataset""" + githubWatchers: GithubWatchers! + + """Fetch data (items, aggregatiosn) related to the dataset""" + githubLabels: GithubLabels! + + """Fetch data (items, aggregatiosn) related to the dataset""" + githubMilestones: GithubMilestones! + + """Fetch data (items, aggregatiosn) related to the dataset""" + githubProjects: GithubProjects! + + """Fetch data (items, aggregatiosn) related to the dataset""" + githubReleases: GithubReleases! + + """Fetch data (items, aggregatiosn) related to the dataset""" + githubIssues: GithubIssues! + + """Fetch data (items, aggregatiosn) related to the dataset""" + githubMavenPoms: GithubMavenPoms! + + """Fetch data (items, aggregatiosn) related to the dataset""" jiraIssues: JiraIssues! """Fetch data (items, aggregatiosn) related to the dataset""" @@ -3733,4 +5017,33 @@ type Query { """Fetch data (items, aggregatiosn) related to the dataset""" testingStates: TestingStates! + + """Fetch data (items, aggregatiosn) related to the dataset""" + testingRuns: TestingRuns! + + """Fetch data (items, aggregatiosn) related to the dataset""" + testingCases: TestingCases! + + """Fetch data (items, aggregatiosn) related to the dataset""" + testingPerfs: TestingPerfs! + + """Fetch data (items, aggregatiosn) related to the dataset""" + bambooRuns: BambooRuns! +} + +type Mutation { + """Prevent a testing perfs run from being included in the results""" + disableTestingsPerfsRuns(username: String = "", id: String!): Perf! + + """Prevent a testing perfs run from being included in the results""" + enableTestingsPerfsRuns(username: String = "", id: String!): Perf! + + """Mark a testing perf run as unverified""" + unverifyTestingsPerfsRuns(username: String = "", id: String!): Perf! + + """Mark a testing perf run as verified""" + verifyTestingsPerfsRuns(username: String = "", id: String!): Perf! + + """Update description and analysis for a run""" + updateTestingsPerfsRun(analysis: String = null, description: String = null, group: String = "", username: String = "", id: String!): Perf! } diff --git a/src/testingCases/config/config.resolvers.ts b/src/testingCases/config/config.resolvers.ts new file mode 100644 index 0000000..3c09a72 --- /dev/null +++ b/src/testingCases/config/config.resolvers.ts @@ -0,0 +1,41 @@ +import { Resolver, ResolveField, Parent } from '@nestjs/graphql'; + +import Config from './config.type'; +import ConfigAggregationsService from '../../utils/config/aggregations/aggregations.service'; +import ConfigAggregations from '../../utils/config/aggregations/aggregations.type'; +import ConfigTableService from '../../utils/config/table/table.service'; +import ConfigTable from '../../utils/config/table/table.type'; + +// https://github.com/nestjs/graphql/issues/475 + +@Resolver(Config) +export default class ConfigResolver { + constructor( + private readonly aggregationsService: ConfigAggregationsService, + private readonly tableService: ConfigTableService, + ) {} + + @ResolveField(() => ConfigAggregations, { + name: 'aggregations', + description: 'Returns a paginated list of available aggregations', + }) + public async getAggregations( + @Parent() + parent: Config, // eslint-disable-line @typescript-eslint/no-unused-vars + ) { + const data = this.aggregationsService.findAll('testingCases'); + return data; + } + + @ResolveField(() => ConfigTable, { + name: 'table', + description: 'Returns the table configuration for this datatype', + }) + public async getTable( + @Parent() + parent: Config, // eslint-disable-line @typescript-eslint/no-unused-vars + ) { + const data = this.tableService.findAll('testingCases'); + return data; + } +} diff --git a/src/testingCases/config/config.type.ts b/src/testingCases/config/config.type.ts new file mode 100644 index 0000000..e049a91 --- /dev/null +++ b/src/testingCases/config/config.type.ts @@ -0,0 +1,10 @@ +import { Field, ObjectType } from '@nestjs/graphql'; + +@ObjectType() +export default class TestingCasesConfig { + @Field({ + nullable: false, + description: 'Query received as a parameter', + }) + query: string; +} diff --git a/src/testingCases/data/aggregations/casesAggregationConnection.type.ts b/src/testingCases/data/aggregations/casesAggregationConnection.type.ts new file mode 100644 index 0000000..d3e0643 --- /dev/null +++ b/src/testingCases/data/aggregations/casesAggregationConnection.type.ts @@ -0,0 +1,6 @@ +import { ObjectType } from '@nestjs/graphql'; + +import AggregationConnection from '../../../utils/data/aggregations/aggregationConnection.type'; + +@ObjectType() +export default class TestingCasesAggregationConnection extends AggregationConnection {} diff --git a/src/testingCases/data/data.resolvers.ts b/src/testingCases/data/data.resolvers.ts new file mode 100644 index 0000000..611fc54 --- /dev/null +++ b/src/testingCases/data/data.resolvers.ts @@ -0,0 +1,182 @@ +import { Int } from '@nestjs/graphql'; +import { Args, Resolver, ResolveField, Parent } from '@nestjs/graphql'; +import { ConfService } from '../../conf.service'; + +import Data from './data.type'; +import Case from '../../utils/testing/types/case'; +import DataTestingCaseConnection from './items/caseConnection.type'; +import ItemSortorder from './items/itemSortorder.type'; +import DataItemsService from '../../utils/data/items/items.service'; +import TestingCasesFailureRateConnection from './failurerate/failurerateConnection.type'; +import DataFailureRateCasesService from '../../utils/data/failureratecases/failurerate.service'; + +import TestingCasesAggregationConnection from './aggregations/casesAggregationConnection.type'; +import DataAggregationsService from '../../utils/data/aggregations/aggregations.service'; + +// https://github.com/nestjs/graphql/issues/475 + +@Resolver(Data) +export default class DataResolver { + constructor( + private readonly confService: ConfService, + private readonly aggregationsService: DataAggregationsService, + private readonly itemsService: DataItemsService, + private readonly failurerateService: DataFailureRateCasesService, + ) {} + + @ResolveField(() => DataTestingCaseConnection, { + name: 'items', + description: 'Returns a paginated list of items', + }) + public async getItemsProperty( + @Args({ + name: 'from', + type: () => Int, + description: 'Return items starting from', + nullable: true, + defaultValue: 0, + }) + first: number, + @Args({ + name: 'size', + type: () => Int, + description: 'Number if items to return ', + nullable: true, + defaultValue: 10, + }) + size: number, + @Args({ + name: 'orderBy', + type: () => ItemSortorder, + nullable: true, + }) + orderBy: ItemSortorder, + @Parent() + parent: Data, + ) { + const userConfig = this.confService.getUserConfig(); + + const data = await this.itemsService.findAll( + first, + size, + parent.query, + orderBy, + userConfig.elasticsearch.dataIndices.testingCases + '*', + ); + return data; + } + + @ResolveField(() => Case, { + name: 'item', + description: 'Returns a single item by providing its ID', + }) + public async getItem( + @Args({ + name: 'id', + type: () => String, + description: 'Return items starting from', + nullable: false, + }) + id: string, + // eslint-disable-next-line @typescript-eslint/no-unused-vars + @Parent() parent: Data, + ): Promise { + const userConfig = this.confService.getUserConfig(); + const item = await this.itemsService.findOneById(id, userConfig.elasticsearch.dataIndices.testingCases); + return item; + } + + @ResolveField(() => TestingCasesAggregationConnection, { + name: 'aggregations', + description: 'Return aggregations (facets)', + }) + public async getAggregationsProperty( + @Args({ + name: 'field', + type: () => String, + description: 'Field to aggregate on, using the node as the root object (examples: states, author.login)', + nullable: false, + }) + field: string, + @Args({ + name: 'aggType', + type: () => String, + description: 'Type of aggregation (default: term)', + nullable: true, + }) + aggType: string, + @Args({ + name: 'aggOptions', + type: () => String, + description: 'Additional options as a stringified object (more details in the documentation)', + nullable: true, + }) + options: string, + @Parent() + parent: Data, + ) { + const userConfig = this.confService.getUserConfig(); + + const data = await this.aggregationsService.findAll( + field, + parent.query, + aggType, + options, + userConfig.elasticsearch.dataIndices.testingCases + '*', + ); + return data; + } + + @ResolveField(() => TestingCasesFailureRateConnection, { + name: 'failurerate', + description: 'Return an aggregation by a provided timeframe (week, day, month) of the average failure rate', + }) + public async getDataTestingCaseFailureRate( + @Args({ + name: 'interval', + type: () => String, + description: 'Specify the interval for the data histogram (day, week, month, year)', + defaultValue: 'week', + nullable: true, + }) + dateInterval: string, + @Args({ + name: 'field', + type: () => String, + description: 'Specify the field to aggregate by (such as plan.name.keyword)', + defaultValue: 'name.keyword', + nullable: false, + }) + field: string, + @Args({ + name: 'datefield', + type: () => String, + description: 'Specify the date field to aggregate on', + defaultValue: 'createdAt', + nullable: false, + }) + datefield: string, + @Args({ + name: 'buckets', + type: () => Number, + description: 'Specify the number of first level buckets to return', + defaultValue: 25, + nullable: false, + }) + buckets: number, + @Parent() + parent: Data, + ) { + const userConfig = this.confService.getUserConfig(); + + const data = await this.failurerateService.getFailure( + userConfig.elasticsearch.dataIndices.testingCases + '*', + dateInterval, + field, + datefield, + buckets, + parent.query + ); + return { ...data, field }; + } +} diff --git a/src/testingCases/data/data.type.ts b/src/testingCases/data/data.type.ts new file mode 100644 index 0000000..da5c02a --- /dev/null +++ b/src/testingCases/data/data.type.ts @@ -0,0 +1,22 @@ +import { Field, ObjectType } from '@nestjs/graphql'; + +@ObjectType() +export default class TestingCasesData { + @Field({ + nullable: false, + description: 'Query received as a parameter', + }) + query: string; + + @Field({ + nullable: false, + description: 'Transformation of the received query in an Elasticsearch query', + }) + esQuery: string; + + @Field({ + nullable: false, + description: 'Total number of documents matching the query', + }) + count: number; +} diff --git a/src/testingCases/data/failurerate/failurerateConnection.type.ts b/src/testingCases/data/failurerate/failurerateConnection.type.ts new file mode 100644 index 0000000..8be39cd --- /dev/null +++ b/src/testingCases/data/failurerate/failurerateConnection.type.ts @@ -0,0 +1,6 @@ +import { ObjectType } from '@nestjs/graphql'; + +import FailureRateCasesConnection from '../../../utils/data/failureratecases/failurerateConnection.type'; + +@ObjectType() +export default class TestingCasesFailureRateConnection extends FailureRateCasesConnection {} diff --git a/src/testingCases/data/items/caseConnection.type.ts b/src/testingCases/data/items/caseConnection.type.ts new file mode 100644 index 0000000..ab02bf0 --- /dev/null +++ b/src/testingCases/data/items/caseConnection.type.ts @@ -0,0 +1,12 @@ +import { Field, ObjectType, Int } from '@nestjs/graphql'; + +import TestingCase from '../../../utils/testing/types/case'; + +@ObjectType() +export default class TestingCasesConnection { + @Field(() => [TestingCase], { nullable: true }) + nodes: TestingCase[]; + + @Field(() => Int) + totalCount: number; +} diff --git a/src/testingCases/data/items/itemSortorder.type.ts b/src/testingCases/data/items/itemSortorder.type.ts new file mode 100644 index 0000000..21617e4 --- /dev/null +++ b/src/testingCases/data/items/itemSortorder.type.ts @@ -0,0 +1,23 @@ +import { Field, InputType } from '@nestjs/graphql'; +import { Order } from '../../../utils/data/items/order.type'; +import { registerEnumType } from '@nestjs/graphql'; + +export enum ItemSortorderField { + id = 'id', + createdAt = 'createdAt', + updatedAt = 'updatedAt', +} + +registerEnumType(ItemSortorderField, { + name: 'ItemOrderField', + description: 'Properties by which issues connections can be ordered.', +}); + +@InputType() +export default class TestingCasesSortorder extends Order { + @Field({ + nullable: true, + description: 'Order field (see config aggregations node for a sample of possible values)', + }) + field: string; +} diff --git a/src/testingCases/testingCases.module.ts b/src/testingCases/testingCases.module.ts new file mode 100644 index 0000000..2cea039 --- /dev/null +++ b/src/testingCases/testingCases.module.ts @@ -0,0 +1,30 @@ +import { Module } from '@nestjs/common'; +import { ConfModule } from '../conf.module'; +import { EsClientModule } from '../esClient.module'; + +import TestingCasesResolvers from './testingCases.resolvers'; + +import ConfigResolver from './config/config.resolvers'; +import ConfigAggregationsService from '../utils/config/aggregations/aggregations.service'; +import ConfigTableService from '../utils/config/table/table.service'; +import DataResolver from './data/data.resolvers'; +import DataAggregationsService from '../utils/data/aggregations/aggregations.service'; +import DataItemsService from '../utils/data/items/items.service'; +import DataCountService from '../utils/data/count/count.service'; +import DataFailureRateCasesService from '../utils/data/failureratecases/failurerate.service'; + +@Module({ + imports: [ConfModule.register(), EsClientModule], + providers: [ + TestingCasesResolvers, + ConfigResolver, + DataResolver, + ConfigAggregationsService, + ConfigTableService, + DataAggregationsService, + DataItemsService, + DataCountService, + DataFailureRateCasesService, + ], +}) +export class TestingCasesModule {} diff --git a/src/testingCases/testingCases.resolvers.ts b/src/testingCases/testingCases.resolvers.ts new file mode 100644 index 0000000..b136c21 --- /dev/null +++ b/src/testingCases/testingCases.resolvers.ts @@ -0,0 +1,79 @@ +import { Args, Query, Resolver, ResolveField, Parent } from '@nestjs/graphql'; +import { buildQuery } from '@arranger/middleware'; +import { ConfService } from '../conf.service'; + +import TestingCases from './testingCases.type'; +import TestingCasesData from './data/data.type'; +import TestingCasesConfig from './config/config.type'; + +import { getNestedFields } from '../utils/query'; +import DataCountService from '../utils/data/count/count.service'; + +const getEsQuery = async (query: string) => { + const queryObj = JSON.parse(query); + const nestedFields = getNestedFields(queryObj); + + const prepQuery = { + nestedFields, + filters: queryObj, + }; + + let updatedQuery = await buildQuery(prepQuery); + if (Object.entries(updatedQuery).length === 0) { + updatedQuery = { + match_all: {}, + }; + } + return JSON.stringify(updatedQuery); +}; + +// https://github.com/nestjs/graphql/issues/475 +@Resolver(TestingCases) +export default class TestingCasesResolver { + constructor(private readonly confService: ConfService, private readonly countService: DataCountService) {} + + @Query(() => TestingCases, { + name: 'testingCases', + description: 'Fetch data (items, aggregatiosn) related to the dataset', + }) + public async geTestingCases(): Promise { + return new TestingCases(); + } + + @ResolveField(() => TestingCasesData, { + name: 'data', + description: 'Access to the dataset as individual items, aggregations and more', + }) + public async getData( + @Args({ + name: 'query', + type: () => String, + description: 'Query used to filter down content', + nullable: true, + }) + query: string, + // eslint-disable-next-line @typescript-eslint/no-unused-vars + @Parent() parent: TestingCases, + ): Promise { + const userConfig = this.confService.getUserConfig(); + + const data = new TestingCasesData(); + if (query === undefined || query === null) { + query = JSON.stringify({}); + } + data.query = query; + data.esQuery = await getEsQuery(query); + data.count = await this.countService.getCount(query, userConfig.elasticsearch.dataIndices.testingCases + '*'); + + return data; + } + + @ResolveField(() => TestingCasesConfig, { + name: 'config', + description: 'Access to configuration values and metadata', + }) + // eslint-disable-next-line @typescript-eslint/no-unused-vars + public async getConfig(@Parent() parent: TestingCases): Promise { + return new TestingCasesConfig(); + } +} diff --git a/src/testingCases/testingCases.type.ts b/src/testingCases/testingCases.type.ts new file mode 100644 index 0000000..40d2fae --- /dev/null +++ b/src/testingCases/testingCases.type.ts @@ -0,0 +1,4 @@ +import { ObjectType } from '@nestjs/graphql'; + +@ObjectType() +export default class TestingCases {} diff --git a/src/utils/data/failureratecases/failurerate.service.ts b/src/utils/data/failureratecases/failurerate.service.ts new file mode 100644 index 0000000..aa12fb1 --- /dev/null +++ b/src/utils/data/failureratecases/failurerate.service.ts @@ -0,0 +1,15 @@ +import { Injectable } from '@nestjs/common'; +import { EsClientService } from '../../../esClient.service'; + +import { getFailureRateCases } from '../../es/getFailureRateCases'; + +@Injectable() +export default class DataFailureRateCasesService { + constructor(private readonly esClientService: EsClientService) {} + + async getFailure(esIndex: string, dateInterval: string, field: string, datefield: string, buckets: number, query: any, ): Promise { + const esClient = this.esClientService.getEsClient(); + + return await getFailureRateCases(esClient, esIndex, dateInterval, field, datefield, buckets, query); + } +} diff --git a/src/utils/data/failureratecases/failurerateBucket.type.ts b/src/utils/data/failureratecases/failurerateBucket.type.ts new file mode 100644 index 0000000..742b455 --- /dev/null +++ b/src/utils/data/failureratecases/failurerateBucket.type.ts @@ -0,0 +1,49 @@ +import { Field, ObjectType } from '@nestjs/graphql'; + +import FailureRateCasesBucketDate from './failurerateBucketDate.type'; + +@ObjectType() +export default class FailureRateCasesBucket { + @Field({ + nullable: false, + description: 'The Bucket key', + }) + key: string; + + @Field({ + nullable: false, + description: 'The number of elements in the bucket', + }) + docCount: number; + + @Field({ + nullable: true, + description: 'Representation of the key as a string (useful for dates)', + }) + keyAsString: string; + + @Field({ + nullable: false, + description: 'The failure rate (in %) over the entire period', + }) + caseFailureRate: number; + + @Field({ + nullable: true, + description: 'The total number of tests that were executed', + }) + caseTotal: number; + + @Field({ + nullable: true, + description: 'The average number of tests that were executed per case', + }) + caseTotalAvg: number; + + // eslint-disable-next-line @typescript-eslint/no-unused-vars + @Field(type => [FailureRateCasesBucketDate], { + nullable: false, + description: 'Breakdown of the dataset per the defined interval', + }) + buckets: FailureRateCasesBucketDate[]; +} diff --git a/src/utils/data/failureratecases/failurerateBucketDate.type.ts b/src/utils/data/failureratecases/failurerateBucketDate.type.ts new file mode 100644 index 0000000..66f4bf2 --- /dev/null +++ b/src/utils/data/failureratecases/failurerateBucketDate.type.ts @@ -0,0 +1,34 @@ +import { Field, ObjectType } from '@nestjs/graphql'; + +@ObjectType() +export default class FailureRateCasesBucketDate { + @Field({ + nullable: false, + description: 'Start date of each bucket depending of the set interval', + }) + dateStart: string; + + @Field({ + nullable: false, + description: 'The number of documents that week', + }) + docCount: number; + + @Field({ + nullable: false, + description: 'The failure rate (in %) over the bucket window', + }) + caseFailureRate: number; + + @Field({ + nullable: false, + description: 'The total number of tests that were executed', + }) + caseTotal: number; + + @Field({ + nullable: true, + description: 'The average number of tests that were executed per case', + }) + caseTotalAvg: number; +} diff --git a/src/utils/data/failureratecases/failurerateConnection.type.ts b/src/utils/data/failureratecases/failurerateConnection.type.ts new file mode 100644 index 0000000..d51c2f0 --- /dev/null +++ b/src/utils/data/failureratecases/failurerateConnection.type.ts @@ -0,0 +1,30 @@ +import { Field, ObjectType } from '@nestjs/graphql'; + +import FailureRateCasesBucket from './failurerateBucket.type'; + +@ObjectType({ isAbstract: true }) +export default abstract class FailureRateCasesConnection { + @Field(() => String, { + nullable: true, + description: 'Field used to aggregate on', + }) + field: string; + + @Field(() => [FailureRateCasesBucket], { + nullable: false, + description: 'A list of aggregation buckets', + }) + buckets: FailureRateCasesBucket[]; + + @Field(() => String, { + nullable: false, + description: 'Overall first week (first day of the week) in the aggregated buckets', + }) + fromDateStart: string; + + @Field(() => String, { + nullable: false, + description: 'Overall last week (first day of the week) in the aggregated buckets', + }) + toDateStart: string; +} diff --git a/src/utils/es/getFailureRateCases.ts b/src/utils/es/getFailureRateCases.ts new file mode 100644 index 0000000..625c96a --- /dev/null +++ b/src/utils/es/getFailureRateCases.ts @@ -0,0 +1,108 @@ +import { convertSqonToEs } from '../query'; +import { ApiResponse } from '@elastic/elasticsearch'; + +export const getFailureRateCases = async (esClient, esIndex, dateInterval, field, datefield, buckets, query) => { + const filterQuery = JSON.parse(query); + + const convertedQuery = await convertSqonToEs(filterQuery); + + const datasets: ApiResponse = await esClient.search({ + index: esIndex, + size: 0, + body: { + query: convertedQuery, + aggs: { + aggregations: { + terms: { + field: field, + size: buckets, + order: { caseFailureRate: 'desc' } + }, + aggs: { + caseFailureRate: { + avg: { + field: 'caseFailureRate', + missing: -1 + } + }, + caseTotal: { + sum: { + field: 'caseTotal', + missing: -1 + } + }, + caseTotalAvg: { + avg: { + field: 'caseTotal', + missing: -1 + } + }, + histogram: { + date_histogram: { + field: datefield, + calendar_interval: dateInterval + }, + aggs: { + caseFailureRate: { + avg: { + field: 'caseFailureRate', + missing: -1 + } + }, + caseTotal: { + sum: { + field: 'caseTotal', + missing: -1 + } + }, + caseTotalAvg: { + avg: { + field: 'caseTotal', + missing: -1 + } + }, + } + } + } + } + }, + }, + }); + let fromDateStart: Date = new Date(); + let toDateStart: Date = new Date(0); + const fullDataset = datasets.body.aggregations.aggregations.buckets.map((b: any) => { + return { + ...b, + key: b.key, + docCount: b.doc_count, + caseFailureRate: Math.round(b.caseFailureRate.value), + caseTotal: Math.round(b.caseTotal.value), + caseTotalAvg: Math.round(b.caseTotalAvg.value), + buckets: b.histogram.buckets.map((bd: any) => { + // console.log(bd) + if (new Date(bd.key_as_string) < fromDateStart) { + fromDateStart = new Date(bd.key_as_string); + } + if (new Date(bd.key_as_string) > toDateStart) { + toDateStart = new Date(bd.key_as_string); + } + return { + ...bd, + dateStart: bd.key_as_string, + docCount: bd.doc_count, + caseFailureRate: Math.round(bd.caseFailureRate.value), + caseTotal: Math.round(bd.caseTotal.value), + caseTotalAvg: Math.round(bd.caseTotalAvg.value), + } + }) + } + }) + + return { + buckets: fullDataset, + fromDateStart: fromDateStart.toISOString(), + toDateStart: toDateStart.toISOString(), + } +}; + +export default getFailureRateCases; diff --git a/src/utils/testing/types/case.ts b/src/utils/testing/types/case.ts new file mode 100644 index 0000000..1a9e7bc --- /dev/null +++ b/src/utils/testing/types/case.ts @@ -0,0 +1,75 @@ +import { Field, ObjectType, ID } from '@nestjs/graphql'; + +import DependencyConnection from './dependencyConnection'; + +@ObjectType() +export default class TestingCase { + @Field(() => ID) + id: string; + + @Field(() => String, { + nullable: false, + description: 'Date at which the state event was recorded (last)', + }) + createdAt: string; + + @Field(() => String, { + nullable: false, + description: 'Name of the test case being executed', + }) + name: string; + + @Field(() => String, { + nullable: false, + description: 'Name of the project in which the test case is being executed', + }) + project: string; + + @Field(() => String, { + nullable: false, + description: 'Name of the test suite containing that the test case', + }) + suite: string; + + @Field(() => String, { + nullable: false, + description: 'Jahia version used during the test', + }) + jahia: string; + + @Field(() => String, { + nullable: false, + description: 'Jahia module used during the test', + }) + module: string; + + @Field(() => String, { + nullable: false, + description: 'Concatenated name and version, separated with a _', + }) + full: string; + + @Field(() => DependencyConnection, { + nullable: false, + description: 'List of dependencyes to the element being tested', + }) + dependencies: DependencyConnection; + + @Field(() => String, { + nullable: false, + description: 'State of the test execution (PASS, FAIL, ...)', + }) + state: string; + + @Field(() => String, { + nullable: false, + description: 'The HTTP URL for this PR.', + }) + url: string; + + @Field({ + nullable: false, + description: 'Total duration of the test case', + }) + duration: number; +}