/* SPDX-License-Identifier: Apache-2.0 * * The OpenSearch Contributors require contributions made to * this file be licensed under the Apache-2.0 license or a * compatible open source license. */ /* * Modifications Copyright OpenSearch Contributors. See * GitHub history for details. * * Licensed to Elasticsearch B.V. under one or more contributor * license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright * ownership. Elasticsearch B.V. licenses this file to you under * the Apache License, Version 2.0 (the "License"); you may * not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ using System; using System.Collections.Generic; using System.Linq; namespace OpenSearch.Client { /// /// Each Request type holds a static instance of this class which creates cached builders for each /// of the defined urls in the json spec. /// internal class ApiUrls { private static readonly RouteValues EmptyRouteValues = new(); private readonly string _errorMessageSuffix; /// /// If the spec only defines a single non parameterizable route this allows us to shortcircuit and avoid hitting /// the cached string builders. /// private readonly string _fixedUrl; /// /// Creates a lookup for number of parts <=> list of routes with that number of parts. /// allows us to quickly find the right url to use in the list. /// public Dictionary> Routes { get; } /// Only intended to be created once per request and stored in a static internal ApiUrls(string[] routes) { if (routes == null || routes.Length == 0) throw new ArgumentException("urls is null or empty", nameof(routes)); if (routes.Length == 1 && !routes[0].Contains("{")) _fixedUrl = routes[0]; else { foreach (var route in routes) { var bracketsCount = route.Count(c => c.Equals('{')); if (Routes == null) Routes = new Dictionary>(); if (Routes.ContainsKey(bracketsCount)) Routes[bracketsCount].Add(new UrlLookup(route)); else Routes.Add(bracketsCount, new List { new UrlLookup(route) }); } } _errorMessageSuffix = string.Join(",", routes); // received multiple urls without brackets we resolve to first if (Routes == null) _fixedUrl = routes[0]; } public string Resolve(RouteValues routeValues, IConnectionSettingsValues settings) { if (_fixedUrl != null) return _fixedUrl; var resolved = routeValues.Resolve(settings); if (!Routes.TryGetValue(resolved.Count, out var routes)) throw new Exception($"No route taking {resolved.Count} parameters{_errorMessageSuffix}"); if (routes.Count == 1) return routes[0].ToUrl(resolved); //find the first url with N parts that has all provided named parts foreach (var u in routes) { if (u.Matches(resolved)) return u.ToUrl(resolved); } throw new Exception($"No route taking {routeValues.Count} parameters{_errorMessageSuffix}"); } } }