Примеры использования API

Внимание. Сервис Яндекс Коннект больше не поддерживается. Directory API прекратит работу с 1 апреля 2023 года. С 24 марта доступность сервиса будет ограничена.

Для управления организациями, пользователями и доменами используйте сервис Яндекс 360 для бизнеса.

Документация API 360

В этом разделе приведены примеры обращения к Directory API с помощью простейших скриптов на Python.

Настройка окружения

Чтобы примеры работали правильно, настройте окружение:

  1. Зарегистрируйте свое приложение на OAuth-сервере Яндекса и получите токен.

  2. Поместите токен в переменную окружения TOKEN.

  3. Создайте в рабочем каталоге csv-файлы с данными об отделах и о сотрудниках:

    departments.csv:

    label,name,parent
    buh, Бухгалтерия,
    management, Менеджмент,
    sales, Продажи,
    sales-msk, Московский отдел продаж, Продажи
    sales-spb, Питерский отдел продаж, Продажи
    tech, IT,
    backoffice, Разработка Бэкофиса, IT
    helpdesk, Helpdesk, IT

    users.csv:

    nickname,first,last,department
    ivan.k, Иван, Котлов, Менеджмент
    star, Люба, Иванова, Бухгалтерия
    petr.g, Пётр, Гуляев, Разработка Бэкофиса

Создание отделов из файла

С помощью этого скрипта вы можете создать несколько новых отделов. Их параметры указываются в файле departments.csv.

#!/usr/bin/env python
# coding: utf-8

import os
import csv
import sys
import requests


TOKEN = os.environ.get('TOKEN')
USER_AGENT = 'Directory Sync Example'


def load_departments(filename):
    """Читает CSV файл с данными про отделы
    и возвращает iterable объект с помощью
    которого можно итерироваться по словарям
    с данными про отделы.
    """
    with open(filename, 'r', encoding='utf-8') as csvfile:
        rows = csv.DictReader(csvfile)
        yield from rows


def create_department(data, already_created):
    """Создаёт в Директории отдел и возвращает его id.

    Принимает на вход словарь:

    {
        'label': 'Короткое английское имя email рассылки',
        'name': 'Название отдела',
        'parent': 'Название родительского отдела или пустая строка',
    }

    А так же словарь {'Название отдела': id_from_directory}
    по которому можно найти id родительского отдела в Директории.
    """
    name = data['name'].strip()
    parent = data['parent'].strip()
    label = data['label'].strip()

    if parent:
        if parent not in already_created:
            raise RuntimeError(
                'Отдел {parent} должен предшествовать '
                'отделу {current} в данных'.format(
                    parent=parent,
                    current=name,
                )
            )
        parent_id = already_created[parent]
    else:
        # Если родитель не указан, надо
        # создавать в "корневом" отделе.
        parent_id = 1

    payload = {
        'label': label,
        'name': name,
        'parent_id': parent_id,
    }
    headers = {
        'Authorization': 'OAuth ' + TOKEN,
        'User-Agent': USER_AGENT,
    }
    response = requests.post(
        'https://api.directory.yandex.net/v6/departments/',
        json=payload,
        headers=headers,
        timeout=10,
    )
    # В случае ошибки, бросим исключение.
    response.raise_for_status()
    # А если всё хорошо, то вернём id.
    response_data = response.json()
    return response_data['id']


def load_already_created():
    """Забирает из Директории данные об уже существующих отделах
    и возвращает словарь {'Название отдела': department_id}
    """
    params = {
        'fields': 'name',
    }
    headers = {
        'Authorization': 'OAuth ' + TOKEN,
        'User-Agent': USER_AGENT,
    }
    # В целях простоты примера мы игнорируем тот факт, что
    # отделов может быть больше 20 и они не поместятся в
    # один запрос. В реальном коде тут надо будет анализировать
    # ключ links и делать дополнительные запросы.
    response = requests.get(
        'https://api.directory.yandex.net/v6/departments/',
        params=params,
        headers=headers,
        timeout=10,
    )
    response.raise_for_status()
    response_data = response.json()
    results = response_data['result']
    results = {
        department['name']: department['id']
        for department in results
    }
    return results



def create_departments(filename):
    """Читает данные из файла и создаёт структуру отделов.
    """
    departments = load_departments(filename)
    already_created = load_already_created()

    for item in departments:
        name = item['name'].strip()
        # Отдел будем создавать только если ещё нет
        # отдела с таким именем
        if name not in already_created:
            dep_id = create_department(item, already_created)
            already_created[name] = dep_id


if __name__ == '__main__':
    if len(sys.argv) != 2:
        print('Usage: {0} departments.csv'.format(sys.argv[0]))
        sys.exit(1)
    else:
        create_departments(sys.argv[1])

Добавление сотрудников

С помощью скрипта вы можете добавить новых сотрудников. Их параметры указываются в файле users.csv.

#!/usr/bin/env python
# coding: utf-8

import os
import csv
import sys
import string
import random
import requests


TOKEN = os.environ.get('TOKEN')
USER_AGENT = 'Directory Sync Example'


def load_users(filename):
    """Читает CSV файл с данными про сотрудников
    и возвращает iterable объект с помощью
    которого можно итерироваться по словарям
    с данными про сотрудников.
    """
    with open(filename, 'r', encoding='utf-8') as csvfile:
        rows = csv.DictReader(csvfile)
        yield from rows


def create_user(data, departments):
    """Создаёт в Директории пользователя.

    Принимает на вход словарь:

    {
        'nickname': 'Логин сотрудника',
        'first': 'Имя',
        'last': 'Фамилия',
        'department': 'Отдела или пустая строка',
    }

    Вторым аргументом идёт словарь {'Название отдела': department_id}.
    Он нужен, чтобы определить id отдела, в котором должен быть заведён
    сотрудник.
    """
    data = {
        key: value.strip()
        for key, value in data.items()
    }

    department_name = data['department']
    if department_name:
        if department_name not in departments:
            raise RuntimeError('Отдел "{0}" не найден'.format(department_name))

        department_id = departments[department_name]
    else:
        # Если отдел не указан, то сотрудник
        # заводится на самом верхнем уровне.
        department_id = 1


    payload = {
        'nickname': data['nickname'],
        'name': {'first': data['first'], 'last': data['last']},
        'department_id': department_id,
        'password': data['password'],
    }
    headers = {
        'Authorization': 'OAuth ' + TOKEN,
        'User-Agent': USER_AGENT,
    }
    response = requests.post(
        'https://api.directory.yandex.net/v6/users/',
        json=payload,
        headers=headers,
        timeout=10,
    )
    # В случае ошибки, бросим исключение.
    response.raise_for_status()
    # А если всё хорошо, то вернём id.
    response_data = response.json()
    return response_data['id']


def get_departments():
    """Забирает из Директории данные об уже существующих отделах
    и возвращает словарь {'Название отдела': department_id}
    """
    params = {
        'fields': 'name',
    }
    headers = {
        'Authorization': 'OAuth ' + TOKEN,
        'User-Agent': USER_AGENT,
    }
    # В целях простоты примера мы игнорируем тот факт, что
    # отделов может быть больше 20 и они не поместятся в
    # один запрос. В реальном коде тут надо будет анализировать
    # ключ links и делать дополнительные запросы.
    response = requests.get(
        'https://api.directory.yandex.net/v6/departments/',
        params=params,
        headers=headers,
        timeout=10,
    )
    response.raise_for_status()
    response_data = response.json()
    results = response_data['result']
    results = {
        department['name']: department['id']
        for department in results
    }
    return results


def create_random_password(length=10):
    symbols = string.ascii_letters + string.digits
    return ''.join(
        random.choice(symbols)
        for i in range(length)
    )


def load_already_created():
    """Забирает из Директории данные об уже существующих сотрудниках.
    и возвращает set из из логинов.
    """
    params = {
        'fields': 'nickname',
    }
    headers = {
        'Authorization': 'OAuth ' + TOKEN,
        'User-Agent': USER_AGENT,
    }
    # В целях простоты примера мы игнорируем тот факт, что
    # сотрудников может быть больше 20 и они не поместятся в
    # один запрос. В реальном коде тут надо будет анализировать
    # ключ links и делать дополнительные запросы.
    response = requests.get(
        'https://api.directory.yandex.net/v6/users/',
        params=params,
        headers=headers,
        timeout=10,
    )
    response.raise_for_status()
    response_data = response.json()
    results = response_data['result']
    return {user['nickname'] for user in results}



def create_users(filename, output):
    """Читает данные из файла и создаёт пользователей.
    """

    # Убедимся, что выходной файл не существует,
    # потому что если он есть, то мы можем случайно
    # затереть пароли тех сотрудников, что были заведены
    # при предыдущем запуске скрипта.
    if os.path.exists(output):
        raise RuntimeError('Файл {0} существует. Выберите другое имя файла.')

    users_data = load_users(filename)
    departments = get_departments()
    already_created = load_already_created()

    # Сюда мы сохраним логины и пароли заведённых сотрудников
    # чтобы позже записать в выходной файл
    new_users = []

    for item in users_data:
        try:
            nickname = item['nickname'].strip()
            # Сотрудника будем создавать только если ещё нет
            # сотрудника с таким логином
            if nickname not in already_created:
                item['password'] = create_random_password()
                create_user(item, departments)
                new_users.append((nickname, item['password']))
        except Exception:
            # Пропустим эту ошибку, чтобы по возможности
            # завести все учётные записи какие получится.
            print('Не получилось завести сотрудника: {0}'.format(item))

    # Теперь сдампим пароли пользователей в новую CSV
    if new_users:
        with open(output, 'w') as output_file:
            output_file.write('nickname,password\n')
            output_file.writelines(
                '{0},{1}\n'.format(nickname, password)
                for nickname, password
                in new_users
            )






if __name__ == '__main__':
    if len(sys.argv) != 3:
        print('Usage: {0} users.csv passwords.csv'.format(sys.argv[0]))
        sys.exit(1)
    else:
        create_users(sys.argv[1], sys.argv[2])