Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: exposing sessions to the api #4352

Draft
wants to merge 6 commits into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 19 additions & 0 deletions apps/consent/app/graphql/generated.ts
Original file line number Diff line number Diff line change
Expand Up @@ -449,6 +449,14 @@ export type CurrencyConversionEstimation = {
readonly usdCentAmount: Scalars['CentAmount']['output'];
};

export type Delegation = {
readonly __typename: 'Delegation';
readonly app: Scalars['String']['output'];
readonly handledAt: Scalars['Timestamp']['output'];
readonly remember: Scalars['Boolean']['output'];
readonly scope: ReadonlyArray<Scalars['String']['output']>;
};

export type DepositFeesInformation = {
readonly __typename: 'DepositFeesInformation';
readonly minBankFee: Scalars['String']['output'];
Expand Down Expand Up @@ -877,6 +885,13 @@ export type MerchantPayload = {
readonly merchant?: Maybe<Merchant>;
};

export type MobileSession = {
readonly __typename: 'MobileSession';
readonly expiresAt: Scalars['Timestamp']['output'];
readonly id: Scalars['ID']['output'];
readonly issuedAt: Scalars['Timestamp']['output'];
};

export type MobileVersions = {
readonly __typename: 'MobileVersions';
readonly currentSupported: Scalars['Int']['output'];
Expand Down Expand Up @@ -1914,6 +1929,8 @@ export type User = {
readonly contacts: ReadonlyArray<UserContact>;
readonly createdAt: Scalars['Timestamp']['output'];
readonly defaultAccount: Account;
/** List of Oauth2 delegations */
readonly delegations: ReadonlyArray<Delegation>;
/** Email address */
readonly email?: Maybe<Email>;
readonly id: Scalars['ID']['output'];
Expand All @@ -1922,6 +1939,8 @@ export type User = {
* When value is 'default' the intent is to use preferred language from OS settings.
*/
readonly language: Scalars['Language']['output'];
/** List of mobile sessions */
readonly mobileSessions: ReadonlyArray<MobileSession>;
/** Phone number with international calling code. */
readonly phone?: Maybe<Scalars['Phone']['output']>;
readonly supportChat: ReadonlyArray<SupportMessage>;
Expand Down
16 changes: 0 additions & 16 deletions apps/dashboard/cypress/e2e/consent-integration.cy.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,22 +32,6 @@ describe("Consent integration Test", () => {
cy.get("[data-testid=verification_code_input]").should("not.be.disabled")
cy.get("[data-testid=verification_code_input]").type(code)

cy.contains("label", "read").should("exist")
cy.contains("label", "read").should("be.visible")
cy.contains("label", "read").should("not.be.disabled")
cy.contains("label", "read").should("not.be.disabled")
cy.contains("label", "read").click()

cy.contains("label", "write").should("exist")
cy.contains("label", "write").should("be.visible")
cy.contains("label", "write").should("not.be.disabled")
cy.contains("label", "write").click()

cy.get("[data-testid=submit_consent_btn]").should("exist")
cy.get("[data-testid=submit_consent_btn]").should("be.visible")
cy.get("[data-testid=submit_consent_btn]").should("not.be.disabled")
cy.get("[data-testid=submit_consent_btn]").click()

Comment on lines -35 to -50
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why this was removed? not related to the PR

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

cy.url().should("eq", Cypress.config().baseUrl + "/")
cy.getCookie("next-auth.session-token").then((cookie) => {
if (cookie && cookie.value) {
Expand Down
42 changes: 42 additions & 0 deletions apps/dashboard/services/graphql/generated.ts
Original file line number Diff line number Diff line change
Expand Up @@ -489,6 +489,14 @@ export type CurrencyConversionEstimation = {
readonly usdCentAmount: Scalars['CentAmount']['output'];
};

export type Delegation = {
readonly __typename: 'Delegation';
readonly app: Scalars['String']['output'];
readonly handledAt: Scalars['Timestamp']['output'];
readonly remember: Scalars['Boolean']['output'];
readonly scope: ReadonlyArray<Scalars['String']['output']>;
};

export type DepositFeesInformation = {
readonly __typename: 'DepositFeesInformation';
readonly minBankFee: Scalars['String']['output'];
Expand Down Expand Up @@ -917,6 +925,13 @@ export type MerchantPayload = {
readonly merchant?: Maybe<Merchant>;
};

export type MobileSession = {
readonly __typename: 'MobileSession';
readonly expiresAt: Scalars['Timestamp']['output'];
readonly id: Scalars['ID']['output'];
readonly issuedAt: Scalars['Timestamp']['output'];
};

export type MobileVersions = {
readonly __typename: 'MobileVersions';
readonly currentSupported: Scalars['Int']['output'];
Expand Down Expand Up @@ -2011,6 +2026,8 @@ export type User = {
readonly contacts: ReadonlyArray<UserContact>;
readonly createdAt: Scalars['Timestamp']['output'];
readonly defaultAccount: Account;
/** List of Oauth2 delegations */
readonly delegations: ReadonlyArray<Delegation>;
/** Email address */
readonly email?: Maybe<Email>;
readonly id: Scalars['ID']['output'];
Expand All @@ -2019,6 +2036,8 @@ export type User = {
* When value is 'default' the intent is to use preferred language from OS settings.
*/
readonly language: Scalars['Language']['output'];
/** List of mobile sessions */
readonly mobileSessions: ReadonlyArray<MobileSession>;
/** Phone number with international calling code. */
readonly phone?: Maybe<Scalars['Phone']['output']>;
readonly statefulNotifications: StatefulNotificationConnection;
Expand Down Expand Up @@ -3462,6 +3481,7 @@ export type ResolversTypes = {
CountryCode: ResolverTypeWrapper<Scalars['CountryCode']['output']>;
Currency: ResolverTypeWrapper<Currency>;
CurrencyConversionEstimation: ResolverTypeWrapper<CurrencyConversionEstimation>;
Delegation: ResolverTypeWrapper<Delegation>;
DepositFeesInformation: ResolverTypeWrapper<DepositFeesInformation>;
DeviceNotificationTokenCreateInput: DeviceNotificationTokenCreateInput;
DisplayCurrency: ResolverTypeWrapper<Scalars['DisplayCurrency']['output']>;
Expand Down Expand Up @@ -3527,6 +3547,7 @@ export type ResolversTypes = {
MerchantMapSuggestInput: MerchantMapSuggestInput;
MerchantPayload: ResolverTypeWrapper<MerchantPayload>;
Minutes: ResolverTypeWrapper<Scalars['Minutes']['output']>;
MobileSession: ResolverTypeWrapper<MobileSession>;
MobileVersions: ResolverTypeWrapper<MobileVersions>;
Mutation: ResolverTypeWrapper<{}>;
MyUpdatesPayload: ResolverTypeWrapper<Omit<MyUpdatesPayload, 'update'> & { update?: Maybe<ResolversTypes['UserUpdate']> }>;
Expand Down Expand Up @@ -3689,6 +3710,7 @@ export type ResolversParentTypes = {
CountryCode: Scalars['CountryCode']['output'];
Currency: Currency;
CurrencyConversionEstimation: CurrencyConversionEstimation;
Delegation: Delegation;
DepositFeesInformation: DepositFeesInformation;
DeviceNotificationTokenCreateInput: DeviceNotificationTokenCreateInput;
DisplayCurrency: Scalars['DisplayCurrency']['output'];
Expand Down Expand Up @@ -3752,6 +3774,7 @@ export type ResolversParentTypes = {
MerchantMapSuggestInput: MerchantMapSuggestInput;
MerchantPayload: MerchantPayload;
Minutes: Scalars['Minutes']['output'];
MobileSession: MobileSession;
MobileVersions: MobileVersions;
Mutation: {};
MyUpdatesPayload: Omit<MyUpdatesPayload, 'update'> & { update?: Maybe<ResolversParentTypes['UserUpdate']> };
Expand Down Expand Up @@ -4128,6 +4151,14 @@ export type CurrencyConversionEstimationResolvers<ContextType = any, ParentType
__isTypeOf?: IsTypeOfResolverFn<ParentType, ContextType>;
};

export type DelegationResolvers<ContextType = any, ParentType extends ResolversParentTypes['Delegation'] = ResolversParentTypes['Delegation']> = {
app?: Resolver<ResolversTypes['String'], ParentType, ContextType>;
handledAt?: Resolver<ResolversTypes['Timestamp'], ParentType, ContextType>;
remember?: Resolver<ResolversTypes['Boolean'], ParentType, ContextType>;
scope?: Resolver<ReadonlyArray<ResolversTypes['String']>, ParentType, ContextType>;
__isTypeOf?: IsTypeOfResolverFn<ParentType, ContextType>;
};

export type DepositFeesInformationResolvers<ContextType = any, ParentType extends ResolversParentTypes['DepositFeesInformation'] = ResolversParentTypes['DepositFeesInformation']> = {
minBankFee?: Resolver<ResolversTypes['String'], ParentType, ContextType>;
minBankFeeThreshold?: Resolver<ResolversTypes['String'], ParentType, ContextType>;
Expand Down Expand Up @@ -4360,6 +4391,13 @@ export interface MinutesScalarConfig extends GraphQLScalarTypeConfig<ResolversTy
name: 'Minutes';
}

export type MobileSessionResolvers<ContextType = any, ParentType extends ResolversParentTypes['MobileSession'] = ResolversParentTypes['MobileSession']> = {
expiresAt?: Resolver<ResolversTypes['Timestamp'], ParentType, ContextType>;
id?: Resolver<ResolversTypes['ID'], ParentType, ContextType>;
issuedAt?: Resolver<ResolversTypes['Timestamp'], ParentType, ContextType>;
__isTypeOf?: IsTypeOfResolverFn<ParentType, ContextType>;
};

export type MobileVersionsResolvers<ContextType = any, ParentType extends ResolversParentTypes['MobileVersions'] = ResolversParentTypes['MobileVersions']> = {
currentSupported?: Resolver<ResolversTypes['Int'], ParentType, ContextType>;
minSupported?: Resolver<ResolversTypes['Int'], ParentType, ContextType>;
Expand Down Expand Up @@ -4817,9 +4855,11 @@ export type UserResolvers<ContextType = any, ParentType extends ResolversParentT
contacts?: Resolver<ReadonlyArray<ResolversTypes['UserContact']>, ParentType, ContextType>;
createdAt?: Resolver<ResolversTypes['Timestamp'], ParentType, ContextType>;
defaultAccount?: Resolver<ResolversTypes['Account'], ParentType, ContextType>;
delegations?: Resolver<ReadonlyArray<ResolversTypes['Delegation']>, ParentType, ContextType>;
email?: Resolver<Maybe<ResolversTypes['Email']>, ParentType, ContextType>;
id?: Resolver<ResolversTypes['ID'], ParentType, ContextType>;
language?: Resolver<ResolversTypes['Language'], ParentType, ContextType>;
mobileSessions?: Resolver<ReadonlyArray<ResolversTypes['MobileSession']>, ParentType, ContextType>;
phone?: Resolver<Maybe<ResolversTypes['Phone']>, ParentType, ContextType>;
statefulNotifications?: Resolver<ResolversTypes['StatefulNotificationConnection'], ParentType, ContextType, RequireFields<UserStatefulNotificationsArgs, 'first'>>;
supportChat?: Resolver<ReadonlyArray<ResolversTypes['SupportMessage']>, ParentType, ContextType>;
Expand Down Expand Up @@ -4973,6 +5013,7 @@ export type Resolvers<ContextType = any> = {
CountryCode?: GraphQLScalarType;
Currency?: CurrencyResolvers<ContextType>;
CurrencyConversionEstimation?: CurrencyConversionEstimationResolvers<ContextType>;
Delegation?: DelegationResolvers<ContextType>;
DepositFeesInformation?: DepositFeesInformationResolvers<ContextType>;
DisplayCurrency?: GraphQLScalarType;
Email?: EmailResolvers<ContextType>;
Expand Down Expand Up @@ -5011,6 +5052,7 @@ export type Resolvers<ContextType = any> = {
Merchant?: MerchantResolvers<ContextType>;
MerchantPayload?: MerchantPayloadResolvers<ContextType>;
Minutes?: GraphQLScalarType;
MobileSession?: MobileSessionResolvers<ContextType>;
MobileVersions?: MobileVersionsResolvers<ContextType>;
Mutation?: MutationResolvers<ContextType>;
MyUpdatesPayload?: MyUpdatesPayloadResolvers<ContextType>;
Expand Down
19 changes: 19 additions & 0 deletions apps/map/services/galoy/graphql/generated.ts
Original file line number Diff line number Diff line change
Expand Up @@ -449,6 +449,14 @@ export type CurrencyConversionEstimation = {
readonly usdCentAmount: Scalars['CentAmount']['output'];
};

export type Delegation = {
readonly __typename: 'Delegation';
readonly app: Scalars['String']['output'];
readonly handledAt: Scalars['Timestamp']['output'];
readonly remember: Scalars['Boolean']['output'];
readonly scope: ReadonlyArray<Scalars['String']['output']>;
};

export type DepositFeesInformation = {
readonly __typename: 'DepositFeesInformation';
readonly minBankFee: Scalars['String']['output'];
Expand Down Expand Up @@ -877,6 +885,13 @@ export type MerchantPayload = {
readonly merchant?: Maybe<Merchant>;
};

export type MobileSession = {
readonly __typename: 'MobileSession';
readonly expiresAt: Scalars['Timestamp']['output'];
readonly id: Scalars['ID']['output'];
readonly issuedAt: Scalars['Timestamp']['output'];
};

export type MobileVersions = {
readonly __typename: 'MobileVersions';
readonly currentSupported: Scalars['Int']['output'];
Expand Down Expand Up @@ -1914,6 +1929,8 @@ export type User = {
readonly contacts: ReadonlyArray<UserContact>;
readonly createdAt: Scalars['Timestamp']['output'];
readonly defaultAccount: Account;
/** List of Oauth2 delegations */
readonly delegations: ReadonlyArray<Delegation>;
/** Email address */
readonly email?: Maybe<Email>;
readonly id: Scalars['ID']['output'];
Expand All @@ -1922,6 +1939,8 @@ export type User = {
* When value is 'default' the intent is to use preferred language from OS settings.
*/
readonly language: Scalars['Language']['output'];
/** List of mobile sessions */
readonly mobileSessions: ReadonlyArray<MobileSession>;
/** Phone number with international calling code. */
readonly phone?: Maybe<Scalars['Phone']['output']>;
readonly supportChat: ReadonlyArray<SupportMessage>;
Expand Down
19 changes: 19 additions & 0 deletions apps/pay/lib/graphql/generated.ts
Original file line number Diff line number Diff line change
Expand Up @@ -448,6 +448,14 @@ export type CurrencyConversionEstimation = {
readonly usdCentAmount: Scalars['CentAmount'];
};

export type Delegation = {
readonly __typename: 'Delegation';
readonly app: Scalars['String'];
readonly handledAt: Scalars['Timestamp'];
readonly remember: Scalars['Boolean'];
readonly scope: ReadonlyArray<Scalars['String']>;
};

export type DepositFeesInformation = {
readonly __typename: 'DepositFeesInformation';
readonly minBankFee: Scalars['String'];
Expand Down Expand Up @@ -876,6 +884,13 @@ export type MerchantPayload = {
readonly merchant?: Maybe<Merchant>;
};

export type MobileSession = {
readonly __typename: 'MobileSession';
readonly expiresAt: Scalars['Timestamp'];
readonly id: Scalars['ID'];
readonly issuedAt: Scalars['Timestamp'];
};

export type MobileVersions = {
readonly __typename: 'MobileVersions';
readonly currentSupported: Scalars['Int'];
Expand Down Expand Up @@ -1913,6 +1928,8 @@ export type User = {
readonly contacts: ReadonlyArray<UserContact>;
readonly createdAt: Scalars['Timestamp'];
readonly defaultAccount: Account;
/** List of Oauth2 delegations */
readonly delegations: ReadonlyArray<Delegation>;
/** Email address */
readonly email?: Maybe<Email>;
readonly id: Scalars['ID'];
Expand All @@ -1921,6 +1938,8 @@ export type User = {
* When value is 'default' the intent is to use preferred language from OS settings.
*/
readonly language: Scalars['Language'];
/** List of mobile sessions */
readonly mobileSessions: ReadonlyArray<MobileSession>;
/** Phone number with international calling code. */
readonly phone?: Maybe<Scalars['Phone']>;
readonly supportChat: ReadonlyArray<SupportMessage>;
Expand Down
16 changes: 0 additions & 16 deletions apps/voucher/cypress/e2e/consent-integration.cy.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,22 +32,6 @@ describe("Consent integration Test", () => {
cy.get("[data-testid=verification_code_input]").should("not.be.disabled")
cy.get("[data-testid=verification_code_input]").type(code)

cy.contains("label", "read").should("exist")
cy.contains("label", "read").should("be.visible")
cy.contains("label", "read").should("not.be.disabled")
cy.contains("label", "read").should("not.be.disabled")
cy.contains("label", "read").click()

cy.contains("label", "write").should("exist")
cy.contains("label", "write").should("be.visible")
cy.contains("label", "write").should("not.be.disabled")
cy.contains("label", "write").click()

cy.get("[data-testid=submit_consent_btn]").should("exist")
cy.get("[data-testid=submit_consent_btn]").should("be.visible")
cy.get("[data-testid=submit_consent_btn]").should("not.be.disabled")
cy.get("[data-testid=submit_consent_btn]").click()

Comment on lines -35 to -50
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

same here

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

cy.url().should("eq", Cypress.config().baseUrl + "/")
cy.getCookie("next-auth.session-token").then((cookie) => {
if (cookie && cookie.value) {
Expand Down
7 changes: 7 additions & 0 deletions bats/core/api/user.bats
Original file line number Diff line number Diff line change
Expand Up @@ -24,3 +24,10 @@ setup_file() {
language="$(graphql_output '.data.me.language')"
[[ "$language" == "$new_language" ]] || exit 1
}

@test "user: list sessions" {
exec_graphql 'alice' 'list-sessions'
sessions="$(graphql_output '.data.me.mobileSessions')"
id="$(echo "$sessions" | jq -r '.[0].id')" # Extracts the ID of the first element
[[ "$sessions" != "[]" ]] && [[ -n "$id" ]] || exit 1
}
17 changes: 17 additions & 0 deletions bats/gql/list-sessions.gql
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
query userDetails {
me {
id
phone
mobileSessions {
id
expiresAt
issuedAt
}
delegations {
app
handledAt
remember
scope
}
Comment on lines +5 to +15
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

probably something to discuss at ACE or another meeting... why not just have a sessions query?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

you mean, as opposed to segmenting between delegation and sessions? we also have API keys btw.

I think the reason to not have them combined is because they have different properties.

for instance, delegated token can currently have smaller scope than a mobile token, which currently is always full access. delegated token are also attached to a particular application.

}
}
13 changes: 12 additions & 1 deletion bats/helpers/user.bash
Original file line number Diff line number Diff line change
Expand Up @@ -75,9 +75,20 @@ create_user_with_metadata() {
}

random_phone() {
printf "+1%010d\n" $(( ($RANDOM * 1000000) + ($RANDOM % 1000000) ))
# Generate an area code: cannot start with 0 or 1
local area_code=415

# Generate an exchange code: cannot start with 0 or 1
local exchange=$(( $RANDOM % 800 + 200 ))

# Generate a subscriber number: four-digit number
local subscriber=$(( $RANDOM % 10000 ))

# Print formatted phone number
printf "+1%03d%03d%04d\n" $area_code $exchange $subscriber
}


user_update_username() {
local token_name="$1"

Expand Down
1 change: 1 addition & 0 deletions core/api/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -182,6 +182,7 @@
"pino-pretty": "^10.3.1",
"prettier": "^3.2.5",
"protoc-gen-js": "^3.21.2",
"puppeteer": "^22.7.1",
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

not sure about this.. integrations tests are already flacky and now introducing a package like this that runs a headless chromium can be problematic.. is not another way to test it?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I understand your point.

I tried with axios and basically spent a day on this, and I could not make it work, so I gave up.

the issue with a OAuth2 flow is that there is a lot of context carried around different screen, whether it's via parameters over the URL or via Cookie. it's quite hard to replicate this, specially for someone not having the full context of OAuth2 in its head.

using puppeteer make it a lot more straightforward.

I suggest we could try it and revert/change if this introduce flackiness to the test suite?

"react": "^18.2.0",
"spectaql": "^2.3.1",
"tiny-secp256k1": "^2.2.3",
Expand Down
1 change: 1 addition & 0 deletions core/api/src/app/users/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import { UsersRepository } from "@/services/mongoose"
export * from "./update-language"
export * from "./get-user-language"
export * from "./add-device-token"
export * from "./list-sessions"

const users = UsersRepository()

Expand Down
Loading
Loading