endpointId() === $endpoint->endpointId(); })); } /** * @param array[] $groupedEndpoints * @param array $configFileOrder The order for groups that users specified in their config file. * * @return array[] */ public static function sortByConfigFileOrder(array $groupedEndpoints, array $configFileOrder): array { if (empty($configFileOrder)) { ksort($groupedEndpoints, SORT_NATURAL); return $groupedEndpoints; } // First, sort groups $groupsOrder = Utils::getTopLevelItemsFromMixedConfigList($configFileOrder); $groupedEndpoints = collect($groupedEndpoints)->sortKeysUsing(self::getOrderListComparator($groupsOrder)); return $groupedEndpoints->map(function (array $group, string $groupName) use ($configFileOrder) { $sortedEndpoints = collect($group['endpoints']); if (isset($configFileOrder[$groupName])) { // Second-level order list. Can contain endpoint or subgroup names $level2Order = Utils::getTopLevelItemsFromMixedConfigList($configFileOrder[$groupName]); $sortedEndpoints = $sortedEndpoints->sortBy( function (OutputEndpointData $e) use ($configFileOrder, $level2Order) { $endpointIdentifier = $e->httpMethods[0] . ' /' . $e->uri; // First, check if there's an ordering specified for the endpoint itself $indexOfEndpointInL2Order = array_search($endpointIdentifier, $level2Order); if ($indexOfEndpointInL2Order !== false) { return $indexOfEndpointInL2Order; } // Check if there's an ordering for the endpoint's subgroup $indexOfSubgroupInL2Order = array_search($e->metadata->subgroup, $level2Order); if ($indexOfSubgroupInL2Order !== false) { // There's a subgroup order; check if there's an endpoints order within that $orderOfEndpointsInSubgroup = $configFileOrder[$e->metadata->groupName][$e->metadata->subgroup] ?? []; $indexOfEndpointInSubGroup = array_search($endpointIdentifier, $orderOfEndpointsInSubgroup); return ($indexOfEndpointInSubGroup === false) ? $indexOfSubgroupInL2Order : ($indexOfSubgroupInL2Order + ($indexOfEndpointInSubGroup * 0.1)); } return INF; }, ); } return [ 'name' => $groupName, 'description' => $group['description'], 'endpoints' => $sortedEndpoints->all(), ]; })->values()->all(); } /** * Prepare endpoints to be turned into HTML. * Map them into OutputEndpointData DTOs, and sort them by the specified order in the config file. * * @param array $groupedEndpoints * * @return array */ public static function prepareGroupedEndpointsForOutput(array $groupedEndpoints, array $configFileOrder = []): array { $groups = array_map(function (array $group) { return [ 'name' => $group['name'], 'description' => $group['description'], 'endpoints' => array_map( fn(array $endpoint) => OutputEndpointData::fromExtractedEndpointArray($endpoint), $group['endpoints'] ), ]; }, $groupedEndpoints); return Camel::sortByConfigFileOrder($groups, $configFileOrder); } /** * Given an $order list like ['first', 'second', ...], return a compare function that can be used to sort * a list of strings based on the order of items in $order. * Any strings not in the list are sorted with natural sort. * * @param array $order */ public static function getOrderListComparator(array $order): \Closure { return function ($a, $b) use ($order) { $indexOfA = array_search($a, $order); $indexOfB = array_search($b, $order); // If both are in the $order list, compare them normally based on their position in the list if ($indexOfA !== false && $indexOfB !== false) { return $indexOfA <=> $indexOfB; } // If only A is in the $order list, then it must come before B. if ($indexOfA !== false) { return -1; } // If only B is in the $order list, then it must come before A. if ($indexOfB !== false) { return 1; } // If neither is present, fall back to natural sort return strnatcmp($a, $b); }; } }