From 3cdb320d4422c732d1e47e0b41a6e4f694ba0ebf Mon Sep 17 00:00:00 2001 From: Andrew Powers Date: Sat, 21 Sep 2024 00:13:36 -0700 Subject: [PATCH] feat: revert draft --- @fiction/site/endpoint.ts | 51 ++++++++- @fiction/site/plugin-builder/SiteEditor.vue | 15 ++- @fiction/site/site.ts | 2 +- @fiction/site/test/manageSite.unit.test.ts | 109 +++++++++++++++++++- @fiction/site/utils/site.ts | 2 +- 5 files changed, 168 insertions(+), 11 deletions(-) diff --git a/@fiction/site/endpoint.ts b/@fiction/site/endpoint.ts index fd20b31bf..e9d5704fc 100644 --- a/@fiction/site/endpoint.ts +++ b/@fiction/site/endpoint.ts @@ -394,6 +394,7 @@ export type ManageSiteRequestParams = | { _action: 'create', fields: Partial } | { _action: 'update', where: WhereSite, fields: Partial } | { _action: 'saveDraft', where: WhereSite, fields: Partial } + | { _action: 'revertDraft', where: WhereSite } | { _action: 'delete', where: WhereSite } | { _action: 'retrieve', where: WhereSite } @@ -421,6 +422,9 @@ export class ManageSite extends SitesQuery { case 'saveDraft': result = await this.saveDraft(params, meta) break + case 'revertDraft': + result = await this.revertDraft(params as ManageSiteParams & { _action: 'revertDraft' }, meta) + break case 'delete': result = await this.deleteSite(params as ManageSiteParams & { _action: 'delete' }, meta) break @@ -448,7 +452,7 @@ export class ManageSite extends SitesQuery { const prepped = this.settings.fictionDb.prep({ type: 'insert', fields: mergedFields, table: t.sites, meta }) const [site] = await this.settings.fictionDb.client().insert({ orgId, userId, ...prepped }).into(t.sites).onConflict('site_id').ignore().returning('*') - this.log.info('creating site', { data: { fields: prepped, site } }) + if (!site?.siteId) throw abort('site not created') @@ -513,7 +517,9 @@ export class ManageSite extends SitesQuery { const draft = (currentDrafts?.draft || {}) as TableSiteConfig - const keysToRemove = ['draft', 'draftHistory', 'siteId', 'userId', 'orgId'] + const draftPages = fields.pages || [] + + const keysToRemove = ['draft', 'draftHistory', 'siteId', 'userId', 'orgId', 'pages'] keysToRemove.forEach((key) => { delete fields[key as keyof typeof fields] @@ -528,16 +534,51 @@ export class ManageSite extends SitesQuery { .update({ draft: JSON.stringify(newDraft) }) .returning('*') - if (fields.pages && fields.pages.length) { - await this.updateSitePages({ siteId: site.siteId, orgId, fields: fields.pages, userId, scope: 'draft' }, meta) + if (draftPages && draftPages.length) { + await this.updateSitePages({ siteId: site.siteId, orgId, fields: draftPages, userId, scope: 'draft' }, meta) } const r = await this.retrieveSite({ _action: 'retrieve', scope: 'draft', caller: 'saveDraft', where }, meta) - this.log.info('saveDraft', { data: { where, fields, newDraft, site: r.data } }) return { status: 'success', data: r.data } } + private async revertDraft(params: ManageSiteParams & { _action: 'revertDraft' }, meta: EndpointMeta): Promise> { + const { where, orgId } = params + const db = this.settings.fictionDb.client() + + const selector = await this.getSiteSelector(where) + + // Revert site draft + const [updatedSite] = await db(t.sites) + .where({ orgId, ...selector }) + .update({ draft: '{}' }) + .returning('*') + + if (!updatedSite) { + throw abort('Site not found') + } + + // Revert page drafts + await db(t.pages) + .where({ siteId: updatedSite.siteId }) + .update({ draft: '{}' }) + + // Fetch the updated site with reverted drafts + const result = await this.retrieveSite({ + _action: 'retrieve', + where: { siteId: updatedSite.siteId }, + orgId, + caller: 'revertDraft', + scope: 'publish', // Use 'publish' to get the site without draft data + }, meta) + + return { + ...result, + message: 'Site reverted successfully', + } + } + private async deleteSite(params: ManageSiteParams & { _action: 'delete' }, _meta: EndpointMeta): Promise> { const { where, orgId } = params const db = this.settings.fictionDb.client() diff --git a/@fiction/site/plugin-builder/SiteEditor.vue b/@fiction/site/plugin-builder/SiteEditor.vue index 950775ea4..c7025ba3b 100644 --- a/@fiction/site/plugin-builder/SiteEditor.vue +++ b/@fiction/site/plugin-builder/SiteEditor.vue @@ -99,8 +99,19 @@ async function save() { async function resetToPublished() { if (!site.value) throw new Error('No site to revert') - - // await saveSiteDraft({ site: site.value, resetToPublished: true }) + const s = site.value + const siteId = s.siteId + + const r = await s.settings.fictionSites.requests.ManageSite.projectRequest({ + _action: 'revertDraft', + where: { siteId }, + caller: 'saveSite', + }) + + if (r.status === 'success') { + console.log('Reverted to published version', r) + await site.value.update({ ...r.data }, { noSave: true, caller: 'resetToPublished' }) + } } diff --git a/@fiction/site/site.ts b/@fiction/site/site.ts index 546fd5015..b8208e13c 100644 --- a/@fiction/site/site.ts +++ b/@fiction/site/site.ts @@ -120,7 +120,7 @@ export class Site extends FictionObject { ) expect(response.status).toBe('success') - expect(response.data?.draft?.title).toBe('Draft Site Title') - expect(response.data?.draft?.userConfig?.draftKey).toBe('draftValue') + expect(response.data?.title).toBe('Draft Site Title') + expect(response.data?.userConfig?.draftKey).toBe('draftValue') }) it('should retrieve a site with merged draft data when scope is draft', async () => { @@ -343,6 +343,111 @@ describe('manageSite query', () => { }) }) + describe('site draft reversion', () => { + let siteId: string + let cardId: string + + beforeEach(async () => { + const fields = createSiteFields('Draft Reversion Test Site') + const response = await testUtils.fictionSites.queries.ManageSite.run( + { _action: 'create', fields, orgId, userId, caller: 'test' }, + { server: true }, + ) + siteId = response.data?.siteId as string + cardId = objectId({ prefix: 'card' }) + + // Add a published page to the site + await testUtils.fictionSites.queries.ManagePage.serve( + { + _action: 'upsert', + siteId, + fields: [{ cardId, templateId: 'wrap', title: 'Published Page', slug: 'published-page' }], + orgId, + userId, + caller: 'test', + scope: 'publish', + }, + { server: true }, + ) + }) + + it('should revert site and page drafts', async () => { + // Create drafts for site and page + const siteDraft = { title: 'Draft Site Title' } + const pageDraft = { cardId, templateId: 'wrap', title: 'Draft Page Title', slug: 'published-page' } + + await testUtils.fictionSites.queries.ManageSite.serve( + { _action: 'saveDraft', where: { siteId }, fields: siteDraft, orgId, userId, caller: 'test' }, + { server: true }, + ) + + await testUtils.fictionSites.queries.ManagePage.serve( + { + _action: 'saveDraft', + siteId, + fields: [pageDraft], + orgId, + userId, + caller: 'test', + scope: 'draft', + }, + { server: true }, + ) + + // Revert drafts + const revertResponse = await testUtils.fictionSites.queries.ManageSite.run( + { _action: 'revertDraft', where: { siteId }, orgId, userId, caller: 'test' }, + { server: true }, + ) + + expect(revertResponse.status).toBe('success') + expect(revertResponse.message).toMatchInlineSnapshot(`"Site reverted successfully"`) + expect(revertResponse.message).toBeTruthy() + + // Verify site draft is reverted + const siteResponse = await testUtils.fictionSites.queries.ManageSite.run( + { _action: 'retrieve', where: { siteId }, orgId, userId, caller: 'test', scope: 'draft' }, + { server: true }, + ) + + expect(siteResponse.data?.title).not.toBe('Draft Site Title') + + // Verify page draft is reverted + const pageResponse = await testUtils.fictionSites.queries.ManagePage.serve( + { + _action: 'retrieve', + where: [{ cardId }], + siteId, + orgId, + caller: 'test', + scope: 'draft', + }, + { server: true }, + ) + + expect(pageResponse.data?.[0]?.title).toBe('Published Page') + }) + + it('should handle revert when no drafts exist', async () => { + const revertResponse = await testUtils.fictionSites.queries.ManageSite.run( + { _action: 'revertDraft', where: { siteId }, orgId, userId, caller: 'test' }, + { server: true }, + ) + + expect(revertResponse.status).toBe('success') + expect(revertResponse.message).toMatchInlineSnapshot(`"Site reverted successfully"`) + expect(revertResponse.message).toBeTruthy() + }) + + it('should throw an error when reverting non-existent site', async () => { + const nonExistentSiteId = objectId({ prefix: 'sit' }) + await expect(testUtils.fictionSites.queries.ManageSite.run( + { _action: 'revertDraft', where: { siteId: nonExistentSiteId }, orgId, userId, caller: 'test' }, + { server: true }, + )).rejects.toThrowErrorMatchingInlineSnapshot(`[EndpointError: Site not found]`) + }) + }) + describe('edge cases and error handling', () => { it('should have correct error message for invalid theme', async () => { const fields = { ...createSiteFields('Invalid Theme Site'), themeId: 'non-existent-theme' } diff --git a/@fiction/site/utils/site.ts b/@fiction/site/utils/site.ts index 1745c1d3b..f8ee0a98a 100644 --- a/@fiction/site/utils/site.ts +++ b/@fiction/site/utils/site.ts @@ -189,7 +189,7 @@ export async function updateSite(args: { site: Site, newConfig: Partial