LoginSignup
0
0

More than 1 year has passed since last update.

Xamarin.Forms+AppSync+Lambdaでデータを取得してみる

Posted at

背景

昨年、Xamarin.FormsでKudanARアプリをリリースしました。
Xamarin.FormsでKudan ARを試してみる

それから約1年が経過し、そろそろKudanARライブラリのAPIキーの使用期限が近づいています。
APIキーを切り替えるためだけに、都度アプリのリリースが必要になるのは面倒…
そこで、KudanARライブラリのAPIキーのデータだけLambdaで管理するよう切り離し、AppSyncを経由して取得することにしました。

環境

主に使用したライブラリ バージョン
Xamarin.Forms v5.0.0.2012
GraphQL.Client v3.2.4
GraphQL.Client.Serializer.Newtonsoft v3.2.4
主に使用したAWS機能
AppSync
Lambda

作成したもの

Lambdaからデータを返却し、AppSyncを経由してレスポンスを取得する点に焦点を当てたサンプルリポジトリを作成しました。
AWS側(AppSync、Lambda)に関してはREADME.mdに記載しています。
当記事では省略している箇所が多々あるので、詳細はこちらをご覧ください。
AppSyncDemo

ソースコード

このサンプルでは、AppSyncに定義されている変数のデータをすべて取得するGraphQLにて実行すると、以下のようなレスポンスが返却されます。
Lambda自体は、以下のGetSampleキーの値にあたる箇所を返却しています。

name="Xamarin"で実行した場合のAppSyncレスポンス
{
  "data": {
    "GetSample": {
      "result": {
        "status_code": 200
      },
      "data": {
        "message": "Hello Xamarin!!!",
        "hoge": "abc",
        "fuga": "あいう",
        "piyo": "xyz",
        "foo": 123,
        "bar": 456
      }
    }
  }
}

Lambdaの返却値の中で、必要なデータがmessageのみだった場合、以下のようにGraphQLを定義します。
GraphQLでは、行の先頭に#をつけると以降がコメント行になるので、コメント行を使用して不要な変数を除いても構いません。

フロントエンドで必要なデータを定義
query MyQuery($name: String) {
  GetSample(name: $name) {
    data {
      message
    }
  }
}

Modelクラスは、GraphQLに合わせて実装します。
今回はGraphQLのdataの内側部分のみを対象としたいと思います。

SampleModel.cs
using System;
using System.Collections.Generic;
using System.Runtime.Serialization;
using System.Text;

namespace AppSyncDemo.Models
{
    [DataContract]
    public class SampleModel
    {
        [DataMember(Name = "message")]
        public string Message { get; set; }
    }
}

以下のソースは、GraphQLを読み込んで、Queryとして実行する処理を行います。
GraphQLは、AppSyncDemoプロジェクトのGraphQLsフォルダに、埋め込みリソースとして格納しています。

AppSyncService.cs
using AppSyncDemo.Models;
using GraphQL;
using GraphQL.Client.Http;
using GraphQL.Client.Serializer.Newtonsoft;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using System;
using System.Collections.Generic;
using System.IO;
using System.Reflection;
using System.Text;
using System.Threading.Tasks;

namespace AppSyncDemo.Services
{
    public class AppSyncService
    {
        public static AppSyncService Instance { get; private set; } = new AppSyncService();
        private GraphQLHttpClient GraphQLHttpClient { get; set; }

        public AppSyncService()
        {
            var options = new GraphQLHttpClientOptions
            {
                EndPoint = new Uri(ApiKey.AppSyncApiUrl),
            };
            this.GraphQLHttpClient = new GraphQLHttpClient(options, new NewtonsoftJsonSerializer());
            this.GraphQLHttpClient.HttpClient.DefaultRequestHeaders.Add("x-api-key", ApiKey.AppSyncApiKey);
        }

        public async Task<SampleModel> GetSampleAsync(string name)
        {
            var apiName = "GetSample";
            var variables = new 
            {
                name = name,
            };
            var response = await ExecQueryAsync<SampleModel>(apiName, variables);
            return response;
        }

        /// <summary>
        /// Queryを実行する
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="apiName"></param>
        /// <param name="variables"></param>
        /// <returns></returns>
        public async Task<T> ExecQueryAsync<T>(string apiName, object variables)
        {
            try
            {
                // GraphQL取得
                var resourceId = $"AppSyncDemo.GraphQLs.{apiName}.gql";
                var query = await GetQueryAsync(resourceId);

                // リクエスト作成
                var request = new GraphQLRequest
                {
                    Query = query,
                    OperationName = "MyQuery",
                    Variables = variables,
                };

                // Query実行
                // Mutation実行の場合はSendMutationAsync()を使用
                var response = await this.GraphQLHttpClient.SendQueryAsync<JObject>(request);
                // [apiName]から先が必要かどうかはレスポンスの構造に応じて変更する必要あり
                var json = response.Data[apiName]["data"].ToString();
                var ret = JsonConvert.DeserializeObject<T>(json);

                return ret;
            }
            catch (Exception)
            {
                return default(T);
            }
        }

        /// <summary>
        /// GraphQLを取得
        /// </summary>
        /// <param name="resourceId"></param>
        /// <returns></returns>
        private async Task<string> GetQueryAsync(string resourceId)
        {
            var assembly = Assembly.GetExecutingAssembly();
            using (var stream = assembly.GetManifestResourceStream(resourceId))
            using (var reader = new StreamReader(stream))
            {
                return await reader.ReadToEndAsync();
            }
        }
    }
}

一通りの流れは以上です。

元々の目的は、APIキーのデータのみ取得だったので、AppSync経由ではなくAPI Gateway経由でもよかったのですが、あまり記事を見かけなかったので、AppSyncの記事を作成しました。
AppSyncの記事をあまり見かけなかった、というよりも、C#とAppSyncを使用した記事をあまり見かけなかった、と言った方が正確かもしれません。
そもそも、「APIキーを取得するAPI」というのがあまりよろしくなさそうな気はしていますが、その点についてはあまり考えないことにしました。

参考リンク

sagulati/dotnet-lambda-refarch-imagerecognition
C#で不定形JSONを自在に扱いたい

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0