Files
request-portal/tools/sp/README.md
T
2026-08-21 10:01:33 +00:00

99 lines
6.4 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# `tools/sp` — запросы к SharePoint портала заявок
Инструмент для **разбора**, а не для правки данных: задать тенанту вопрос и получить
ответ текстом, который можно приложить к задаче.
Появился из-за производительности: раз за разом нужно было выяснить «а что реально
отдаёт поисковый индекс?», «сколько на самом деле партиций у типа заявки?» — и каждый
раз это была ручная работа со снипетом в консоли браузера и копипастой результата.
## Что важно понимать про доступ
**Секреты через инструмент не проходят.** Вход — device code: команда печатает код,
пользователь вводит его в браузере сам, дальше CLI for Microsoft 365 держит токен у
себя. Ни пароля, ни client secret в репозитории и в командах нет.
**Работаем от имени пользователя, а не приложения.** Это не удобство, а требование:
SharePoint Search REST (`/_api/search/query`) под app-only-токеном обычно не отдаёт
ничего, ему нужен пользовательский контекст. А Search — главное, что здесь измеряют.
Побочный плюс: видно ровно то же, что видит пользователь в портале.
## Разовая подготовка
CLI for Microsoft 365 начиная с 7-й версии **не возит собственное приложение** — нужен
свой Entra app. Регистрируется один раз на всю команду.
1. Entra admin center → **App registrations** → **New registration**
- Name: `Portal Tools CLI`
- Supported account types: **Single tenant**
- Redirect URI: **Public client/native** → `http://localhost`
2. **Authentication** → Advanced settings → **Allow public client flows** → **Yes**
(без этого device code не работает)
3. **API permissions** → Add a permission → **SharePoint** → **Delegated**:
- `AllSites.Read` — чтение списков
- `Sites.Search.All` — поисковый индекс
4. **Grant admin consent** (нужен админ тенанта)
5. Скопировать **Application (client) ID** и **Directory (tenant) ID**
Дальше — переменные окружения (PowerShell, на сессию):
```powershell
$env:SP_APP_ID = "<application-client-id>"
$env:SP_TENANT = "<directory-tenant-id>"
```
`SP_SITE` по умолчанию `https://tnsukz.sharepoint.com/sites/requests` — тот же адрес,
что в `src/webparts/requestPortal/shared/config.ts`. Задавать нужно, только если
списки типов заявок живут на другом сайте.
## Использование
```bash
cd tools/sp
npm install # один раз
node sp.mjs login # покажет код — ввести его в браузере
node sp.mjs status
```
| команда | зачем |
| --- | --- |
| `node sp.mjs props` | отдаёт ли индекс поля заявки (`Retrievable`) — от этого зависит, можно ли убрать REST-дочитывание в `loadByIds` |
| `node sp.mjs lists` | все портальные списки с размерами и адресами — сколько реально партиций |
| `node sp.mjs search "<KQL>" --select a,b --rows N` | тот же поисковый запрос, что делает портал |
| `node sp.mjs get "/_api/web/..."` | произвольный GET, когда отдельной команды нет |
Примеры:
```bash
# Свои заявки глазами индекса — ровно как их видит searchOwnRequests
node sp.mjs search 'AuthorOWSUSER:"i:0#.f|membership|user@tnsu.kz"' \
--select ListItemID,Title,RequestStatusOWSCHCS,Created --rows 5
# Сколько элементов в каждой партиции
node sp.mjs lists
```
## Грабли, на которые уже наступали
**`Queryable` ≠ `Retrievable`.** У managed property это разные флаги. То, по чему
получается фильтровать, не обязано приходить в ответе. Проверка от 2026-08-17
подтвердила, что по `RequestStatusOWSCHCS` можно фильтровать, и это НЕ было
доказательством, что его можно получить (проверено отдельно 2026-08-20 — можно).
Команда `props` печатает `<нет в ответе>` и `<null>` разными пометками именно
поэтому: схлопывать их в пустую строку — значит потерять ответ на вопрос.
**Имена managed properties нестандартные.** Суффикс — по типу исходной колонки:
`OWSTEXT` (Text), `OWSCHCS` (Choice), `OWSUSER` (Person/Group). У колонок из группы
колонок сайта добавляется префикс группы — поэтому «ожидаемого» `ApproverOWSUSER` в
схеме нет, а есть `TansuSiteApproverOWSUSER` (разбирались 2026-08-06). Актуальные
имена — в константах `MP_*` в `features/myRequests/myRequests.service.ts`.
**`AuthorOWSUSER` — это не почта.** Приходит строкой `почта | ФИО`, а при
claims-логине частей будет четыре. Разбирать сплитом по первому элементу нельзя.
**POST `postquery`, а не GET `query`.** KQL портала перечисляет через `OR` адреса всех
партиций и в query-строке упирается в предел длины URL. Форма тела взята один в один
из PnPjs (`@pnp/sp/search/query.js`): `{ request: { ...query } }`, массивы плоские,
`odata=nometadata`. Иначе инструмент мерил бы не то, что портал.