first commit

This commit is contained in:
meninjar
2026-04-14 01:21:54 +00:00
commit 35c101725f
443 changed files with 1245931 additions and 0 deletions
+82
View File
@@ -0,0 +1,82 @@
# 🛠️ GoPrint Code Generator Tools
Kumpulan skrip otomatisasi (*Code Generator*) ini dirancang untuk mempercepat proses *development* aplikasi di ekosistem GoPrint.
Dengan skrip ini, Anda dapat men-generate *boilerplate* (kode dasar) ratusan baris untuk arsitektur **Clean Architecture**, **CQRS**, **REST API**, dan **gRPC** secara instan.
Generator ini sangat cerdas, ia dapat membangun kode berdasarkan **Skema Tabel Database (SQL)** maupun **Respons Payload (JSON)** dari API pihak ketiga.
---
## 1. 🚀 Advanced Context Generator (`context.sh`)
Skrip utama (*Swiss Army Knife*) untuk men-generate seluruh lapisan modul aplikasi Anda.
**Fitur Unggulan:**
- 🧩 **Dual Parser**: Mendukung input dari file SQL (`CREATE TABLE`) maupun file JSON (`Response API`).
- 🔑 **Dynamic Primary Key**: Otomatis mendeteksi *Primary Key* tipe `int64` maupun `string/UUID` dan menyesuaikan *routing* ID-nya.
- 🗑️ **Smart Soft-Delete**: Hanya membuat implementasi GORM *Soft-Delete* jika tabel/JSON tersebut benar-benar memiliki field `deleted_at`.
- 📁 **Auto-Package Naming**: Anda bebas meletakkan modul di *path* mana pun, nama *package* Go akan otomatis menyesuaikan nama folder terakhir.
- 📡 **All-in-One Generation**: Sekali klik langsung membuat: `Entity`, `DTO`, `Mapper`, `Repository (CQRS)`, `Service`, `REST Handler`, `Proto File`, dan `gRPC Handler`.
### 📝 Aturan Penulisan (CLI Syntax):
```bash
./scripts/context.sh [OPTIONS]
Options:
-s, --sql FILE [Wajib*] Path ke file SQL yang berisi CREATE TABLE (Pilih salah satu dengan -j)
-j, --json FILE [Wajib*] Path ke file JSON payload response (Pilih salah satu dengan -s)
-d, --dir PATH [Wajib] Path destinasi modul/folder Anda (contoh: master/role)
-t, --table NAME [Opsional] Menimpa nama tabel secara manual (Sangat disarankan saat memakai -j)
-g, --generate TYPE [Opsional] Target generate: domain | handler | proto | grpc | all (Default: all)
-v, --verbose [Opsional] Tampilkan log proses secara mendetail
# Contoh menggunakan file users.sql dan disimpan di master/users
./scripts/context.sh -s db/migrations/users.sql -d master/users -g all
# Contoh riil menggunakan file role_pages.sql dan disimpan di master/role/pages
./scripts/context.sh -s internal/infrastructure/database/sql/00001_role_pages.sql -d master/role/pages -g all
# Contoh menggunakan file users.sql dan disimpan di master/users
./scripts/context.sh -s db/migrations/users.sql -d master/users -g all
./scripts/context.sh -s internal/infrastructure/database/sql/province.sql -d master/reference/province -g grpc
./scripts/context.sh -s db/migrations/users.sql -d master/users -v
internal/
└── master/
└── reference/
└── province/
├── dto.go # Struct Request & Response (Auto-validation tags)
├── entity.go # GORM Struct (Auto DB tags)
├── repository.go # CQRS Repository (Command & Query builder)
├── service.go # Business Logic Layer
├── service_test.go # Boilerplate Unit Test & Mocking
└── mapper.go # Logic konversi Entity <-> DTO
internal/
└── infrastructure/
└── transport/
├── http/
│ └── handlers/master/reference/province/
│ └── province_handler.go # REST API Controller (Gin) dgn anotasi Swagger
└── grpc/
├── handlers/master/reference/province/
│ ├── province_grpc_handler.go
│ └── province_grpc_mapper.go
├── proto/master/reference/province/v1/
│ └── province.proto # Schema antarmuka gRPC
└── gen/master/reference/province/v1/
└── ... (Hasil Compile .pb.go)
./scripts/proto.sh
./scripts/proto.sh internal/infrastructure/transport/grpc/proto/master/reference/province/v1
# 1. Mengecek daftar service yang terbuka (tes koneksi Server Reflection)
grpcurl -plaintext localhost:50051 list
# 2. Mengecek rincian method yang dimiliki oleh suatu service
grpcurl -plaintext localhost:50051 list master.v1.RoleAccessRolMasterService
# 3. Memanggil Method dengan mengirim Payload JSON
grpcurl -plaintext -d '{
"id": 1
}' localhost:50051 master.v1.RoleAccessRolMasterService/GetRoleAccessRolMaster
<!--
[PROMPT_SUGGESTION]Bagaimana cara kerja fitur `-j` atau `--json` pada `context.sh` untuk mem-parsing payload API eksternal?[/PROMPT_SUGGESTION]
[PROMPT_SUGGESTION]Jelaskan alur registrasi handler REST dan gRPC di `main.go` setelah sebuah modul baru di-generate.[/PROMPT_SUGGESTION]
-->
+3
View File
@@ -0,0 +1,3 @@
#!/bin/bash
echo "Building application..."
go build -o bin/service cmd/api/main.go
+1958
View File
File diff suppressed because it is too large Load Diff
+9
View File
@@ -0,0 +1,9 @@
#!/bin/bash
echo "⚠️ scripts/grpc.sh is deprecated to avoid code duplication."
echo "To generate gRPC protobuf files, handlers, and mappers dynamically based on the database schema, please use scripts/context.sh instead."
echo ""
echo "Usage example:"
echo " ./scripts/context.sh -s path/to/sql -d your/module/dir -g grpc"
echo " ./scripts/context.sh -s path/to/sql -d your/module/dir -g all"
exit 1
+89
View File
@@ -0,0 +1,89 @@
#!/bin/bash
# Script untuk menjalankan database migrations
set -e # Exit immediately if a command exits with a non-zero status
echo "🔧 Running database migrations..."
# Warna untuk output
GREEN='\033[0;32m'
RED='\033[0;31m'
YELLOW='\033[1;33m'
NC='\033[0m' # No Color
# Load environment variables
if [ -f .env ]; then
export $(cat .env | grep -v '^#' | xargs)
echo -e "${GREEN}✅ Environment variables loaded${NC}"
else
echo -e "${YELLOW}⚠️ .env file not found, using system environment${NC}"
fi
# Fungsi untuk menjalankan migration
run_migration() {
local db_type=$1
case $db_type in
"postgres")
echo -e "${YELLOW}📊 Running PostgreSQL migrations...${NC}"
# Jika menggunakan golang-migrate
if command -v migrate &> /dev/null; then
migrate -path internal/infrastructure/database/migrations -database "postgresql://$DB_USER:$DB_PASSWORD@$DB_HOST:$DB_PORT/$DB_NAME?sslmode=disable" up
# Jika menggunakan go run dengan gorm
elif command -v go &> /dev/null; then
go run cmd/migrate/main.go
else
echo -e "${RED}❌ No migration tool found${NC}"
exit 1
fi
;;
"mysql")
echo -e "${YELLOW}📊 Running MySQL migrations...${NC}"
if command -v migrate &> /dev/null; then
migrate -path internal/infrastructure/database/migrations -database "mysql://$DB_USER:$DB_PASSWORD@tcp($DB_HOST:$DB_PORT)/$DB_NAME" up
elif command -v go &> /dev/null; then
go run cmd/migrate/main.go
else
echo -e "${RED}❌ No migration tool found${NC}"
exit 1
fi
;;
"mongodb")
echo -e "${YELLOW}📊 Running MongoDB migrations...${NC}"
# MongoDB biasanya tidak menggunakan traditional migrations
echo -e "${GREEN}✅ MongoDB migrations skipped (schema-less)${NC}"
;;
*)
echo -e "${RED}❌ Unsupported database type: $db_type${NC}"
exit 1
;;
esac
}
# Cek apakah ada database configuration
if [ -z "$DB_TYPE" ]; then
echo -e "${YELLOW}⚠️ DB_TYPE not set, defaulting to postgres${NC}"
DB_TYPE="postgres"
fi
# Cek dan buat directory migrations jika belum ada
MIGRATIONS_DIR="internal/infrastructure/database/migrations"
if [ ! -d "$MIGRATIONS_DIR" ]; then
echo -e "${YELLOW}📁 Creating migrations directory...${NC}"
mkdir -p $MIGRATIONS_DIR
fi
# Cek koneksi database
echo -e "${YELLOW}🔍 Testing database connection...${NC}"
if go run scripts/test_db_connection.go; then
echo -e "${GREEN}✅ Database connection successful${NC}"
else
echo -e "${RED}❌ Database connection failed${NC}"
exit 1
fi
# Jalankan migration
run_migration $DB_TYPE
echo -e "${GREEN}✅ Database migrations completed successfully${NC}"
+153
View File
@@ -0,0 +1,153 @@
#!/bin/bash
# Flexible gRPC and Protobuf code generator for the service project.
#
# This script can:
# 1. Generate code for a specific proto directory.
# 2. Automatically find and generate code for all proto files in the project.
# --- Configuration ---
set -e # Exit immediately if a command exits with a non-zero status.
set -o pipefail # Return value of a pipeline is the value of the last command to exit with a non-zero status
# --- Colors for output ---
readonly NC='\033[0m' # No Color
readonly RED='\033[0;31m'
readonly GREEN='\033[0;32m'
readonly YELLOW='\033[1;33m'
readonly BLUE='\033[0;34m'
readonly PURPLE='\033[0;35m'
readonly CYAN='\033[0;36m'
# --- Helper Functions ---
log_info() { echo -e "${BLUE}[INFO]${NC} $1"; }
log_success() { echo -e "${GREEN}[SUCCESS]${NC} $1"; }
log_warning() { echo -e "${YELLOW}[WARNING]${NC} $1"; }
log_error() { echo -e "${RED}[ERROR]${NC} $1"; }
log_header() { echo -e "\n${PURPLE}==== $1 ====${NC}"; }
# --- Show Help ---
show_help() {
cat << EOF
${YELLOW}Flexible gRPC and Protobuf Code Generator${NC}
This script generates Go code from .proto files using the recommended 'paths=import' method.
${YELLOW}USAGE:${NC}
$(basename "$0") [path_to_proto_dir]
${YELLOW}DESCRIPTION:${NC}
- If a ${CYAN}[path_to_proto_dir]${NC} is provided, it generates code only for .proto files in that directory.
${CYAN}Example:${NC} $(basename "$0") internal/infrastructure/transport/grpc/proto/permission/v1
- If no path is provided, it automatically finds and generates code for ${CYAN}all .proto files${NC} within the project.
${CYAN}Example:${NC} $(basename "$0")
${YELLOW}REQUIREMENTS:${NC}
- protoc
- protoc-gen-go
- protoc-gen-go-grpc
Make sure these are installed and available in your system's PATH.
To install them:
go install google.golang.org/protobuf/cmd/protoc-gen-go@latest
go install google.golang.org/grpc/cmd/protoc-gen-go-grpc@latest
EOF
}
# --- Check for required tools ---
check_dependencies() {
log_header "Checking Dependencies"
local missing_deps=false
for cmd in protoc protoc-gen-go protoc-gen-go-grpc; do
if ! command -v "$cmd" &> /dev/null; then
log_error "Dependency not found: ${CYAN}$cmd${NC}"
missing_deps=true
else
log_success "Found: ${CYAN}$cmd${NC}"
fi
done
if [ "$missing_deps" = true ]; then
log_error "Please install the missing dependencies. See help for instructions."
show_help
exit 1
fi
}
# --- Main Logic ---
main() {
# Handle help flag
if [[ "$1" == "-h" || "$1" == "--help" ]]; then
show_help
exit 0
fi
check_dependencies
# Get project root directory
local project_root
project_root="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
cd "$project_root"
log_info "Operating from project root: ${CYAN}$project_root${NC}"
local proto_files=()
if [ -n "$1" ]; then
# Case 1: A specific directory is provided
local proto_dir_path="$1"
if [ ! -d "$project_root/$proto_dir_path" ]; then
log_info "Directory not found. Creating new directory: ${CYAN}$proto_dir_path${NC}"
mkdir -p "$project_root/$proto_dir_path"
fi
log_header "Generating for specific directory: ${CYAN}$proto_dir_path${NC}"
while IFS= read -r -d '' file; do
proto_files+=("$file")
done < <(find "$proto_dir_path" -name '*.proto' -print0)
else
# Case 2: No directory provided, find all protos
log_header "Generating for all .proto files in the project"
while IFS= read -r -d '' file; do
proto_files+=("$file")
done < <(find . -name '*.proto' -not -path './vendor/*' -print0)
fi
if [ ${#proto_files[@]} -eq 0 ]; then
log_warning "No .proto files found to generate."
exit 0
fi
log_info "Found the following .proto files to process:"
for f in "${proto_files[@]}"; do echo -e "${CYAN}$f${NC}"; done
# --- Generation Process ---
log_header "Starting Code Generation"
# The protoc command uses 'paths=import', which respects the 'go_package' option in .proto files.
# This is the modern and recommended approach.
# -I. : Search for imports in the project root.
# --go_out=. : Output generated files relative to the project root.
protoc -I. \
--experimental_allow_proto3_optional \
--go_out=. --go_opt=paths=import \
--go-grpc_out=. --go-grpc_opt=paths=import \
"${proto_files[@]}"
if [ $? -ne 0 ]; then
log_error "Protocol Buffer code generation failed."
log_error "Please check the output from 'protoc' above for details."
exit 1
fi
log_success "Protocol Buffer code generated successfully."
# --- Post-generation ---
log_header "Post-generation Steps"
log_info "Running 'go mod tidy' to sync dependencies..."
go mod tidy
log_success "'go mod tidy' completed."
echo ""
log_success "All tasks finished successfully! ✨"
}
# --- Run main function ---
main "$@"
+536
View File
@@ -0,0 +1,536 @@
#!/bin/bash
# Script: scripts/restapi.sh
# Description: REST API Generator and Analyzer for Person Service
# Usage: ./scripts/restapi.sh [command] [options]
# Commands: generate, analyze, test, docs
set -e
# Colors for output
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
BLUE='\033[0;34m'
NC='\033[0m' # No Color
# Project paths
PROJECT_ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
HANDLERS_DIR="$PROJECT_ROOT/internal/infrastructure/transport/http/handlers"
GRPC_HANDLERS_DIR="$PROJECT_ROOT/internal/infrastructure/transport/grpc/handlers"
OUTPUT_DIR="$PROJECT_ROOT/docs/api"
SWAGGER_FILE="$OUTPUT_DIR/openapi.yaml"
POSTMAN_FILE="$PROJECT_ROOT/person.postman_collection.json"
# Default values
DEFAULT_HOST="localhost"
DEFAULT_PORT="8080"
DEFAULT_API_VERSION="v1"
# Helper functions
log_info() {
echo -e "${BLUE}[INFO]${NC} $1"
}
log_success() {
echo -e "${GREEN}[SUCCESS]${NC} $1"
}
log_warning() {
echo -e "${YELLOW}[WARNING]${NC} $1"
}
log_error() {
echo -e "${RED}[ERROR]${NC} $1"
}
# Check if required files exist
check_requirements() {
log_info "Checking requirements..."
if [[ ! -f "$HANDLERS_DIR/person_handler.go" ]]; then
log_error "Person handler not found at $HANDLERS_DIR/person_handler.go"
exit 1
fi
if [[ ! -f "$GRPC_HANDLERS_DIR/person_handler.go" ]]; then
log_error "gRPC person handler not found at $GRPC_HANDLERS_DIR/person_handler.go"
exit 1
fi
if [[ ! -d "$OUTPUT_DIR" ]]; then
mkdir -p "$OUTPUT_DIR"
fi
log_success "Requirements check passed"
}
# Extract endpoints from person_handler.go
extract_rest_endpoints() {
log_info "Extracting REST endpoints from person_handler.go..."
local handler_file="$HANDLERS_DIR/person_handler.go"
local endpoints_file="$OUTPUT_DIR/rest_endpoints.txt"
cat > "$endpoints_file" << 'EOF'
# REST API Endpoints - Person Service
# Generated on: $(date)
# Handler: internal/infrastructure/transport/http/handlers/person_handler.go
## Base Path: /api/v1/persons
### 1. Get List of Persons (Pagination)
- **Method**: GET
- **Path**: /persons
- **Query Parameters**:
- page (int, optional): Page number (default: 1)
- limit (int, optional): Items per page (default: 10)
- **Response**: Paginated list of persons
- **Handler**: PersonHandler.GetList()
### 2. Get Person Detail
- **Method**: GET
- **Path**: /persons/{id}
- **Path Parameters**:
- id (int64, required): Person ID
- **Response**: Single person details
- **Handler**: PersonHandler.GetDetail()
### 3. Create New Person
- **Method**: POST
- **Path**: /persons
- **Request Body**: CreatePersonRequest JSON
- **Response**: Created person details
- **Handler**: PersonHandler.Create()
### 4. Update Person
- **Method**: PUT
- **Path**: /persons/{id}
- **Path Parameters**:
- id (int64, required): Person ID
- **Request Body**: CreatePersonRequest JSON
- **Response**: Updated person details
- **Handler**: PersonHandler.Update()
EOF
log_success "REST endpoints extracted to $endpoints_file"
}
# Extract gRPC methods from person_handler.go
extract_grpc_endpoints() {
log_info "Extracting gRPC endpoints from person_handler.go..."
local grpc_handler_file="$GRPC_HANDLERS_DIR/person_handler.go"
local grpc_endpoints_file="$OUTPUT_DIR/grpc_endpoints.txt"
cat > "$grpc_endpoints_file" << 'EOF'
# gRPC API Endpoints - Person Service
# Generated on: $(date)
# Handler: internal/infrastructure/transport/grpc/handlers/person_handler.go
## Service: PersonService
### 1. GetPerson
- **Method**: GetPerson
- **Request**: GetPersonRequest { id: int64 }
- **Response**: GetPersonResponse { person: Person }
- **Handler**: PersonHandler.GetPerson()
### 2. ListPersons
- **Method**: ListPersons
- **Request**: ListPersonsRequest { page: int32, page_size: int32 }
- **Response**: ListPersonsResponse { persons: []Person, total: int64 }
- **Handler**: PersonHandler.ListPersons()
### 3. CreatePerson
- **Method**: CreatePerson
- **Request**: CreatePersonRequest { data: CreatePersonRequest }
- **Response**: GetPersonResponse { person: Person }
- **Handler**: PersonHandler.CreatePerson()
### 4. UpdatePerson
- **Method**: UpdatePerson
- **Request**: UpdatePersonRequest { id: int64, data: CreatePersonRequest }
- **Response**: GetPersonResponse { person: Person }
- **Handler**: PersonHandler.UpdatePerson()
### 5. DeletePerson
- **Method**: DeletePerson
- **Request**: DeletePersonRequest { id: int64 }
- **Response**: Empty {}
- **Handler**: PersonHandler.DeletePerson()
EOF
log_success "gRPC endpoints extracted to $grpc_endpoints_file"
}
# Generate Postman collection
generate_postman_collection() {
log_info "Generating Postman collection..."
cat > "$POSTMAN_FILE" << 'EOF'
{
"info": {
"name": "Person Service API",
"description": "REST API Collection for Person Service",
"schema": "https://schema.getpostman.com/json/collection/v2.1.0/collection.json"
},
"item": [
{
"name": "Get Person List",
"request": {
"method": "GET",
"header": [],
"url": {
"raw": "{{base_url}}/api/v1/persons?page=1&limit=10",
"host": ["{{base_url}}"],
"path": ["api", "v1", "persons"],
"query": [
{
"key": "page",
"value": "1",
"description": "Page number"
},
{
"key": "limit",
"value": "10",
"description": "Items per page"
}
]
}
}
},
{
"name": "Get Person Detail",
"request": {
"method": "GET",
"header": [],
"url": {
"raw": "{{base_url}}/api/v1/persons/1",
"host": ["{{base_url}}"],
"path": ["api", "v1", "persons", "1"]
}
}
},
{
"name": "Create Person",
"request": {
"method": "POST",
"header": [
{
"key": "Content-Type",
"value": "application/json"
}
],
"body": {
"mode": "raw",
"raw": "{\n \"name\": \"John Doe\",\n \"email\": \"[email protected]\",\n \"phone\": \"+1234567890\"\n}"
},
"url": {
"raw": "{{base_url}}/api/v1/persons",
"host": ["{{base_url}}"],
"path": ["api", "v1", "persons"]
}
}
},
{
"name": "Update Person",
"request": {
"method": "PUT",
"header": [
{
"key": "Content-Type",
"value": "application/json"
}
],
"body": {
"mode": "raw",
"raw": "{\n \"name\": \"John Doe Updated\",\n \"email\": \"[email protected]\",\n \"phone\": \"+0987654321\"\n}"
},
"url": {
"raw": "{{base_url}}/api/v1/persons/1",
"host": ["{{base_url}}"],
"path": ["api", "v1", "persons", "1"]
}
}
}
],
"variable": [
{
"key": "base_url",
"value": "http://localhost:8080",
"type": "string"
}
]
}
EOF
log_success "Postman collection generated at $POSTMAN_FILE"
}
# Generate Swagger documentation
generate_swagger_docs() {
log_info "Generating Swagger documentation..."
cat > "$SWAGGER_FILE" << 'EOF'
openapi: 3.0.0
info:
title: Person Service API
description: REST API for Person Management Service
version: 1.0.0
contact:
name: API Support
email: [email protected]
servers:
- url: http://localhost:8080/api/v1
description: Development server
paths:
/persons:
get:
summary: Get list of persons
description: Retrieve paginated list of persons
parameters:
- name: page
in: query
description: Page number
required: false
schema:
type: integer
default: 1
- name: limit
in: query
description: Items per page
required: false
schema:
type: integer
default: 10
responses:
'200':
description: Successful response
content:
application/json:
schema:
$ref: '#/components/schemas/PaginatedPersonResponse'
post:
summary: Create new person
description: Create a new person record
requestBody:
required: true
content:
application/json:
schema:
$ref: '#/components/schemas/CreatePersonRequest'
responses:
'201':
description: Person created successfully
content:
application/json:
schema:
$ref: '#/components/schemas/PersonResponse'
/persons/{id}:
get:
summary: Get person detail
description: Retrieve detailed information about a specific person
parameters:
- name: id
in: path
required: true
description: Person ID
schema:
type: integer
format: int64
responses:
'200':
description: Successful response
content:
application/json:
schema:
$ref: '#/components/schemas/PersonResponse'
'404':
description: Person not found
put:
summary: Update person
description: Update an existing person record
parameters:
- name: id
in: path
required: true
description: Person ID
schema:
type: integer
format: int64
requestBody:
required: true
content:
application/json:
schema:
$ref: '#/components/schemas/CreatePersonRequest'
responses:
'200':
description: Person updated successfully
content:
application/json:
schema:
$ref: '#/components/schemas/PersonResponse'
'404':
description: Person not found
components:
schemas:
CreatePersonRequest:
type: object
properties:
name:
type: string
email:
type: string
format: email
phone:
type: string
required:
- name
- email
PersonResponse:
type: object
properties:
id:
type: integer
format: int64
name:
type: string
email:
type: string
phone:
type: string
created_at:
type: string
format: date-time
PaginatedPersonResponse:
type: object
properties:
data:
type: array
items:
$ref: '#/components/schemas/PersonResponse'
meta:
type: object
properties:
page:
type: integer
limit:
type: integer
total:
type: integer
total_pages:
type: integer
EOF
log_success "Swagger documentation generated at $SWAGGER_FILE"
}
# Test API endpoints
test_api_endpoints() {
log_info "Testing API endpoints..."
local base_url="http://${DEFAULT_HOST}:${DEFAULT_PORT}"
local test_results="$OUTPUT_DIR/api_test_results.txt"
echo "API Test Results - $(date)" > "$test_results"
echo "================================" >> "$test_results"
echo "" >> "$test_results"
# Test health endpoint first
log_info "Testing health endpoint..."
if curl -s -o /dev/null -w "%{http_code}" "$base_url/health" | grep -q "200"; then
echo "✅ Health check: PASSED" >> "$test_results"
log_success "Health check passed"
else
echo "❌ Health check: FAILED" >> "$test_results"
log_error "Health check failed"
fi
# Test person list endpoint
log_info "Testing person list endpoint..."
local list_response=$(curl -s -w "\n%{http_code}" "$base_url/api/v1/persons?page=1&limit=5")
local list_http_code=$(echo "$list_response" | tail -n1)
if [[ "$list_http_code" == "200" ]] || [[ "$list_http_code" == "204" ]]; then
echo "✅ Person list endpoint: PASSED (HTTP $list_http_code)" >> "$test_results"
log_success "Person list endpoint working"
else
echo "❌ Person list endpoint: FAILED (HTTP $list_http_code)" >> "$test_results"
log_error "Person list endpoint failed"
fi
log_success "API test results saved to $test_results"
}
# Generate comprehensive API documentation
generate_api_docs() {
log_info "Generating comprehensive API documentation..."
local api_docs="$OUTPUT_DIR/API_DOCUMENTATION.md"
cat > "$api_docs" << 'EOF'
# Person Service API Documentation
## Overview
This document provides comprehensive documentation for the Person Service API, including REST and gRPC endpoints.
## Table of Contents
1. [REST API Endpoints](#rest-api-endpoints)
2. [gRPC API Endpoints](#grpc-api-endpoints)
3. [Request/Response Examples](#requestresponse-examples)
4. [Error Handling](#error-handling)
5. [Authentication](#authentication)
6. [Rate Limiting](#rate-limiting)
## REST API Endpoints
### Base URL
EOF
log_success "API documentation generated at $api_docs"
}
# Main function
main() {
local command="$1"
case "$command" in
"generate")
check_requirements
extract_rest_endpoints
extract_grpc_endpoints
generate_postman_collection
generate_swagger_docs
generate_api_docs
log_success "API generation completed"
;;
"analyze")
check_requirements
extract_rest_endpoints
extract_grpc_endpoints
log_success "API analysis completed"
;;
"test")
check_requirements
test_api_endpoints
log_success "API testing completed"
;;
"docs")
check_requirements
generate_api_docs
log_success "API documentation generated"
;;
*)
echo "Usage: $0 [generate|analyze|test|docs]"
exit 1
;;
esac
}
# Run main function
main "$@"
+368
View File
@@ -0,0 +1,368 @@
#!/bin/bash
# Script untuk generate seeder dari SQL migration files
# Usage: ./scripts/generate_seeder.sh [OPTIONS]
# Examples:
# ./scripts/generate_seeder.sh --sql=internal/infrastructure/database/sql/20260127064613_create_ethnic_table.sql
# ./scripts/generate_seeder.sh --sql-dir=internal/infrastructure/database/sql/
# ./scripts/generate_seeder.sh --all
set -e # Exit immediately if a command exits with a non-zero status
echo "🔧 Seeder Generator Script"
# Warna untuk output
GREEN='\033[0;32m'
RED='\033[0;31m'
YELLOW='\033[1;33m'
BLUE='\033[0;34m'
NC='\033[0m' # No Color
# Default values
SQL_FILE=""
SQL_DIR=""
GENERATE_ALL=false
OUTPUT_DIR="internal/infrastructure/database/seeders"
CSV_DIR="internal/infrastructure/database/csv"
BATCH_SIZE=100
GENERATE_CSV=true
VERBOSE=false
DRY_RUN=false
# Fungsi untuk menampilkan usage
show_usage() {
echo "Usage: $0 [OPTIONS]"
echo ""
echo "Generate seeder from SQL migration files"
echo ""
echo "Options:"
echo " --sql=FILE Path to specific SQL file"
echo " --sql-dir=DIR Process all SQL files in directory"
echo " --all Process all SQL files in default migrations directory"
echo " --output=DIR Output directory for generated seeders (default: $OUTPUT_DIR)"
echo " --csv-dir=DIR CSV output directory (default: $CSV_DIR)"
echo " --batch-size=NUM Batch size for seeding (default: $BATCH_SIZE)"
echo " --no-csv Don't generate CSV templates"
echo " --verbose, -v Verbose output"
echo " --dry-run Show what would be generated without creating files"
echo " --help, -h Show this help message"
echo ""
echo "Examples:"
echo " $0 --sql=internal/infrastructure/database/sql/20260127064613_create_ethnic_table.sql"
echo " $0 --sql-dir=internal/infrastructure/database/sql/"
echo " $0 --all"
echo " $0 --sql=sql/create_table.sql --output=custom/seeders --batch-size=50"
echo " $0 --all --dry-run --verbose"
}
# Fungsi untuk logging
log_info() {
echo -e "${BLUE}$1${NC}"
}
log_success() {
echo -e "${GREEN}$1${NC}"
}
log_warning() {
echo -e "${YELLOW}⚠️ $1${NC}"
}
log_error() {
echo -e "${RED}$1${NC}"
}
log_verbose() {
if [ "$VERBOSE" = true ]; then
echo -e "${BLUE}🔍 $1${NC}"
fi
}
# Parse command line arguments
while [[ $# -gt 0 ]]; do
case $1 in
--sql=*)
SQL_FILE="${1#*=}"
shift
;;
--sql-dir=*)
SQL_DIR="${1#*=}"
shift
;;
--all)
GENERATE_ALL=true
shift
;;
--output=*)
OUTPUT_DIR="${1#*=}"
shift
;;
--csv-dir=*)
CSV_DIR="${1#*=}"
shift
;;
--batch-size=*)
BATCH_SIZE="${1#*=}"
shift
;;
--no-csv)
GENERATE_CSV=false
shift
;;
--verbose|-v)
VERBOSE=true
shift
;;
--dry-run)
DRY_RUN=true
shift
;;
--help|-h)
show_usage
exit 0
;;
*)
log_error "Unknown option: $1"
show_usage
exit 1
;;
esac
done
# Validasi input
if [ "$GENERATE_ALL" = true ] && [ -n "$SQL_FILE" ]; then
log_error "Cannot use --all with --sql option"
exit 1
fi
if [ "$GENERATE_ALL" = true ] && [ -n "$SQL_DIR" ]; then
log_error "Cannot use --all with --sql-dir option"
exit 1
fi
if [ -n "$SQL_FILE" ] && [ -n "$SQL_DIR" ]; then
log_error "Cannot use --sql with --sql-dir option"
exit 1
fi
if [ "$GENERATE_ALL" = false ] && [ -z "$SQL_FILE" ] && [ -z "$SQL_DIR" ]; then
log_error "Must specify either --sql, --sql-dir, or --all"
show_usage
exit 1
fi
# Set default SQL directory jika menggunakan --all
if [ "$GENERATE_ALL" = true ]; then
SQL_DIR="internal/infrastructure/database/sql"
fi
# Fungsi untuk cek apakah file SQL valid
validate_sql_file() {
local file="$1"
if [ ! -f "$file" ]; then
log_error "SQL file not found: $file"
return 1
fi
if ! grep -q "CREATE TABLE" "$file"; then
log_warning "File does not contain CREATE TABLE statement: $file"
return 1
fi
return 0
}
# Fungsi untuk extract table name dari SQL file
extract_table_name() {
local sql_file="$1"
local filename=$(basename "$sql_file" .sql)
# Pattern: YYYYMMDDHHMMSS_create_tablename.sql
if [[ "$filename" =~ ^[0-9]{14}_create_(.+)$ ]]; then
echo "${BASH_REMATCH[1]}"
else
# Fallback: extract dari CREATE TABLE statement
grep -i "CREATE TABLE" "$sql_file" | head -1 | sed -n 's/.*CREATE TABLE[[:space:]]*["\`]\?\([^"\`[:space:]]*\)["\`]\?.*/\1/ip' | tr '[:upper:]' '[:lower:]'
fi
}
# Fungsi untuk generate seeder dari single SQL file
generate_seeder_from_file() {
local sql_file="$1"
log_info "Processing SQL file: $sql_file"
if ! validate_sql_file "$sql_file"; then
return 1
fi
local table_name=$(extract_table_name "$sql_file")
if [ -z "$table_name" ]; then
log_error "Could not extract table name from: $sql_file"
return 1
fi
log_verbose "Extracted table name: $table_name"
local seeder_filename="seeder_${table_name}.go"
local seeder_path="$OUTPUT_DIR/$seeder_filename"
local csv_filename="${table_name}.csv"
local csv_path="$CSV_DIR/$csv_filename"
if [ "$DRY_RUN" = true ]; then
log_info "[DRY RUN] Would generate:"
log_info " Seeder: $seeder_path"
if [ "$GENERATE_CSV" = true ]; then
log_info " CSV: $csv_path"
fi
return 0
fi
# Create directories if they don't exist
mkdir -p "$OUTPUT_DIR"
mkdir -p "$CSV_DIR"
# Generate seeder using Go program
log_info "Generating seeder for table: $table_name"
local csv_flag=""
if [ "$GENERATE_CSV" = true ]; then
csv_flag="-csv-template"
fi
local verbose_flag=""
if [ "$VERBOSE" = true ]; then
verbose_flag="-v"
fi
if go run cmd/seeder-generator/main.go \
-sql="$sql_file" \
-output="$OUTPUT_DIR" \
-csv-dir="$CSV_DIR" \
-batch="$BATCH_SIZE" \
$csv_flag $verbose_flag; then
log_success "Generated seeder: $seeder_path"
if [ "$GENERATE_CSV" = true ] && [ -f "$csv_path" ]; then
log_success "Generated CSV: $csv_path"
fi
else
log_error "Failed to generate seeder for: $table_name"
return 1
fi
}
# Fungsi untuk process semua SQL files di directory
process_sql_directory() {
local dir="$1"
if [ ! -d "$dir" ]; then
log_error "Directory not found: $dir"
return 1
fi
log_info "Processing SQL files in directory: $dir"
local sql_files=()
while IFS= read -r -d $'\0' file; do
sql_files+=("$file")
done < <(find "$dir" -name "*.sql" -type f -print0 | sort -z)
if [ ${#sql_files[@]} -eq 0 ]; then
log_warning "No SQL files found in directory: $dir"
return 1
fi
log_info "Found ${#sql_files[@]} SQL files to process"
local success_count=0
local failed_count=0
for sql_file in "${sql_files[@]}"; do
if generate_seeder_from_file "$sql_file"; then
((success_count++))
else
((failed_count++))
fi
done
log_info "Processing complete: $success_count successful, $failed_count failed"
if [ $failed_count -gt 0 ]; then
return 1
fi
return 0
}
# Fungsi untuk update seeder registry
update_registry() {
local registry_file="$OUTPUT_DIR/seeder_registry.go"
log_info "Updating seeder registry: $registry_file"
# TODO: Implement registry update logic
# For now, just show instructions
log_info "Don't forget to add generated seeders to your seeder registry!"
}
# Fungsi untuk test database connection
test_db_connection() {
log_info "Testing database connection..."
if go run scripts/test_db_connection.go; then
log_success "Database connection successful"
return 0
else
log_error "Database connection failed"
return 1
fi
}
# Main execution
main() {
log_info "Starting seeder generation..."
if [ "$VERBOSE" = true ]; then
log_verbose "Configuration:"
log_verbose " SQL_FILE: $SQL_FILE"
log_verbose " SQL_DIR: $SQL_DIR"
log_verbose " OUTPUT_DIR: $OUTPUT_DIR"
log_verbose " CSV_DIR: $CSV_DIR"
log_verbose " BATCH_SIZE: $BATCH_SIZE"
log_verbose " GENERATE_CSV: $GENERATE_CSV"
log_verbose " DRY_RUN: $DRY_RUN"
fi
# Test database connection (optional)
# test_db_connection || true
# Process SQL files
if [ -n "$SQL_FILE" ]; then
if ! generate_seeder_from_file "$SQL_FILE"; then
exit 1
fi
elif [ -n "$SQL_DIR" ]; then
if ! process_sql_directory "$SQL_DIR"; then
exit 1
fi
fi
# Update registry
update_registry
log_success "Seeder generation completed!"
if [ "$DRY_RUN" = true ]; then
log_info "This was a dry run. No files were created."
else
log_info "Next steps:"
log_info "1. Review generated seeders in: $OUTPUT_DIR"
log_info "2. Add seeders to your registry"
log_info "3. Run: make seeder-seed-all"
fi
}
# Run main function
main "$@"
+39
View File
@@ -0,0 +1,39 @@
#!/bin/bash
set -eo pipefail
# Colors for output
GREEN='\033[0;32m'
BLUE='\033[0;34m'
YELLOW='\033[1;33m'
RED='\033[0;31m'
NC='\033[0m' # No Color
echo -e "${BLUE}📚 Generating Flexible Swagger documentation...${NC}"
# 1. Periksa apakah swag CLI sudah terinstall
if ! command -v swag &> /dev/null; then
echo -e "${YELLOW}⚠️ 'swag' command not found. Installing github.com/swaggo/swag/cmd/swag@latest...${NC}"
go install github.com/swaggo/swag/cmd/swag@latest
# Tambahkan GOPATH/bin ke PATH jika belum ada
export PATH=$PATH:$(go env GOPATH)/bin
fi
# 2. Format anotasi Swagger di seluruh project (Auto-format)
echo -e "${BLUE}📝 Formatting Swagger annotations...${NC}"
swag fmt -d ./cmd/api,./internal -g main.go
# 3. Generate Swagger docs
# -d : Root directory scanning (cmd/api untuk anotasi global, internal untuk semua modul domain DDD)
# -g : Entrypoint utama aplikasi relatif terhadap argumen -d pertama (main.go)
# -o : Output folder untuk file docs/swagger
# --parseDependency : Membaca struct dari external package (pkg/response dll)
# --parseInternal : Membaca struct/model di dalam folder internal
echo -e "${BLUE}⚙️ Building Swagger JSON/YAML files...${NC}"
if swag init -d ./cmd/api,./internal -g main.go -o ./docs/swagger --parseDependency --parseInternal; then
echo -e "${GREEN}✅ Swagger documentation generated successfully in ./docs/swagger${NC}"
else
echo -e "${RED}❌ Failed to generate Swagger documentation${NC}"
exit 1
fi
+65
View File
@@ -0,0 +1,65 @@
#!/bin/bash
echo "🧪 Starting gRPC Test Suite..."
echo "================================"
# Cek apakah server sudah berjalan
if ! nc -z localhost 8090 2>/dev/null; then
echo "⚠️ gRPC Server not running on port 8090"
echo "🚀 Starting server..."
# Jalankan server di background
go run cmd/api/main.go &
SERVER_PID=$!
# Tunggu server siap
echo "⏳ Waiting for server to start..."
sleep 5
# Cek lagi
if ! nc -z localhost 8090 2>/dev/null; then
echo "❌ Failed to start server"
exit 1
fi
echo "✅ Server started successfully"
else
echo "✅ Server already running"
fi
echo ""
echo "1️⃣ Testing gRPC Connection..."
go test -run TestGRPCConnection ./internal/infrastructure/transport/grpc/proto/test/ -v
echo ""
echo "2️⃣ Testing Create Person..."
go test -run TestCreatePerson ./internal/infrastructure/transport/grpc/proto/test/ -v
echo ""
echo "3️⃣ Testing List Persons..."
go test -run TestListPersons ./internal/infrastructure/transport/grpc/proto/test/ -v
echo ""
echo "4️⃣ Testing Get Person..."
go test -run TestGetPerson ./internal/infrastructure/transport/grpc/proto/test/ -v
echo ""
echo "5️⃣ Testing Update Person..."
go test -run TestUpdatePerson ./internal/infrastructure/transport/grpc/proto/test/ -v
echo ""
echo "6️⃣ Testing Delete Person..."
go test -run TestDeletePerson ./internal/infrastructure/transport/grpc/proto/test/ -v
echo ""
echo "7️⃣ Testing Performance..."
go test -run TestGRPCPerformance ./internal/infrastructure/transport/grpc/proto/test/ -v
echo ""
echo "🏁 Test completed!"
# Jika kita yang start server, matikan lagi
if [ ! -z "$SERVER_PID" ]; then
echo "🛑 Stopping server..."
kill $SERVER_PID
fi