Skip to content
This repository was archived by the owner on Jan 1, 2026. It is now read-only.

Commit 33f4991

Browse files
authored
Add schema tests and implement user authentication (#3)
* Enhance .gitattributes for SQL file detection * Fix .gitattributes for SQL file detection and update test file handling * Add schema tests for database table existence verification * Enhance schema tests to verify types, tables, and indexes * Add seed data tests for language existence verification * Implement user authentication procedures and functions, including login attempt logging, 2FA checks, and user registration. * Refactor register_user procedure to handle unique constraint violations for phone numbers and improve error handling * Add GitHub Actions workflows for PR label retrieval and version bump suggestion * Add workflow for creating releases with version bump suggestion * Add permissions section to suggest version bump workflow * Update workflow triggers to limit actions to SQL files in the database directory * Add permissions for pull-requests and statuses in version bump workflow * chore: fix linting issues * feat: enhance version bump summary reporting in workflow * chore: add missing job name for PR labels in version bump workflow * refactor: improve summary reporting format in version bump workflow
1 parent baa2293 commit 33f4991

17 files changed

Lines changed: 392 additions & 95 deletions

.gitattributes

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1 +1,3 @@
11
* text=auto
2+
*.sql linguist-language=SQL linguist-detectable=true
3+
*.test.sql -linguist-language
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
---
2+
name: Create Release
3+
4+
permissions:
5+
contents: write
6+
7+
on:
8+
push:
9+
branches:
10+
- 'main'
11+
paths:
12+
- 'database/**/*.sql'
13+
14+
jobs:
15+
suggest-bump:
16+
uses: ./.github/workflows/suggest-version-bump.yml
17+
18+
create-release:
19+
runs-on: ubuntu-latest
20+
needs: suggest-bump
21+
steps:
22+
- name: Checkout code
23+
uses: actions/checkout@v4
24+
with:
25+
fetch-depth: 0
26+
27+
- name: Create release draft
28+
uses: actions/create-release@v1
29+
with:
30+
tag_name: ${{ needs.suggest-bump.outputs.next_version }}
31+
release_name: ${{ needs.suggest-bump.outputs.next_version }}
32+
draft: false
33+
prerelease: false
34+
env:
35+
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
---
2+
name: Get PR Labels
3+
4+
permissions:
5+
contents: read
6+
statuses: read
7+
pull-requests: read
8+
9+
on:
10+
workflow_call:
11+
outputs:
12+
labels:
13+
description: 'Labels on the pull request'
14+
value: ${{ jobs.get-labels.outputs.labels }}
15+
16+
jobs:
17+
get-labels:
18+
name: Get PR Labels
19+
runs-on: ubuntu-latest
20+
outputs:
21+
labels: ${{ steps.labels.outputs.labels }}
22+
23+
steps:
24+
- name: Checkout code
25+
uses: actions/checkout@v4
26+
with:
27+
fetch-depth: 0
28+
29+
- name: Get PR labels
30+
id: labels
31+
run: |
32+
LABELS=$(gh pr view "${{ github.event.pull_request.number }}" --json labels --jq '[.labels[].name] | join(" ")')
33+
echo "labels=$LABELS" >> "$GITHUB_OUTPUT"
34+
env:
35+
GH_TOKEN: ${{ github.token }}
Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,83 @@
1+
---
2+
name: Suggest Version Bump
3+
4+
permissions:
5+
contents: read
6+
pull-requests: read
7+
statuses: read
8+
9+
on:
10+
pull_request:
11+
branches:
12+
- main
13+
paths:
14+
- 'database/**/*.sql'
15+
workflow_call:
16+
outputs:
17+
next_version:
18+
description: 'The next suggested version'
19+
value: ${{ jobs.suggest-bump.outputs.next_version }}
20+
21+
jobs:
22+
get-labels:
23+
name: Get PR Labels
24+
uses: ./.github/workflows/get-pr-labels.yml
25+
26+
suggest-bump:
27+
name: Suggest Version Bump
28+
needs: get-labels
29+
runs-on: ubuntu-latest
30+
outputs:
31+
next_version: ${{ steps.next_version.outputs.next_version }}
32+
steps:
33+
- name: Checkout code
34+
uses: actions/checkout@v4
35+
with:
36+
fetch-depth: 0
37+
38+
- name: Determine bump type
39+
id: bump
40+
run: |
41+
LABELS="${{ needs.get-labels.outputs.labels }}"
42+
BUMP="patch"
43+
echo "$LABELS" | grep -q 'type: feature' && BUMP="minor"
44+
echo "$LABELS" | grep -q 'type: security' && BUMP="minor"
45+
echo "$LABELS" | grep -q 'type: breaking' && BUMP="major"
46+
echo "bump=$BUMP" >> "$GITHUB_OUTPUT"
47+
48+
- name: Get latest tag
49+
id: latest_tag
50+
run: |
51+
TAG=$(git tag --list 'v*' --sort=-v:refname | head -n1)
52+
echo "tag=$TAG" >> "$GITHUB_OUTPUT"
53+
54+
- name: Calculate next version
55+
id: next_version
56+
run: |
57+
TAG="${{ steps.latest_tag.outputs.tag }}"
58+
BUMP="${{ steps.bump.outputs.bump }}"
59+
if [ -z "$TAG" ]; then
60+
TAG="v0.0.0"
61+
fi
62+
VERSION=$(echo "$TAG" | sed -E 's/^v([0-9]+\.[0-9]+\.[0-9]+).*/\1/')
63+
PRERELEASE=$(echo "$TAG" | sed -nE 's/^v[0-9]+\.[0-9]+\.[0-9]+(-[A-Za-z0-9.-]+)?$/\1/p')
64+
IFS='.' read -r MAJOR MINOR PATCH <<< "$VERSION"
65+
case "$BUMP" in
66+
major) MAJOR=$((MAJOR + 1)); MINOR=0; PATCH=0 ;;
67+
minor) MINOR=$((MINOR + 1)); PATCH=0 ;;
68+
patch) PATCH=$((PATCH + 1)) ;;
69+
esac
70+
if [ -n "$PRERELEASE" ]; then
71+
NEXT_VERSION="v${MAJOR}.${MINOR}.${PATCH}${PRERELEASE}"
72+
else
73+
NEXT_VERSION="v${MAJOR}.${MINOR}.${PATCH}"
74+
fi
75+
echo "next_version=$NEXT_VERSION" >> "$GITHUB_OUTPUT"
76+
77+
- name: Report summary
78+
run: |
79+
{
80+
echo "### 🚀 Suggested Version Bump: **${{ steps.bump.outputs.bump }}**"
81+
echo "#### Latest tag: \`${{ steps.latest_tag.outputs.tag }}\`"
82+
echo "#### Next version: \`${{ steps.next_version.outputs.next_version }}\`"
83+
} >> "$GITHUB_STEP_SUMMARY"

.vscode/settings.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,6 @@
2222
}
2323
],
2424
"triggerTaskOnSave.tasks": {
25-
"Lint SQL File": ["**/*.sql"]
25+
"Lint SQL File": ["database/**/*.sql"]
2626
}
2727
}
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
-- Authenticate a user and log the result
2+
CREATE OR REPLACE FUNCTION authenticate_user(
3+
p_username VARCHAR,
4+
p_password_hash VARCHAR,
5+
p_ip_address INET,
6+
p_user_agent TEXT
7+
) RETURNS BOOLEAN AS $$
8+
DECLARE
9+
v_user_id INTEGER;
10+
BEGIN
11+
-- Attempt to find the user by username and hashed password
12+
SELECT user_id INTO v_user_id
13+
FROM users
14+
WHERE username = p_username AND password_hash = p_password_hash
15+
LIMIT 1;
16+
17+
IF v_user_id IS NOT NULL THEN
18+
-- Successful login: handle and log
19+
CALL handle_successful_login(v_user_id, p_ip_address, p_user_agent);
20+
RETURN TRUE;
21+
ELSE
22+
-- Failed login: log with NULL user_id
23+
CALL log_login_attempt(NULL, p_ip_address, p_user_agent, FALSE);
24+
RETURN FALSE;
25+
END IF;
26+
END;
27+
$$ LANGUAGE plpgsql;

database/functions/disable_2fa.sql

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
-- Disable 2FA for a user
2+
CREATE OR REPLACE FUNCTION disable_2fa(p_user_id INTEGER) RETURNS VOID AS $$
3+
BEGIN
4+
UPDATE user_authentication_methods
5+
SET is_enabled = FALSE, updated_at = NOW()
6+
WHERE user_id = p_user_id;
7+
END;
8+
$$ LANGUAGE plpgsql;
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
-- Get the user's authentication methods secret
2+
CREATE OR REPLACE FUNCTION get_user_authentication_method_secret(p_user_id INTEGER) RETURNS TABLE (method TEXT, secret TEXT) AS $$
3+
BEGIN
4+
RETURN QUERY
5+
SELECT authentication_method, user_authentication_method_secret
6+
FROM user_authentication_methods
7+
WHERE user_id = p_user_id AND is_enabled = TRUE;
8+
END;
9+
$$ LANGUAGE plpgsql;
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
-- Check if the user has 2FA enabled
2+
CREATE OR REPLACE FUNCTION is_2fa_enabled(p_user_id INTEGER) RETURNS BOOLEAN AS $$
3+
DECLARE
4+
v_enabled BOOLEAN;
5+
BEGIN
6+
SELECT is_enabled INTO v_enabled
7+
FROM user_authentication_methods
8+
WHERE user_id = p_user_id;
9+
10+
RETURN COALESCE(v_enabled, FALSE);
11+
END;
12+
$$ LANGUAGE plpgsql;

database/procedures/authentication.sql

Lines changed: 0 additions & 90 deletions
This file was deleted.

0 commit comments

Comments
 (0)