@nativesquare/upwork 0.1.1 → 0.2.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -1,141 +1,192 @@
1
- # Convex Component Template
1
+ # Upwork Component for Convex
2
2
 
3
- This is a Convex component, ready to be published on npm.
3
+ [![npm version](https://badge.fury.io/js/@nativesquare%2Fupwork.svg)](https://badge.fury.io/js/@nativesquare%2Fupwork)
4
4
 
5
- To create your own component:
5
+ <!-- START: Include on https://convex.dev/components -->
6
6
 
7
- 1. Write code in src/component for your component. Component-specific tables,
8
- queries, mutations, and actions go here.
9
- 2. Write code in src/client for the Class that interfaces with the component.
10
- This is the bridge your users will access to get information into and out of
11
- your component
12
- 3. Write example usage in example/convex/example.ts.
13
- 4. Delete the text in this readme until `---` and flesh out the README.
14
- 5. Publish to npm with `pnpm run alpha` or `pnpm run release`.
7
+ A [Convex component](https://convex.dev/components) that integrates the
8
+ [Upwork API](https://developers.upwork.com/) into your Convex app. Search job
9
+ postings, fetch individual listings, and manage OAuth authentication all with
10
+ built-in caching that complies with Upwork's API terms.
15
11
 
16
- To develop your component run a dev process in the example project:
12
+ ```ts
13
+ const upwork = new Upwork(components.upwork);
17
14
 
18
- ```sh
19
- pnpm i
20
- pnpm run dev
21
- ```
15
+ // Search jobs
16
+ const results = await upwork.searchJobPostings(ctx, { searchQuery: "react" });
22
17
 
23
- `pnpm i` will do the install and an initial build. `pnpm run dev` will start a
24
- file watcher to re-build the component, as well as the example project frontend
25
- and backend, which does codegen and installs the component.
18
+ // Get a single posting (cache-first, fetches from API if not cached)
19
+ const posting = await upwork.getJobPosting(ctx, { upworkId: "~01abc123" });
20
+ ```
26
21
 
27
- Modify the schema and index files in src/component/ to define your component.
22
+ Found a bug? Feature request?
23
+ [File it here](https://github.com/NativeSquare/upwork/issues).
28
24
 
29
- Write a client for using this component in src/client/index.ts.
25
+ ## Prerequisites
30
26
 
31
- If you won't be adding frontend code (e.g. React components) to this component
32
- you can delete "./react" references in package.json and "src/react/" directory.
33
- If you will be adding frontend code, add a peer dependency on React in
34
- package.json.
27
+ You'll need an Upwork API application. Create one at the
28
+ [Upwork Developer Portal](https://www.upwork.com/developer/keys/apply).
35
29
 
36
- ### Component Directory structure
30
+ ## Installation
37
31
 
38
- ```
39
- .
40
- ├── README.md documentation of your component
41
- ├── package.json component name, version number, other metadata
42
- ├── pnpm-lock.yaml Components are like libraries, pnpm-lock.yaml
43
- │ is .gitignored and ignored by consumers.
44
- ├── src
45
- │   ├── component/
46
- │   │   ├── _generated/ Files here are generated for the component.
47
- │   │   ├── convex.config.ts Name your component here and use other components
48
- │   │   ├── lib.ts Define functions here and in new files in this directory
49
- │   │   └── schema.ts schema specific to this component
50
- │   ├── client/
51
- │   │   └── index.ts Code that needs to run in the app that uses the
52
- │   │   component. Generally the app interacts directly with
53
- │   │   the component's exposed API (src/component/*).
54
- │   └── react/ Code intended to be used on the frontend goes here.
55
- │ │ Your are free to delete this if this component
56
- │ │ does not provide code.
57
- │   └── index.ts
58
- ├── example/ example Convex app that uses this component
59
- │   └── convex/
60
- │      ├── _generated/ Files here are generated for the example app.
61
- │      ├── convex.config.ts Imports and uses this component
62
- │      ├── myFunctions.ts Functions that use the component
63
- │      └── schema.ts Example app schema
64
- └── dist/ Publishing artifacts will be created here.
32
+ ```sh
33
+ npm install @nativesquare/upwork
65
34
  ```
66
35
 
67
- ---
36
+ Register the component in your `convex/convex.config.ts`:
68
37
 
69
- # My Convex Component
38
+ ```ts
39
+ // convex/convex.config.ts
40
+ import { defineApp } from "convex/server";
41
+ import upwork from "@nativesquare/upwork/convex.config.js";
70
42
 
71
- [![npm version](https://badge.fury.io/js/@nativesquare%2Fmy-component.svg)](https://badge.fury.io/js/@nativesquare%2Fmy-component)
43
+ const app = defineApp();
44
+ app.use(upwork);
72
45
 
73
- <!-- START: Include on https://convex.dev/components -->
46
+ export default app;
47
+ ```
74
48
 
75
- - [ ] What is some compelling syntax as a hook?
76
- - [ ] Why should you use this component?
77
- - [ ] Links to docs / other resources?
49
+ ## Environment Variables
78
50
 
79
- Found a bug? Feature request?
80
- [File it here](https://github.com/NativeSquare/my-component/issues).
51
+ Set the following environment variables in your Convex deployment:
81
52
 
82
- ## Installation
53
+ | Variable | Description |
54
+ | ---------------------- | ----------------------------------------- |
55
+ | `UPWORK_CLIENT_ID` | Your Upwork API application client ID |
56
+ | `UPWORK_CLIENT_SECRET` | Your Upwork API application client secret |
57
+ | `CONVEX_SITE_URL` | Your Convex deployment's HTTP Actions URL |
58
+
59
+ The component reads these automatically — you never need to pass credentials
60
+ in your code.
61
+
62
+ ## Setup
63
+
64
+ ### 1. Register HTTP routes
83
65
 
84
- Create a `convex.config.ts` file in your app's `convex/` folder and install the
85
- component by calling `use`:
66
+ The component needs an HTTP route to handle the OAuth callback from Upwork.
67
+ Add this to your `convex/http.ts`:
86
68
 
87
69
  ```ts
88
- // convex/convex.config.ts
89
- import { defineApp } from "convex/server";
90
- import myComponent from "@nativesquare/my-component/convex.config.js";
70
+ // convex/http.ts
71
+ import { httpRouter } from "convex/server";
72
+ import { registerRoutes } from "@nativesquare/upwork";
73
+ import { components } from "./_generated/api";
91
74
 
92
- const app = defineApp();
93
- app.use(myComponent);
75
+ const http = httpRouter();
94
76
 
95
- export default app;
77
+ registerRoutes(http, components.upwork, {
78
+ onSuccess: "http://localhost:5173", // redirect after successful auth
79
+ });
80
+
81
+ export default http;
96
82
  ```
97
83
 
98
- ## Usage
84
+ The `onSuccess` option is where the user is redirected after connecting their
85
+ Upwork account. If omitted, a plain text success message is shown instead.
86
+
87
+ ### 2. Create the client
99
88
 
100
89
  ```ts
90
+ // convex/example.ts
91
+ import { action, query } from "./_generated/server";
101
92
  import { components } from "./_generated/api";
93
+ import { Upwork } from "@nativesquare/upwork";
94
+
95
+ const upwork = new Upwork(components.upwork);
96
+ ```
102
97
 
103
- export const doSomething = mutation({
104
- args: { text: v.string() },
98
+ ## API Reference
99
+
100
+ ### `getAuthorizationUrl()`
101
+
102
+ Returns the Upwork OAuth authorization URL. Redirect users here to connect
103
+ their Upwork account.
104
+
105
+ ```ts
106
+ export const getAuthUrl = query({
107
+ args: {},
108
+ handler: async () => {
109
+ return upwork.getAuthorizationUrl();
110
+ },
111
+ });
112
+ ```
113
+
114
+ ### `getAuthStatus(ctx)`
115
+
116
+ Returns the current OAuth connection status: `"connected"`, `"disconnected"`,
117
+ or `"expired"`.
118
+
119
+ ```ts
120
+ export const authStatus = query({
121
+ args: {},
122
+ handler: async (ctx) => {
123
+ return await upwork.getAuthStatus(ctx);
124
+ },
125
+ });
126
+ ```
127
+
128
+ ### `searchJobPostings(ctx, opts?)`
129
+
130
+ Searches the Upwork marketplace and caches the results. Requires an action
131
+ context since it makes a live API call.
132
+
133
+ ```ts
134
+ export const search = action({
135
+ args: { searchQuery: v.optional(v.string()) },
105
136
  handler: async (ctx, args) => {
106
- return await ctx.runMutation(components.myComponent.lib.doSomething, {
107
- text: args.text,
137
+ return await upwork.searchJobPostings(ctx, {
138
+ searchQuery: args.searchQuery,
139
+ sortField: "RECENCY", // optional, defaults to "RECENCY"
108
140
  });
109
141
  },
110
142
  });
111
143
  ```
112
144
 
113
- See more example usage in [example.ts](./example/convex/example.ts).
145
+ Returns `{ totalCount, postings, hasNextPage }`.
114
146
 
115
- ### HTTP Routes
147
+ ### `getJobPosting(ctx, opts)`
116
148
 
117
- You can register HTTP routes for the component to expose HTTP endpoints:
149
+ Gets a single job posting by its Upwork ID. Uses a hybrid strategy: checks the
150
+ local cache first, and if not found, fetches from the Upwork API and stores the
151
+ result. Requires an action context.
118
152
 
119
153
  ```ts
120
- import { httpRouter } from "convex/server";
121
- import { registerRoutes } from "@nativesquare/my-component";
122
- import { components } from "./_generated/api";
154
+ export const getJob = action({
155
+ args: { upworkId: v.string() },
156
+ handler: async (ctx, args) => {
157
+ return await upwork.getJobPosting(ctx, { upworkId: args.upworkId });
158
+ },
159
+ });
160
+ ```
123
161
 
124
- const http = httpRouter();
162
+ Returns a `JobPosting` or `null` if the posting doesn't exist.
125
163
 
126
- registerRoutes(http, components.myComponent, {
127
- pathPrefix: "/my-component",
128
- });
164
+ ### `listJobPostings(ctx, opts?)`
129
165
 
130
- export default http;
166
+ Lists cached job postings from the database. This is a query (no API call),
167
+ so it's reactive and fast.
168
+
169
+ ```ts
170
+ export const list = query({
171
+ args: { limit: v.optional(v.number()) },
172
+ handler: async (ctx, args) => {
173
+ return await upwork.listJobPostings(ctx, { limit: args.limit });
174
+ },
175
+ });
131
176
  ```
132
177
 
133
- See [http.ts](./example/convex/http.ts) for a complete example.
178
+ Returns up to `limit` postings (default 50) cached within the last 23 hours.
179
+
180
+ ### `exchangeAuthCode(ctx, opts)`
181
+
182
+ Exchanges an OAuth authorization code for access tokens. You typically don't
183
+ need to call this directly — `registerRoutes` handles it via the callback
184
+ endpoint.
134
185
 
135
186
  ## Upwork API Compliance
136
187
 
137
188
  This component caches job postings from the Upwork API to improve performance
138
- and reduce API calls. Users of this component must comply with
189
+ and reduce API calls. It is designed to comply with
139
190
  [Upwork's API Terms of Use](https://www.upwork.com/legal#api):
140
191
 
141
192
  - **24-hour caching limit**: Upwork does not allow storing API data for more
@@ -145,16 +196,21 @@ and reduce API calls. Users of this component must comply with
145
196
 
146
197
  - **Rate limiting**: The Upwork API enforces a rate limit of **300 requests per
147
198
  minute per IP address**. Exceeding this limit will result in HTTP 429
148
- "Too Many Requests" responses. The caching layer in this component helps you
149
- stay within these limits -- cached data is served directly from the database
150
- without hitting the Upwork API. Be mindful of how frequently you call
151
- `searchJobPostings`, as each invocation makes a live API request.
199
+ "Too Many Requests" responses. The caching layer helps you stay within these
200
+ limits cached data is served directly from the database without hitting the
201
+ Upwork API. Be mindful of how frequently you call `searchJobPostings` and
202
+ `getJobPosting`, as they may make live API requests.
203
+
204
+ - **Token refresh**: Access tokens are automatically refreshed when expired.
152
205
 
153
206
  <!-- END: Include on https://convex.dev/components -->
154
207
 
155
- Run the example:
208
+ ## Development
156
209
 
157
210
  ```sh
158
211
  pnpm i
159
212
  pnpm run dev
160
213
  ```
214
+
215
+ See the [example app](./example/convex/example.ts) for a complete working
216
+ integration.
@@ -5,30 +5,23 @@ export type UpworkComponent = ComponentApi;
5
5
  export declare class Upwork {
6
6
  component: UpworkComponent;
7
7
  constructor(component: UpworkComponent);
8
- getAuthorizationUrl(opts: {
9
- clientId: string;
10
- siteUrl: string;
11
- }): string;
8
+ getAuthorizationUrl(): string;
12
9
  exchangeAuthCode(ctx: ActionCtx, opts: {
13
- clientId: string;
14
- clientSecret: string;
15
10
  code: string;
16
- siteUrl: string;
17
11
  }): Promise<void>;
18
- searchJobPostings(ctx: ActionCtx, opts: {
19
- clientId: string;
20
- clientSecret: string;
12
+ searchJobPostings(ctx: ActionCtx, opts?: {
21
13
  searchQuery?: string;
22
14
  sortField?: string;
23
15
  }): Promise<SearchResult>;
16
+ getJobPosting(ctx: ActionCtx, opts: {
17
+ upworkId: string;
18
+ }): Promise<JobPosting | null>;
24
19
  listJobPostings(ctx: QueryCtx, opts?: {
25
20
  limit?: number;
26
21
  }): Promise<JobPosting[]>;
27
22
  getAuthStatus(ctx: QueryCtx): Promise<AuthStatus>;
28
23
  }
29
- export declare function registerRoutes(http: HttpRouter, component: UpworkComponent, opts: {
30
- clientId: string;
31
- clientSecret: string;
24
+ export declare function registerRoutes(http: HttpRouter, component: UpworkComponent, opts?: {
32
25
  onSuccess?: string;
33
26
  }): void;
34
27
  export { CALLBACK_PATH } from "./types.js";
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/client/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,sCAAsC,CAAC;AACzE,OAAO,KAAK,EAAE,SAAS,EAAE,QAAQ,EAAE,UAAU,EAAE,YAAY,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AAE5F,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,eAAe,CAAC;AAKhD,MAAM,MAAM,eAAe,GAAG,YAAY,CAAC;AAE3C,qBAAa,MAAM;IACE,SAAS,EAAE,eAAe;gBAA1B,SAAS,EAAE,eAAe;IAE7C,mBAAmB,CAAC,IAAI,EAAE;QACxB,QAAQ,EAAE,MAAM,CAAC;QACjB,OAAO,EAAE,MAAM,CAAC;KACjB,GAAG,MAAM;IAUJ,gBAAgB,CACpB,GAAG,EAAE,SAAS,EACd,IAAI,EAAE;QACJ,QAAQ,EAAE,MAAM,CAAC;QACjB,YAAY,EAAE,MAAM,CAAC;QACrB,IAAI,EAAE,MAAM,CAAC;QACb,OAAO,EAAE,MAAM,CAAC;KACjB,GACA,OAAO,CAAC,IAAI,CAAC;IAUV,iBAAiB,CACrB,GAAG,EAAE,SAAS,EACd,IAAI,EAAE;QACJ,QAAQ,EAAE,MAAM,CAAC;QACjB,YAAY,EAAE,MAAM,CAAC;QACrB,WAAW,CAAC,EAAE,MAAM,CAAC;QACrB,SAAS,CAAC,EAAE,MAAM,CAAC;KACpB,GACA,OAAO,CAAC,YAAY,CAAC;IASlB,eAAe,CACnB,GAAG,EAAE,QAAQ,EACb,IAAI,CAAC,EAAE;QAAE,KAAK,CAAC,EAAE,MAAM,CAAA;KAAE,GACxB,OAAO,CAAC,UAAU,EAAE,CAAC;IAMlB,aAAa,CAAC,GAAG,EAAE,QAAQ,GAAG,OAAO,CAAC,UAAU,CAAC;CAGxD;AAED,wBAAgB,cAAc,CAC5B,IAAI,EAAE,UAAU,EAChB,SAAS,EAAE,eAAe,EAC1B,IAAI,EAAE;IACJ,QAAQ,EAAE,MAAM,CAAC;IACjB,YAAY,EAAE,MAAM,CAAC;IACrB,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB,QA+CF;AAED,OAAO,EAAE,aAAa,EAAE,MAAM,YAAY,CAAC;AAC3C,YAAY,EAAE,UAAU,EAAE,YAAY,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/client/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,sCAAsC,CAAC;AACzE,OAAO,KAAK,EAAE,SAAS,EAAE,QAAQ,EAAE,UAAU,EAAE,YAAY,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AAE5F,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,eAAe,CAAC;AAKhD,MAAM,MAAM,eAAe,GAAG,YAAY,CAAC;AAE3C,qBAAa,MAAM;IACE,SAAS,EAAE,eAAe;gBAA1B,SAAS,EAAE,eAAe;IAE7C,mBAAmB,IAAI,MAAM;IAkBvB,gBAAgB,CACpB,GAAG,EAAE,SAAS,EACd,IAAI,EAAE;QAAE,IAAI,EAAE,MAAM,CAAA;KAAE,GACrB,OAAO,CAAC,IAAI,CAAC;IAYV,iBAAiB,CACrB,GAAG,EAAE,SAAS,EACd,IAAI,CAAC,EAAE;QACL,WAAW,CAAC,EAAE,MAAM,CAAC;QACrB,SAAS,CAAC,EAAE,MAAM,CAAC;KACpB,GACA,OAAO,CAAC,YAAY,CAAC;IAOlB,aAAa,CACjB,GAAG,EAAE,SAAS,EACd,IAAI,EAAE;QAAE,QAAQ,EAAE,MAAM,CAAA;KAAE,GACzB,OAAO,CAAC,UAAU,GAAG,IAAI,CAAC;IAgBvB,eAAe,CACnB,GAAG,EAAE,QAAQ,EACb,IAAI,CAAC,EAAE;QAAE,KAAK,CAAC,EAAE,MAAM,CAAA;KAAE,GACxB,OAAO,CAAC,UAAU,EAAE,CAAC;IAMlB,aAAa,CAAC,GAAG,EAAE,QAAQ,GAAG,OAAO,CAAC,UAAU,CAAC;CAGxD;AAED,wBAAgB,cAAc,CAC5B,IAAI,EAAE,UAAU,EAChB,SAAS,EAAE,eAAe,EAC1B,IAAI,CAAC,EAAE;IACL,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB,QAmCF;AAED,OAAO,EAAE,aAAa,EAAE,MAAM,YAAY,CAAC;AAC3C,YAAY,EAAE,UAAU,EAAE,YAAY,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC"}
@@ -6,30 +6,53 @@ export class Upwork {
6
6
  constructor(component) {
7
7
  this.component = component;
8
8
  }
9
- getAuthorizationUrl(opts) {
10
- const redirectUri = `${opts.siteUrl}${CALLBACK_PATH}`;
9
+ getAuthorizationUrl() {
10
+ const clientId = process.env.UPWORK_CLIENT_ID;
11
+ const siteUrl = process.env.CONVEX_SITE_URL;
12
+ if (!clientId) {
13
+ throw new Error("Missing UPWORK_CLIENT_ID environment variable.");
14
+ }
15
+ if (!siteUrl) {
16
+ throw new Error("Missing CONVEX_SITE_URL environment variable.");
17
+ }
18
+ const redirectUri = `${siteUrl}${CALLBACK_PATH}`;
11
19
  const params = new URLSearchParams({
12
20
  response_type: "code",
13
- client_id: opts.clientId,
21
+ client_id: clientId,
14
22
  redirect_uri: redirectUri,
15
23
  });
16
24
  return `${BASE_URL}/ab/account-security/oauth2/authorize?${params.toString()}`;
17
25
  }
18
26
  async exchangeAuthCode(ctx, opts) {
19
- const redirectUri = `${opts.siteUrl}${CALLBACK_PATH}`;
27
+ const siteUrl = process.env.CONVEX_SITE_URL;
28
+ if (!siteUrl) {
29
+ throw new Error("Missing CONVEX_SITE_URL environment variable.");
30
+ }
31
+ const redirectUri = `${siteUrl}${CALLBACK_PATH}`;
20
32
  await ctx.runAction(this.component.public.exchangeAuthCode, {
21
- clientId: opts.clientId,
22
- clientSecret: opts.clientSecret,
23
33
  code: opts.code,
24
34
  redirectUri,
25
35
  });
26
36
  }
27
37
  async searchJobPostings(ctx, opts) {
28
38
  return await ctx.runAction(this.component.public.searchJobPostings, {
29
- clientId: opts.clientId,
30
- clientSecret: opts.clientSecret,
31
- searchQuery: opts.searchQuery,
32
- sortField: opts.sortField,
39
+ searchQuery: opts?.searchQuery,
40
+ sortField: opts?.sortField,
41
+ });
42
+ }
43
+ async getJobPosting(ctx, opts) {
44
+ const cached = await ctx.runQuery(this.component.public.getJobPosting, {
45
+ upworkId: opts.upworkId,
46
+ });
47
+ if (cached)
48
+ return cached;
49
+ const fetched = await ctx.runAction(this.component.public.fetchJobPosting, {
50
+ upworkId: opts.upworkId,
51
+ });
52
+ if (!fetched)
53
+ return null;
54
+ return await ctx.runQuery(this.component.public.getJobPosting, {
55
+ upworkId: opts.upworkId,
33
56
  });
34
57
  }
35
58
  async listJobPostings(ctx, opts) {
@@ -42,15 +65,6 @@ export class Upwork {
42
65
  }
43
66
  }
44
67
  export function registerRoutes(http, component, opts) {
45
- const missing = [];
46
- if (!opts.clientId)
47
- missing.push("clientId");
48
- if (!opts.clientSecret)
49
- missing.push("clientSecret");
50
- if (missing.length > 0) {
51
- throw new Error(`registerRoutes: missing required options: ${missing.join(", ")}. ` +
52
- `Make sure UPWORK_CLIENT_ID and UPWORK_CLIENT_SECRET environment variables are set.`);
53
- }
54
68
  http.route({
55
69
  path: CALLBACK_PATH,
56
70
  method: "GET",
@@ -63,8 +77,6 @@ export function registerRoutes(http, component, opts) {
63
77
  const redirectUri = `${url.origin}${url.pathname}`;
64
78
  try {
65
79
  await ctx.runAction(component.public.exchangeAuthCode, {
66
- clientId: opts.clientId,
67
- clientSecret: opts.clientSecret,
68
80
  code,
69
81
  redirectUri,
70
82
  });
@@ -73,7 +85,7 @@ export function registerRoutes(http, component, opts) {
73
85
  const message = error instanceof Error ? error.message : "Unknown error";
74
86
  return new Response(`OAuth callback failed: ${message}`, { status: 500 });
75
87
  }
76
- if (opts.onSuccess) {
88
+ if (opts?.onSuccess) {
77
89
  return new Response(null, {
78
90
  status: 302,
79
91
  headers: { Location: opts.onSuccess },
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/client/index.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,aAAa,EAAE,MAAM,YAAY,CAAC;AAE3C,OAAO,EAAE,iBAAiB,EAAE,MAAM,eAAe,CAAC;AAElD,MAAM,QAAQ,GAAG,yCAAyC,CAAC;AAI3D,MAAM,OAAO,MAAM;IACE;IAAnB,YAAmB,SAA0B;QAA1B,cAAS,GAAT,SAAS,CAAiB;IAAG,CAAC;IAEjD,mBAAmB,CAAC,IAGnB;QACC,MAAM,WAAW,GAAG,GAAG,IAAI,CAAC,OAAO,GAAG,aAAa,EAAE,CAAC;QACtD,MAAM,MAAM,GAAG,IAAI,eAAe,CAAC;YACjC,aAAa,EAAE,MAAM;YACrB,SAAS,EAAE,IAAI,CAAC,QAAQ;YACxB,YAAY,EAAE,WAAW;SAC1B,CAAC,CAAC;QACH,OAAO,GAAG,QAAQ,yCAAyC,MAAM,CAAC,QAAQ,EAAE,EAAE,CAAC;IACjF,CAAC;IAED,KAAK,CAAC,gBAAgB,CACpB,GAAc,EACd,IAKC;QAED,MAAM,WAAW,GAAG,GAAG,IAAI,CAAC,OAAO,GAAG,aAAa,EAAE,CAAC;QACtD,MAAM,GAAG,CAAC,SAAS,CAAC,IAAI,CAAC,SAAS,CAAC,MAAM,CAAC,gBAAgB,EAAE;YAC1D,QAAQ,EAAE,IAAI,CAAC,QAAQ;YACvB,YAAY,EAAE,IAAI,CAAC,YAAY;YAC/B,IAAI,EAAE,IAAI,CAAC,IAAI;YACf,WAAW;SACZ,CAAC,CAAC;IACL,CAAC;IAED,KAAK,CAAC,iBAAiB,CACrB,GAAc,EACd,IAKC;QAED,OAAO,MAAM,GAAG,CAAC,SAAS,CAAC,IAAI,CAAC,SAAS,CAAC,MAAM,CAAC,iBAAiB,EAAE;YAClE,QAAQ,EAAE,IAAI,CAAC,QAAQ;YACvB,YAAY,EAAE,IAAI,CAAC,YAAY;YAC/B,WAAW,EAAE,IAAI,CAAC,WAAW;YAC7B,SAAS,EAAE,IAAI,CAAC,SAAS;SAC1B,CAAC,CAAC;IACL,CAAC;IAED,KAAK,CAAC,eAAe,CACnB,GAAa,EACb,IAAyB;QAEzB,OAAO,MAAM,GAAG,CAAC,QAAQ,CAAC,IAAI,CAAC,SAAS,CAAC,MAAM,CAAC,eAAe,EAAE;YAC/D,KAAK,EAAE,IAAI,EAAE,KAAK;SACnB,CAAC,CAAC;IACL,CAAC;IAED,KAAK,CAAC,aAAa,CAAC,GAAa;QAC/B,OAAO,MAAM,GAAG,CAAC,QAAQ,CAAC,IAAI,CAAC,SAAS,CAAC,MAAM,CAAC,aAAa,EAAE,EAAE,CAAC,CAAC;IACrE,CAAC;CACF;AAED,MAAM,UAAU,cAAc,CAC5B,IAAgB,EAChB,SAA0B,EAC1B,IAIC;IAED,MAAM,OAAO,GAAG,EAAE,CAAC;IACnB,IAAI,CAAC,IAAI,CAAC,QAAQ;QAAE,OAAO,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC;IAC7C,IAAI,CAAC,IAAI,CAAC,YAAY;QAAE,OAAO,CAAC,IAAI,CAAC,cAAc,CAAC,CAAC;IACrD,IAAI,OAAO,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QACvB,MAAM,IAAI,KAAK,CACb,6CAA6C,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI;YACjE,oFAAoF,CACvF,CAAC;IACJ,CAAC;IAED,IAAI,CAAC,KAAK,CAAC;QACT,IAAI,EAAE,aAAa;QACnB,MAAM,EAAE,KAAK;QACb,OAAO,EAAE,iBAAiB,CAAC,KAAK,EAAE,GAAG,EAAE,OAAO,EAAE,EAAE;YAChD,MAAM,GAAG,GAAG,IAAI,GAAG,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC;YACjC,MAAM,IAAI,GAAG,GAAG,CAAC,YAAY,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC;YAE1C,IAAI,CAAC,IAAI,EAAE,CAAC;gBACV,OAAO,IAAI,QAAQ,CAAC,4BAA4B,EAAE,EAAE,MAAM,EAAE,GAAG,EAAE,CAAC,CAAC;YACrE,CAAC;YAED,MAAM,WAAW,GAAG,GAAG,GAAG,CAAC,MAAM,GAAG,GAAG,CAAC,QAAQ,EAAE,CAAC;YAEnD,IAAI,CAAC;gBACH,MAAM,GAAG,CAAC,SAAS,CAAC,SAAS,CAAC,MAAM,CAAC,gBAAgB,EAAE;oBACrD,QAAQ,EAAE,IAAI,CAAC,QAAQ;oBACvB,YAAY,EAAE,IAAI,CAAC,YAAY;oBAC/B,IAAI;oBACJ,WAAW;iBACZ,CAAC,CAAC;YACL,CAAC;YAAC,OAAO,KAAK,EAAE,CAAC;gBACf,MAAM,OAAO,GAAG,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,eAAe,CAAC;gBACzE,OAAO,IAAI,QAAQ,CAAC,0BAA0B,OAAO,EAAE,EAAE,EAAE,MAAM,EAAE,GAAG,EAAE,CAAC,CAAC;YAC5E,CAAC;YAED,IAAI,IAAI,CAAC,SAAS,EAAE,CAAC;gBACnB,OAAO,IAAI,QAAQ,CAAC,IAAI,EAAE;oBACxB,MAAM,EAAE,GAAG;oBACX,OAAO,EAAE,EAAE,QAAQ,EAAE,IAAI,CAAC,SAAS,EAAE;iBACtC,CAAC,CAAC;YACL,CAAC;YAED,OAAO,IAAI,QAAQ,CAAC,mCAAmC,EAAE,EAAE,MAAM,EAAE,GAAG,EAAE,CAAC,CAAC;QAC5E,CAAC,CAAC;KACH,CAAC,CAAC;AACL,CAAC;AAED,OAAO,EAAE,aAAa,EAAE,MAAM,YAAY,CAAC"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/client/index.ts"],"names":[],"mappings":"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"}
@@ -57,22 +57,21 @@ export type ComponentApi<Name extends string | undefined = string | undefined> =
57
57
  };
58
58
  public: {
59
59
  exchangeAuthCode: FunctionReference<"action", "internal", {
60
- clientId: string;
61
- clientSecret: string;
62
60
  code: string;
63
61
  redirectUri: string;
64
62
  }, any, Name>;
63
+ fetchJobPosting: FunctionReference<"action", "internal", {
64
+ upworkId: string;
65
+ }, any, Name>;
65
66
  getAuthStatus: FunctionReference<"query", "internal", {}, any, Name>;
67
+ getJobPosting: FunctionReference<"query", "internal", {
68
+ upworkId: string;
69
+ }, any, Name>;
66
70
  listJobPostings: FunctionReference<"query", "internal", {
67
71
  limit?: number;
68
72
  }, any, Name>;
69
- refreshAccessToken: FunctionReference<"action", "internal", {
70
- clientId: string;
71
- clientSecret: string;
72
- }, any, Name>;
73
+ refreshAccessToken: FunctionReference<"action", "internal", {}, any, Name>;
73
74
  searchJobPostings: FunctionReference<"action", "internal", {
74
- clientId: string;
75
- clientSecret: string;
76
75
  searchQuery?: string;
77
76
  sortField?: string;
78
77
  }, any, Name>;
@@ -1 +1 @@
1
- {"version":3,"file":"component.d.ts","sourceRoot":"","sources":["../../../src/component/_generated/component.ts"],"names":[],"mappings":"AACA;;;;;;;GAOG;AAEH,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,eAAe,CAAC;AAEvD;;;;;;;;;;GAUG;AACH,MAAM,MAAM,YAAY,CAAC,IAAI,SAAS,MAAM,GAAG,SAAS,GAAG,MAAM,GAAG,SAAS,IAC3E;IACE,OAAO,EAAE;QACP,SAAS,EAAE,iBAAiB,CAC1B,OAAO,EACP,UAAU,EACV,EAAE,EACF;YACE,aAAa,EAAE,MAAM,CAAC;YACtB,GAAG,EAAE,MAAM,CAAC;YACZ,WAAW,EAAE,MAAM,CAAC;YACpB,SAAS,EAAE,MAAM,CAAC;YAClB,YAAY,EAAE,MAAM,CAAC;YACrB,SAAS,EAAE,MAAM,CAAC;SACnB,GAAG,IAAI,EACR,IAAI,CACL,CAAC;QACF,WAAW,EAAE,iBAAiB,CAC5B,UAAU,EACV,UAAU,EACV;YACE,WAAW,EAAE,MAAM,CAAC;YACpB,SAAS,EAAE,MAAM,CAAC;YAClB,YAAY,EAAE,MAAM,CAAC;YACrB,SAAS,EAAE,MAAM,CAAC;SACnB,EACD,IAAI,EACJ,IAAI,CACL,CAAC;QACF,iBAAiB,EAAE,iBAAiB,CAClC,UAAU,EACV,UAAU,EACV;YACE,QAAQ,EAAE,KAAK,CAAC;gBACd,YAAY,CAAC,EAAE,MAAM,CAAC;gBACtB,cAAc,CAAC,EAAE,MAAM,CAAC;gBACxB,QAAQ,CAAC,EAAE,MAAM,CAAC;gBAClB,iBAAiB,CAAC,EAAE,MAAM,CAAC;gBAC3B,gBAAgB,CAAC,EAAE,MAAM,CAAC;gBAC1B,eAAe,EAAE,MAAM,CAAC;gBACxB,WAAW,EAAE,MAAM,CAAC;gBACpB,QAAQ,CAAC,EAAE,MAAM,CAAC;gBAClB,eAAe,EAAE,MAAM,CAAC;gBACxB,iBAAiB,EAAE,MAAM,CAAC;gBAC1B,MAAM,EAAE,KAAK,CAAC;oBAAE,IAAI,EAAE,MAAM,CAAA;iBAAE,CAAC,CAAC;gBAChC,WAAW,CAAC,EAAE,MAAM,CAAC;gBACrB,KAAK,EAAE,MAAM,CAAC;gBACd,QAAQ,EAAE,MAAM,CAAC;aAClB,CAAC,CAAC;SACJ,EACD,IAAI,EACJ,IAAI,CACL,CAAC;KACH,CAAC;IACF,MAAM,EAAE;QACN,gBAAgB,EAAE,iBAAiB,CACjC,QAAQ,EACR,UAAU,EACV;YACE,QAAQ,EAAE,MAAM,CAAC;YACjB,YAAY,EAAE,MAAM,CAAC;YACrB,IAAI,EAAE,MAAM,CAAC;YACb,WAAW,EAAE,MAAM,CAAC;SACrB,EACD,GAAG,EACH,IAAI,CACL,CAAC;QACF,aAAa,EAAE,iBAAiB,CAAC,OAAO,EAAE,UAAU,EAAE,EAAE,EAAE,GAAG,EAAE,IAAI,CAAC,CAAC;QACrE,eAAe,EAAE,iBAAiB,CAChC,OAAO,EACP,UAAU,EACV;YAAE,KAAK,CAAC,EAAE,MAAM,CAAA;SAAE,EAClB,GAAG,EACH,IAAI,CACL,CAAC;QACF,kBAAkB,EAAE,iBAAiB,CACnC,QAAQ,EACR,UAAU,EACV;YAAE,QAAQ,EAAE,MAAM,CAAC;YAAC,YAAY,EAAE,MAAM,CAAA;SAAE,EAC1C,GAAG,EACH,IAAI,CACL,CAAC;QACF,iBAAiB,EAAE,iBAAiB,CAClC,QAAQ,EACR,UAAU,EACV;YACE,QAAQ,EAAE,MAAM,CAAC;YACjB,YAAY,EAAE,MAAM,CAAC;YACrB,WAAW,CAAC,EAAE,MAAM,CAAC;YACrB,SAAS,CAAC,EAAE,MAAM,CAAC;SACpB,EACD,GAAG,EACH,IAAI,CACL,CAAC;KACH,CAAC;CACH,CAAC"}
1
+ {"version":3,"file":"component.d.ts","sourceRoot":"","sources":["../../../src/component/_generated/component.ts"],"names":[],"mappings":"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"}
@@ -21,22 +21,41 @@ type SearchResult = {
21
21
  postings: JobPostingRow[];
22
22
  hasNextPage: boolean;
23
23
  };
24
- export declare const refreshAccessToken: import("convex/server").RegisteredAction<"public", {
25
- clientId: string;
26
- clientSecret: string;
27
- }, Promise<void>>;
24
+ export declare const refreshAccessToken: import("convex/server").RegisteredAction<"public", {}, Promise<void>>;
28
25
  export declare const exchangeAuthCode: import("convex/server").RegisteredAction<"public", {
29
- clientId: string;
30
- clientSecret: string;
31
26
  code: string;
32
27
  redirectUri: string;
33
28
  }, Promise<void>>;
34
29
  export declare const searchJobPostings: import("convex/server").RegisteredAction<"public", {
35
30
  searchQuery?: string | undefined;
36
31
  sortField?: string | undefined;
37
- clientId: string;
38
- clientSecret: string;
39
32
  }, Promise<SearchResult>>;
33
+ export declare const getJobPosting: import("convex/server").RegisteredQuery<"public", {
34
+ upworkId: string;
35
+ }, Promise<{
36
+ _id: import("convex/values").GenericId<"jobPostings">;
37
+ _creationTime: number;
38
+ category?: string | undefined;
39
+ subcategory?: string | undefined;
40
+ duration?: string | undefined;
41
+ budgetAmount?: string | undefined;
42
+ budgetCurrency?: string | undefined;
43
+ clientTotalHires?: number | undefined;
44
+ clientCompanyName?: string | undefined;
45
+ upworkId: string;
46
+ title: string;
47
+ description: string;
48
+ skills: {
49
+ name: string;
50
+ }[];
51
+ experienceLevel: string;
52
+ createdDateTime: string;
53
+ publishedDateTime: string;
54
+ cachedAt: number;
55
+ } | null>>;
56
+ export declare const fetchJobPosting: import("convex/server").RegisteredAction<"public", {
57
+ upworkId: string;
58
+ }, Promise<JobPostingRow | null>>;
40
59
  export declare const listJobPostings: import("convex/server").RegisteredQuery<"public", {
41
60
  limit?: number | undefined;
42
61
  }, Promise<{
@@ -1 +1 @@
1
- {"version":3,"file":"public.d.ts","sourceRoot":"","sources":["../../src/component/public.ts"],"names":[],"mappings":"AA0DA,KAAK,aAAa,GAAG;IACnB,QAAQ,EAAE,MAAM,CAAC;IACjB,KAAK,EAAE,MAAM,CAAC;IACd,WAAW,EAAE,MAAM,CAAC;IACpB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,MAAM,EAAE,KAAK,CAAC;QAAE,IAAI,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;IAChC,eAAe,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,eAAe,EAAE,MAAM,CAAC;IACxB,iBAAiB,EAAE,MAAM,CAAC;IAC1B,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,iBAAiB,CAAC,EAAE,MAAM,CAAC;CAC5B,CAAC;AAEF,KAAK,YAAY,GAAG;IAClB,UAAU,EAAE,MAAM,CAAC;IACnB,QAAQ,EAAE,aAAa,EAAE,CAAC;IAC1B,WAAW,EAAE,OAAO,CAAC;CACtB,CAAC;AAEF,eAAO,MAAM,kBAAkB;;;iBA0C7B,CAAC;AAEH,eAAO,MAAM,gBAAgB;;;;;iBA+C3B,CAAC;AAEH,eAAO,MAAM,iBAAiB;;;;;yBA0G5B,CAAC;AAIH,eAAO,MAAM,eAAe;;;;;;;;;;;;;;;;;;;;;;KAc1B,CAAC;AAEH,eAAO,MAAM,aAAa,0GAYxB,CAAC"}
1
+ {"version":3,"file":"public.d.ts","sourceRoot":"","sources":["../../src/component/public.ts"],"names":[],"mappings":"AAqEA,KAAK,aAAa,GAAG;IACnB,QAAQ,EAAE,MAAM,CAAC;IACjB,KAAK,EAAE,MAAM,CAAC;IACd,WAAW,EAAE,MAAM,CAAC;IACpB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,MAAM,EAAE,KAAK,CAAC;QAAE,IAAI,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;IAChC,eAAe,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,eAAe,EAAE,MAAM,CAAC;IACxB,iBAAiB,EAAE,MAAM,CAAC;IAC1B,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,iBAAiB,CAAC,EAAE,MAAM,CAAC;CAC5B,CAAC;AAEF,KAAK,YAAY,GAAG;IAClB,UAAU,EAAE,MAAM,CAAC;IACnB,QAAQ,EAAE,aAAa,EAAE,CAAC;IAC1B,WAAW,EAAE,OAAO,CAAC;CACtB,CAAC;AAEF,eAAO,MAAM,kBAAkB,uEAwC7B,CAAC;AAEH,eAAO,MAAM,gBAAgB;;;iBA8C3B,CAAC;AAEH,eAAO,MAAM,iBAAiB;;;yBAqG5B,CAAC;AAEH,eAAO,MAAM,aAAa;;;;;;;;;;;;;;;;;;;;;;UAUxB,CAAC;AA0BH,eAAO,MAAM,eAAe;;iCAgF1B,CAAC;AAIH,eAAO,MAAM,eAAe;;;;;;;;;;;;;;;;;;;;;;KAc1B,CAAC;AAEH,eAAO,MAAM,aAAa,0GAYxB,CAAC"}
@@ -2,6 +2,14 @@ import { query, action } from "./_generated/server.js";
2
2
  import { api } from "./_generated/api.js";
3
3
  import { v } from "convex/values";
4
4
  const BASE_URL = "https://upwork-mock-server.onrender.com";
5
+ function getCredentials() {
6
+ const clientId = process.env.UPWORK_CLIENT_ID;
7
+ const clientSecret = process.env.UPWORK_CLIENT_SECRET;
8
+ if (!clientId || !clientSecret) {
9
+ throw new Error("Missing UPWORK_CLIENT_ID or UPWORK_CLIENT_SECRET environment variables.");
10
+ }
11
+ return { clientId, clientSecret };
12
+ }
5
13
  function buildJobPostingsQuery(opts) {
6
14
  const filterArg = opts.searchQuery
7
15
  ? `marketPlaceJobFilter: $marketPlaceJobFilter,`
@@ -46,11 +54,9 @@ query ${variableDefs} {
46
54
  return { query, variables };
47
55
  }
48
56
  export const refreshAccessToken = action({
49
- args: {
50
- clientId: v.string(),
51
- clientSecret: v.string(),
52
- },
53
- handler: async (ctx, args) => {
57
+ args: {},
58
+ handler: async (ctx) => {
59
+ const { clientId, clientSecret } = getCredentials();
54
60
  const tokens = await ctx.runQuery(api.private.getTokens, {});
55
61
  if (!tokens) {
56
62
  throw new Error("No stored tokens found. Complete OAuth authorization first.");
@@ -64,8 +70,8 @@ export const refreshAccessToken = action({
64
70
  },
65
71
  body: new URLSearchParams({
66
72
  grant_type: "refresh_token",
67
- client_id: args.clientId,
68
- client_secret: args.clientSecret,
73
+ client_id: clientId,
74
+ client_secret: clientSecret,
69
75
  refresh_token: tokens.refreshToken,
70
76
  }).toString(),
71
77
  });
@@ -85,23 +91,22 @@ export const refreshAccessToken = action({
85
91
  });
86
92
  export const exchangeAuthCode = action({
87
93
  args: {
88
- clientId: v.string(),
89
- clientSecret: v.string(),
90
94
  code: v.string(),
91
95
  redirectUri: v.string(),
92
96
  },
93
97
  handler: async (ctx, args) => {
98
+ const { clientId, clientSecret } = getCredentials();
94
99
  const tokenUrl = `${BASE_URL}/api/v3/oauth2/token`;
95
100
  const body = new URLSearchParams({
96
101
  grant_type: "authorization_code",
97
- client_id: args.clientId,
98
- client_secret: args.clientSecret,
102
+ client_id: clientId,
103
+ client_secret: clientSecret,
99
104
  code: args.code,
100
105
  redirect_uri: args.redirectUri,
101
106
  }).toString();
102
107
  console.log("[exchangeAuthCode] POST", tokenUrl);
103
108
  console.log("[exchangeAuthCode] redirect_uri:", args.redirectUri);
104
- console.log("[exchangeAuthCode] client_id:", args.clientId);
109
+ console.log("[exchangeAuthCode] client_id:", clientId);
105
110
  console.log("[exchangeAuthCode] code:", args.code);
106
111
  const response = await fetch(tokenUrl, {
107
112
  method: "POST",
@@ -127,8 +132,6 @@ export const exchangeAuthCode = action({
127
132
  });
128
133
  export const searchJobPostings = action({
129
134
  args: {
130
- clientId: v.string(),
131
- clientSecret: v.string(),
132
135
  searchQuery: v.optional(v.string()),
133
136
  sortField: v.optional(v.string()),
134
137
  },
@@ -138,10 +141,7 @@ export const searchJobPostings = action({
138
141
  throw new Error("Not connected to Upwork. Complete OAuth authorization first.");
139
142
  }
140
143
  if (tokens.expiresAt < Date.now()) {
141
- await ctx.runAction(api.public.refreshAccessToken, {
142
- clientId: args.clientId,
143
- clientSecret: args.clientSecret,
144
- });
144
+ await ctx.runAction(api.public.refreshAccessToken, {});
145
145
  tokens = await ctx.runQuery(api.private.getTokens, {});
146
146
  if (!tokens) {
147
147
  throw new Error("Failed to refresh token.");
@@ -206,6 +206,97 @@ export const searchJobPostings = action({
206
206
  };
207
207
  },
208
208
  });
209
+ export const getJobPosting = query({
210
+ args: { upworkId: v.string() },
211
+ handler: async (ctx, args) => {
212
+ return ((await ctx.db
213
+ .query("jobPostings")
214
+ .withIndex("byUpworkId", (q) => q.eq("upworkId", args.upworkId))
215
+ .first()) ?? null);
216
+ },
217
+ });
218
+ function buildJobPostingQuery(upworkId) {
219
+ const query = `
220
+ query($id: ID!) {
221
+ marketplaceJobPosting(id: $id) {
222
+ id
223
+ title
224
+ description
225
+ category { id name }
226
+ subcategory { id name }
227
+ skills { name }
228
+ experienceLevel
229
+ duration
230
+ budget { amount currency }
231
+ createdDateTime
232
+ publishedDateTime
233
+ client { totalHires companyName }
234
+ }
235
+ }`;
236
+ return { query, variables: { id: upworkId } };
237
+ }
238
+ export const fetchJobPosting = action({
239
+ args: {
240
+ upworkId: v.string(),
241
+ },
242
+ handler: async (ctx, args) => {
243
+ let tokens = await ctx.runQuery(api.private.getTokens, {});
244
+ if (!tokens) {
245
+ throw new Error("Not connected to Upwork. Complete OAuth authorization first.");
246
+ }
247
+ if (tokens.expiresAt < Date.now()) {
248
+ await ctx.runAction(api.public.refreshAccessToken, {});
249
+ tokens = await ctx.runQuery(api.private.getTokens, {});
250
+ if (!tokens) {
251
+ throw new Error("Failed to refresh token.");
252
+ }
253
+ }
254
+ const graphqlUrl = `${BASE_URL}/graphql`;
255
+ const { query, variables } = buildJobPostingQuery(args.upworkId);
256
+ const response = await fetch(graphqlUrl, {
257
+ method: "POST",
258
+ headers: {
259
+ "Content-Type": "application/json",
260
+ Authorization: `Bearer ${tokens.accessToken}`,
261
+ },
262
+ body: JSON.stringify({ query, variables }),
263
+ });
264
+ if (!response.ok) {
265
+ const text = await response.text();
266
+ throw new Error(`Upwork GraphQL request failed (${response.status}): ${text}`);
267
+ }
268
+ const result = await response.json();
269
+ if (result.errors?.length) {
270
+ throw new Error(`Upwork GraphQL errors: ${result.errors.map((e) => e.message).join(", ")}`);
271
+ }
272
+ const node = result.data?.marketplaceJobPosting;
273
+ if (!node)
274
+ return null;
275
+ const budget = node.budget;
276
+ const client = node.client;
277
+ const category = node.category;
278
+ const subcategory = node.subcategory;
279
+ const skills = node.skills ?? [];
280
+ const posting = {
281
+ upworkId: String(node.id),
282
+ title: String(node.title ?? ""),
283
+ description: String(node.description ?? ""),
284
+ category: category?.name ?? undefined,
285
+ subcategory: subcategory?.name ?? undefined,
286
+ skills: skills.map((s) => ({ name: s.name })),
287
+ experienceLevel: String(node.experienceLevel ?? ""),
288
+ duration: node.duration != null ? String(node.duration) : undefined,
289
+ budgetAmount: budget?.amount != null ? String(budget.amount) : undefined,
290
+ budgetCurrency: budget?.currency != null ? String(budget.currency) : undefined,
291
+ createdDateTime: String(node.createdDateTime ?? ""),
292
+ publishedDateTime: String(node.publishedDateTime ?? ""),
293
+ clientTotalHires: client?.totalHires ?? undefined,
294
+ clientCompanyName: client?.companyName ?? undefined,
295
+ };
296
+ await ctx.runMutation(api.private.upsertJobPostings, { postings: [posting] });
297
+ return posting;
298
+ },
299
+ });
209
300
  const TWENTY_THREE_HOURS_MS = 23 * 60 * 60 * 1000;
210
301
  export const listJobPostings = query({
211
302
  args: {
@@ -1 +1 @@
1
- {"version":3,"file":"public.js","sourceRoot":"","sources":["../../src/component/public.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"public.js","sourceRoot":"","sources":["../../src/component/public.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -6,7 +6,7 @@
6
6
  "bugs": {
7
7
  "url": "https://github.com/NativeSquare/upwork/issues"
8
8
  },
9
- "version": "0.1.1",
9
+ "version": "0.2.1",
10
10
  "license": "Apache-2.0",
11
11
  "keywords": [
12
12
  "convex",
@@ -11,14 +11,19 @@ export type UpworkComponent = ComponentApi;
11
11
  export class Upwork {
12
12
  constructor(public component: UpworkComponent) {}
13
13
 
14
- getAuthorizationUrl(opts: {
15
- clientId: string;
16
- siteUrl: string;
17
- }): string {
18
- const redirectUri = `${opts.siteUrl}${CALLBACK_PATH}`;
14
+ getAuthorizationUrl(): string {
15
+ const clientId = process.env.UPWORK_CLIENT_ID;
16
+ const siteUrl = process.env.CONVEX_SITE_URL;
17
+ if (!clientId) {
18
+ throw new Error("Missing UPWORK_CLIENT_ID environment variable.");
19
+ }
20
+ if (!siteUrl) {
21
+ throw new Error("Missing CONVEX_SITE_URL environment variable.");
22
+ }
23
+ const redirectUri = `${siteUrl}${CALLBACK_PATH}`;
19
24
  const params = new URLSearchParams({
20
25
  response_type: "code",
21
- client_id: opts.clientId,
26
+ client_id: clientId,
22
27
  redirect_uri: redirectUri,
23
28
  });
24
29
  return `${BASE_URL}/ab/account-security/oauth2/authorize?${params.toString()}`;
@@ -26,17 +31,14 @@ export class Upwork {
26
31
 
27
32
  async exchangeAuthCode(
28
33
  ctx: ActionCtx,
29
- opts: {
30
- clientId: string;
31
- clientSecret: string;
32
- code: string;
33
- siteUrl: string;
34
- },
34
+ opts: { code: string },
35
35
  ): Promise<void> {
36
- const redirectUri = `${opts.siteUrl}${CALLBACK_PATH}`;
36
+ const siteUrl = process.env.CONVEX_SITE_URL;
37
+ if (!siteUrl) {
38
+ throw new Error("Missing CONVEX_SITE_URL environment variable.");
39
+ }
40
+ const redirectUri = `${siteUrl}${CALLBACK_PATH}`;
37
41
  await ctx.runAction(this.component.public.exchangeAuthCode, {
38
- clientId: opts.clientId,
39
- clientSecret: opts.clientSecret,
40
42
  code: opts.code,
41
43
  redirectUri,
42
44
  });
@@ -44,18 +46,32 @@ export class Upwork {
44
46
 
45
47
  async searchJobPostings(
46
48
  ctx: ActionCtx,
47
- opts: {
48
- clientId: string;
49
- clientSecret: string;
49
+ opts?: {
50
50
  searchQuery?: string;
51
51
  sortField?: string;
52
52
  },
53
53
  ): Promise<SearchResult> {
54
54
  return await ctx.runAction(this.component.public.searchJobPostings, {
55
- clientId: opts.clientId,
56
- clientSecret: opts.clientSecret,
57
- searchQuery: opts.searchQuery,
58
- sortField: opts.sortField,
55
+ searchQuery: opts?.searchQuery,
56
+ sortField: opts?.sortField,
57
+ });
58
+ }
59
+
60
+ async getJobPosting(
61
+ ctx: QueryCtx,
62
+ opts: { upworkId: string },
63
+ ): Promise<JobPosting | null> {
64
+ return await ctx.runQuery(this.component.public.getJobPosting, {
65
+ upworkId: opts.upworkId,
66
+ });
67
+ }
68
+
69
+ async fetchJobPosting(
70
+ ctx: ActionCtx,
71
+ opts: { upworkId: string },
72
+ ): Promise<JobPosting | null> {
73
+ return await ctx.runAction(this.component.public.fetchJobPosting, {
74
+ upworkId: opts.upworkId,
59
75
  });
60
76
  }
61
77
 
@@ -76,22 +92,10 @@ export class Upwork {
76
92
  export function registerRoutes(
77
93
  http: HttpRouter,
78
94
  component: UpworkComponent,
79
- opts: {
80
- clientId: string;
81
- clientSecret: string;
95
+ opts?: {
82
96
  onSuccess?: string;
83
97
  },
84
98
  ) {
85
- const missing = [];
86
- if (!opts.clientId) missing.push("clientId");
87
- if (!opts.clientSecret) missing.push("clientSecret");
88
- if (missing.length > 0) {
89
- throw new Error(
90
- `registerRoutes: missing required options: ${missing.join(", ")}. ` +
91
- `Make sure UPWORK_CLIENT_ID and UPWORK_CLIENT_SECRET environment variables are set.`,
92
- );
93
- }
94
-
95
99
  http.route({
96
100
  path: CALLBACK_PATH,
97
101
  method: "GET",
@@ -107,8 +111,6 @@ export function registerRoutes(
107
111
 
108
112
  try {
109
113
  await ctx.runAction(component.public.exchangeAuthCode, {
110
- clientId: opts.clientId,
111
- clientSecret: opts.clientSecret,
112
114
  code,
113
115
  redirectUri,
114
116
  });
@@ -117,7 +119,7 @@ export function registerRoutes(
117
119
  return new Response(`OAuth callback failed: ${message}`, { status: 500 });
118
120
  }
119
121
 
120
- if (opts.onSuccess) {
122
+ if (opts?.onSuccess) {
121
123
  return new Response(null, {
122
124
  status: 302,
123
125
  headers: { Location: opts.onSuccess },
@@ -79,16 +79,25 @@ export type ComponentApi<Name extends string | undefined = string | undefined> =
79
79
  exchangeAuthCode: FunctionReference<
80
80
  "action",
81
81
  "internal",
82
- {
83
- clientId: string;
84
- clientSecret: string;
85
- code: string;
86
- redirectUri: string;
87
- },
82
+ { code: string; redirectUri: string },
83
+ any,
84
+ Name
85
+ >;
86
+ fetchJobPosting: FunctionReference<
87
+ "action",
88
+ "internal",
89
+ { upworkId: string },
88
90
  any,
89
91
  Name
90
92
  >;
91
93
  getAuthStatus: FunctionReference<"query", "internal", {}, any, Name>;
94
+ getJobPosting: FunctionReference<
95
+ "query",
96
+ "internal",
97
+ { upworkId: string },
98
+ any,
99
+ Name
100
+ >;
92
101
  listJobPostings: FunctionReference<
93
102
  "query",
94
103
  "internal",
@@ -99,19 +108,14 @@ export type ComponentApi<Name extends string | undefined = string | undefined> =
99
108
  refreshAccessToken: FunctionReference<
100
109
  "action",
101
110
  "internal",
102
- { clientId: string; clientSecret: string },
111
+ {},
103
112
  any,
104
113
  Name
105
114
  >;
106
115
  searchJobPostings: FunctionReference<
107
116
  "action",
108
117
  "internal",
109
- {
110
- clientId: string;
111
- clientSecret: string;
112
- searchQuery?: string;
113
- sortField?: string;
114
- },
118
+ { searchQuery?: string; sortField?: string },
115
119
  any,
116
120
  Name
117
121
  >;
@@ -4,6 +4,17 @@ import { v } from "convex/values";
4
4
 
5
5
  const BASE_URL = "https://upwork-mock-server.onrender.com";
6
6
 
7
+ function getCredentials(): { clientId: string; clientSecret: string } {
8
+ const clientId = process.env.UPWORK_CLIENT_ID;
9
+ const clientSecret = process.env.UPWORK_CLIENT_SECRET;
10
+ if (!clientId || !clientSecret) {
11
+ throw new Error(
12
+ "Missing UPWORK_CLIENT_ID or UPWORK_CLIENT_SECRET environment variables.",
13
+ );
14
+ }
15
+ return { clientId, clientSecret };
16
+ }
17
+
7
18
  function buildJobPostingsQuery(opts: {
8
19
  searchQuery?: string;
9
20
  sortField?: string;
@@ -80,11 +91,9 @@ type SearchResult = {
80
91
  };
81
92
 
82
93
  export const refreshAccessToken = action({
83
- args: {
84
- clientId: v.string(),
85
- clientSecret: v.string(),
86
- },
87
- handler: async (ctx, args) => {
94
+ args: {},
95
+ handler: async (ctx) => {
96
+ const { clientId, clientSecret } = getCredentials();
88
97
  const tokens = await ctx.runQuery(api.private.getTokens, {});
89
98
  if (!tokens) {
90
99
  throw new Error("No stored tokens found. Complete OAuth authorization first.");
@@ -100,8 +109,8 @@ export const refreshAccessToken = action({
100
109
  },
101
110
  body: new URLSearchParams({
102
111
  grant_type: "refresh_token",
103
- client_id: args.clientId,
104
- client_secret: args.clientSecret,
112
+ client_id: clientId,
113
+ client_secret: clientSecret,
105
114
  refresh_token: tokens.refreshToken,
106
115
  }).toString(),
107
116
  });
@@ -125,25 +134,24 @@ export const refreshAccessToken = action({
125
134
 
126
135
  export const exchangeAuthCode = action({
127
136
  args: {
128
- clientId: v.string(),
129
- clientSecret: v.string(),
130
137
  code: v.string(),
131
138
  redirectUri: v.string(),
132
139
  },
133
140
  handler: async (ctx, args): Promise<void> => {
141
+ const { clientId, clientSecret } = getCredentials();
134
142
  const tokenUrl = `${BASE_URL}/api/v3/oauth2/token`;
135
143
 
136
144
  const body = new URLSearchParams({
137
145
  grant_type: "authorization_code",
138
- client_id: args.clientId,
139
- client_secret: args.clientSecret,
146
+ client_id: clientId,
147
+ client_secret: clientSecret,
140
148
  code: args.code,
141
149
  redirect_uri: args.redirectUri,
142
150
  }).toString();
143
151
 
144
152
  console.log("[exchangeAuthCode] POST", tokenUrl);
145
153
  console.log("[exchangeAuthCode] redirect_uri:", args.redirectUri);
146
- console.log("[exchangeAuthCode] client_id:", args.clientId);
154
+ console.log("[exchangeAuthCode] client_id:", clientId);
147
155
  console.log("[exchangeAuthCode] code:", args.code);
148
156
 
149
157
  const response = await fetch(tokenUrl, {
@@ -174,8 +182,6 @@ export const exchangeAuthCode = action({
174
182
 
175
183
  export const searchJobPostings = action({
176
184
  args: {
177
- clientId: v.string(),
178
- clientSecret: v.string(),
179
185
  searchQuery: v.optional(v.string()),
180
186
  sortField: v.optional(v.string()),
181
187
  },
@@ -186,10 +192,7 @@ export const searchJobPostings = action({
186
192
  }
187
193
 
188
194
  if (tokens.expiresAt < Date.now()) {
189
- await ctx.runAction(api.public.refreshAccessToken, {
190
- clientId: args.clientId,
191
- clientSecret: args.clientSecret,
192
- });
195
+ await ctx.runAction(api.public.refreshAccessToken, {});
193
196
  tokens = await ctx.runQuery(api.private.getTokens, {});
194
197
  if (!tokens) {
195
198
  throw new Error("Failed to refresh token.");
@@ -280,6 +283,124 @@ export const searchJobPostings = action({
280
283
  },
281
284
  });
282
285
 
286
+ export const getJobPosting = query({
287
+ args: { upworkId: v.string() },
288
+ handler: async (ctx, args) => {
289
+ return (
290
+ (await ctx.db
291
+ .query("jobPostings")
292
+ .withIndex("byUpworkId", (q) => q.eq("upworkId", args.upworkId))
293
+ .first()) ?? null
294
+ );
295
+ },
296
+ });
297
+
298
+ function buildJobPostingQuery(upworkId: string): {
299
+ query: string;
300
+ variables: Record<string, unknown>;
301
+ } {
302
+ const query = `
303
+ query($id: ID!) {
304
+ marketplaceJobPosting(id: $id) {
305
+ id
306
+ title
307
+ description
308
+ category { id name }
309
+ subcategory { id name }
310
+ skills { name }
311
+ experienceLevel
312
+ duration
313
+ budget { amount currency }
314
+ createdDateTime
315
+ publishedDateTime
316
+ client { totalHires companyName }
317
+ }
318
+ }`;
319
+ return { query, variables: { id: upworkId } };
320
+ }
321
+
322
+ export const fetchJobPosting = action({
323
+ args: {
324
+ upworkId: v.string(),
325
+ },
326
+ handler: async (ctx, args): Promise<JobPostingRow | null> => {
327
+ let tokens = await ctx.runQuery(api.private.getTokens, {});
328
+ if (!tokens) {
329
+ throw new Error("Not connected to Upwork. Complete OAuth authorization first.");
330
+ }
331
+
332
+ if (tokens.expiresAt < Date.now()) {
333
+ await ctx.runAction(api.public.refreshAccessToken, {});
334
+ tokens = await ctx.runQuery(api.private.getTokens, {});
335
+ if (!tokens) {
336
+ throw new Error("Failed to refresh token.");
337
+ }
338
+ }
339
+
340
+ const graphqlUrl = `${BASE_URL}/graphql`;
341
+
342
+ const { query, variables } = buildJobPostingQuery(args.upworkId);
343
+
344
+ const response = await fetch(graphqlUrl, {
345
+ method: "POST",
346
+ headers: {
347
+ "Content-Type": "application/json",
348
+ Authorization: `Bearer ${tokens.accessToken}`,
349
+ },
350
+ body: JSON.stringify({ query, variables }),
351
+ });
352
+
353
+ if (!response.ok) {
354
+ const text = await response.text();
355
+ throw new Error(`Upwork GraphQL request failed (${response.status}): ${text}`);
356
+ }
357
+
358
+ const result: {
359
+ data?: { marketplaceJobPosting?: Record<string, unknown> };
360
+ errors?: Array<{ message: string }>;
361
+ } = await response.json();
362
+
363
+ if (result.errors?.length) {
364
+ throw new Error(
365
+ `Upwork GraphQL errors: ${result.errors.map((e) => e.message).join(", ")}`,
366
+ );
367
+ }
368
+
369
+ const node = result.data?.marketplaceJobPosting;
370
+ if (!node) return null;
371
+
372
+ const budget = node.budget as { amount?: string; currency?: string } | null;
373
+ const client = node.client as {
374
+ totalHires?: number;
375
+ companyName?: string;
376
+ } | null;
377
+ const category = node.category as { name?: string } | null;
378
+ const subcategory = node.subcategory as { name?: string } | null;
379
+ const skills = (node.skills as Array<{ name: string }>) ?? [];
380
+
381
+ const posting: JobPostingRow = {
382
+ upworkId: String(node.id),
383
+ title: String(node.title ?? ""),
384
+ description: String(node.description ?? ""),
385
+ category: category?.name ?? undefined,
386
+ subcategory: subcategory?.name ?? undefined,
387
+ skills: skills.map((s) => ({ name: s.name })),
388
+ experienceLevel: String(node.experienceLevel ?? ""),
389
+ duration: node.duration != null ? String(node.duration) : undefined,
390
+ budgetAmount: budget?.amount != null ? String(budget.amount) : undefined,
391
+ budgetCurrency: budget?.currency != null ? String(budget.currency) : undefined,
392
+ createdDateTime: String(node.createdDateTime ?? ""),
393
+ publishedDateTime: String(node.publishedDateTime ?? ""),
394
+ clientTotalHires: client?.totalHires ?? undefined,
395
+ clientCompanyName: client?.companyName ?? undefined,
396
+ };
397
+
398
+ await ctx.runMutation(api.private.upsertJobPostings, { postings: [posting] });
399
+
400
+ return posting;
401
+ },
402
+ });
403
+
283
404
  const TWENTY_THREE_HOURS_MS = 23 * 60 * 60 * 1000;
284
405
 
285
406
  export const listJobPostings = query({