Number entries on publish, delete media, read the cover
- drafts stay unnumbered, the number is handed out when an entry goes live - DELETE /api/v1/media/:id and a delete control in the media library - cover_media_id in the draft read, coverMediaId and coverUrl in the archive read
This commit is contained in:
@@ -65,7 +65,7 @@ describe('POST /api/v1/posts', () => {
|
||||
|
||||
expect(response.status).toBe(201)
|
||||
expect(body.status).toBe('draft')
|
||||
expect(body.number).toBe(1)
|
||||
expect(body.number).toBeNull()
|
||||
expect(body.slug).toBe('regel-engine')
|
||||
})
|
||||
|
||||
|
||||
+63
-14
@@ -3,10 +3,12 @@ import { asc, eq } from 'drizzle-orm'
|
||||
import { db } from '~/data/db'
|
||||
import { posts } from '~/data/schema'
|
||||
import {
|
||||
assignEntryNumber,
|
||||
createEntry,
|
||||
listEntries,
|
||||
listEntryBlocks,
|
||||
replaceEntryBlocks,
|
||||
setEntryStatus,
|
||||
type EntryCreateValues,
|
||||
} from '~/data/repositories/entries'
|
||||
import { makeBrand, makeProject } from '../support/admin'
|
||||
@@ -26,7 +28,7 @@ function values(projectId: string, slug: string, title = 'Beitrag'): EntryCreate
|
||||
}
|
||||
}
|
||||
|
||||
async function numbersOf(projectId: string): Promise<number[]> {
|
||||
async function numbersOf(projectId: string): Promise<(number | null)[]> {
|
||||
const rows = await db
|
||||
.select({ number: posts.number })
|
||||
.from(posts)
|
||||
@@ -36,28 +38,62 @@ async function numbersOf(projectId: string): Promise<number[]> {
|
||||
return rows.map(row => row.number)
|
||||
}
|
||||
|
||||
describe('createEntry vergibt Beitragsnummern', () => {
|
||||
it('zählt je Projekt getrennt hoch', async () => {
|
||||
async function publish(id: string): Promise<number | null> {
|
||||
const post = await setEntryStatus(id, 'published', new Date())
|
||||
|
||||
return post?.number ?? null
|
||||
}
|
||||
|
||||
describe('Beitragsnummern', () => {
|
||||
it('lässt Entwürfe ohne Nummer', async () => {
|
||||
const brand = await makeBrand()
|
||||
const project = await makeProject(brand.id)
|
||||
|
||||
const created = await createEntry(values(project.id, 'entwurf'))
|
||||
|
||||
expect(created.number).toBeNull()
|
||||
expect(await numbersOf(project.id)).toEqual([null])
|
||||
})
|
||||
|
||||
it('zählt beim Veröffentlichen je Projekt getrennt hoch', async () => {
|
||||
const brand = await makeBrand()
|
||||
const trakk = await makeProject(brand.id, 'trakk', 'Trakk')
|
||||
const orbit = await makeProject(brand.id, 'orbit', 'Orbit')
|
||||
|
||||
await createEntry(values(trakk.id, 'eins'))
|
||||
await createEntry(values(trakk.id, 'zwei'))
|
||||
await createEntry(values(orbit.id, 'eins'))
|
||||
const first = await createEntry(values(trakk.id, 'eins'))
|
||||
const second = await createEntry(values(trakk.id, 'zwei'))
|
||||
const third = await createEntry(values(orbit.id, 'eins'))
|
||||
|
||||
expect(await numbersOf(trakk.id)).toEqual([1, 2])
|
||||
expect(await numbersOf(orbit.id)).toEqual([1])
|
||||
expect(await publish(first.id)).toBe(1)
|
||||
expect(await publish(second.id)).toBe(2)
|
||||
expect(await publish(third.id)).toBe(1)
|
||||
})
|
||||
|
||||
it('vergibt bei gleichzeitigem Anlegen lückenlose Nummern ohne Doppelung', async () => {
|
||||
it('lässt keine Lücke, wenn ein Entwurf dazwischen gelöscht wird', async () => {
|
||||
const brand = await makeBrand()
|
||||
const project = await makeProject(brand.id)
|
||||
|
||||
await Promise.all(
|
||||
const first = await createEntry(values(project.id, 'eins'))
|
||||
const scrapped = await createEntry(values(project.id, 'verworfen'))
|
||||
const second = await createEntry(values(project.id, 'zwei'))
|
||||
|
||||
await publish(first.id)
|
||||
await db.delete(posts).where(eq(posts.id, scrapped.id))
|
||||
await publish(second.id)
|
||||
|
||||
expect(await numbersOf(project.id)).toEqual([1, 2])
|
||||
})
|
||||
|
||||
it('vergibt bei gleichzeitigem Veröffentlichen lückenlose Nummern ohne Doppelung', async () => {
|
||||
const brand = await makeBrand()
|
||||
const project = await makeProject(brand.id)
|
||||
|
||||
const created = await Promise.all(
|
||||
Array.from({ length: 20 }, (_, index) => createEntry(values(project.id, `beitrag-${index}`))),
|
||||
)
|
||||
|
||||
await Promise.all(created.map(entry => publish(entry.id)))
|
||||
|
||||
expect(await numbersOf(project.id)).toEqual(Array.from({ length: 20 }, (_, index) => index + 1))
|
||||
})
|
||||
|
||||
@@ -69,10 +105,23 @@ describe('createEntry vergibt Beitragsnummern', () => {
|
||||
|
||||
const created = await createEntry(values(project.id, 'neuer-beitrag'))
|
||||
|
||||
expect(created.number).toBe(143)
|
||||
expect(await publish(created.id)).toBe(143)
|
||||
})
|
||||
|
||||
it('hängt die Nummer an, wenn der Slug im Projekt schon vergeben ist', async () => {
|
||||
it('vergibt eine bereits gesetzte Nummer nicht neu', async () => {
|
||||
const brand = await makeBrand()
|
||||
const project = await makeProject(brand.id)
|
||||
|
||||
const created = await createEntry(values(project.id, 'eins'))
|
||||
|
||||
await publish(created.id)
|
||||
|
||||
const again = await assignEntryNumber(created.id)
|
||||
|
||||
expect(again?.number).toBe(1)
|
||||
})
|
||||
|
||||
it('hängt eine Zahl an, wenn der Slug im Projekt schon vergeben ist', async () => {
|
||||
const brand = await makeBrand()
|
||||
const project = await makeProject(brand.id)
|
||||
|
||||
@@ -80,7 +129,7 @@ describe('createEntry vergibt Beitragsnummern', () => {
|
||||
const second = await createEntry(values(project.id, 'regel-engine'))
|
||||
|
||||
expect(first.slug).toBe('regel-engine')
|
||||
expect(second.slug).toBe(`regel-engine-${second.number}`)
|
||||
expect(second.slug).toBe('regel-engine-2')
|
||||
})
|
||||
|
||||
it('übernimmt Titel, Autor und Zielgruppe', async () => {
|
||||
@@ -94,7 +143,7 @@ describe('createEntry vergibt Beitragsnummern', () => {
|
||||
authorName: 'Paul',
|
||||
audience: 'customer',
|
||||
status: 'draft',
|
||||
number: 1,
|
||||
number: null,
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
@@ -29,7 +29,7 @@ describe('performSaveEntry legt an', () => {
|
||||
it('speichert Beitrag, Blöcke und Autor', async () => {
|
||||
const { entry } = await ready()
|
||||
|
||||
expect(entry.number).toBe(1)
|
||||
expect(entry.number).toBeNull()
|
||||
expect(entry.slug).toBe('regel-engine')
|
||||
expect(entry.status).toBe('draft')
|
||||
|
||||
@@ -91,7 +91,7 @@ describe('performSaveEntry ändert', () => {
|
||||
|
||||
expect(state).toMatchObject({ ok: true })
|
||||
expect(state.ok && state.entry.slug).toBe('regel-engine')
|
||||
expect(state.ok && state.entry.number).toBe(1)
|
||||
expect(state.ok && state.entry.number).toBeNull()
|
||||
})
|
||||
|
||||
it('schreibt Blöcke in neuer Reihenfolge', async () => {
|
||||
@@ -120,7 +120,7 @@ describe('performSaveEntry ändert', () => {
|
||||
expect(state).toMatchObject({ ok: false, error: 'slugTaken' })
|
||||
})
|
||||
|
||||
it('lässt das Projekt wechseln und nummeriert im Zielprojekt neu', async () => {
|
||||
it('lässt das Projekt wechseln, ohne dem Entwurf eine Nummer zu geben', async () => {
|
||||
const brand = await makeBrand()
|
||||
const trakk = await makeProject(brand.id, 'trakk', 'Trakk')
|
||||
const orbit = await makeProject(brand.id, 'orbit', 'Orbit')
|
||||
@@ -136,7 +136,7 @@ describe('performSaveEntry ändert', () => {
|
||||
const saved = await findEntry(created.ok ? created.entry.id : '')
|
||||
|
||||
expect(saved?.projectId).toBe(orbit.id)
|
||||
expect(saved?.number).toBe(1)
|
||||
expect(saved?.number).toBeNull()
|
||||
})
|
||||
})
|
||||
|
||||
@@ -279,7 +279,7 @@ describe('Veröffentlichungsprüfungen', () => {
|
||||
})
|
||||
|
||||
describe('performSaveEntry wechselt das Projekt', () => {
|
||||
it('vergibt im Zielprojekt die nächste freie Nummer', async () => {
|
||||
it('lässt die Nummer des Entwurfs im Zielprojekt offen', async () => {
|
||||
const brand = await makeBrand()
|
||||
const from = await makeProject(brand.id, 'quelle', 'Quelle')
|
||||
const to = await makeProject(brand.id, 'ziel', 'Ziel')
|
||||
@@ -303,7 +303,7 @@ describe('performSaveEntry wechselt das Projekt', () => {
|
||||
const saved = await findEntry(created.entry.id)
|
||||
|
||||
expect(saved?.projectId).toBe(to.id)
|
||||
expect(moved.entry.number).toBe(2)
|
||||
expect(moved.entry.number).toBeNull()
|
||||
})
|
||||
|
||||
it('macht den Slug im Zielprojekt eindeutig', async () => {
|
||||
|
||||
@@ -25,7 +25,7 @@ describe('seed', () => {
|
||||
expect.arrayContaining(['trakk', 'mta360', 'mta-telematik', 'orbit', 'pulsar']),
|
||||
)
|
||||
expect(postRows.length).toBeGreaterThanOrEqual(6)
|
||||
expect(postRows.every(post => post.number > 0)).toBe(true)
|
||||
expect(postRows.every(post => post.status !== 'published' || (post.number ?? 0) > 0)).toBe(true)
|
||||
})
|
||||
|
||||
it('ist zweimal hintereinander ausführbar', async () => {
|
||||
|
||||
Reference in New Issue
Block a user