Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 19 additions & 0 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,25 @@ npx prettier --write .
* Prefer functional components and React hooks over class components.
* Keep UI elements consistent with Material-UI standards.

### Making API Calls

* **Always use `apiFetch()` from `helpers.js` instead of direct `fetch()` calls** when making API requests to Sippy's backend.
* `apiFetch()` automatically prepends `REACT_APP_API_URL` to relative URLs, making the code cleaner and handling development/production environments correctly.

```javascript
// ✅ Good - uses apiFetch
import { apiFetch } from '../helpers'

apiFetch('/api/releases')
.then(response => response.json())
.then(data => console.log(data))

// ❌ Bad - manual concatenation
fetch(process.env.REACT_APP_API_URL + '/api/releases')
.then(response => response.json())
.then(data => console.log(data))
```

## General Notes

* Favor clarity and maintainability over cleverness.
Expand Down
12 changes: 0 additions & 12 deletions chat/sippy_agent/web_server.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,6 @@
from typing import List, Dict, Any, Optional
import uvicorn
from fastapi import FastAPI, WebSocket, WebSocketDisconnect, Response
from fastapi.middleware.cors import CORSMiddleware
from prometheus_client import generate_latest, CONTENT_TYPE_LATEST

from .agent import SippyAgent
Expand Down Expand Up @@ -92,7 +91,6 @@ def __init__(self, config: Config, metrics_port: Optional[int] = None):
version="1.0.0",
)
self.websocket_manager = WebSocketManager()
self._setup_middleware()
self._setup_routes()

# Initialize agent info metrics
Expand All @@ -103,16 +101,6 @@ def __init__(self, config: Config, metrics_port: Optional[int] = None):
"persona": config.persona,
})

def _setup_middleware(self):
"""Setup CORS and other middleware."""
self.app.add_middleware(
CORSMiddleware,
allow_origins=["*"], # Configure this for production
allow_credentials=True,
allow_methods=["*"],
allow_headers=["*"],
)

def _setup_routes(self):
"""Setup API routes."""

Expand Down
9 changes: 1 addition & 8 deletions pkg/sippyserver/chatproxy.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,14 +12,7 @@ import (

// rewriteChatPath rewrites /api/chat paths to /chat for the target service
func rewriteChatPath(path string) string {
// Only replace /api/chat when it's followed by end of string or a slash
if path == "/api/chat" {
return "/chat"
}
if strings.HasPrefix(path, "/api/chat/") {
return "/chat" + strings.TrimPrefix(path, "/api/chat")
}
return path
return strings.Replace(path, "/api/chat", "/chat", 1)
}

// ChatProxy handles proxying HTTP and WebSocket requests to the sippy-chat service
Expand Down
7 changes: 4 additions & 3 deletions sippy-ng/src/App.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import { cyan, green, orange, red } from '@mui/material/colors'
import { DarkMode, LightMode, ToggleOff, ToggleOn } from '@mui/icons-material'
import { ErrorBoundary } from 'react-error-boundary'
import {
apiFetch,
findFirstNonGARelease,
getReportStartDate,
getUrlWithoutParams,
Expand Down Expand Up @@ -433,9 +434,9 @@ function App(props) {

const fetchData = () => {
Promise.all([
fetch(process.env.REACT_APP_API_URL + '/api/releases'),
fetch(process.env.REACT_APP_API_URL + '/api/capabilities'),
fetch(process.env.REACT_APP_API_URL + '/api/report_date'),
apiFetch('/api/releases'),
apiFetch('/api/capabilities'),
apiFetch('/api/report_date'),
])
.then(([releases, capabilities, reportDate]) => {
if (releases.status !== 200) {
Expand Down
13 changes: 5 additions & 8 deletions sippy-ng/src/bugs/BugTable.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,7 @@ import {
Tooltip,
Typography,
} from '@mui/material'
import { getTriagesAPIUrl } from '../component_readiness/CompReadyUtils'
import { relativeTime, safeEncodeURIComponent } from '../helpers'
import { apiFetch, relativeTime, safeEncodeURIComponent } from '../helpers'
import Alert from '@mui/material/Alert'
import PropTypes from 'prop-types'
import React, { useEffect } from 'react'
Expand All @@ -26,12 +25,10 @@ export default function BugTable(props) {
const fetchData = () => {
let bugsURL = props.bugsURL
if (!bugsURL || bugsURL.length === 0) {
bugsURL = `${
process.env.REACT_APP_API_URL
}/api/tests/bugs?test=${safeEncodeURIComponent(props.testName)}`
bugsURL = `/api/tests/bugs?test=${safeEncodeURIComponent(props.testName)}`
}

Promise.all([fetch(bugsURL)])
Promise.all([apiFetch(bugsURL)])
.then(([bugs]) => {
if (bugs.status !== 200) {
throw new Error('server returned when fetching bugs' + bugs.status)
Expand All @@ -47,7 +44,7 @@ export default function BugTable(props) {
props.regressionId &&
bugs.length > 0
) {
return fetch(getTriagesAPIUrl())
return apiFetch('/api/component_readiness/triages')
.then((res) => {
if (res.status !== 200) {
throw new Error(
Expand Down Expand Up @@ -96,7 +93,7 @@ export default function BugTable(props) {

const addToTriage = (triage) => {
triage.regressions.push({ id: props.regressionId })
fetch(getTriagesAPIUrl(triage.id), {
apiFetch(`/api/component_readiness/triages/${triage.id}`, {
method: 'PUT',
body: JSON.stringify(triage),
}).then((res) => {
Expand Down
9 changes: 3 additions & 6 deletions sippy-ng/src/bugs/FileBug.js
Original file line number Diff line number Diff line change
Expand Up @@ -21,10 +21,7 @@ import {
} from '@mui/material'
import { AutoAwesome as AutoAwesomeIcon, Close } from '@mui/icons-material'
import { CapabilitiesContext } from '../App'
import {
getBugsAPIUrl,
getTriagesAPIUrl,
} from '../component_readiness/CompReadyUtils'
import { apiFetch } from '../helpers'
import { makeStyles } from '@mui/styles'
import BugButton from './BugButton'
import OneShotChatModal from '../chat/OneShotChatModal'
Expand Down Expand Up @@ -197,7 +194,7 @@ See the [sippy test details|${document.location.href}] for additional context.
affects_versions: formData.affectsVersions,
}

fetch(getBugsAPIUrl(), {
apiFetch('/api/component_readiness/bugs', {
method: 'POST',
body: JSON.stringify(bugData),
})
Expand All @@ -215,7 +212,7 @@ See the [sippy test details|${document.location.href}] for additional context.
type: formData.triageType,
}

return fetch(getTriagesAPIUrl(), {
return apiFetch('/api/component_readiness/triages', {
method: 'POST',
body: JSON.stringify(triageData),
}).then((triageResponse) => {
Expand Down
7 changes: 2 additions & 5 deletions sippy-ng/src/build_clusters/BuildClusterHealthChart.js
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { apiFetch } from '../helpers'
import { Line } from 'react-chartjs-2'
import Alert from '@mui/material/Alert'
import chroma from 'chroma-js'
Expand All @@ -10,11 +11,7 @@ export default function BuildClusterHealthChart(props) {
const [data, setData] = React.useState([])

const fetchData = () => {
fetch(
process.env.REACT_APP_API_URL +
'/api/health/build_cluster/analysis?period=' +
props.period
)
apiFetch('/api/health/build_cluster/analysis?period=' + props.period)
.then((response) => {
if (response.status !== 200) {
throw new Error('server returned ' + response.status)
Expand Down
6 changes: 2 additions & 4 deletions sippy-ng/src/build_clusters/BuildClusterTable.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import { generateClasses } from '../datagrid/utils'
import { Link } from 'react-router-dom'
import { makeStyles } from '@mui/styles'
import { NumberParam, StringParam, useQueryParam } from 'use-query-params'
import { safeEncodeURIComponent, SafeJSONParam } from '../helpers'
import { apiFetch, safeEncodeURIComponent, SafeJSONParam } from '../helpers'
import { withStyles } from '@mui/styles'
import Alert from '@mui/material/Alert'
import GridToolbar from '../datagrid/GridToolbar'
Expand Down Expand Up @@ -134,9 +134,7 @@ function BuildClusterTable(props) {
queryString += '?period=' + safeEncodeURIComponent(period)
}

fetch(
process.env.REACT_APP_API_URL + '/api/health/build_cluster' + queryString
)
apiFetch('/api/health/build_cluster' + queryString)
.then((response) => {
if (response.status !== 200) {
throw new Error('server returned ' + response.status)
Expand Down
17 changes: 4 additions & 13 deletions sippy-ng/src/chat/chatUtils.js
Original file line number Diff line number Diff line change
Expand Up @@ -57,20 +57,11 @@ function getRelativeTime(date) {
return `${diffDays} days ago`
}

// Get WebSocket URL based on current environment
// Get WebSocket URL for the chat proxy
export function getChatWebSocketUrl() {
const protocol = window.location.protocol === 'https:' ? 'wss:' : 'ws:'
const baseUrl = process.env.REACT_APP_CHAT_API_URL || '/api/chat'

let url
if (baseUrl.startsWith('/')) {
url = new URL(baseUrl, window.location.origin)
} else {
url = new URL(baseUrl)
url.protocol = protocol
}

url.pathname = url.pathname.replace(/\/$/, '') + '/stream'
const apiUrl = process.env.REACT_APP_API_URL || window.location.origin
const url = new URL('/api/chat/stream', apiUrl)
url.protocol = url.protocol === 'https:' ? 'wss:' : 'ws:'
return url.toString()
}

Expand Down
8 changes: 3 additions & 5 deletions sippy-ng/src/chat/store/personaSlice.js
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import { apiFetch } from '../../helpers'

/**
* Persona slice - manages available chat personas
*/
Expand All @@ -7,13 +9,9 @@ export const createPersonaSlice = (set, get) => ({
personasError: null,

loadPersonas: () => {
const apiUrl =
process.env.REACT_APP_CHAT_API_URL || window.location.origin + '/api/chat'
const baseUrl = apiUrl.replace(/\/$/, '').replace(/\/stream$/, '')

set({ personasLoading: true, personasError: null })

fetch(`${baseUrl}/personas`)
apiFetch('/api/chat/personas')
.then((response) => {
if (!response.ok) {
throw new Error(`Failed to fetch personas: ${response.statusText}`)
Expand Down
11 changes: 5 additions & 6 deletions sippy-ng/src/chat/store/shareSlice.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { apiFetch, relativeTime } from '../../helpers'
import { createMessage, MESSAGE_TYPES } from '../chatUtils'
import { relativeTime } from '../../helpers'
import { SESSION_TYPES } from './sessionSlice'

/**
Expand Down Expand Up @@ -32,10 +32,9 @@ export const createShareSlice = (set, get) => ({

set({ loadingShared: true })

fetch(
`${process.env.REACT_APP_API_URL}/api/chat/conversations/${conversationId}`,
{ signal: abortController.signal }
)
apiFetch(`/api/chat/conversations/${conversationId}`, {
signal: abortController.signal,
})
.then((response) => {
if (!response.ok) {
return response.json().then(
Expand Down Expand Up @@ -175,7 +174,7 @@ export const createShareSlice = (set, get) => ({
payload.parent_id = activeSession.sharedId || activeSession.parentId
}

fetch(process.env.REACT_APP_API_URL + '/api/chat/conversations', {
apiFetch('/api/chat/conversations', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
Expand Down
18 changes: 8 additions & 10 deletions sippy-ng/src/chat/useSessionRating.js
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { apiFetch } from '../helpers'
import { MESSAGE_TYPES } from './chatUtils'
import { useCallback } from 'react'
import { useSessionActions, useSettings } from './store/useChatStore'
Expand Down Expand Up @@ -74,16 +75,13 @@ export function useSessionRating() {

try {
// Submit to API
const response = await fetch(
process.env.REACT_APP_API_URL + '/api/chat/ratings',
{
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify(payload),
}
)
const response = await apiFetch('/api/chat/ratings', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify(payload),
})

if (!response.ok) {
throw new Error(`HTTP error! status: ${response.status}`)
Expand Down
4 changes: 2 additions & 2 deletions sippy-ng/src/component_readiness/AddRegressionPanel.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { getTriagesAPIUrl } from './CompReadyUtils'
import { apiFetch } from '../helpers'
import { makeStyles } from '@mui/styles'
import { Tab, Tabs } from '@mui/material'
import DialogContent from '@mui/material/DialogContent'
Expand Down Expand Up @@ -76,7 +76,7 @@ export default function AddRegressionPanel({
],
}

fetch(getTriagesAPIUrl(existingTriageId), {
apiFetch(`/api/component_readiness/triages/${existingTriageId}`, {
method: 'PUT',
body: JSON.stringify(updatedTriage),
}).then((response) => {
Expand Down
4 changes: 2 additions & 2 deletions sippy-ng/src/component_readiness/CompReadyEnvCapabilities.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ import {
} from './CompReadyUtils'
import { ComponentReadinessStyleContext } from './ComponentReadiness'
import { CompReadyVarsContext } from './CompReadyVars'
import { escapeRegex, safeEncodeURIComponent } from '../helpers'
import { apiFetch, escapeRegex, safeEncodeURIComponent } from '../helpers'
import { Link } from 'react-router-dom'
import { TableContainer, Tooltip, Typography } from '@mui/material'
import CompCapRow from './CompCapRow'
Expand Down Expand Up @@ -81,7 +81,7 @@ export default function CompReadyEnvCapabilities(props) {
apiCallStr += '&forceRefresh=true'
}

fetch(apiCallStr, { signal: abortController.signal })
apiFetch(apiCallStr, { signal: abortController.signal })
.then((response) => response.json())
.then((data) => {
if (data.code < 200 || data.code >= 300) {
Expand Down
4 changes: 2 additions & 2 deletions sippy-ng/src/component_readiness/CompReadyEnvCapability.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ import {
} from './CompReadyUtils'
import { ComponentReadinessStyleContext } from './ComponentReadiness'
import { CompReadyVarsContext } from './CompReadyVars'
import { escapeRegex, safeEncodeURIComponent } from '../helpers'
import { apiFetch, escapeRegex, safeEncodeURIComponent } from '../helpers'
import { Link } from 'react-router-dom'
import { TableContainer, Tooltip, Typography } from '@mui/material'
import ComponentReadinessToolBar from './ComponentReadinessToolBar'
Expand Down Expand Up @@ -80,7 +80,7 @@ export default function CompReadyEnvCapability(props) {
apiCallStr += '&forceRefresh=true'
}

fetch(apiCallStr, { signal: abortController.signal })
apiFetch(apiCallStr, { signal: abortController.signal })
.then((response) => response.json())
.then((data) => {
if (data.code < 200 || data.code >= 300) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ import {
import { ComponentReadinessStyleContext } from './ComponentReadiness'
import { CompReadyVarsContext } from './CompReadyVars'
import { Link } from 'react-router-dom'
import { safeEncodeURIComponent } from '../helpers'
import { apiFetch, safeEncodeURIComponent } from '../helpers'
import { TableContainer, Tooltip, Typography } from '@mui/material'
import CompCapTestRow from './CompCapTestRow'
import ComponentReadinessToolBar from './ComponentReadinessToolBar'
Expand Down Expand Up @@ -133,7 +133,7 @@ export default function CompReadyEnvCapabilityTest(props) {
apiCallStr += '&forceRefresh=true'
}

fetch(apiCallStr, { signal: abortController.signal })
apiFetch(apiCallStr, { signal: abortController.signal })
.then((response) => response.json())
.then((data) => {
if (data.code < 200 || data.code >= 300) {
Expand Down
2 changes: 1 addition & 1 deletion sippy-ng/src/component_readiness/CompReadyUtils.js
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,7 @@ export const jiraUrlPrefix = 'https://issues.redhat.com/browse/'

// Make one place to create the Component Readiness api call
export function getAPIUrl(endpoint) {
return `${process.env.REACT_APP_API_URL}/api/${endpoint}`
return `/api/${endpoint}`
}

export function getCRMainAPIUrl() {
Expand Down
Loading