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
68 changes: 61 additions & 7 deletions .github/workflows/onPushToMain.yml
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
name: version, tag and publish package
name: Version, Tag and Publish to npm

on:
push:
Expand All @@ -16,11 +16,13 @@ jobs:
uses: actions/setup-node@v3
with:
node-version: '18' # Set to your Node.js version
registry-url: 'https://npm.pkg.github.com'
scope: '@apsoai'
registry-url: 'https://registry.npmjs.org/'

- name: Install dependencies
run: npm install
run: npm ci

- name: Build
run: npm run build

- name: Bump version and tag
id: bump_version
Expand All @@ -29,9 +31,61 @@ jobs:
tag-prefix: 'v' # Set this to 'v' if you want your tags like 'v1.0.0'
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}

- name: Explicitly Create and Push Tag
run: |
# Configure git
git config user.name "GitHub Actions"
git config user.email "actions@github.com"

# Get version from package.json
VERSION=$(node -p "require('./package.json').version")
TAG="v$VERSION"

echo "Creating and pushing tag: $TAG"

# Check if tag exists locally
if git rev-parse "$TAG" >/dev/null 2>&1; then
echo "Tag $TAG already exists locally, skipping tag creation"
else
# Create tag
git tag "$TAG" -m "Release $TAG"
fi

# Check if tag exists on remote
if git ls-remote --tags origin | grep -q "refs/tags/$TAG$"; then
echo "Tag $TAG already exists on remote, skipping tag push"
else
# Push tag to remote
git push origin "$TAG"
fi

- name: Debug Version Output
run: echo "New tag is ${{ steps.bump_version.outputs.new_tag }}"

- name: Create GitHub Release
run: |
if [ -n "${{ steps.bump_version.outputs.new_tag }}" ]; then
gh release create "${{ steps.bump_version.outputs.new_tag }}" \
--title "Release ${{ steps.bump_version.outputs.new_tag }}" \
--generate-notes
else
echo "No new tag was created, skipping release creation"
fi
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}

- run: npm ci
- run: npm publish
- name: Setup NPM Authentication
run: |
echo "//registry.npmjs.org/:_authToken=${NODE_AUTH_TOKEN}" > ~/.npmrc
cat ~/.npmrc
env:
NODE_AUTH_TOKEN: ${{ secrets.NODE_AUTH_TOKEN }}

- name: Publish to npm
run: |
npm config set access public
npm publish
env:
NODE_AUTH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
NODE_AUTH_TOKEN: ${{ secrets.NODE_AUTH_TOKEN }}

6 changes: 3 additions & 3 deletions docs/content/docs/index.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -10,21 +10,21 @@ The Apso SDK allows developers to easily interact with Apso services that expose
To use the Apso SDK, you need to install it via npm or yarn.

```sh
npm install @apsoai/sdk
npm install @apso/sdk
```

or

```sh
yarn add @apsoai/sdk
yarn add @apso/sdk
```

## Getting Started
### Importing the SDK
First, import the `ApsoClient` from the SDK:

```typescript
import { ApsoClient, ApsoClientConfig } from '@apsoai/sdk';
import { ApsoClient, ApsoClientConfig } from '@apso/sdk';
```

### Configuration
Expand Down
161 changes: 155 additions & 6 deletions docs/content/docs/usage.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,63 @@ Adds a filter to the query.
const activeUsers = await apsoClient.entity('users').where({ status: 'active' }).get();
```

### `join(joinTables: string[]): EntityClient`
Adds joins to include related entities in the query response. This allows you to fetch associated data in a single request.

- **`joinTables`**: An array of related entity names to join (e.g., `['profile', 'orders']`).

#### Basic Join Example
```typescript
// Fetch users with their profile information
const usersWithProfiles = await apsoClient.entity('users')
.join(['profile'])
.get();
```

#### Multiple Joins Example
```typescript
// Fetch users with profiles, orders, and addresses
const usersWithRelatedData = await apsoClient.entity('users')
.join(['profile', 'orders', 'addresses'])
.where({ status: 'active' })
.get();
```

#### Complex Join with Filtering
```typescript
// Fetch orders with customer and product information, filtering by date
const recentOrdersWithDetails = await apsoClient.entity('orders')
.join(['customer', 'products', 'shipping_address'])
.where({ created_at: { gte: '2024-01-01' } })
.orderBy({ created_at: 'DESC' })
.limit(50)
.get();
```

#### Nested Entity Joins
```typescript
// Fetch companies with their departments and employees
const companiesWithStructure = await apsoClient.entity('companies')
.join(['departments', 'departments.employees'])
.where({ active: true })
.get();
```

### `select(fields: string[]): EntityClient`
Specifies which fields to return in the response. This is especially useful with joins to control the amount of data returned.

- **`fields`**: An array of field names to include in the response.

#### Example with Joins
```typescript
// Fetch users with specific fields from joined entities
const selectedUserData = await apsoClient.entity('users')
.select(['id', 'name', 'email', 'profile.avatar', 'profile.bio', 'orders.id', 'orders.total'])
.join(['profile', 'orders'])
.where({ status: 'active' })
.get();
```

### `limit(limit: number): EntityClient`
Limits the number of returned records.

Expand Down Expand Up @@ -71,20 +128,109 @@ Performs a DELETE request to remove a resource.
await apsoClient.entity('users').where({ id: 1 }).delete();
```

## Real-World Join Examples

### E-commerce Scenarios

#### Product Catalog with Categories and Reviews
```typescript
// Fetch products with their categories, reviews, and inventory data
const productCatalog = await apsoClient.entity('products')
.join(['category', 'reviews', 'inventory'])
.select([
'id', 'name', 'price', 'description',
'category.name', 'category.slug',
'reviews.rating', 'reviews.comment', 'reviews.user_name',
'inventory.quantity', 'inventory.warehouse_location'
])
.where({ status: 'active', 'inventory.quantity': { gt: 0 } })
.orderBy({ created_at: 'DESC' })
.limit(20)
.get();
```

#### Order Management with Full Details
```typescript
// Fetch orders with customer, items, and shipping information
const orderDetails = await apsoClient.entity('orders')
.join(['customer', 'order_items', 'order_items.product', 'shipping_address', 'payment_method'])
.select([
'id', 'order_number', 'status', 'total', 'created_at',
'customer.name', 'customer.email', 'customer.phone',
'order_items.quantity', 'order_items.price',
'order_items.product.name', 'order_items.product.sku',
'shipping_address.street', 'shipping_address.city', 'shipping_address.postal_code',
'payment_method.type', 'payment_method.last_four'
])
.where({ status: { in: ['pending', 'processing'] } })
.orderBy({ created_at: 'DESC' })
.get();
```

### Content Management Scenarios

#### Blog Posts with Authors and Comments
```typescript
// Fetch blog posts with author details and recent comments
const blogPostsWithDetails = await apsoClient.entity('posts')
.join(['author', 'comments', 'comments.user', 'tags'])
.select([
'id', 'title', 'slug', 'content', 'published_at',
'author.name', 'author.bio', 'author.avatar',
'comments.content', 'comments.created_at',
'comments.user.name', 'comments.user.avatar',
'tags.name', 'tags.slug'
])
.where({ published: true })
.orderBy({ published_at: 'DESC' })
.limit(10)
.get();
```

### User Management Scenarios

#### User Profiles with Permissions and Activity
```typescript
// Fetch users with their roles, permissions, and recent activity
const userManagement = await apsoClient.entity('users')
.join(['profile', 'roles', 'roles.permissions', 'activity_logs'])
.select([
'id', 'username', 'email', 'status', 'last_login',
'profile.first_name', 'profile.last_name', 'profile.department',
'roles.name', 'roles.description',
'roles.permissions.resource', 'roles.permissions.action',
'activity_logs.action', 'activity_logs.timestamp'
])
.where({ status: 'active' })
.orderBy({ last_login: 'DESC' })
.get();
```

## Query Parameters
The SDK supports several query parameters for GET requests to filter, sort, or paginate data.

### Available Query Methods
- **`where(filter: Record<string, any>)`**: Filter records (e.g., `{ active: true }`).
- **`join(joinTables: string[])`**: Include related entities in the response.
- **`select(fields: string[])`**: Specify which fields to return.
- **`limit(limit: number)`**: Limit the number of returned records.
- **`offset(offset: number)`**: Skip a number of records.
- **`page(page: number)`**: Specify the page number for pagination.
- **`orderBy(sort: Record<string, 'ASC' | 'DESC'>)`**: Sort the results.
- **`or(orCondition: Record<string, any>)`**: Add OR conditions to the query.
- **`cache(useCache?: boolean, duration?: number)`**: Enable caching for the request.

#### Example
#### Advanced Query Example
```typescript
const result = await apsoClient.entity('users')
.where({ active: true })
.limit(5)
.select(['id', 'name', 'email', 'profile.avatar', 'orders.total'])
.join(['profile', 'orders'])
.where({ active: true, 'profile.verified': true })
.or({ role: 'admin' })
.orderBy({ created_at: 'DESC' })
.limit(25)
.offset(50)
.cache(true, 300) // Cache for 5 minutes
.get();
```

Expand All @@ -93,10 +239,13 @@ The `ApsoClient` will throw an error if the request fails. Make sure to wrap you

```typescript
try {
const user = await apsoClient.entity('users').where({ id: 1 }).get();
console.log(user);
const userWithOrders = await apsoClient.entity('users')
.join(['orders', 'profile'])
.where({ id: 1 })
.get();
console.log(userWithOrders);
} catch (error) {
console.error('Error fetching user:', error);
console.error('Error fetching user with related data:', error);
}
```

Loading