Skip to content
Open
2 changes: 2 additions & 0 deletions src/app/app.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,7 @@ import { MarkdownViewDialogComponent } from './components/markdown-view-dialog/m
import { MultipleChoiceDialogComponent } from './components/multiple-choice-dialog/multiple-choice-dialog.component';
import { NavigationComponent } from './components/navigation/navigation.component';
import { ReferenceEditDialogComponent } from './components/reference-edit-dialog/reference-edit-dialog.component';
import { ReleasePreviewDialogComponent } from './components/release-preview-dialog/release-preview-dialog.component';
import { HistoryTimelineComponent } from './components/stix/stix-page-tabs/history-timeline/history-timeline.component';
import { MembershipSectionComponent } from './components/stix/stix-page-tabs/membership-section/membership-section.component';
import { ReferenceSidebarComponent } from './components/resources-drawer/reference-sidebar/reference-sidebar.component';
Expand Down Expand Up @@ -268,6 +269,7 @@ export function initConfig(appConfigService: AppConfigService) {
MembershipSectionComponent,
ReferenceSidebarComponent,
ReferenceEditDialogComponent,
ReleasePreviewDialogComponent,
MultipleChoiceDialogComponent,
ValidationResultsComponent,
AddRelationshipButtonComponent,
Expand Down
3 changes: 1 addition & 2 deletions src/app/classes/release-tracks/api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -35,9 +35,8 @@ export interface UpdateContentsPayload {
}

export interface BumpPayload {
type?: 'major' | 'minor';
increment?: 'major' | 'minor';
version?: string;
dry_run?: boolean;
}

export interface ClonePayload {
Expand Down
3 changes: 2 additions & 1 deletion src/app/classes/release-tracks/tiers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,8 @@ import { WorkflowStatusType } from 'src/app/utils/types';
import { SnapshotTier } from './enums';

export type ReleaseTrackObjectTier =
SnapshotTier.Candidate | SnapshotTier.Staged;
| SnapshotTier.Candidate
| SnapshotTier.Staged;

export interface TierEntryModifiedByUser {
id?: string;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,257 @@
<div class="release-preview-dialog">
<header class="release-preview-header">
<div>
<h2 id="release-preview-dialog-title">
Release Preview: {{ trackName }}
</h2>
<p class="release-version-summary">
<span
>Current: <strong>{{ currentVersion }}</strong></span
>
<span
>Minor Bump: <strong>{{ minorVersion }}</strong></span
>
<span
>Major Bump: <strong>{{ majorVersion }}</strong></span
>
</p>
</div>

<button
mat-icon-button
type="button"
aria-label="Close draft snapshot preview"
(click)="close()">
<mat-icon>close</mat-icon>
</button>
</header>

<mat-tab-group
class="release-preview-tabs"
animationDuration="0ms"
mat-stretch-tabs="false">
<mat-tab>
<ng-template mat-tab-label>
<mat-icon aria-hidden="true">info_outline</mat-icon>
<span>Summary</span>
</ng-template>

<section class="release-summary" aria-label="Draft snapshot summary">
<article class="release-stat release-stat--included">
<strong>{{ totalIncludedCount }}</strong>
<span>Total objects included</span>
</article>
<article class="release-stat release-stat--new">
<strong>{{ newObjectCount }}</strong>
<span>New objects</span>
</article>
<article class="release-stat release-stat--updated">
<strong>{{ updatedMemberCount }}</strong>
<span>Updated members</span>
</article>
<article class="release-stat release-stat--unchanged">
<strong>{{ unchangedObjectCount }}</strong>
<span>Unchanged objects</span>
</article>
<article
*ngIf="!isVirtualTrack"
class="release-stat release-stat--excluded">
<strong>{{ excludedCandidates.length }}</strong>
<span>Candidates not included</span>
</article>
<article
*ngIf="isVirtualTrack"
class="release-stat release-stat--excluded">
<strong>{{ removedObjectCount }}</strong>
<span>Removed objects</span>
</article>
<article
*ngIf="isVirtualTrack"
class="release-stat release-stat--excluded">
<strong>{{ quarantinedObjectCount }}</strong>
<span>Quarantined objects</span>
</article>
</section>
</mat-tab>

<mat-tab>
<ng-template mat-tab-label>
<mat-icon aria-hidden="true">verified</mat-icon>
<span>Included ({{ includedObjects.length }})</span>
</ng-template>

<section class="release-object-list" aria-label="Included objects">
<p *ngIf="includedObjects.length === 0" class="release-object-empty">
No members or staged objects will be included.
</p>
<div *ngIf="includedObjects.length" class="release-table-wrapper">
<table class="release-preview-table">
<thead>
<tr>
<th scope="col">Name / ID</th>
<th scope="col">Type</th>
<th scope="col">Version</th>
<th scope="col">Source</th>
</tr>
</thead>
<tbody>
<tr
*ngFor="
let item of includedObjects;
trackBy: trackByIncludedObject
">
<td>
<strong>{{ getObjectName(item.object) }}</strong>
<small>
{{ item.object.attack_id || item.object.object_ref }}
</small>
</td>
<td>{{ getObjectType(item.object) }}</td>
<td>{{ getObjectVersion(item.object) }}</td>
<td>
<span
class="source-chip"
[class.source-chip--staged]="
getIncludedSource(item) === 'staged'
">
{{ getIncludedSource(item) }}
</span>
</td>
</tr>
</tbody>
</table>
</div>
</section>
</mat-tab>

<mat-tab>
<ng-template mat-tab-label>
<mat-icon aria-hidden="true">cancel</mat-icon>
<span>Excluded ({{ excludedCandidates.length }})</span>
</ng-template>

<section class="release-object-list" aria-label="Excluded candidates">
<p *ngIf="excludedCandidates.length === 0" class="release-object-empty">
No candidates are excluded from this snapshot.
</p>
<div *ngIf="excludedCandidates.length" class="release-table-wrapper">
<table class="release-preview-table release-preview-table--excluded">
<thead>
<tr>
<th scope="col">Name / ID</th>
<th scope="col">Workflow State</th>
</tr>
</thead>
<tbody>
<tr
*ngFor="let item of excludedCandidates; trackBy: trackByObject">
<td>
<strong>{{ getObjectName(item) }}</strong>
<small>{{ item.attack_id || item.object_ref }}</small>
</td>
<td>
<span class="workflow-chip">
{{ getWorkflowState(item) }}
</span>
</td>
</tr>
</tbody>
</table>
</div>
</section>
</mat-tab>

<mat-tab>
<ng-template mat-tab-label>
<mat-icon aria-hidden="true">swap_horiz</mat-icon>
<span>Replacements ({{ replacements.length }})</span>
</ng-template>

<section class="release-object-list" aria-label="Replaced members">
<p *ngIf="replacements.length === 0" class="release-object-empty">
No existing members will be replaced.
</p>
<div *ngIf="replacements.length" class="release-table-wrapper">
<table
class="release-preview-table release-preview-table--replacements">
<thead>
<tr>
<th scope="col">Name / ID</th>
<th scope="col">Current Member Version</th>
<th scope="col">Incoming Staged Version</th>
</tr>
</thead>
<tbody>
<tr
*ngFor="
let item of replacements;
trackBy: trackByIncludedObject
"
[class.release-table-row--invalid]="item.invalidVersionBump">
<td>
<strong>{{ getObjectName(item.object) }}</strong>
<small>
{{ item.object.attack_id || item.object.object_ref }}
</small>
</td>
<td>{{ getObjectVersion(item.currentMember) }}</td>
<td>
<strong
class="incoming-version"
[class.incoming-version--invalid]="item.invalidVersionBump">
{{ getObjectVersion(item.incomingStaged) }}
</strong>
<mat-icon
*ngIf="item.invalidVersionBump"
class="invalid-version-icon"
aria-label="Invalid version bump">
warning_amber
</mat-icon>
</td>
</tr>
</tbody>
</table>
</div>
</section>
</mat-tab>
</mat-tab-group>

<footer class="release-preview-footer">
<p
*ngIf="hasInvalidVersionBumps"
class="release-blocked-message"
role="alert">
<mat-icon aria-hidden="true">warning_amber</mat-icon>
<span> Release is blocked by invalid incoming version bumps. </span>
Comment on lines +221 to +225

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Small wording tweak: this always mentions the release is blocked by invalid version bumps, even if it's just blocked by an API promotion conflict (e.g. staged_to_members: abort). Could we keep this message for when there is an invalid version bump (v1.2 -> v1.4), and add a generic conflict message otherwise? Maybe like "Release is blocked by promotion conflicts."

</p>
<p
*ngIf="!hasInvalidVersionBumps && hasPromotionConflicts"
class="release-blocked-message"
role="alert">
<mat-icon aria-hidden="true">warning_amber</mat-icon>
<span> Release is blocked by promotion conflicts. </span>
</p>

<div class="release-preview-actions">
<button mat-stroked-button type="button" (click)="close()">Cancel</button>
<button
mat-raised-button
class="release-preview-tag-minor-button"
type="button"
color="primary"
[disabled]="isReleaseBlocked"
(click)="tagVersion('minor')">
Tag Minor ({{ minorVersion }})
</button>
<button
mat-raised-button
class="release-preview-tag-major-button"
type="button"
color="primary"
[disabled]="isReleaseBlocked"
(click)="tagVersion('major')">
Tag Major ({{ majorVersion }})
</button>
</div>
</footer>
</div>
Loading