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

Integrated project creation with abc #24

Merged
merged 5 commits into from
Aug 15, 2024
Merged
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
7 changes: 7 additions & 0 deletions config/example.env
Original file line number Diff line number Diff line change
Expand Up @@ -271,3 +271,10 @@ ZKEVM_MAINNET_NODE_HTTP_URL=
ZKEVM_CARDONA_NODE_HTTP_URL=

ENDAOMENT_ADMIN_WALLET_ADDRESS=0xfE3524e04E4e564F9935D34bB5e80c5CaB07F5b4

ABC_LAUNCH_API_SECRET=
ABC_LAUNCH_API_URL=
ABC_LAUNCH_DATA_SOURCE=

QACC_NETWORK_ID=
ABC_LAUNCHER_ADAPTER=
4 changes: 4 additions & 0 deletions config/test.env
Original file line number Diff line number Diff line change
Expand Up @@ -220,3 +220,7 @@ ZKEVM_MAINNET_NODE_HTTP_URL=https://polygon-zkevm.drpc.org
ZKEVM_CARDONA_NODE_HTTP_URL=https://rpc.cardona.zkevm-rpc.com

ENDAOMENT_ADMIN_WALLET_ADDRESS=0xfE3524e04E4e564F9935D34bB5e80c5CaB07F5b4

ABC_LAUNCH_API_SECRET=
ABC_LAUNCH_API_URL=
ABC_LAUNCH_DATA_SOURCE=
67 changes: 67 additions & 0 deletions src/adapters/abcLauncher/AbcLauncherAdapter.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
// a method the get objects from mongodb api read from config DONATION_SAVE_BACKUP_API_URL with sercret read from DONATION_SAVE_BACKUP_API_SECRET,
// it must filter objects by those doesn't have `imported` field with true value
// also must support pagination

import axios from 'axios';
import { logger } from '../../utils/logger';
import config from '../../config';
import { IAbcLauncher } from './AbcLauncherInterface';
import { Abc } from '../../entities/project';

const ABC_LAUNCH_API_URL = config.get('ABC_LAUNCH_API_URL') as string;
const ABC_LAUNCH_API_SECRET = config.get('ABC_LAUNCH_API_SECRET') as string;
const ABC_LAUNCH_DATA_SOURCE = config.get('ABC_LAUNCH_DATA_SOURCE') as string;
const ABC_LAUNCH_COLLECTION = config.get('ABC_LAUNCH_COLLECTION') || 'project';
const ABC_LAUNCH_DATABASE = config.get('ABC_LAUNCH_DATABASE') || 'abc-launcher';

// add '/' if doesn't exist at the
const baseUrl = ABC_LAUNCH_API_URL.endsWith('/')
? ABC_LAUNCH_API_URL
: `${ABC_LAUNCH_API_URL}/`;

export class AbcLauncherAdapter implements IAbcLauncher {
async getProjectAbcLaunchData(
projectAddress: string,
): Promise<Abc | undefined> {
try {
const result = await axios.post(
`${baseUrl}find`,
{
collection: ABC_LAUNCH_COLLECTION,
database: ABC_LAUNCH_DATABASE,
dataSource: ABC_LAUNCH_DATA_SOURCE,
filter: {
projectAddress: projectAddress.toLocaleLowerCase(),
},
},
{
headers: {
'api-key': ABC_LAUNCH_API_SECRET,
'Content-Type': 'application/json',
'Access-Control-Request-Headers': '*',
},
},
);

if (result.status !== 200) {
logger.error('getNotImportedDonationsFromBackup error', result.data);
throw new Error(
'getNotImportedDonationsFromBackup error, status: ' + result.status,
);
}
const data = result.data.documents[0];
if (!data) return undefined;
return {
tokenTicker: data.tokenTicker,
tokenName: data.tokenName,
icon: data.iconHash,
orchestratorAddress: data.orchestratorAddress,
issuanceTokenAddress: data.issuanceTokenAddress,
projectAddress: data.projectAddress,
};
} catch (e) {
logger.error('getNotImportedDonationsFromBackup error', e);
throw e;
}
}
}
34 changes: 34 additions & 0 deletions src/adapters/abcLauncher/AbcLauncherAdapterMock.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
import { Abc } from '../../entities/project';
import { IAbcLauncher } from './AbcLauncherInterface';

export class AbcLauncherAdapterMock implements IAbcLauncher {
private _nextData: Abc;

getDefaultData(): Abc {
return {
tokenTicker: 'MOCK',
tokenName: 'Mock Token Name',
icon: 'moch_icon_hash',
orchestratorAddress: 'mock_address',
issuanceTokenAddress: 'mock_issue_address',
projectAddress: 'mock_project_address',
};
}

setNextData(data: Abc) {
Copy link
Collaborator

Choose a reason for hiding this comment

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

Why do we need this method?
We can not directly use just getDefaultData body inside of getPorjectAbcLunchData?

Copy link
Author

Choose a reason for hiding this comment

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

I put it just to set the expected next output.

this._nextData = data;
}

constructor() {
this._nextData = this.getDefaultData();
}

async getProjectAbcLaunchData(projectAddress: string) {
const data = this._nextData;
this._nextData = this.getDefaultData();
return {
...data,
projectAddress,
};
}
}
5 changes: 5 additions & 0 deletions src/adapters/abcLauncher/AbcLauncherInterface.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
import { Abc } from '../../entities/project';

export interface IAbcLauncher {
getProjectAbcLaunchData(projectAddress: string): Promise<Abc | undefined>;
}
16 changes: 16 additions & 0 deletions src/adapters/adaptersFactory.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@ import { DonationSaveBackupMockAdapter } from './donationSaveBackup/DonationSave
import { SuperFluidAdapter } from './superFluid/superFluidAdapter';
import { SuperFluidMockAdapter } from './superFluid/superFluidMockAdapter';
import { SuperFluidAdapterInterface } from './superFluid/superFluidAdapterInterface';
import { AbcLauncherAdapter } from './abcLauncher/AbcLauncherAdapter';
import { AbcLauncherAdapterMock } from './abcLauncher/AbcLauncherAdapterMock';

const discordAdapter = new DiscordAdapter();
const googleAdapter = new GoogleAdapter();
Expand Down Expand Up @@ -111,3 +113,17 @@ export const getSuperFluidAdapter = (): SuperFluidAdapterInterface => {
return superFluidMockAdapter;
}
};

const abcLauncherAdapter = new AbcLauncherAdapter();
const abcLauncherMockAdapter = new AbcLauncherAdapterMock();

export const getAbcLauncherAdapter = () => {
switch (process.env.ABC_LAUNCHER_ADAPTER) {
case 'abcLauncher':
return abcLauncherAdapter;
case 'mock':
return abcLauncherMockAdapter;
default:
return abcLauncherMockAdapter;
}
};
45 changes: 45 additions & 0 deletions src/entities/project.ts
Original file line number Diff line number Diff line change
Expand Up @@ -117,6 +117,39 @@ export enum ReviewStatus {
Listed = 'Listed',
NotListed = 'Not Listed',
}
@ObjectType()
class ProjectTeamMember {
@Field()
name: string;

@Field({ nullable: true })
image?: string;

@Field({ nullable: true })
twitter?: string;

@Field({ nullable: true })
linkedin?: string;

@Field({ nullable: true })
farcaster?: string;
}

@ObjectType()
export class Abc {
@Field()
tokenName: string;
@Field()
tokenTicker: string;
@Field()
issuanceTokenAddress: string;
@Field()
icon: string;
@Field()
orchestratorAddress: string;
@Field()
projectAddress: string;
}

@Entity()
@ObjectType()
Expand Down Expand Up @@ -198,6 +231,18 @@ export class Project extends BaseEntity {
@Column({ nullable: true })
image?: string;

@Field({ nullable: true })
@Column({ nullable: true })
teaser?: string;

@Field(_ => [ProjectTeamMember], { nullable: true })
@Column('jsonb', { nullable: true })
teamMembers: ProjectTeamMember[];

@Field(_ => Abc, { nullable: true })
@Column('jsonb', { nullable: true })
abc: Abc;

@Index('trgm_idx_project_impact_location', { synchronize: false })
@Field({ nullable: true })
@Column({ nullable: true })
Expand Down
4 changes: 4 additions & 0 deletions src/provider.ts
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,10 @@ export const NETWORK_IDS = {
SOLANA_DEVNET: 103,
};

export const QACC_NETWORK_ID = config.get('QACC_NETWORK_ID')
? +config.get('QACC_NETWORK_ID')
: NETWORK_IDS.ZKEVM_CARDONA;

export const superTokensToToken = {
ETHx: 'ETH',
USDCx: 'USDC',
Expand Down
85 changes: 85 additions & 0 deletions src/resolvers/projectResolver.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
import axios from 'axios';
import { assert, expect } from 'chai';
import {
generateRandomEtheriumAddress,
generateTestAccessToken,
graphqlUrl,
saveUserDirectlyToDb,
} from '../../test/testUtils';
import { User } from '../entities/user';
import { createProjectQuery } from '../../test/graphqlQueries';
import {
CreateProjectInput,
ProjectTeamMemberInput,
} from './types/project-input';
import { getAbcLauncherAdapter } from '../adapters/adaptersFactory';

describe('ProjectCreate test', createProjectTestCases);

function createProjectTestCases() {
let user: User;
let accessToken: string;

beforeEach(async () => {
user = await saveUserDirectlyToDb(generateRandomEtheriumAddress());
accessToken = await generateTestAccessToken(user.id);
});

it('should create project with team members successfully', async () => {
assert.isOk(user);
assert.isOk(accessToken);

const teamMembers: ProjectTeamMemberInput[] = [
{
name: 'John Doe',
image: 'https://example.com/john-doe.jpg',
twitter: 'https://twitter.com/johndoe',
linkedin: 'https://linkedin.com/johndoe',
farcaster: 'https://farcaster.com/johndoe',
},
{
name: 'Jane Doe',
image: 'https://example.com/jane-doe.jpg',
twitter: 'https://twitter.com/janedoe',
linkedin: 'https://linkedin.com/janedoe',
farcaster: 'https://farcaster.com/janedoe',
},
];

const projectAddress = generateRandomEtheriumAddress();
const createProjectInput: CreateProjectInput = {
title: 'Test Create Project 1',
adminUserId: user.id,
description: 'Test Project Description',
categories: [],
image: 'https://example.com/test-project.jpg',
teaser: 'https://example.com/test-project-teaser.jpg',
impactLocation: 'Test Impact Location',
isDraft: false,
teamMembers,
address: projectAddress,
};

const result = await axios.post(
graphqlUrl,
{
query: createProjectQuery,
variables: {
project: createProjectInput,
},
},
{
headers: {
Authorization: `Bearer ${accessToken}`,
},
},
);

const project = result.data.data.createProject;
assert.isOk(project);
expect(project.teamMembers).to.deep.equal(teamMembers);
const expectedAbc =
await getAbcLauncherAdapter().getProjectAbcLaunchData(projectAddress);
expect(project.abc).to.deep.equal(expectedAbc);
});
}
Loading
Loading