fix failure of re-subscribing rss feed

This commit is contained in:
Hongbo Wu 2023-10-20 11:05:58 +08:00
parent efb58f8604
commit 4e36a5809e
6 changed files with 100 additions and 79 deletions

View file

@ -2616,9 +2616,8 @@ export enum SubscribeErrorCode {
}
export type SubscribeInput = {
name?: InputMaybe<Scalars['String']>;
subscriptionType?: InputMaybe<SubscriptionType>;
url?: InputMaybe<Scalars['String']>;
url: Scalars['String'];
};
export type SubscribeResult = SubscribeError | SubscribeSuccess;

View file

@ -2058,9 +2058,8 @@ enum SubscribeErrorCode {
}
input SubscribeInput {
name: String
subscriptionType: SubscriptionType
url: String
url: String!
}
union SubscribeResult = SubscribeError | SubscribeSuccess

View file

@ -175,25 +175,8 @@ export const subscribeResolver = authorized<
SubscribeSuccessPartial,
SubscribeError,
MutationSubscribeArgs
>(async (_, { input }, { authTrx, uid, log }) => {
log.info('subscribeResolver')
>(async (_, { input }, { uid, log }) => {
try {
// find existing subscription
const subscription = await authTrx((t) =>
t.getRepository(Subscription).findOneBy({
url: input.url || undefined,
name: input.name || undefined,
user: { id: uid },
type: input.subscriptionType || SubscriptionType.Rss, // default to rss
})
)
if (subscription) {
return {
errorCodes: [SubscribeErrorCode.AlreadySubscribed],
}
}
analytics.track({
userId: uid,
event: 'subscribed',
@ -203,58 +186,83 @@ export const subscribeResolver = authorized<
},
})
// create new rss subscription
if (input.url) {
const MAX_RSS_SUBSCRIPTIONS = 150
// validate rss feed
const feed = await parser.parseURL(input.url)
// limit number of rss subscriptions to 50
const newSubscriptions = (await authTrx((t) =>
t.query(
`insert into omnivore.subscriptions (name, url, description, type, user_id, icon)
select $1, $2, $3, $4, $5, $6 from omnivore.subscriptions
where user_id = $5 and type = 'RSS' and status = 'ACTIVE'
having count(*) < $7
returning *;`,
[
feed.title,
input.url,
feed.description || null,
SubscriptionType.Rss,
uid,
feed.image?.url || null,
MAX_RSS_SUBSCRIPTIONS,
]
)
)) as Subscription[]
if (newSubscriptions.length === 0) {
// find existing subscription
const existingSubscription = await getRepository(Subscription).findOneBy({
url: input.url,
user: { id: uid },
type: SubscriptionType.Rss,
})
if (existingSubscription) {
if (existingSubscription.status === SubscriptionStatus.Active) {
return {
errorCodes: [SubscribeErrorCode.ExceededMaxSubscriptions],
errorCodes: [SubscribeErrorCode.AlreadySubscribed],
}
}
const newSubscription = newSubscriptions[0]
// re-subscribe
const updatedSubscription = await getRepository(Subscription).save({
...existingSubscription,
status: SubscriptionStatus.Active,
})
// create a cloud task to fetch rss feed item for the new subscription
// create a cloud task to fetch rss feed item for resub subscription
await enqueueRssFeedFetch({
userIds: [uid],
url: input.url,
subscriptionIds: [newSubscription.id],
subscriptionIds: [updatedSubscription.id],
scheduledDates: [new Date()], // fetch immediately
fetchedDates: [null],
checksums: [null],
fetchedDates: [updatedSubscription.lastFetchedAt || null],
checksums: [updatedSubscription.lastFetchedChecksum || null],
})
return {
subscriptions: [newSubscription],
subscriptions: [updatedSubscription],
}
}
log.info('missing url or name')
// create new rss subscription
const MAX_RSS_SUBSCRIPTIONS = 150
// validate rss feed
const feed = await parser.parseURL(input.url)
// limit number of rss subscriptions to 150
const results = (await getRepository(Subscription).query(
`insert into omnivore.subscriptions (name, url, description, type, user_id, icon)
select $1, $2, $3, $4, $5, $6 from omnivore.subscriptions
where user_id = $5 and type = 'RSS' and status = 'ACTIVE'
having count(*) < $7
returning *;`,
[
feed.title,
input.url,
feed.description || null,
SubscriptionType.Rss,
uid,
feed.image?.url || null,
MAX_RSS_SUBSCRIPTIONS,
]
)) as Subscription[]
if (results.length === 0) {
return {
errorCodes: [SubscribeErrorCode.ExceededMaxSubscriptions],
}
}
const newSubscription = results[0]
// create a cloud task to fetch rss feed item for the new subscription
await enqueueRssFeedFetch({
userIds: [uid],
url: input.url,
subscriptionIds: [newSubscription.id],
scheduledDates: [new Date()], // fetch immediately
fetchedDates: [null],
checksums: [null],
})
return {
errorCodes: [SubscribeErrorCode.BadRequest],
subscriptions: [newSubscription],
}
} catch (error) {
log.error('failed to subscribe', error)

View file

@ -2538,8 +2538,7 @@ const schema = gql`
}
input SubscribeInput {
url: String
name: String
url: String!
subscriptionType: SubscriptionType
}

View file

@ -205,15 +205,8 @@ export const createSubscription = async (
})
}
export const deleteSubscription = async (
id: string,
userId: string
): Promise<DeleteResult> => {
return authTrx(
(tx) => tx.getRepository(Subscription).delete(id),
undefined,
userId
)
export const deleteSubscription = async (id: string): Promise<DeleteResult> => {
return getRepository(Subscription).delete(id)
}
export const createRssSubscriptions = async (

View file

@ -397,21 +397,47 @@ describe('Subscriptions API', () => {
})
after(async () => {
await deleteSubscription(existingSubscription.id, user.id)
await deleteSubscription(existingSubscription.id)
})
it('returns an error', async () => {
const res = await graphqlRequest(
query,
authToken,
{ input: { url, subscriptionType } },
).expect(200)
const res = await graphqlRequest(query, authToken, {
input: { url, subscriptionType },
}).expect(200)
expect(res.body.data.subscribe.errorCodes).to.eql([
'ALREADY_SUBSCRIBED',
])
})
})
context('when the user unsubscribed the feed', () => {
let existingSubscription: Subscription
before(async () => {
existingSubscription = await createSubscription(
user.id,
'RSS Feed',
undefined,
SubscriptionStatus.Unsubscribed,
url,
subscriptionType,
url
)
})
after(async () => {
await deleteSubscription(existingSubscription.id)
})
it('re-subscribes the user', async () => {
const res = await graphqlRequest(query, authToken, {
input: { url, subscriptionType },
}).expect(200)
expect(res.body.data.subscribe.subscriptions).to.have.lengthOf(1)
expect(res.body.data.subscribe.subscriptions[0].id).to.be.a('string')
})
})
it('creates a rss subscription', async () => {
const res = await graphqlRequest(
query,
@ -422,10 +448,7 @@ describe('Subscriptions API', () => {
expect(res.body.data.subscribe.subscriptions[0].id).to.be.a('string')
// clean up
await deleteSubscription(
res.body.data.subscribe.subscriptions[0].id,
user.id
)
await deleteSubscription(res.body.data.subscribe.subscriptions[0].id)
})
})
})