Esempio n. 1
0
        public async Task <IEnumerable <ModEntryModel> > PostAsync([FromBody] ModSearchModel model)
        {
            if (model?.Mods == null)
            {
                return(new ModEntryModel[0]);
            }

            // fetch wiki data
            WikiModEntry[] wikiData = this.WikiCache.GetWikiMods().Select(p => p.GetModel()).ToArray();
            IDictionary <string, ModEntryModel> mods = new Dictionary <string, ModEntryModel>(StringComparer.CurrentCultureIgnoreCase);

            foreach (ModSearchEntryModel mod in model.Mods)
            {
                if (string.IsNullOrWhiteSpace(mod.ID))
                {
                    continue;
                }

                ModEntryModel result = await this.GetModData(mod, wikiData, model.IncludeExtendedMetadata);

                mods[mod.ID] = result;
            }

            // return data
            return(mods.Values);
        }
Esempio n. 2
0
        public async Task <IEnumerable <ModEntryModel> > PostAsync([FromBody] ModSearchModel model, [FromRoute] string version)
        {
            if (model?.Mods == null)
            {
                return(new ModEntryModel[0]);
            }

            // fetch wiki data
            WikiModEntry[] wikiData = this.WikiCache.GetWikiMods().Select(p => p.Data).ToArray();
            IDictionary <string, ModEntryModel> mods = new Dictionary <string, ModEntryModel>(StringComparer.CurrentCultureIgnoreCase);

            foreach (ModSearchEntryModel mod in model.Mods)
            {
                if (string.IsNullOrWhiteSpace(mod.ID))
                {
                    continue;
                }

                ModEntryModel result = await this.GetModData(mod, wikiData, model.IncludeExtendedMetadata, model.ApiVersion);

                if (!model.IncludeExtendedMetadata && (model.ApiVersion == null || mod.InstalledVersion == null))
                {
                    var errors = new List <string>(result.Errors);
                    errors.Add($"This API can't suggest an update because {nameof(model.ApiVersion)} or {nameof(mod.InstalledVersion)} are null, and you didn't specify {nameof(model.IncludeExtendedMetadata)} to get other info. See the SMAPI technical docs for usage.");
                    result.Errors = errors.ToArray();
                }

                mods[mod.ID] = result;
            }

            // return data
            return(mods.Values);
        }
Esempio n. 3
0
        public async Task <object> PostAsync([FromBody] ModSearchModel model)
        {
            // parse request data
            ISemanticVersion apiVersion = this.GetApiVersion();

            ModSearchEntryModel[] searchMods = this.GetSearchMods(model, apiVersion).ToArray();

            // fetch wiki data
            WikiCompatibilityEntry[] wikiData = await this.GetWikiDataAsync();

            // fetch data
            IDictionary <string, ModEntryModel> mods = new Dictionary <string, ModEntryModel>(StringComparer.CurrentCultureIgnoreCase);

            foreach (ModSearchEntryModel mod in searchMods)
            {
                if (string.IsNullOrWhiteSpace(mod.ID))
                {
                    continue;
                }

                ModEntryModel result = await this.GetModData(mod, wikiData, model.IncludeExtendedMetadata);

                result.SetBackwardsCompatibility(apiVersion);
                mods[mod.ID] = result;
            }

            // return in expected structure
            return(apiVersion.IsNewerThan("2.6-beta.18")
                ? mods.Values
                : (object)mods);
        }
Esempio n. 4
0
        /// <summary>Get the mods for which the API should return data.</summary>
        /// <param name="model">The search model.</param>
        /// <param name="apiVersion">The requested API version.</param>
        private IEnumerable <ModSearchEntryModel> GetSearchMods(ModSearchModel model, ISemanticVersion apiVersion)
        {
            if (model == null)
            {
                yield break;
            }

            // yield standard entries
            if (model.Mods != null)
            {
                foreach (ModSearchEntryModel mod in model.Mods)
                {
                    yield return(mod);
                }
            }

            // yield mod update keys if backwards compatible
            if (model.ModKeys != null && model.ModKeys.Any() && !apiVersion.IsNewerThan("2.6-beta.17"))
            {
                foreach (string updateKey in model.ModKeys.Distinct())
                {
                    yield return(new ModSearchEntryModel(updateKey, new[] { updateKey }));
                }
            }
        }
Esempio n. 5
0
        public async Task <IDictionary <string, ModInfoModel> > PostAsync([FromBody] ModSearchModel search)
        {
            // parse model
            bool allowInvalidVersions = search?.AllowInvalidVersions ?? false;

            string[] modKeys = (search?.ModKeys?.ToArray() ?? new string[0])
                               .Distinct(StringComparer.CurrentCultureIgnoreCase)
                               .OrderBy(p => p, StringComparer.CurrentCultureIgnoreCase)
                               .ToArray();

            // fetch mod info
            IDictionary <string, ModInfoModel> result = new Dictionary <string, ModInfoModel>(StringComparer.CurrentCultureIgnoreCase);

            foreach (string modKey in modKeys)
            {
                // parse mod key
                if (!this.TryParseModKey(modKey, out string vendorKey, out string modID))
                {
                    result[modKey] = new ModInfoModel("The mod key isn't in a valid format. It should contain the site key and mod ID like 'Nexus:541'.");
                    continue;
                }

                // get matching repository
                if (!this.Repositories.TryGetValue(vendorKey, out IModRepository repository))
                {
                    result[modKey] = new ModInfoModel($"There's no mod site with key '{vendorKey}'. Expected one of [{string.Join(", ", this.Repositories.Keys)}].");
                    continue;
                }

                // fetch mod info
                result[modKey] = await this.Cache.GetOrCreateAsync($"{repository.VendorKey}:{modID}".ToLower(), async entry =>
                {
                    // fetch info
                    ModInfoModel info = await repository.GetModInfoAsync(modID);

                    // validate
                    if (info.Error == null)
                    {
                        if (info.Version == null)
                        {
                            info = new ModInfoModel(name: info.Name, version: info.Version, url: info.Url, error: "Mod has no version number.");
                        }
                        if (!allowInvalidVersions && !Regex.IsMatch(info.Version, this.VersionRegex, RegexOptions.CultureInvariant | RegexOptions.IgnoreCase))
                        {
                            info = new ModInfoModel(name: info.Name, version: info.Version, url: info.Url, error: $"Mod has invalid semantic version '{info.Version}'.");
                        }
                    }

                    // cache & return
                    entry.AbsoluteExpiration = DateTimeOffset.UtcNow.AddMinutes(info.Error == null ? this.SuccessCacheMinutes : this.ErrorCacheMinutes);
                    return(info);
                });
            }

            return(result);
        }
Esempio n. 6
0
        public async Task <IEnumerable <ModEntryModel> > PostAsync([FromBody] ModSearchModel model)
        {
            using IClient client = new FluentClient("https://smapi.io/api");

            Startup.ConfigureJsonNet(client.Formatters.JsonFormatter.SerializerSettings);

            return(await client
                   .PostAsync(this.Request.Path)
                   .WithBody(model)
                   .AsArray <ModEntryModel>());
        }
Esempio n. 7
0
        public async Task <IEnumerable <ModEntryModel> > PostAsync([FromBody] ModSearchModel model, [FromRoute] string version)
        {
            if (model?.Mods == null)
            {
                return(new ModEntryModel[0]);
            }

            bool legacyMode = SemanticVersion.TryParse(version, out ISemanticVersion parsedVersion) && parsedVersion.IsOlderThan("3.0.0-beta.20191109");

            // fetch wiki data
            WikiModEntry[] wikiData = this.WikiCache.GetWikiMods().Select(p => p.GetModel()).ToArray();
            IDictionary <string, ModEntryModel> mods = new Dictionary <string, ModEntryModel>(StringComparer.CurrentCultureIgnoreCase);

            foreach (ModSearchEntryModel mod in model.Mods)
            {
                if (string.IsNullOrWhiteSpace(mod.ID))
                {
                    continue;
                }

                ModEntryModel result = await this.GetModData(mod, wikiData, model.IncludeExtendedMetadata || legacyMode, model.ApiVersion);

                if (legacyMode)
                {
                    result.Main              = result.Metadata.Main;
                    result.Optional          = result.Metadata.Optional;
                    result.Unofficial        = result.Metadata.Unofficial;
                    result.UnofficialForBeta = result.Metadata.UnofficialForBeta;
                    result.HasBetaInfo       = result.Metadata.BetaCompatibilityStatus != null;
                    result.SuggestedUpdate   = null;
                    if (!model.IncludeExtendedMetadata)
                    {
                        result.Metadata = null;
                    }
                }
                else if (!model.IncludeExtendedMetadata && (model.ApiVersion == null || mod.InstalledVersion == null))
                {
                    var errors = new List <string>(result.Errors);
                    errors.Add($"This API can't suggest an update because {nameof(model.ApiVersion)} or {nameof(mod.InstalledVersion)} are null, and you didn't specify {nameof(model.IncludeExtendedMetadata)} to get other info. See the SMAPI technical docs for usage.");
                    result.Errors = errors.ToArray();
                }

                mods[mod.ID] = result;
            }

            // return data
            return(mods.Values);
        }
Esempio n. 8
0
        public async Task <IEnumerable <ModEntryModel> > PostAsync([FromBody] ModSearchModel model, [FromRoute] string version)
        {
            if (model?.Mods == null)
            {
                return(Array.Empty <ModEntryModel>());
            }

            ModUpdateCheckConfig config = this.Config.Value;

            // fetch wiki data
            WikiModEntry[] wikiData = this.WikiCache.GetWikiMods().Select(p => p.Data).ToArray();
            IDictionary <string, ModEntryModel> mods = new Dictionary <string, ModEntryModel>(StringComparer.CurrentCultureIgnoreCase);

            foreach (ModSearchEntryModel mod in model.Mods)
            {
                if (string.IsNullOrWhiteSpace(mod.ID))
                {
                    continue;
                }

                // special case: if this is an update check for the official SMAPI repo, check the Nexus mod page for beta versions
                if (mod.ID == config.SmapiInfo.ID && mod.UpdateKeys?.Any(key => key == config.SmapiInfo.DefaultUpdateKey) == true && mod.InstalledVersion?.IsPrerelease() == true)
                {
                    mod.UpdateKeys = mod.UpdateKeys.Concat(config.SmapiInfo.AddBetaUpdateKeys).ToArray();
                }

                // fetch result
                ModEntryModel result = await this.GetModData(mod, wikiData, model.IncludeExtendedMetadata, model.ApiVersion);

                if (!model.IncludeExtendedMetadata && (model.ApiVersion == null || mod.InstalledVersion == null))
                {
                    var errors = new List <string>(result.Errors);
                    errors.Add($"This API can't suggest an update because {nameof(model.ApiVersion)} or {nameof(mod.InstalledVersion)} are null, and you didn't specify {nameof(model.IncludeExtendedMetadata)} to get other info. See the SMAPI technical docs for usage.");
                    result.Errors = errors.ToArray();
                }

                mods[mod.ID] = result;
            }

            // return data
            return(mods.Values);
        }