rozsireni
This commit is contained in:
@@ -14,6 +14,15 @@ Node.js TypeScript sluzba pro komunikaci s Google API za AppFactory reverse prox
|
|||||||
- `POST /google/oauth/service-account-token`
|
- `POST /google/oauth/service-account-token`
|
||||||
- `POST /google/oauth/revoke`
|
- `POST /google/oauth/revoke`
|
||||||
- `GET /google/oauth/tokeninfo`
|
- `GET /google/oauth/tokeninfo`
|
||||||
|
- `GET|POST|PATCH|DELETE /google/calendar/...`
|
||||||
|
- `GET|POST|PUT /google/sheets/...`
|
||||||
|
- `GET|POST|PATCH|DELETE /google/drive/...`
|
||||||
|
- `GET|POST /google/gmail/...`
|
||||||
|
- `GET|POST /google/docs/...`
|
||||||
|
- `GET|POST /google/slides/...`
|
||||||
|
- `GET|POST /google/forms/...`
|
||||||
|
- `GET|POST|PATCH|DELETE /google/people/...`
|
||||||
|
- `GET|POST|PATCH|DELETE /google/tasks/...`
|
||||||
- `POST /google/request`
|
- `POST /google/request`
|
||||||
|
|
||||||
## Konfigurace
|
## Konfigurace
|
||||||
@@ -50,3 +59,46 @@ Priklad:
|
|||||||
"accessTokenEnv": "GOOGLE_ACCESS_TOKEN"
|
"accessTokenEnv": "GOOGLE_ACCESS_TOKEN"
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
## Konkretni Google API wrappery
|
||||||
|
|
||||||
|
Sluzba ma konkretni endpointy pro bezne Google produkty, aby klient nemusel skladat cilove Google URL sam:
|
||||||
|
|
||||||
|
- Calendar: kalendare, eventy, presun eventu
|
||||||
|
- Sheets: spreadsheet metadata, batch update, cteni hodnot, update hodnot, append, batch clear
|
||||||
|
- Drive: soubory, export, opravneni
|
||||||
|
- Gmail: profil, zpravy, odeslani zpravy, labels
|
||||||
|
- Docs: vytvoreni dokumentu, nacteni dokumentu, batch update
|
||||||
|
- Slides: vytvoreni prezentace, nacteni prezentace, batch update
|
||||||
|
- Forms: vytvoreni formulare, nacteni formulare, batch update, responses
|
||||||
|
- People: connections, kontakt, vytvoreni/uprava/smazani kontaktu
|
||||||
|
- Tasks: task lists, tasky, uprava/smazani tasku
|
||||||
|
|
||||||
|
Autorizace je stejna jako u obecne proxy: `Authorization: Bearer <token>`, pripadne `accessToken`, `accessTokenEnv`, `apiKey` nebo `apiKeyEnv` v JSON body.
|
||||||
|
|
||||||
|
Priklad vytvoreni udalosti:
|
||||||
|
|
||||||
|
```json
|
||||||
|
POST /google/calendar/calendars/primary/events
|
||||||
|
{
|
||||||
|
"summary": "Schuzka",
|
||||||
|
"start": {
|
||||||
|
"dateTime": "2026-06-15T10:00:00+02:00"
|
||||||
|
},
|
||||||
|
"end": {
|
||||||
|
"dateTime": "2026-06-15T11:00:00+02:00"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Priklad append do Google Sheets:
|
||||||
|
|
||||||
|
```json
|
||||||
|
POST /google/sheets/spreadsheets/{spreadsheetId}/values/append?range=Sheet1!A1
|
||||||
|
{
|
||||||
|
"majorDimension": "ROWS",
|
||||||
|
"values": [
|
||||||
|
["A", "B", "C"]
|
||||||
|
]
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|||||||
@@ -44,6 +44,83 @@ Priklad volani Google Drive:
|
|||||||
|
|
||||||
Z bezpecnostnich duvodu jsou povolene jen HTTPS URL na Google domenach. Requesty na jine hosty sluzba odmita.
|
Z bezpecnostnich duvodu jsou povolene jen HTTPS URL na Google domenach. Requesty na jine hosty sluzba odmita.
|
||||||
|
|
||||||
|
## Konkretni produktove endpointy
|
||||||
|
|
||||||
|
Krome obecne proxy jsou dostupne konkretni wrappery nad nejbeznejsimi Google API:
|
||||||
|
|
||||||
|
### Calendar
|
||||||
|
|
||||||
|
- `GET /google/calendar/calendars`
|
||||||
|
- `POST /google/calendar/calendars`
|
||||||
|
- `GET /google/calendar/calendars/{calendarId}`
|
||||||
|
- `PATCH /google/calendar/calendars/{calendarId}`
|
||||||
|
- `DELETE /google/calendar/calendars/{calendarId}`
|
||||||
|
- `GET /google/calendar/calendars/{calendarId}/events`
|
||||||
|
- `POST /google/calendar/calendars/{calendarId}/events`
|
||||||
|
- `GET /google/calendar/calendars/{calendarId}/events/{eventId}`
|
||||||
|
- `PATCH /google/calendar/calendars/{calendarId}/events/{eventId}`
|
||||||
|
- `DELETE /google/calendar/calendars/{calendarId}/events/{eventId}`
|
||||||
|
- `POST /google/calendar/calendars/{calendarId}/events/{eventId}/move`
|
||||||
|
|
||||||
|
### Sheets
|
||||||
|
|
||||||
|
- `POST /google/sheets/spreadsheets`
|
||||||
|
- `GET /google/sheets/spreadsheets/{spreadsheetId}`
|
||||||
|
- `POST /google/sheets/spreadsheets/{spreadsheetId}/batch-update`
|
||||||
|
- `GET /google/sheets/spreadsheets/{spreadsheetId}/values?range=Sheet1!A1:B10`
|
||||||
|
- `PUT /google/sheets/spreadsheets/{spreadsheetId}/values?range=Sheet1!A1`
|
||||||
|
- `POST /google/sheets/spreadsheets/{spreadsheetId}/values/append?range=Sheet1!A1`
|
||||||
|
- `POST /google/sheets/spreadsheets/{spreadsheetId}/values/batch-update`
|
||||||
|
- `POST /google/sheets/spreadsheets/{spreadsheetId}/values/batch-clear`
|
||||||
|
|
||||||
|
### Drive
|
||||||
|
|
||||||
|
- `GET /google/drive/files`
|
||||||
|
- `POST /google/drive/files`
|
||||||
|
- `GET /google/drive/files/{fileId}`
|
||||||
|
- `PATCH /google/drive/files/{fileId}`
|
||||||
|
- `DELETE /google/drive/files/{fileId}`
|
||||||
|
- `GET /google/drive/files/{fileId}/export?mimeType=application/pdf`
|
||||||
|
- `GET /google/drive/files/{fileId}/permissions`
|
||||||
|
- `POST /google/drive/files/{fileId}/permissions`
|
||||||
|
- `DELETE /google/drive/files/{fileId}/permissions/{permissionId}`
|
||||||
|
|
||||||
|
### Gmail
|
||||||
|
|
||||||
|
- `GET /google/gmail/profile`
|
||||||
|
- `GET /google/gmail/messages`
|
||||||
|
- `GET /google/gmail/messages/{messageId}`
|
||||||
|
- `POST /google/gmail/messages/send`
|
||||||
|
- `GET /google/gmail/labels`
|
||||||
|
- `POST /google/gmail/labels`
|
||||||
|
|
||||||
|
### Docs, Slides a Forms
|
||||||
|
|
||||||
|
- `POST /google/docs/documents`
|
||||||
|
- `GET /google/docs/documents/{documentId}`
|
||||||
|
- `POST /google/docs/documents/{documentId}/batch-update`
|
||||||
|
- `POST /google/slides/presentations`
|
||||||
|
- `GET /google/slides/presentations/{presentationId}`
|
||||||
|
- `POST /google/slides/presentations/{presentationId}/batch-update`
|
||||||
|
- `POST /google/forms/forms`
|
||||||
|
- `GET /google/forms/forms/{formId}`
|
||||||
|
- `POST /google/forms/forms/{formId}/batch-update`
|
||||||
|
- `GET /google/forms/forms/{formId}/responses`
|
||||||
|
|
||||||
|
### People a Tasks
|
||||||
|
|
||||||
|
- `GET /google/people/connections`
|
||||||
|
- `GET /google/people/{resourceName}`
|
||||||
|
- `POST /google/people/contacts`
|
||||||
|
- `PATCH /google/people/{resourceName}`
|
||||||
|
- `DELETE /google/people/{resourceName}`
|
||||||
|
- `GET /google/tasks/lists`
|
||||||
|
- `POST /google/tasks/lists`
|
||||||
|
- `GET /google/tasks/lists/{tasklistId}/tasks`
|
||||||
|
- `POST /google/tasks/lists/{tasklistId}/tasks`
|
||||||
|
- `PATCH /google/tasks/lists/{tasklistId}/tasks/{taskId}`
|
||||||
|
- `DELETE /google/tasks/lists/{tasklistId}/tasks/{taskId}`
|
||||||
|
|
||||||
## AppFactory overeni
|
## AppFactory overeni
|
||||||
|
|
||||||
Po deploy over:
|
Po deploy over:
|
||||||
|
|||||||
+683
@@ -35,6 +35,14 @@ type ServiceAccountTokenBody = {
|
|||||||
privateKey?: string;
|
privateKey?: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
type GoogleRouteOptions = {
|
||||||
|
baseUrl: string;
|
||||||
|
path: string;
|
||||||
|
method?: string;
|
||||||
|
query?: Record<string, string | number | boolean | null | undefined>;
|
||||||
|
body?: JsonValue;
|
||||||
|
};
|
||||||
|
|
||||||
const app = express();
|
const app = express();
|
||||||
const port = Number(process.env.PORT || 3000);
|
const port = Number(process.env.PORT || 3000);
|
||||||
const rootPath = normalizeRootPath(process.env.ROOT_PATH || "");
|
const rootPath = normalizeRootPath(process.env.ROOT_PATH || "");
|
||||||
@@ -261,6 +269,435 @@ app.get("/google/oauth/tokeninfo", async (req, res) => {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
app.get("/google/calendar/calendars", async (req, res) => {
|
||||||
|
await callGoogle(req, res, { baseUrl: "https://www.googleapis.com", path: "/calendar/v3/users/me/calendarList" });
|
||||||
|
});
|
||||||
|
|
||||||
|
app.post("/google/calendar/calendars", async (req, res) => {
|
||||||
|
await callGoogle(req, res, {
|
||||||
|
baseUrl: "https://www.googleapis.com",
|
||||||
|
path: "/calendar/v3/calendars",
|
||||||
|
method: "POST",
|
||||||
|
body: req.body as JsonValue
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
app.get("/google/calendar/calendars/:calendarId", async (req, res) => {
|
||||||
|
await callGoogle(req, res, {
|
||||||
|
baseUrl: "https://www.googleapis.com",
|
||||||
|
path: `/calendar/v3/calendars/${encodePathParam(req.params.calendarId)}`
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
app.patch("/google/calendar/calendars/:calendarId", async (req, res) => {
|
||||||
|
await callGoogle(req, res, {
|
||||||
|
baseUrl: "https://www.googleapis.com",
|
||||||
|
path: `/calendar/v3/calendars/${encodePathParam(req.params.calendarId)}`,
|
||||||
|
method: "PATCH",
|
||||||
|
body: req.body as JsonValue
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
app.delete("/google/calendar/calendars/:calendarId", async (req, res) => {
|
||||||
|
await callGoogle(req, res, {
|
||||||
|
baseUrl: "https://www.googleapis.com",
|
||||||
|
path: `/calendar/v3/calendars/${encodePathParam(req.params.calendarId)}`,
|
||||||
|
method: "DELETE"
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
app.get("/google/calendar/calendars/:calendarId/events", async (req, res) => {
|
||||||
|
await callGoogle(req, res, {
|
||||||
|
baseUrl: "https://www.googleapis.com",
|
||||||
|
path: `/calendar/v3/calendars/${encodePathParam(req.params.calendarId)}/events`
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
app.post("/google/calendar/calendars/:calendarId/events", async (req, res) => {
|
||||||
|
await callGoogle(req, res, {
|
||||||
|
baseUrl: "https://www.googleapis.com",
|
||||||
|
path: `/calendar/v3/calendars/${encodePathParam(req.params.calendarId)}/events`,
|
||||||
|
method: "POST",
|
||||||
|
body: req.body as JsonValue
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
app.get("/google/calendar/calendars/:calendarId/events/:eventId", async (req, res) => {
|
||||||
|
await callGoogle(req, res, {
|
||||||
|
baseUrl: "https://www.googleapis.com",
|
||||||
|
path: `/calendar/v3/calendars/${encodePathParam(req.params.calendarId)}/events/${encodePathParam(req.params.eventId)}`
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
app.patch("/google/calendar/calendars/:calendarId/events/:eventId", async (req, res) => {
|
||||||
|
await callGoogle(req, res, {
|
||||||
|
baseUrl: "https://www.googleapis.com",
|
||||||
|
path: `/calendar/v3/calendars/${encodePathParam(req.params.calendarId)}/events/${encodePathParam(req.params.eventId)}`,
|
||||||
|
method: "PATCH",
|
||||||
|
body: req.body as JsonValue
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
app.delete("/google/calendar/calendars/:calendarId/events/:eventId", async (req, res) => {
|
||||||
|
await callGoogle(req, res, {
|
||||||
|
baseUrl: "https://www.googleapis.com",
|
||||||
|
path: `/calendar/v3/calendars/${encodePathParam(req.params.calendarId)}/events/${encodePathParam(req.params.eventId)}`,
|
||||||
|
method: "DELETE"
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
app.post("/google/calendar/calendars/:calendarId/events/:eventId/move", async (req, res) => {
|
||||||
|
await callGoogle(req, res, {
|
||||||
|
baseUrl: "https://www.googleapis.com",
|
||||||
|
path: `/calendar/v3/calendars/${encodePathParam(req.params.calendarId)}/events/${encodePathParam(req.params.eventId)}/move`,
|
||||||
|
method: "POST"
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
app.post("/google/sheets/spreadsheets", async (req, res) => {
|
||||||
|
await callGoogle(req, res, {
|
||||||
|
baseUrl: "https://sheets.googleapis.com",
|
||||||
|
path: "/v4/spreadsheets",
|
||||||
|
method: "POST",
|
||||||
|
body: req.body as JsonValue
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
app.get("/google/sheets/spreadsheets/:spreadsheetId", async (req, res) => {
|
||||||
|
await callGoogle(req, res, {
|
||||||
|
baseUrl: "https://sheets.googleapis.com",
|
||||||
|
path: `/v4/spreadsheets/${encodePathParam(req.params.spreadsheetId)}`
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
app.post("/google/sheets/spreadsheets/:spreadsheetId/batch-update", async (req, res) => {
|
||||||
|
await callGoogle(req, res, {
|
||||||
|
baseUrl: "https://sheets.googleapis.com",
|
||||||
|
path: `/v4/spreadsheets/${encodePathParam(req.params.spreadsheetId)}:batchUpdate`,
|
||||||
|
method: "POST",
|
||||||
|
body: req.body as JsonValue
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
app.get("/google/sheets/spreadsheets/:spreadsheetId/values", async (req, res) => {
|
||||||
|
const range = requireQueryString(req, res, "range");
|
||||||
|
if (!range) return;
|
||||||
|
await callGoogle(req, res, {
|
||||||
|
baseUrl: "https://sheets.googleapis.com",
|
||||||
|
path: `/v4/spreadsheets/${encodePathParam(req.params.spreadsheetId)}/values/${encodePathParam(range)}`
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
app.put("/google/sheets/spreadsheets/:spreadsheetId/values", async (req, res) => {
|
||||||
|
const range = requireQueryString(req, res, "range");
|
||||||
|
if (!range) return;
|
||||||
|
await callGoogle(req, res, {
|
||||||
|
baseUrl: "https://sheets.googleapis.com",
|
||||||
|
path: `/v4/spreadsheets/${encodePathParam(req.params.spreadsheetId)}/values/${encodePathParam(range)}`,
|
||||||
|
method: "PUT",
|
||||||
|
body: req.body as JsonValue
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
app.post("/google/sheets/spreadsheets/:spreadsheetId/values/append", async (req, res) => {
|
||||||
|
const range = requireQueryString(req, res, "range");
|
||||||
|
if (!range) return;
|
||||||
|
await callGoogle(req, res, {
|
||||||
|
baseUrl: "https://sheets.googleapis.com",
|
||||||
|
path: `/v4/spreadsheets/${encodePathParam(req.params.spreadsheetId)}/values/${encodePathParam(range)}:append`,
|
||||||
|
method: "POST",
|
||||||
|
body: req.body as JsonValue
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
app.post("/google/sheets/spreadsheets/:spreadsheetId/values/batch-update", async (req, res) => {
|
||||||
|
await callGoogle(req, res, {
|
||||||
|
baseUrl: "https://sheets.googleapis.com",
|
||||||
|
path: `/v4/spreadsheets/${encodePathParam(req.params.spreadsheetId)}/values:batchUpdate`,
|
||||||
|
method: "POST",
|
||||||
|
body: req.body as JsonValue
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
app.post("/google/sheets/spreadsheets/:spreadsheetId/values/batch-clear", async (req, res) => {
|
||||||
|
await callGoogle(req, res, {
|
||||||
|
baseUrl: "https://sheets.googleapis.com",
|
||||||
|
path: `/v4/spreadsheets/${encodePathParam(req.params.spreadsheetId)}/values:batchClear`,
|
||||||
|
method: "POST",
|
||||||
|
body: req.body as JsonValue
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
app.get("/google/drive/files", async (req, res) => {
|
||||||
|
await callGoogle(req, res, { baseUrl: "https://www.googleapis.com", path: "/drive/v3/files" });
|
||||||
|
});
|
||||||
|
|
||||||
|
app.post("/google/drive/files", async (req, res) => {
|
||||||
|
await callGoogle(req, res, {
|
||||||
|
baseUrl: "https://www.googleapis.com",
|
||||||
|
path: "/drive/v3/files",
|
||||||
|
method: "POST",
|
||||||
|
body: req.body as JsonValue
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
app.get("/google/drive/files/:fileId", async (req, res) => {
|
||||||
|
await callGoogle(req, res, {
|
||||||
|
baseUrl: "https://www.googleapis.com",
|
||||||
|
path: `/drive/v3/files/${encodePathParam(req.params.fileId)}`
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
app.patch("/google/drive/files/:fileId", async (req, res) => {
|
||||||
|
await callGoogle(req, res, {
|
||||||
|
baseUrl: "https://www.googleapis.com",
|
||||||
|
path: `/drive/v3/files/${encodePathParam(req.params.fileId)}`,
|
||||||
|
method: "PATCH",
|
||||||
|
body: req.body as JsonValue
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
app.delete("/google/drive/files/:fileId", async (req, res) => {
|
||||||
|
await callGoogle(req, res, {
|
||||||
|
baseUrl: "https://www.googleapis.com",
|
||||||
|
path: `/drive/v3/files/${encodePathParam(req.params.fileId)}`,
|
||||||
|
method: "DELETE"
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
app.get("/google/drive/files/:fileId/export", async (req, res) => {
|
||||||
|
await callGoogle(req, res, {
|
||||||
|
baseUrl: "https://www.googleapis.com",
|
||||||
|
path: `/drive/v3/files/${encodePathParam(req.params.fileId)}/export`
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
app.get("/google/drive/files/:fileId/permissions", async (req, res) => {
|
||||||
|
await callGoogle(req, res, {
|
||||||
|
baseUrl: "https://www.googleapis.com",
|
||||||
|
path: `/drive/v3/files/${encodePathParam(req.params.fileId)}/permissions`
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
app.post("/google/drive/files/:fileId/permissions", async (req, res) => {
|
||||||
|
await callGoogle(req, res, {
|
||||||
|
baseUrl: "https://www.googleapis.com",
|
||||||
|
path: `/drive/v3/files/${encodePathParam(req.params.fileId)}/permissions`,
|
||||||
|
method: "POST",
|
||||||
|
body: req.body as JsonValue
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
app.delete("/google/drive/files/:fileId/permissions/:permissionId", async (req, res) => {
|
||||||
|
await callGoogle(req, res, {
|
||||||
|
baseUrl: "https://www.googleapis.com",
|
||||||
|
path: `/drive/v3/files/${encodePathParam(req.params.fileId)}/permissions/${encodePathParam(req.params.permissionId)}`,
|
||||||
|
method: "DELETE"
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
app.get("/google/gmail/profile", async (req, res) => {
|
||||||
|
await callGoogle(req, res, { baseUrl: "https://gmail.googleapis.com", path: "/gmail/v1/users/me/profile" });
|
||||||
|
});
|
||||||
|
|
||||||
|
app.get("/google/gmail/messages", async (req, res) => {
|
||||||
|
await callGoogle(req, res, { baseUrl: "https://gmail.googleapis.com", path: "/gmail/v1/users/me/messages" });
|
||||||
|
});
|
||||||
|
|
||||||
|
app.get("/google/gmail/messages/:messageId", async (req, res) => {
|
||||||
|
await callGoogle(req, res, {
|
||||||
|
baseUrl: "https://gmail.googleapis.com",
|
||||||
|
path: `/gmail/v1/users/me/messages/${encodePathParam(req.params.messageId)}`
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
app.post("/google/gmail/messages/send", async (req, res) => {
|
||||||
|
await callGoogle(req, res, {
|
||||||
|
baseUrl: "https://gmail.googleapis.com",
|
||||||
|
path: "/gmail/v1/users/me/messages/send",
|
||||||
|
method: "POST",
|
||||||
|
body: req.body as JsonValue
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
app.get("/google/gmail/labels", async (req, res) => {
|
||||||
|
await callGoogle(req, res, { baseUrl: "https://gmail.googleapis.com", path: "/gmail/v1/users/me/labels" });
|
||||||
|
});
|
||||||
|
|
||||||
|
app.post("/google/gmail/labels", async (req, res) => {
|
||||||
|
await callGoogle(req, res, {
|
||||||
|
baseUrl: "https://gmail.googleapis.com",
|
||||||
|
path: "/gmail/v1/users/me/labels",
|
||||||
|
method: "POST",
|
||||||
|
body: req.body as JsonValue
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
app.post("/google/docs/documents", async (req, res) => {
|
||||||
|
await callGoogle(req, res, {
|
||||||
|
baseUrl: "https://docs.googleapis.com",
|
||||||
|
path: "/v1/documents",
|
||||||
|
method: "POST",
|
||||||
|
body: req.body as JsonValue
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
app.get("/google/docs/documents/:documentId", async (req, res) => {
|
||||||
|
await callGoogle(req, res, {
|
||||||
|
baseUrl: "https://docs.googleapis.com",
|
||||||
|
path: `/v1/documents/${encodePathParam(req.params.documentId)}`
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
app.post("/google/docs/documents/:documentId/batch-update", async (req, res) => {
|
||||||
|
await callGoogle(req, res, {
|
||||||
|
baseUrl: "https://docs.googleapis.com",
|
||||||
|
path: `/v1/documents/${encodePathParam(req.params.documentId)}:batchUpdate`,
|
||||||
|
method: "POST",
|
||||||
|
body: req.body as JsonValue
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
app.post("/google/slides/presentations", async (req, res) => {
|
||||||
|
await callGoogle(req, res, {
|
||||||
|
baseUrl: "https://slides.googleapis.com",
|
||||||
|
path: "/v1/presentations",
|
||||||
|
method: "POST",
|
||||||
|
body: req.body as JsonValue
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
app.get("/google/slides/presentations/:presentationId", async (req, res) => {
|
||||||
|
await callGoogle(req, res, {
|
||||||
|
baseUrl: "https://slides.googleapis.com",
|
||||||
|
path: `/v1/presentations/${encodePathParam(req.params.presentationId)}`
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
app.post("/google/slides/presentations/:presentationId/batch-update", async (req, res) => {
|
||||||
|
await callGoogle(req, res, {
|
||||||
|
baseUrl: "https://slides.googleapis.com",
|
||||||
|
path: `/v1/presentations/${encodePathParam(req.params.presentationId)}:batchUpdate`,
|
||||||
|
method: "POST",
|
||||||
|
body: req.body as JsonValue
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
app.get("/google/forms/forms/:formId", async (req, res) => {
|
||||||
|
await callGoogle(req, res, {
|
||||||
|
baseUrl: "https://forms.googleapis.com",
|
||||||
|
path: `/v1/forms/${encodePathParam(req.params.formId)}`
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
app.post("/google/forms/forms", async (req, res) => {
|
||||||
|
await callGoogle(req, res, {
|
||||||
|
baseUrl: "https://forms.googleapis.com",
|
||||||
|
path: "/v1/forms",
|
||||||
|
method: "POST",
|
||||||
|
body: req.body as JsonValue
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
app.post("/google/forms/forms/:formId/batch-update", async (req, res) => {
|
||||||
|
await callGoogle(req, res, {
|
||||||
|
baseUrl: "https://forms.googleapis.com",
|
||||||
|
path: `/v1/forms/${encodePathParam(req.params.formId)}:batchUpdate`,
|
||||||
|
method: "POST",
|
||||||
|
body: req.body as JsonValue
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
app.get("/google/forms/forms/:formId/responses", async (req, res) => {
|
||||||
|
await callGoogle(req, res, {
|
||||||
|
baseUrl: "https://forms.googleapis.com",
|
||||||
|
path: `/v1/forms/${encodePathParam(req.params.formId)}/responses`
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
app.get("/google/people/connections", async (req, res) => {
|
||||||
|
await callGoogle(req, res, { baseUrl: "https://people.googleapis.com", path: "/v1/people/me/connections" });
|
||||||
|
});
|
||||||
|
|
||||||
|
app.get("/google/people/:resourceName", async (req, res) => {
|
||||||
|
await callGoogle(req, res, {
|
||||||
|
baseUrl: "https://people.googleapis.com",
|
||||||
|
path: `/v1/people/${encodePathParam(req.params.resourceName)}`
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
app.post("/google/people/contacts", async (req, res) => {
|
||||||
|
await callGoogle(req, res, {
|
||||||
|
baseUrl: "https://people.googleapis.com",
|
||||||
|
path: "/v1/people:createContact",
|
||||||
|
method: "POST",
|
||||||
|
body: req.body as JsonValue
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
app.patch("/google/people/:resourceName", async (req, res) => {
|
||||||
|
await callGoogle(req, res, {
|
||||||
|
baseUrl: "https://people.googleapis.com",
|
||||||
|
path: `/v1/people/${encodePathParam(req.params.resourceName)}:updateContact`,
|
||||||
|
method: "PATCH",
|
||||||
|
body: req.body as JsonValue
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
app.delete("/google/people/:resourceName", async (req, res) => {
|
||||||
|
await callGoogle(req, res, {
|
||||||
|
baseUrl: "https://people.googleapis.com",
|
||||||
|
path: `/v1/people/${encodePathParam(req.params.resourceName)}:deleteContact`,
|
||||||
|
method: "DELETE"
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
app.get("/google/tasks/lists", async (req, res) => {
|
||||||
|
await callGoogle(req, res, { baseUrl: "https://tasks.googleapis.com", path: "/tasks/v1/users/@me/lists" });
|
||||||
|
});
|
||||||
|
|
||||||
|
app.post("/google/tasks/lists", async (req, res) => {
|
||||||
|
await callGoogle(req, res, {
|
||||||
|
baseUrl: "https://tasks.googleapis.com",
|
||||||
|
path: "/tasks/v1/users/@me/lists",
|
||||||
|
method: "POST",
|
||||||
|
body: req.body as JsonValue
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
app.get("/google/tasks/lists/:tasklistId/tasks", async (req, res) => {
|
||||||
|
await callGoogle(req, res, {
|
||||||
|
baseUrl: "https://tasks.googleapis.com",
|
||||||
|
path: `/tasks/v1/lists/${encodePathParam(req.params.tasklistId)}/tasks`
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
app.post("/google/tasks/lists/:tasklistId/tasks", async (req, res) => {
|
||||||
|
await callGoogle(req, res, {
|
||||||
|
baseUrl: "https://tasks.googleapis.com",
|
||||||
|
path: `/tasks/v1/lists/${encodePathParam(req.params.tasklistId)}/tasks`,
|
||||||
|
method: "POST",
|
||||||
|
body: req.body as JsonValue
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
app.patch("/google/tasks/lists/:tasklistId/tasks/:taskId", async (req, res) => {
|
||||||
|
await callGoogle(req, res, {
|
||||||
|
baseUrl: "https://tasks.googleapis.com",
|
||||||
|
path: `/tasks/v1/lists/${encodePathParam(req.params.tasklistId)}/tasks/${encodePathParam(req.params.taskId)}`,
|
||||||
|
method: "PATCH",
|
||||||
|
body: req.body as JsonValue
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
app.delete("/google/tasks/lists/:tasklistId/tasks/:taskId", async (req, res) => {
|
||||||
|
await callGoogle(req, res, {
|
||||||
|
baseUrl: "https://tasks.googleapis.com",
|
||||||
|
path: `/tasks/v1/lists/${encodePathParam(req.params.tasklistId)}/tasks/${encodePathParam(req.params.taskId)}`,
|
||||||
|
method: "DELETE"
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
app.post("/google/request", async (req, res) => {
|
app.post("/google/request", async (req, res) => {
|
||||||
try {
|
try {
|
||||||
const requestBody = req.body as GoogleRequestBody;
|
const requestBody = req.body as GoogleRequestBody;
|
||||||
@@ -305,6 +742,73 @@ function withRootPath(path: string): string {
|
|||||||
return rootPath + path;
|
return rootPath + path;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async function callGoogle(req: Request, res: Response, options: GoogleRouteOptions): Promise<void> {
|
||||||
|
try {
|
||||||
|
const requestBody: GoogleRequestBody = {
|
||||||
|
baseUrl: options.baseUrl,
|
||||||
|
path: options.path,
|
||||||
|
method: options.method || req.method,
|
||||||
|
query: { ...queryToRecord(req.query), ...(options.query || {}) },
|
||||||
|
body: options.body,
|
||||||
|
accessToken: readBodyString(req.body, "accessToken"),
|
||||||
|
accessTokenEnv: readBodyString(req.body, "accessTokenEnv"),
|
||||||
|
apiKey: readBodyString(req.body, "apiKey"),
|
||||||
|
apiKeyEnv: readBodyString(req.body, "apiKeyEnv")
|
||||||
|
};
|
||||||
|
|
||||||
|
const targetUrl = buildGoogleUrl(requestBody);
|
||||||
|
const headers = buildGoogleHeaders(req, requestBody);
|
||||||
|
const method = (requestBody.method || "GET").toUpperCase();
|
||||||
|
const init: RequestInit = { method, headers };
|
||||||
|
|
||||||
|
if (!["GET", "HEAD"].includes(method) && options.body !== undefined) {
|
||||||
|
init.body = JSON.stringify(options.body);
|
||||||
|
}
|
||||||
|
|
||||||
|
const response = await fetch(targetUrl, init);
|
||||||
|
await relayResponse(res, response);
|
||||||
|
} catch (error) {
|
||||||
|
sendError(res, error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function queryToRecord(query: Request["query"]): Record<string, string | number | boolean | null | undefined> {
|
||||||
|
const result: Record<string, string | number | boolean | null | undefined> = {};
|
||||||
|
|
||||||
|
for (const [key, value] of Object.entries(query)) {
|
||||||
|
if (typeof value === "string") {
|
||||||
|
result[key] = value;
|
||||||
|
} else if (Array.isArray(value)) {
|
||||||
|
result[key] = value.map((item) => String(item)).join(",");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
function readBodyString(body: unknown, key: string): string | undefined {
|
||||||
|
if (!body || typeof body !== "object") {
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
const value = (body as Record<string, unknown>)[key];
|
||||||
|
return typeof value === "string" ? value : undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
function requireQueryString(req: Request, res: Response, key: string): string | undefined {
|
||||||
|
const value = req.query[key];
|
||||||
|
if (typeof value === "string" && value) {
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
badRequest(res, `Missing required query parameter: ${key}`);
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
function encodePathParam(value: string): string {
|
||||||
|
return encodeURIComponent(value);
|
||||||
|
}
|
||||||
|
|
||||||
function buildGoogleUrl(body: GoogleRequestBody): string {
|
function buildGoogleUrl(body: GoogleRequestBody): string {
|
||||||
const rawUrl = body.url || joinBaseAndPath(body.baseUrl || "https://www.googleapis.com", body.path || "");
|
const rawUrl = body.url || joinBaseAndPath(body.baseUrl || "https://www.googleapis.com", body.path || "");
|
||||||
const url = new URL(rawUrl);
|
const url = new URL(rawUrl);
|
||||||
@@ -486,6 +990,15 @@ function buildOpenApiDocument(): Record<string, unknown> {
|
|||||||
{ name: "System" },
|
{ name: "System" },
|
||||||
{ name: "Google Discovery" },
|
{ name: "Google Discovery" },
|
||||||
{ name: "Google OAuth" },
|
{ name: "Google OAuth" },
|
||||||
|
{ name: "Google Calendar" },
|
||||||
|
{ name: "Google Sheets" },
|
||||||
|
{ name: "Google Drive" },
|
||||||
|
{ name: "Gmail" },
|
||||||
|
{ name: "Google Docs" },
|
||||||
|
{ name: "Google Slides" },
|
||||||
|
{ name: "Google Forms" },
|
||||||
|
{ name: "Google People" },
|
||||||
|
{ name: "Google Tasks" },
|
||||||
{ name: "Google API" }
|
{ name: "Google API" }
|
||||||
],
|
],
|
||||||
paths: {
|
paths: {
|
||||||
@@ -553,6 +1066,7 @@ function buildOpenApiDocument(): Record<string, unknown> {
|
|||||||
responses: { "200": { description: "Token info" }, "400": { description: "Invalid request" } }
|
responses: { "200": { description: "Token info" }, "400": { description: "Invalid request" } }
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
...buildGoogleServiceOpenApiPaths(),
|
||||||
"/google/request": {
|
"/google/request": {
|
||||||
post: {
|
post: {
|
||||||
tags: ["Google API"],
|
tags: ["Google API"],
|
||||||
@@ -591,3 +1105,172 @@ function jsonRequestBody(example: Record<string, unknown>): Record<string, unkno
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function buildGoogleServiceOpenApiPaths(): Record<string, unknown> {
|
||||||
|
return {
|
||||||
|
"/google/calendar/calendars": {
|
||||||
|
get: googleOperation("Google Calendar", "List calendars"),
|
||||||
|
post: googleOperation("Google Calendar", "Create calendar", { summary: "Team calendar" })
|
||||||
|
},
|
||||||
|
"/google/calendar/calendars/{calendarId}": {
|
||||||
|
get: googleOperation("Google Calendar", "Get calendar", undefined, ["calendarId"]),
|
||||||
|
patch: googleOperation("Google Calendar", "Update calendar", { summary: "Updated calendar" }, ["calendarId"]),
|
||||||
|
delete: googleOperation("Google Calendar", "Delete calendar", undefined, ["calendarId"])
|
||||||
|
},
|
||||||
|
"/google/calendar/calendars/{calendarId}/events": {
|
||||||
|
get: googleOperation("Google Calendar", "List events", undefined, ["calendarId"]),
|
||||||
|
post: googleOperation("Google Calendar", "Create event", { summary: "Meeting", start: {}, end: {} }, ["calendarId"])
|
||||||
|
},
|
||||||
|
"/google/calendar/calendars/{calendarId}/events/{eventId}": {
|
||||||
|
get: googleOperation("Google Calendar", "Get event", undefined, ["calendarId", "eventId"]),
|
||||||
|
patch: googleOperation("Google Calendar", "Update event", { summary: "Updated meeting" }, ["calendarId", "eventId"]),
|
||||||
|
delete: googleOperation("Google Calendar", "Delete event", undefined, ["calendarId", "eventId"])
|
||||||
|
},
|
||||||
|
"/google/calendar/calendars/{calendarId}/events/{eventId}/move": {
|
||||||
|
post: googleOperation("Google Calendar", "Move event to another calendar", undefined, ["calendarId", "eventId"])
|
||||||
|
},
|
||||||
|
"/google/sheets/spreadsheets": {
|
||||||
|
post: googleOperation("Google Sheets", "Create spreadsheet", { properties: { title: "New spreadsheet" } })
|
||||||
|
},
|
||||||
|
"/google/sheets/spreadsheets/{spreadsheetId}": {
|
||||||
|
get: googleOperation("Google Sheets", "Get spreadsheet", undefined, ["spreadsheetId"])
|
||||||
|
},
|
||||||
|
"/google/sheets/spreadsheets/{spreadsheetId}/batch-update": {
|
||||||
|
post: googleOperation("Google Sheets", "Batch update spreadsheet", { requests: [] }, ["spreadsheetId"])
|
||||||
|
},
|
||||||
|
"/google/sheets/spreadsheets/{spreadsheetId}/values": {
|
||||||
|
get: googleOperation("Google Sheets", "Get values by range query parameter", undefined, ["spreadsheetId"], ["range"]),
|
||||||
|
put: googleOperation("Google Sheets", "Update values by range query parameter", { values: [["value"]] }, ["spreadsheetId"], ["range"])
|
||||||
|
},
|
||||||
|
"/google/sheets/spreadsheets/{spreadsheetId}/values/append": {
|
||||||
|
post: googleOperation("Google Sheets", "Append values by range query parameter", { values: [["value"]] }, ["spreadsheetId"], ["range"])
|
||||||
|
},
|
||||||
|
"/google/sheets/spreadsheets/{spreadsheetId}/values/batch-update": {
|
||||||
|
post: googleOperation("Google Sheets", "Batch update values", { data: [], valueInputOption: "USER_ENTERED" }, ["spreadsheetId"])
|
||||||
|
},
|
||||||
|
"/google/sheets/spreadsheets/{spreadsheetId}/values/batch-clear": {
|
||||||
|
post: googleOperation("Google Sheets", "Batch clear values", { ranges: [] }, ["spreadsheetId"])
|
||||||
|
},
|
||||||
|
"/google/drive/files": {
|
||||||
|
get: googleOperation("Google Drive", "List files"),
|
||||||
|
post: googleOperation("Google Drive", "Create file metadata", { name: "New file" })
|
||||||
|
},
|
||||||
|
"/google/drive/files/{fileId}": {
|
||||||
|
get: googleOperation("Google Drive", "Get file metadata", undefined, ["fileId"]),
|
||||||
|
patch: googleOperation("Google Drive", "Update file metadata", { name: "Renamed file" }, ["fileId"]),
|
||||||
|
delete: googleOperation("Google Drive", "Delete file", undefined, ["fileId"])
|
||||||
|
},
|
||||||
|
"/google/drive/files/{fileId}/export": {
|
||||||
|
get: googleOperation("Google Drive", "Export Google Workspace file", undefined, ["fileId"], ["mimeType"])
|
||||||
|
},
|
||||||
|
"/google/drive/files/{fileId}/permissions": {
|
||||||
|
get: googleOperation("Google Drive", "List file permissions", undefined, ["fileId"]),
|
||||||
|
post: googleOperation("Google Drive", "Create file permission", { type: "user", role: "reader", emailAddress: "user@example.com" }, ["fileId"])
|
||||||
|
},
|
||||||
|
"/google/drive/files/{fileId}/permissions/{permissionId}": {
|
||||||
|
delete: googleOperation("Google Drive", "Delete file permission", undefined, ["fileId", "permissionId"])
|
||||||
|
},
|
||||||
|
"/google/gmail/profile": {
|
||||||
|
get: googleOperation("Gmail", "Get Gmail profile")
|
||||||
|
},
|
||||||
|
"/google/gmail/messages": {
|
||||||
|
get: googleOperation("Gmail", "List messages")
|
||||||
|
},
|
||||||
|
"/google/gmail/messages/{messageId}": {
|
||||||
|
get: googleOperation("Gmail", "Get message", undefined, ["messageId"])
|
||||||
|
},
|
||||||
|
"/google/gmail/messages/send": {
|
||||||
|
post: googleOperation("Gmail", "Send message", { raw: "base64url-rfc2822-message" })
|
||||||
|
},
|
||||||
|
"/google/gmail/labels": {
|
||||||
|
get: googleOperation("Gmail", "List labels"),
|
||||||
|
post: googleOperation("Gmail", "Create label", { name: "Label name" })
|
||||||
|
},
|
||||||
|
"/google/docs/documents": {
|
||||||
|
post: googleOperation("Google Docs", "Create document", { title: "New document" })
|
||||||
|
},
|
||||||
|
"/google/docs/documents/{documentId}": {
|
||||||
|
get: googleOperation("Google Docs", "Get document", undefined, ["documentId"])
|
||||||
|
},
|
||||||
|
"/google/docs/documents/{documentId}/batch-update": {
|
||||||
|
post: googleOperation("Google Docs", "Batch update document", { requests: [] }, ["documentId"])
|
||||||
|
},
|
||||||
|
"/google/slides/presentations": {
|
||||||
|
post: googleOperation("Google Slides", "Create presentation", { title: "New presentation" })
|
||||||
|
},
|
||||||
|
"/google/slides/presentations/{presentationId}": {
|
||||||
|
get: googleOperation("Google Slides", "Get presentation", undefined, ["presentationId"])
|
||||||
|
},
|
||||||
|
"/google/slides/presentations/{presentationId}/batch-update": {
|
||||||
|
post: googleOperation("Google Slides", "Batch update presentation", { requests: [] }, ["presentationId"])
|
||||||
|
},
|
||||||
|
"/google/forms/forms": {
|
||||||
|
post: googleOperation("Google Forms", "Create form", { info: { title: "New form" } })
|
||||||
|
},
|
||||||
|
"/google/forms/forms/{formId}": {
|
||||||
|
get: googleOperation("Google Forms", "Get form", undefined, ["formId"])
|
||||||
|
},
|
||||||
|
"/google/forms/forms/{formId}/batch-update": {
|
||||||
|
post: googleOperation("Google Forms", "Batch update form", { requests: [] }, ["formId"])
|
||||||
|
},
|
||||||
|
"/google/forms/forms/{formId}/responses": {
|
||||||
|
get: googleOperation("Google Forms", "List form responses", undefined, ["formId"])
|
||||||
|
},
|
||||||
|
"/google/people/connections": {
|
||||||
|
get: googleOperation("Google People", "List connections")
|
||||||
|
},
|
||||||
|
"/google/people/{resourceName}": {
|
||||||
|
get: googleOperation("Google People", "Get person/contact", undefined, ["resourceName"]),
|
||||||
|
patch: googleOperation("Google People", "Update contact", {}, ["resourceName"]),
|
||||||
|
delete: googleOperation("Google People", "Delete contact", undefined, ["resourceName"])
|
||||||
|
},
|
||||||
|
"/google/people/contacts": {
|
||||||
|
post: googleOperation("Google People", "Create contact", { names: [{ givenName: "Ada", familyName: "Lovelace" }] })
|
||||||
|
},
|
||||||
|
"/google/tasks/lists": {
|
||||||
|
get: googleOperation("Google Tasks", "List task lists"),
|
||||||
|
post: googleOperation("Google Tasks", "Create task list", { title: "Task list" })
|
||||||
|
},
|
||||||
|
"/google/tasks/lists/{tasklistId}/tasks": {
|
||||||
|
get: googleOperation("Google Tasks", "List tasks", undefined, ["tasklistId"]),
|
||||||
|
post: googleOperation("Google Tasks", "Create task", { title: "Task" }, ["tasklistId"])
|
||||||
|
},
|
||||||
|
"/google/tasks/lists/{tasklistId}/tasks/{taskId}": {
|
||||||
|
patch: googleOperation("Google Tasks", "Update task", { title: "Updated task" }, ["tasklistId", "taskId"]),
|
||||||
|
delete: googleOperation("Google Tasks", "Delete task", undefined, ["tasklistId", "taskId"])
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
function googleOperation(
|
||||||
|
tag: string,
|
||||||
|
summary: string,
|
||||||
|
example?: Record<string, unknown>,
|
||||||
|
pathParams: string[] = [],
|
||||||
|
queryParams: string[] = []
|
||||||
|
): Record<string, unknown> {
|
||||||
|
const parameters = [
|
||||||
|
...pathParams.map((name) => ({ name, in: "path", required: true, schema: { type: "string" } })),
|
||||||
|
...queryParams.map((name) => ({ name, in: "query", required: true, schema: { type: "string" } }))
|
||||||
|
];
|
||||||
|
|
||||||
|
const operation: Record<string, unknown> = {
|
||||||
|
tags: [tag],
|
||||||
|
summary,
|
||||||
|
security: [{ bearerAuth: [] }],
|
||||||
|
responses: {
|
||||||
|
"200": { description: "Google API response" },
|
||||||
|
"400": { description: "Invalid request or Google API error" }
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
if (parameters.length > 0) {
|
||||||
|
operation.parameters = parameters;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (example) {
|
||||||
|
operation.requestBody = jsonRequestBody(example);
|
||||||
|
}
|
||||||
|
|
||||||
|
return operation;
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user