Skip to content
Merged
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
39 changes: 23 additions & 16 deletions recipe-portal/.gitignore
Original file line number Diff line number Diff line change
@@ -1,35 +1,42 @@
# Dependencies
node_modules/
# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.

# dependencies
/node_modules
/.pnp
.pnp.js

# Testing
# testing
/coverage

# Next.js
# next.js
/.next/
/out/

# Production
# production
/build

# Misc
# misc
.DS_Store
*.tsbuildinfo
next-env.d.ts
*.pem

# Debug
# debug
npm-debug.log*
yarn-debug.log*
yarn-error.log*

# Local env files
# local env files
.env*.local

# IDE
.vscode/
.idea/
# vercel
.vercel

# OS
.DS_Store
Thumbs.db
# typescript
*.tsbuildinfo
next-env.d.ts

# Sigma API encrypted credentials (security)
.sigma-portal/
sigma-portal-keys.json

# Environment files
.env
129 changes: 129 additions & 0 deletions recipe-portal/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,129 @@
# QuickStarts API Toolkit
Experiment with Sigma API calls and learn common request flows

## Features

### Recipes:
- **Smart Parameter Detection**: Automatically detects and provides dropdown selection for Sigma resources (teams, members, workbooks, etc.)
- **Interactive Execution**: Run recipes directly in the browser with real-time results
- **Parameter Summary**: View which parameters were used in each request
- **Code Viewing**: Browse the actual JavaScript code for each recipe

### Quick API Explorer:
- **Common Endpoints**: Curated list of the most useful Sigma API endpoints
- **Zero Setup**: List endpoints require no parameters - perfect for quick exploration
- **One Parameter**: Detail endpoints need just one ID to get specific resource information
- **Alphabetical Organization**: Easy to find the endpoint you need

## Authentication & Config Management

### Smart Config System:
- **Complete Configuration Storage**: Server endpoints + API credentials stored together as named "configs"
- **Multi-Environment Support**: Easily switch between Production, Staging, Development environments
- **One-Click Environment Switching**: Load complete configurations instantly
- **Encrypted Local Storage**: AES-256 encryption for credential security

### Config Management Features:
- **Quick Start**: Load saved configs with one click - no manual entry needed
- **Create New Configs**: Mix and match server endpoints with credentials
- **Update Existing Configs**: Modify and save changes to existing configurations
- **Delete Configs**: Remove configs you no longer need
- **Auto-Save**: Configs saved automatically during authentication when enabled
- **Manual Save**: Explicit save button for immediate config storage

### Token Management:
- **File-Based Storage**: Authentication tokens cached in system temp directory
- **Persistent Sessions**: Tokens survive browser/server restarts for the full hour
- **Automatic Expiration**: Tokens expire after 1 hour (Sigma's standard lifetime)
- **Auto-Cleanup**: Expired tokens automatically detected and removed
- **Manual Session End**: Clear authentication anytime with 🚪 End Session button

### Storage Locations

**Config Storage (encrypted)**:
- **macOS**: `~/Library/Application Support/.sigma-portal/encrypted-keys.json`
- **Windows**: `%APPDATA%\.sigma-portal\encrypted-keys.json`
- **Linux**: `~/.config/.sigma-portal/encrypted-keys.json`

**Token Cache (temporary)**:
- **macOS**: `/var/folders/.../sigma-portal-token.json`
- **Windows**: `%TEMP%\sigma-portal-token.json`
- **Linux**: `/tmp/sigma-portal-token.json`

### Developer Experience Benefits
- **Environment Switching**: Instant switch between Production ↔ Staging ↔ Development
- **Zero Re-entry**: Load complete configs without typing credentials repeatedly
- **Secure Storage**: Military-grade AES-256 encryption for stored credentials
- **Clean Separation**: Configs stored outside project directory (never committed to git)
- **Visual Feedback**: Clear indicators show saved/unsaved state and notifications
- **Flexible Workflow**: Session-only credentials OR persistent named configs

### Config Workflow
1. **First Time**: Enter server endpoint + credentials → Save as named config (e.g., "Production")
2. **Daily Use**: Quick Start → Select "Production" → Instantly loaded and ready
3. **Environment Switch**: Quick Start → Select "Staging" → Switched in one click
4. **New Environment**: "✨ New Config" → Enter details → Save with new name

## Getting Started
Sigma_QuickStart_Public_Repo


1. **Setup**: `npm install && npm run dev`
2. **First-Time Config**: Open any recipe → **Config** tab → Enter server endpoint + credentials → Save as named config
3. **Daily Use**: **Quick Start** section → Select your saved config → Ready to go!
4. **Explore**: Use the ⚡ Quick API tab to explore common endpoints with smart parameters
5. **Run Recipes**: Browse recipes by category and execute them with real-time results

### Config Tab Features
- **Quick Start**: Load saved configs instantly (appears when configs exist)
- **Server Endpoint**: Choose your Sigma organization's server location
- **API Credentials**: Enter Client ID and Client Secret
- **Config Storage**: Save complete configurations with names like "Production", "Staging"
- **Save Config**: Manual save button for immediate storage
- **New Config**: Clear form to create fresh configurations
- **Delete**: Remove configs you no longer need (🗑️ button when config selected)

## Requirements
- Node.js 18+
- Sigma API credentials (Client ID and Secret)
- Valid Sigma organization access

## Development
```bash
npm install
npm run dev
```

Navigate to `http://localhost:3001` to start exploring the Sigma API.

## Project Structure
```
recipe-portal/
├── app/ # Next.js app directory
│ ├── api/ # API routes
│ │ ├── execute/ # Recipe execution
│ │ ├── resources/ # Resource fetching for dropdowns
│ │ ├── keys/ # Config management (CRUD operations)
│ │ ├── token/ # Token management & caching
│ │ └── call/ # Quick API endpoint calls
├── components/ # React components
│ ├── QuickApiExplorer.tsx # Quick API exploration interface
│ ├── QuickApiModal.tsx # API endpoint execution modal
│ ├── SmartParameterForm.tsx # Smart parameter detection & forms
│ ├── CodeViewer.tsx # Recipe viewer with Config tab
│ ├── AuthRecipeCard.tsx # Authentication recipe card
│ └── RecipeCard.tsx # Standard recipe cards
├── lib/ # Utilities
│ ├── smartParameters.ts # Parameter detection logic
│ ├── keyStorage.ts # Encrypted config storage
│ └── recipeScanner.ts # Recipe discovery & analysis
└── recipes/ # Self-contained recipe files (copied from sigma-api-recipes)
├── connections/ # Connection-related recipes
├── members/ # Member management recipes
├── teams/ # Team management recipes
├── workbooks/ # Workbook operations
├── embedding/ # Embedding examples
└── get-access-token.js # Authentication helper
```

For setup instructions and API credential creation, visit the QuickStart: [Sigma REST API Recipes](https://quickstarts.sigmacomputing.com/guide/developers_api_code_samples/index.html?index=..%2F..index#0)
141 changes: 141 additions & 0 deletions recipe-portal/app/api/call/route.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,141 @@
import { NextResponse } from 'next/server';
import axios from 'axios';
import fs from 'fs';
import path from 'path';
import os from 'os';

const TOKEN_CACHE_FILE = path.join(os.tmpdir(), 'sigma-portal-token.json');

function getCachedToken(): string | null {
try {
if (fs.existsSync(TOKEN_CACHE_FILE)) {
const tokenData = JSON.parse(fs.readFileSync(TOKEN_CACHE_FILE, 'utf8'));
const now = Date.now();

// Check if token is still valid (not expired)
if (tokenData.expiresAt && now < tokenData.expiresAt) {
return tokenData.token;
} else {
// Token expired, remove file
fs.unlinkSync(TOKEN_CACHE_FILE);
}
}
} catch (error) {
// Ignore errors, just return null
}
return null;
}

export async function POST(request: Request) {
try {
const { endpoint, method, parameters = {}, requestBody } = await request.json();

if (!endpoint) {
return NextResponse.json(
{ error: 'Endpoint is required' },
{ status: 400 }
);
}

// Get cached token
const token = getCachedToken();
if (!token) {
return NextResponse.json(
{
error: 'Authentication required',
message: 'No valid authentication token found. Please authenticate first.'
},
{ status: 401 }
);
}

// Build the full URL
const baseURL = process.env.SIGMA_BASE_URL || 'https://aws-api.sigmacomputing.com/v2';
let url = `${baseURL}${endpoint}`;

// Add query parameters
if (parameters.query && Object.keys(parameters.query).length > 0) {
const queryParams = new URLSearchParams();
Object.entries(parameters.query).forEach(([key, value]) => {
if (value !== undefined && value !== '') {
queryParams.append(key, String(value));
}
});
if (queryParams.toString()) {
url += `?${queryParams.toString()}`;
}
}

// Prepare headers
const headers: Record<string, string> = {
'Authorization': `Bearer ${token}`,
'Accept': 'application/json',
'Content-Type': 'application/json'
};

// Add header parameters
if (parameters.header) {
Object.entries(parameters.header).forEach(([key, value]) => {
if (value !== undefined && value !== '') {
headers[key] = String(value);
}
});
}

// Make the API call
const response = await axios({
method: method.toLowerCase(),
url,
headers,
data: requestBody,
timeout: 30000 // 30 second timeout
});

// Return successful response
return NextResponse.json({
output: JSON.stringify(response.data, null, 2),
error: '',
success: true,
timestamp: new Date().toISOString(),
httpStatus: response.status,
httpStatusText: response.statusText,
requestUrl: url,
requestMethod: method
});

} catch (error: any) {
console.error('API call error:', error);

let errorMessage = 'Unknown error occurred';
let httpStatus = 500;
let httpStatusText = 'Internal Server Error';

if (axios.isAxiosError(error)) {
if (error.response) {
// Server responded with error status
httpStatus = error.response.status;
httpStatusText = error.response.statusText;
errorMessage = error.response.data?.message || error.response.data?.error || `HTTP ${httpStatus}: ${httpStatusText}`;
} else if (error.request) {
// Request made but no response
errorMessage = 'No response received from server';
httpStatus = 0;
httpStatusText = 'Network Error';
} else {
// Error setting up request
errorMessage = error.message;
}
} else {
errorMessage = error.message || 'Unknown error';
}

return NextResponse.json({
output: '',
error: errorMessage,
success: false,
timestamp: new Date().toISOString(),
httpStatus,
httpStatusText
});
}
}
6 changes: 3 additions & 3 deletions recipe-portal/app/api/code/route.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,14 +14,14 @@ export async function GET(request: Request) {
);
}

// Security check: ensure the file is within the sigma-api-recipes directory
const recipesPath = path.join(process.cwd(), '..', 'sigma-api-recipes');
// Security check: ensure the file is within the recipes directory
const recipesPath = path.join(process.cwd(), 'recipes');
const resolvedPath = path.resolve(filePath);
const resolvedRecipesPath = path.resolve(recipesPath);

if (!resolvedPath.startsWith(resolvedRecipesPath)) {
return NextResponse.json(
{ error: 'Access denied: File must be within sigma-api-recipes directory' },
{ error: 'Access denied: File must be within recipes directory' },
{ status: 403 }
);
}
Expand Down
Loading