Skip to content

Commit 7740b05

Browse files
[TOOL-176] Period extract public repositories (#351)
* add public-repos package * add app extract trigger for public repos * disable dark theme bcuz clerk is weird * add cron for period extract * move ui things ? idk * sort dependencies * refactor * revert layout changes * revert overrideClerkUserToken * add ALL the environment variables
1 parent e96e79f commit 7740b05

File tree

3 files changed

+128
-51
lines changed

3 files changed

+128
-51
lines changed

apps/stack/src/extract/extract-repository.ts

Lines changed: 83 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,50 @@ const context: Context<GetRepositorySourceControl, GetRepositoryEntities> = {
3737
db,
3838
};
3939

40+
const inputSchema = z.object({
41+
repositoryId: z.number(),
42+
repositoryName: z.string(),
43+
namespaceName: z.string(),
44+
sourceControl: z.literal("gitlab").or(z.literal("github")),
45+
from: z.coerce.date(),
46+
to: z.coerce.date()
47+
});
48+
49+
type Input = z.infer<typeof inputSchema>;
50+
const extractRepository = async (input: Input, userId: string) => {
51+
const { repositoryId, repositoryName, namespaceName, sourceControl, from, to } = input;
52+
53+
const sourceControlAccessToken = await getClerkUserToken(userId, `oauth_${sourceControl}`);
54+
55+
if (sourceControl === "gitlab") {
56+
context.integrations.sourceControl = new GitlabSourceControl(sourceControlAccessToken);
57+
} else if (sourceControl === "github") {
58+
context.integrations.sourceControl = new GitHubSourceControl(sourceControlAccessToken);
59+
}
60+
61+
const { repository, namespace } = await getRepository({ externalRepositoryId: repositoryId, repositoryName, namespaceName }, context);
62+
63+
const { instanceId } = await setInstance({ repositoryId: repository.id, userId }, { db: crawlDb, entities: { instances } });
64+
65+
await extractRepositoryEvent.publish(
66+
{
67+
repositoryId: repository.id,
68+
namespaceId: namespace.id
69+
},
70+
{
71+
crawlId: instanceId,
72+
caller: 'extract-repository',
73+
timestamp: new Date().getTime(),
74+
version: 1,
75+
sourceControl,
76+
userId,
77+
from,
78+
to,
79+
}
80+
);
81+
82+
}
83+
4084
const contextSchema = z.object({
4185
authorizer: z.object({
4286
jwt: z.object({
@@ -49,17 +93,6 @@ const contextSchema = z.object({
4993

5094
type CTX = z.infer<typeof contextSchema>;
5195

52-
const inputSchema = z.object({
53-
repositoryId: z.number(),
54-
repositoryName: z.string(),
55-
namespaceName: z.string(),
56-
sourceControl: z.literal("gitlab").or(z.literal("github")),
57-
from: z.coerce.date(),
58-
to: z.coerce.date()
59-
});
60-
61-
type Input = z.infer<typeof inputSchema>;
62-
6396
export const handler = ApiHandler(async (ev) => {
6497

6598
const body = useJsonBody() as unknown;
@@ -76,7 +109,6 @@ export const handler = ApiHandler(async (ev) => {
76109
}
77110

78111
let input: Input;
79-
let sourceControlAccessToken: string;
80112

81113
try {
82114
input = inputSchema.parse(body);
@@ -90,48 +122,49 @@ export const handler = ApiHandler(async (ev) => {
90122

91123
const { sub } = lambdaContext.authorizer.jwt.claims;
92124

93-
94-
const { repositoryId, repositoryName, namespaceName, sourceControl, from, to } = input;
95-
96-
try {
97-
sourceControlAccessToken = await getClerkUserToken(sub, `oauth_${sourceControl}`);
98-
} catch (error) {
99-
return {
100-
statusCode: 500,
101-
body: JSON.stringify({ error: (error as Error).message }),
102-
}
103-
}
104-
105-
if (sourceControl === "gitlab") {
106-
context.integrations.sourceControl = new GitlabSourceControl(sourceControlAccessToken);
107-
} else if (sourceControl === "github") {
108-
context.integrations.sourceControl = new GitHubSourceControl(sourceControlAccessToken);
125+
try {
126+
await extractRepository(input, sub);
127+
} catch (error) {
128+
return {
129+
statusCode: 500,
130+
body: JSON.stringify({ error: (error as Error).toString() })
109131
}
110-
111-
const { repository, namespace } = await getRepository({ externalRepositoryId: repositoryId, repositoryName, namespaceName }, context);
112-
113-
const { instanceId } = await setInstance({ repositoryId: repository.id, userId: sub }, { db: crawlDb, entities: { instances } });
114-
115-
await extractRepositoryEvent.publish(
116-
{
117-
repositoryId: repository.id,
118-
namespaceId: namespace.id
119-
},
120-
{
121-
crawlId: instanceId,
122-
caller: 'extract-repository',
123-
timestamp: new Date().getTime(),
124-
version: 1,
125-
sourceControl,
126-
userId: sub,
127-
from,
128-
to,
129-
}
130-
);
131-
132+
}
132133

133134
return {
134135
statusCode: 200,
135136
body: JSON.stringify({})
136137
};
137138
});
139+
140+
const CRON_ENV = z.object({
141+
CRON_USER_ID: z.string(),
142+
PUBLIC_REPO_NAME: z.string(),
143+
PUBLIC_REPO_OWNER: z.string(),
144+
})
145+
export const cronHandler = async ()=> {
146+
147+
const validEnv = CRON_ENV.safeParse(process.env);
148+
149+
if (!validEnv.success) {
150+
console.error("Invalid environment in lambda 'extract-repository.cronHandler':", ...validEnv.error.issues);
151+
throw new Error("Invalid environment");
152+
}
153+
154+
const { CRON_USER_ID, PUBLIC_REPO_NAME, PUBLIC_REPO_OWNER } = validEnv.data;
155+
156+
const utcTodayAt10AM = new Date();
157+
utcTodayAt10AM.setUTCHours(10, 0, 0, 0);
158+
const utcYesterdayAt10AM = new Date(utcTodayAt10AM);
159+
utcYesterdayAt10AM.setHours(utcTodayAt10AM.getUTCHours() - 24);
160+
161+
await extractRepository({
162+
namespaceName: PUBLIC_REPO_OWNER,
163+
repositoryId: 0,
164+
repositoryName: PUBLIC_REPO_NAME,
165+
sourceControl: 'github',
166+
from: utcYesterdayAt10AM,
167+
to: utcTodayAt10AM,
168+
}, CRON_USER_ID);
169+
170+
}

apps/stack/stacks/ExtractStack.ts

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import {
33
Config,
44
EventBus,
55
Queue,
6+
Cron,
67
type StackContext,
78
} from "sst/constructs";
89
import { z } from "zod";
@@ -158,9 +159,18 @@ export function ExtractStack({ stack }: StackContext) {
158159
const ENVSchema = z.object({
159160
CLERK_JWT_ISSUER: z.string(),
160161
CLERK_JWT_AUDIENCE: z.string(),
162+
PUBLIC_REPOS: z.string(),
163+
CRON_USER_ID: z.string(),
161164
});
165+
const publicReposSchema = z.array(
166+
z.object({
167+
owner: z.string(),
168+
name: z.string(),
169+
})
170+
);
162171

163172
const ENV = ENVSchema.parse(process.env);
173+
const PUBLIC_REPOS = publicReposSchema.parse(JSON.parse(ENV.PUBLIC_REPOS));
164174

165175
const api = new Api(stack, "ExtractApi", {
166176
defaults: {
@@ -195,6 +205,34 @@ export function ExtractStack({ stack }: StackContext) {
195205
},
196206
});
197207

208+
PUBLIC_REPOS.forEach(publicRepo => {
209+
new Cron(stack, `${publicRepo.name}_ExtractCron`, {
210+
schedule: "cron(00 10 * * ? *)",
211+
job: {
212+
function: {
213+
handler: "src/extract/extract-repository.cronHandler",
214+
environment: {
215+
CRON_USER_ID: ENV.CRON_USER_ID,
216+
PUBLIC_REPO_OWNER: publicRepo.owner,
217+
PUBLIC_REPO_NAME: publicRepo.name,
218+
},
219+
bind: [
220+
bus,
221+
EXTRACT_DATABASE_URL,
222+
EXTRACT_DATABASE_AUTH_TOKEN,
223+
CRAWL_DATABASE_URL,
224+
CRAWL_DATABASE_AUTH_TOKEN,
225+
CLERK_SECRET_KEY,
226+
REDIS_URL,
227+
REDIS_TOKEN,
228+
REDIS_USER_TOKEN_TTL
229+
],
230+
runtime: "nodejs18.x",
231+
}
232+
}
233+
})
234+
});
235+
198236
stack.addOutputs({
199237
ApiEndpoint: api.url,
200238
});

turbo.json

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,12 @@
4141
"CRAWL_DATABASE_URL",
4242
"CRAWL_DATABASE_AUTH_TOKEN",
4343
"NEXT_PUBLIC_EXTRACT_API_URL",
44-
"NEXT_PUBLIC_TRANSFORM_API_URL"
44+
"NEXT_PUBLIC_TRANSFORM_API_URL",
45+
"PUBLIC_REPOS",
46+
"CRON_USER_ID",
47+
"PUBLIC_REPO_OWNER",
48+
"PUBLIC_REPO_NAME",
49+
"CLERK_JWT_ISSUER",
50+
"CLERK_JWT_AUDIENCE"
4551
]
4652
}

0 commit comments

Comments
 (0)