Mirror of https://github.com/roostorg/coop github.com/roostorg/coop
0
fork

Configure Feed

Select the types of activity you want to include in your feed.

at 557ff54b2b435e5f1e789c6a8a4e1bebf2d7deb6 225 lines 6.6 kB view raw
1import { DataSource } from 'apollo-datasource'; 2 3import { inject, type Dependencies } from '../../iocContainer/index.js'; 4import '../../services/signalAuthService/index.js'; 5import { Integration } from '../../services/signalsService/index.js'; 6import { 7 CoopError, 8 ErrorType, 9 type ErrorInstanceData, 10} from '../../utils/errors.js'; 11import { getIntegrationRegistry } from '../../services/integrationRegistry/index.js'; 12import type { 13 IntegrationManifestEntry, 14 ModelCard, 15} from './integrationManifests.js'; 16import { type GQLSetIntegrationConfigInput } from '../generated.js'; 17 18export type TIntegrationConfigWithMetadata = Readonly<{ 19 name: string; 20 apiCredential: Readonly<Record<string, unknown>>; 21 modelCard: ModelCard; 22 modelCardLearnMoreUrl?: string; 23 title: string; 24 docsUrl: string; 25 requiresConfig: boolean; 26 logoUrl?: string; 27 logoWithBackgroundUrl?: string; 28}>; 29 30function defaultCredentialForIntegrationId( 31 integrationId: string, 32): Record<string, unknown> { 33 switch (integrationId) { 34 case Integration.GOOGLE_CONTENT_SAFETY_API: 35 case Integration.OPEN_AI: 36 return { apiKey: '' }; 37 case Integration.ZENTROPI: 38 return { apiKey: '', labelerVersions: [] }; 39 default: 40 return {}; 41 } 42} 43 44function mergeManifest( 45 integrationId: string, 46 apiCredential: Record<string, unknown>, 47 manifest: IntegrationManifestEntry, 48): TIntegrationConfigWithMetadata { 49 return { 50 name: integrationId, 51 apiCredential: { ...apiCredential, name: integrationId }, 52 modelCard: manifest.modelCard, 53 modelCardLearnMoreUrl: manifest.modelCardLearnMoreUrl, 54 title: manifest.title, 55 docsUrl: manifest.docsUrl, 56 requiresConfig: manifest.requiresConfig, 57 logoUrl: manifest.logoUrl, 58 logoWithBackgroundUrl: manifest.logoWithBackgroundUrl, 59 }; 60} 61 62/** 63 * TODO: this whole class should probably be merged into the signal auth service. 64 */ 65class IntegrationAPI extends DataSource { 66 constructor( 67 private readonly signalAuthService: Dependencies['SignalAuthService'], 68 ) { 69 super(); 70 } 71 72 async setConfig( 73 params: GQLSetIntegrationConfigInput, 74 orgId: string, 75 ): Promise<TIntegrationConfigWithMetadata> { 76 const { apiCredential } = params; 77 78 if (apiCredential.googleContentSafetyApi) { 79 return this.setConfigByIntegrationId( 80 'GOOGLE_CONTENT_SAFETY_API', 81 { apiKey: apiCredential.googleContentSafetyApi.apiKey }, 82 orgId, 83 ); 84 } 85 if (apiCredential.openAi) { 86 return this.setConfigByIntegrationId( 87 'OPEN_AI', 88 { apiKey: apiCredential.openAi.apiKey }, 89 orgId, 90 ); 91 } 92 if (apiCredential.zentropi) { 93 return this.setConfigByIntegrationId( 94 'ZENTROPI', 95 { 96 apiKey: apiCredential.zentropi.apiKey, 97 labelerVersions: [...(apiCredential.zentropi.labelerVersions ?? [])], 98 }, 99 orgId, 100 ); 101 } 102 103 throw new Error('No credentials provided'); 104 } 105 106 async setConfigByIntegrationId( 107 integrationId: string, 108 credential: Record<string, unknown>, 109 orgId: string, 110 ): Promise<TIntegrationConfigWithMetadata> { 111 const registry = getIntegrationRegistry(); 112 const manifest = registry.getManifest(integrationId); 113 if (manifest == null) { 114 throw new Error(`Unknown integration: ${integrationId}`); 115 } 116 const newCredential = await this.signalAuthService.setByIntegrationId( 117 integrationId, 118 orgId, 119 credential, 120 ); 121 return mergeManifest(integrationId, newCredential, manifest); 122 } 123 124 async getConfig( 125 orgId: string, 126 integrationId: string, 127 ): Promise<TIntegrationConfigWithMetadata | undefined> { 128 const registry = getIntegrationRegistry(); 129 const manifest = registry.getManifest(integrationId); 130 if (manifest == null) return undefined; 131 const credential = await this.signalAuthService.getByIntegrationId( 132 integrationId, 133 orgId, 134 ); 135 if (credential == null) return undefined; 136 return mergeManifest(integrationId, credential, manifest); 137 } 138 139 async getConfigWithMetadata( 140 orgId: string, 141 integrationId: string, 142 ): Promise<TIntegrationConfigWithMetadata> { 143 const registry = getIntegrationRegistry(); 144 const manifest = registry.getManifest(integrationId); 145 if (manifest == null) { 146 throw new Error(`Unknown integration: ${integrationId}`); 147 } 148 const credential = await this.signalAuthService.getByIntegrationId( 149 integrationId, 150 orgId, 151 ); 152 const apiCredential = 153 credential ?? defaultCredentialForIntegrationId(integrationId); 154 return mergeManifest(integrationId, apiCredential, manifest); 155 } 156 157 getAvailableIntegrations() { 158 return getIntegrationRegistry().getAvailableIntegrations(); 159 } 160 161 async getAllIntegrationConfigs( 162 orgId: string, 163 ): Promise<TIntegrationConfigWithMetadata[]> { 164 const ids = getIntegrationRegistry().getConfigurableIds(); 165 return Promise.all( 166 ids.map(async (integrationId) => 167 this.getConfigWithMetadata(orgId, integrationId), 168 ), 169 ); 170 } 171} 172 173export default inject(['SignalAuthService'], IntegrationAPI); 174export type { IntegrationAPI }; 175 176export type IntegrationErrorType = 177 | 'IntegrationConfigTooManyCredentialsError' 178 | 'IntegrationConfigUnsupportedIntegrationError' 179 | 'IntegrationNoInputCredentialsError' 180 | 'IntegrationEmptyInputCredentialsError'; 181 182export const makeIntegrationConfigTooManyCredentialsError = ( 183 data: ErrorInstanceData, 184) => 185 new CoopError({ 186 status: 400, 187 type: [ErrorType.InvalidUserInput], 188 title: 'This integration type expects a single api credential.', 189 name: 'IntegrationConfigTooManyCredentialsError', 190 ...data, 191 }); 192 193export const makeIntegrationConfigUnsupportedIntegrationError = ( 194 data: ErrorInstanceData, 195) => 196 new CoopError({ 197 status: 400, 198 type: [ErrorType.InvalidUserInput], 199 title: 'This integration type is not supported.', 200 name: 'IntegrationConfigUnsupportedIntegrationError', 201 ...data, 202 }); 203 204export const makeIntegrationNoInputCredentialsError = ( 205 data: ErrorInstanceData, 206) => 207 new CoopError({ 208 status: 400, 209 type: [ErrorType.InvalidUserInput], 210 title: 211 'This integration config creation expects at least one API credential.', 212 name: 'IntegrationNoInputCredentialsError', 213 ...data, 214 }); 215 216export const makeIntegrationEmptyInputCredentialsError = ( 217 data: ErrorInstanceData, 218) => 219 new CoopError({ 220 status: 400, 221 type: [ErrorType.InvalidUserInput], 222 title: 'This integration config creation expects no empty API credentials.', 223 name: 'IntegrationEmptyInputCredentialsError', 224 ...data, 225 });