Skip to content

Commit 40b3125

Browse files
authored
Merge pull request #2125 from digma-ai/auth-manager-api-client
auth manager analytics provider Closes #2124
2 parents b010cd2 + 7eb0742 commit 40b3125

File tree

13 files changed

+179
-91
lines changed

13 files changed

+179
-91
lines changed

analytics-provider/src/main/java/org/digma/intellij/plugin/analytics/RestAnalyticsProvider.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -208,7 +208,7 @@ public VersionResponse getVersions(VersionRequest request) {
208208

209209
@Override
210210
public AboutResult getAbout() {
211-
return execute(() -> client.analyticsProvider.getAbout());
211+
return execute(client.analyticsProvider::getAbout);
212212
}
213213

214214
@Override

ide-common/src/main/java/org/digma/intellij/plugin/analytics/AnalyticsService.java

Lines changed: 6 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,7 @@
5353
import static org.digma.intellij.plugin.analytics.EnvUtilsKt.getAllEnvironmentsNames;
5454
import static org.digma.intellij.plugin.analytics.EnvironmentRefreshSchedulerKt.scheduleEnvironmentRefresh;
5555
import static org.digma.intellij.plugin.common.ExceptionUtils.*;
56+
import static org.digma.intellij.plugin.log.Log.API_LOGGER_NAME;
5657

5758

5859
public class AnalyticsService implements Disposable {
@@ -62,7 +63,7 @@ public class AnalyticsService implements Disposable {
6263
private static final Logger LOGGER = Logger.getInstance(AnalyticsService.class);
6364

6465
private final Environment environment;
65-
private String myApiUrl;
66+
6667
private final Project project;
6768

6869
/**
@@ -85,35 +86,10 @@ public AnalyticsService(@NotNull Project project) {
8586
BackendConnectionMonitor.getInstance(project);
8687
//initialize MainToolWindowCardsController when starting, so it is aware early on connection statuses
8788
MainToolWindowCardsController.getInstance(project);
88-
SettingsState settingsState = SettingsState.getInstance();
8989
environment = new Environment(project, this);
9090
this.project = project;
91-
myApiUrl = settingsState.apiUrl;
92-
replaceClient(myApiUrl);
91+
replaceClient(SettingsState.getInstance().apiUrl);
9392
scheduleEnvironmentRefresh(this, environment);
94-
95-
settingsState.addChangeListener(state -> {
96-
97-
Log.log(LOGGER::debug, "settings changed event");
98-
99-
boolean shouldReplaceClient = false;
100-
101-
if (!Objects.equals(state.apiUrl, myApiUrl)) {
102-
myApiUrl = state.apiUrl;
103-
shouldReplaceClient = true;
104-
}
105-
106-
//replace the client only when apiUrl is changed.
107-
//there is no need top replace the client when api token is changed because there is an
108-
// AuthenticationProvider that always takes it from the settings
109-
if (shouldReplaceClient) {
110-
Log.log(LOGGER::debug, "api url changed to {}, calling replace client", myApiUrl);
111-
AuthManager.getInstance().logout();
112-
AuthManager.getInstance().pauseBeforeClientChange();
113-
replaceClient(myApiUrl);
114-
}
115-
116-
}, this);
11793
}
11894

11995

@@ -128,7 +104,8 @@ public Environment getEnvironment() {
128104

129105
//just replace the client and do not fire any events
130106
//this method should be synchronized, and it shouldn't be a problem, it doesn't happen too often.
131-
private synchronized void replaceClient(String url) {
107+
//package private, should not be called other than from AnalyticsServiceSettingsWatcher
108+
synchronized void replaceClient(String url) {
132109

133110
Log.log(LOGGER::debug, "replacing AnalyticsProvider for url {}", url);
134111

@@ -144,7 +121,7 @@ private synchronized void replaceClient(String url) {
144121
AnalyticsProvider analyticsProvider =
145122
AuthManager.getInstance().withAuth(new RestAnalyticsProvider(url, AuthManager.getInstance().getAuthenticationProviders(),
146123
message -> {
147-
var apiLogger = Logger.getInstance("api.digma.org");
124+
var apiLogger = Logger.getInstance(API_LOGGER_NAME);
148125
Log.log(apiLogger::debug, "API: {}", message);
149126
}));
150127
Log.log(LOGGER::debug, "AuthManager.withAuth successfully wrapped AnalyticsProvider for url {}", url);
Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
package org.digma.intellij.plugin.analytics;
2+
3+
import com.intellij.openapi.Disposable;
4+
import com.intellij.openapi.application.ApplicationManager;
5+
import com.intellij.openapi.components.Service;
6+
import com.intellij.openapi.diagnostic.Logger;
7+
import com.intellij.openapi.project.*;
8+
import org.digma.intellij.plugin.auth.AuthManager;
9+
import org.digma.intellij.plugin.common.ProjectUtilsKt;
10+
import org.digma.intellij.plugin.log.Log;
11+
import org.digma.intellij.plugin.settings.SettingsState;
12+
13+
import java.util.Objects;
14+
15+
16+
/**
17+
* catch settingsChanged events for apiUrl and instructs all AnalyticsService for all
18+
* open projects to replace the api client.
19+
* it is an application service , the reason is that it needs to instruct AuthManager to logout
20+
* and to replace its client, AuthManager is also an application service, if this listener was a
21+
* project service the AuthManager would replace its client for every open project which is unnecessary.
22+
*/
23+
@Service(Service.Level.APP)
24+
public final class AnalyticsServiceSettingsWatcher implements Disposable {
25+
26+
private static final Logger LOGGER = Logger.getInstance(AnalyticsService.class);
27+
28+
private String myApiUrl;
29+
30+
public AnalyticsServiceSettingsWatcher() {
31+
myApiUrl = SettingsState.getInstance().apiUrl;
32+
33+
SettingsState.getInstance().addChangeListener(state -> {
34+
35+
Log.log(LOGGER::debug, "settings changed event");
36+
37+
boolean shouldReplaceClient = false;
38+
39+
if (!Objects.equals(state.apiUrl, myApiUrl)) {
40+
myApiUrl = state.apiUrl;
41+
shouldReplaceClient = true;
42+
}
43+
44+
//replace the client only when apiUrl is changed.
45+
//there is no need to replace the client when api token is changed because there is an
46+
// AuthenticationProvider that always takes it from the settings
47+
if (shouldReplaceClient) {
48+
Log.log(LOGGER::debug, "api url changed to {}, calling replace client", myApiUrl);
49+
AuthManager.getInstance().logout();
50+
AuthManager.getInstance().pauseBeforeClientChange();
51+
AuthManager.getInstance().replaceClient(myApiUrl);
52+
53+
for (Project openProject : ProjectManager.getInstance().getOpenProjects()) {
54+
if (ProjectUtilsKt.isProjectValid(openProject)) {
55+
var analyticsService = AnalyticsService.getInstance(openProject);
56+
analyticsService.replaceClient(myApiUrl);
57+
}
58+
}
59+
}
60+
61+
}, this);
62+
63+
64+
}
65+
66+
public static AnalyticsServiceSettingsWatcher getInstance() {
67+
return ApplicationManager.getApplication().getService(AnalyticsServiceSettingsWatcher.class);
68+
}
69+
70+
@Override
71+
public void dispose() {
72+
//nothing to do , used as parent disposable
73+
}
74+
}

ide-common/src/main/java/org/digma/intellij/plugin/analytics/AnalyticsServiceStarter.java

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,9 @@ public class AnalyticsServiceStarter implements StartupActivity.DumbAware {
1313
@Override
1414
public void runActivity(@NotNull Project project) {
1515
Backgroundable.ensureBackgroundWithoutReadAccess(project, "initializing analytics service", () -> {
16+
//start AnalyticsServiceSettingsWatcher so it will start listening to settings change events
17+
AnalyticsServiceSettingsWatcher.getInstance();
18+
1619
AnalyticsService.getInstance(project);
1720
//make sure BackendInfoHolder is initialized after AnalyticsService
1821
BackendInfoHolder.getInstance();

ide-common/src/main/java/org/digma/intellij/plugin/log/Log.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ public class Log {
1515

1616
public static final String DIGMA = "Digma: ";
1717
public static final String DIGMA_PROJECT = "Digma: Project:";
18+
public static final String API_LOGGER_NAME = "api.digma.org";
1819

1920

2021
private static final FrequentErrorDetector FREQUENT_ERROR_DETECTOR = new FrequentErrorDetector(Duration.ofMinutes(10));

ide-common/src/main/java/org/digma/intellij/plugin/settings/SettingsComponent.java

Lines changed: 4 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,6 @@
88

99
import javax.swing.*;
1010
import java.awt.*;
11-
import java.awt.event.*;
1211
import java.net.*;
1312
import java.util.Objects;
1413

@@ -145,7 +144,7 @@ public boolean verify(JComponent input) {
145144
var resetButton = new JButton("Reset to defaults");
146145
resetButton.addActionListener(e -> resetToDefaults());
147146

148-
var builder = FormBuilder.createFormBuilder()
147+
myMainPanel = FormBuilder.createFormBuilder()
149148
.addLabeledComponent(myUrlLabel, myApiUrlText, 1, false)
150149
.addLabeledComponent(new JBLabel("Api token:"), myApiToken, 1, false)
151150
.addLabeledComponent(myRefreshLabel, myRefreshDelay, 1, false)
@@ -157,29 +156,9 @@ public boolean verify(JComponent input) {
157156
.addLabeledComponent(myRuntimeObservabilityBackendUrlLabel, myRuntimeObservabilityBackendUrlText, 1, false)
158157
.addComponentToRightColumn(feedbackForRuntimeObservabilityBackendUrl, 1)
159158
.addLabeledComponent("Extended Observability (beta)", extendedObservabilityTextBox, 1, false)
160-
.addComponent(resetButton);
161-
//.addComponentFillVertically(new JPanel(), 0)
162-
163-
if ("digma".equals(System.getenv("devenv"))) {
164-
builder.addComponent(getSwitchToCentralizedButton());
165-
166-
}
167-
168-
builder.addComponentFillVertically(new JPanel(), 0);
169-
myMainPanel = builder.getPanel();
170-
}
171-
172-
private JComponent getSwitchToCentralizedButton() {
173-
var switchButton = new JButton("Switch To Centralized");
174-
switchButton.addActionListener(new ActionListener() {
175-
@Override
176-
public void actionPerformed(ActionEvent e) {
177-
myApiUrlText.setText("https://k8s-test-testanal-8fb5588e77-656549b60155bd92.elb.eu-west-1.amazonaws.com:5051");
178-
myJaegerQueryUrlText.setText("http://k8s-test-testembe-bc3a31f355-363f3f02cca5c825.elb.eu-west-1.amazonaws.com:17686");
179-
myRuntimeObservabilityBackendUrlText.setText("http://k8s-test-testcoll-9cc2840664-5a79b938206dc6d6.elb.eu-west-1.amazonaws.com:5050");
180-
}
181-
});
182-
return switchButton;
159+
.addComponent(resetButton)
160+
.addComponentFillVertically(new JPanel(), 0)
161+
.getPanel();
183162
}
184163

185164

ide-common/src/main/kotlin/org/digma/intellij/plugin/analytics/ApiClientChangedEvent.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ import com.intellij.util.messages.Topic
77
* this is an application event that should fire when we change the api client,
88
* usually when user changes the api url in settings.
99
*/
10-
interface ApiClientChangedEvent {
10+
fun interface ApiClientChangedEvent {
1111
companion object {
1212
@JvmStatic
1313
@Topic.AppLevel

ide-common/src/main/kotlin/org/digma/intellij/plugin/auth/AuthManager.kt

Lines changed: 36 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,8 @@ import org.digma.intellij.plugin.auth.account.DigmaDefaultAccountHolder
1414
import org.digma.intellij.plugin.common.ExceptionUtils
1515
import org.digma.intellij.plugin.errorreporting.ErrorReporter
1616
import org.digma.intellij.plugin.log.Log
17+
import org.digma.intellij.plugin.log.Log.API_LOGGER_NAME
18+
import org.digma.intellij.plugin.settings.SettingsState
1719
import java.io.Closeable
1820
import java.lang.reflect.InvocationHandler
1921
import java.lang.reflect.InvocationTargetException
@@ -28,7 +30,7 @@ class AuthManager : Disposable {
2830
private val logger: Logger = Logger.getInstance(AuthManager::class.java)
2931

3032
private val listeners: MutableList<AuthInfoChangeListener> = ArrayList()
31-
private var analyticsProvider: RestAnalyticsProvider? = null
33+
private var myAnalyticsProvider: RestAnalyticsProvider = createMyAnalyticsProvider(SettingsState.getInstance().apiUrl)
3234

3335
private val isPaused: AtomicBoolean = AtomicBoolean(true)
3436

@@ -39,8 +41,25 @@ class AuthManager : Disposable {
3941
fun getInstance(): AuthManager {
4042
return service<AuthManager>()
4143
}
44+
45+
private fun createMyAnalyticsProvider(url: String): RestAnalyticsProvider {
46+
return RestAnalyticsProvider(url, listOf(DigmaAccessTokenAuthenticationProvider(SettingsTokenProvider())))
47+
{ message: String? ->
48+
val apiLogger = Logger.getInstance(API_LOGGER_NAME)
49+
Log.log(apiLogger::debug, "API:AuthManager: {}", message)
50+
}
51+
}
52+
53+
}
54+
55+
@Synchronized
56+
fun replaceClient(url: String) {
57+
Log.log(logger::info, "replacing myAnalyticsProvider to url {}", url)
58+
this.myAnalyticsProvider = createMyAnalyticsProvider(url)
4259
}
4360

61+
62+
4463
override fun dispose() {
4564
//nothing to do, used as parent disposable
4665
}
@@ -73,15 +92,10 @@ class AuthManager : Disposable {
7392

7493
Log.log(logger::info, "wrapping analyticsProvider with auth for url {}", analyticsProvider.apiUrl)
7594

76-
//keep the RestAnalyticsProvider every time withAuth is called, so it will always be the correct and latest one
77-
Log.log(logger::info, "setting new analyticsProvider url {}", analyticsProvider.apiUrl)
78-
this.analyticsProvider = analyticsProvider
79-
8095
val loginHandler = LoginHandler.createLoginHandler(analyticsProvider)
8196

82-
8397
if (!loginHandler.loginOrRefresh()) {
84-
Log.log(logger::warn, "loginOrRefresh failed for url {}", this.analyticsProvider?.apiUrl)
98+
Log.log(logger::warn, "loginOrRefresh failed for url {}", this.myAnalyticsProvider.apiUrl)
8599
}
86100

87101
//always return a proxy. even if login failed. the proxy will try to loginOrRefresh on AuthenticationException
@@ -93,7 +107,7 @@ class AuthManager : Disposable {
93107
analyticsProvider.apiUrl
94108
)
95109

96-
Log.log(logger::info, "resuming current proxy, analytics url {}", this.analyticsProvider?.apiUrl)
110+
Log.log(logger::info, "resuming current proxy, analytics url {}", this.myAnalyticsProvider.apiUrl)
97111
isPaused.set(false)
98112

99113
fireChange()
@@ -105,9 +119,9 @@ class AuthManager : Disposable {
105119
@Synchronized
106120
fun login(user: String, password: String): LoginResult {
107121

108-
Log.log(logger::info, "login called, analytics url {}", analyticsProvider?.apiUrl)
122+
Log.log(logger::info, "login called, analytics url {}", myAnalyticsProvider.apiUrl)
109123

110-
val loginHandler = LoginHandler.createLoginHandler(analyticsProvider)
124+
val loginHandler = LoginHandler.createLoginHandler(myAnalyticsProvider)
111125
val loginResult = loginHandler.login(user, password)
112126

113127
Log.log(logger::info, "login result {}", loginResult)
@@ -120,9 +134,9 @@ class AuthManager : Disposable {
120134
@Synchronized
121135
fun logout() {
122136

123-
Log.log(logger::info, "logout called, analytics url {}", analyticsProvider?.apiUrl)
137+
Log.log(logger::info, "logout called, analytics url {}", myAnalyticsProvider.apiUrl)
124138

125-
val loginHandler = LoginHandler.createLoginHandler(analyticsProvider)
139+
val loginHandler = LoginHandler.createLoginHandler(myAnalyticsProvider)
126140
loginHandler.logout()
127141

128142
fireChange()
@@ -132,9 +146,9 @@ class AuthManager : Disposable {
132146
@Synchronized
133147
private fun onAuthenticationException() {
134148

135-
Log.log(logger::trace, "onAuthenticationException called, analytics url {}", analyticsProvider?.apiUrl)
149+
Log.log(logger::trace, "onAuthenticationException called, analytics url {}", myAnalyticsProvider.apiUrl)
136150

137-
val loginHandler = LoginHandler.createLoginHandler(analyticsProvider)
151+
val loginHandler = LoginHandler.createLoginHandler(myAnalyticsProvider)
138152
loginHandler.loginOrRefresh(true)
139153

140154
fireChange()
@@ -157,12 +171,18 @@ class AuthManager : Disposable {
157171
Log.log(
158172
logger::trace, "firing authInfoChanged, default account {}, analytics url {}",
159173
DigmaDefaultAccountHolder.getInstance().account,
160-
analyticsProvider?.apiUrl
174+
myAnalyticsProvider.apiUrl
161175
)
162176

163177
val authInfo = AuthInfo(DigmaDefaultAccountHolder.getInstance().account?.userId)
164178
listeners.forEach {
165179
try {
180+
Log.log(
181+
logger::trace, "firing authInfoChanged to listener {}, default account {}, analytics url {}",
182+
it,
183+
DigmaDefaultAccountHolder.getInstance().account,
184+
myAnalyticsProvider.apiUrl
185+
)
166186
it.authInfoChanged(authInfo)
167187
} catch (e: Throwable) {
168188
ErrorReporter.getInstance().reportError("AuthManager.fireChange", e)
@@ -175,8 +195,7 @@ class AuthManager : Disposable {
175195
//pause the AuthManager before replacing the analytics provider.
176196
//can be resumed only from this class after a new client is set
177197
fun pauseBeforeClientChange() {
178-
Log.log(logger::info, "pausing current proxy, analytics url {}", analyticsProvider?.apiUrl)
179-
analyticsProvider = null
198+
Log.log(logger::info, "pausing current proxy, analytics url {}", myAnalyticsProvider.apiUrl)
180199
isPaused.set(true)
181200
}
182201

0 commit comments

Comments
 (0)