- «Не удалось создать Страницу компании и заявить права на неё. Повторите попытку позже.» Решение проблемы
- [Что не так с GraphQL]… И как с этим бороться
- 1.2 NON_NULL INPUT
- 3. Полиморфизм
- 4. Дженерики.
- Читают сейчас
- Редакторский дайджест
- Похожие публикации
- Что не так с GraphQL
- Работа с данными при построении API на основе GraphQL
- Напиши мне GraphQL сервер на C#
- Средняя зарплата в IT
- Минуточку внимания
- Комментарии 23
- graphql — подводные камни
- Разделение доступа для пользователей и групп пользователей
- Обработка ошибок
- Проблема SELECT N + 1
«Не удалось создать Страницу компании и заявить права на неё. Повторите попытку позже.» Решение проблемы
Данная ошибка решается двумя способами, в зависимости от ситуации.
1. Ранее вы использовали данный Фейсбук аккаунт, который вы пытаетесь связать Инстаграм, для рекламы.
В данном случае, вам не нужно создавать новую страницу и компанию.
— Заходим в свой Фейсбук аккаунт
— Жмем открывающееся меню сверху справа и выбираем пункт страницы.
— В данном меню находим ранее созданию страницу, а не ту, которую создали последнее.
— Данная страница может быть скрыта из видимости. Если это так- нажмите на неё и включите видимость страницы.
После чего повторите действия с привязкой Фейсбук и Инстаграм ( Настройки Инстаграм- Редактировать профиль — Создать или привязать уже существующую страницу и из списка выберите ранее созданную рекламную страницу)
2. Аккаунт Фейсбук новый либо ранее не использовался для связи с Инстаграм и рекламы.
Не создавайте страницу компании через Инстаграм. Первоначально, создайте рекламную страницу в Вашем аккаунте Фейсбук.
— Заходим в свой Фейсбук аккаунт
— Жмем открывающееся меню сверху справа и выбираем пункт страницы.
— Жмём пункт «Создать» и далее по инструкции придумайте название страницы и выберите необходимую категорию.
После чего повторите действия с привязкой Фейсбук и Инстаграм ( Настройки Инстаграм- Редактировать профиль — Создать или привязать уже существующую страницу и из списка выберите созданную рекламную страницу).
Это должно помочь, но в некоторых случаях, не смотря на созданную рекламную страницу , страницу компании создать не удается.
Сюда вы можете обратиться с дополнительными вопросами.
Источник
[Что не так с GraphQL]… И как с этим бороться
В прошлом материале, мы рассмотрели неудобные моменты в системе типов GraphQL.
А теперь мы попробуем победить некоторые из них. Всех заинтересованных, прошу под кат.
Нумерация разделов соответствует тем проблемам, с которыми мне удалось справится.
1.2 NON_NULL INPUT
В этом пункте, мы рассмотрели неоднозначность, которую порождает особенность реализации nullable в GraphQL.
А проблема в том, что это не позволяет с наскока реализовать концепцию частичного обновления (partial update) — аналог HTTP-метода PATCH в архитектуре REST. В комментариях к прошлому материалу меня сильно критиковали за «REST»-мышление. Я же скажу лишь то, что к этому меня обязывает CRUD архитектура. И я не был готов отказываться от преимуществ REST, просто потому, что «не делай так». Да и решение данной проблемы нашлось.
И так, вернемся к проблеме. Как мы все знаем, сценарий работы CRUD, при обновлении записи выглядит так:
- Получили запись с бэка.
- Отредактировали поля записи.
- Отправили запись на бэк.
Концепция partial update, в этом случае, должна позволять нам отправлять назад только те поля, которые были изменены.
Итак, если мы определим модель ввода таким образом
то при маппинге переменной типа ExampleInput с таким значением
на DTO с такой структурой:
мы получим объект DTO c таким значением:
а при маппинге переменной с таким значением
мы получим объект DTO c таким же значением, как в прошлый раз:
То есть, происходит энтропия — мы теряем информацию, о том было передано поле от клиента, или нет.
В этом случае не понятно, что нужно сделать с полем конечного объекта: не трогать его потому, что клиент не передал поле, или установить ему значение null , потому что клиент передал null .
Строго говоря, GraphQL — это RPC протокол. И я стал размышлять о том, как я делаю такие вещи на бэке и какие процедуры я должен вызывать, чтобы сделать именно так, как мне хочется. А на бэкенде я делаю частичное обновление полей так:
То есть, я буквально не трогаю сеттер свойства сущности, если мне не нужно изменять значение этого свойства. Если переложить это на схему GraphQL, то получится вот такой результат:
теперь, если захотим, мы можем вызвать метод setBar , и установить его значение в null, или не трогать этот метод, и тогда значение не будет изменено. Таким образом, выходит недурная реализация partial update . Не хуже, чем PATCH из пресловутого REST.
В комментариях к прошлому материалу, summerwind спрашивал: зачем нужен partial update ? Отвечаю: бывают ОЧЕНЬ большие поля.
3. Полиморфизм
Часто бывает, что нужно подавать на ввод сущности, которые вроде «одно и то же» но не совсем. Я воспользуюсь примером с созданием аккаунта из прошлого материала.
Очевидно, что мы не можем подать данные с такой структурой на один аргумент — GraphQL просто не разрешит нам это сделать. Значит, нужно как-то решить эту проблему.
Способ 0 — в лоб
Первое, что приходит в голову — это разделение вариативной части ввода:
Мда… когда я вижу такой код, я часто вспоминаю Жозефину Павловну. Мне это не подходит.
Способ 1 — не в лоб, а по лбу
Тут мне на помощь пришел тот факт, что для идентификации сущностей, я использую я использую UUID (вообще всем рекомендую — не один раз выручит). А это значит, что я могу создавать валидные сущности прямо на клиенте, связывать их между собой по идентификатору, и отправлять на бэк, по отдельности.
Тогда мы можем сделать что-то в духе:
или, что оказалось еще удобнее (почему это удобнее, я расскажу, когда мы доберемся до генерации пользовательских интерфейсов), разделить это на разные методы:
Тогда, нам нужно будет отправить запрос на createAccount и createOrganization/createPerson
одним батчем. Стоит отметить, что тогда обработку батча нужно обязательно обернуть в транзакцию.
Способ 2 — волшебный скаляр
Фишка в том, что скаляр в GraphQL, это не только Int , Sting , Float и т.д. Это вообще всё что угодно (ну, пока с этим может справится JSON, конечно).
Тогда мы можем просто объявить скаляр:
Потом, написать на него свой обработчик, и не парится. Тогда мы сможем без проблем подсовывать вариативные поля на ввод.
Какой из способов выбрать? Я использую оба, и выработал для себя такое правило:
Если родительская сущность является Aggregate Root для дочерней, то я выбираю второй способ, иначе — первый.
4. Дженерики.
Тут всё банально и ничего лучше генерации кода я не придумал. И без Рельсы (пакет railt/sdl) я бы не справился (точнее, сделал бы тоже самое но с костылями). Фишка в том, что Рельса позволяет определять директивы уровня документа (в спеке нет такой позиции для директив).
То есть, директивы непривязанные, к чему либо, кроме документа, в котором они вызваны.
Я ввел такие директивы:
Думаю, что объяснять суть макросов никому не нужно.
На этом пока всё. Не думаю, что этот материал вызовет столько же шума, как прошлый. Всё таки заголовок там был довольно «желтым» )
В комментариях к прошлому материалу хабровчане топили за разделение доступа… значит следующий материал будет об авторизации.
Читают сейчас
Редакторский дайджест
Присылаем лучшие статьи раз в месяц
Скоро на этот адрес придет письмо. Подтвердите подписку, если всё в силе.
Похожие публикации
Что не так с GraphQL
Работа с данными при построении API на основе GraphQL
Напиши мне GraphQL сервер на C#
Средняя зарплата в IT
AdBlock похитил этот баннер, но баннеры не зубы — отрастут
Минуточку внимания
Комментарии 23
Теперь давайте все-таки отложим в сторону REST с его различиями null\undefined и попробуем посмотреть на мутации как на функции в языках программирования. И, вместо вашего оригинального технического решения, которое меняет состояние БД в query-запросах (причем, каждое поле в отдельном запросе к БД), можно решить задачу, например, так:
причем, каждое поле в отдельном запросе к БД
Для этого умные дядьки давным давно придумали unit of work — запрос к бд будет один. Я очень ответственно подхожу к вопросу лишних запросов.
Я думал о предложенном вами варианте. Но это порождает запросы с неопределенной структурой — в вашем варианте в onlyFields можно передавать что угодно. Ну или как минимум, завести какой-то Enum с перечислением всех полей доступных в данной сущности.
В общем, этот вариант будет работать, но он мне субъективно не нравится.
Потому что у нас получается два источника истины: один — это список полей самой сущности, второй — это Enum перечисляющий поля.
Для этого умные дядьки давным давно придумали unit of work — запрос к бд будет один.
Будет одна транзакция. Запросов UPDATE будет несколько.
Ну или как минимум, завести какой-то Enum с перечислением всех полей доступных в данной сущности.
Естественно, можно много как организовать валидацию. Основной смысл не меняется.
… но он мне субъективно не нравится.
Будет одна транзакция. Запросов UPDATE будет несколько.
Вам нужно немного поработать с нормальной ORM(Datamapper) уровня Hibernate или Doctrine, чтобы понять, что это не так. У меня нет сил вам это доказывать.
Люди поработавшие с Doctrine в комментариях непременно меня опровергнут, если я не прав.
С таким аргументом и не поспоришь 🙂
Это было мнение. Аргументация была в следующем предложении
Там просто у greabock, насколько я могу припомнить, админка вся строится автоматически на уровне интроспекции сервера, по-этому всё так и сложно. Т.е. нужные унифицированные методы, которые позволяют автоматически строить интерфейс.
По-этому, подозреваю, и потребовалось уйти от красивой доменной модели и реализовать RESTful-подобную CRUD API.
админка вся строится автоматически на уровне интроспекции сервера
А что вы думаете о там решеиние для partial update?
Думаю, что объяснять суть макросов никому не нужно.
А лучше бы объяснил. Я вообще даже представить не могу как оно там может решить проблему кривого полиморфизма в gql.
Лично у себя в проекте я добавил директиву на поля:
Но работает оно через одно место, т.к. внедряется внутрь цикла компилятора, подменяя структуры, а значит всякие проверки и вывод типов может тут убиться об стену неконсистентными правилами LSP при имплементации интерфейсов.
Отличный материал, спасибо! Мои пробы с GraphQL меня озадачили несколько другими вещами. Было бы интересно услышать мнение практика.
Сразу упомяну стек, на котором пробовал: nodejs + mongodb
Проблема N+1. DataLoader, конечно, её решает почти полностью, особенно с кэшированием. Но в целом у меня сложилось впечатление, что GraphQL хорошо подойдет для нормализованной реляционной базы, подменяя собой join-ы (и уменьшая стоимость абстракций с помощью DataLoader). Но вот если в проекте, например, mongodb и документы набиты подколлекциями, возникает необходимость ограничивать выборку по полям. И, если, где-то в API всё-таки надо получить к ним доступ, то нужно делать другой тип в Query, резолвер которого уже не ограничен по полям при выборе из базы (например, не использует проекцию в mongo).
Почему не заложена возможность получить набор запрошенных полей в самом резолвере? Есть способы вынимания их из аргумента info, но те что я видел, не позволяют просто понять, какие поля относятся к какой ветке и какому уровню. Или я проглядел?
Я обратил внимание на GraphQL по нескольким причинам:
- нафиг эти статусы, методы и кучу эндпоинтов
- возможность собрать несколько запросов в один на фронте
- схема и автодокументирование
- возможность завернуть в другой транспорт вместо http
Но вот реализация на бэке мне кажется не столько гибкой, как хотелось бы, если я хочу снизить стоимость абстракций.
Как вариант, городить свою версию на JSON-RPC.
Источник
graphql — подводные камни
Наверное, не существует идеальных технологий. Не является исключением и graphql. Если у Вас еще не было опыта работы с этой технологией, то нужно хорошо представлять, какие проблемы могут у Вас возникнуть и заблаговременно к ним подготовиться.
Для начала скажу, что я скорее сторонник, чем противник, применения graphql кругом, где это только возможно. И совершенно не собираюсь разубеждать кого-нибудь в целесообразности применения этой технологии. И именно поэтому поднимаю вопросы, которые относятся к нерешенным вопросам в рамках технологии graphql.
Например, для кого-то может быть неожиданным, что каждый объект в graphql придется описывать минимум дважды: один раз в качестве возвращаемого типа объекта, и еще один раз в качестве input типа объекта (см. graphql.org/graphql-js/mutations-and-input-types). Впрочем, это я рассказал для начала и даже не считаю существенным недостатком. Сегодня речь пойдет о таких вопросах, которые, как правило, приходится решать, разрабатывая приложение с применением graphql технологии:
- Разделение доступа для пользователей и групп пользователей.
- Обработка ошибок.
- Проблема SELECT N + 1
Разделение доступа для пользователей и групп пользователей
graphql вообще ничего не знает о разделении доступа для пользователей и групп. Таким образом, вся работа по разделению доступа на ответственности разработчика приложения. В функцию-резольвер третьим параметром передается объект контекста приложения. Поэтому, если Вы, например, работаете с реализацией graphql JavaScript+express, то в параметре контекста Вы можете получить текущего пользователя из объекта request express.js. Но дальнейшая работа по разграничению доступа должна проводиться непосредственно в каждом резольвере:
Естественно, такой подход усложняет контроль прав доступа, т.к. нет возможности задавать права доступа в декларативной манере и контроль прав рассредоточен по десяткам (для некоторых больших систем по тысячам) функциям-резольверам. Поэтому существует целый ряд библиотек, которые решают эту проблему. Некоторые из них достаточно популярны (судя по количеству звезд на github.com), например github.com/maticzav/graphql-shield.
Обработка ошибок
Если Ваш фронтенд требует валидацию ввода и формирование подробных сообщений для каждого поля, не прошедшего валидацию, то обработка ошибок в graphql, скорее всего, покажется Вам недостаточно гибкой. Например, если входной параметр был описан как строка, а пришло числовое значение, то сообщение об ошибке будет мало подходящим для этого:
Если есть грубая ошибка в типе входного параметра, то сообщение об ошибке будет генерироваться автоматически и контролировать это процесс нет возможности. Если валидация по типу входного параметра прошла успешно, то есть возможность отправить клиенту кастомное сообщение об ошибке, выбросив объект new Error (‘custom message . ‘) . Добавить кастомные поля к объекту ошибки не получится (кастомизация ошибки реализована в библиотеках apollo-server-express и apollo-errors при совместном их использовании). Разумеется, всегда есть возможность сериализовать объект в строку message на сервере и десериализовать на клиенте. Но нужно ли так поступать?
Проблема SELECT N + 1
graphql построен на функциях-резольверах. Это означает, что выборка данных из базы данных может порождать проблему, которая называется SELECT N+1. Предположим что в функции-резольвере был получен список объектов, в котором связанные с этим объектом данные представлены идентификаторами (внешними ключами). Для каждого такого идентификатора будет вызвана своя функиця-резольвер, в которой (в каждой) будет дополнительно сделан запрос к базе данных. Таким образом, вместо одного запроса к базе данных (с SQL JOIN) будет выполнено много запросов, что перегружает базу данных запросами.
Источник