Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Google Dynamic Links with .NET

I'm trying to get google firebase dynamic link work on my .net core project my code is as below

public static async Task<string> GetShortLink(string longLink)
{
    var service = AuthenticateServiceAccount("[email protected]", "Opt/Keys/quallogi-keys.json", new[] { "https://www.googleapis.com/auth/firebase" });
    var request = service.ManagedShortLinks.Create(new CreateManagedShortLinkRequest
    {
        DynamicLinkInfo = new DynamicLinkInfo
        {
            //DynamicLinkDomain = "https://quallogi.page.link",
            DomainUriPrefix = "quallogi.page.link",
            AnalyticsInfo = new AnalyticsInfo(),
            IosInfo = new IosInfo(),
            Link = "https://github.com/distriqt/ANE-Firebase/wiki/DynamicLinks---Create-Dynamic-Links",

        },

        Suffix = new Suffix { Option = "SHORT" },
        Name = "shortlink",


    });
    var response = await request.ExecuteAsync();
    return response.PreviewLink;
}

public static FirebaseDynamicLinksService AuthenticateServiceAccount(string serviceAccountEmail, string serviceAccountCredentialFilePath, string[] scopes)
{
    try
    {
        if (string.IsNullOrEmpty(serviceAccountCredentialFilePath))
            throw new Exception("Path to the service account credentials file is required.");
        if (!File.Exists(serviceAccountCredentialFilePath))
            throw new Exception("The service account credentials file does not exist at: " + serviceAccountCredentialFilePath);
        if (string.IsNullOrEmpty(serviceAccountEmail))
            throw new Exception("ServiceAccountEmail is required.");

        if (Path.GetExtension(serviceAccountCredentialFilePath).ToLower() == ".json")
        {
            GoogleCredential credential;
            using (var stream = new FileStream(serviceAccountCredentialFilePath, FileMode.Open, FileAccess.Read))
            {
                credential = GoogleCredential.FromStream(stream)
                     .CreateScoped(scopes);
            }

            return new FirebaseDynamicLinksService(new BaseClientService.Initializer()
            {
                HttpClientInitializer = credential,
                ApplicationName = "Firebasedynamiclinks Service account Authentication Sample",
            });
        }
        else if (Path.GetExtension(serviceAccountCredentialFilePath).ToLower() == ".p12")
        {

            var certificate = new X509Certificate2(serviceAccountCredentialFilePath, "notasecret", X509KeyStorageFlags.MachineKeySet | X509KeyStorageFlags.Exportable);
            var credential = new ServiceAccountCredential(new ServiceAccountCredential.Initializer(serviceAccountEmail)
            {
                Scopes = scopes
            }.FromCertificate(certificate));

            return new FirebaseDynamicLinksService(new BaseClientService.Initializer()
            {
                HttpClientInitializer = credential,
                ApplicationName = "Firebasedynamiclinks Authentication Sample",
            });
        }
        else
        {
            throw new Exception("Unsupported Service accounts credentials.");
        }

    }
    catch (Exception ex)
    {
        throw new Exception("CreateServiceAccountFirebasedynamiclinksFailed", ex);
    }
}

but when i run the code google throws exception

Google.Apis.Requests.RequestError Internal error encountered. [500] Errors [ Message[Internal error encountered.] Location[ - ] Reason[backendError] Domain[global] ]

enter image description here

what was the issue ?

like image 841
Gayan Avatar asked Nov 12 '18 04:11

Gayan


People also ask

Is Firebase dynamic links free?

Dynamic Links are smart URLs that allow you to send existing and potential users to any location within your iOS or Android app. They survive the app install process, so even new users see the content they're looking for when they open the app for the first time. Dynamic Links are no-cost forever, for any scale.

How many dynamic links can be created in Firebase?

To receive Dynamic Links in your app, see the documentation for iOS, Android, C++, and Unity. Requests are limited to 5 requests/IP address/second, and 200,000 requests/day.

How do I get data from Firebase dynamic link?

Firebase Dynamic Links records events related to your Dynamic Links' performance, including events that happen outside of your apps, such as the number of times someone clicks on one of your short Dynamic Links. This data can be viewed in the Dynamic Links section of the Firebase console and retrieved using a REST API.


1 Answers

I'm a little surprised you got as far as you did. Currently, this library has two issues:

  1. When using ManagedShortLinks, it blows up as described. It only gets so far if you deal with the second issue, however.
  2. The ETag members are undecorated, and get serialized as is. You will see an error like so:
Google.Apis.Requests.RequestError
Invalid JSON payload received. Unknown name "ETag" at 'dynamic_link_info.android_info': Cannot find field.
Invalid JSON payload received. Unknown name "ETag" at 'dynamic_link_info.ios_info': Cannot find field.
Invalid JSON payload received. Unknown name "ETag" at 'dynamic_link_info': Cannot find field.
Invalid JSON payload received. Unknown name "ETag" at 'suffix': Cannot find field.
Invalid JSON payload received. Unknown name "ETag": Cannot find field. [400]

I haven't found a way around ManagedShortLinks. However, ShortLinks will work. I'll show you how I did it.

       public async Task<string> GetDeepLink(Invitation inv)
       {
           var playId = _configurationProvider.GetSetting(AppSettingNames.GooglePlayAppId);
           var iosId = _configurationProvider.GetSetting(AppSettingNames.AppleAppStoreAppId);
           var domain = _configurationProvider.GetSetting(AppSettingNames.GoogleFirebaseDynamicLinkDomain);

           NameValueCollection queryString = System.Web.HttpUtility.ParseQueryString(string.Empty);

           queryString["Key1"] = "value1";

           var mslReq = new CreateShortDynamicLinkRequest();
           mslReq.DynamicLinkInfo = new DynamicLinkInfo();
           mslReq.DynamicLinkInfo.AndroidInfo = new AndroidInfo() { AndroidPackageName = playId };
           mslReq.DynamicLinkInfo.IosInfo = new IosInfo() { IosAppStoreId = iosId, IosBundleId = playId };
           mslReq.DynamicLinkInfo.DomainUriPrefix = $"https://{domain}";
           mslReq.DynamicLinkInfo.Link = $"https://www.example.com/?{queryString}";
           mslReq.Suffix = new Suffix() { Option = "SHORT" };
           var json = JsonConvert.SerializeObject(mslReq, Formatting.Indented, new CreateShortDynamicLinkRequestConverter());

           var request = _firebaseDynamicLinksService.ShortLinks.Create(new CreateShortDynamicLinkRequest());

           request.ModifyRequest = message =>
               message.Content = new StringContent(json, Encoding.UTF8, "application/json");

           var res = await request.ExecuteAsync();
           return res.ShortLink;
       }

This depends upon CreateShortDynamicLinkRequestConverter:

    public class CreateShortDynamicLinkRequestConverter : JsonConverter
    {
        public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
        {
            serializer.NullValueHandling = NullValueHandling.Ignore;
            var t = JToken.FromObject(value);
            var modified = t.RemoveFields("ETag");

            modified.WriteTo(writer);
        }

        public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
        {
            throw new NotImplementedException();
        }

        public override bool CanConvert(Type objectType)
        {
            return true;
        }

        public override bool CanRead => false;
    }

which in turn depends upon RemoveFields:

        // source: https://stackoverflow.com/a/31581951/773673
        public static JToken RemoveFields(this JToken token, params string[] fields)
        {
            JContainer container = token as JContainer;
            if (container == null) return token;

            List<JToken> removeList = new List<JToken>();
            foreach (JToken el in container.Children())
            {
                JProperty p = el as JProperty;
                if (p != null && (fields.Contains(p.Name)))
                {
                    removeList.Add(el);
                }
                el.RemoveFields(fields);
            }

            foreach (JToken el in removeList)
            {
                el.Remove();
            }

            return token;
        }

At the end of the day, the big problem here is the lack of decoration of the ETag members. We need to work around that. I believe that customizing BaseClientService.Initializer.Serializer when the service is instantiated with the public NewtonsoftJsonSerializer(JsonSerializerSettings settings) constructor will allow you to specify the Converters to use, but I stopped when I got it working. The real fix for this is to simply decorate the ETag members to not participate in serialization (providing that doesn't break anything else!).

like image 109
Adam Skinner Avatar answered Nov 11 '22 19:11

Adam Skinner