Ajouter des opérations API TypeSpec
Ajoutez des opérations RESTful à un plugin API TypeSpec existant pour Microsoft 365 Copilot.
Ajouter des opérations GET
GET simple - Lister tous les éléments
/**
* List all items.
*/
@route("/items")
@get op listItems(): Item[];
GET avec paramètre de requête - Filtrer les résultats
/**
* List items filtered by criteria.
* @param userId Optional user ID to filter items
*/
@route("/items")
@get op listItems(@query userId?: integer): Item[];
GET avec paramètre de chemin - Obtenir un élément unique
/**
* Get a specific item by ID.
* @param id The ID of the item to retrieve
*/
@route("/items/{id}")
@get op getItem(@path id: integer): Item;
GET avec Adaptive Card
/**
* List items with adaptive card visualization.
*/
@route("/items")
@card(#{
dataPath: "$",
title: "$.title",
file: "item-card.json"
})
@get op listItems(): Item[];
Créez l'Adaptive Card (appPackage/item-card.json) :
{
"type": "AdaptiveCard",
"$schema": "http://adaptivecards.io/schemas/adaptive-card.json",
"version": "1.5",
"body": [
{
"type": "Container",
"$data": "${$root}",
"items": [
{
"type": "TextBlock",
"text": "**${if(title, title, 'N/A')}**",
"wrap": true
},
{
"type": "TextBlock",
"text": "${if(description, description, 'N/A')}",
"wrap": true
}
]
}
],
"actions": [
{
"type": "Action.OpenUrl",
"title": "View Details",
"url": "https://example.com/items/${id}"
}
]
}
Ajouter des opérations POST
POST simple - Créer un élément
/**
* Create a new item.
* @param item The item to create
*/
@route("/items")
@post op createItem(@body item: CreateItemRequest): Item;
model CreateItemRequest {
title: string;
description?: string;
userId: integer;
}
POST avec confirmation
/**
* Create a new item with confirmation.
*/
@route("/items")
@post
@capabilities(#{
confirmation: #{
type: "AdaptiveCard",
title: "Create Item",
body: """
Are you sure you want to create this item?
* **Title**: {{ function.parameters.item.title }}
* **User ID**: {{ function.parameters.item.userId }}
"""
}
})
op createItem(@body item: CreateItemRequest): Item;
Ajouter des opérations PATCH
PATCH simple - Mettre à jour un élément
/**
* Update an existing item.
* @param id The ID of the item to update
* @param item The updated item data
*/
@route("/items/{id}")
@patch op updateItem(
@path id: integer,
@body item: UpdateItemRequest
): Item;
model UpdateItemRequest {
title?: string;
description?: string;
status?: "active" | "completed" | "archived";
}
PATCH avec confirmation
/**
* Update an item with confirmation.
*/
@route("/items/{id}")
@patch
@capabilities(#{
confirmation: #{
type: "AdaptiveCard",
title: "Update Item",
body: """
Updating item #{{ function.parameters.id }}:
* **Title**: {{ function.parameters.item.title }}
* **Status**: {{ function.parameters.item.status }}
"""
}
})
op updateItem(
@path id: integer,
@body item: UpdateItemRequest
): Item;
Ajouter des opérations DELETE
DELETE simple
/**
* Delete an item.
* @param id The ID of the item to delete
*/
@route("/items/{id}")
@delete op deleteItem(@path id: integer): void;
DELETE avec confirmation
/**
* Delete an item with confirmation.
*/
@route("/items/{id}")
@delete
@capabilities(#{
confirmation: #{
type: "AdaptiveCard",
title: "Delete Item",
body: """
⚠️ Are you sure you want to delete item #{{ function.parameters.id }}?
This action cannot be undone.
"""
}
})
op deleteItem(@path id: integer): void;
Exemple CRUD complet
Définir le service et les modèles
@service
@server("https://api.example.com")
@actions(#{
nameForHuman: "Items API",
descriptionForHuman: "Manage items",
descriptionForModel: "Read, create, update, and delete items"
})
namespace ItemsAPI {
// Models
model Item {
@visibility(Lifecycle.Read)
id: integer;
userId: integer;
title: string;
description?: string;
status: "active" | "completed" | "archived";
@format("date-time")
createdAt: utcDateTime;
@format("date-time")
updatedAt?: utcDateTime;
}
model CreateItemRequest {
userId: integer;
title: string;
description?: string;
}
model UpdateItemRequest {
title?: string;
description?: string;
status?: "active" | "completed" | "archived";
}
// Operations
@route("/items")
@card(#{ dataPath: "$", title: "$.title", file: "item-card.json" })
@get op listItems(@query userId?: integer): Item[];
@route("/items/{id}")
@card(#{ dataPath: "$", title: "$.title", file: "item-card.json" })
@get op getItem(@path id: integer): Item;
@route("/items")
@post
@capabilities(#{
confirmation: #{
type: "AdaptiveCard",
title: "Create Item",
body: "Creating: **{{ function.parameters.item.title }}**"
}
})
op createItem(@body item: CreateItemRequest): Item;
@route("/items/{id}")
@patch
@capabilities(#{
confirmation: #{
type: "AdaptiveCard",
title: "Update Item",
body: "Updating item #{{ function.parameters.id }}"
}
})
op updateItem(@path id: integer, @body item: UpdateItemRequest): Item;
@route("/items/{id}")
@delete
@capabilities(#{
confirmation: #{
type: "AdaptiveCard",
title: "Delete Item",
body: "⚠️ Delete item #{{ function.parameters.id }}?"
}
})
op deleteItem(@path id: integer): void;
}
Fonctionnalités avancées
Plusieurs paramètres de requête
@route("/items")
@get op listItems(
@query userId?: integer,
@query status?: "active" | "completed" | "archived",
@query limit?: integer,
@query offset?: integer
): ItemList;
model ItemList {
items: Item[];
total: integer;
hasMore: boolean;
}
Paramètres d'en-tête
@route("/items")
@get op listItems(
@header("X-API-Version") apiVersion?: string,
@query userId?: integer
): Item[];
Modèles de réponse personnalisés
@route("/items/{id}")
@delete op deleteItem(@path id: integer): DeleteResponse;
model DeleteResponse {
success: boolean;
message: string;
deletedId: integer;
}
Réponses d'erreur
model ErrorResponse {
error: {
code: string;
message: string;
details?: string[];
};
}
@route("/items/{id}")
@get op getItem(@path id: integer): Item | ErrorResponse;
Prompts de test
Après l'ajout d'opérations, testez avec ces prompts :
Opérations GET :
- « Lister tous les éléments et les afficher dans un tableau »
- « Affiche-moi les éléments pour l'ID utilisateur 1 »
- « Obtiens les détails de l'élément 42 »
Opérations POST :
- « Créer un nouvel élément avec le titre 'Ma tâche' pour l'utilisateur 1 »
- « Ajoute un élément : titre 'Nouvelle fonctionnalité', description 'Ajouter la connexion' »
Opérations PATCH :
- « Mets à jour l'élément 10 avec le titre 'Titre mis à jour' »
- « Change le statut de l'élément 5 à complété »
Opérations DELETE :
- « Supprime l'élément 99 »
- « Supprime l'élément avec l'ID 15 »
Bonnes pratiques
Nommage des paramètres
- Utilisez des noms de paramètres explicites :
userIdau lieu deuid - Soyez cohérent entre les opérations
- Utilisez des paramètres optionnels (
?) pour les filtres
Documentation
- Ajoutez des commentaires JSDoc à toutes les opérations
- Décrivez ce que fait chaque paramètre
- Documentez les réponses attendues
Modèles
- Utilisez
@visibility(Lifecycle.Read)pour les champs en lecture seule commeid - Utilisez
@format("date-time")pour les champs de date - Utilisez les types union pour les énumérations :
"active" | "completed" | "archived" - Rendez explicites les champs optionnels avec
?
Confirmations
- Ajoutez toujours des confirmations aux opérations destructrices (DELETE, PATCH)
- Affichez les détails clés dans le corps de la confirmation
- Utilisez l'emoji d'avertissement (⚠️) pour les actions irréversibles
Adaptive Cards
- Gardez les cartes simples et ciblées
- Utilisez le rendu conditionnel avec
${if(..., ..., 'N/A')} - Incluez des boutons d'action pour les prochaines étapes courantes
- Testez la liaison de données avec les réponses API réelles
Routage
- Utilisez les conventions RESTful :
GET /items- ListerGET /items/{id}- Obtenir unPOST /items- CréerPATCH /items/{id}- Mettre à jourDELETE /items/{id}- Supprimer
- Regroupez les opérations connexes dans le même namespace
- Utilisez les routes imbriquées pour les ressources hiérarchiques
Problèmes courants
Problème : Le paramètre n'apparaît pas dans Copilot
Solution : Vérifiez que le paramètre est correctement décoré avec @query, @path ou @body
Problème : L'Adaptive Card ne s'affiche pas
Solution : Vérifiez le chemin du fichier dans le décorateur @card et vérifiez la syntaxe JSON
Problème : La confirmation n'apparaît pas
Solution : Assurez-vous que le décorateur @capabilities est correctement formaté avec l'objet confirmation
Problème : La propriété du modèle n'apparaît pas dans la réponse
Solution : Vérifiez si la propriété a besoin de @visibility(Lifecycle.Read) ou supprimez-la si elle doit être modifiable