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
feat: list and ability to delete psa request created though deleted tokens #2054
Changes from 11 commits
8f2fd9c
5ea75cc
1e1dfa5
7346261
17abd51
b9a461d
0896777
96b4e34
1f6e6b0
708d330
fc730b0
c1c699a
413e567
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Large diffs are not rendered by default.
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,7 +1,7 @@ | ||
import * as JWT from './utils/jwt.js' | ||
import { JSONResponse, notFound } from './utils/json-response.js' | ||
import { JWT_ISSUER } from './constants.js' | ||
import { HTTPError, RangeNotSatisfiableError } from './errors.js' | ||
import { HTTPError, PSAErrorInvalidData, PSAErrorRequiredData, PSAErrorResourceNotFound, RangeNotSatisfiableError } from './errors.js' | ||
import { getTagValue, hasPendingTagProposal, hasTag } from './utils/tags.js' | ||
import { | ||
NO_READ_OR_WRITE, | ||
|
@@ -11,7 +11,7 @@ import { | |
} from './maintenance.js' | ||
import { pagination } from './utils/pagination.js' | ||
import { toPinStatusResponse } from './pins.js' | ||
import { validateSearchParams } from './utils/psa.js' | ||
import { INVALID_REQUEST_ID, REQUIRED_REQUEST_ID, validateSearchParams } from './utils/psa.js' | ||
import { magicLinkBypassForE2ETestingInTestmode } from './magic.link.js' | ||
import { CustomerNotFound, getPaymentSettings, initializeBillingForNewUser, isStoragePriceName, savePaymentSettings } from './utils/billing.js' | ||
|
||
|
@@ -315,7 +315,6 @@ export async function userRequestPost (request, env) { | |
* @param {import('./env').Env} env | ||
*/ | ||
export async function userTokensGet (request, env) { | ||
// @ts-ignore | ||
const tokens = await env.db.listKeys(request.auth.user._id) | ||
|
||
return new JSONResponse(tokens) | ||
|
@@ -514,8 +513,7 @@ export async function userPinsGet (request, env) { | |
throw psaParams.error | ||
} | ||
|
||
// @ts-ignore | ||
const tokens = (await env.db.listKeys(request.auth.user._id)).map((key) => key._id) | ||
const tokens = (await env.db.listKeys(request.auth.user._id, { includeDeleted: true })).map((key) => key._id) | ||
|
||
let pinRequests | ||
|
||
|
@@ -569,6 +567,42 @@ export async function userPinsGet (request, env) { | |
}, { headers }) | ||
} | ||
|
||
/** | ||
* List a user's pins regardless of the token used. | ||
* As we don't want to scope the Pinning Service API to users | ||
* we need a new endpoint as an umbrella. | ||
* | ||
* @param {import('./user').AuthenticatedRequest} request | ||
* @param {import('./env').Env} env | ||
* @param {import('./index').Ctx} ctx | ||
* @return {Promise<JSONResponse>} | ||
*/ | ||
export async function userPinDelete (request, env, ctx) { | ||
// @ts-ignore | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Why are we ignoring this? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Good catch! The issue here is that The challenge here is that some of the property definitions do not quite match with That said, I wonder if it's better to tackle this in its own ticket to gauge what the best approach here, and given it's a common pattern to use Possible fixes:
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Cool, yep that’s a great shout to address the wider issue in a separate ticket. |
||
const requestId = request.params?.requestId | ||
|
||
if (!requestId) { | ||
throw new PSAErrorRequiredData(REQUIRED_REQUEST_ID) | ||
} | ||
|
||
if (typeof requestId !== 'string') { | ||
throw new PSAErrorInvalidData(INVALID_REQUEST_ID) | ||
} | ||
|
||
// TODO: Improve me, it is un-optimal to get the tokens in a separate request to the db. | ||
const tokens = (await env.db.listKeys(request.auth.user._id, { includeDeleted: true })).map((key) => key._id) | ||
joshJarr marked this conversation as resolved.
Show resolved
Hide resolved
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Why not just add a new SQL function that deletes a pin request by user ID? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. TL;DR: to keep PSA APIs as performant as possible. Longer piece of rationalGetting the tokens first and then using them to get the pins is already an "accepted" pattern and used in I reckon we could actually achieve this without resorting to a DB function. We could just change the However, that would be another join, with the subsequent performance implication. A join that would be superfluous for "normal" pin list requests, given the PSA APIs are currently scoped by token. With the assumption that when it comes to PSA pin requests, the most common scenario is we receive calls from the "exposed" APIs ( my assumption is that 90% of the call are likely from On the plus side, this solution does not require a migration and therefore a dependency on you. While I know there are solutions that could maximize both, I did not consider the effort worth the shot especially in the context of leaving Postgres quite soon in the future. I think that once we get to the agreement the APIs should be scoped by user and not token, if required we could consider running a migration to de-normalise the Let me know what you think |
||
|
||
try { | ||
// Update deleted_at (and updated_at) timestamp for the pin request. | ||
await env.db.deletePsaPinRequest(requestId, tokens) | ||
} catch (e) { | ||
console.error(e) | ||
throw new PSAErrorResourceNotFound() | ||
} | ||
|
||
return new JSONResponse({}, { status: 202 }) | ||
} | ||
|
||
/** | ||
* @param {string} userProposalForm | ||
* @param {string} tagName | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,23 @@ | ||
DROP FUNCTION IF EXISTS user_auth_keys_list; | ||
|
||
CREATE OR REPLACE FUNCTION user_auth_keys_list(query_user_id BIGINT, include_deleted BOOLEAN default false) | ||
RETURNS TABLE | ||
( | ||
"id" text, | ||
"name" text, | ||
"secret" text, | ||
"created" timestamptz, | ||
"has_uploads" boolean | ||
) | ||
LANGUAGE sql | ||
AS | ||
$$ | ||
SELECT (ak.id)::TEXT AS id, | ||
ak.name AS name, | ||
ak.secret AS secret, | ||
ak.inserted_at AS created, | ||
EXISTS(SELECT 42 FROM upload u WHERE u.auth_key_id = ak.id) AS has_uploads | ||
FROM auth_key ak | ||
WHERE ak.user_id = query_user_id AND | ||
(include_deleted OR ak.deleted_at IS NULL) | ||
$$; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why not just add a SQL function that lists pin requests by user ID?