Перейти к основному содержимому

Программы Академии для VK mini-app

Здесь сложнее, чем с заездами

1. Контент страницы "Академия"

Ручка (со слешом на конце)

POST 
https://apicms.tavrida.art/api/block/list/public/
к сведению

https://demo.tavrida-cms-backend.kube.aventica.tech/api/swagger-ui/static/index.html

Сваггер ЦМС, на продакшен не деплоится, поэтому ссылка на демо.

Тело запроса


{
"ids": [ "66c2f122-ff22-4e50-8f92-9b342c50c6ed" ] // blockId
}

к сведению

Текущий blockId для страницы Академия 2024 | 2 мая, 02:26 - 66c2f122-ff22-4e50-8f92-9b342c50c6ed. При смене страницы придется прописать другой id в запросе.

Ответ

{
"result": {
"blocks": [
{
"id": "66c2f122-ff22-4e50-8f92-9b342c50c6ed", // тот самый blockId
"content": {
"data": {
"pageSettings": {
"multipleEvents": {
"ids": [
"ad03c794-4144-4b8d-82bb-a5f8e0b4e04b",
"ca0aa10a-b3f1-44f8-aa02-6b3d9425bfa6",
"441d587b-add8-4b07-aa0c-62175f21bdc4"
]
}
}
}
}
}
]
}
}

Если зарыться сюда, можно получить список Event.ID, которые являются программами Академии.


2. Подтянуть ивенты из АИС

Ручка POST https://api.tavrida.art/graphql, см подробнее про GraphQL

Метод eventList

осторожно

Метод не поддерживает пагинацию больше 30, поэтому в запрос нужно положить несколько query. 0-29, 30-59, и т.д.

Пример, как это реализовано на юзер-фронте

  /** @param { Object } opts
* @param { string[] } eventIds
*
* @returns { import('../types/tavrida.js').Tavrida.Event } */
async getMultipleEvents({
eventIds
}) {
const maxLimit = 30
const buildQuery = ({
offset,
name,
}) => `${name}: eventList${GraphQLArguments.stringify({
IDs: eventIds ?? null,
pagination: {
offset,
limit: maxLimit,
}
})} {
nodes {
ID,
name,
beginsAt,
endsAt,
active,
registrationBeginsAt,
registrationEndsAt,
type,
}
}`

const steps = eventIds ? Math.ceil(eventIds.length / maxLimit) : 1

const multipleQueries = []
for (let i = 0; i < steps; i++) {
const offset = i * maxLimit
multipleQueries.push(buildQuery({
offset,
name: `events${offset}`,
}))
}

const query = `query events {
${multipleQueries.join('')}
}`
const request = new GraphQLRequest({
query,
})
await request.run()
if (!request.isSuccessful) {
throw new ErrorWithProps(request.errorMessage, {
from: 'api.getMultipleEventsByIds'
})
}

const result = []
for (let i = 0; i < steps; i++) {
const offset = i * maxLimit
const nodes = request.data[`events${offset}`]?.nodes
if (!Array.isArray(nodes)) {
throw new ErrorWithProps('One of pieces of GraphQL response was bad', {
from: 'api.getMultipleEventsByIds',
nodes,
offset,
data: request.data,
})
}
nodes.forEach(it => result.push(it))
}
return result
}

Что приходит в ответ

Можно понять по полям GraphQL


3. Коллекция программ в CMS

Нужна та же ручка, что в шаге с получением контента страницы:

POST 
https://apicms.tavrida.art/api/block/list/public/

Тело запроса

{
"ids": [ "44ff599f-f72f-4c7c-8bc0-ae8a495f7495" ] // blockId
}

Актуальный blockId коллекции Программы Академии 2024 | 27 апр., 02:50 - 44ff599f-f72f-4c7c-8bc0-ae8a495f7495

Ответ

{
"result": {
"blocks": [
{
"id": "44ff599f-f72f-4c7c-8bc0-ae8a495f7495", // тот же самый blockId
"data": {
"items": [
{
"id": "132", // id в коллекции, это нам не нужно
"eventId": "cf8be064-6705-44e4-bae6-33758e949543", // eventId
// ... и еще куча полей
}
]
}
}
]
}
}

Каждый итем имеет поле eventId, по которому можно его контент заджойнить в Event из АИС.

Обратите внимание, что в CMS name - это объект с переводами. В отличие от АИС, где name - строка.

const textByLocale = {
RU: 'Программа такая-то',
EN: 'Some academy program'
}

Интерфейс коллекции на typescript

Итем любой возможной коллекции

export interface CollectionItem {
id: string;
hidden: boolean;
createdAt: string;
name: Record<string, string>;
}

Итем любой коллекции Ивентов АИС

export interface EventFromCMS extends CollectionItem {
eventId?: string;
}
Итем коллекции конкретно программ Академии
export interface AcademyProgramFromCMS extends EventFromCMS {
_meta?: CollectionItemMeta;

image?: FileCMS;

/**
* Цвет карточки или обложки программы/онлайн-курса академии.
* На юзер-фронте каждому цвету отсюда должен соответствовать secondary цвет графического элемента
* (сделанный через опасити или как другой secondary цвет)
* */
color?: string;

/**
* Выбирается id графического элемента. Приходит как строка '1' / '2' / и т.д.
* */
graphicElement?: string;

/** id сложности из другой коллекции */
difficulty?: string;

/** Флаг.
* Если true - карточка считается онлайн-курсом */
isOnline?: boolean;

/** Флаг.
* Если true, выведется плашка "Новый".
* Возможно пригодится для фильтрации в будущем. */
isNew?: boolean;

externalRegistrationLink?: string;

/** Массив плашек. */
bars?: {
text: Record<string, string>;
}[];

/** Краткое описание */
anounce?: string;

/** Массив ID категорий из коллекции */
tagIds?: string[];

bottomBars?: {
icon?: CMSIcon.Pure,
text: Record<string, string>;
}[];

/** Серые карточки под обложкой.
* Обе деталки.
* */
infoBlocks?: {
graphicElement: string;
title: Record<string, string>;
text: Record<string, string>;
}[],

about?: {
hidden: boolean;
image?: FileCMS;
title: Record<string, string>;
text: Record<string, string>;
};

forWhom?: {
hidden: boolean;
title: Record<string, string>;
cards: {
title: Record<string, string>;
description: Record<string, string>;
color: string;
graphicElement: string;
}[];
};

persons?: SliderPersons.Module;

producers?: {
hidden: boolean;
title: Record<string, string>;
list: {
image?: FileCMS;
title: Record<string, string>;
text: Record<string, string>;
}[];
};

whatWillYouGet?: {
hidden: boolean;
title: Record<string, string>;
content: {
title: Record<string, string>;
text: Record<string, string>;
img?: FileCMS;
}[];
};

path?: {
hidden: boolean;
title: Record<string, string>;
steps: {
title: Record<string, string>;
text: Record<string, string>;
}[];
};

program?: {
hidden: boolean;
title: Record<string, string>;
bars: {
icon: CMSIcon.Pure,
text: Record<string, string>;
color: string;
}[];
prefix: Record<string, string>;
list: {
title: Record<string, string>;
description: Record<string, string>;
bars: {
text: Record<string, string>;
}[];
}[];
};

faq?: Omit<Faq.Module, 'title'>;

showBanner?: boolean;
}

4. Коллекция направлений

Нужна та же ручка, что в шаге с получением контента страницы:

POST 
https://apicms.tavrida.art/api/block/list/public/

Тело запроса

{
"ids": [ "44ff599f-f72f-4c7c-8bc0-ae8a495f7495" ] // blockId
}

Актуальный blockId коллекции Направления программ Академии | 9 июл., 21:09 - 2a516897-fcf6-400c-82e0-dcece038ced3

Джойн с объектами программ

У программы, если она успешно заджойнилась с элементом коллекции, есть поле:

categoryIds: string[];

Каждый из этих ID соответствует полю id у объекта направления. Можно джойнить.


подсказка

Можно получить и коллекцию, и контент страницы одним запросом.

{
"ids": [
"66c2f122-ff22-4e50-8f92-9b342c50c6ed", // blockId страницы
"44ff599f-f72f-4c7c-8bc0-ae8a495f7495", // blockId коллекции
"2a516897-fcf6-400c-82e0-dcece038ced3" // blockId коллекции направлений
]
}

Тогда в ответе в массиве result.blocks будут все три объекта.


5. Подытог

  • либо тремя запросами, либо одним совмещенным достать публичный контент трех схем:

по ручке POST https://apicms.tavrida.art/api/block/list/public/

-- коллекции Программы Академии | ...

-- страницы Академия | ...

-- коллекции Направления программ Академии | ...

  • подтянуть из АИС с помощью GraphQL query мероприятия, ID которых указаны в pageSettings страницы Академия | ...
  • заджойнить элементы коллекции Программы Академии по ItemFromCMS.eventId к Ивентам АИС по Event.ID
  • заджойнить элементы коллекции Направления программ Академии по ItemFromCMS.id к элементам коллекции по ItemFromCMS.categoryIds[]