@@ -7,6 +7,7 @@ import { NotificationContext } from "context/notification";
77import { AppContext } from "context/app" ;
88import configAPI from "services/entities/config" ;
99import paths from "router/paths" ;
10+ import { UNCHANGED_PASSWORD_API_RESPONSE } from "utilities/constants" ;
1011
1112// @ts -ignore
1213import InputField from "components/forms/fields/InputField" ;
@@ -46,6 +47,17 @@ const API_KEY_JSON_PLACEHOLDER = `{
4647 "universe_domain": "googleapis.com"
4748}` ;
4849
50+ // Check if the API key JSON object contains obfuscated values
51+ const isObfuscatedApiKey = ( apiKeyJson : Record < string , string > ) : boolean => {
52+ if ( ! apiKeyJson || Object . keys ( apiKeyJson ) . length === 0 ) {
53+ return false ;
54+ }
55+ // If all values are "********", the API key is obfuscated
56+ return Object . values ( apiKeyJson ) . every (
57+ ( value ) => value === UNCHANGED_PASSWORD_API_RESPONSE
58+ ) ;
59+ } ;
60+
4961interface ICalendarsFormErrors {
5062 domain ?: string | null ;
5163 apiKeyJson ?: string | null ;
@@ -87,16 +99,27 @@ const Calendars = (): JSX.Element => {
8799 } = useQuery < IConfig , Error , IConfig > ( [ "config" ] , ( ) => configAPI . loadAll ( ) , {
88100 select : ( data : IConfig ) => data ,
89101 onSuccess : ( data ) => {
90- if ( data . integrations . google_calendar ) {
91- setFormData ( {
92- domain : data . integrations . google_calendar [ 0 ] . domain ,
93- // Formats string for better UI readability
94- apiKeyJson : JSON . stringify (
95- data . integrations . google_calendar [ 0 ] . api_key_json ,
96- null ,
97- "\t"
98- ) ,
99- } ) ;
102+ if (
103+ Array . isArray ( data . integrations . google_calendar ) &&
104+ data . integrations . google_calendar . length > 0
105+ ) {
106+ const apiKeyJsonObj = data . integrations . google_calendar [ 0 ] . api_key_json ;
107+
108+ // Check if the API key is obfuscated
109+ if ( isObfuscatedApiKey ( apiKeyJsonObj ) ) {
110+ // Show masked value in UI
111+ setFormData ( {
112+ domain : data . integrations . google_calendar [ 0 ] . domain ,
113+ apiKeyJson : UNCHANGED_PASSWORD_API_RESPONSE ,
114+ } ) ;
115+ } else {
116+ // Show the actual API key JSON
117+ setFormData ( {
118+ domain : data . integrations . google_calendar [ 0 ] . domain ,
119+ // Formats string for better UI readability
120+ apiKeyJson : JSON . stringify ( apiKeyJsonObj , null , "\t" ) ,
121+ } ) ;
122+ }
100123 }
101124 } ,
102125 } ) ;
@@ -115,7 +138,11 @@ const Calendars = (): JSX.Element => {
115138 if ( ! curFormData . domain && ! ! curFormData . apiKeyJson ) {
116139 errors . domain = "Domain must be completed" ;
117140 }
118- if ( curFormData . apiKeyJson ) {
141+ // Skip JSON validation if the value is the masked placeholder
142+ if (
143+ curFormData . apiKeyJson &&
144+ curFormData . apiKeyJson !== UNCHANGED_PASSWORD_API_RESPONSE
145+ ) {
119146 try {
120147 JSON . parse ( curFormData . apiKeyJson ) ;
121148 } catch ( e : unknown ) {
@@ -150,16 +177,33 @@ const Calendars = (): JSX.Element => {
150177
151178 evt . preventDefault ( ) ;
152179
180+ // Determine the API key to submit
181+ let apiKeyToSubmit ;
182+ if ( formData . apiKeyJson === UNCHANGED_PASSWORD_API_RESPONSE ) {
183+ // User didn't change the masked value, don't send it (backend will preserve existing)
184+ apiKeyToSubmit = undefined ;
185+ } else if (
186+ formData . apiKeyJson &&
187+ formData . apiKeyJson !== UNCHANGED_PASSWORD_API_RESPONSE
188+ ) {
189+ // User provided a new API key
190+ apiKeyToSubmit = JSON . parse ( formData . apiKeyJson ) ;
191+ } else {
192+ // No API key
193+ apiKeyToSubmit = null ;
194+ }
195+
153196 // Format for API
154197 const formDataToSubmit =
155- formData . apiKeyJson === "" && formData . domain === ""
198+ apiKeyToSubmit === null && formData . domain === ""
156199 ? [ ] // Send empty array if no keys are set
157200 : [
158201 {
159202 domain : formData . domain ,
160- api_key_json :
161- ( formData . apiKeyJson && JSON . parse ( formData . apiKeyJson ) ) ||
162- null ,
203+ // Only include api_key_json if it's not undefined (masked value not changed)
204+ ...( apiKeyToSubmit !== undefined && {
205+ api_key_json : apiKeyToSubmit ,
206+ } ) ,
163207 } ,
164208 ] ;
165209
@@ -282,6 +326,11 @@ const Calendars = (): JSX.Element => {
282326 inputClassName = { `${ baseClass } __api-key-json` }
283327 error = { formErrors . apiKeyJson }
284328 disabled = { gomEnabled }
329+ helpText = {
330+ apiKeyJson === UNCHANGED_PASSWORD_API_RESPONSE
331+ ? "API key is configured. Replace with a new key to update."
332+ : undefined
333+ }
285334 />
286335 < InputField
287336 label = "Primary domain"
0 commit comments