Skip to content

Commit

Permalink
First integration steps for LensClient
Browse files Browse the repository at this point in the history
  • Loading branch information
cesarenaldi committed Sep 13, 2023
1 parent 7983116 commit 08c86cf
Show file tree
Hide file tree
Showing 37 changed files with 253 additions and 200 deletions.
1 change: 1 addition & 0 deletions packages/client/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@
},
"license": "MIT",
"dependencies": {
"@lens-protocol/gated-content": "workspace:*",
"@lens-protocol/shared-kernel": "workspace:*",
"@lens-protocol/storage": "workspace:*",
"dotenv": "^16.3.1",
Expand Down
45 changes: 43 additions & 2 deletions packages/client/src/LensClient.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
import { IStorageProvider } from '@lens-protocol/storage';
import { Authentication, IAuthentication } from './authentication';
import { LensConfig } from './consts/config';
import { LensContext, MediaTransformsConfig } from './context';
import { Environment } from './environments';
import {
Explore,
Feed,
Expand All @@ -15,6 +17,37 @@ import {
Wallet,
} from './submodules';

/**
* LensClient configuration
*/
export type LensClientConfig = {
/**
* The environment to use. See {@link production}, {@link development}, and {@link sandbox}.
*/
environment: Environment;
/**
* The storage provider to use.
*
* @defaultValue {@link InMemoryStorageProvider}
*/
storage?: IStorageProvider;

/**
* Media returned from the publication and profile queries can be transformed
* to sizes needed by the SDK consuming application.
* To overwrite default transformation values, provide a `mediaTransforms` object.
*
* @see {@link MediaTransformsConfig} for more information
*/
mediaTransforms?: MediaTransformsConfig;

/**
* The app ids to use to read data from the Lens Protocol.
* If not provided, all apps will be used.
*/
forApps?: string[];
};

/**
* The LensClient is the main entry point for the LensClient SDK.
* It provides access to all the different modules.
Expand All @@ -32,7 +65,7 @@ import {
*/
export class LensClient {
private readonly _authentication: Authentication;
private readonly config: LensConfig;
private readonly context: LensContext;

/**
* @param config - The configuration for the LensClient
Expand All @@ -42,6 +75,14 @@ export class LensClient {
this.config = config;
}

get storage(): {
return this.config.storage;
}

get environment() {
return this.config.environment;
}

get authentication(): IAuthentication {
return this._authentication;
}
Expand Down
5 changes: 3 additions & 2 deletions packages/client/src/__helpers__/buildTestEnvironment.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,13 @@
import * as GatedEnvironments from '@lens-protocol/gated-content/environments';
import { invariant } from '@lens-protocol/shared-kernel';
import * as dotenv from 'dotenv';

import { Environment } from '../consts/environments';
import { Environment } from '../environments';

dotenv.config();

export const buildTestEnvironment = (): Environment => {
invariant(process.env.TESTING_ENV_URL, 'TESTING_ENV_URL is not defined in .env file');

return new Environment('testing', process.env.TESTING_ENV_URL);
return new Environment('testing', process.env.TESTING_ENV_URL, GatedEnvironments.sandbox);
};
31 changes: 14 additions & 17 deletions packages/client/src/authentication/Authentication.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import { failure, PromiseResult, success } from '@lens-protocol/shared-kernel';
import { InMemoryStorageProvider } from '@lens-protocol/storage';

import type { LensConfig } from '../consts/config';
import { CredentialsExpiredError, NotAuthenticatedError } from '../consts/errors';
import type { LensContext } from '../context';
import { CredentialsExpiredError, NotAuthenticatedError } from '../errors';
import type { ChallengeRequest, SignedAuthChallenge } from '../graphql/types.generated';
import type { IAuthentication } from './IAuthentication';
import { AuthenticationApi } from './adapters/AuthenticationApi';
Expand All @@ -14,14 +14,11 @@ import type { AuthChallengeFragment } from './graphql/auth.generated';
*/
export class Authentication implements IAuthentication {
private readonly api: AuthenticationApi;
private readonly storage: CredentialsStorage;

constructor(config: LensConfig) {
this.api = new AuthenticationApi(config);
this.storage = new CredentialsStorage(
config.storage || new InMemoryStorageProvider(),
config.environment.name,
);
private readonly credentials: CredentialsStorage;

constructor(context: LensContext) {
this.api = new AuthenticationApi(context);
this.credentials = new CredentialsStorage(context.storage, context.environment.name);
}

async generateChallenge(request: ChallengeRequest): Promise<AuthChallengeFragment> {
Expand All @@ -30,15 +27,15 @@ export class Authentication implements IAuthentication {

async authenticate(request: SignedAuthChallenge): Promise<void> {
const credentials = await this.api.authenticate(request);
await this.storage.set(credentials);
await this.credentials.set(credentials);
}

async verify(accessToken: string): Promise<boolean> {
return this.api.verify(accessToken);
}

async isAuthenticated(): Promise<boolean> {
const credentials = await this.storage.get();
const credentials = await this.credentials.get();

if (!credentials) {
return false;
Expand All @@ -50,7 +47,7 @@ export class Authentication implements IAuthentication {

if (credentials.canRefresh()) {
const newCredentials = await this.api.refresh(credentials.refreshToken);
await this.storage.set(newCredentials);
await this.credentials.set(newCredentials);
return true;
}

Expand All @@ -59,7 +56,7 @@ export class Authentication implements IAuthentication {
}

async getAccessToken(): PromiseResult<string, CredentialsExpiredError | NotAuthenticatedError> {
const credentials = await this.storage.get();
const credentials = await this.credentials.get();

if (!credentials) {
return failure(new NotAuthenticatedError());
Expand All @@ -71,7 +68,7 @@ export class Authentication implements IAuthentication {

if (credentials.canRefresh()) {
const newCredentials = await this.api.refresh(credentials.refreshToken);
await this.storage.set(newCredentials);
await this.credentials.set(newCredentials);

if (!newCredentials.accessToken) {
return failure(new CredentialsExpiredError());
Expand All @@ -87,7 +84,7 @@ export class Authentication implements IAuthentication {
Record<string, string>,
CredentialsExpiredError | NotAuthenticatedError
> {
const credentials = await this.storage.get();
const credentials = await this.credentials.get();

if (!credentials) {
return failure(new NotAuthenticatedError());
Expand All @@ -99,7 +96,7 @@ export class Authentication implements IAuthentication {

if (credentials.canRefresh()) {
const newCredentials = await this.api.refresh(credentials.refreshToken);
await this.storage.set(newCredentials);
await this.credentials.set(newCredentials);
return success(this.buildHeader(newCredentials.accessToken));
}

Expand Down
2 changes: 1 addition & 1 deletion packages/client/src/authentication/IAuthentication.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { PromiseResult } from '@lens-protocol/shared-kernel';

import { CredentialsExpiredError, NotAuthenticatedError } from '../consts/errors';
import { CredentialsExpiredError, NotAuthenticatedError } from '../errors';
import type { ChallengeRequest, SignedAuthChallenge } from '../graphql/types.generated';
import type { AuthChallengeFragment } from './graphql/auth.generated';

Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import type { LensConfig } from '../../consts/config';
import type { LensContext } from '../../context';
import { FetchGraphQLClient } from '../../graphql/FetchGraphQLClient';
import type { ChallengeRequest, SignedAuthChallenge } from '../../graphql/types.generated';
import { AuthChallengeFragment, getSdk, Sdk } from '../graphql/auth.generated';
Expand All @@ -7,8 +7,8 @@ import { Credentials } from './Credentials';
export class AuthenticationApi {
private readonly sdk: Sdk;

constructor(config: LensConfig) {
const client = new FetchGraphQLClient(config.environment.gqlEndpoint);
constructor(context: LensContext) {
const client = new FetchGraphQLClient(context.environment.gqlEndpoint);
this.sdk = getSdk(client);
}

Expand Down
42 changes: 0 additions & 42 deletions packages/client/src/consts/config.ts

This file was deleted.

19 changes: 0 additions & 19 deletions packages/client/src/consts/environments.ts

This file was deleted.

34 changes: 34 additions & 0 deletions packages/client/src/context.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
import { IStorageProvider } from '@lens-protocol/storage';

import { Environment } from './environments';
import { ImageTransform } from './graphql/types.generated';

/**
* The media transforms configuration.
*/
export type MediaTransformsConfig = {
/**
* The transforms for the publication images.
*/
publication?: ImageTransform;

/**
* The transforms for the profile images.
*/
profilePicture?: ImageTransform;

/**
* The transforms for the profile cover images.
*/
profileCover?: ImageTransform;
};

export type LensContext = {
environment: Environment;

storage: IStorageProvider;

mediaTransforms: MediaTransformsConfig;

forApps: string[];
};
34 changes: 34 additions & 0 deletions packages/client/src/environments.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
import * as GatedEnvironments from '@lens-protocol/gated-content/environments';

/**
* @internal
*/
export class Environment {
constructor(
public readonly name: string,
private url: string,
public readonly gated: GatedEnvironments.EnvironmentConfig,
) {}

get gqlEndpoint() {
return this.url;
}
}

export const production = new Environment(
'production',
'https://api.lens.dev',
GatedEnvironments.production,
);

export const development = new Environment(
'development',
'https://api-mumbai.lens.dev',
GatedEnvironments.development,
);

export const sandbox = new Environment(
'sandbox',
'https://api-sandbox-mumbai.lens.dev',
GatedEnvironments.sandbox,
);
File renamed without changes.
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { MediaTransformsConfig } from '../consts/config';
import { MediaTransformsConfig } from '../context';

export function buildImageTransformsFromConfig(config: MediaTransformsConfig = {}) {
return {
Expand Down
6 changes: 3 additions & 3 deletions packages/client/src/helpers/profileStatistics.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { LensConfig } from '../consts/config';
import { LensContext } from '../context';
import { ProfileStatsArg, ProfileStatsCountOpenActionArgs } from '../graphql/types.generated';

export type ProfileQueryOptions = {
Expand All @@ -7,12 +7,12 @@ export type ProfileQueryOptions = {
};

export function buildProfileQueryOptions(args: {
config: LensConfig;
context: LensContext;
profileStatsArg?: ProfileStatsArg;
profileStatsCountOpenActionArgs?: ProfileStatsCountOpenActionArgs;
}): ProfileQueryOptions {
return {
profileStatsArg: args.profileStatsArg ?? { forApps: args.config.forApps },
profileStatsArg: args.profileStatsArg ?? { forApps: args.context.forApps },
profileStatsCountOpenActionArgs: args.profileStatsCountOpenActionArgs ?? {
anyOf: [],
},
Expand Down
2 changes: 1 addition & 1 deletion packages/client/src/helpers/requireAuthHeaders.spec.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { buildTestEnvironment } from '../__helpers__';
import { Authentication } from '../authentication';
import { NotAuthenticatedError } from '../consts/errors';
import { NotAuthenticatedError } from '../errors';
import { requireAuthHeaders } from './requireAuthHeaders';

const testConfig = {
Expand Down
2 changes: 1 addition & 1 deletion packages/client/src/helpers/requireAuthHeaders.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { failure, PromiseResult, success } from '@lens-protocol/shared-kernel';

import type { Authentication } from '../authentication';
import { CredentialsExpiredError, NotAuthenticatedError } from '../consts/errors';
import { CredentialsExpiredError, NotAuthenticatedError } from '../errors';

type Handler<Val> = (headers: Record<string, string>) => Promise<Val>;

Expand Down
Loading

0 comments on commit 08c86cf

Please sign in to comment.