Skip to content

Commit fc00ff9

Browse files
Merge pull request #251 from supertokens/feat/dashboard-analytics
feat: Add telemetry API to dashboard recipe
2 parents b8298eb + 45f7b15 commit fc00ff9

File tree

7 files changed

+138
-48
lines changed

7 files changed

+138
-48
lines changed

CHANGELOG.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
77

88
## [unreleased]
99

10+
## [0.10.4] - 2023-03-30
11+
12+
- Adds a telemetry API to the dashboard recipe
13+
1014
## [0.10.3] - 2023-03-29
1115
- Adds unit test for Apple callback form post
1216
- Updates all example apps to also initialise dashboard recipe
Lines changed: 122 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,122 @@
1+
package api
2+
3+
import (
4+
"bytes"
5+
"encoding/json"
6+
"github.com/supertokens/supertokens-golang/recipe/dashboard/dashboardmodels"
7+
"github.com/supertokens/supertokens-golang/supertokens"
8+
"net/http"
9+
)
10+
11+
type analyticsPostResponse struct {
12+
Status string `json:"status"`
13+
}
14+
15+
type analyticsPostRequestBody struct {
16+
Email *string `json:"email"`
17+
DashboardVersion *string `json:"dashboardVersion"`
18+
}
19+
20+
func AnalyticsPost(apiInterface dashboardmodels.APIInterface, options dashboardmodels.APIOptions) (analyticsPostResponse, error) {
21+
supertokensInstance, instanceError := supertokens.GetInstanceOrThrowError()
22+
23+
if supertokens.IsRunningInTestMode() {
24+
return analyticsPostResponse{
25+
Status: "OK",
26+
}, nil
27+
}
28+
29+
if instanceError != nil {
30+
return analyticsPostResponse{}, instanceError
31+
}
32+
33+
if supertokensInstance.Telemetry != nil && !*supertokensInstance.Telemetry {
34+
return analyticsPostResponse{
35+
Status: "OK",
36+
}, nil
37+
}
38+
39+
body, err := supertokens.ReadFromRequest(options.Req)
40+
41+
if err != nil {
42+
return analyticsPostResponse{}, err
43+
}
44+
45+
var readBody analyticsPostRequestBody
46+
err = json.Unmarshal(body, &readBody)
47+
if err != nil {
48+
return analyticsPostResponse{}, err
49+
}
50+
51+
if readBody.Email == nil {
52+
return analyticsPostResponse{}, supertokens.BadInputError{
53+
Msg: "Required parameter 'email' is missing",
54+
}
55+
}
56+
57+
if readBody.DashboardVersion == nil {
58+
return analyticsPostResponse{}, supertokens.BadInputError{
59+
Msg: "Required parameter 'dashboardVersion' is missing",
60+
}
61+
}
62+
63+
data := map[string]interface{}{
64+
"websiteDomain": supertokensInstance.AppInfo.WebsiteDomain.GetAsStringDangerous(),
65+
"apiDomain": supertokensInstance.AppInfo.APIDomain.GetAsStringDangerous(),
66+
"appName": supertokensInstance.AppInfo.AppName,
67+
"sdk": "golang",
68+
"sdkVersion": supertokens.VERSION,
69+
"email": *readBody.Email,
70+
"dashboardVersion": *readBody.DashboardVersion,
71+
}
72+
73+
querier, err := supertokens.GetNewQuerierInstanceOrThrowError("")
74+
if err != nil {
75+
return analyticsPostResponse{}, err
76+
}
77+
78+
response, err := querier.SendGetRequest("/telemetry", nil)
79+
if err != nil {
80+
// We don't send telemetry events if this fails
81+
return analyticsPostResponse{
82+
Status: "OK",
83+
}, nil
84+
}
85+
86+
exists := response["exists"].(bool)
87+
88+
if exists {
89+
data["telemetryId"] = response["telemetryId"].(string)
90+
}
91+
92+
numberOfUsers, err := supertokens.GetUserCount(nil)
93+
if err != nil {
94+
// We don't send telemetry events if this fails
95+
return analyticsPostResponse{
96+
Status: "OK",
97+
}, nil
98+
}
99+
100+
data["numberOfUsers"] = numberOfUsers
101+
102+
jsonData, err := json.Marshal(data)
103+
if err != nil {
104+
return analyticsPostResponse{}, err
105+
}
106+
107+
url := "https://api.supertokens.com/0/st/telemetry"
108+
req, err := http.NewRequest("POST", url, bytes.NewBuffer(jsonData))
109+
if err != nil {
110+
return analyticsPostResponse{
111+
Status: "OK",
112+
}, nil
113+
}
114+
req.Header.Set("content-type", "application/json; charset=utf-8")
115+
req.Header.Set("api-version", "3")
116+
client := &http.Client{}
117+
client.Do(req)
118+
119+
return analyticsPostResponse{
120+
Status: "OK",
121+
}, nil
122+
}

recipe/dashboard/constants.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,3 +12,4 @@ const userEmailVerifyTokenAPI = "/api/user/email/verify/token"
1212
const userPasswordAPI = "/api/user/password"
1313
const signInAPI = "/api/signin"
1414
const signOutAPI = "/api/signout"
15+
const dashboardAnalyticsAPI = "/api/analytics"

recipe/dashboard/recipe.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -165,6 +165,8 @@ func (r *Recipe) handleAPIRequest(id string, req *http.Request, res http.Respons
165165
return userdetails.UserPasswordPut(r.APIImpl, options)
166166
} else if id == signOutAPI {
167167
return api.SignOutPost(r.APIImpl, options)
168+
} else if id == dashboardAnalyticsAPI {
169+
return api.AnalyticsPost(r.APIImpl, options)
168170
}
169171
return nil, errors.New("should never come here")
170172
})

recipe/dashboard/utils.go

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -139,5 +139,10 @@ func getApiIdIfMatched(path supertokens.NormalisedURLPath, method string) (*stri
139139
return &val, nil
140140
}
141141

142+
if method == http.MethodPost && strings.HasSuffix(path.GetAsStringDangerous(), dashboardAnalyticsAPI) {
143+
val := dashboardAnalyticsAPI
144+
return &val, nil
145+
}
146+
142147
return nil, nil
143148
}

supertokens/constants.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,10 +21,10 @@ const (
2121
)
2222

2323
// VERSION current version of the lib
24-
const VERSION = "0.10.3"
24+
const VERSION = "0.10.4"
2525

2626
var (
2727
cdiSupported = []string{"2.8", "2.9", "2.10", "2.11", "2.12", "2.13", "2.14", "2.15", "2.16", "2.17", "2.18", "2.19"}
2828
)
2929

30-
const DashboardVersion = "0.4"
30+
const DashboardVersion = "0.5"

supertokens/supertokens.go

Lines changed: 2 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,6 @@
1616
package supertokens
1717

1818
import (
19-
"bytes"
2019
"encoding/json"
2120
"errors"
2221
"flag"
@@ -30,6 +29,7 @@ type superTokens struct {
3029
SuperTokens ConnectionInfo
3130
RecipeModules []RecipeModule
3231
OnSuperTokensAPIError func(err error, req *http.Request, res http.ResponseWriter)
32+
Telemetry *bool
3333
}
3434

3535
// this will be set to true if this is used in a test app environment
@@ -99,12 +99,9 @@ func supertokensInit(config TypeInput) error {
9999
superTokens.RecipeModules = append(superTokens.RecipeModules, *recipeModule)
100100
}
101101

102+
superTokens.Telemetry = config.Telemetry
102103
superTokensInstance = superTokens
103104

104-
if config.Telemetry == nil || *config.Telemetry {
105-
sendTelemetry()
106-
}
107-
108105
return nil
109106
}
110107

@@ -119,47 +116,6 @@ func GetInstanceOrThrowError() (*superTokens, error) {
119116
return nil, errors.New("initialisation not done. Did you forget to call the SuperTokens.init function?")
120117
}
121118

122-
func sendTelemetry() {
123-
if IsRunningInTestMode() {
124-
// if running in test mode, we do not want to send this.
125-
return
126-
}
127-
querier, err := GetNewQuerierInstanceOrThrowError("")
128-
if err != nil {
129-
return
130-
}
131-
132-
response, err := querier.SendGetRequest("/telemetry", nil)
133-
if err != nil {
134-
return
135-
}
136-
exists := response["exists"].(bool)
137-
138-
url := "https://api.supertokens.com/0/st/telemetry"
139-
140-
data := map[string]interface{}{
141-
"appName": superTokensInstance.AppInfo.AppName,
142-
"websiteDomain": superTokensInstance.AppInfo.WebsiteDomain.GetAsStringDangerous(),
143-
"sdk": "golang",
144-
}
145-
if exists {
146-
data["telemetryId"] = response["telemetryId"].(string)
147-
}
148-
jsonData, err := json.Marshal(data)
149-
if err != nil {
150-
return
151-
}
152-
req, err := http.NewRequest("POST", url, bytes.NewBuffer(jsonData))
153-
if err != nil {
154-
return
155-
}
156-
req.Header.Set("content-type", "application/json; charset=utf-8")
157-
req.Header.Set("api-version", "2")
158-
159-
client := &http.Client{}
160-
client.Do(req)
161-
}
162-
163119
func (s *superTokens) middleware(theirHandler http.Handler) http.Handler {
164120
LogDebugMessage("middleware: Started")
165121
if theirHandler == nil {

0 commit comments

Comments
 (0)