Initial commit: Webex CC + Jira + xAI service
Core capabilities: - Jira ticket lifecycle: status, update, comment, transitions, close - JSM Store Support request creation with Assets object resolution - Assets AQL diagnostic probe endpoint with schema/type introspection - Webex transcript ingestion (audio + JSON + human-readable) with restricted-visibility summary comments - Grok-powered single-ticket and open-tickets-by-reporter summaries Repo hygiene: - .gitignore covering .env, node_modules, logs, IDE dirs - .env.example documenting every env var - discover-ss-*.js scripts refactored to read credentials from .env - README covering setup, endpoints, and the Assets scope-vs-role gotcha Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
commit
1070967870
34 changed files with 7102 additions and 0 deletions
17
.dockerignore
Normal file
17
.dockerignore
Normal file
|
|
@ -0,0 +1,17 @@
|
|||
node_modules
|
||||
npm-debug.log
|
||||
Dockerfile
|
||||
.dockerignore
|
||||
.git
|
||||
.gitignore
|
||||
.gitattributes
|
||||
README.md
|
||||
.env
|
||||
.env.*
|
||||
!.env.example
|
||||
logs
|
||||
*.log
|
||||
.continue
|
||||
.vscode
|
||||
.idea
|
||||
*.md
|
||||
50
.env.example
Normal file
50
.env.example
Normal file
|
|
@ -0,0 +1,50 @@
|
|||
# =============================================
|
||||
# .env.example
|
||||
# Copy to .env and fill in real values. DO NOT commit .env.
|
||||
# =============================================
|
||||
|
||||
# --- Server ---
|
||||
PORT=1866
|
||||
NODE_ENV=development
|
||||
|
||||
# --- Jira ---
|
||||
# Preferred: use the JIRA_CLOUD_ID gateway form. When set, requests go to
|
||||
# https://api.atlassian.com/ex/jira/{cloudId}. If unset, JIRA_BASE_URL is used
|
||||
# directly (e.g. https://your-site.atlassian.net).
|
||||
JIRA_CLOUD_ID=
|
||||
JIRA_BASE_URL=https://your-site.atlassian.net
|
||||
|
||||
# Auth. 'basic' = email + API token (Atlassian API tokens).
|
||||
# 'bearer' = OAuth bearer token in Authorization header.
|
||||
JIRA_AUTH_TYPE=basic
|
||||
JIRA_EMAIL=service-account@example.com
|
||||
JIRA_API_TOKEN=REPLACE_ME
|
||||
|
||||
# JSM Service Desk (Store Support). Numeric service desk id.
|
||||
JIRA_SERVICE_DESK_ID=170
|
||||
|
||||
# Role name used to restrict visibility on comments posted by this service.
|
||||
# Common values: "Administrators", "Service Desk Team".
|
||||
JIRA_COMMENT_VISIBILITY_ROLE=Service Desk Team
|
||||
|
||||
# --- Jira Assets (Store Number -> Assets object resolution) ---
|
||||
# Workspace id for Jira Assets. If unset, the app tries to auto-discover via
|
||||
# /rest/servicedeskapi/assets/workspace, but setting it explicitly is safer
|
||||
# on tenants with more than one Assets workspace.
|
||||
JIRA_ASSETS_WORKSPACE_ID=
|
||||
|
||||
# Numeric object schema and object type id for the Stores schema in Assets.
|
||||
JIRA_ASSETS_STORE_SCHEMA_ID=68
|
||||
JIRA_ASSETS_STORE_OBJECT_TYPE_ID=109
|
||||
|
||||
# The attribute name (as shown in the Assets UI) holding the store number.
|
||||
JIRA_ASSETS_STORE_NUMBER_ATTRIBUTE=Store Number
|
||||
# Optional. If set, the app will also try attribute[<id>]=... form in AQL.
|
||||
JIRA_ASSETS_STORE_NUMBER_ATTRIBUTE_ID=
|
||||
|
||||
# The custom field on the JSM request that holds the Store Assets reference.
|
||||
JIRA_STORE_CUSTOM_FIELD_ID=customfield_10261
|
||||
|
||||
# --- xAI (Grok) ---
|
||||
XAI_API_KEY=REPLACE_ME
|
||||
XAI_BASE_URL=https://api.x.ai/v1
|
||||
54
.gitignore
vendored
Normal file
54
.gitignore
vendored
Normal file
|
|
@ -0,0 +1,54 @@
|
|||
# =============================================
|
||||
# Secrets — NEVER commit these
|
||||
# =============================================
|
||||
.env
|
||||
.env.*
|
||||
!.env.example
|
||||
*.pem
|
||||
*.key
|
||||
*.p12
|
||||
*.pfx
|
||||
credentials*
|
||||
secrets*
|
||||
|
||||
# =============================================
|
||||
# Node
|
||||
# =============================================
|
||||
node_modules/
|
||||
npm-debug.log*
|
||||
yarn-debug.log*
|
||||
yarn-error.log*
|
||||
pnpm-debug.log*
|
||||
.npm/
|
||||
.yarn/
|
||||
.pnp.*
|
||||
|
||||
# =============================================
|
||||
# Runtime / build artifacts
|
||||
# =============================================
|
||||
logs/
|
||||
*.log
|
||||
dist/
|
||||
build/
|
||||
coverage/
|
||||
.nyc_output/
|
||||
tmp/
|
||||
.tmp/
|
||||
|
||||
# =============================================
|
||||
# Editor / IDE / OS
|
||||
# =============================================
|
||||
.vscode/
|
||||
.idea/
|
||||
.continue/
|
||||
*.swp
|
||||
*.swo
|
||||
*~
|
||||
.DS_Store
|
||||
Thumbs.db
|
||||
Desktop.ini
|
||||
|
||||
# =============================================
|
||||
# Docker
|
||||
# =============================================
|
||||
docker-compose.override.yml
|
||||
58
Dockerfile
Normal file
58
Dockerfile
Normal file
|
|
@ -0,0 +1,58 @@
|
|||
# syntax=docker/dockerfile:1
|
||||
|
||||
# =============================================
|
||||
# Stage 1: Builder (install deps + prepare app)
|
||||
# =============================================
|
||||
FROM node:22-alpine AS builder
|
||||
|
||||
WORKDIR /app
|
||||
|
||||
# Copy package files first for better layer caching
|
||||
COPY package*.json ./
|
||||
|
||||
# Install all dependencies (including dev if needed)
|
||||
RUN npm ci --ignore-scripts
|
||||
|
||||
# Copy source code
|
||||
COPY . .
|
||||
|
||||
# Optional: If you ever add a build step (e.g. TypeScript), run it here
|
||||
# RUN npm run build
|
||||
|
||||
# =============================================
|
||||
# Stage 2: Production (lean runtime image)
|
||||
# =============================================
|
||||
FROM node:22-alpine AS production
|
||||
|
||||
# Create non-root user for security
|
||||
RUN addgroup -g 1001 -S nodejs && \
|
||||
adduser -S -u 1001 -G nodejs nodejs
|
||||
|
||||
WORKDIR /app
|
||||
|
||||
# Copy package files
|
||||
COPY package*.json ./
|
||||
|
||||
# Install ONLY production dependencies
|
||||
RUN npm ci --only=production --ignore-scripts && \
|
||||
npm cache clean --force
|
||||
|
||||
# Copy built/ready files from builder stage
|
||||
COPY --from=builder /app/src ./src
|
||||
COPY --from=builder /app/node_modules ./node_modules
|
||||
|
||||
# Change ownership to non-root user
|
||||
RUN chown -R nodejs:nodejs /app
|
||||
|
||||
# Switch to non-root user
|
||||
USER nodejs
|
||||
|
||||
# Expose the port your app listens on (from your config)
|
||||
EXPOSE 1866
|
||||
|
||||
# Detailed healthcheck using the new /health endpoint
|
||||
HEALTHCHECK --interval=30s --timeout=10s --start-period=20s --retries=3 \
|
||||
CMD wget --no-verbose --tries=1 --spider http://localhost:1866/health || exit 1
|
||||
|
||||
# Start the app directly with node (better than npm start in containers)
|
||||
CMD ["node", "src/app.js"]
|
||||
84
README.md
Normal file
84
README.md
Normal file
|
|
@ -0,0 +1,84 @@
|
|||
# wxcc-ai
|
||||
|
||||
Node/Express service that wires Webex Contact Center summaries, Jira tickets (core + JSM Store Support), and xAI Grok summarization into a single set of internal HTTP endpoints.
|
||||
|
||||
## Setup
|
||||
|
||||
```bash
|
||||
npm install
|
||||
cp .env.example .env
|
||||
# fill in JIRA_*, XAI_*, and optionally the Assets values
|
||||
npm run dev
|
||||
```
|
||||
|
||||
The server listens on `PORT` (default `1866`).
|
||||
|
||||
## Environment variables
|
||||
|
||||
See [`.env.example`](./.env.example) for the full list. The important ones:
|
||||
|
||||
| Var | Purpose |
|
||||
| --- | --- |
|
||||
| `JIRA_CLOUD_ID` | If set, requests use `https://api.atlassian.com/ex/jira/{cloudId}`. |
|
||||
| `JIRA_BASE_URL` | Fallback for legacy site URLs (`https://your-site.atlassian.net`). |
|
||||
| `JIRA_EMAIL`, `JIRA_API_TOKEN` | Basic auth to Jira. `JIRA_AUTH_TYPE=bearer` switches to bearer. |
|
||||
| `JIRA_SERVICE_DESK_ID` | Numeric JSM service desk id for Store Support. |
|
||||
| `JIRA_ASSETS_WORKSPACE_ID` | Assets workspace id (auto-discovered if omitted). |
|
||||
| `JIRA_ASSETS_STORE_SCHEMA_ID`, `_OBJECT_TYPE_ID` | Locate the Store schema/type. |
|
||||
| `JIRA_ASSETS_STORE_NUMBER_ATTRIBUTE[_ID]` | Attribute name (or id) holding the store number in Assets. |
|
||||
| `JIRA_STORE_CUSTOM_FIELD_ID` | Custom field on the JSM request that holds the Store Assets reference. |
|
||||
| `XAI_API_KEY`, `XAI_BASE_URL` | Grok credentials for summary generation. |
|
||||
|
||||
## Endpoints
|
||||
|
||||
Base path: `/api/wxccai`.
|
||||
|
||||
### Read
|
||||
|
||||
- `GET /getticket?jiraKey=CS-1234` — Grok-summarized single ticket.
|
||||
- `GET /open-tickets-by-reporter?email=user@example.com` — Grok-summarized list of open tickets a person reported.
|
||||
- `GET /ticket/:key/status` — raw status fields (no Grok).
|
||||
- `GET /ticket/:key/transitions` — available workflow transitions.
|
||||
|
||||
### Write
|
||||
|
||||
- `PATCH /ticket/:key` — body: `{ summary?, description?, priority?, labels?, assigneeAccountId?, additional? }`
|
||||
- `POST /ticket/:key/comment` — body: `{ text, internal? }`
|
||||
- `POST /ticket/:key/close` — body: `{ transitionName?, resolution?, comment?, internal? }` (auto-picks the first "done" transition when `transitionName` omitted).
|
||||
|
||||
### Store Support (JSM requests)
|
||||
|
||||
- `GET /ssRequestTypes` — supported `subType` values + Assets config summary.
|
||||
- `POST /createSSRequest` — body includes `subType`, `summary`, `storeNumber` (auto-resolved via Assets), `onBehalfOf`, `description`, `additional`.
|
||||
|
||||
### Webex webhook
|
||||
|
||||
- `POST /issueTranscript/:jiraKey` — attaches audio + JSON transcript + human-readable transcript, then posts a restricted-visibility summary comment.
|
||||
|
||||
### Debug (non-production only)
|
||||
|
||||
- `GET /debug/assetsProbe?storeNumber=305` — runs several AQL variants against Jira Assets and returns visible schemas + object-type detail + a computed diagnosis. Returns 404 when `NODE_ENV=production`.
|
||||
|
||||
## Jira Assets gotcha
|
||||
|
||||
`createSSRequest` resolves a store number to an Assets object reference (`customfield_10261`). Two independent permission layers must both grant access, or every AQL query silently returns `total: 0`:
|
||||
|
||||
1. **OAuth scopes** on the API token: `read:cmdb-schema:jira`, `read:cmdb-type:jira`, `read:cmdb-object:jira`, `read:cmdb-attribute:jira` (and the `write:` equivalents for updates).
|
||||
2. **Object Schema role membership** in Jira Assets itself — the underlying user needs to be added to a role on the Store schema in Jira → Assets → Object schemas → Configure → Roles.
|
||||
|
||||
If AQL keeps returning `total: 0` with HTTP 200, run the probe endpoint above; the `diagnosis` field will tell you exactly which layer is missing.
|
||||
|
||||
## Docker
|
||||
|
||||
```bash
|
||||
npm run docker:build
|
||||
npm run docker:run
|
||||
```
|
||||
|
||||
The image runs as a non-root user and exposes `1866`. `HEALTHCHECK` pings `GET /health`.
|
||||
|
||||
## Repo hygiene
|
||||
|
||||
- Secrets live only in `.env`, which is `.gitignore`d.
|
||||
- The two `discover-ss-*.js` helper scripts read from `.env` — never hardcode credentials in them.
|
||||
- The debug logger no longer echoes the outbound `Authorization` header. Rotate any token that appears in older `logs/*.log` files.
|
||||
51
discover-ss-fields.js
Normal file
51
discover-ss-fields.js
Normal file
|
|
@ -0,0 +1,51 @@
|
|||
// discover-ss-fields.js
|
||||
// One-off helper to dump JSM request-type field definitions for the Store
|
||||
// Support service desk. Output files (ss-fields-<id>.json) are used to keep
|
||||
// REQUEST_TYPE_MAP in src/services/jiraService.js in sync with what JSM
|
||||
// actually accepts.
|
||||
//
|
||||
// Usage: node discover-ss-fields.js
|
||||
// Reads credentials from .env — never commit real tokens into this file.
|
||||
|
||||
import 'dotenv/config';
|
||||
import fetch from 'node-fetch';
|
||||
import fs from 'fs/promises';
|
||||
|
||||
const BASE_URL = (process.env.JIRA_BASE_URL || 'https://your-site.atlassian.net').replace(/\/$/, '');
|
||||
const SERVICE_DESK_ID = process.env.JIRA_SERVICE_DESK_ID || '170';
|
||||
const EMAIL = process.env.JIRA_EMAIL;
|
||||
const TOKEN = process.env.JIRA_API_TOKEN;
|
||||
|
||||
if (!EMAIL || !TOKEN) {
|
||||
console.error('JIRA_EMAIL and JIRA_API_TOKEN must be set in .env');
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
const headers = {
|
||||
'Accept': 'application/json',
|
||||
'Content-Type': 'application/json',
|
||||
'Authorization': 'Basic ' + Buffer.from(`${EMAIL}:${TOKEN}`).toString('base64')
|
||||
};
|
||||
|
||||
// Request-type ids that we care about (matches REQUEST_TYPE_MAP in jiraService.js).
|
||||
const importantTypes = [269, 266, 273, 274, 267, 275, 272, 268, 271, 270, 426, 493];
|
||||
|
||||
async function getFields(requestTypeId) {
|
||||
console.log(`Fetching fields for request type ${requestTypeId}...`);
|
||||
const res = await fetch(`${BASE_URL}/rest/servicedeskapi/servicedesk/${SERVICE_DESK_ID}/requesttype/${requestTypeId}/field`, { headers });
|
||||
const data = await res.json();
|
||||
|
||||
if (res.ok) {
|
||||
await fs.writeFile(`ss-fields-${requestTypeId}.json`, JSON.stringify(data, null, 2));
|
||||
console.log(` saved ss-fields-${requestTypeId}.json`);
|
||||
} else {
|
||||
console.error(' error:', data);
|
||||
}
|
||||
return data;
|
||||
}
|
||||
|
||||
for (const id of importantTypes) {
|
||||
await getFields(id);
|
||||
}
|
||||
|
||||
console.log('\nDone. Check the generated ss-fields-*.json files.');
|
||||
80
discover-ss-request-types.js
Normal file
80
discover-ss-request-types.js
Normal file
|
|
@ -0,0 +1,80 @@
|
|||
// discover-ss-request-types.js
|
||||
// One-off helper to list all JSM request types on the Store Support service
|
||||
// desk. Produces ss-request-types.json (raw) and ss-request-types-clean.json
|
||||
// (id/name/issueTypeId only) — both useful for maintaining REQUEST_TYPE_MAP
|
||||
// in src/services/jiraService.js.
|
||||
//
|
||||
// Usage: node discover-ss-request-types.js
|
||||
// Reads credentials from .env — never commit real tokens into this file.
|
||||
|
||||
import 'dotenv/config';
|
||||
import fetch from 'node-fetch';
|
||||
import fs from 'fs/promises';
|
||||
|
||||
const BASE_URL = (process.env.JIRA_BASE_URL || 'https://your-site.atlassian.net').replace(/\/$/, '');
|
||||
const SERVICE_DESK_ID = process.env.JIRA_SERVICE_DESK_ID || '170';
|
||||
const EMAIL = process.env.JIRA_EMAIL;
|
||||
const TOKEN = process.env.JIRA_API_TOKEN;
|
||||
|
||||
if (!EMAIL || !TOKEN) {
|
||||
console.error('JIRA_EMAIL and JIRA_API_TOKEN must be set in .env');
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
const headers = {
|
||||
'Accept': 'application/json',
|
||||
'Content-Type': 'application/json',
|
||||
'Authorization': 'Basic ' + Buffer.from(`${EMAIL}:${TOKEN}`).toString('base64')
|
||||
};
|
||||
|
||||
async function getRequestTypes() {
|
||||
console.log(`Fetching request types for service desk ${SERVICE_DESK_ID}...\n`);
|
||||
|
||||
const res = await fetch(`${BASE_URL}/rest/servicedeskapi/servicedesk/${SERVICE_DESK_ID}/requesttype?limit=100`, { headers });
|
||||
const data = await res.json();
|
||||
|
||||
if (!res.ok) {
|
||||
console.error('Error:', data);
|
||||
return;
|
||||
}
|
||||
|
||||
console.log(`Found ${data.values.length} request types.\n`);
|
||||
|
||||
await fs.writeFile('ss-request-types.json', JSON.stringify(data, null, 2));
|
||||
console.log('Full list saved to ss-request-types.json');
|
||||
|
||||
const mapping = data.values.map(rt => ({
|
||||
id: rt.id,
|
||||
name: rt.name,
|
||||
defaultName: rt.defaultName,
|
||||
description: rt.description,
|
||||
issueTypeId: rt.issueTypeId,
|
||||
groupIds: rt.groupIds,
|
||||
canCreateRequest: rt.canCreateRequest
|
||||
}));
|
||||
|
||||
await fs.writeFile('ss-request-types-clean.json', JSON.stringify(mapping, null, 2));
|
||||
console.log('Clean mapping saved to ss-request-types-clean.json');
|
||||
}
|
||||
|
||||
async function getFieldsForRequestType(requestTypeId) {
|
||||
console.log(`\nFetching fields for request type ${requestTypeId}...`);
|
||||
const res = await fetch(`${BASE_URL}/rest/servicedeskapi/servicedesk/${SERVICE_DESK_ID}/requesttype/${requestTypeId}/field`, { headers });
|
||||
const data = await res.json();
|
||||
|
||||
if (!res.ok) {
|
||||
console.error('Error:', data);
|
||||
return null;
|
||||
}
|
||||
|
||||
await fs.writeFile(`fields-requesttype-${requestTypeId}.json`, JSON.stringify(data, null, 2));
|
||||
console.log(`Fields saved to fields-requesttype-${requestTypeId}.json`);
|
||||
return data;
|
||||
}
|
||||
|
||||
await getRequestTypes();
|
||||
|
||||
// Fetch fields for specific request types by uncommenting below.
|
||||
// await getFieldsForRequestType(269); // Register Not Functioning Properly
|
||||
// await getFieldsForRequestType(266); // Broken Device / Hardware
|
||||
// await getFieldsForRequestType(426); // UKG Pro / Workforce Management Issues
|
||||
15
docker-compose.yml
Normal file
15
docker-compose.yml
Normal file
|
|
@ -0,0 +1,15 @@
|
|||
services:
|
||||
wxcc-jira-grok:
|
||||
build: .
|
||||
ports:
|
||||
- "1866:1866"
|
||||
env_file:
|
||||
- .env
|
||||
restart: unless-stopped
|
||||
# Optional: healthcheck at compose level
|
||||
healthcheck:
|
||||
test: ["CMD", "wget", "--no-verbose", "--tries=1", "--spider", "http://localhost:1866/health"]
|
||||
interval: 30s
|
||||
timeout: 8s
|
||||
start_period: 15s
|
||||
retries: 3
|
||||
1872
package-lock.json
generated
Normal file
1872
package-lock.json
generated
Normal file
File diff suppressed because it is too large
Load diff
26
package.json
Normal file
26
package.json
Normal file
|
|
@ -0,0 +1,26 @@
|
|||
{
|
||||
"name": "wxcc-ai",
|
||||
"version": "1.0.0",
|
||||
"description": "Webex Contact Center Jira summarizer powered by xAI Grok",
|
||||
"main": "src/app.js",
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"start": "node src/app.js",
|
||||
"dev": "nodemon src/app.js",
|
||||
"docker:build": "docker build -t wxcc-jira-grok:latest .",
|
||||
"docker:run": "docker run -p 1866:1866 --env-file .env --rm wxcc-jira-grok:latest"
|
||||
},
|
||||
"dependencies": {
|
||||
"axios": "^1.8.0",
|
||||
"axios-retry": "^4.5.0",
|
||||
"dotenv": "^16.4.5",
|
||||
"express": "^4.21.0",
|
||||
"form-data": "^4.0.5",
|
||||
"node-fetch": "^3.3.2",
|
||||
"openai": "^4.85.0",
|
||||
"winston": "^3.17.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"nodemon": "^3.1.0"
|
||||
}
|
||||
}
|
||||
134
src/app.js
Normal file
134
src/app.js
Normal file
|
|
@ -0,0 +1,134 @@
|
|||
import express from 'express';
|
||||
import config from './config/index.js';
|
||||
import wxccRoutes from './routes/wxccRoutes.js';
|
||||
import { getDetailedHealth } from './services/healthService.js';
|
||||
import logger from './utilities/logger.js';
|
||||
|
||||
const app = express();
|
||||
|
||||
// Early request logging (BEFORE body parsers).
|
||||
// This guarantees we see evidence of requests reaching the app (even if JSON
|
||||
// parsing fails, wrong method, or 404). Critical for debugging webhooks from
|
||||
// external systems like Webex CC where the request may never reach our
|
||||
// route handler.
|
||||
app.use((req, res, next) => {
|
||||
const isApi = req.path.startsWith('/api/') || req.url.startsWith('/api/');
|
||||
const isTranscript = req.path.includes('/issueTranscript') || req.url.includes('/issueTranscript');
|
||||
|
||||
if (isApi || isTranscript) {
|
||||
const logInfo = {
|
||||
method: req.method,
|
||||
url: req.originalUrl || req.url,
|
||||
ip: req.headers['x-forwarded-for'] || req.socket?.remoteAddress,
|
||||
contentType: req.headers['content-type'],
|
||||
contentLength: req.headers['content-length'],
|
||||
userAgent: req.headers['user-agent']
|
||||
};
|
||||
|
||||
// Direct console for maximum visibility in docker logs / platform stdout
|
||||
if (isTranscript) {
|
||||
console.log('TRANSCRIPT WEBHOOK REQUEST:', JSON.stringify(logInfo));
|
||||
} else {
|
||||
console.log('API REQUEST:', JSON.stringify(logInfo));
|
||||
}
|
||||
|
||||
// Also via structured logger (goes to app.log + console per config)
|
||||
logger.info('Incoming API request', logInfo);
|
||||
}
|
||||
next();
|
||||
});
|
||||
|
||||
// Special handling for the Webex transcript webhook.
|
||||
// The upstream Java client sometimes includes unescaped control characters
|
||||
// (newlines, etc.) inside the Summaries strings, which breaks strict JSON.parse.
|
||||
// We use express.raw() for this specific path so we always get the buffer,
|
||||
// then we sanitize and parse inside the route handler.
|
||||
app.use('/api/wxccai/issueTranscript', express.raw({
|
||||
type: 'application/json',
|
||||
limit: '10mb',
|
||||
verify: (req, res, buf) => {
|
||||
req.rawBody = buf; // keep raw for possible future needs
|
||||
}
|
||||
}));
|
||||
|
||||
// Middleware - normal JSON parser for all other routes
|
||||
app.use(express.json({
|
||||
limit: '10mb',
|
||||
verify: (req, res, buf) => {
|
||||
req.rawBody = buf; // Store raw buffer for fallback parsing
|
||||
}
|
||||
}));
|
||||
|
||||
// ========================
|
||||
// Health Check Routes
|
||||
// ========================
|
||||
app.get('/health', async (req, res) => {
|
||||
try {
|
||||
const health = await getDetailedHealth();
|
||||
const statusCode = health.status === 'unhealthy' ? 503 : 200;
|
||||
res.status(statusCode).json(health);
|
||||
} catch (err) {
|
||||
logger.error('Health check failed:', err);
|
||||
res.status(503).json({
|
||||
status: 'unhealthy',
|
||||
message: 'Health check failed',
|
||||
error: err.message
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
app.get('/health/detailed', async (req, res) => {
|
||||
try {
|
||||
const health = await getDetailedHealth();
|
||||
res.json(health);
|
||||
} catch (err) {
|
||||
res.status(503).json({
|
||||
status: 'unhealthy',
|
||||
message: 'Detailed health check failed',
|
||||
error: err.message
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
// Simple welcome route at root
|
||||
app.get('/', (req, res) => {
|
||||
res.json({
|
||||
message: "Webex Contact Center + Jira + xAI Grok API service is running",
|
||||
version: process.env.npm_package_version || "1.0.0",
|
||||
endpoints: {
|
||||
health: "/health",
|
||||
singleTicket: "/api/wxccai/getticket?jiraKey=CS-1234 (Grok-summarized)",
|
||||
openTicketsByReporter: "/api/wxccai/open-tickets-by-reporter?email=user@example.com",
|
||||
ticketStatus: "GET /api/wxccai/ticket/:key/status (raw fields, no Grok)",
|
||||
updateTicket: "PATCH /api/wxccai/ticket/:key body: { summary?, description?, priority?, labels?, assigneeAccountId?, additional? }",
|
||||
addComment: "POST /api/wxccai/ticket/:key/comment body: { text, internal? }",
|
||||
transitions: "GET /api/wxccai/ticket/:key/transitions",
|
||||
closeTicket: "POST /api/wxccai/ticket/:key/close body: { transitionName?, resolution?, comment?, internal? }",
|
||||
createSSRequest: "POST /api/wxccai/createSSRequest (store support ticket creator; storeNumber auto-resolved via Assets)",
|
||||
ssRequestTypes: "GET /api/wxccai/ssRequestTypes (lists exact subType values + config notes)",
|
||||
assetsProbe: "GET /api/wxccai/debug/assetsProbe?storeNumber=305 (non-production only)"
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
// ========================
|
||||
// Main API Routes (mounted under /api)
|
||||
// ========================
|
||||
app.use('/api', wxccRoutes);
|
||||
|
||||
// 404 handler
|
||||
app.use((req, res) => {
|
||||
logger.warn('Route not found', {
|
||||
method: req.method,
|
||||
url: req.originalUrl || req.url,
|
||||
ip: req.headers['x-forwarded-for'] || req.socket?.remoteAddress
|
||||
});
|
||||
res.status(404).json({
|
||||
error: "Route not found",
|
||||
message: "Available endpoints are under /api/wxccai/..."
|
||||
});
|
||||
});
|
||||
|
||||
app.listen(config.port, () => {
|
||||
logger.info(`Server running on port ${config.port} in ${config.nodeEnv} mode`);
|
||||
});
|
||||
67
src/config/index.js
Normal file
67
src/config/index.js
Normal file
|
|
@ -0,0 +1,67 @@
|
|||
import dotenv from 'dotenv';
|
||||
dotenv.config();
|
||||
|
||||
const cloudId = process.env.JIRA_CLOUD_ID?.trim();
|
||||
let baseUrl = process.env.JIRA_BASE_URL?.trim();
|
||||
|
||||
if (cloudId) {
|
||||
// When JIRA_CLOUD_ID is provided, use the api.atlassian.com/ex/jira gateway form.
|
||||
// This is the current style for addressing a specific Jira Cloud site (e.g. for
|
||||
// /rest/servicedeskapi/... and /rest/api/3/... calls). Falls back to JIRA_BASE_URL
|
||||
// for legacy site-based URLs like https://your-site.atlassian.net when no cloud ID is set.
|
||||
baseUrl = `https://api.atlassian.com/ex/jira/${cloudId}`;
|
||||
} else if (baseUrl) {
|
||||
// Normalize legacy site base (remove trailing slash)
|
||||
baseUrl = baseUrl.replace(/\/$/, '');
|
||||
}
|
||||
|
||||
// Validate required environment variables
|
||||
const requiredEnvVars = [
|
||||
'JIRA_EMAIL',
|
||||
'JIRA_API_TOKEN',
|
||||
'XAI_API_KEY'
|
||||
];
|
||||
|
||||
const missingVars = requiredEnvVars.filter(env => !process.env[env]);
|
||||
if (missingVars.length > 0) {
|
||||
console.warn(`Missing required environment variables: ${missingVars.join(', ')}`);
|
||||
}
|
||||
|
||||
export const config = {
|
||||
port: process.env.PORT || 1866,
|
||||
nodeEnv: process.env.NODE_ENV || 'development',
|
||||
|
||||
jira: {
|
||||
baseUrl,
|
||||
cloudId: cloudId || null,
|
||||
authType: (process.env.JIRA_AUTH_TYPE || 'basic').toLowerCase(), // 'basic' or 'bearer'
|
||||
email: process.env.JIRA_EMAIL,
|
||||
apiToken: process.env.JIRA_API_TOKEN,
|
||||
serviceDeskId: process.env.JIRA_SERVICE_DESK_ID,
|
||||
// Role used for restricted/internal visibility on summary comments.
|
||||
// Common values: "Administrators", or a project role you have defined.
|
||||
commentVisibilityRole: process.env.JIRA_COMMENT_VISIBILITY_ROLE || 'Administrators',
|
||||
|
||||
// Assets / CMDB configuration for Store Number object references.
|
||||
// These are used for the "service object" / Assets object lookup when creating SS tickets.
|
||||
// The lookup now uses the api.atlassian.com workspace-scoped endpoint:
|
||||
// https://api.atlassian.com/jsm/assets/workspace/{workspaceId}/v1/object/aql
|
||||
assetsWorkspaceId: process.env.JIRA_ASSETS_WORKSPACE_ID,
|
||||
// (optional) numeric schema or object type details
|
||||
assetsStoreSchemaId: process.env.JIRA_ASSETS_STORE_SCHEMA_ID,
|
||||
assetsStoreObjectTypeId: process.env.JIRA_ASSETS_STORE_OBJECT_TYPE_ID || '109',
|
||||
assetsStoreNumberAttribute: process.env.JIRA_ASSETS_STORE_NUMBER_ATTRIBUTE || 'Store Number',
|
||||
// If you know the attribute ID (e.g. 635 from schema), we can try "attribute[635]" form in AQL
|
||||
assetsStoreNumberAttributeId: process.env.JIRA_ASSETS_STORE_NUMBER_ATTRIBUTE_ID || null,
|
||||
|
||||
// The custom field ID on the request that holds the Store Assets reference
|
||||
storeCustomFieldId: process.env.JIRA_STORE_CUSTOM_FIELD_ID || 'customfield_10261',
|
||||
},
|
||||
|
||||
xai: {
|
||||
apiKey: process.env.XAI_API_KEY,
|
||||
baseUrl: process.env.XAI_BASE_URL || 'https://api.x.ai/v1',
|
||||
}
|
||||
};
|
||||
|
||||
export default config;
|
||||
30
src/prompts/openTicketsSummary.txt
Normal file
30
src/prompts/openTicketsSummary.txt
Normal file
|
|
@ -0,0 +1,30 @@
|
|||
You are assisting a Webex Contact Center agent. Return ONLY a valid JSON array with one object per ticket. No extra text, no markdown, no explanations.
|
||||
|
||||
For each ticket, create a short, neutral, spoken-friendly summary (maximum 2 sentences) that includes:
|
||||
- The initial problem / request from the customer (from the description)
|
||||
- Key actions that have been taken so far (from description and public comments)
|
||||
- Current state and any blockers or next steps (based especially on the most recent public notes/comments)
|
||||
|
||||
Rules:
|
||||
- Do NOT mention any names or people
|
||||
- Be factual and concise — easy to read aloud
|
||||
- Prioritize the latest public comments to determine current status
|
||||
|
||||
Each object must have exactly these fields:
|
||||
- jiraKey: string
|
||||
- currentStatus: the exact current status name from Jira (e.g. "In Progress", "Waiting for Customer", "To Do")
|
||||
- summary: string (the spoken summary described above)
|
||||
- lastUpdated: ISO timestamp string from the ticket or null
|
||||
|
||||
Example:
|
||||
[
|
||||
{
|
||||
"jiraKey": "CS-1234",
|
||||
"currentStatus": "In Progress",
|
||||
"summary": "Customer reported slow performance when generating reports. Backend indexing was optimized and a new cache layer was added last week. Final performance testing is now underway.",
|
||||
"lastUpdated": "2026-04-13T08:38:00-05:00"
|
||||
}
|
||||
]
|
||||
|
||||
Tickets context:
|
||||
{{context}}
|
||||
30
src/prompts/singleTicketSummary.txt
Normal file
30
src/prompts/singleTicketSummary.txt
Normal file
|
|
@ -0,0 +1,30 @@
|
|||
You are assisting a Webex Contact Center agent. Return ONLY a valid JSON array with one object per ticket. No extra text, no markdown, no explanations.
|
||||
|
||||
For each ticket, create a short, neutral, spoken-friendly summary (maximum 2 sentences) that includes:
|
||||
- The initial problem / request from the customer (from the description)
|
||||
- Key actions that have been taken so far (from description and public comments)
|
||||
- Current state and any blockers or next steps (based especially on the most recent public notes/comments)
|
||||
|
||||
Rules:
|
||||
- Do NOT mention any names or people
|
||||
- Be factual and concise — easy to read aloud
|
||||
- Prioritize the latest public comments to determine current status
|
||||
|
||||
Each object must have exactly these fields:
|
||||
- jiraKey: string
|
||||
- currentStatus: the exact current status name from Jira (e.g. "In Progress", "Waiting for Customer", "To Do")
|
||||
- summary: string (the spoken summary described above)
|
||||
- lastUpdated: ISO timestamp string from the ticket or null
|
||||
|
||||
Example:
|
||||
[
|
||||
{
|
||||
"jiraKey": "CS-1234",
|
||||
"currentStatus": "In Progress",
|
||||
"summary": "Customer reported slow performance when generating reports. Backend indexing was optimized and a new cache layer was added last week. Final performance testing is now underway.",
|
||||
"lastUpdated": "2026-04-13T08:38:00-05:00"
|
||||
}
|
||||
]
|
||||
|
||||
Tickets context:
|
||||
{{context}}
|
||||
574
src/routes/wxccRoutes.js
Normal file
574
src/routes/wxccRoutes.js
Normal file
|
|
@ -0,0 +1,574 @@
|
|||
import express from 'express';
|
||||
import axios from 'axios';
|
||||
import axiosRetry from 'axios-retry';
|
||||
import { logger, webexLogger } from '../utilities/logger.js';
|
||||
import * as jiraService from '../services/jiraService.js';
|
||||
import grokService from '../services/grokService.js';
|
||||
import config from '../config/index.js';
|
||||
|
||||
// Configure retry for transient failures
|
||||
axiosRetry(axios, {
|
||||
retries: 3,
|
||||
retryDelay: (retryCount) => Math.pow(2, retryCount) * 1000,
|
||||
retryCondition: (error) => {
|
||||
return axiosRetry.isNetworkOrIdempotentRequestError(error) ||
|
||||
error.response?.status === 429 ||
|
||||
error.response?.status >= 500;
|
||||
}
|
||||
});
|
||||
|
||||
const router = express.Router();
|
||||
|
||||
// ========================
|
||||
// Single Ticket Lookup
|
||||
// ========================
|
||||
router.get('/wxccai/getticket', async (req, res) => {
|
||||
const jiraKey = req.query.jiraKey?.trim().toUpperCase();
|
||||
|
||||
if (!jiraKey || !/^[A-Z0-9]+-\d+$/.test(jiraKey)) {
|
||||
return res.status(400).json({
|
||||
jiraKey: jiraKey || "missing",
|
||||
assignedTo: "Error",
|
||||
status: "Error",
|
||||
summary: "Invalid or missing Jira key"
|
||||
});
|
||||
}
|
||||
|
||||
try {
|
||||
const [issueData, description, comments] = await Promise.all([
|
||||
jiraService.fetchJiraIssue(jiraKey).catch(() => ({})),
|
||||
jiraService.fetchPlainDescription(jiraKey).catch(() => "No description."),
|
||||
jiraService.fetchPublicComments(jiraKey).catch(() => [])
|
||||
]);
|
||||
|
||||
const fields = issueData.fields || {};
|
||||
const assignedTo = fields.assignee?.displayName || fields.assignee?.name || "Unassigned";
|
||||
const status = fields.status?.name || "Unknown";
|
||||
const title = fields.summary || "No title";
|
||||
|
||||
let commentsText = "";
|
||||
if (Array.isArray(comments) && comments.length > 0) {
|
||||
const recent = comments.slice(-5);
|
||||
commentsText = "\nRecent comments:\n" + recent.map(c => {
|
||||
let dateStr = "unknown-date";
|
||||
if (typeof c.created === 'string') {
|
||||
dateStr = c.created.slice(0, 10);
|
||||
} else if (c.created?.iso8601) {
|
||||
dateStr = c.created.iso8601.slice(0, 10);
|
||||
} else if (c.created?.jira) {
|
||||
dateStr = c.created.jira.slice(0, 10);
|
||||
}
|
||||
|
||||
const bodyPreview = typeof c.body === 'string'
|
||||
? c.body.substring(0, 250) + (c.body.length > 250 ? '...' : '')
|
||||
: '[No body]';
|
||||
|
||||
return `[${dateStr}] ${c.author || 'Unknown'}: ${bodyPreview}`;
|
||||
}).join('\n');
|
||||
}
|
||||
|
||||
const context = `
|
||||
Ticket: ${jiraKey}
|
||||
Title: ${title}
|
||||
Status: ${status}
|
||||
Assigned: ${assignedTo}
|
||||
Description: ${description}
|
||||
${commentsText}
|
||||
`.trim();
|
||||
|
||||
const summary = await grokService.generateTicketSummary(context);
|
||||
|
||||
res.json({
|
||||
jiraKey,
|
||||
assignedTo,
|
||||
status,
|
||||
summary
|
||||
});
|
||||
|
||||
} catch (error) {
|
||||
logger.error(`Ticket lookup failed for ${jiraKey}:`, error);
|
||||
res.status(200).json({
|
||||
jiraKey,
|
||||
assignedTo: "Error",
|
||||
status: "Lookup failed",
|
||||
summary: `Unable to fetch ticket details: ${error.message.substring(0, 150)}`
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
// ========================
|
||||
// Open Tickets by Reporter
|
||||
// ========================
|
||||
router.get('/wxccai/open-tickets-by-reporter', async (req, res) => {
|
||||
const email = req.query.email?.trim().toLowerCase();
|
||||
|
||||
if (!email || !email.includes('@')) {
|
||||
return res.status(400).json({
|
||||
error: "Invalid or missing email address",
|
||||
message: "Please provide a valid email address to search for open tickets"
|
||||
});
|
||||
}
|
||||
|
||||
try {
|
||||
const issues = await jiraService.searchOpenTicketsByReporterEmail(email);
|
||||
const ticketArray = await grokService.generateOpenTicketsSummary(issues);
|
||||
|
||||
res.status(200).json(ticketArray);
|
||||
|
||||
} catch (error) {
|
||||
logger.error(`Open tickets by reporter failed for ${email}:`, error);
|
||||
// Return safe empty response on runtime failures (Jira/Grok down, etc.)
|
||||
// to match the degrade-gracefully + 200 pattern used by /getticket and the
|
||||
// webhook endpoint. 400 is still used above for clear input validation errors.
|
||||
res.status(200).json([]);
|
||||
}
|
||||
});
|
||||
|
||||
// ========================
|
||||
// Store Support (SS) Ticket Creation
|
||||
// ========================
|
||||
// GET /api/wxccai/ssRequestTypes → list of exact subType strings you can use
|
||||
// POST /api/wxccai/createSSRequest with body (see comment in handler)
|
||||
router.get('/wxccai/ssRequestTypes', (req, res) => {
|
||||
try {
|
||||
const types = jiraService.getSupportedSSSubTypes ? jiraService.getSupportedSSSubTypes() : [];
|
||||
res.json({
|
||||
serviceDeskId: config.jira.serviceDeskId || '170',
|
||||
subTypes: types,
|
||||
note: "storeNumber padded to 5 digits. Uses api.atlassian.com workspace-scoped Assets AQL (https://api.atlassian.com/jsm/assets/workspace/{id}/v1/object/aql) because this is a service object.",
|
||||
assetsConfig: {
|
||||
objectTypeId: config.jira.assetsStoreObjectTypeId || '109',
|
||||
attribute: config.jira.assetsStoreNumberAttribute || 'Store Number',
|
||||
storeCustomFieldId: config.jira.storeCustomFieldId || 'customfield_10261',
|
||||
workspaceId: config.jira.assetsWorkspaceId || '(auto-discovered via /rest/servicedeskapi/assets/workspace)',
|
||||
note: 'Always uses https://api.atlassian.com/jsm/assets/workspace/.../v1/object/aql (required for service objects)',
|
||||
requiredEnv: [
|
||||
"JIRA_ASSETS_WORKSPACE_ID (recommended; auto-discovered if possible)",
|
||||
"JIRA_ASSETS_STORE_OBJECT_TYPE_ID (default 109)",
|
||||
"JIRA_STORE_CUSTOM_FIELD_ID (default customfield_10261)"
|
||||
]
|
||||
},
|
||||
example: {
|
||||
subType: types[0] || "Register Not functioning properly",
|
||||
onBehalfOf: "user@ae.com",
|
||||
summary: "Register 3 is frozen at login screen",
|
||||
description: "Detailed description (optional)",
|
||||
storeNumber: "00305", // looked up → [{ "objectId": "82288" }]
|
||||
additional: {
|
||||
customfield_10294: "2026-06-18T14:30:00.000+0000"
|
||||
}
|
||||
}
|
||||
});
|
||||
} catch (e) {
|
||||
res.status(500).json({ error: 'Failed to list types' });
|
||||
}
|
||||
});
|
||||
|
||||
// POST body example:
|
||||
// {
|
||||
// "subType": "Register Not functioning properly",
|
||||
// "onBehalfOf": "user@ae.com",
|
||||
// "summary": "Register 3 is frozen",
|
||||
// "description": "Customer reports...",
|
||||
// "storeNumber": "00305", // 5 digits; resolved via api.atlassian.com Assets endpoint to { "objectId": "..." }
|
||||
// "additional": {
|
||||
// "customfield_10294": "2026-06-18T14:30:00.000+0000",
|
||||
// "customfield_10557": "Transaction failed"
|
||||
// }
|
||||
// }
|
||||
// subType must match exactly one of the keys in the REQUEST_TYPE_MAP.
|
||||
router.post('/wxccai/createSSRequest', async (req, res) => {
|
||||
const body = req.body || {};
|
||||
const timestamp = new Date().toISOString();
|
||||
const ip = req.headers['x-forwarded-for'] || req.socket.remoteAddress;
|
||||
|
||||
logger.info('Create SS Request received', {
|
||||
timestamp,
|
||||
ip,
|
||||
subType: body.subType,
|
||||
storeNumber: body.storeNumber,
|
||||
hasOnBehalfOf: !!body.onBehalfOf
|
||||
});
|
||||
|
||||
if (!body.subType || !body.summary) {
|
||||
return res.status(400).json({
|
||||
success: false,
|
||||
error: 'subType and summary are required'
|
||||
});
|
||||
}
|
||||
|
||||
try {
|
||||
const result = await jiraService.createSSRequest(body);
|
||||
|
||||
logger.info('Create SS Request succeeded', {
|
||||
issueKey: result?.issueKey,
|
||||
subType: body.subType
|
||||
});
|
||||
|
||||
res.status(200).json({
|
||||
success: true,
|
||||
issueKey: result?.issueKey,
|
||||
issueId: result?.issueId,
|
||||
requestTypeId: result?.requestType?.id,
|
||||
raw: result // full response for debugging / future use
|
||||
});
|
||||
} catch (error) {
|
||||
logger.error('Create SS Request failed', {
|
||||
subType: body.subType,
|
||||
storeNumber: body.storeNumber,
|
||||
error: error.message,
|
||||
details: error.details || error.response?.data
|
||||
});
|
||||
|
||||
res.status(400).json({
|
||||
success: false,
|
||||
error: error.message,
|
||||
details: error.details || error.response?.data || null
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
// ========================
|
||||
// Ticket lifecycle: status / update / comment / transitions / close
|
||||
// These are the raw operations (no Grok). Use /wxccai/getticket for the
|
||||
// Grok-summarized variant.
|
||||
// ========================
|
||||
|
||||
const KEY_RE = /^[A-Z]+-\d+$/;
|
||||
|
||||
router.get('/wxccai/ticket/:key/status', async (req, res) => {
|
||||
const key = req.params.key?.trim().toUpperCase();
|
||||
if (!key || !KEY_RE.test(key)) {
|
||||
return res.status(400).json({ error: 'Invalid Jira key' });
|
||||
}
|
||||
try {
|
||||
const status = await jiraService.getTicketStatus(key);
|
||||
res.json(status);
|
||||
} catch (err) {
|
||||
logger.error('status route failed', { key, error: err.message, details: err.details });
|
||||
res.status(err.status && err.status < 500 ? err.status : 500).json({
|
||||
error: err.message, details: err.details || null
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
// Body: { summary?, description?, priority?, labels?, assigneeAccountId?, additional? }
|
||||
router.patch('/wxccai/ticket/:key', async (req, res) => {
|
||||
const key = req.params.key?.trim().toUpperCase();
|
||||
if (!key || !KEY_RE.test(key)) {
|
||||
return res.status(400).json({ error: 'Invalid Jira key' });
|
||||
}
|
||||
try {
|
||||
const result = await jiraService.updateTicket(key, req.body || {});
|
||||
res.json({ success: true, ...result });
|
||||
} catch (err) {
|
||||
logger.error('update route failed', { key, error: err.message, details: err.details });
|
||||
res.status(err.status && err.status < 500 ? err.status : 500).json({
|
||||
success: false, error: err.message, details: err.details || null
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
// Body: { text: "...", internal?: boolean }
|
||||
router.post('/wxccai/ticket/:key/comment', async (req, res) => {
|
||||
const key = req.params.key?.trim().toUpperCase();
|
||||
if (!key || !KEY_RE.test(key)) {
|
||||
return res.status(400).json({ error: 'Invalid Jira key' });
|
||||
}
|
||||
const { text, internal } = req.body || {};
|
||||
try {
|
||||
const result = await jiraService.addComment(key, text, { internal: !!internal });
|
||||
res.json({ success: true, ...result });
|
||||
} catch (err) {
|
||||
logger.error('comment route failed', { key, error: err.message, details: err.details });
|
||||
res.status(err.status && err.status < 500 ? err.status : 500).json({
|
||||
success: false, error: err.message, details: err.details || null
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
router.get('/wxccai/ticket/:key/transitions', async (req, res) => {
|
||||
const key = req.params.key?.trim().toUpperCase();
|
||||
if (!key || !KEY_RE.test(key)) {
|
||||
return res.status(400).json({ error: 'Invalid Jira key' });
|
||||
}
|
||||
try {
|
||||
const transitions = await jiraService.getTransitions(key);
|
||||
res.json({ key, transitions });
|
||||
} catch (err) {
|
||||
logger.error('transitions route failed', { key, error: err.message });
|
||||
res.status(500).json({ error: err.message });
|
||||
}
|
||||
});
|
||||
|
||||
// Body: { transitionName?, resolution?, comment?, internal? }
|
||||
// If transitionName is omitted, we auto-pick the first "done" transition.
|
||||
router.post('/wxccai/ticket/:key/close', async (req, res) => {
|
||||
const key = req.params.key?.trim().toUpperCase();
|
||||
if (!key || !KEY_RE.test(key)) {
|
||||
return res.status(400).json({ error: 'Invalid Jira key' });
|
||||
}
|
||||
const { transitionName, resolution, comment, internal } = req.body || {};
|
||||
try {
|
||||
const result = await jiraService.closeTicket(key, {
|
||||
transitionName,
|
||||
resolution: resolution || 'Done',
|
||||
comment,
|
||||
internal: !!internal
|
||||
});
|
||||
res.json({ success: true, ...result });
|
||||
} catch (err) {
|
||||
logger.error('close route failed', { key, error: err.message, details: err.details });
|
||||
res.status(err.status && err.status < 500 ? err.status : 500).json({
|
||||
success: false, error: err.message, details: err.details || null
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
// ========================
|
||||
// Assets diagnostic (non-production only)
|
||||
// GET /api/wxccai/debug/assetsProbe?storeNumber=305
|
||||
// Runs several AQL variants against the Assets workspace so we can see which
|
||||
// query shape actually matches (attribute name vs Name field, padded vs
|
||||
// unpadded, etc.) and dumps the real attribute names + values from any
|
||||
// returned objects. This is intentionally verbose; do not expose in prod.
|
||||
// ========================
|
||||
router.get('/wxccai/debug/assetsProbe', async (req, res) => {
|
||||
if ((config.nodeEnv || '').toLowerCase() === 'production') {
|
||||
return res.status(404).json({ error: 'Not found' });
|
||||
}
|
||||
|
||||
const storeNumber = (req.query.storeNumber ?? '').toString().trim();
|
||||
|
||||
try {
|
||||
const result = await jiraService.probeAssetsForStore(storeNumber);
|
||||
res.json(result);
|
||||
} catch (err) {
|
||||
logger.error('Assets probe failed', { storeNumber, error: err.message });
|
||||
res.status(500).json({ error: err.message });
|
||||
}
|
||||
});
|
||||
|
||||
// ========================
|
||||
// Webex → Jira Transcript + Audio + Summary Comment
|
||||
// ========================
|
||||
router.post('/wxccai/issueTranscript/:jiraKey', async (req, res) => {
|
||||
const jiraKey = req.params.jiraKey?.trim().toUpperCase();
|
||||
const timestamp = new Date().toISOString();
|
||||
const ip = req.headers['x-forwarded-for'] || req.socket.remoteAddress;
|
||||
|
||||
let payload = req.body || {};
|
||||
|
||||
// If we hit the raw() parser for this webhook (because of potential bad JSON
|
||||
// from transcription text), sanitize control characters and parse ourselves.
|
||||
if (Buffer.isBuffer(req.body)) {
|
||||
const rawBodyStr = req.body.toString('utf8');
|
||||
try {
|
||||
let rawStr = rawBodyStr;
|
||||
|
||||
// Trim any leading control characters that would make the top-level JSON invalid.
|
||||
rawStr = rawStr.replace(/^[\u0000-\u001F]+/, '');
|
||||
|
||||
// Only escape control characters that appear *inside* JSON string values.
|
||||
// Global replacement would turn structural newlines/tabs (the pretty-printing
|
||||
// in the JSON itself) into literal "\n" text, breaking the top-level structure
|
||||
// (which is exactly what caused "Expected property name or '}' at position 1").
|
||||
//
|
||||
// This regex finds "..." strings (handling escaped inner quotes) and cleans
|
||||
// only the content inside them. Structural whitespace stays intact so JSON.parse
|
||||
// still sees valid tokens.
|
||||
rawStr = rawStr.replace(/"((?:[^"\\]|\\.)*)"/g, (fullMatch, content) => {
|
||||
const cleaned = content.replace(/[\u0000-\u001F]/g, (ch) => {
|
||||
switch (ch) {
|
||||
case '\n': return '\\n';
|
||||
case '\r': return '\\r';
|
||||
case '\t': return '\\t';
|
||||
case '\b': return '\\b';
|
||||
case '\f': return '\\f';
|
||||
default:
|
||||
return '\\u' + ch.charCodeAt(0).toString(16).padStart(4, '0');
|
||||
}
|
||||
});
|
||||
return '"' + cleaned + '"';
|
||||
});
|
||||
|
||||
payload = JSON.parse(rawStr);
|
||||
} catch (parseErr) {
|
||||
// Make replay easy: a one-liner using base64 so special chars/newlines in the payload don't break the log line
|
||||
const base64 = req.body.toString('base64');
|
||||
const replayCmd = `echo '${base64}' | base64 -d | curl -X POST -H 'Content-Type: application/json' --data-binary @- 'http://localhost:1866${req.originalUrl || req.url}'`;
|
||||
|
||||
logger.error('Failed to parse transcript webhook payload even after sanitization', {
|
||||
jiraKey,
|
||||
error: parseErr.message,
|
||||
rawBody: rawBodyStr,
|
||||
rawBodyBase64: base64,
|
||||
replayCurl: replayCmd,
|
||||
// The body after our trimming + escaping attempt (truncated for log size if huge)
|
||||
sanitizedAttempt: rawBodyStr.substring(0, 2000)
|
||||
});
|
||||
|
||||
// Last-resort fallback: extract fields with regex so we can still attempt
|
||||
// the attachments and summary comment even if the overall JSON is badly broken
|
||||
// (common when transcription text contains unescaped quotes, newlines, etc.)
|
||||
try {
|
||||
const get = (name) => {
|
||||
// Simple string value extractor that tolerates some bad escaping inside the value
|
||||
const re = new RegExp(`"${name}"\\s*:\\s*"((?:[^"\\\\]|\\\\.)*)"`, 'i');
|
||||
const m = rawBodyStr.match(re);
|
||||
if (!m) return '';
|
||||
return m[1]
|
||||
.replace(/\\"/g, '"')
|
||||
.replace(/\\n/g, '\n')
|
||||
.replace(/\\r/g, '\r')
|
||||
.replace(/\\t/g, '\t')
|
||||
.replace(/\\\\/g, '\\');
|
||||
};
|
||||
|
||||
payload = {
|
||||
orgId: get('orgId'),
|
||||
taskId: get('taskId'),
|
||||
audioFileName: get('audioFileName'),
|
||||
audioFilePath: get('audioFilePath'),
|
||||
textFileName: get('textFileName'),
|
||||
textFilePath: get('textFilePath'),
|
||||
Summaries: {
|
||||
intialContactReason: get('intialContactReason'),
|
||||
additionalContext: get('additionalContext'),
|
||||
keyActionsTake: get('keyActionsTake'),
|
||||
nextSteps: get('nextSteps'),
|
||||
resolution: get('resolution')
|
||||
}
|
||||
};
|
||||
logger.warn('Used regex fallback extraction for malformed payload', { jiraKey });
|
||||
} catch (fallbackErr) {
|
||||
logger.error('Fallback extraction also failed', { jiraKey, error: fallbackErr.message });
|
||||
return res.status(400).json({ status: "error", message: "Invalid JSON payload from upstream" });
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Convenience unwrap for Postman / local testing.
|
||||
// When users copy the "payload" value (or the whole meta object) from our previous
|
||||
// WEBEX_TRANSCRIPT or error logs into Postman, the body often arrives wrapped as
|
||||
// { jiraKey, ip, payload: <real flat data>, level }.
|
||||
// Auto-unwrap so the rest of the handler sees the real fields (audioFilePath, Summaries etc.)
|
||||
// and actually performs the attachments + comment.
|
||||
if (payload && typeof payload === 'object' && payload.payload &&
|
||||
(payload.payload.orgId || payload.payload.audioFilePath || payload.payload.Summaries)) {
|
||||
logger.info('Unwrapped log meta / copied-from-log payload for testing', { jiraKey });
|
||||
payload = payload.payload;
|
||||
}
|
||||
|
||||
logger.info('Webex Transcript Webhook Received', {
|
||||
timestamp, jiraKey, ip, payloadKeys: Object.keys(payload)
|
||||
});
|
||||
|
||||
webexLogger.info('WEBEX_TRANSCRIPT', { timestamp, jiraKey, ip, payload });
|
||||
|
||||
if (!jiraKey || !/^[A-Z0-9]+-\d+$/.test(jiraKey)) {
|
||||
return res.status(400).json({ status: "error", message: "Invalid Jira key" });
|
||||
}
|
||||
|
||||
const results = {
|
||||
attachments: [],
|
||||
commentPosted: false,
|
||||
errors: []
|
||||
};
|
||||
|
||||
// Process each step independently so one failure (e.g. 401 scope) doesn't prevent others.
|
||||
// Always return 200 to the caller; use "partial_error" status if anything failed.
|
||||
// This prevents the webhook from "crashing" the processing for non-fatal issues.
|
||||
|
||||
// 1. Attach Audio File (if present)
|
||||
if (payload.audioFilePath) {
|
||||
try {
|
||||
await jiraService.attachFileToJira(jiraKey, payload.audioFilePath, payload.audioFileName || `audio-${jiraKey}.wav`);
|
||||
results.attachments.push('audio');
|
||||
} catch (error) {
|
||||
logger.error('Audio file attach failed', {
|
||||
jiraKey,
|
||||
error: error.response?.data?.message || error.message,
|
||||
status: error.response?.status,
|
||||
responseData: error.response?.data
|
||||
});
|
||||
results.errors.push({
|
||||
step: 'audio',
|
||||
error: error.response?.data?.message || error.message,
|
||||
status: error.response?.status,
|
||||
details: error.response?.data
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// 2. Attach Text Transcript (only if it actually exists)
|
||||
if (payload.textFilePath && payload.textFilePath.trim() !== '') {
|
||||
try {
|
||||
await jiraService.attachFileToJira(jiraKey, payload.textFilePath, payload.textFileName || `transcript-${jiraKey}.json`);
|
||||
results.attachments.push('transcript');
|
||||
|
||||
// Convert the JSON transcript to human-readable text and upload as well
|
||||
const readableAttached = await jiraService.attachReadableTranscript(jiraKey, payload.textFilePath, payload.textFileName);
|
||||
if (readableAttached) {
|
||||
results.attachments.push('transcript-readable');
|
||||
}
|
||||
} catch (error) {
|
||||
logger.error('Text transcript attach failed', {
|
||||
jiraKey,
|
||||
error: error.response?.data?.message || error.message,
|
||||
status: error.response?.status,
|
||||
responseData: error.response?.data
|
||||
});
|
||||
results.errors.push({
|
||||
step: 'text',
|
||||
error: error.response?.data?.message || error.message,
|
||||
status: error.response?.status,
|
||||
details: error.response?.data
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// 3. Post Summaries as a clean Jira comment (restricted/internal via role)
|
||||
if (payload.Summaries && Object.keys(payload.Summaries).length > 0) {
|
||||
try {
|
||||
// Map internal attachment keys to friendly names for the comment body
|
||||
const friendly = results.attachments.map(a => {
|
||||
if (a === 'audio') return 'audio recording';
|
||||
if (a === 'transcript') return 'JSON transcript';
|
||||
if (a === 'transcript-readable') return 'human-readable transcript';
|
||||
return a;
|
||||
});
|
||||
await jiraService.postWebexSummaryComment(jiraKey, payload.Summaries, friendly);
|
||||
results.commentPosted = true;
|
||||
} catch (error) {
|
||||
logger.error('Summary comment post failed', {
|
||||
jiraKey,
|
||||
error: error.response?.data?.message || error.message,
|
||||
status: error.response?.status,
|
||||
responseData: error.response?.data
|
||||
});
|
||||
results.errors.push({
|
||||
step: 'comment',
|
||||
error: error.response?.data?.message || error.message,
|
||||
status: error.response?.status,
|
||||
details: error.response?.data
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
const overallStatus = results.errors.length > 0 ? 'partial_error' : 'success';
|
||||
const message = overallStatus === 'success' ? 'Processing completed' : 'Some operations failed (see errors)';
|
||||
|
||||
logger.info('Webex webhook processed', { jiraKey, results, overallStatus });
|
||||
|
||||
res.status(200).json({
|
||||
status: overallStatus,
|
||||
message,
|
||||
jiraKey,
|
||||
attachments: results.attachments,
|
||||
commentPosted: results.commentPosted,
|
||||
errors: results.errors.length ? results.errors : undefined
|
||||
});
|
||||
});
|
||||
|
||||
export default router;
|
||||
135
src/services/grokService.js
Normal file
135
src/services/grokService.js
Normal file
|
|
@ -0,0 +1,135 @@
|
|||
import fs from 'fs/promises';
|
||||
import path from 'path';
|
||||
import { fileURLToPath } from 'url';
|
||||
import OpenAI from 'openai';
|
||||
import config from '../config/index.js';
|
||||
import logger from '../utilities/logger.js';
|
||||
|
||||
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
||||
const PROMPTS_DIR = path.join(__dirname, '../prompts');
|
||||
|
||||
const client = new OpenAI({
|
||||
apiKey: config.xai.apiKey,
|
||||
baseURL: config.xai.baseUrl,
|
||||
});
|
||||
|
||||
// Helper to load prompt from file
|
||||
async function loadPrompt(templateName, variables = {}) {
|
||||
const filePath = path.join(PROMPTS_DIR, `${templateName}.txt`);
|
||||
let prompt = await fs.readFile(filePath, 'utf8');
|
||||
|
||||
Object.keys(variables).forEach(key => {
|
||||
const placeholder = new RegExp(`{{${key}}}`, 'g');
|
||||
prompt = prompt.replace(placeholder, variables[key]);
|
||||
});
|
||||
|
||||
return prompt;
|
||||
}
|
||||
|
||||
// Single ticket summary (your original /getticket endpoint)
|
||||
export async function generateTicketSummary(context) {
|
||||
try {
|
||||
const prompt = await loadPrompt('singleTicketSummary', { context });
|
||||
|
||||
const completion = await client.chat.completions.create({
|
||||
model: "grok-4.20-non-reasoning",
|
||||
messages: [{ role: "user", content: prompt }],
|
||||
temperature: 0.2,
|
||||
max_tokens: 400,
|
||||
});
|
||||
|
||||
return completion.choices[0]?.message?.content?.trim() || "Summary unavailable at this time.";
|
||||
} catch (err) {
|
||||
logger.error(`Grok single ticket summary failed: ${err.message}`);
|
||||
return "AI summary is temporarily unavailable. Please review the ticket directly in Jira.";
|
||||
}
|
||||
}
|
||||
|
||||
// Multi-ticket summary (your /open-tickets-by-reporter endpoint)
|
||||
export async function generateOpenTicketsSummary(tickets) {
|
||||
if (!tickets || tickets.length === 0) {
|
||||
return [];
|
||||
}
|
||||
|
||||
let context = `Open tickets reported by the customer:\n`;
|
||||
|
||||
tickets.forEach(ticket => {
|
||||
const fields = ticket.fields || {};
|
||||
const key = ticket.key;
|
||||
const status = fields.status?.name || "Unknown";
|
||||
const updated = fields.updated || null;
|
||||
const title = fields.summary || "No title";
|
||||
|
||||
const notes = ticket.enrichedNotes || {};
|
||||
const description = notes.description || "No description available.";
|
||||
|
||||
let commentsText = "";
|
||||
if (Array.isArray(notes.publicComments) && notes.publicComments.length > 0) {
|
||||
const recent = notes.publicComments.slice(-8).reverse();
|
||||
commentsText = "\nRecent public comments (most recent first):\n" + recent.map(c => {
|
||||
const date = c.createdIso ? c.createdIso.slice(0, 10) : "unknown";
|
||||
const body = c.body ? c.body.substring(0, 300) : "";
|
||||
return `[${date}] ${body}${body.length === 300 ? '...' : ''}`;
|
||||
}).join("\n");
|
||||
}
|
||||
|
||||
context += `\nTicket: ${key}
|
||||
Status: ${status}
|
||||
Last updated: ${updated || "unknown"}
|
||||
Title: ${title}
|
||||
Initial Problem / Description: ${description}
|
||||
${commentsText}
|
||||
`;
|
||||
});
|
||||
|
||||
try {
|
||||
const prompt = await loadPrompt('openTicketsSummary', { context });
|
||||
|
||||
const completion = await client.chat.completions.create({
|
||||
model: "grok-4.20-non-reasoning",
|
||||
messages: [{ role: "user", content: prompt }],
|
||||
temperature: 0.3,
|
||||
max_tokens: 900,
|
||||
});
|
||||
|
||||
const rawOutput = completion.choices[0]?.message?.content?.trim();
|
||||
|
||||
if (!rawOutput) {
|
||||
return tickets.map(t => createFallbackTicket(t));
|
||||
}
|
||||
|
||||
let parsed;
|
||||
try {
|
||||
parsed = JSON.parse(rawOutput);
|
||||
} catch (e) {
|
||||
const jsonMatch = rawOutput.match(/\[[\s\S]*\]/);
|
||||
if (jsonMatch) {
|
||||
parsed = JSON.parse(jsonMatch[0]);
|
||||
} else {
|
||||
parsed = null;
|
||||
}
|
||||
}
|
||||
|
||||
return Array.isArray(parsed) ? parsed : tickets.map(t => createFallbackTicket(t));
|
||||
|
||||
} catch (err) {
|
||||
logger.error(`Grok multi-ticket summary failed: ${err.message}`);
|
||||
return tickets.map(t => createFallbackTicket(t));
|
||||
}
|
||||
}
|
||||
|
||||
// Fallback helper
|
||||
function createFallbackTicket(ticket) {
|
||||
const fields = ticket.fields || {};
|
||||
return {
|
||||
jiraKey: ticket.key,
|
||||
currentStatus: fields.status?.name || "Unknown",
|
||||
summary: `${fields.status?.name || "Unknown"} - ${fields.summary || "No details available. Check public notes for latest updates."}`,
|
||||
lastUpdated: fields.updated || null
|
||||
};
|
||||
}
|
||||
|
||||
export default {
|
||||
generateTicketSummary,
|
||||
generateOpenTicketsSummary
|
||||
};
|
||||
106
src/services/healthService.js
Normal file
106
src/services/healthService.js
Normal file
|
|
@ -0,0 +1,106 @@
|
|||
import axios from 'axios';
|
||||
import { jiraClient } from './jiraService.js';
|
||||
import config from '../config/index.js';
|
||||
import logger from '../utilities/logger.js';
|
||||
|
||||
export async function getDetailedHealth() {
|
||||
const startTime = Date.now();
|
||||
const health = {
|
||||
status: 'healthy',
|
||||
uptime: process.uptime(),
|
||||
timestamp: new Date().toISOString(),
|
||||
environment: config.nodeEnv || 'development',
|
||||
version: process.env.npm_package_version || '1.0.0',
|
||||
checks: {}
|
||||
};
|
||||
|
||||
// Self check
|
||||
health.checks.self = {
|
||||
status: 'healthy',
|
||||
responseTimeMs: Date.now() - startTime
|
||||
};
|
||||
|
||||
// Jira Check (critical) - uses the shared jiraClient so it automatically
|
||||
// gets the correct base URL (including api.atlassian.com/ex/jira/{cloudId} form)
|
||||
// and the configured auth (basic kept).
|
||||
try {
|
||||
const jiraStart = Date.now();
|
||||
await jiraClient.get('/rest/api/3/myself', { timeout: 6000 });
|
||||
|
||||
health.checks.jira = {
|
||||
status: 'healthy',
|
||||
responseTimeMs: Date.now() - jiraStart,
|
||||
message: 'Jira authentication successful'
|
||||
};
|
||||
} catch (err) {
|
||||
const status = err.response?.status || 'unknown';
|
||||
health.checks.jira = {
|
||||
status: (status === 401 || status === 403) ? 'unhealthy' : 'degraded',
|
||||
responseTimeMs: Date.now() - startTime,
|
||||
message: status === 401 ? 'Jira authentication failed (check credentials)' : `Jira error (${status})`
|
||||
};
|
||||
if (health.status === 'healthy') health.status = 'degraded';
|
||||
}
|
||||
|
||||
// xAI Check - Tolerant + tries both common endpoints
|
||||
let xaiSuccess = false;
|
||||
const xaiStart = Date.now();
|
||||
|
||||
// Try 1: Legacy chat/completions (still widely used)
|
||||
try {
|
||||
await axios.post(`${config.xai.baseUrl}/v1/chat/completions`, {
|
||||
model: "grok-4.20-non-reasoning",
|
||||
messages: [{ role: 'user', content: 'Say \'ok\'.' }],
|
||||
max_tokens: 5,
|
||||
temperature: 0
|
||||
}, {
|
||||
headers: {
|
||||
Authorization: `Bearer ${config.xai.apiKey}`,
|
||||
'Content-Type': 'application/json'
|
||||
},
|
||||
timeout: 7000
|
||||
});
|
||||
xaiSuccess = true;
|
||||
} catch (_) {}
|
||||
|
||||
// Try 2: New Responses API (if legacy fails)
|
||||
if (!xaiSuccess) {
|
||||
try {
|
||||
await axios.post(`${config.xai.baseUrl}/v1/responses`, {
|
||||
model: "grok-4.20-non-reasoning",
|
||||
input: [{ role: 'user', content: 'Say \'ok\'.' }],
|
||||
max_tokens: 5,
|
||||
temperature: 0
|
||||
}, {
|
||||
headers: {
|
||||
Authorization: `Bearer ${config.xai.apiKey}`,
|
||||
'Content-Type': 'application/json'
|
||||
},
|
||||
timeout: 7000
|
||||
});
|
||||
xaiSuccess = true;
|
||||
} catch (_) {}
|
||||
}
|
||||
|
||||
if (xaiSuccess) {
|
||||
health.checks.xai = {
|
||||
status: 'healthy',
|
||||
responseTimeMs: Date.now() - xaiStart,
|
||||
message: 'xAI API reachable'
|
||||
};
|
||||
} else {
|
||||
health.checks.xai = {
|
||||
status: 'degraded',
|
||||
responseTimeMs: Date.now() - xaiStart,
|
||||
message: 'xAI API test failed (summaries still working)'
|
||||
};
|
||||
// Do NOT degrade overall status for xAI issues
|
||||
}
|
||||
|
||||
// Overall status only fails if Jira is unhealthy
|
||||
if (health.checks.jira?.status === 'unhealthy') {
|
||||
health.status = 'unhealthy';
|
||||
}
|
||||
|
||||
return health;
|
||||
}
|
||||
1425
src/services/jiraService.js
Normal file
1425
src/services/jiraService.js
Normal file
File diff suppressed because it is too large
Load diff
30
src/utilities/adfToPlainText.js
Normal file
30
src/utilities/adfToPlainText.js
Normal file
|
|
@ -0,0 +1,30 @@
|
|||
export function adfToPlainText(node) {
|
||||
if (!node || typeof node !== 'object') return '';
|
||||
let text = '';
|
||||
|
||||
if (node.type === 'text' && node.text) {
|
||||
text += node.text;
|
||||
if (node.marks) {
|
||||
node.marks.forEach(mark => {
|
||||
if (mark.type === 'link' && mark.attrs?.href) {
|
||||
text += ` (${mark.attrs.href})`;
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
if (node.content && Array.isArray(node.content)) {
|
||||
node.content.forEach(child => {
|
||||
const childText = adfToPlainText(child);
|
||||
if (childText) {
|
||||
text += childText;
|
||||
if (['paragraph', 'heading', 'bulletList', 'orderedList', 'listItem'].includes(child.type)) {
|
||||
text += '\n\n';
|
||||
} else {
|
||||
text += ' ';
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
return text.trim();
|
||||
}
|
||||
60
src/utilities/logger.js
Normal file
60
src/utilities/logger.js
Normal file
|
|
@ -0,0 +1,60 @@
|
|||
import winston from 'winston';
|
||||
import path from 'path';
|
||||
import { fileURLToPath } from 'url';
|
||||
|
||||
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
||||
const LOG_DIR = path.join(__dirname, '../../logs');
|
||||
|
||||
// Ensure logs directory exists
|
||||
import fs from 'fs';
|
||||
if (!fs.existsSync(LOG_DIR)) {
|
||||
fs.mkdirSync(LOG_DIR, { recursive: true });
|
||||
}
|
||||
|
||||
const logger = winston.createLogger({
|
||||
level: process.env.NODE_ENV === 'production' ? 'info' : 'debug',
|
||||
format: winston.format.combine(
|
||||
winston.format.timestamp(),
|
||||
winston.format.json()
|
||||
),
|
||||
transports: [
|
||||
// Console (for development)
|
||||
new winston.transports.Console({
|
||||
format: winston.format.simple()
|
||||
}),
|
||||
// Main application log file
|
||||
new winston.transports.File({
|
||||
filename: path.join(LOG_DIR, 'app.log'),
|
||||
maxsize: 10 * 1024 * 1024, // 10MB
|
||||
maxFiles: 5,
|
||||
tailable: true
|
||||
})
|
||||
]
|
||||
});
|
||||
|
||||
// Dedicated transport for Webex callbacks (human-readable + JSON)
|
||||
// IMPORTANT: This now also goes to console (stdout) so that `docker logs` and
|
||||
// container platforms can see the WEBEX_TRANSCRIPT events without needing
|
||||
// to inspect files inside the container.
|
||||
const webexLogger = winston.createLogger({
|
||||
level: 'info',
|
||||
format: winston.format.combine(
|
||||
winston.format.timestamp(),
|
||||
winston.format.printf(({ timestamp, message, ...meta }) => {
|
||||
return `[${timestamp}] ${message} | ${JSON.stringify(meta)}`;
|
||||
})
|
||||
),
|
||||
transports: [
|
||||
// Console so webhook activity is visible in docker logs / platform logs
|
||||
new winston.transports.Console(),
|
||||
new winston.transports.File({
|
||||
filename: path.join(LOG_DIR, 'webex-callbacks.log'),
|
||||
maxsize: 20 * 1024 * 1024, // 20MB
|
||||
maxFiles: 10,
|
||||
tailable: true
|
||||
})
|
||||
]
|
||||
});
|
||||
|
||||
export { logger, webexLogger };
|
||||
export default logger;
|
||||
89
ss-fields-266.json
Normal file
89
ss-fields-266.json
Normal file
|
|
@ -0,0 +1,89 @@
|
|||
{
|
||||
"requestTypeFields": [
|
||||
{
|
||||
"fieldId": "summary",
|
||||
"name": "Summary",
|
||||
"description": "Type in short description of issue.",
|
||||
"required": true,
|
||||
"defaultValues": [],
|
||||
"validValues": [],
|
||||
"jiraSchema": {
|
||||
"type": "string",
|
||||
"system": "summary"
|
||||
},
|
||||
"visible": true
|
||||
},
|
||||
{
|
||||
"fieldId": "customfield_10261",
|
||||
"name": "Store Number",
|
||||
"description": "Type in your store number and click on it to select.",
|
||||
"required": true,
|
||||
"defaultValues": [],
|
||||
"validValues": [],
|
||||
"jiraSchema": {
|
||||
"type": "array",
|
||||
"items": "cmdb-object-field",
|
||||
"custom": "com.atlassian.jira.plugins.cmdb:cmdb-object-cftype",
|
||||
"customId": 10261
|
||||
},
|
||||
"visible": true
|
||||
},
|
||||
{
|
||||
"fieldId": "description",
|
||||
"name": "Description",
|
||||
"description": "Please provide as much detail as possible (ie: What is broken with the device? Is it a specific cord that needs to be replaced? Include all relevant information. )",
|
||||
"required": true,
|
||||
"defaultValues": [],
|
||||
"validValues": [],
|
||||
"jiraSchema": {
|
||||
"type": "string",
|
||||
"system": "description"
|
||||
},
|
||||
"visible": true
|
||||
},
|
||||
{
|
||||
"fieldId": "customfield_10544",
|
||||
"name": "Affected Device",
|
||||
"description": "",
|
||||
"required": false,
|
||||
"defaultValues": [],
|
||||
"validValues": [],
|
||||
"jiraSchema": {
|
||||
"type": "string",
|
||||
"custom": "com.atlassian.jira.plugin.system.customfieldtypes:textfield",
|
||||
"customId": 10544
|
||||
},
|
||||
"visible": true
|
||||
},
|
||||
{
|
||||
"fieldId": "customfield_10294",
|
||||
"name": "Date/Time Issue Occured",
|
||||
"description": "",
|
||||
"required": false,
|
||||
"defaultValues": [],
|
||||
"validValues": [],
|
||||
"jiraSchema": {
|
||||
"type": "datetime",
|
||||
"custom": "com.atlassian.jira.plugin.system.customfieldtypes:datetime",
|
||||
"customId": 10294
|
||||
},
|
||||
"visible": true
|
||||
},
|
||||
{
|
||||
"fieldId": "attachment",
|
||||
"name": "Attachment",
|
||||
"description": "",
|
||||
"required": false,
|
||||
"defaultValues": [],
|
||||
"validValues": [],
|
||||
"jiraSchema": {
|
||||
"type": "array",
|
||||
"items": "attachment",
|
||||
"system": "attachment"
|
||||
},
|
||||
"visible": true
|
||||
}
|
||||
],
|
||||
"canRaiseOnBehalfOf": true,
|
||||
"canAddRequestParticipants": true
|
||||
}
|
||||
115
ss-fields-267.json
Normal file
115
ss-fields-267.json
Normal file
|
|
@ -0,0 +1,115 @@
|
|||
{
|
||||
"requestTypeFields": [
|
||||
{
|
||||
"fieldId": "summary",
|
||||
"name": "Summary",
|
||||
"description": "Insert Short Description of Issue",
|
||||
"required": true,
|
||||
"defaultValues": [],
|
||||
"validValues": [],
|
||||
"jiraSchema": {
|
||||
"type": "string",
|
||||
"system": "summary"
|
||||
},
|
||||
"visible": true
|
||||
},
|
||||
{
|
||||
"fieldId": "customfield_10261",
|
||||
"name": "Store Number",
|
||||
"description": "Type in your store number and click on it to select.",
|
||||
"required": true,
|
||||
"defaultValues": [],
|
||||
"validValues": [],
|
||||
"jiraSchema": {
|
||||
"type": "array",
|
||||
"items": "cmdb-object-field",
|
||||
"custom": "com.atlassian.jira.plugins.cmdb:cmdb-object-cftype",
|
||||
"customId": 10261
|
||||
},
|
||||
"visible": true
|
||||
},
|
||||
{
|
||||
"fieldId": "customfield_10381",
|
||||
"name": "Report System",
|
||||
"description": "Click on the drop down and select how this report is accessed.",
|
||||
"required": false,
|
||||
"defaultValues": [],
|
||||
"validValues": [
|
||||
{
|
||||
"value": "11381",
|
||||
"label": "Cognos",
|
||||
"children": []
|
||||
},
|
||||
{
|
||||
"value": "11382",
|
||||
"label": "Metric Insight",
|
||||
"children": []
|
||||
},
|
||||
{
|
||||
"value": "11383",
|
||||
"label": "Tableau",
|
||||
"children": []
|
||||
},
|
||||
{
|
||||
"value": "11384",
|
||||
"label": "Stores Productivity Report",
|
||||
"children": []
|
||||
},
|
||||
{
|
||||
"value": "11385",
|
||||
"label": "Insights to Action (ITA)",
|
||||
"children": []
|
||||
}
|
||||
],
|
||||
"jiraSchema": {
|
||||
"type": "option",
|
||||
"custom": "com.atlassian.jira.plugin.system.customfieldtypes:select",
|
||||
"customId": 10381
|
||||
},
|
||||
"visible": true
|
||||
},
|
||||
{
|
||||
"fieldId": "customfield_10480",
|
||||
"name": "Report Name",
|
||||
"description": "What is the name of the report?",
|
||||
"required": false,
|
||||
"defaultValues": [],
|
||||
"validValues": [],
|
||||
"jiraSchema": {
|
||||
"type": "string",
|
||||
"custom": "com.atlassian.jira.plugin.system.customfieldtypes:textfield",
|
||||
"customId": 10480
|
||||
},
|
||||
"visible": true
|
||||
},
|
||||
{
|
||||
"fieldId": "description",
|
||||
"name": "Description",
|
||||
"description": "Describe the issue you are having. The more info you provide will help us get you help faster!",
|
||||
"required": true,
|
||||
"defaultValues": [],
|
||||
"validValues": [],
|
||||
"jiraSchema": {
|
||||
"type": "string",
|
||||
"system": "description"
|
||||
},
|
||||
"visible": true
|
||||
},
|
||||
{
|
||||
"fieldId": "attachment",
|
||||
"name": "Attachment",
|
||||
"description": "",
|
||||
"required": false,
|
||||
"defaultValues": [],
|
||||
"validValues": [],
|
||||
"jiraSchema": {
|
||||
"type": "array",
|
||||
"items": "attachment",
|
||||
"system": "attachment"
|
||||
},
|
||||
"visible": true
|
||||
}
|
||||
],
|
||||
"canRaiseOnBehalfOf": true,
|
||||
"canAddRequestParticipants": true
|
||||
}
|
||||
202
ss-fields-268.json
Normal file
202
ss-fields-268.json
Normal file
|
|
@ -0,0 +1,202 @@
|
|||
{
|
||||
"requestTypeFields": [
|
||||
{
|
||||
"fieldId": "summary",
|
||||
"name": "Summary",
|
||||
"description": "Insert short description of issue.",
|
||||
"required": true,
|
||||
"defaultValues": [],
|
||||
"validValues": [],
|
||||
"jiraSchema": {
|
||||
"type": "string",
|
||||
"system": "summary"
|
||||
},
|
||||
"visible": true
|
||||
},
|
||||
{
|
||||
"fieldId": "customfield_10261",
|
||||
"name": "Store Number",
|
||||
"description": "Type in your store number and select",
|
||||
"required": true,
|
||||
"defaultValues": [],
|
||||
"validValues": [],
|
||||
"jiraSchema": {
|
||||
"type": "array",
|
||||
"items": "cmdb-object-field",
|
||||
"custom": "com.atlassian.jira.plugins.cmdb:cmdb-object-cftype",
|
||||
"customId": 10261
|
||||
},
|
||||
"visible": true
|
||||
},
|
||||
{
|
||||
"fieldId": "customfield_10548",
|
||||
"name": "Reason for Omni Request",
|
||||
"description": "Click the 1st drop down to select what needs enabled / disabled. Click the 2nd drop down to select reason code.",
|
||||
"required": true,
|
||||
"defaultValues": [],
|
||||
"validValues": [
|
||||
{
|
||||
"value": "13461",
|
||||
"label": "BOSS Enable",
|
||||
"children": [
|
||||
{
|
||||
"value": "13467",
|
||||
"label": "Printer Issues Resolved",
|
||||
"children": []
|
||||
},
|
||||
{
|
||||
"value": "13468",
|
||||
"label": "NSO/Maintenance Completed",
|
||||
"children": []
|
||||
},
|
||||
{
|
||||
"value": "13469",
|
||||
"label": "Other",
|
||||
"children": []
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"value": "13462",
|
||||
"label": "BOSS Disable",
|
||||
"children": [
|
||||
{
|
||||
"value": "13470",
|
||||
"label": "Printer Issues",
|
||||
"children": []
|
||||
},
|
||||
{
|
||||
"value": "13471",
|
||||
"label": "NSO/Maintenance",
|
||||
"children": []
|
||||
},
|
||||
{
|
||||
"value": "13472",
|
||||
"label": "Other",
|
||||
"children": []
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"value": "13463",
|
||||
"label": "Pickup Enable",
|
||||
"children": [
|
||||
{
|
||||
"value": "13473",
|
||||
"label": "Printer Issues Resolved",
|
||||
"children": []
|
||||
},
|
||||
{
|
||||
"value": "13474",
|
||||
"label": "NSO/Maintenance Completed",
|
||||
"children": []
|
||||
},
|
||||
{
|
||||
"value": "13475",
|
||||
"label": "Other",
|
||||
"children": []
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"value": "13464",
|
||||
"label": "Pickup Disable",
|
||||
"children": [
|
||||
{
|
||||
"value": "13476",
|
||||
"label": "Printer Issues",
|
||||
"children": []
|
||||
},
|
||||
{
|
||||
"value": "13477",
|
||||
"label": "NSO/Maintenance",
|
||||
"children": []
|
||||
},
|
||||
{
|
||||
"value": "13478",
|
||||
"label": "Other",
|
||||
"children": []
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"value": "13465",
|
||||
"label": "BOSS and Pickup Enable",
|
||||
"children": [
|
||||
{
|
||||
"value": "13479",
|
||||
"label": "Printer Issues Resolved",
|
||||
"children": []
|
||||
},
|
||||
{
|
||||
"value": "13480",
|
||||
"label": "NSO/Maintenance Completed",
|
||||
"children": []
|
||||
},
|
||||
{
|
||||
"value": "13481",
|
||||
"label": "Other",
|
||||
"children": []
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"value": "13466",
|
||||
"label": "BOSS and Pickup Disable",
|
||||
"children": [
|
||||
{
|
||||
"value": "13482",
|
||||
"label": "Printer Issues",
|
||||
"children": []
|
||||
},
|
||||
{
|
||||
"value": "13483",
|
||||
"label": "NSO/Maintenance",
|
||||
"children": []
|
||||
},
|
||||
{
|
||||
"value": "13484",
|
||||
"label": "Other",
|
||||
"children": []
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"jiraSchema": {
|
||||
"type": "option-with-child",
|
||||
"custom": "com.atlassian.jira.plugin.system.customfieldtypes:cascadingselect",
|
||||
"customId": 10548
|
||||
},
|
||||
"visible": true
|
||||
},
|
||||
{
|
||||
"fieldId": "description",
|
||||
"name": "Description",
|
||||
"description": "If you selected \"Other\" as the Reason Code above please provide detail here. Otherwise please briefly describe why you need Omni Turn Off / On Completed.",
|
||||
"required": true,
|
||||
"defaultValues": [],
|
||||
"validValues": [],
|
||||
"jiraSchema": {
|
||||
"type": "string",
|
||||
"system": "description"
|
||||
},
|
||||
"visible": true
|
||||
},
|
||||
{
|
||||
"fieldId": "attachment",
|
||||
"name": "Attachment",
|
||||
"description": "",
|
||||
"required": false,
|
||||
"defaultValues": [],
|
||||
"validValues": [],
|
||||
"jiraSchema": {
|
||||
"type": "array",
|
||||
"items": "attachment",
|
||||
"system": "attachment"
|
||||
},
|
||||
"visible": true
|
||||
}
|
||||
],
|
||||
"canRaiseOnBehalfOf": true,
|
||||
"canAddRequestParticipants": true
|
||||
}
|
||||
89
ss-fields-269.json
Normal file
89
ss-fields-269.json
Normal file
|
|
@ -0,0 +1,89 @@
|
|||
{
|
||||
"requestTypeFields": [
|
||||
{
|
||||
"fieldId": "summary",
|
||||
"name": "Summary",
|
||||
"description": "Insert Short Description of Issue",
|
||||
"required": true,
|
||||
"defaultValues": [],
|
||||
"validValues": [],
|
||||
"jiraSchema": {
|
||||
"type": "string",
|
||||
"system": "summary"
|
||||
},
|
||||
"visible": true
|
||||
},
|
||||
{
|
||||
"fieldId": "customfield_10261",
|
||||
"name": "Store Number",
|
||||
"description": "Type in your store number and click on it to select.",
|
||||
"required": true,
|
||||
"defaultValues": [],
|
||||
"validValues": [],
|
||||
"jiraSchema": {
|
||||
"type": "array",
|
||||
"items": "cmdb-object-field",
|
||||
"custom": "com.atlassian.jira.plugins.cmdb:cmdb-object-cftype",
|
||||
"customId": 10261
|
||||
},
|
||||
"visible": true
|
||||
},
|
||||
{
|
||||
"fieldId": "description",
|
||||
"name": "Description",
|
||||
"description": "What is the issue? Give examples of what is happening? What troubleshooting have you tried?",
|
||||
"required": true,
|
||||
"defaultValues": [],
|
||||
"validValues": [],
|
||||
"jiraSchema": {
|
||||
"type": "string",
|
||||
"system": "description"
|
||||
},
|
||||
"visible": true
|
||||
},
|
||||
{
|
||||
"fieldId": "customfield_10294",
|
||||
"name": "Date/Time Issue Occured",
|
||||
"description": "Date and Time of Incident.",
|
||||
"required": false,
|
||||
"defaultValues": [],
|
||||
"validValues": [],
|
||||
"jiraSchema": {
|
||||
"type": "datetime",
|
||||
"custom": "com.atlassian.jira.plugin.system.customfieldtypes:datetime",
|
||||
"customId": 10294
|
||||
},
|
||||
"visible": true
|
||||
},
|
||||
{
|
||||
"fieldId": "customfield_10557",
|
||||
"name": "Transaction Detail",
|
||||
"description": "Provide transaction details to ensure the correct information is reviewed.",
|
||||
"required": false,
|
||||
"defaultValues": [],
|
||||
"validValues": [],
|
||||
"jiraSchema": {
|
||||
"type": "string",
|
||||
"custom": "com.atlassian.jira.plugin.system.customfieldtypes:textfield",
|
||||
"customId": 10557
|
||||
},
|
||||
"visible": true
|
||||
},
|
||||
{
|
||||
"fieldId": "attachment",
|
||||
"name": "Attachment",
|
||||
"description": "",
|
||||
"required": false,
|
||||
"defaultValues": [],
|
||||
"validValues": [],
|
||||
"jiraSchema": {
|
||||
"type": "array",
|
||||
"items": "attachment",
|
||||
"system": "attachment"
|
||||
},
|
||||
"visible": true
|
||||
}
|
||||
],
|
||||
"canRaiseOnBehalfOf": true,
|
||||
"canAddRequestParticipants": true
|
||||
}
|
||||
105
ss-fields-270.json
Normal file
105
ss-fields-270.json
Normal file
|
|
@ -0,0 +1,105 @@
|
|||
{
|
||||
"requestTypeFields": [
|
||||
{
|
||||
"fieldId": "summary",
|
||||
"name": "Summary",
|
||||
"description": "Insert short description of issue.",
|
||||
"required": true,
|
||||
"defaultValues": [],
|
||||
"validValues": [],
|
||||
"jiraSchema": {
|
||||
"type": "string",
|
||||
"system": "summary"
|
||||
},
|
||||
"visible": true
|
||||
},
|
||||
{
|
||||
"fieldId": "customfield_10261",
|
||||
"name": "Store Number",
|
||||
"description": "Type in your store number and click on it to select.",
|
||||
"required": false,
|
||||
"defaultValues": [],
|
||||
"validValues": [],
|
||||
"jiraSchema": {
|
||||
"type": "array",
|
||||
"items": "cmdb-object-field",
|
||||
"custom": "com.atlassian.jira.plugins.cmdb:cmdb-object-cftype",
|
||||
"customId": 10261
|
||||
},
|
||||
"visible": true
|
||||
},
|
||||
{
|
||||
"fieldId": "description",
|
||||
"name": "Description",
|
||||
"description": "Describe the issue you are having. The more info you provide will help us get you help faster!",
|
||||
"required": true,
|
||||
"defaultValues": [],
|
||||
"validValues": [],
|
||||
"jiraSchema": {
|
||||
"type": "string",
|
||||
"system": "description"
|
||||
},
|
||||
"visible": true
|
||||
},
|
||||
{
|
||||
"fieldId": "customfield_10454",
|
||||
"name": "Preferred Contact Method",
|
||||
"description": "",
|
||||
"required": false,
|
||||
"defaultValues": [],
|
||||
"validValues": [
|
||||
{
|
||||
"value": "11325",
|
||||
"label": "Email",
|
||||
"children": []
|
||||
},
|
||||
{
|
||||
"value": "11326",
|
||||
"label": "Phone",
|
||||
"children": []
|
||||
},
|
||||
{
|
||||
"value": "11327",
|
||||
"label": "Webex",
|
||||
"children": []
|
||||
}
|
||||
],
|
||||
"jiraSchema": {
|
||||
"type": "option",
|
||||
"custom": "com.atlassian.jira.plugin.system.customfieldtypes:select",
|
||||
"customId": 10454
|
||||
},
|
||||
"visible": true
|
||||
},
|
||||
{
|
||||
"fieldId": "customfield_10426",
|
||||
"name": "Phone",
|
||||
"description": "",
|
||||
"required": false,
|
||||
"defaultValues": [],
|
||||
"validValues": [],
|
||||
"jiraSchema": {
|
||||
"type": "string",
|
||||
"custom": "com.atlassian.jira.plugin.system.customfieldtypes:textfield",
|
||||
"customId": 10426
|
||||
},
|
||||
"visible": true
|
||||
},
|
||||
{
|
||||
"fieldId": "attachment",
|
||||
"name": "Attachment",
|
||||
"description": "",
|
||||
"required": false,
|
||||
"defaultValues": [],
|
||||
"validValues": [],
|
||||
"jiraSchema": {
|
||||
"type": "array",
|
||||
"items": "attachment",
|
||||
"system": "attachment"
|
||||
},
|
||||
"visible": true
|
||||
}
|
||||
],
|
||||
"canRaiseOnBehalfOf": true,
|
||||
"canAddRequestParticipants": true
|
||||
}
|
||||
89
ss-fields-271.json
Normal file
89
ss-fields-271.json
Normal file
|
|
@ -0,0 +1,89 @@
|
|||
{
|
||||
"requestTypeFields": [
|
||||
{
|
||||
"fieldId": "summary",
|
||||
"name": "Summary",
|
||||
"description": "Insert short description of issue.",
|
||||
"required": true,
|
||||
"defaultValues": [],
|
||||
"validValues": [],
|
||||
"jiraSchema": {
|
||||
"type": "string",
|
||||
"system": "summary"
|
||||
},
|
||||
"visible": true
|
||||
},
|
||||
{
|
||||
"fieldId": "customfield_10261",
|
||||
"name": "Store Number",
|
||||
"description": "Type in your store number and click on it to select.",
|
||||
"required": true,
|
||||
"defaultValues": [],
|
||||
"validValues": [],
|
||||
"jiraSchema": {
|
||||
"type": "array",
|
||||
"items": "cmdb-object-field",
|
||||
"custom": "com.atlassian.jira.plugins.cmdb:cmdb-object-cftype",
|
||||
"customId": 10261
|
||||
},
|
||||
"visible": true
|
||||
},
|
||||
{
|
||||
"fieldId": "description",
|
||||
"name": "Description",
|
||||
"description": "When did you start noticing this issue? Is this an issue daily? Does traffic tend to “catch up” after some time?",
|
||||
"required": true,
|
||||
"defaultValues": [],
|
||||
"validValues": [],
|
||||
"jiraSchema": {
|
||||
"type": "string",
|
||||
"system": "description"
|
||||
},
|
||||
"visible": true
|
||||
},
|
||||
{
|
||||
"fieldId": "customfield_10294",
|
||||
"name": "Date/Time Issue Occured",
|
||||
"description": "What date / time did you first notice that there was a traffic issue?",
|
||||
"required": false,
|
||||
"defaultValues": [],
|
||||
"validValues": [],
|
||||
"jiraSchema": {
|
||||
"type": "datetime",
|
||||
"custom": "com.atlassian.jira.plugin.system.customfieldtypes:datetime",
|
||||
"customId": 10294
|
||||
},
|
||||
"visible": true
|
||||
},
|
||||
{
|
||||
"fieldId": "customfield_10544",
|
||||
"name": "Affected Device",
|
||||
"description": "Do you have two traffic counters? Which traffic counter is having an issue?",
|
||||
"required": false,
|
||||
"defaultValues": [],
|
||||
"validValues": [],
|
||||
"jiraSchema": {
|
||||
"type": "string",
|
||||
"custom": "com.atlassian.jira.plugin.system.customfieldtypes:textfield",
|
||||
"customId": 10544
|
||||
},
|
||||
"visible": true
|
||||
},
|
||||
{
|
||||
"fieldId": "attachment",
|
||||
"name": "Attachment",
|
||||
"description": "",
|
||||
"required": false,
|
||||
"defaultValues": [],
|
||||
"validValues": [],
|
||||
"jiraSchema": {
|
||||
"type": "array",
|
||||
"items": "attachment",
|
||||
"system": "attachment"
|
||||
},
|
||||
"visible": true
|
||||
}
|
||||
],
|
||||
"canRaiseOnBehalfOf": true,
|
||||
"canAddRequestParticipants": true
|
||||
}
|
||||
75
ss-fields-272.json
Normal file
75
ss-fields-272.json
Normal file
|
|
@ -0,0 +1,75 @@
|
|||
{
|
||||
"requestTypeFields": [
|
||||
{
|
||||
"fieldId": "summary",
|
||||
"name": "Summary",
|
||||
"description": "Insert short description of issue.",
|
||||
"required": true,
|
||||
"defaultValues": [],
|
||||
"validValues": [],
|
||||
"jiraSchema": {
|
||||
"type": "string",
|
||||
"system": "summary"
|
||||
},
|
||||
"visible": true
|
||||
},
|
||||
{
|
||||
"fieldId": "customfield_10261",
|
||||
"name": "Store Number",
|
||||
"description": "Type in your store number and select",
|
||||
"required": true,
|
||||
"defaultValues": [],
|
||||
"validValues": [],
|
||||
"jiraSchema": {
|
||||
"type": "array",
|
||||
"items": "cmdb-object-field",
|
||||
"custom": "com.atlassian.jira.plugins.cmdb:cmdb-object-cftype",
|
||||
"customId": 10261
|
||||
},
|
||||
"visible": true
|
||||
},
|
||||
{
|
||||
"fieldId": "description",
|
||||
"name": "Description",
|
||||
"description": "Please provide detailed description of Sterling Application issue. Please make sure to include any order information available",
|
||||
"required": true,
|
||||
"defaultValues": [],
|
||||
"validValues": [],
|
||||
"jiraSchema": {
|
||||
"type": "string",
|
||||
"system": "description"
|
||||
},
|
||||
"visible": true
|
||||
},
|
||||
{
|
||||
"fieldId": "customfield_10294",
|
||||
"name": "Date/Time Issue Occured",
|
||||
"description": "",
|
||||
"required": false,
|
||||
"defaultValues": [],
|
||||
"validValues": [],
|
||||
"jiraSchema": {
|
||||
"type": "datetime",
|
||||
"custom": "com.atlassian.jira.plugin.system.customfieldtypes:datetime",
|
||||
"customId": 10294
|
||||
},
|
||||
"visible": true
|
||||
},
|
||||
{
|
||||
"fieldId": "attachment",
|
||||
"name": "Attachment",
|
||||
"description": "Please include a screenshot of what you are showing related to your Sterling Issue",
|
||||
"required": false,
|
||||
"defaultValues": [],
|
||||
"validValues": [],
|
||||
"jiraSchema": {
|
||||
"type": "array",
|
||||
"items": "attachment",
|
||||
"system": "attachment"
|
||||
},
|
||||
"visible": true
|
||||
}
|
||||
],
|
||||
"canRaiseOnBehalfOf": true,
|
||||
"canAddRequestParticipants": true
|
||||
}
|
||||
103
ss-fields-273.json
Normal file
103
ss-fields-273.json
Normal file
|
|
@ -0,0 +1,103 @@
|
|||
{
|
||||
"requestTypeFields": [
|
||||
{
|
||||
"fieldId": "summary",
|
||||
"name": "Summary",
|
||||
"description": "Insert short description of issue.",
|
||||
"required": true,
|
||||
"defaultValues": [],
|
||||
"validValues": [],
|
||||
"jiraSchema": {
|
||||
"type": "string",
|
||||
"system": "summary"
|
||||
},
|
||||
"visible": true
|
||||
},
|
||||
{
|
||||
"fieldId": "customfield_10261",
|
||||
"name": "Store Number",
|
||||
"description": "Type in your store number and click to select it from the list.",
|
||||
"required": true,
|
||||
"defaultValues": [],
|
||||
"validValues": [],
|
||||
"jiraSchema": {
|
||||
"type": "array",
|
||||
"items": "cmdb-object-field",
|
||||
"custom": "com.atlassian.jira.plugins.cmdb:cmdb-object-cftype",
|
||||
"customId": 10261
|
||||
},
|
||||
"visible": true
|
||||
},
|
||||
{
|
||||
"fieldId": "description",
|
||||
"name": "Description",
|
||||
"description": "Please provide as much detail as possible regarding the missing hardware.",
|
||||
"required": true,
|
||||
"defaultValues": [],
|
||||
"validValues": [],
|
||||
"jiraSchema": {
|
||||
"type": "string",
|
||||
"system": "description"
|
||||
},
|
||||
"visible": true
|
||||
},
|
||||
{
|
||||
"fieldId": "customfield_10544",
|
||||
"name": "Affected Device",
|
||||
"description": "What device / hardware are you missing?",
|
||||
"required": false,
|
||||
"defaultValues": [],
|
||||
"validValues": [],
|
||||
"jiraSchema": {
|
||||
"type": "string",
|
||||
"custom": "com.atlassian.jira.plugin.system.customfieldtypes:textfield",
|
||||
"customId": 10544
|
||||
},
|
||||
"visible": true
|
||||
},
|
||||
{
|
||||
"fieldId": "customfield_10549",
|
||||
"name": "Police Report Number",
|
||||
"description": "A Police Report needs to be filed for any lost/stolen hardware. Please make sure to include this.",
|
||||
"required": false,
|
||||
"defaultValues": [],
|
||||
"validValues": [],
|
||||
"jiraSchema": {
|
||||
"type": "string",
|
||||
"custom": "com.atlassian.jira.plugin.system.customfieldtypes:textfield",
|
||||
"customId": 10549
|
||||
},
|
||||
"visible": true
|
||||
},
|
||||
{
|
||||
"fieldId": "customfield_10294",
|
||||
"name": "Date / Time Hardware Last Seen or Used",
|
||||
"description": "When is the last day / time this device was used or in your store?",
|
||||
"required": false,
|
||||
"defaultValues": [],
|
||||
"validValues": [],
|
||||
"jiraSchema": {
|
||||
"type": "datetime",
|
||||
"custom": "com.atlassian.jira.plugin.system.customfieldtypes:datetime",
|
||||
"customId": 10294
|
||||
},
|
||||
"visible": true
|
||||
},
|
||||
{
|
||||
"fieldId": "attachment",
|
||||
"name": "Attachment",
|
||||
"description": "",
|
||||
"required": false,
|
||||
"defaultValues": [],
|
||||
"validValues": [],
|
||||
"jiraSchema": {
|
||||
"type": "array",
|
||||
"items": "attachment",
|
||||
"system": "attachment"
|
||||
},
|
||||
"visible": true
|
||||
}
|
||||
],
|
||||
"canRaiseOnBehalfOf": true,
|
||||
"canAddRequestParticipants": true
|
||||
}
|
||||
61
ss-fields-274.json
Normal file
61
ss-fields-274.json
Normal file
|
|
@ -0,0 +1,61 @@
|
|||
{
|
||||
"requestTypeFields": [
|
||||
{
|
||||
"fieldId": "summary",
|
||||
"name": "Summary",
|
||||
"description": "Insert short description of request.",
|
||||
"required": true,
|
||||
"defaultValues": [],
|
||||
"validValues": [],
|
||||
"jiraSchema": {
|
||||
"type": "string",
|
||||
"system": "summary"
|
||||
},
|
||||
"visible": true
|
||||
},
|
||||
{
|
||||
"fieldId": "customfield_10261",
|
||||
"name": "Store Number",
|
||||
"description": "Type in your store number and select",
|
||||
"required": true,
|
||||
"defaultValues": [],
|
||||
"validValues": [],
|
||||
"jiraSchema": {
|
||||
"type": "array",
|
||||
"items": "cmdb-object-field",
|
||||
"custom": "com.atlassian.jira.plugins.cmdb:cmdb-object-cftype",
|
||||
"customId": 10261
|
||||
},
|
||||
"visible": true
|
||||
},
|
||||
{
|
||||
"fieldId": "description",
|
||||
"name": "Description",
|
||||
"description": "Please provide as much detail as possible for why your store needs additional hardware.",
|
||||
"required": true,
|
||||
"defaultValues": [],
|
||||
"validValues": [],
|
||||
"jiraSchema": {
|
||||
"type": "string",
|
||||
"system": "description"
|
||||
},
|
||||
"visible": true
|
||||
},
|
||||
{
|
||||
"fieldId": "attachment",
|
||||
"name": "Attachment",
|
||||
"description": "",
|
||||
"required": false,
|
||||
"defaultValues": [],
|
||||
"validValues": [],
|
||||
"jiraSchema": {
|
||||
"type": "array",
|
||||
"items": "attachment",
|
||||
"system": "attachment"
|
||||
},
|
||||
"visible": true
|
||||
}
|
||||
],
|
||||
"canRaiseOnBehalfOf": true,
|
||||
"canAddRequestParticipants": true
|
||||
}
|
||||
105
ss-fields-275.json
Normal file
105
ss-fields-275.json
Normal file
|
|
@ -0,0 +1,105 @@
|
|||
{
|
||||
"requestTypeFields": [
|
||||
{
|
||||
"fieldId": "summary",
|
||||
"name": "Summary",
|
||||
"description": "Insert short description of issue.",
|
||||
"required": true,
|
||||
"defaultValues": [],
|
||||
"validValues": [],
|
||||
"jiraSchema": {
|
||||
"type": "string",
|
||||
"system": "summary"
|
||||
},
|
||||
"visible": true
|
||||
},
|
||||
{
|
||||
"fieldId": "customfield_10261",
|
||||
"name": "Store Number",
|
||||
"description": "Type in your store number and select",
|
||||
"required": true,
|
||||
"defaultValues": [],
|
||||
"validValues": [],
|
||||
"jiraSchema": {
|
||||
"type": "array",
|
||||
"items": "cmdb-object-field",
|
||||
"custom": "com.atlassian.jira.plugins.cmdb:cmdb-object-cftype",
|
||||
"customId": 10261
|
||||
},
|
||||
"visible": true
|
||||
},
|
||||
{
|
||||
"fieldId": "description",
|
||||
"name": "Description",
|
||||
"description": "Please provide as much detail as possible about your login issue.",
|
||||
"required": true,
|
||||
"defaultValues": [],
|
||||
"validValues": [],
|
||||
"jiraSchema": {
|
||||
"type": "string",
|
||||
"system": "description"
|
||||
},
|
||||
"visible": true
|
||||
},
|
||||
{
|
||||
"fieldId": "customfield_10454",
|
||||
"name": "Preferred Contact Method",
|
||||
"description": "",
|
||||
"required": false,
|
||||
"defaultValues": [],
|
||||
"validValues": [
|
||||
{
|
||||
"value": "11325",
|
||||
"label": "Email",
|
||||
"children": []
|
||||
},
|
||||
{
|
||||
"value": "11326",
|
||||
"label": "Phone",
|
||||
"children": []
|
||||
},
|
||||
{
|
||||
"value": "11327",
|
||||
"label": "Webex",
|
||||
"children": []
|
||||
}
|
||||
],
|
||||
"jiraSchema": {
|
||||
"type": "option",
|
||||
"custom": "com.atlassian.jira.plugin.system.customfieldtypes:select",
|
||||
"customId": 10454
|
||||
},
|
||||
"visible": true
|
||||
},
|
||||
{
|
||||
"fieldId": "customfield_10426",
|
||||
"name": "Phone",
|
||||
"description": "",
|
||||
"required": false,
|
||||
"defaultValues": [],
|
||||
"validValues": [],
|
||||
"jiraSchema": {
|
||||
"type": "string",
|
||||
"custom": "com.atlassian.jira.plugin.system.customfieldtypes:textfield",
|
||||
"customId": 10426
|
||||
},
|
||||
"visible": true
|
||||
},
|
||||
{
|
||||
"fieldId": "attachment",
|
||||
"name": "Attachment",
|
||||
"description": "",
|
||||
"required": false,
|
||||
"defaultValues": [],
|
||||
"validValues": [],
|
||||
"jiraSchema": {
|
||||
"type": "array",
|
||||
"items": "attachment",
|
||||
"system": "attachment"
|
||||
},
|
||||
"visible": true
|
||||
}
|
||||
],
|
||||
"canRaiseOnBehalfOf": true,
|
||||
"canAddRequestParticipants": true
|
||||
}
|
||||
103
ss-fields-426.json
Normal file
103
ss-fields-426.json
Normal file
|
|
@ -0,0 +1,103 @@
|
|||
{
|
||||
"requestTypeFields": [
|
||||
{
|
||||
"fieldId": "summary",
|
||||
"name": "Summary",
|
||||
"description": "",
|
||||
"required": true,
|
||||
"defaultValues": [],
|
||||
"validValues": [],
|
||||
"jiraSchema": {
|
||||
"type": "string",
|
||||
"system": "summary"
|
||||
},
|
||||
"visible": true
|
||||
},
|
||||
{
|
||||
"fieldId": "customfield_10261",
|
||||
"name": "Store Number",
|
||||
"description": "",
|
||||
"required": true,
|
||||
"defaultValues": [],
|
||||
"validValues": [],
|
||||
"jiraSchema": {
|
||||
"type": "array",
|
||||
"items": "cmdb-object-field",
|
||||
"custom": "com.atlassian.jira.plugins.cmdb:cmdb-object-cftype",
|
||||
"customId": 10261
|
||||
},
|
||||
"visible": true
|
||||
},
|
||||
{
|
||||
"fieldId": "customfield_10563",
|
||||
"name": "Description",
|
||||
"description": "",
|
||||
"required": true,
|
||||
"defaultValues": [],
|
||||
"validValues": [],
|
||||
"jiraSchema": {
|
||||
"type": "string",
|
||||
"custom": "com.atlassian.jira.plugin.system.customfieldtypes:textarea",
|
||||
"customId": 10563
|
||||
},
|
||||
"visible": true
|
||||
},
|
||||
{
|
||||
"fieldId": "customfield_10264",
|
||||
"name": "Urgency",
|
||||
"description": "",
|
||||
"required": false,
|
||||
"defaultValues": [
|
||||
{
|
||||
"value": "10480",
|
||||
"label": "Medium",
|
||||
"children": []
|
||||
}
|
||||
],
|
||||
"validValues": [
|
||||
{
|
||||
"value": "10478",
|
||||
"label": "Critical",
|
||||
"children": []
|
||||
},
|
||||
{
|
||||
"value": "10479",
|
||||
"label": "High",
|
||||
"children": []
|
||||
},
|
||||
{
|
||||
"value": "10480",
|
||||
"label": "Medium",
|
||||
"children": []
|
||||
},
|
||||
{
|
||||
"value": "10481",
|
||||
"label": "Low",
|
||||
"children": []
|
||||
}
|
||||
],
|
||||
"jiraSchema": {
|
||||
"type": "option",
|
||||
"custom": "com.atlassian.jira.plugin.system.customfieldtypes:select",
|
||||
"customId": 10264
|
||||
},
|
||||
"visible": true
|
||||
},
|
||||
{
|
||||
"fieldId": "attachment",
|
||||
"name": "Attachment",
|
||||
"description": "",
|
||||
"required": false,
|
||||
"defaultValues": [],
|
||||
"validValues": [],
|
||||
"jiraSchema": {
|
||||
"type": "array",
|
||||
"items": "attachment",
|
||||
"system": "attachment"
|
||||
},
|
||||
"visible": true
|
||||
}
|
||||
],
|
||||
"canRaiseOnBehalfOf": true,
|
||||
"canAddRequestParticipants": true
|
||||
}
|
||||
216
ss-fields-493.json
Normal file
216
ss-fields-493.json
Normal file
|
|
@ -0,0 +1,216 @@
|
|||
{
|
||||
"requestTypeFields": [
|
||||
{
|
||||
"fieldId": "summary",
|
||||
"name": "Summary",
|
||||
"description": "",
|
||||
"required": true,
|
||||
"defaultValues": [],
|
||||
"validValues": [],
|
||||
"jiraSchema": {
|
||||
"type": "string",
|
||||
"system": "summary"
|
||||
},
|
||||
"visible": true
|
||||
},
|
||||
{
|
||||
"fieldId": "customfield_10261",
|
||||
"name": "Store Number",
|
||||
"description": "",
|
||||
"required": true,
|
||||
"defaultValues": [],
|
||||
"validValues": [],
|
||||
"jiraSchema": {
|
||||
"type": "array",
|
||||
"items": "cmdb-object-field",
|
||||
"custom": "com.atlassian.jira.plugins.cmdb:cmdb-object-cftype",
|
||||
"customId": 10261
|
||||
},
|
||||
"visible": true
|
||||
},
|
||||
{
|
||||
"fieldId": "customfield_11688",
|
||||
"name": "Reason for Transportation Request",
|
||||
"description": "",
|
||||
"required": true,
|
||||
"defaultValues": [],
|
||||
"validValues": [
|
||||
{
|
||||
"value": "13706",
|
||||
"label": "Urgent Store Closure",
|
||||
"children": [
|
||||
{
|
||||
"value": "13711",
|
||||
"label": "Power Outage",
|
||||
"children": []
|
||||
},
|
||||
{
|
||||
"value": "13712",
|
||||
"label": "Fire",
|
||||
"children": []
|
||||
},
|
||||
{
|
||||
"value": "13713",
|
||||
"label": "Mall Closure",
|
||||
"children": []
|
||||
},
|
||||
{
|
||||
"value": "13714",
|
||||
"label": "Weather Hazard",
|
||||
"children": []
|
||||
},
|
||||
{
|
||||
"value": "13715",
|
||||
"label": "Unexpected Closure",
|
||||
"children": []
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"value": "13707",
|
||||
"label": "Unprofessional Driver Issue",
|
||||
"children": [
|
||||
{
|
||||
"value": "13716",
|
||||
"label": "Unsafe / Hostile Work Environment",
|
||||
"children": []
|
||||
},
|
||||
{
|
||||
"value": "13717",
|
||||
"label": "Profanity / Offensive Language",
|
||||
"children": []
|
||||
},
|
||||
{
|
||||
"value": "13718",
|
||||
"label": "Receiving shipment anywhere but back room stock door",
|
||||
"children": []
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"value": "13708",
|
||||
"label": "Carrier Issue",
|
||||
"children": [
|
||||
{
|
||||
"value": "13719",
|
||||
"label": "Delivering outside of delivery window (includes after 5p & Saturday)",
|
||||
"children": []
|
||||
},
|
||||
{
|
||||
"value": "13720",
|
||||
"label": "Unprofessional Driver",
|
||||
"children": []
|
||||
},
|
||||
{
|
||||
"value": "13721",
|
||||
"label": "Damaged Carton (if issue is consistently happening)",
|
||||
"children": []
|
||||
},
|
||||
{
|
||||
"value": "13722",
|
||||
"label": "Delivery not meeting company standard / policies",
|
||||
"children": []
|
||||
},
|
||||
{
|
||||
"value": "13723",
|
||||
"label": "Picking up outside of pickup window provided",
|
||||
"children": []
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"value": "13709",
|
||||
"label": "Missed Pickup",
|
||||
"children": [
|
||||
{
|
||||
"value": "13724",
|
||||
"label": "BOSS FedEx Express",
|
||||
"children": []
|
||||
},
|
||||
{
|
||||
"value": "13725",
|
||||
"label": "BOSS FedEx Ground",
|
||||
"children": []
|
||||
},
|
||||
{
|
||||
"value": "13726",
|
||||
"label": "BOSS USPS",
|
||||
"children": []
|
||||
},
|
||||
{
|
||||
"value": "13727",
|
||||
"label": "FedEx Ground (Transfer)",
|
||||
"children": []
|
||||
},
|
||||
{
|
||||
"value": "13728",
|
||||
"label": "FedEx Express (Transfer)",
|
||||
"children": []
|
||||
},
|
||||
{
|
||||
"value": "13729",
|
||||
"label": "CA Post (BOSS)",
|
||||
"children": []
|
||||
},
|
||||
{
|
||||
"value": "13730",
|
||||
"label": "Purolator (BOSS)",
|
||||
"children": []
|
||||
},
|
||||
{
|
||||
"value": "13731",
|
||||
"label": "UPS",
|
||||
"children": []
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"value": "13710",
|
||||
"label": "Ship Manager Issue",
|
||||
"children": [
|
||||
{
|
||||
"value": "13732",
|
||||
"label": "Unable to Login - Fedex Ship Manager or UPS Campus Ship Manager (CANADA ONLY)",
|
||||
"children": []
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"jiraSchema": {
|
||||
"type": "option-with-child",
|
||||
"custom": "com.atlassian.jira.plugin.system.customfieldtypes:cascadingselect",
|
||||
"customId": 11688
|
||||
},
|
||||
"visible": true
|
||||
},
|
||||
{
|
||||
"fieldId": "description",
|
||||
"name": "Detailed Description",
|
||||
"description": "",
|
||||
"required": true,
|
||||
"defaultValues": [],
|
||||
"validValues": [],
|
||||
"jiraSchema": {
|
||||
"type": "string",
|
||||
"system": "description"
|
||||
},
|
||||
"visible": true
|
||||
},
|
||||
{
|
||||
"fieldId": "attachment",
|
||||
"name": "Attachment",
|
||||
"description": "",
|
||||
"required": false,
|
||||
"defaultValues": [],
|
||||
"validValues": [],
|
||||
"jiraSchema": {
|
||||
"type": "array",
|
||||
"items": "attachment",
|
||||
"system": "attachment"
|
||||
},
|
||||
"visible": true
|
||||
}
|
||||
],
|
||||
"canRaiseOnBehalfOf": true,
|
||||
"canAddRequestParticipants": true
|
||||
}
|
||||
209
ss-request-types-clean.json
Normal file
209
ss-request-types-clean.json
Normal file
|
|
@ -0,0 +1,209 @@
|
|||
[
|
||||
{
|
||||
"id": "266",
|
||||
"name": "Broken Device / Hardware",
|
||||
"defaultName": "Broken Device / Hardware",
|
||||
"description": "",
|
||||
"issueTypeId": "10085",
|
||||
"groupIds": [
|
||||
"156",
|
||||
"157"
|
||||
],
|
||||
"canCreateRequest": true
|
||||
},
|
||||
{
|
||||
"id": "267",
|
||||
"name": "Business Report Issue",
|
||||
"defaultName": "Business Report Issue",
|
||||
"description": "Business Report not updating or showing incorrect data? Click here",
|
||||
"issueTypeId": "10085",
|
||||
"groupIds": [
|
||||
"156",
|
||||
"158"
|
||||
],
|
||||
"canCreateRequest": true
|
||||
},
|
||||
{
|
||||
"id": "268",
|
||||
"name": "Omni Turn Off / On",
|
||||
"defaultName": "Omni Turn Off / On",
|
||||
"description": "",
|
||||
"issueTypeId": "10082",
|
||||
"groupIds": [
|
||||
"158"
|
||||
],
|
||||
"canCreateRequest": true
|
||||
},
|
||||
{
|
||||
"id": "269",
|
||||
"name": "Register Not Functioning Properly",
|
||||
"defaultName": "Register Not Functioning Properly",
|
||||
"description": "",
|
||||
"issueTypeId": "10085",
|
||||
"groupIds": [
|
||||
"156"
|
||||
],
|
||||
"canCreateRequest": true
|
||||
},
|
||||
{
|
||||
"id": "270",
|
||||
"name": "Report a Technology Issue",
|
||||
"defaultName": "Report a Technology Issue",
|
||||
"description": "Select this option if something you use for work that usually functions, is not functioning as expected.",
|
||||
"issueTypeId": "10085",
|
||||
"groupIds": [
|
||||
"158"
|
||||
],
|
||||
"canCreateRequest": true
|
||||
},
|
||||
{
|
||||
"id": "271",
|
||||
"name": "Report a Traffic Counter Issue",
|
||||
"defaultName": "Report a Traffic Counter Issue",
|
||||
"description": "",
|
||||
"issueTypeId": "10085",
|
||||
"groupIds": [
|
||||
"158"
|
||||
],
|
||||
"canCreateRequest": true
|
||||
},
|
||||
{
|
||||
"id": "272",
|
||||
"name": "Report an Issue with Sterling Application",
|
||||
"defaultName": "Report an Issue with Sterling Application",
|
||||
"description": "",
|
||||
"issueTypeId": "10085",
|
||||
"groupIds": [
|
||||
"158"
|
||||
],
|
||||
"canCreateRequest": true
|
||||
},
|
||||
{
|
||||
"id": "273",
|
||||
"name": "Report Missing Hardware",
|
||||
"defaultName": "Report Missing Hardware",
|
||||
"description": "",
|
||||
"issueTypeId": "10085",
|
||||
"groupIds": [
|
||||
"157"
|
||||
],
|
||||
"canCreateRequest": true
|
||||
},
|
||||
{
|
||||
"id": "274",
|
||||
"name": "Request Additional Hardware",
|
||||
"defaultName": "Request Additional Hardware",
|
||||
"description": "",
|
||||
"issueTypeId": "10082",
|
||||
"groupIds": [
|
||||
"157"
|
||||
],
|
||||
"canCreateRequest": true
|
||||
},
|
||||
{
|
||||
"id": "493",
|
||||
"name": "Store Transportation Request",
|
||||
"defaultName": "Store Transportation Request",
|
||||
"description": "Click here to report an issue / request assistance with store transportation.",
|
||||
"issueTypeId": "10085",
|
||||
"groupIds": [
|
||||
"158"
|
||||
],
|
||||
"canCreateRequest": true
|
||||
},
|
||||
{
|
||||
"id": "426",
|
||||
"name": "UKG Pro / Workforce Management Issues",
|
||||
"defaultName": "UKG Pro / Workforce Management Issues",
|
||||
"description": "",
|
||||
"issueTypeId": "10085",
|
||||
"groupIds": [
|
||||
"158"
|
||||
],
|
||||
"canCreateRequest": true
|
||||
},
|
||||
{
|
||||
"id": "275",
|
||||
"name": "Unable to Login",
|
||||
"defaultName": "Unable to Login",
|
||||
"description": "Try the AEO2GO.com password reset and [unlock|https://aeo.onelogin.com/login2#action=unlock_account] options!\n[Forgot Password| https://aeo.onelogin.com/login2#action=password_reset]\n[Instructions| https://wiki.ae.com/display/KB/AEO2GO+-+How+to+Unlock+Your+SSO+Network+Account]\n\nIt takes seconds to regain access! If that does not work, select this option.",
|
||||
"issueTypeId": "10085",
|
||||
"groupIds": [
|
||||
"156",
|
||||
"158"
|
||||
],
|
||||
"canCreateRequest": true
|
||||
},
|
||||
{
|
||||
"id": "276",
|
||||
"name": "Bug Resolution",
|
||||
"defaultName": "Bug Resolution",
|
||||
"description": "",
|
||||
"issueTypeId": "10079",
|
||||
"groupIds": [],
|
||||
"canCreateRequest": true
|
||||
},
|
||||
{
|
||||
"id": "263",
|
||||
"name": "Emailed request",
|
||||
"defaultName": "Emailed request",
|
||||
"description": "Request received from your email support channel.",
|
||||
"issueTypeId": "10082",
|
||||
"groupIds": [],
|
||||
"canCreateRequest": true
|
||||
},
|
||||
{
|
||||
"id": "359",
|
||||
"name": "Hardware Request",
|
||||
"defaultName": "Hardware Request",
|
||||
"description": "",
|
||||
"issueTypeId": "10091",
|
||||
"groupIds": [],
|
||||
"canCreateRequest": true
|
||||
},
|
||||
{
|
||||
"id": "393",
|
||||
"name": "Payment Table Validation ",
|
||||
"defaultName": "Payment Table Validation ",
|
||||
"description": "",
|
||||
"issueTypeId": "10082",
|
||||
"groupIds": [],
|
||||
"canCreateRequest": true
|
||||
},
|
||||
{
|
||||
"id": "277",
|
||||
"name": "Process Problem",
|
||||
"defaultName": "Process Problem",
|
||||
"description": "",
|
||||
"issueTypeId": "10079",
|
||||
"groupIds": [],
|
||||
"canCreateRequest": true
|
||||
},
|
||||
{
|
||||
"id": "494",
|
||||
"name": "Questions",
|
||||
"defaultName": "Questions",
|
||||
"description": "Request type used by live chat for fallback, only when other request types don't work.",
|
||||
"issueTypeId": "10008",
|
||||
"groupIds": [],
|
||||
"canCreateRequest": true
|
||||
},
|
||||
{
|
||||
"id": "278",
|
||||
"name": "Recurring Incident",
|
||||
"defaultName": "Recurring Incident",
|
||||
"description": "",
|
||||
"issueTypeId": "10079",
|
||||
"groupIds": [],
|
||||
"canCreateRequest": true
|
||||
},
|
||||
{
|
||||
"id": "280",
|
||||
"name": "SolarWinds Alert",
|
||||
"defaultName": "SolarWinds Alert",
|
||||
"description": "",
|
||||
"issueTypeId": "10085",
|
||||
"groupIds": [],
|
||||
"canCreateRequest": true
|
||||
}
|
||||
]
|
||||
643
ss-request-types.json
Normal file
643
ss-request-types.json
Normal file
|
|
@ -0,0 +1,643 @@
|
|||
{
|
||||
"_expands": [
|
||||
"field"
|
||||
],
|
||||
"size": 20,
|
||||
"start": 0,
|
||||
"limit": 100,
|
||||
"isLastPage": true,
|
||||
"_links": {
|
||||
"self": "https://aeo.atlassian.net/rest/servicedeskapi/servicedesk/170/requesttype?limit=100",
|
||||
"base": "https://aeo.atlassian.net",
|
||||
"context": ""
|
||||
},
|
||||
"values": [
|
||||
{
|
||||
"_expands": [
|
||||
"field"
|
||||
],
|
||||
"id": "266",
|
||||
"_links": {
|
||||
"self": "https://aeo.atlassian.net/rest/servicedeskapi/servicedesk/170/requesttype/266"
|
||||
},
|
||||
"name": "Broken Device / Hardware",
|
||||
"description": "",
|
||||
"helpText": "",
|
||||
"defaultName": "Broken Device / Hardware",
|
||||
"issueTypeId": "10085",
|
||||
"serviceDeskId": "170",
|
||||
"portalId": "170",
|
||||
"groupIds": [
|
||||
"156",
|
||||
"157"
|
||||
],
|
||||
"icon": {
|
||||
"id": "10472",
|
||||
"_links": {
|
||||
"iconUrls": {
|
||||
"48x48": "https://aeo.atlassian.net/rest/api/2/universal_avatar/view/type/SD_REQTYPE/avatar/10472?size=large",
|
||||
"24x24": "https://aeo.atlassian.net/rest/api/2/universal_avatar/view/type/SD_REQTYPE/avatar/10472?size=small",
|
||||
"16x16": "https://aeo.atlassian.net/rest/api/2/universal_avatar/view/type/SD_REQTYPE/avatar/10472?size=xsmall",
|
||||
"32x32": "https://aeo.atlassian.net/rest/api/2/universal_avatar/view/type/SD_REQTYPE/avatar/10472?size=medium"
|
||||
}
|
||||
}
|
||||
},
|
||||
"restrictionStatus": "OPEN",
|
||||
"canCreateRequest": true
|
||||
},
|
||||
{
|
||||
"_expands": [
|
||||
"field"
|
||||
],
|
||||
"id": "267",
|
||||
"_links": {
|
||||
"self": "https://aeo.atlassian.net/rest/servicedeskapi/servicedesk/170/requesttype/267"
|
||||
},
|
||||
"name": "Business Report Issue",
|
||||
"description": "Business Report not updating or showing incorrect data? Click here",
|
||||
"helpText": "",
|
||||
"defaultName": "Business Report Issue",
|
||||
"issueTypeId": "10085",
|
||||
"serviceDeskId": "170",
|
||||
"portalId": "170",
|
||||
"groupIds": [
|
||||
"156",
|
||||
"158"
|
||||
],
|
||||
"icon": {
|
||||
"id": "10492",
|
||||
"_links": {
|
||||
"iconUrls": {
|
||||
"48x48": "https://aeo.atlassian.net/rest/api/2/universal_avatar/view/type/SD_REQTYPE/avatar/10492?size=large",
|
||||
"24x24": "https://aeo.atlassian.net/rest/api/2/universal_avatar/view/type/SD_REQTYPE/avatar/10492?size=small",
|
||||
"16x16": "https://aeo.atlassian.net/rest/api/2/universal_avatar/view/type/SD_REQTYPE/avatar/10492?size=xsmall",
|
||||
"32x32": "https://aeo.atlassian.net/rest/api/2/universal_avatar/view/type/SD_REQTYPE/avatar/10492?size=medium"
|
||||
}
|
||||
}
|
||||
},
|
||||
"restrictionStatus": "OPEN",
|
||||
"canCreateRequest": true
|
||||
},
|
||||
{
|
||||
"_expands": [
|
||||
"field"
|
||||
],
|
||||
"id": "268",
|
||||
"_links": {
|
||||
"self": "https://aeo.atlassian.net/rest/servicedeskapi/servicedesk/170/requesttype/268"
|
||||
},
|
||||
"name": "Omni Turn Off / On",
|
||||
"description": "",
|
||||
"helpText": "",
|
||||
"defaultName": "Omni Turn Off / On",
|
||||
"issueTypeId": "10082",
|
||||
"serviceDeskId": "170",
|
||||
"portalId": "170",
|
||||
"groupIds": [
|
||||
"158"
|
||||
],
|
||||
"icon": {
|
||||
"id": "10487",
|
||||
"_links": {
|
||||
"iconUrls": {
|
||||
"48x48": "https://aeo.atlassian.net/rest/api/2/universal_avatar/view/type/SD_REQTYPE/avatar/10487?size=large",
|
||||
"24x24": "https://aeo.atlassian.net/rest/api/2/universal_avatar/view/type/SD_REQTYPE/avatar/10487?size=small",
|
||||
"16x16": "https://aeo.atlassian.net/rest/api/2/universal_avatar/view/type/SD_REQTYPE/avatar/10487?size=xsmall",
|
||||
"32x32": "https://aeo.atlassian.net/rest/api/2/universal_avatar/view/type/SD_REQTYPE/avatar/10487?size=medium"
|
||||
}
|
||||
}
|
||||
},
|
||||
"restrictionStatus": "OPEN",
|
||||
"canCreateRequest": true
|
||||
},
|
||||
{
|
||||
"_expands": [
|
||||
"field"
|
||||
],
|
||||
"id": "269",
|
||||
"_links": {
|
||||
"self": "https://aeo.atlassian.net/rest/servicedeskapi/servicedesk/170/requesttype/269"
|
||||
},
|
||||
"name": "Register Not Functioning Properly",
|
||||
"description": "",
|
||||
"helpText": "",
|
||||
"defaultName": "Register Not Functioning Properly",
|
||||
"issueTypeId": "10085",
|
||||
"serviceDeskId": "170",
|
||||
"portalId": "170",
|
||||
"groupIds": [
|
||||
"156"
|
||||
],
|
||||
"icon": {
|
||||
"id": "10484",
|
||||
"_links": {
|
||||
"iconUrls": {
|
||||
"48x48": "https://aeo.atlassian.net/rest/api/2/universal_avatar/view/type/SD_REQTYPE/avatar/10484?size=large",
|
||||
"24x24": "https://aeo.atlassian.net/rest/api/2/universal_avatar/view/type/SD_REQTYPE/avatar/10484?size=small",
|
||||
"16x16": "https://aeo.atlassian.net/rest/api/2/universal_avatar/view/type/SD_REQTYPE/avatar/10484?size=xsmall",
|
||||
"32x32": "https://aeo.atlassian.net/rest/api/2/universal_avatar/view/type/SD_REQTYPE/avatar/10484?size=medium"
|
||||
}
|
||||
}
|
||||
},
|
||||
"restrictionStatus": "OPEN",
|
||||
"canCreateRequest": true
|
||||
},
|
||||
{
|
||||
"_expands": [
|
||||
"field"
|
||||
],
|
||||
"id": "270",
|
||||
"_links": {
|
||||
"self": "https://aeo.atlassian.net/rest/servicedeskapi/servicedesk/170/requesttype/270"
|
||||
},
|
||||
"name": "Report a Technology Issue",
|
||||
"description": "Select this option if something you use for work that usually functions, is not functioning as expected.",
|
||||
"helpText": "",
|
||||
"defaultName": "Report a Technology Issue",
|
||||
"issueTypeId": "10085",
|
||||
"serviceDeskId": "170",
|
||||
"portalId": "170",
|
||||
"groupIds": [
|
||||
"158"
|
||||
],
|
||||
"icon": {
|
||||
"id": "10470",
|
||||
"_links": {
|
||||
"iconUrls": {
|
||||
"48x48": "https://aeo.atlassian.net/rest/api/2/universal_avatar/view/type/SD_REQTYPE/avatar/10470?size=large",
|
||||
"24x24": "https://aeo.atlassian.net/rest/api/2/universal_avatar/view/type/SD_REQTYPE/avatar/10470?size=small",
|
||||
"16x16": "https://aeo.atlassian.net/rest/api/2/universal_avatar/view/type/SD_REQTYPE/avatar/10470?size=xsmall",
|
||||
"32x32": "https://aeo.atlassian.net/rest/api/2/universal_avatar/view/type/SD_REQTYPE/avatar/10470?size=medium"
|
||||
}
|
||||
}
|
||||
},
|
||||
"restrictionStatus": "OPEN",
|
||||
"canCreateRequest": true
|
||||
},
|
||||
{
|
||||
"_expands": [
|
||||
"field"
|
||||
],
|
||||
"id": "271",
|
||||
"_links": {
|
||||
"self": "https://aeo.atlassian.net/rest/servicedeskapi/servicedesk/170/requesttype/271"
|
||||
},
|
||||
"name": "Report a Traffic Counter Issue",
|
||||
"description": "",
|
||||
"helpText": "",
|
||||
"defaultName": "Report a Traffic Counter Issue",
|
||||
"issueTypeId": "10085",
|
||||
"serviceDeskId": "170",
|
||||
"portalId": "170",
|
||||
"groupIds": [
|
||||
"158"
|
||||
],
|
||||
"icon": {
|
||||
"id": "10648",
|
||||
"_links": {
|
||||
"iconUrls": {
|
||||
"48x48": "https://aeo.atlassian.net/rest/api/2/universal_avatar/view/type/SD_REQTYPE/avatar/10648?size=large",
|
||||
"24x24": "https://aeo.atlassian.net/rest/api/2/universal_avatar/view/type/SD_REQTYPE/avatar/10648?size=small",
|
||||
"16x16": "https://aeo.atlassian.net/rest/api/2/universal_avatar/view/type/SD_REQTYPE/avatar/10648?size=xsmall",
|
||||
"32x32": "https://aeo.atlassian.net/rest/api/2/universal_avatar/view/type/SD_REQTYPE/avatar/10648?size=medium"
|
||||
}
|
||||
}
|
||||
},
|
||||
"restrictionStatus": "OPEN",
|
||||
"canCreateRequest": true
|
||||
},
|
||||
{
|
||||
"_expands": [
|
||||
"field"
|
||||
],
|
||||
"id": "272",
|
||||
"_links": {
|
||||
"self": "https://aeo.atlassian.net/rest/servicedeskapi/servicedesk/170/requesttype/272"
|
||||
},
|
||||
"name": "Report an Issue with Sterling Application",
|
||||
"description": "",
|
||||
"helpText": "",
|
||||
"defaultName": "Report an Issue with Sterling Application",
|
||||
"issueTypeId": "10085",
|
||||
"serviceDeskId": "170",
|
||||
"portalId": "170",
|
||||
"groupIds": [
|
||||
"158"
|
||||
],
|
||||
"icon": {
|
||||
"id": "10469",
|
||||
"_links": {
|
||||
"iconUrls": {
|
||||
"48x48": "https://aeo.atlassian.net/rest/api/2/universal_avatar/view/type/SD_REQTYPE/avatar/10469?size=large",
|
||||
"24x24": "https://aeo.atlassian.net/rest/api/2/universal_avatar/view/type/SD_REQTYPE/avatar/10469?size=small",
|
||||
"16x16": "https://aeo.atlassian.net/rest/api/2/universal_avatar/view/type/SD_REQTYPE/avatar/10469?size=xsmall",
|
||||
"32x32": "https://aeo.atlassian.net/rest/api/2/universal_avatar/view/type/SD_REQTYPE/avatar/10469?size=medium"
|
||||
}
|
||||
}
|
||||
},
|
||||
"restrictionStatus": "OPEN",
|
||||
"canCreateRequest": true
|
||||
},
|
||||
{
|
||||
"_expands": [
|
||||
"field"
|
||||
],
|
||||
"id": "273",
|
||||
"_links": {
|
||||
"self": "https://aeo.atlassian.net/rest/servicedeskapi/servicedesk/170/requesttype/273"
|
||||
},
|
||||
"name": "Report Missing Hardware",
|
||||
"description": "",
|
||||
"helpText": "",
|
||||
"defaultName": "Report Missing Hardware",
|
||||
"issueTypeId": "10085",
|
||||
"serviceDeskId": "170",
|
||||
"portalId": "170",
|
||||
"groupIds": [
|
||||
"157"
|
||||
],
|
||||
"icon": {
|
||||
"id": "10489",
|
||||
"_links": {
|
||||
"iconUrls": {
|
||||
"48x48": "https://aeo.atlassian.net/rest/api/2/universal_avatar/view/type/SD_REQTYPE/avatar/10489?size=large",
|
||||
"24x24": "https://aeo.atlassian.net/rest/api/2/universal_avatar/view/type/SD_REQTYPE/avatar/10489?size=small",
|
||||
"16x16": "https://aeo.atlassian.net/rest/api/2/universal_avatar/view/type/SD_REQTYPE/avatar/10489?size=xsmall",
|
||||
"32x32": "https://aeo.atlassian.net/rest/api/2/universal_avatar/view/type/SD_REQTYPE/avatar/10489?size=medium"
|
||||
}
|
||||
}
|
||||
},
|
||||
"restrictionStatus": "OPEN",
|
||||
"canCreateRequest": true
|
||||
},
|
||||
{
|
||||
"_expands": [
|
||||
"field"
|
||||
],
|
||||
"id": "274",
|
||||
"_links": {
|
||||
"self": "https://aeo.atlassian.net/rest/servicedeskapi/servicedesk/170/requesttype/274"
|
||||
},
|
||||
"name": "Request Additional Hardware",
|
||||
"description": "",
|
||||
"helpText": "",
|
||||
"defaultName": "Request Additional Hardware",
|
||||
"issueTypeId": "10082",
|
||||
"serviceDeskId": "170",
|
||||
"portalId": "170",
|
||||
"groupIds": [
|
||||
"157"
|
||||
],
|
||||
"icon": {
|
||||
"id": "10511",
|
||||
"_links": {
|
||||
"iconUrls": {
|
||||
"48x48": "https://aeo.atlassian.net/rest/api/2/universal_avatar/view/type/SD_REQTYPE/avatar/10511?size=large",
|
||||
"24x24": "https://aeo.atlassian.net/rest/api/2/universal_avatar/view/type/SD_REQTYPE/avatar/10511?size=small",
|
||||
"16x16": "https://aeo.atlassian.net/rest/api/2/universal_avatar/view/type/SD_REQTYPE/avatar/10511?size=xsmall",
|
||||
"32x32": "https://aeo.atlassian.net/rest/api/2/universal_avatar/view/type/SD_REQTYPE/avatar/10511?size=medium"
|
||||
}
|
||||
}
|
||||
},
|
||||
"restrictionStatus": "OPEN",
|
||||
"canCreateRequest": true
|
||||
},
|
||||
{
|
||||
"_expands": [
|
||||
"field"
|
||||
],
|
||||
"id": "493",
|
||||
"_links": {
|
||||
"self": "https://aeo.atlassian.net/rest/servicedeskapi/servicedesk/170/requesttype/493"
|
||||
},
|
||||
"name": "Store Transportation Request",
|
||||
"description": "Click here to report an issue / request assistance with store transportation.",
|
||||
"helpText": "",
|
||||
"defaultName": "Store Transportation Request",
|
||||
"issueTypeId": "10085",
|
||||
"serviceDeskId": "170",
|
||||
"portalId": "170",
|
||||
"groupIds": [
|
||||
"158"
|
||||
],
|
||||
"icon": {
|
||||
"id": "10664",
|
||||
"_links": {
|
||||
"iconUrls": {
|
||||
"48x48": "https://aeo.atlassian.net/rest/api/2/universal_avatar/view/type/SD_REQTYPE/avatar/10664?size=large",
|
||||
"24x24": "https://aeo.atlassian.net/rest/api/2/universal_avatar/view/type/SD_REQTYPE/avatar/10664?size=small",
|
||||
"16x16": "https://aeo.atlassian.net/rest/api/2/universal_avatar/view/type/SD_REQTYPE/avatar/10664?size=xsmall",
|
||||
"32x32": "https://aeo.atlassian.net/rest/api/2/universal_avatar/view/type/SD_REQTYPE/avatar/10664?size=medium"
|
||||
}
|
||||
}
|
||||
},
|
||||
"restrictionStatus": "OPEN",
|
||||
"canCreateRequest": true
|
||||
},
|
||||
{
|
||||
"_expands": [
|
||||
"field"
|
||||
],
|
||||
"id": "426",
|
||||
"_links": {
|
||||
"self": "https://aeo.atlassian.net/rest/servicedeskapi/servicedesk/170/requesttype/426"
|
||||
},
|
||||
"name": "UKG Pro / Workforce Management Issues",
|
||||
"description": "",
|
||||
"helpText": "",
|
||||
"defaultName": "UKG Pro / Workforce Management Issues",
|
||||
"issueTypeId": "10085",
|
||||
"serviceDeskId": "170",
|
||||
"portalId": "170",
|
||||
"groupIds": [
|
||||
"158"
|
||||
],
|
||||
"icon": {
|
||||
"id": "10499",
|
||||
"_links": {
|
||||
"iconUrls": {
|
||||
"48x48": "https://aeo.atlassian.net/rest/api/2/universal_avatar/view/type/SD_REQTYPE/avatar/10499?size=large",
|
||||
"24x24": "https://aeo.atlassian.net/rest/api/2/universal_avatar/view/type/SD_REQTYPE/avatar/10499?size=small",
|
||||
"16x16": "https://aeo.atlassian.net/rest/api/2/universal_avatar/view/type/SD_REQTYPE/avatar/10499?size=xsmall",
|
||||
"32x32": "https://aeo.atlassian.net/rest/api/2/universal_avatar/view/type/SD_REQTYPE/avatar/10499?size=medium"
|
||||
}
|
||||
}
|
||||
},
|
||||
"restrictionStatus": "OPEN",
|
||||
"canCreateRequest": true
|
||||
},
|
||||
{
|
||||
"_expands": [
|
||||
"field"
|
||||
],
|
||||
"id": "275",
|
||||
"_links": {
|
||||
"self": "https://aeo.atlassian.net/rest/servicedeskapi/servicedesk/170/requesttype/275"
|
||||
},
|
||||
"name": "Unable to Login",
|
||||
"description": "Try the AEO2GO.com password reset and [unlock|https://aeo.onelogin.com/login2#action=unlock_account] options!\n[Forgot Password| https://aeo.onelogin.com/login2#action=password_reset]\n[Instructions| https://wiki.ae.com/display/KB/AEO2GO+-+How+to+Unlock+Your+SSO+Network+Account]\n\nIt takes seconds to regain access! If that does not work, select this option.",
|
||||
"helpText": "",
|
||||
"defaultName": "Unable to Login",
|
||||
"issueTypeId": "10085",
|
||||
"serviceDeskId": "170",
|
||||
"portalId": "170",
|
||||
"groupIds": [
|
||||
"156",
|
||||
"158"
|
||||
],
|
||||
"icon": {
|
||||
"id": "10610",
|
||||
"_links": {
|
||||
"iconUrls": {
|
||||
"48x48": "https://aeo.atlassian.net/rest/api/2/universal_avatar/view/type/SD_REQTYPE/avatar/10610?size=large",
|
||||
"24x24": "https://aeo.atlassian.net/rest/api/2/universal_avatar/view/type/SD_REQTYPE/avatar/10610?size=small",
|
||||
"16x16": "https://aeo.atlassian.net/rest/api/2/universal_avatar/view/type/SD_REQTYPE/avatar/10610?size=xsmall",
|
||||
"32x32": "https://aeo.atlassian.net/rest/api/2/universal_avatar/view/type/SD_REQTYPE/avatar/10610?size=medium"
|
||||
}
|
||||
}
|
||||
},
|
||||
"restrictionStatus": "OPEN",
|
||||
"canCreateRequest": true
|
||||
},
|
||||
{
|
||||
"_expands": [
|
||||
"field"
|
||||
],
|
||||
"id": "276",
|
||||
"_links": {
|
||||
"self": "https://aeo.atlassian.net/rest/servicedeskapi/servicedesk/170/requesttype/276"
|
||||
},
|
||||
"name": "Bug Resolution",
|
||||
"description": "",
|
||||
"helpText": "",
|
||||
"defaultName": "Bug Resolution",
|
||||
"issueTypeId": "10079",
|
||||
"serviceDeskId": "170",
|
||||
"portalId": "170",
|
||||
"groupIds": [],
|
||||
"icon": {
|
||||
"id": "10476",
|
||||
"_links": {
|
||||
"iconUrls": {
|
||||
"48x48": "https://aeo.atlassian.net/rest/api/2/universal_avatar/view/type/SD_REQTYPE/avatar/10476?size=large",
|
||||
"24x24": "https://aeo.atlassian.net/rest/api/2/universal_avatar/view/type/SD_REQTYPE/avatar/10476?size=small",
|
||||
"16x16": "https://aeo.atlassian.net/rest/api/2/universal_avatar/view/type/SD_REQTYPE/avatar/10476?size=xsmall",
|
||||
"32x32": "https://aeo.atlassian.net/rest/api/2/universal_avatar/view/type/SD_REQTYPE/avatar/10476?size=medium"
|
||||
}
|
||||
}
|
||||
},
|
||||
"restrictionStatus": "OPEN",
|
||||
"canCreateRequest": true
|
||||
},
|
||||
{
|
||||
"_expands": [
|
||||
"field"
|
||||
],
|
||||
"id": "263",
|
||||
"_links": {
|
||||
"self": "https://aeo.atlassian.net/rest/servicedeskapi/servicedesk/170/requesttype/263"
|
||||
},
|
||||
"name": "Emailed request",
|
||||
"description": "Request received from your email support channel.",
|
||||
"helpText": "",
|
||||
"defaultName": "Emailed request",
|
||||
"issueTypeId": "10082",
|
||||
"serviceDeskId": "170",
|
||||
"portalId": "170",
|
||||
"groupIds": [],
|
||||
"icon": {
|
||||
"id": "10527",
|
||||
"_links": {
|
||||
"iconUrls": {
|
||||
"48x48": "https://aeo.atlassian.net/rest/api/2/universal_avatar/view/type/SD_REQTYPE/avatar/10527?size=large",
|
||||
"24x24": "https://aeo.atlassian.net/rest/api/2/universal_avatar/view/type/SD_REQTYPE/avatar/10527?size=small",
|
||||
"16x16": "https://aeo.atlassian.net/rest/api/2/universal_avatar/view/type/SD_REQTYPE/avatar/10527?size=xsmall",
|
||||
"32x32": "https://aeo.atlassian.net/rest/api/2/universal_avatar/view/type/SD_REQTYPE/avatar/10527?size=medium"
|
||||
}
|
||||
}
|
||||
},
|
||||
"restrictionStatus": "OPEN",
|
||||
"canCreateRequest": true
|
||||
},
|
||||
{
|
||||
"_expands": [
|
||||
"field"
|
||||
],
|
||||
"id": "359",
|
||||
"_links": {
|
||||
"self": "https://aeo.atlassian.net/rest/servicedeskapi/servicedesk/170/requesttype/359"
|
||||
},
|
||||
"name": "Hardware Request",
|
||||
"description": "",
|
||||
"helpText": "",
|
||||
"defaultName": "Hardware Request",
|
||||
"issueTypeId": "10091",
|
||||
"serviceDeskId": "170",
|
||||
"portalId": "170",
|
||||
"groupIds": [],
|
||||
"icon": {
|
||||
"id": "10511",
|
||||
"_links": {
|
||||
"iconUrls": {
|
||||
"48x48": "https://aeo.atlassian.net/rest/api/2/universal_avatar/view/type/SD_REQTYPE/avatar/10511?size=large",
|
||||
"24x24": "https://aeo.atlassian.net/rest/api/2/universal_avatar/view/type/SD_REQTYPE/avatar/10511?size=small",
|
||||
"16x16": "https://aeo.atlassian.net/rest/api/2/universal_avatar/view/type/SD_REQTYPE/avatar/10511?size=xsmall",
|
||||
"32x32": "https://aeo.atlassian.net/rest/api/2/universal_avatar/view/type/SD_REQTYPE/avatar/10511?size=medium"
|
||||
}
|
||||
}
|
||||
},
|
||||
"restrictionStatus": "OPEN",
|
||||
"canCreateRequest": true
|
||||
},
|
||||
{
|
||||
"_expands": [
|
||||
"field"
|
||||
],
|
||||
"id": "393",
|
||||
"_links": {
|
||||
"self": "https://aeo.atlassian.net/rest/servicedeskapi/servicedesk/170/requesttype/393"
|
||||
},
|
||||
"name": "Payment Table Validation ",
|
||||
"description": "",
|
||||
"helpText": "",
|
||||
"defaultName": "Payment Table Validation ",
|
||||
"issueTypeId": "10082",
|
||||
"serviceDeskId": "170",
|
||||
"portalId": "170",
|
||||
"groupIds": [],
|
||||
"icon": {
|
||||
"id": "10471",
|
||||
"_links": {
|
||||
"iconUrls": {
|
||||
"48x48": "https://aeo.atlassian.net/rest/api/2/universal_avatar/view/type/SD_REQTYPE/avatar/10471?size=large",
|
||||
"24x24": "https://aeo.atlassian.net/rest/api/2/universal_avatar/view/type/SD_REQTYPE/avatar/10471?size=small",
|
||||
"16x16": "https://aeo.atlassian.net/rest/api/2/universal_avatar/view/type/SD_REQTYPE/avatar/10471?size=xsmall",
|
||||
"32x32": "https://aeo.atlassian.net/rest/api/2/universal_avatar/view/type/SD_REQTYPE/avatar/10471?size=medium"
|
||||
}
|
||||
}
|
||||
},
|
||||
"restrictionStatus": "OPEN",
|
||||
"canCreateRequest": true
|
||||
},
|
||||
{
|
||||
"_expands": [
|
||||
"field"
|
||||
],
|
||||
"id": "277",
|
||||
"_links": {
|
||||
"self": "https://aeo.atlassian.net/rest/servicedeskapi/servicedesk/170/requesttype/277"
|
||||
},
|
||||
"name": "Process Problem",
|
||||
"description": "",
|
||||
"helpText": "",
|
||||
"defaultName": "Process Problem",
|
||||
"issueTypeId": "10079",
|
||||
"serviceDeskId": "170",
|
||||
"portalId": "170",
|
||||
"groupIds": [],
|
||||
"icon": {
|
||||
"id": "10472",
|
||||
"_links": {
|
||||
"iconUrls": {
|
||||
"48x48": "https://aeo.atlassian.net/rest/api/2/universal_avatar/view/type/SD_REQTYPE/avatar/10472?size=large",
|
||||
"24x24": "https://aeo.atlassian.net/rest/api/2/universal_avatar/view/type/SD_REQTYPE/avatar/10472?size=small",
|
||||
"16x16": "https://aeo.atlassian.net/rest/api/2/universal_avatar/view/type/SD_REQTYPE/avatar/10472?size=xsmall",
|
||||
"32x32": "https://aeo.atlassian.net/rest/api/2/universal_avatar/view/type/SD_REQTYPE/avatar/10472?size=medium"
|
||||
}
|
||||
}
|
||||
},
|
||||
"restrictionStatus": "OPEN",
|
||||
"canCreateRequest": true
|
||||
},
|
||||
{
|
||||
"_expands": [
|
||||
"field"
|
||||
],
|
||||
"id": "494",
|
||||
"_links": {
|
||||
"self": "https://aeo.atlassian.net/rest/servicedeskapi/servicedesk/170/requesttype/494"
|
||||
},
|
||||
"name": "Questions",
|
||||
"description": "Request type used by live chat for fallback, only when other request types don't work.",
|
||||
"helpText": "",
|
||||
"defaultName": "Questions",
|
||||
"issueTypeId": "10008",
|
||||
"serviceDeskId": "170",
|
||||
"portalId": "170",
|
||||
"groupIds": [],
|
||||
"icon": {
|
||||
"id": "10466",
|
||||
"_links": {
|
||||
"iconUrls": {
|
||||
"48x48": "https://aeo.atlassian.net/rest/api/2/universal_avatar/view/type/SD_REQTYPE/avatar/10466?size=large",
|
||||
"24x24": "https://aeo.atlassian.net/rest/api/2/universal_avatar/view/type/SD_REQTYPE/avatar/10466?size=small",
|
||||
"16x16": "https://aeo.atlassian.net/rest/api/2/universal_avatar/view/type/SD_REQTYPE/avatar/10466?size=xsmall",
|
||||
"32x32": "https://aeo.atlassian.net/rest/api/2/universal_avatar/view/type/SD_REQTYPE/avatar/10466?size=medium"
|
||||
}
|
||||
}
|
||||
},
|
||||
"restrictionStatus": "OPEN",
|
||||
"canCreateRequest": true
|
||||
},
|
||||
{
|
||||
"_expands": [
|
||||
"field"
|
||||
],
|
||||
"id": "278",
|
||||
"_links": {
|
||||
"self": "https://aeo.atlassian.net/rest/servicedeskapi/servicedesk/170/requesttype/278"
|
||||
},
|
||||
"name": "Recurring Incident",
|
||||
"description": "",
|
||||
"helpText": "",
|
||||
"defaultName": "Recurring Incident",
|
||||
"issueTypeId": "10079",
|
||||
"serviceDeskId": "170",
|
||||
"portalId": "170",
|
||||
"groupIds": [],
|
||||
"icon": {
|
||||
"id": "10507",
|
||||
"_links": {
|
||||
"iconUrls": {
|
||||
"48x48": "https://aeo.atlassian.net/rest/api/2/universal_avatar/view/type/SD_REQTYPE/avatar/10507?size=large",
|
||||
"24x24": "https://aeo.atlassian.net/rest/api/2/universal_avatar/view/type/SD_REQTYPE/avatar/10507?size=small",
|
||||
"16x16": "https://aeo.atlassian.net/rest/api/2/universal_avatar/view/type/SD_REQTYPE/avatar/10507?size=xsmall",
|
||||
"32x32": "https://aeo.atlassian.net/rest/api/2/universal_avatar/view/type/SD_REQTYPE/avatar/10507?size=medium"
|
||||
}
|
||||
}
|
||||
},
|
||||
"restrictionStatus": "OPEN",
|
||||
"canCreateRequest": true
|
||||
},
|
||||
{
|
||||
"_expands": [
|
||||
"field"
|
||||
],
|
||||
"id": "280",
|
||||
"_links": {
|
||||
"self": "https://aeo.atlassian.net/rest/servicedeskapi/servicedesk/170/requesttype/280"
|
||||
},
|
||||
"name": "SolarWinds Alert",
|
||||
"description": "",
|
||||
"helpText": "",
|
||||
"defaultName": "SolarWinds Alert",
|
||||
"issueTypeId": "10085",
|
||||
"serviceDeskId": "170",
|
||||
"portalId": "170",
|
||||
"groupIds": [],
|
||||
"icon": {
|
||||
"id": "10511",
|
||||
"_links": {
|
||||
"iconUrls": {
|
||||
"48x48": "https://aeo.atlassian.net/rest/api/2/universal_avatar/view/type/SD_REQTYPE/avatar/10511?size=large",
|
||||
"24x24": "https://aeo.atlassian.net/rest/api/2/universal_avatar/view/type/SD_REQTYPE/avatar/10511?size=small",
|
||||
"16x16": "https://aeo.atlassian.net/rest/api/2/universal_avatar/view/type/SD_REQTYPE/avatar/10511?size=xsmall",
|
||||
"32x32": "https://aeo.atlassian.net/rest/api/2/universal_avatar/view/type/SD_REQTYPE/avatar/10511?size=medium"
|
||||
}
|
||||
}
|
||||
},
|
||||
"restrictionStatus": "OPEN",
|
||||
"canCreateRequest": true
|
||||
}
|
||||
]
|
||||
}
|
||||
Loading…
Reference in a new issue