go.chromium.org/luci@v0.0.0-20240309015107-7cdc2e660f33/milo/ui/src/common/store/services.ts (about) 1 // Copyright 2022 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 { PrpcClientOptions } from '@chopsui/prpc-client'; 16 import { computed, untracked } from 'mobx'; 17 import { 18 addDisposer, 19 Instance, 20 isAlive, 21 SnapshotIn, 22 SnapshotOut, 23 types, 24 } from 'mobx-state-tree'; 25 import { keepAlive } from 'mobx-utils'; 26 27 import { MAY_REQUIRE_SIGNIN } from '@/common/common_tags'; 28 import { POTENTIAL_PERM_ERROR_CODES } from '@/common/constants/rpc'; 29 import { BuildsService } from '@/common/services/buildbucket'; 30 import { 31 ClustersService, 32 TestHistoryService, 33 } from '@/common/services/luci_analysis'; 34 import { MiloInternal } from '@/common/services/milo_internal'; 35 import { ResultDb } from '@/common/services/resultdb'; 36 import { PrpcClientExt } from '@/generic_libs/tools/prpc_client_ext'; 37 import { attachTags } from '@/generic_libs/tools/tag'; 38 39 import { AuthStateStore, AuthStateStoreInstance } from './auth_state'; 40 41 export const ServicesStore = types 42 .model('ServicesStore', { 43 id: types.optional(types.identifierNumber, () => Math.random()), 44 authState: types.safeReference(AuthStateStore), 45 }) 46 .views((self) => { 47 function makeClient(opts: PrpcClientOptions) { 48 // Don't track the access token so services won't be refreshed when the 49 // access token is updated. 50 return new PrpcClientExt( 51 opts, 52 () => 53 untracked( 54 () => (isAlive(self) && self.authState?.value?.accessToken) || '', 55 ), 56 (e) => { 57 if (POTENTIAL_PERM_ERROR_CODES.includes(e.code)) { 58 attachTags(e, MAY_REQUIRE_SIGNIN); 59 } 60 throw e; 61 }, 62 ); 63 } 64 65 return { 66 get resultDb() { 67 if (!self.authState?.identity) { 68 return null; 69 } 70 return new ResultDb(makeClient({ host: SETTINGS.resultdb.host })); 71 }, 72 get testHistory() { 73 if (!self.authState?.identity) { 74 return null; 75 } 76 return new TestHistoryService( 77 makeClient({ host: SETTINGS.luciAnalysis.host }), 78 ); 79 }, 80 get milo() { 81 if (!self.authState?.identity) { 82 return null; 83 } 84 return new MiloInternal( 85 makeClient({ host: '', insecure: location.protocol === 'http:' }), 86 ); 87 }, 88 get builds() { 89 if (!self.authState?.identity) { 90 return null; 91 } 92 return new BuildsService( 93 makeClient({ host: SETTINGS.buildbucket.host }), 94 ); 95 }, 96 get clusters() { 97 if (!self.authState?.identity) { 98 return null; 99 } 100 return new ClustersService( 101 makeClient({ host: SETTINGS.luciAnalysis.host }), 102 ); 103 }, 104 }; 105 }) 106 .actions((self) => ({ 107 setDependencies({ authState }: { authState: AuthStateStoreInstance }) { 108 self.authState = authState; 109 }, 110 afterCreate() { 111 // These computed properties contains internal caches. Keep them alive. 112 addDisposer(self, keepAlive(computed(() => self.resultDb))); 113 addDisposer(self, keepAlive(computed(() => self.testHistory))); 114 addDisposer(self, keepAlive(computed(() => self.milo))); 115 addDisposer(self, keepAlive(computed(() => self.builds))); 116 addDisposer(self, keepAlive(computed(() => self.clusters))); 117 }, 118 })); 119 120 export type ServicesStoreInstance = Instance<typeof ServicesStore>; 121 export type ServicesStoreSnapshotIn = SnapshotIn<typeof ServicesStore>; 122 export type ServicesStoreSnapshotOut = SnapshotOut<typeof ServicesStore>;