Skip to content

Commit

Permalink
Merge pull request #24 from GeneralMagicio/project-creation
Browse files Browse the repository at this point in the history
Integrated project creation with abc
  • Loading branch information
aminlatifi authored Aug 15, 2024
2 parents 20b9959 + c91105f commit 7b343f0
Show file tree
Hide file tree
Showing 15 changed files with 591 additions and 281 deletions.
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) {
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

0 comments on commit 7b343f0

Please sign in to comment.