Projektarten: Schema-Skript fuer websiteProjects
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
a2e308711e
commit
85b972f9dc
2 files changed
+143
No files matched your search
@@ -0,0 +1,96 @@
|
||||
#!/usr/bin/env node
|
||||
/**
|
||||
* Projektarten: legt die neuen Attribute an der Collection websiteProjects an.
|
||||
* Idempotent: vorhandene Attribute werden uebersprungen.
|
||||
* Usage:
|
||||
* node scripts/projektarten-setup.mjs --check (nur melden, nichts anlegen)
|
||||
* node scripts/projektarten-setup.mjs (fehlende Attribute anlegen)
|
||||
*/
|
||||
import { pathToFileURL } from 'node:url'
|
||||
import { config } from '../server/config.js'
|
||||
|
||||
export const PROJECT_KIND_ATTRIBUTES = [
|
||||
{ key: 'kind', type: 'string', size: 20, required: false },
|
||||
{ key: 'forCustomer', type: 'boolean', required: false },
|
||||
{ key: 'kindSetBy', type: 'string', size: 64, required: false },
|
||||
{ key: 'kindSetAt', type: 'string', size: 40, required: false },
|
||||
{ key: 'createdByGitea', type: 'string', size: 64, required: false },
|
||||
{ key: 'assignMailSentAt', type: 'string', size: 40, required: false },
|
||||
{ key: 'assignReminderSentAt', type: 'string', size: 40, required: false },
|
||||
{ key: 'assignMailAttempts', type: 'integer', required: false },
|
||||
]
|
||||
|
||||
/** Echter Appwrite-Client (REST) fuer die Collection websiteProjects. */
|
||||
export function createAppwriteClient() {
|
||||
const { endpoint, projectId, databaseId, apiKey } = config.appwrite
|
||||
const collectionId = config.collections.websiteProjects
|
||||
const base = `${endpoint.replace(/\/$/, '')}/databases/${databaseId}/collections/${collectionId}`
|
||||
|
||||
async function aw(path, { method = 'GET', body } = {}) {
|
||||
const response = await fetch(`${base}${path}`, {
|
||||
method,
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
'X-Appwrite-Project': projectId,
|
||||
'X-Appwrite-Key': apiKey,
|
||||
},
|
||||
body: body ? JSON.stringify(body) : undefined,
|
||||
})
|
||||
const data = await response.json().catch(() => ({}))
|
||||
if (!response.ok) throw new Error(data.message || `Appwrite ${response.status}`)
|
||||
return data
|
||||
}
|
||||
|
||||
return {
|
||||
async listAttributes() {
|
||||
const data = await aw('/attributes')
|
||||
return data.attributes || []
|
||||
},
|
||||
async createAttribute({ type, ...body }) {
|
||||
await aw(`/attributes/${type}`, { method: 'POST', body })
|
||||
},
|
||||
async waitForAttribute(key, maxSec = 30) {
|
||||
for (let i = 0; i < maxSec; i += 1) {
|
||||
const attr = await aw(`/attributes/${key}`).catch(() => null)
|
||||
if (attr?.status === 'available') return
|
||||
await new Promise((r) => setTimeout(r, 1000))
|
||||
}
|
||||
throw new Error(`Attribut ${key} wurde nicht verfuegbar`)
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
export async function main({ check = false, client, log = console.log } = {}) {
|
||||
const api = client || createAppwriteClient()
|
||||
const existing = new Set((await api.listAttributes()).map((a) => a.key))
|
||||
const missing = PROJECT_KIND_ATTRIBUTES.filter((a) => !existing.has(a.key))
|
||||
|
||||
for (const attr of PROJECT_KIND_ATTRIBUTES) {
|
||||
if (existing.has(attr.key)) log(`= Attribut ${attr.key} (existiert)`)
|
||||
else if (check) log(`- Attribut ${attr.key} FEHLT`)
|
||||
}
|
||||
|
||||
if (check) {
|
||||
log(missing.length ? `${missing.length} Attribut(e) fehlen.` : 'Alle Attribute vorhanden.')
|
||||
return { missing }
|
||||
}
|
||||
|
||||
for (const attr of missing) {
|
||||
await api.createAttribute(attr)
|
||||
log(`+ Attribut ${attr.key}`)
|
||||
}
|
||||
for (const attr of missing) await api.waitForAttribute(attr.key)
|
||||
log('Fertig.')
|
||||
return { missing }
|
||||
}
|
||||
|
||||
const invokedDirectly = process.argv[1] && import.meta.url === pathToFileURL(process.argv[1]).href
|
||||
if (invokedDirectly) {
|
||||
const check = process.argv.includes('--check')
|
||||
main({ check })
|
||||
.then(({ missing }) => process.exit(check && missing.length ? 1 : 0))
|
||||
.catch((e) => {
|
||||
console.error(e.message)
|
||||
process.exit(1)
|
||||
})
|
||||
}
|
||||
@@ -0,0 +1,47 @@
|
||||
import { test } from 'node:test'
|
||||
import assert from 'node:assert/strict'
|
||||
import { PROJECT_KIND_ATTRIBUTES, main } from '../scripts/projektarten-setup.mjs'
|
||||
|
||||
function mockClient(existing = []) {
|
||||
const created = []
|
||||
return {
|
||||
created,
|
||||
async listAttributes() {
|
||||
return existing.map((key) => ({ key }))
|
||||
},
|
||||
async createAttribute(attr) {
|
||||
created.push(attr.key)
|
||||
},
|
||||
async waitForAttribute() {},
|
||||
}
|
||||
}
|
||||
|
||||
test('Schema: genau die 8 Attribute, alle optional', () => {
|
||||
assert.deepEqual(
|
||||
PROJECT_KIND_ATTRIBUTES.map((a) => a.key),
|
||||
['kind', 'forCustomer', 'kindSetBy', 'kindSetAt', 'createdByGitea', 'assignMailSentAt', 'assignReminderSentAt', 'assignMailAttempts']
|
||||
)
|
||||
assert.ok(PROJECT_KIND_ATTRIBUTES.every((a) => a.required === false))
|
||||
const byKey = Object.fromEntries(PROJECT_KIND_ATTRIBUTES.map((a) => [a.key, a]))
|
||||
assert.equal(byKey.kind.size, 20)
|
||||
assert.equal(byKey.kindSetBy.size, 64)
|
||||
assert.equal(byKey.createdByGitea.size, 64)
|
||||
assert.equal(byKey.kindSetAt.size, 40)
|
||||
assert.equal(byKey.forCustomer.type, 'boolean')
|
||||
assert.equal(byKey.assignMailAttempts.type, 'integer')
|
||||
})
|
||||
|
||||
test('main legt nur fehlende Attribute an (kind existiert schon)', async () => {
|
||||
const client = mockClient(['kind'])
|
||||
const result = await main({ check: false, client, log: () => {} })
|
||||
assert.equal(client.created.length, 7)
|
||||
assert.ok(!client.created.includes('kind'))
|
||||
assert.deepEqual(result.missing.length, 7)
|
||||
})
|
||||
|
||||
test('main --check legt nichts an und meldet Fehlende', async () => {
|
||||
const client = mockClient(['kind'])
|
||||
const result = await main({ check: true, client, log: () => {} })
|
||||
assert.equal(client.created.length, 0)
|
||||
assert.equal(result.missing.length, 7)
|
||||
})
|
||||
Reference in new issue
Block a user