Add all vendor project keys and update docs for issue type dropdown
Expand VENDOR_PROJECT_KEYS to include all vendor projects: AA_ADTRAN, AA_ADVA, AA_CASA, AA_CISCO, AACOMMSCOP, AA_COMMSCOP, AA_HARMONI, AA_JUNIPER, AA_VECIMA, AA_VIAVI. Both AACOMMSCOP and AA_COMMSCOP variants are included for safety. Update property tests to exercise the full vendor key list instead of only AA_VECIMA. Update full-reference-manual.md with vendor-specific issue type dropdown documentation.
This commit is contained in:
@@ -102,5 +102,40 @@ No ESLint is configured for backend — the pipeline uses `node -c` syntax check
|
||||
|
||||
| Environment | URL | Notes |
|
||||
|---|---|---|
|
||||
| Production / Dev server | http://IP:3001 | Express serves API + static frontend build |
|
||||
| Production | http://71.85.90.6:3001 | Express serves API + static frontend build |
|
||||
| Staging | http://71.85.90.9:3100 | Auto-deploy on master push |
|
||||
| Local dev (frontend only) | http://localhost:3000 | React dev server with hot-reload, proxies API to :3001 |
|
||||
|
||||
## CI/CD Pipeline
|
||||
|
||||
### Infrastructure
|
||||
|
||||
| Role | Host | Notes |
|
||||
|---|---|---|
|
||||
| GitLab instance | steam-gitlab.charterlab.com | Self-hosted GitLab |
|
||||
| CI Runner (LXC 108) | 71.85.90.8 | Docker executor, Runner #6, project-locked |
|
||||
| Staging target | 71.85.90.9 | Auto-deploy on master, port 3100 |
|
||||
| Production target | 71.85.90.6 | Manual deploy trigger, port 3001 |
|
||||
|
||||
### Executor: Docker
|
||||
|
||||
The pipeline uses **Docker executor** on Runner #6. Jobs run in isolated containers:
|
||||
|
||||
- **Install / Lint / Test / Build stages**: `node:18` image
|
||||
- **Deploy stages**: `alpine:latest` image (installs `openssh-client` and `rsync` at runtime)
|
||||
|
||||
Deploy jobs SSH from inside the Alpine container to the target hosts using a base64-encoded `$SSH_PRIVATE_KEY` stored as a GitLab CI/CD variable.
|
||||
|
||||
### CI/CD Variables (project-level)
|
||||
|
||||
These are set in GitLab → Settings → CI/CD → Variables:
|
||||
|
||||
| Variable | Purpose |
|
||||
|---|---|
|
||||
| `DATABASE_URL` | PostgreSQL connection string for backend integration tests |
|
||||
| `SSH_PRIVATE_KEY` | Base64-encoded private key for deploy SSH access |
|
||||
| `GITLAB_PAT` | Project access token for issue comments and release creation |
|
||||
|
||||
### Pipeline file
|
||||
|
||||
The pipeline is defined in `.gitlab-ci.yml` at the project root. Stages: install → lint → test → build → deploy → verify.
|
||||
|
||||
@@ -15,7 +15,18 @@ const fc = require('fast-check');
|
||||
// Replicate the pure functions from JiraPage.js for testing
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
const VENDOR_PROJECT_KEYS = ['AA_VECIMA'];
|
||||
const VENDOR_PROJECT_KEYS = [
|
||||
'AA_ADTRAN',
|
||||
'AA_ADVA',
|
||||
'AA_CASA',
|
||||
'AA_CISCO',
|
||||
'AACOMMSCOP',
|
||||
'AA_COMMSCOP',
|
||||
'AA_HARMONI',
|
||||
'AA_JUNIPER',
|
||||
'AA_VECIMA',
|
||||
'AA_VIAVI',
|
||||
];
|
||||
|
||||
const VENDOR_ISSUE_TYPES = [
|
||||
'Epic',
|
||||
@@ -58,22 +69,33 @@ function simulateProjectKeyChange(oldKey, newKey, currentIssueType, vendorKeys)
|
||||
return (wasVendor !== isNowVendor) ? '' : currentIssueType;
|
||||
}
|
||||
|
||||
// Helper: generate a vendor key with random casing and optional whitespace
|
||||
const arbVendorKey = fc.constantFrom(...VENDOR_PROJECT_KEYS).chain(key =>
|
||||
fc.oneof(
|
||||
fc.constant(key),
|
||||
fc.constant(key.toLowerCase()),
|
||||
fc.constant(` ${key} `),
|
||||
)
|
||||
);
|
||||
|
||||
// Helper: generate a string that does NOT match any vendor key after normalization
|
||||
const arbNonVendorKey = fc.string({ minLength: 0, maxLength: 50 }).filter(s => {
|
||||
const normalized = s.trim().toUpperCase();
|
||||
return !VENDOR_PROJECT_KEYS.includes(normalized);
|
||||
});
|
||||
|
||||
const arbNonVendorKeyNonEmpty = fc.string({ minLength: 1, maxLength: 30 }).filter(s => {
|
||||
const normalized = s.trim().toUpperCase();
|
||||
return !VENDOR_PROJECT_KEYS.includes(normalized) && normalized.length > 0;
|
||||
});
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Property 1: Issue type list determination
|
||||
// ---------------------------------------------------------------------------
|
||||
describe('Feature: vendor-issue-type-dropdown, Property 1: Issue type list determination', () => {
|
||||
it('returns VENDOR_ISSUE_TYPES when project key matches a vendor key (case-insensitive, trimmed)', () => {
|
||||
// Generate variations of the vendor key with different casing and whitespace
|
||||
const vendorKeyVariants = fc.oneof(
|
||||
fc.constant('AA_VECIMA'),
|
||||
fc.constant('aa_vecima'),
|
||||
fc.constant('Aa_Vecima'),
|
||||
fc.constant(' AA_VECIMA '),
|
||||
fc.constant('aa_VECIMA'),
|
||||
);
|
||||
|
||||
fc.assert(
|
||||
fc.property(vendorKeyVariants, (key) => {
|
||||
fc.property(arbVendorKey, (key) => {
|
||||
const result = getIssueTypesForProject(key, VENDOR_PROJECT_KEYS, VENDOR_ISSUE_TYPES, STEAM_ISSUE_TYPES);
|
||||
expect(result).toBe(VENDOR_ISSUE_TYPES);
|
||||
}),
|
||||
@@ -82,14 +104,8 @@ describe('Feature: vendor-issue-type-dropdown, Property 1: Issue type list deter
|
||||
});
|
||||
|
||||
it('returns STEAM_ISSUE_TYPES for any string that does not match a vendor key after normalization', () => {
|
||||
// Generate arbitrary strings that are NOT 'AA_VECIMA' after trim+uppercase
|
||||
const nonVendorKey = fc.string({ minLength: 0, maxLength: 50 }).filter(s => {
|
||||
const normalized = s.trim().toUpperCase();
|
||||
return normalized !== 'AA_VECIMA';
|
||||
});
|
||||
|
||||
fc.assert(
|
||||
fc.property(nonVendorKey, (key) => {
|
||||
fc.property(arbNonVendorKey, (key) => {
|
||||
const result = getIssueTypesForProject(key, VENDOR_PROJECT_KEYS, VENDOR_ISSUE_TYPES, STEAM_ISSUE_TYPES);
|
||||
expect(result).toBe(STEAM_ISSUE_TYPES);
|
||||
}),
|
||||
@@ -120,20 +136,10 @@ describe('Feature: vendor-issue-type-dropdown, Property 1: Issue type list deter
|
||||
// ---------------------------------------------------------------------------
|
||||
describe('Feature: vendor-issue-type-dropdown, Property 2: Context switch resets issue type', () => {
|
||||
it('resets issue_type to empty when switching from vendor to non-vendor context', () => {
|
||||
// Generate a vendor key variant and a non-vendor key
|
||||
const vendorKey = fc.oneof(
|
||||
fc.constant('AA_VECIMA'),
|
||||
fc.constant('aa_vecima'),
|
||||
fc.constant(' AA_VECIMA '),
|
||||
);
|
||||
const nonVendorKey = fc.string({ minLength: 1, maxLength: 30 }).filter(s => {
|
||||
const normalized = s.trim().toUpperCase();
|
||||
return normalized !== 'AA_VECIMA' && normalized.length > 0;
|
||||
});
|
||||
const anyIssueType = fc.string({ minLength: 1, maxLength: 50 });
|
||||
|
||||
fc.assert(
|
||||
fc.property(vendorKey, nonVendorKey, anyIssueType, (oldKey, newKey, issueType) => {
|
||||
fc.property(arbVendorKey, arbNonVendorKeyNonEmpty, anyIssueType, (oldKey, newKey, issueType) => {
|
||||
const result = simulateProjectKeyChange(oldKey, newKey, issueType, VENDOR_PROJECT_KEYS);
|
||||
expect(result).toBe('');
|
||||
}),
|
||||
@@ -142,18 +148,10 @@ describe('Feature: vendor-issue-type-dropdown, Property 2: Context switch resets
|
||||
});
|
||||
|
||||
it('resets issue_type to empty when switching from non-vendor to vendor context', () => {
|
||||
const nonVendorKey = fc.string({ minLength: 1, maxLength: 30 }).filter(s => {
|
||||
const normalized = s.trim().toUpperCase();
|
||||
return normalized !== 'AA_VECIMA' && normalized.length > 0;
|
||||
});
|
||||
const vendorKey = fc.oneof(
|
||||
fc.constant('AA_VECIMA'),
|
||||
fc.constant('aa_vecima'),
|
||||
);
|
||||
const anyIssueType = fc.string({ minLength: 1, maxLength: 50 });
|
||||
|
||||
fc.assert(
|
||||
fc.property(nonVendorKey, vendorKey, anyIssueType, (oldKey, newKey, issueType) => {
|
||||
fc.property(arbNonVendorKeyNonEmpty, arbVendorKey, anyIssueType, (oldKey, newKey, issueType) => {
|
||||
const result = simulateProjectKeyChange(oldKey, newKey, issueType, VENDOR_PROJECT_KEYS);
|
||||
expect(result).toBe('');
|
||||
}),
|
||||
@@ -167,18 +165,10 @@ describe('Feature: vendor-issue-type-dropdown, Property 2: Context switch resets
|
||||
// ---------------------------------------------------------------------------
|
||||
describe('Feature: vendor-issue-type-dropdown, Property 3: Same context preserves issue type', () => {
|
||||
it('preserves issue_type when both old and new keys resolve to STEAM context', () => {
|
||||
const nonVendorKey1 = fc.string({ minLength: 0, maxLength: 30 }).filter(s => {
|
||||
const normalized = s.trim().toUpperCase();
|
||||
return normalized !== 'AA_VECIMA';
|
||||
});
|
||||
const nonVendorKey2 = fc.string({ minLength: 0, maxLength: 30 }).filter(s => {
|
||||
const normalized = s.trim().toUpperCase();
|
||||
return normalized !== 'AA_VECIMA';
|
||||
});
|
||||
const anyIssueType = fc.string({ minLength: 0, maxLength: 50 });
|
||||
|
||||
fc.assert(
|
||||
fc.property(nonVendorKey1, nonVendorKey2, anyIssueType, (oldKey, newKey, issueType) => {
|
||||
fc.property(arbNonVendorKey, arbNonVendorKey, anyIssueType, (oldKey, newKey, issueType) => {
|
||||
const result = simulateProjectKeyChange(oldKey, newKey, issueType, VENDOR_PROJECT_KEYS);
|
||||
expect(result).toBe(issueType);
|
||||
}),
|
||||
@@ -187,21 +177,10 @@ describe('Feature: vendor-issue-type-dropdown, Property 3: Same context preserve
|
||||
});
|
||||
|
||||
it('preserves issue_type when both old and new keys resolve to vendor context', () => {
|
||||
// With only one vendor key, both must be variants of AA_VECIMA
|
||||
const vendorKey1 = fc.oneof(
|
||||
fc.constant('AA_VECIMA'),
|
||||
fc.constant('aa_vecima'),
|
||||
fc.constant(' AA_VECIMA '),
|
||||
);
|
||||
const vendorKey2 = fc.oneof(
|
||||
fc.constant('AA_VECIMA'),
|
||||
fc.constant('Aa_Vecima'),
|
||||
fc.constant('aa_VECIMA'),
|
||||
);
|
||||
const anyIssueType = fc.string({ minLength: 0, maxLength: 50 });
|
||||
|
||||
fc.assert(
|
||||
fc.property(vendorKey1, vendorKey2, anyIssueType, (oldKey, newKey, issueType) => {
|
||||
fc.property(arbVendorKey, arbVendorKey, anyIssueType, (oldKey, newKey, issueType) => {
|
||||
const result = simulateProjectKeyChange(oldKey, newKey, issueType, VENDOR_PROJECT_KEYS);
|
||||
expect(result).toBe(issueType);
|
||||
}),
|
||||
|
||||
@@ -533,6 +533,8 @@ A dedicated page for managing Jira Data Center tickets linked to CVE/vendor pair
|
||||
- **Sync** — refresh a single ticket's status and summary from Jira, or bulk-sync all tracked tickets via JQL search
|
||||
- **Create / Edit / Delete** — manage local ticket records linking Jira keys to CVE/vendor pairs
|
||||
|
||||
**Vendor-specific issue types:** The Issue Type dropdown in the creation modal is context-aware. When the Project Key field matches a recognized vendor project key (e.g., `AA_VECIMA`, `AA_CISCO`, `AA_ADTRAN`), the dropdown switches to vendor-specific issue types (Epic, Story, Task, Defect, Production Defect/Incident Fix, New Feature, Spike, Release Candidate, Documentation). For all other project keys — including the default from `JIRA_PROJECT_KEY` — the dropdown shows STEAM issue types (Story, Epic, Program, Project, Reservation, Automation Maintenance). Matching is case-insensitive and trims whitespace. Changing the project key such that the context switches (STEAM to vendor or vice versa) resets the selected issue type. The same behavior applies when creating a Jira ticket from the Ivanti Queue. The list of recognized vendor project keys is defined in `VENDOR_PROJECT_KEYS` in `frontend/src/components/pages/JiraPage.js`.
|
||||
|
||||
**Connection test (Admin)** — verify Jira API credentials and connectivity from the page header.
|
||||
|
||||
**Rate limit monitoring (Admin)** — view current burst and daily rate limit usage against Charter's posted limits (60/minute burst, 1 440/day).
|
||||
|
||||
@@ -160,7 +160,18 @@ function getStatusColor(status) {
|
||||
// Vendor issue type configuration
|
||||
// ---------------------------------------------------------------------------
|
||||
// Add new vendor project keys here to enable vendor-specific issue types
|
||||
const VENDOR_PROJECT_KEYS = ['AA_VECIMA'];
|
||||
const VENDOR_PROJECT_KEYS = [
|
||||
'AA_ADTRAN',
|
||||
'AA_ADVA',
|
||||
'AA_CASA',
|
||||
'AA_CISCO',
|
||||
'AACOMMSCOP',
|
||||
'AA_COMMSCOP',
|
||||
'AA_HARMONI',
|
||||
'AA_JUNIPER',
|
||||
'AA_VECIMA',
|
||||
'AA_VIAVI',
|
||||
];
|
||||
|
||||
const VENDOR_ISSUE_TYPES = [
|
||||
'Epic',
|
||||
|
||||
Reference in New Issue
Block a user