Apollo 3.0 для работы с GraphQL в многомодульном Android приложении

—

от автора

Давайте рассмотрим, каким образом настроить и использовать последнюю на данный момент версию клиента apollo в многомодульном приложении под android.

Что такое Apollo и когда он используется?

Apollo Android — это клиент GraphQL, который генерирует Kotlin модели на основе запросов GraphQL. Эти модели предоставляют вам типобезопасный API для работы с серверами GraphQL. Apollo помогает вам объединять, организовывать и легко получать доступ к вашим операторам запросов GraphQL.

Ссылка на официальный Github

Создаем проект подключаем плагин и зависимости

Создаем новый проект на основе Empty Activity, я назову его ApolloConfig_Example

Создание нового проекта

Создадим несколько модулей, я назову их ‘common’, и ‘modules’,последний в свою очередь будет содержать ‘module_1’ и ‘module_2’

Модули проекта

Подключаем JS GraphQL плагин — он добавляет поддержку языка, подсветку синтаксиса GraphQL

Плагин JS GraphQL

Подключаем плагин Apollo 3.0 во всех gradle файлах наших модулях

id 'com.apollographql.apollo3' version("3.0.0-alpha07")
Плагин Apollo 3.0

После добавления apollo плагина появится сообщение о необходимости синхронизации проекта, но перед этим необходимо добавить настройки для плагина

Для gradle файла модуля ‘app’

apollo {         //Устанавливает имя пакета куда будут помещены автоматически генерируемые классы.         packageName.set("com.alab.app")         //Устанавливает путь к вашей схеме (В данном случае указывает на файл в корне проекта).         schemaFile.set(file("../schema.graphqls"))     }

Для gradle файла модуля ‘common’

apollo {         //Устанавливает имя пакета куда будут помещены автоматически генерируемые классы.         packageName.set("com.alab.common")         //Устанавливает путь к вашей схеме (В данном случае указывает на файл в корне проекта).         schemaFile.set(file("../schema.graphqls"))         //Устанавливает сопоставление скаляра из graphql схемы к вашему классу.         customScalarsMapping = [                 "DateTime" : "java.util.Date"         ]     }

Для gradle файла модуля ‘module_1’ и ‘module_2’

apollo {         //Устанавливает имя пакета куда будут помещены автоматически генерируемые классы.         packageName.set("com.alab.module_1")         //Устанавливает путь к вашей схеме (В данном случае указывает на файл в корне проекта).         schemaFile.set(file("../../schema.graphqls"))         //Устанавливает путь к пакету с файлами .qraphql, которые описывают ваши запросы.         srcDir(file("src/main/java/com/alab/module_1/services/"))         //Устанавливает сопоставление скаляра из graphql схемы к вашему классу.         customScalarsMapping = [                 "DateTime" : "java.util.Date"         ]     }
apollo {         //Устанавливает имя пакета куда будут помещены автоматически генерируемые классы.         packageName.set("com.alab.module_2")         //Устанавливает путь к вашей схеме (В данном случае указывает на файл в корне проекта).         schemaFile.set(file("../../schema.graphqls"))         //Устанавливает путь к пакету с файлами .qraphql, которые описывают ваши запросы.         srcDir(file("src/main/java/com/alab/module_2/services/"))         //Устанавливает сопоставление скаляра из graphql схемы к вашему классу.         customScalarsMapping = [                 "DateTime" : "java.util.Date"         ]     }

Нажимаем кнопку ‘Sync Now’

Кнопка ‘Sync Now’

Добавляем зависимости в модули ‘common’, ‘module_1’ и ‘module_2’

implementation "com.apollographql.apollo3:apollo-runtime:3.0.0-alpha07" implementation "com.apollographql.apollo3:apollo-api:3.0.0-alpha07"
Зависимости

В Gradle файлах ‘app’, ‘module_1’ и ‘module_2’ добавляем в зависимости модуль ‘common’. Он станет видим для этих модулей.

implementation(project(":common"))

Создаем необходимые классы

Начнем с файла scheme.graphqls, это файл описывает, какие данные могут быть запрошены, разместить его нужно в корне проекта, на одном уровне с папками ‘app’, ‘common’, ‘modules’.

schema {   query: Query }  type Query {   "Возвращает сотрудников.\n\n\n**Returns:**\nСотрудник, если он найден."   employee("Идентификатор сотрудника." id: String): Employee }  "Представляет сотрудника." type Employee @source(name: "Employee", schema: "Employees") {   "Возвращает id пользователя"   id: String   "Возвращает полное имя."   fullName: String!   "Возвращает табельный номер."   personnelNumber: String!   "Возвращает статус, показывающий присутствие сотрудника на работе."   workStatus: Employees_WorkingPeriod! }  "Определяет рабочий период." type Employees_WorkingPeriod @source(name: "NonWorkingPeriod", schema: "Employees") {   "Возвращает дату начала."   beginDate: DateTime!   "Возвращает дату окончания."   endDate: DateTime! }  "Annotates the original name of a type." directive @source("The original name of the annotated type." name: Name! "The name of the schema to which this type belongs to." schema: Name!) repeatable on ENUM | OBJECT | INTERFACE | UNION | INPUT_OBJECT | FIELD_DEFINITION | INPUT_FIELD_DEFINITION | ARGUMENT_DEFINITION | ENUM_VALUE  "The name scalar represents a valid GraphQL name as specified in the spec and can be used to refer to fields or types." scalar Name  "The `DateTime` scalar represents an ISO-8601 compliant date time type." scalar DateTime @specifiedBy(url: "https:\/\/www.graphql-scalars.com\/date-time")

В модуле ‘common’ создадим класс ‘ApolloClient’, в нем напишем клиент, поскольку он находится в общем модуле, он будет виден всем другим модулям.

/**  * Представляет клиент Apollo.  */ val apolloClient = ApolloClient(     networkTransport = HttpNetworkTransport(         serverUrl = "https://your_url.com/api/graphQl",   //Адресс Вашего Api         okHttpClient = OkHttpClient.Builder()             .addInterceptor(Interceptor { chain ->                 val newRequestBuilder = chain.request().newBuilder()                 newRequestBuilder.apply {                     addHeader(                         "Authorization",                         "Bearer <Your Token>"   //Ваш токен                     )                     addHeader("Content-Type", "application/json")                         .build()                 }                 val newRequest = newRequestBuilder.build()                 return@Interceptor chain.proceed(newRequest)             }).build()     ) )     .withCustomScalarAdapter(DateTime.type, dateTimeAdapter)

В этом же модуле создадим класс ‘GraphqlAdapters’, в нем напишем скалярный адаптер для типа DateTime, который присутствует в схеме, данные которые будут приходить с этим типом, будут автоматически преобразованы в привычный класс Date.

/**  * Адаптер для преобразования Any в Date.  */ val dateTimeAdapter = object : Adapter<Date> {     override fun fromJson(reader: JsonReader, customScalarAdapters: CustomScalarAdapters): Date {         val source = reader.nextString()         val date: Date = SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.S'Z'", Locale.getDefault())             .parse(source) ?: throw RuntimeException("Не удалось распарсить строку '${source}' в дату")         return date     }      override fun toJson(writer: JsonWriter, customScalarAdapters: CustomScalarAdapters, value: Date) {         AnyAdapter.toJson(writer, value)     } }

Перейдем к module_1 и module_2, следующие файлы и классы будут почти идентичны для обоих модулей, и сделано для примера.
Создадим файл ‘GetEmployee.graphql’, он будет на основе scheme.graphqls описывать наш запрос, а apollo на основе ‘GetEmployee.graphql’ сгенерирует все необходимые классы.

query GetEmployee($id: String) {     employee(id: $id) {         id,         fullName,         personnelNumber,         workStatus {             beginDate,             endDate         }     } }

Далее создадим класс ApiService и cоответствующий ему интерфейс IApiService, там будем описывать наши запросы.

Интерфейс ‘IApiService’

/**  * Описывает методы запросов к api.  */ interface IApiService {      /**      * Возвращает сотрудника по указанному id.      * @param id Идентификатор сотрудника.      */     suspend fun getEmployee(id: String): GetEmployeeQuery.Employee?  }

Класс ‘ApiService’

/**  * Представляет сервис запросов к api.  * @param apolloClient Apollo client.  */ class ApiService(     private val apolloClient: ApolloClient, ) : IApiService {      override suspend fun getEmployee(id: String): GetEmployeeQuery.Employee? {         return apolloClient.query(GetEmployeeQuery(id)).data?.employee     }  }

Все готово для написания самого запроса, создадим класс фрагмента, а в нем запрос

/**  * Представляет фрагмент экрана.  */ class ModuleFragment : Fragment() {      override fun onCreateView(         inflater: LayoutInflater,         container: ViewGroup?,         savedInstanceState: Bundle?     ): View {         return View(requireContext())     }      override fun onViewCreated(view: View, savedInstanceState: Bundle?) {         super.onViewCreated(view, savedInstanceState)          GlobalScope.launch {             val employeeResponse = apolloClient.query(GetEmployeeQuery("12345")).data?.employee         }      } }
Структура классов в module_1 и module_2

Скачать пример можно на моем GitHub.


ссылка на оригинал статьи https://habr.com/ru/articles/582552/


Комментарии

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *