Skip to content

Commit

Permalink
Merge branch 'master' into israel.5309.embedded-magic-ui
Browse files Browse the repository at this point in the history
  • Loading branch information
Israellund authored Dec 3, 2024
2 parents db1dc98 + bf5718a commit a85735e
Show file tree
Hide file tree
Showing 602 changed files with 2,989 additions and 1,601 deletions.
1 change: 0 additions & 1 deletion libs/chains/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,6 @@
"protobufjs": "^6.1.13"
},
"devDependencies": {
"@types/moment": "^2.13.0",
"tsx": "^4.7.2"
}
}
1 change: 0 additions & 1 deletion libs/core/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,6 @@
"zod": "^3.22.4"
},
"devDependencies": {
"@types/moment": "^2.13.0",
"@types/sinon": "^17.0.3",
"sinon": "^17.0.2",
"tsx": "^4.7.2"
Expand Down
5 changes: 5 additions & 0 deletions libs/core/src/integration/events.schemas.ts
Original file line number Diff line number Diff line change
Expand Up @@ -263,4 +263,9 @@ export const FarcasterVoteCreated = FarcasterAction.extend({
contest_address: z.string(),
}).describe('When a farcaster action is initiated on a cast reply');

export const SignUpFlowCompleted = z.object({
user_id: z.number(),
referral_link: z.string(),
});

export const ContestRolloverTimerTicked = z.object({});
7 changes: 7 additions & 0 deletions libs/core/src/integration/events.ts
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,9 @@ export enum EventNames {

// Preferences
SubscriptionPreferencesUpdated = 'SubscriptionPreferencesUpdated',

// Referrals
SignUpFlowCompleted = 'SignUpFlowCompleted',
}

export type EventPairs =
Expand Down Expand Up @@ -141,4 +144,8 @@ export type EventPairs =
| {
event_name: EventNames.DiscordThreadDeleted;
event_payload: z.infer<typeof events.DiscordThreadDeleted>;
}
| {
event_name: EventNames.SignUpFlowCompleted;
event_payload: z.infer<typeof events.SignUpFlowCompleted>;
};
1 change: 1 addition & 0 deletions libs/core/src/integration/outbox.schema.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ const outboxEvents = {
[E.GroupCreated]: P.GroupCreated,
[E.OneOffContestManagerDeployed]: P.OneOffContestManagerDeployed,
[E.RecurringContestManagerDeployed]: P.RecurringContestManagerDeployed,
[E.SignUpFlowCompleted]: P.SignUpFlowCompleted,
[E.SnapshotProposalCreated]: P.SnapshotProposalCreated,
[E.SubscriptionPreferencesUpdated]: P.SubscriptionPreferencesUpdated,
[E.ThreadCreated]: P.ThreadCreated,
Expand Down
1 change: 0 additions & 1 deletion libs/model/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,6 @@
"zod": "^3.22.4"
},
"devDependencies": {
"@types/moment": "^2.13.0",
"@types/node": "^20.11.25",
"@types/sinon": "^17.0.3",
"sinon": "^17.0.2",
Expand Down
8 changes: 4 additions & 4 deletions libs/model/src/community/UpdateCustomDomain.command.ts
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ export function UpdateCustomDomain(): Command<
}

const magicRequestDomain = await fetch(
`https://api.magic.link/v1/api/magic_client/domain/allowlist/add`,
`https://api.magic.link/v2/api/magic_client/domain/allowlist/add`,
{
method: 'POST',
headers: {
Expand All @@ -55,7 +55,7 @@ export function UpdateCustomDomain(): Command<
);

const magicRequestRedirectUrl = await fetch(
`https://api.magic.link/v1/api/magic_client/redirect_url/allowlist/add`,
`https://api.magic.link/v2/api/magic_client/redirect_url/allowlist/add`,
{
method: 'POST',
headers: {
Expand All @@ -77,14 +77,14 @@ export function UpdateCustomDomain(): Command<
magicResponseDomain.status === 'failed' &&
magicResponseDomain.error_code != 'ALREADY_WHITELISTED_DOMAIN'
) {
throw new AppError(magicResponseDomain);
throw new AppError(magicResponseDomain.message);
}

if (
magicResponseRedirectUrl.status === 'failed' &&
magicResponseRedirectUrl.error_code != 'ALREADY_WHITELISTED_DOMAIN'
) {
throw new AppError(magicResponseRedirectUrl);
throw new AppError(magicResponseRedirectUrl.message);
}

response = await fetch(url, {
Expand Down
8 changes: 6 additions & 2 deletions libs/model/src/contest/Contests.projection.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,11 @@ import { models } from '../database';
import { mustExist } from '../middleware/guards';
import { EvmEventSourceAttributes } from '../models';
import * as protocol from '../services/commonProtocol';
import { decodeThreadContentUrl, getChainNodeUrl } from '../utils';
import {
decodeThreadContentUrl,
getChainNodeUrl,
getDefaultContestImage,
} from '../utils';

const log = logger(import.meta);

Expand Down Expand Up @@ -104,7 +108,7 @@ async function updateOrCreateWithAlert(
decimals,
created_at: new Date(),
name: community.name,
image_url: 'http://default.image', // TODO: can we have a default image for this?
image_url: getDefaultContestImage(),
payout_structure: [],
is_farcaster_contest: false,
},
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import { models } from '../database';
import { authRoles } from '../middleware';
import { mustExist } from '../middleware/guards';
import { TopicInstance } from '../models';
import { getDefaultContestImage } from '../utils';

const Errors = {
InvalidTopics: 'Invalid topic',
Expand Down Expand Up @@ -53,6 +54,7 @@ export function CreateContestManagerMetadata(): Command<
: null,
topic_id: topic?.id || null,
is_farcaster_contest: !!is_farcaster_contest,
image_url: rest.image_url || getDefaultContestImage(),
},
{ transaction },
);
Expand Down
7 changes: 2 additions & 5 deletions libs/model/src/contest/GetAllContests.query.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { InvalidInput, Query } from '@hicommonwealth/core';
import { Query } from '@hicommonwealth/core';
import * as schemas from '@hicommonwealth/schemas';
import { QueryTypes } from 'sequelize';
import { z } from 'zod';
Expand All @@ -10,9 +10,6 @@ export function GetAllContests(): Query<typeof schemas.GetAllContests> {
auth: [],
secure: false,
body: async ({ payload }) => {
if (!payload.community_id && !payload.contest_address) {
throw new InvalidInput('Must specify community_id or contest_address');
}
const results = await models.sequelize.query<
z.infer<typeof schemas.ContestResults>
>(
Expand Down Expand Up @@ -72,7 +69,7 @@ from
${payload.contest_id ? `where c.contest_id = ${payload.contest_id}` : ''}
group by c.contest_address
) as c on cm.contest_address = c.contest_address
where
${payload.community_id || payload.contest_address ? 'where' : ''}
${payload.community_id ? 'cm.community_id = :community_id' : ''}
${payload.community_id && payload.contest_address ? 'and' : ''}
${payload.contest_address ? `cm.contest_address = :contest_address` : ''}
Expand Down
20 changes: 17 additions & 3 deletions libs/model/src/user/UpdateUser.command.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
import { InvalidInput, type Command } from '@hicommonwealth/core';
import { EventNames, InvalidInput, type Command } from '@hicommonwealth/core';
import * as schemas from '@hicommonwealth/schemas';
import { DEFAULT_NAME } from '@hicommonwealth/shared';
import { models } from '../database';
import { mustExist } from '../middleware/guards';
import { decodeContent, getDelta, updateTags } from '../utils';
import { decodeContent, emitEvent, getDelta, updateTags } from '../utils';

export function UpdateUser(): Command<typeof schemas.UpdateUser> {
return {
Expand All @@ -14,7 +14,7 @@ export function UpdateUser(): Command<typeof schemas.UpdateUser> {
if (actor.user.id != payload.id)
throw new InvalidInput('Invalid user id');

const { id, profile, tag_ids } = payload;
const { id, profile, tag_ids, referral_link } = payload;
const {
slug,
name,
Expand Down Expand Up @@ -78,7 +78,21 @@ export function UpdateUser(): Command<typeof schemas.UpdateUser> {
async (transaction) => {
if (update_tags)
await updateTags(tag_ids!, user.id!, 'user_id', transaction);

if (update_user) {
// emit sign-up flow completed event when:
if (user_delta.is_welcome_onboard_flow_complete && referral_link)
await emitEvent(
models.Outbox,
[
{
event_name: EventNames.SignUpFlowCompleted,
event_payload: { user_id: id, referral_link },
},
],
transaction,
);

// TODO: utility to deep merge deltas
const updates = {
...user_delta,
Expand Down
14 changes: 14 additions & 0 deletions libs/model/src/user/UserReferrals.projection.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import { models } from '../database';

const inputs = {
CommunityCreated: events.CommunityCreated,
SignUpFlowCompleted: events.SignUpFlowCompleted,
};

export function UserReferrals(): Projection<typeof inputs> {
Expand All @@ -22,6 +23,19 @@ export function UserReferrals(): Projection<typeof inputs> {
});
}
},
SignUpFlowCompleted: async ({ payload }) => {
const referral_link = payload.referral_link;
if (referral_link?.startsWith('ref_')) {
const referrer_id = parseInt(referral_link.split('_').at(1)!);
await models.Referral.create({
referrer_id,
referee_id: payload.user_id,
event_name: 'SignUpFlowCompleted',
event_payload: payload,
created_at: new Date(),
});
}
},
},
};
}
1 change: 1 addition & 0 deletions libs/model/src/user/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ export * from './GetNewContent.query';
export * from './GetReferralLink.query';
export * from './GetUserAddresses.query';
export * from './GetUserProfile.query';
export * from './GetUserReferrals.query';
export * from './SearchUserProfiles.query';
export * from './UpdateUser.command';
export * from './UserReferrals.projection';
10 changes: 10 additions & 0 deletions libs/model/src/utils/getDefaultContestImage.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
const defaultImages = [
'https://assets.commonwealth.im/42b9d2d9-79b8-473d-b404-b4e819328ded.png',
'https://assets.commonwealth.im/496806e3-f662-4fb5-8da6-24a969f161f1.png',
'https://assets.commonwealth.im/e4111236-8bdb-48bd-8821-4f03e8e978a6.png',
'https://assets.commonwealth.im/fab3f073-9bf1-4ac3-8625-8b2ee258b5a8.png',
];

export function getDefaultContestImage() {
return defaultImages[Math.floor(Math.random() * defaultImages.length)];
}
1 change: 1 addition & 0 deletions libs/model/src/utils/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ export * from './buildFarcasterWebhookName';
export * from './decodeContent';
export * from './defaultAvatar';
export * from './denormalizedCountUtils';
export * from './getDefaultContestImage';
export * from './getDelta';
export * from './makeGetBalancesOptions';
export * from './parseUserMentions';
Expand Down
4 changes: 1 addition & 3 deletions libs/model/test/contest/check-contests.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ import { expect } from 'chai';
import { Contests } from 'model/src/contest';
import { literal } from 'sequelize';
import { afterAll, beforeAll, describe, test } from 'vitest';
import { bootstrap_testing, seed } from '../../src/tester';
import { seed } from '../../src/tester';
import { drainOutbox } from '../utils';

describe('Check Contests', () => {
Expand All @@ -25,8 +25,6 @@ describe('Check Contests', () => {
const topicId: number = 0;

beforeAll(async () => {
await bootstrap_testing(import.meta);

const [chainNode] = await seed('ChainNode', { contracts: [] });
const [user] = await seed(
'User',
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import { afterAll, beforeAll, describe, test } from 'vitest';
import { commonProtocol, emitEvent, models } from '../../src';
import { Contests } from '../../src/contest';
import { ContestWorker } from '../../src/policies';
import { bootstrap_testing, seed } from '../../src/tester';
import { seed } from '../../src/tester';
import { drainOutbox } from '../utils/outbox-drain';

describe('Contest Worker Policy Lifecycle', () => {
Expand All @@ -21,8 +21,6 @@ describe('Contest Worker Policy Lifecycle', () => {
const topicId: number = 0;

beforeAll(async () => {
await bootstrap_testing(import.meta);

const [chainNode] = await seed('ChainNode', { contracts: [] });
const [user] = await seed(
'User',
Expand Down
59 changes: 57 additions & 2 deletions libs/model/test/referral/referral-lifecycle.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,12 @@ import { GetUserReferrals } from 'model/src/user/GetUserReferrals.query';
import { afterAll, beforeAll, describe, expect, it } from 'vitest';
import { z } from 'zod';
import { CreateCommunity } from '../../src/community';
import { CreateReferralLink, UserReferrals } from '../../src/user';
import {
CreateReferralLink,
GetReferralLink,
UpdateUser,
UserReferrals,
} from '../../src/user';
import { drainOutbox, seedCommunity } from '../utils';

describe('Referral lifecycle', () => {
Expand All @@ -26,7 +31,7 @@ describe('Referral lifecycle', () => {
await dispose()();
});

it('should create a referral', async () => {
it('should create a referral when creating a community with a referral link', async () => {
// admin creates a referral link
const response = await command(CreateReferralLink(), {
actor: admin,
Expand Down Expand Up @@ -84,4 +89,54 @@ describe('Referral lifecycle', () => {
},
});
});

it('should create a referral when signing up with a referral link', async () => {
const response = await query(GetReferralLink(), {
actor: admin,
payload: {},
});

// member signs up with the referral link
await command(UpdateUser(), {
actor: member,
payload: {
id: member.user.id!,
referral_link: response?.referral_link,
profile: { name: 'member' }, // this flags is_welcome_onboard_flow_complete
},
});

await drainOutbox(['SignUpFlowCompleted'], UserReferrals);

// get referrals
const referrals = await query(GetUserReferrals(), {
actor: admin,
payload: {},
});

expect(referrals?.length).toBe(2);

const ref = referrals!.at(1)!;
expect(ref).toMatchObject({
referrer: {
id: admin.user.id,
profile: {
name: 'admin',
avatar_url: ref.referrer.profile.avatar_url,
},
},
referee: {
id: member.user.id,
profile: {
name: 'member',
avatar_url: ref.referee.profile.avatar_url,
},
},
event_name: 'SignUpFlowCompleted',
event_payload: {
user_id: member.user.id,
referral_link: response?.referral_link,
},
});
});
});
3 changes: 1 addition & 2 deletions libs/model/test/seed/seed.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -45,8 +45,7 @@ async function testSeed<T extends schemas.Aggregates>(
return data;
}

// skip for now, seeds are being widely used in lifecycle tests
describe.skip('Seed functions', () => {
describe('Seed functions', () => {
let shouldExit = true;

afterAll(async () => {
Expand Down
Loading

0 comments on commit a85735e

Please sign in to comment.