refactor: flatten Microsoft skills from nested to flat directory structure
Rewrote sync_microsoft_skills.py (v4) to use each SKILL.md's frontmatter 'name' field as the flat directory name under skills/, replacing the nested skills/official/microsoft/<lang>/<category>/<service>/ hierarchy. This fixes CI failures caused by the indexing, validation, and catalog scripts expecting skills/<id>/SKILL.md (depth 1). Changes: - Rewrite scripts/sync_microsoft_skills.py for flat output with collision detection - Update scripts/tests/inspect_microsoft_repo.py for flat name mapping - Update scripts/tests/test_comprehensive_coverage.py for name uniqueness checks - Delete skills/official/ nested directory - Add 129 Microsoft skills as flat directories (e.g. skills/azure-mgmt-botservice-dotnet/) - Move attribution files to docs/ (LICENSE-MICROSOFT, microsoft-skills-attribution.json) - Rebuild skills_index.json, CATALOG.md, README.md (845 total skills)
This commit is contained in:
471
skills/azure-cosmos-ts/SKILL.md
Normal file
471
skills/azure-cosmos-ts/SKILL.md
Normal file
@@ -0,0 +1,471 @@
|
||||
---
|
||||
name: azure-cosmos-ts
|
||||
description: |
|
||||
Azure Cosmos DB JavaScript/TypeScript SDK (@azure/cosmos) for data plane operations. Use for CRUD operations on documents, queries, bulk operations, and container management. Triggers: "Cosmos DB", "@azure/cosmos", "CosmosClient", "document CRUD", "NoSQL queries", "bulk operations", "partition key", "container.items".
|
||||
package: @azure/cosmos
|
||||
---
|
||||
|
||||
# @azure/cosmos (TypeScript/JavaScript)
|
||||
|
||||
Data plane SDK for Azure Cosmos DB NoSQL API operations — CRUD on documents, queries, bulk operations.
|
||||
|
||||
> **⚠️ Data vs Management Plane**
|
||||
> - **This SDK (@azure/cosmos)**: CRUD operations on documents, queries, stored procedures
|
||||
> - **Management SDK (@azure/arm-cosmosdb)**: Create accounts, databases, containers via ARM
|
||||
|
||||
## Installation
|
||||
|
||||
```bash
|
||||
npm install @azure/cosmos @azure/identity
|
||||
```
|
||||
|
||||
**Current Version**: 4.9.0
|
||||
**Node.js**: >= 20.0.0
|
||||
|
||||
## Environment Variables
|
||||
|
||||
```bash
|
||||
COSMOS_ENDPOINT=https://<account>.documents.azure.com:443/
|
||||
COSMOS_DATABASE=<database-name>
|
||||
COSMOS_CONTAINER=<container-name>
|
||||
# For key-based auth only (prefer AAD)
|
||||
COSMOS_KEY=<account-key>
|
||||
```
|
||||
|
||||
## Authentication
|
||||
|
||||
### AAD with DefaultAzureCredential (Recommended)
|
||||
|
||||
```typescript
|
||||
import { CosmosClient } from "@azure/cosmos";
|
||||
import { DefaultAzureCredential } from "@azure/identity";
|
||||
|
||||
const client = new CosmosClient({
|
||||
endpoint: process.env.COSMOS_ENDPOINT!,
|
||||
aadCredentials: new DefaultAzureCredential(),
|
||||
});
|
||||
```
|
||||
|
||||
### Key-Based Authentication
|
||||
|
||||
```typescript
|
||||
import { CosmosClient } from "@azure/cosmos";
|
||||
|
||||
// Option 1: Endpoint + Key
|
||||
const client = new CosmosClient({
|
||||
endpoint: process.env.COSMOS_ENDPOINT!,
|
||||
key: process.env.COSMOS_KEY!,
|
||||
});
|
||||
|
||||
// Option 2: Connection String
|
||||
const client = new CosmosClient(process.env.COSMOS_CONNECTION_STRING!);
|
||||
```
|
||||
|
||||
## Resource Hierarchy
|
||||
|
||||
```
|
||||
CosmosClient
|
||||
└── Database
|
||||
└── Container
|
||||
├── Items (documents)
|
||||
├── Scripts (stored procedures, triggers, UDFs)
|
||||
└── Conflicts
|
||||
```
|
||||
|
||||
## Core Operations
|
||||
|
||||
### Database & Container Setup
|
||||
|
||||
```typescript
|
||||
const { database } = await client.databases.createIfNotExists({
|
||||
id: "my-database",
|
||||
});
|
||||
|
||||
const { container } = await database.containers.createIfNotExists({
|
||||
id: "my-container",
|
||||
partitionKey: { paths: ["/partitionKey"] },
|
||||
});
|
||||
```
|
||||
|
||||
### Create Document
|
||||
|
||||
```typescript
|
||||
interface Product {
|
||||
id: string;
|
||||
partitionKey: string;
|
||||
name: string;
|
||||
price: number;
|
||||
}
|
||||
|
||||
const item: Product = {
|
||||
id: "product-1",
|
||||
partitionKey: "electronics",
|
||||
name: "Laptop",
|
||||
price: 999.99,
|
||||
};
|
||||
|
||||
const { resource } = await container.items.create<Product>(item);
|
||||
```
|
||||
|
||||
### Read Document
|
||||
|
||||
```typescript
|
||||
const { resource } = await container
|
||||
.item("product-1", "electronics") // id, partitionKey
|
||||
.read<Product>();
|
||||
|
||||
if (resource) {
|
||||
console.log(resource.name);
|
||||
}
|
||||
```
|
||||
|
||||
### Update Document (Replace)
|
||||
|
||||
```typescript
|
||||
const { resource: existing } = await container
|
||||
.item("product-1", "electronics")
|
||||
.read<Product>();
|
||||
|
||||
if (existing) {
|
||||
existing.price = 899.99;
|
||||
const { resource: updated } = await container
|
||||
.item("product-1", "electronics")
|
||||
.replace<Product>(existing);
|
||||
}
|
||||
```
|
||||
|
||||
### Upsert Document
|
||||
|
||||
```typescript
|
||||
const item: Product = {
|
||||
id: "product-1",
|
||||
partitionKey: "electronics",
|
||||
name: "Laptop Pro",
|
||||
price: 1299.99,
|
||||
};
|
||||
|
||||
const { resource } = await container.items.upsert<Product>(item);
|
||||
```
|
||||
|
||||
### Delete Document
|
||||
|
||||
```typescript
|
||||
await container.item("product-1", "electronics").delete();
|
||||
```
|
||||
|
||||
### Patch Document (Partial Update)
|
||||
|
||||
```typescript
|
||||
import { PatchOperation } from "@azure/cosmos";
|
||||
|
||||
const operations: PatchOperation[] = [
|
||||
{ op: "replace", path: "/price", value: 799.99 },
|
||||
{ op: "add", path: "/discount", value: true },
|
||||
{ op: "remove", path: "/oldField" },
|
||||
];
|
||||
|
||||
const { resource } = await container
|
||||
.item("product-1", "electronics")
|
||||
.patch<Product>(operations);
|
||||
```
|
||||
|
||||
## Queries
|
||||
|
||||
### Simple Query
|
||||
|
||||
```typescript
|
||||
const { resources } = await container.items
|
||||
.query<Product>("SELECT * FROM c WHERE c.price < 1000")
|
||||
.fetchAll();
|
||||
```
|
||||
|
||||
### Parameterized Query (Recommended)
|
||||
|
||||
```typescript
|
||||
import { SqlQuerySpec } from "@azure/cosmos";
|
||||
|
||||
const querySpec: SqlQuerySpec = {
|
||||
query: "SELECT * FROM c WHERE c.partitionKey = @category AND c.price < @maxPrice",
|
||||
parameters: [
|
||||
{ name: "@category", value: "electronics" },
|
||||
{ name: "@maxPrice", value: 1000 },
|
||||
],
|
||||
};
|
||||
|
||||
const { resources } = await container.items
|
||||
.query<Product>(querySpec)
|
||||
.fetchAll();
|
||||
```
|
||||
|
||||
### Query with Pagination
|
||||
|
||||
```typescript
|
||||
const queryIterator = container.items.query<Product>(querySpec, {
|
||||
maxItemCount: 10, // Items per page
|
||||
});
|
||||
|
||||
while (queryIterator.hasMoreResults()) {
|
||||
const { resources, continuationToken } = await queryIterator.fetchNext();
|
||||
console.log(`Page with ${resources?.length} items`);
|
||||
// Use continuationToken for next page if needed
|
||||
}
|
||||
```
|
||||
|
||||
### Cross-Partition Query
|
||||
|
||||
```typescript
|
||||
const { resources } = await container.items
|
||||
.query<Product>(
|
||||
"SELECT * FROM c WHERE c.price > 500",
|
||||
{ enableCrossPartitionQuery: true }
|
||||
)
|
||||
.fetchAll();
|
||||
```
|
||||
|
||||
## Bulk Operations
|
||||
|
||||
### Execute Bulk Operations
|
||||
|
||||
```typescript
|
||||
import { BulkOperationType, OperationInput } from "@azure/cosmos";
|
||||
|
||||
const operations: OperationInput[] = [
|
||||
{
|
||||
operationType: BulkOperationType.Create,
|
||||
resourceBody: { id: "1", partitionKey: "cat-a", name: "Item 1" },
|
||||
},
|
||||
{
|
||||
operationType: BulkOperationType.Upsert,
|
||||
resourceBody: { id: "2", partitionKey: "cat-a", name: "Item 2" },
|
||||
},
|
||||
{
|
||||
operationType: BulkOperationType.Read,
|
||||
id: "3",
|
||||
partitionKey: "cat-b",
|
||||
},
|
||||
{
|
||||
operationType: BulkOperationType.Replace,
|
||||
id: "4",
|
||||
partitionKey: "cat-b",
|
||||
resourceBody: { id: "4", partitionKey: "cat-b", name: "Updated" },
|
||||
},
|
||||
{
|
||||
operationType: BulkOperationType.Delete,
|
||||
id: "5",
|
||||
partitionKey: "cat-c",
|
||||
},
|
||||
{
|
||||
operationType: BulkOperationType.Patch,
|
||||
id: "6",
|
||||
partitionKey: "cat-c",
|
||||
resourceBody: {
|
||||
operations: [{ op: "replace", path: "/name", value: "Patched" }],
|
||||
},
|
||||
},
|
||||
];
|
||||
|
||||
const response = await container.items.executeBulkOperations(operations);
|
||||
|
||||
response.forEach((result, index) => {
|
||||
if (result.statusCode >= 200 && result.statusCode < 300) {
|
||||
console.log(`Operation ${index} succeeded`);
|
||||
} else {
|
||||
console.error(`Operation ${index} failed: ${result.statusCode}`);
|
||||
}
|
||||
});
|
||||
```
|
||||
|
||||
## Partition Keys
|
||||
|
||||
### Simple Partition Key
|
||||
|
||||
```typescript
|
||||
const { container } = await database.containers.createIfNotExists({
|
||||
id: "products",
|
||||
partitionKey: { paths: ["/category"] },
|
||||
});
|
||||
```
|
||||
|
||||
### Hierarchical Partition Key (MultiHash)
|
||||
|
||||
```typescript
|
||||
import { PartitionKeyDefinitionVersion, PartitionKeyKind } from "@azure/cosmos";
|
||||
|
||||
const { container } = await database.containers.createIfNotExists({
|
||||
id: "orders",
|
||||
partitionKey: {
|
||||
paths: ["/tenantId", "/userId", "/sessionId"],
|
||||
version: PartitionKeyDefinitionVersion.V2,
|
||||
kind: PartitionKeyKind.MultiHash,
|
||||
},
|
||||
});
|
||||
|
||||
// Operations require array of partition key values
|
||||
const { resource } = await container.items.create({
|
||||
id: "order-1",
|
||||
tenantId: "tenant-a",
|
||||
userId: "user-123",
|
||||
sessionId: "session-xyz",
|
||||
total: 99.99,
|
||||
});
|
||||
|
||||
// Read with hierarchical partition key
|
||||
const { resource: order } = await container
|
||||
.item("order-1", ["tenant-a", "user-123", "session-xyz"])
|
||||
.read();
|
||||
```
|
||||
|
||||
## Error Handling
|
||||
|
||||
```typescript
|
||||
import { ErrorResponse } from "@azure/cosmos";
|
||||
|
||||
try {
|
||||
const { resource } = await container.item("missing", "pk").read();
|
||||
} catch (error) {
|
||||
if (error instanceof ErrorResponse) {
|
||||
switch (error.code) {
|
||||
case 404:
|
||||
console.log("Document not found");
|
||||
break;
|
||||
case 409:
|
||||
console.log("Conflict - document already exists");
|
||||
break;
|
||||
case 412:
|
||||
console.log("Precondition failed (ETag mismatch)");
|
||||
break;
|
||||
case 429:
|
||||
console.log("Rate limited - retry after:", error.retryAfterInMs);
|
||||
break;
|
||||
default:
|
||||
console.error(`Cosmos error ${error.code}: ${error.message}`);
|
||||
}
|
||||
}
|
||||
throw error;
|
||||
}
|
||||
```
|
||||
|
||||
## Optimistic Concurrency (ETags)
|
||||
|
||||
```typescript
|
||||
// Read with ETag
|
||||
const { resource, etag } = await container
|
||||
.item("product-1", "electronics")
|
||||
.read<Product>();
|
||||
|
||||
if (resource && etag) {
|
||||
resource.price = 899.99;
|
||||
|
||||
try {
|
||||
// Replace only if ETag matches
|
||||
await container.item("product-1", "electronics").replace(resource, {
|
||||
accessCondition: { type: "IfMatch", condition: etag },
|
||||
});
|
||||
} catch (error) {
|
||||
if (error instanceof ErrorResponse && error.code === 412) {
|
||||
console.log("Document was modified by another process");
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## TypeScript Types Reference
|
||||
|
||||
```typescript
|
||||
import {
|
||||
// Client & Resources
|
||||
CosmosClient,
|
||||
Database,
|
||||
Container,
|
||||
Item,
|
||||
Items,
|
||||
|
||||
// Operations
|
||||
OperationInput,
|
||||
BulkOperationType,
|
||||
PatchOperation,
|
||||
|
||||
// Queries
|
||||
SqlQuerySpec,
|
||||
SqlParameter,
|
||||
FeedOptions,
|
||||
|
||||
// Partition Keys
|
||||
PartitionKeyDefinition,
|
||||
PartitionKeyDefinitionVersion,
|
||||
PartitionKeyKind,
|
||||
|
||||
// Responses
|
||||
ItemResponse,
|
||||
FeedResponse,
|
||||
ResourceResponse,
|
||||
|
||||
// Errors
|
||||
ErrorResponse,
|
||||
} from "@azure/cosmos";
|
||||
```
|
||||
|
||||
## Best Practices
|
||||
|
||||
1. **Use AAD authentication** — Prefer `DefaultAzureCredential` over keys
|
||||
2. **Always use parameterized queries** — Prevents injection, improves plan caching
|
||||
3. **Specify partition key** — Avoid cross-partition queries when possible
|
||||
4. **Use bulk operations** — For multiple writes, use `executeBulkOperations`
|
||||
5. **Handle 429 errors** — Implement retry logic with exponential backoff
|
||||
6. **Use ETags for concurrency** — Prevent lost updates in concurrent scenarios
|
||||
7. **Close client on shutdown** — Call `client.dispose()` in cleanup
|
||||
|
||||
## Common Patterns
|
||||
|
||||
### Service Layer Pattern
|
||||
|
||||
```typescript
|
||||
export class ProductService {
|
||||
private container: Container;
|
||||
|
||||
constructor(client: CosmosClient) {
|
||||
this.container = client
|
||||
.database(process.env.COSMOS_DATABASE!)
|
||||
.container(process.env.COSMOS_CONTAINER!);
|
||||
}
|
||||
|
||||
async getById(id: string, category: string): Promise<Product | null> {
|
||||
try {
|
||||
const { resource } = await this.container
|
||||
.item(id, category)
|
||||
.read<Product>();
|
||||
return resource ?? null;
|
||||
} catch (error) {
|
||||
if (error instanceof ErrorResponse && error.code === 404) {
|
||||
return null;
|
||||
}
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
async create(product: Omit<Product, "id">): Promise<Product> {
|
||||
const item = { ...product, id: crypto.randomUUID() };
|
||||
const { resource } = await this.container.items.create<Product>(item);
|
||||
return resource!;
|
||||
}
|
||||
|
||||
async findByCategory(category: string): Promise<Product[]> {
|
||||
const querySpec: SqlQuerySpec = {
|
||||
query: "SELECT * FROM c WHERE c.partitionKey = @category",
|
||||
parameters: [{ name: "@category", value: category }],
|
||||
};
|
||||
const { resources } = await this.container.items
|
||||
.query<Product>(querySpec)
|
||||
.fetchAll();
|
||||
return resources;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Related SDKs
|
||||
|
||||
| SDK | Purpose | Install |
|
||||
|-----|---------|---------|
|
||||
| `@azure/cosmos` | Data plane (this SDK) | `npm install @azure/cosmos` |
|
||||
| `@azure/arm-cosmosdb` | Management plane (ARM) | `npm install @azure/arm-cosmosdb` |
|
||||
| `@azure/identity` | Authentication | `npm install @azure/identity` |
|
||||
Reference in New Issue
Block a user