Skip to content
Open
Show file tree
Hide file tree
Changes from 2 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
5 changes: 5 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,11 @@ __pycache__/
*.py[codz]
*$py.class

# Node / frontend
node_modules/
frontend/dist/
frontend/dist-ssr/

# C extensions
*.so

Expand Down
67 changes: 66 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -1 +1,66 @@
# github-agentic-workflows
# github-agentic-workflows

A React + Flask application. The React frontend communicates with a Flask API backend.

## Project Structure

```
.
├── backend/ # Flask API
│ ├── app.py
│ ├── requirements.txt
│ └── test_app.py
└── frontend/ # React (Vite)
├── src/
│ ├── App.jsx
│ └── test/
├── package.json
└── vite.config.js
```

## Getting Started

### Backend

```bash
cd backend
pip install -r requirements.txt
python app.py
```

The Flask API runs on `http://localhost:5000`.

### Frontend

```bash
cd frontend
npm install
npm run dev
```

The React app runs on `http://localhost:5173` and proxies `/api` requests to the Flask backend.

## API Endpoints

| Method | Path | Description |
|--------|------|-------------|
| GET | `/api/health` | Health check |
| GET | `/api/items` | List all items |
| POST | `/api/items` | Create an item (`{"name": "..."}`) |
| DELETE | `/api/items/:id` | Delete an item |

## Running Tests

**Backend**

```bash
cd backend
python -m pytest test_app.py -v
```

**Frontend**

```bash
cd frontend
npm test
```
54 changes: 54 additions & 0 deletions backend/app.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
import os
from flask import Flask, jsonify, request
Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

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

/grumpy

from flask_cors import CORS

app = Flask(__name__)
CORS(app)

# In-memory store for items
items = [
{"id": 1, "name": "Item One"},
{"id": 2, "name": "Item Two"},
]
next_id = 3


@app.route("/api/health", methods=["GET"])
def health():
"""Health check endpoint."""
return jsonify({"status": "ok"})


@app.route("/api/items", methods=["GET"])
def get_items():
"""Return all items."""
return jsonify(items)


@app.route("/api/items", methods=["POST"])
def create_item():
"""Create a new item."""
global next_id
data = request.get_json()
if not data or not data.get("name"):
return jsonify({"error": "name is required"}), 400
item = {"id": next_id, "name": data["name"]}
next_id += 1
items.append(item)
return jsonify(item), 201


@app.route("/api/items/<int:item_id>", methods=["DELETE"])
def delete_item(item_id):
"""Delete an item by id."""
global items
original_len = len(items)
items = [i for i in items if i["id"] != item_id]
if len(items) == original_len:
return jsonify({"error": "item not found"}), 404
return jsonify({"deleted": item_id})


if __name__ == "__main__":
debug = os.environ.get("FLASK_DEBUG", "false").lower() == "true"
app.run(debug=debug, port=5000)
3 changes: 3 additions & 0 deletions backend/requirements.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
flask>=3.1.0
flask-cors>=5.0.0
pytest>=8.0.0
61 changes: 61 additions & 0 deletions backend/test_app.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
import pytest
from app import app, items


@pytest.fixture(autouse=True)
def reset_items():
"""Reset items list and next_id before each test."""
import app as app_module
app_module.items = [
{"id": 1, "name": "Item One"},
{"id": 2, "name": "Item Two"},
]
app_module.next_id = 3
yield


@pytest.fixture
def client():
app.config["TESTING"] = True
with app.test_client() as client:
yield client


def test_health(client):
response = client.get("/api/health")
assert response.status_code == 200
assert response.get_json() == {"status": "ok"}


def test_get_items(client):
response = client.get("/api/items")
assert response.status_code == 200
data = response.get_json()
assert len(data) == 2
assert data[0]["name"] == "Item One"


def test_create_item(client):
response = client.post("/api/items", json={"name": "New Item"})
assert response.status_code == 201
data = response.get_json()
assert data["name"] == "New Item"
assert "id" in data


def test_create_item_missing_name(client):
response = client.post("/api/items", json={})
assert response.status_code == 400
assert "error" in response.get_json()


def test_delete_item(client):
response = client.delete("/api/items/1")
assert response.status_code == 200
assert response.get_json()["deleted"] == 1


def test_delete_item_not_found(client):
response = client.delete("/api/items/999")
assert response.status_code == 404
assert "error" in response.get_json()
24 changes: 24 additions & 0 deletions frontend/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
# Logs
logs
*.log
npm-debug.log*
yarn-debug.log*
yarn-error.log*
pnpm-debug.log*
lerna-debug.log*

node_modules
dist
dist-ssr
*.local

# Editor directories and files
.vscode/*
!.vscode/extensions.json
.idea
.DS_Store
*.suo
*.ntvs*
*.njsproj
*.sln
*.sw?
16 changes: 16 additions & 0 deletions frontend/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
# React + Vite

This template provides a minimal setup to get React working in Vite with HMR and some ESLint rules.

Currently, two official plugins are available:

- [@vitejs/plugin-react](https://github.com/vitejs/vite-plugin-react/blob/main/packages/plugin-react) uses [Oxc](https://oxc.rs)
- [@vitejs/plugin-react-swc](https://github.com/vitejs/vite-plugin-react/blob/main/packages/plugin-react-swc) uses [SWC](https://swc.rs/)

## React Compiler

The React Compiler is not enabled on this template because of its impact on dev & build performances. To add it, see [this documentation](https://react.dev/learn/react-compiler/installation).

## Expanding the ESLint configuration

If you are developing a production application, we recommend using TypeScript with type-aware lint rules enabled. Check out the [TS template](https://github.com/vitejs/vite/tree/main/packages/create-vite/template-react-ts) for information on how to integrate TypeScript and [`typescript-eslint`](https://typescript-eslint.io) in your project.
29 changes: 29 additions & 0 deletions frontend/eslint.config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
import js from '@eslint/js'
import globals from 'globals'
import reactHooks from 'eslint-plugin-react-hooks'
import reactRefresh from 'eslint-plugin-react-refresh'
import { defineConfig, globalIgnores } from 'eslint/config'

export default defineConfig([
globalIgnores(['dist']),
{
files: ['**/*.{js,jsx}'],
extends: [
js.configs.recommended,
reactHooks.configs.flat.recommended,
reactRefresh.configs.vite,
],
languageOptions: {
ecmaVersion: 2020,
globals: globals.browser,
parserOptions: {
ecmaVersion: 'latest',
ecmaFeatures: { jsx: true },
sourceType: 'module',
},
},
rules: {
'no-unused-vars': ['error', { varsIgnorePattern: '^[A-Z_]' }],
},
},
])
13 changes: 13 additions & 0 deletions frontend/index.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<link rel="icon" type="image/svg+xml" href="/favicon.svg" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>frontend</title>
</head>
<body>
<div id="root"></div>
<script type="module" src="/src/main.jsx"></script>
</body>
</html>
Loading