go.chromium.org/luci@v0.0.0-20240309015107-7cdc2e660f33/milo/ui/src/swarming/pages/swarming_build_page.tsx (about)

     1  // Copyright 2023 The LUCI Authors.
     2  //
     3  // Licensed under the Apache License, Version 2.0 (the "License");
     4  // you may not use this file except in compliance with the License.
     5  // You may obtain a copy of the License at
     6  //
     7  //      http://www.apache.org/licenses/LICENSE-2.0
     8  //
     9  // Unless required by applicable law or agreed to in writing, software
    10  // distributed under the License is distributed on an "AS IS" BASIS,
    11  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    12  // See the License for the specific language governing permissions and
    13  // limitations under the License.
    14  
    15  import { CircularProgress } from '@mui/material';
    16  import { useEffect, useMemo } from 'react';
    17  import { useNavigate, useParams } from 'react-router-dom';
    18  
    19  import { RecoverableErrorBoundary } from '@/common/components/error_handling';
    20  import { usePrpcQuery } from '@/common/hooks/legacy_prpc_query';
    21  import { TasksServices } from '@/common/services/swarming';
    22  import { useSyncedSearchParams } from '@/generic_libs/hooks/synced_search_params';
    23  import { getBuildURLPathFromTags } from '@/swarming/tools/utils';
    24  
    25  const ALLOWED_HOSTS = Object.freeze([
    26    SETTINGS.swarming.defaultHost,
    27    ...(SETTINGS.swarming.allowedHosts || []),
    28  ]);
    29  
    30  export function SwarmingBuildPage() {
    31    const { taskId } = useParams();
    32    const [searchParams] = useSyncedSearchParams();
    33    const navigate = useNavigate();
    34  
    35    if (!taskId) {
    36      throw new Error('invariant violated: taskId should be set');
    37    }
    38  
    39    const swarmingHost =
    40      searchParams.get('server') || SETTINGS.swarming.defaultHost;
    41    if (!ALLOWED_HOSTS.includes(swarmingHost)) {
    42      throw new Error(`'${swarmingHost}' is not an allowed host`);
    43    }
    44  
    45    const { data, error } = usePrpcQuery({
    46      host: swarmingHost,
    47      Service: TasksServices,
    48      method: 'getRequest',
    49      request: {
    50        taskId,
    51      },
    52    });
    53  
    54    if (error) {
    55      throw error;
    56    }
    57  
    58    const targetUrlPath = useMemo(
    59      () => data?.tags && getBuildURLPathFromTags(data.tags),
    60      [data],
    61    );
    62  
    63    useEffect(() => {
    64      if (!targetUrlPath) {
    65        return;
    66      }
    67  
    68      navigate(targetUrlPath);
    69    }, [targetUrlPath, navigate]);
    70  
    71    if (data && !targetUrlPath) {
    72      throw new Error(`task: ${taskId} does not have an associated build.`);
    73    }
    74  
    75    return (
    76      <div css={{ padding: '10px' }}>
    77        Redirecting to the build page associated with the task: {taskId}{' '}
    78        <CircularProgress />
    79      </div>
    80    );
    81  }
    82  
    83  export const element = (
    84    // See the documentation for `<LoginPage />` for why we handle error this way.
    85    <RecoverableErrorBoundary key="swarming-build">
    86      <SwarmingBuildPage />
    87    </RecoverableErrorBoundary>
    88  );