Skip to content

Commit

Permalink
Merge pull request #84 from supertokens/feat/analytics
Browse files Browse the repository at this point in the history
chore: Add analytics API call after user list loads
  • Loading branch information
rishabhpoddar authored Mar 30, 2023
2 parents d40cca2 + 90f3ffd commit a716b3d
Show file tree
Hide file tree
Showing 17 changed files with 139 additions and 13 deletions.
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

## [unreleased]

## [0.5.0] - 2023-03-29

- Adds telemetry to the dashboard

## [0.4.5] - 2023-03-10

- Fixes an issue where notifications would appear behind the sign out button
Expand Down
60 changes: 60 additions & 0 deletions api_spec.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@ tags:
description: APIs for user management features
- name: "User Details"
description: APIs for fetching and modifying information specific to one user
- name: "Telemetry"
description: APIs related to recording telemetry from the dashboard

paths:
/signin:
Expand Down Expand Up @@ -1086,6 +1088,64 @@ paths:
type: string
enum:
- Not Found
/api/analytics:
post:
tags:
- Telemetry
summary: Signals the backend SDK to send telemetry to SuperTokens
operationId: telemetryAnalyticsPost
parameters:
- name: authorization
in: header
required: true
schema:
type: string
example: "Bearer API_KEY"
requestBody:
content:
application/json:
schema:
type: object
properties:
email:
type: string
example: [email protected]
dashboardVersion:
type: string
example: 0.1.2
responses:
200:
description: Success
content:
application/json:
schema:
type: object
properties:
status:
type: string
default: "OK"
400:
description: error code 400
content:
text/plain:
schema:
type: string
401:
description: Unauthorised access
content:
text/plain:
schema:
type: string
enum:
- Unauthorised access
404:
description: error code 404
content:
text/plain:
schema:
type: string
enum:
- Not Found
servers:
# Added by API Auto Mocking Plugin
- description: SwaggerHub API Auto Mocking
Expand Down
2 changes: 1 addition & 1 deletion build/static/js/bundle.js

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion build/static/js/bundle.js.map

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "dashboard",
"version": "0.4.5",
"version": "0.5.0",
"private": true,
"dependencies": {
"@babel/core": "^7.16.0",
Expand Down
2 changes: 1 addition & 1 deletion server/go/go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ module github.com/supertokens/dashboard

go 1.17

require github.com/supertokens/supertokens-golang v0.9.9-0.20221128061618-ae87bd74f9b8
require github.com/supertokens/supertokens-golang v0.10.3-0.20230327084802-52a32af8ea1b

require (
github.com/MicahParks/keyfunc v1.0.0 // indirect
Expand Down
4 changes: 4 additions & 0 deletions server/go/go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,10 @@ github.com/supertokens/supertokens-golang v0.9.9-0.20221128055111-28526078f74e h
github.com/supertokens/supertokens-golang v0.9.9-0.20221128055111-28526078f74e/go.mod h1:GtnhBUpE3LgIWRQbGP1aWwh34aDiUPo/hRUSy4Haf+w=
github.com/supertokens/supertokens-golang v0.9.9-0.20221128061618-ae87bd74f9b8 h1:Q7SCGQ/5ry6u0ygylNK/bJOnqbV/drH+tt4b5HcY1J0=
github.com/supertokens/supertokens-golang v0.9.9-0.20221128061618-ae87bd74f9b8/go.mod h1:GtnhBUpE3LgIWRQbGP1aWwh34aDiUPo/hRUSy4Haf+w=
github.com/supertokens/supertokens-golang v0.10.3-0.20230327073759-2200049b5c1a h1:zB6e/l8MlorBA7wDwniIaGirfd3SUnbbdxEbNtH7qSw=
github.com/supertokens/supertokens-golang v0.10.3-0.20230327073759-2200049b5c1a/go.mod h1:GtnhBUpE3LgIWRQbGP1aWwh34aDiUPo/hRUSy4Haf+w=
github.com/supertokens/supertokens-golang v0.10.3-0.20230327084802-52a32af8ea1b h1:vKIXZTDOCvRytDiGlyLfwM5wuoaayI5Kn0Mdxkh7Rgc=
github.com/supertokens/supertokens-golang v0.10.3-0.20230327084802-52a32af8ea1b/go.mod h1:GtnhBUpE3LgIWRQbGP1aWwh34aDiUPo/hRUSy4Haf+w=
github.com/twilio/twilio-go v0.26.0 h1:wFW4oTe3/LKt6bvByP7eio8JsjtaLHjMQKOUEzQry7U=
github.com/twilio/twilio-go v0.26.0/go.mod h1:lz62Hopu4vicpQ056H5TJ0JE4AP0rS3sQ35/ejmgOwE=
github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
Expand Down
4 changes: 2 additions & 2 deletions server/go/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,12 +24,12 @@ func main() {
ConnectionURI: "https://try.supertokens.com",
},
AppInfo: supertokens.AppInfo{
AppName: "Dashboard Dev",
AppName: "Dashboard Dev Go",
WebsiteDomain: "localhost:3000",
APIDomain: "localhost:3001",
},
RecipeList: []supertokens.Recipe{
dashboard.Init(dashboardmodels.TypeInput{
dashboard.Init(&dashboardmodels.TypeInput{
ApiKey: "someapikey",
Override: &dashboardmodels.OverrideStruct{
Functions: func(originalImplementation dashboardmodels.RecipeInterface) dashboardmodels.RecipeInterface {
Expand Down
3 changes: 1 addition & 2 deletions server/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -37,15 +37,14 @@ SuperTokens.init({
connectionURI: "https://try.supertokens.com",
},
appInfo: {
appName: "Dashboard Dev",
appName: "Dashboard Dev Node",
apiDomain: "http://localhost:3001",
websiteDomain,
apiBasePath: "/auth",
},
recipeList: [
Dashboard.init({
// Keep this so that the dev server uses api key based login
apiKey: "someapikey",
override: {
functions: (original) => {
return {
Expand Down
1 change: 1 addition & 0 deletions src/api/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ const useAuthService = () => {
const body = await response.json();
if (body.status === "OK") {
localStorageHandler.removeItem(StorageKeys.AUTH_KEY);
localStorageHandler.removeItem(StorageKeys.EMAIL);
window.location.reload();
}
};
Expand Down
1 change: 1 addition & 0 deletions src/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@

export class StorageKeys {
static AUTH_KEY = "auth-token";
static EMAIL = "email";
}

// Add types as required
Expand Down
1 change: 1 addition & 0 deletions src/ui/components/auth/SignInContent.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@ const SignInContent: React.FC<SignInContentProps> = ({
switch (body.status) {
case "OK":
localStorageHandler.setItem(StorageKeys.AUTH_KEY, body.sessionId);
localStorageHandler.setItem(StorageKeys.EMAIL, email);
onSuccess();
break;
case "USER_LIMIT_REACHED_ERROR":
Expand Down
9 changes: 8 additions & 1 deletion src/ui/components/authWrapper/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,14 @@ export default function AuthWrapper(props: { children: any }) {

useEffect(() => {
const apiKey = localStorageHandler.getItem(StorageKeys.AUTH_KEY);
setShouldShowAuthForm(apiKey === undefined);
const _shouldShowAuthForm = apiKey === undefined;

if (_shouldShowAuthForm) {
localStorageHandler.removeItem(StorageKeys.AUTH_KEY);
localStorageHandler.removeItem(StorageKeys.EMAIL);
}

setShouldShowAuthForm(_shouldShowAuthForm);
setIsLoading(false);
}, []);

Expand Down
40 changes: 40 additions & 0 deletions src/ui/pages/usersList/UsersList.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,11 @@ import useVerifyEmailService from "../../../api/user/email/verify";
import useVerifyUserTokenService from "../../../api/user/email/verify/token";
import useFetchUsersService from "../../../api/users";
import useFetchCount from "../../../api/users/count";
import { StorageKeys } from "../../../constants";
import { localStorageHandler } from "../../../services/storage";
import { AppEnvContextProvider, useAppEnvContext } from "../../../ui/contexts/AppEnvContext";
import { getApiUrl, getAuthMode, useFetchData } from "../../../utils";
import { package_version } from "../../../version";
import { Footer, LOGO_ICON_LIGHT } from "../../components/footer/footer";
import InfoConnection from "../../components/info-connection/info-connection";
import NoUsers from "../../components/noUsers/NoUsers";
Expand Down Expand Up @@ -53,6 +57,8 @@ type UserListProps = {

type NextPaginationTokenByOffset = Record<number, string | undefined>;

let isAnalyticsFired = false;

export const UsersList: React.FC<UserListProps> = ({
onSelect,
css,
Expand All @@ -70,6 +76,7 @@ export const UsersList: React.FC<UserListProps> = ({
const { fetchUsers } = useFetchUsersService();

const { fetchCount } = useFetchCount();
const fetchData = useFetchData();

const insertUsersAtOffset = useCallback(
(paramUsers: UserWithRecipeId[], paramOffset?: number) => {
Expand Down Expand Up @@ -120,12 +127,44 @@ export const UsersList: React.FC<UserListProps> = ({
[offset, errorOffsets, limit, paginationTokenByOffset, insertUsersAtOffset, getOffsetByPaginationToken]
);

const fireAnalyticsEvent = async () => {
if (isAnalyticsFired) {
return;
}

isAnalyticsFired = true;

try {
let email: string | undefined = "[email protected]";

if (getAuthMode() === "email-password") {
email = localStorageHandler.getItem(StorageKeys.EMAIL);
}

await fetchData({
url: getApiUrl("/api/analytics"),
method: "POST",
config: {
body: JSON.stringify({
email,
dashboardVersion: package_version,
}),
},
// We dont want to trigger the error boundary if this API fails
ignoreErrors: true,
});
} catch (_) {
// ignored
}
};

const loadCount = useCallback(async () => {
setLoading(true);
const [countResult] = await Promise.all([fetchCount().catch(() => undefined), loadUsers()]);
if (countResult) {
setCount(countResult.count);
}

setLoading(false);
}, []);

Expand All @@ -138,6 +177,7 @@ export const UsersList: React.FC<UserListProps> = ({

useEffect(() => {
void loadCount();
void fireAnalyticsEvent();
}, [loadCount]);

useEffect(() => {
Expand Down
13 changes: 11 additions & 2 deletions src/utils/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -56,12 +56,20 @@ interface IFetchDataArgs {
query?: { [key: string]: string };
config?: RequestInit;
shouldRedirectOnUnauthorised?: boolean;
ignoreErrors?: boolean;
}

export const useFetchData = () => {
const [statusCode, setStatusCode] = useState<number>(0);

const fetchData = async ({ url, method, query, config, shouldRedirectOnUnauthorised = true }: IFetchDataArgs) => {
const fetchData = async ({
url,
method,
query,
config,
shouldRedirectOnUnauthorised = true,
ignoreErrors = false,
}: IFetchDataArgs) => {
const apiKeyInStorage = localStorageHandler.getItem(StorageKeys.AUTH_KEY);

let additionalHeaders: { [key: string]: string } = {};
Expand Down Expand Up @@ -90,14 +98,15 @@ export const useFetchData = () => {
window.localStorage.removeItem(StorageKeys.AUTH_KEY);
window.location.reload();
} else {
setStatusCode(response.status);
setStatusCode(ignoreErrors ? 200 : response.status);
}
return response;
};

if (statusCode < 300 || statusCode === HTTPStatusCodes.UNAUTHORIZED) {
return fetchData;
}

throw Error(`Error: ${statusCode}. Some error Occurred`);
};

Expand Down
2 changes: 1 addition & 1 deletion src/version.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,4 +13,4 @@
* under the License.
*/

export const package_version = "0.4.5";
export const package_version = "0.5.0";

0 comments on commit a716b3d

Please sign in to comment.