feat(backend): Added curatedSets data type
This commit is contained in:
parent
19a81a0aed
commit
e4f2aab9d0
15 changed files with 1006 additions and 48 deletions
177
sites/backend/scripts/import-sizing-table.mjs
Normal file
177
sites/backend/scripts/import-sizing-table.mjs
Normal file
|
@ -0,0 +1,177 @@
|
|||
import path from 'path'
|
||||
import fs from 'fs'
|
||||
import { PrismaClient } from '@prisma/client'
|
||||
import { hash, encryption } from '../src/utils/crypto.mjs'
|
||||
import { clean } from '../src/utils/index.mjs'
|
||||
import { verifyConfig } from '../src/config.mjs'
|
||||
import {
|
||||
cisFemaleAdult,
|
||||
cisMaleAdult,
|
||||
cisFemaleDoll,
|
||||
cisMaleDoll,
|
||||
cisFemaleGiant,
|
||||
cisMaleGiant,
|
||||
} from '../../../packages/models/src/index.mjs'
|
||||
const prisma = new PrismaClient()
|
||||
const config = verifyConfig()
|
||||
const { encrypt, decrypt } = encryption(config.encryption.key)
|
||||
|
||||
/*
|
||||
* Note: This will import the FreeSewing (v2) sizing table into the
|
||||
* FreeSewing curator account.
|
||||
*
|
||||
* The curator account is just a regular user account, but one that
|
||||
* we use to store the 'official' measurement sets.
|
||||
*
|
||||
* Unless you are a very involved contributor, there is probably
|
||||
* no reason for you to run this script.
|
||||
*/
|
||||
|
||||
// Holds the sets to create
|
||||
const sets = []
|
||||
|
||||
// Helper method to create the set data
|
||||
const createSetData = ({ name, measies, imperial }) => ({
|
||||
imperial,
|
||||
name,
|
||||
measies,
|
||||
userId: config.curator.id,
|
||||
public: true,
|
||||
})
|
||||
|
||||
// CIS Female Adult
|
||||
sets.push(
|
||||
...Object.keys(cisFemaleAdult).map((size) =>
|
||||
createSetData({
|
||||
name: `Metric Cis Female Adult - Size ${size} (EU)`,
|
||||
measies: cisFemaleAdult[size],
|
||||
imperial: false,
|
||||
})
|
||||
)
|
||||
)
|
||||
sets.push(
|
||||
...Object.keys(cisFemaleAdult).map((size) =>
|
||||
createSetData({
|
||||
name: `Imperial Cis Female Adult - Size ${size} (EU)`,
|
||||
measies: cisFemaleAdult[size],
|
||||
imperial: true,
|
||||
})
|
||||
)
|
||||
)
|
||||
|
||||
// CIS Male Adult
|
||||
sets.push(
|
||||
...Object.keys(cisMaleAdult).map((size) =>
|
||||
createSetData({
|
||||
name: `Metric Cis Male Adult - Size ${size} (EU)`,
|
||||
measies: cisMaleAdult[size],
|
||||
imperial: false,
|
||||
})
|
||||
)
|
||||
)
|
||||
sets.push(
|
||||
...Object.keys(cisMaleAdult).map((size) =>
|
||||
createSetData({
|
||||
name: `Imperial Cis Male Adult - Size ${size} (EU)`,
|
||||
measies: cisMaleAdult[size],
|
||||
imperial: true,
|
||||
})
|
||||
)
|
||||
)
|
||||
|
||||
// CIS Female Doll
|
||||
sets.push(
|
||||
...Object.keys(cisFemaleDoll).map((size) =>
|
||||
createSetData({
|
||||
name: `Metric Cis Female Doll - ${size}%`,
|
||||
measies: cisFemaleDoll[size],
|
||||
imperial: false,
|
||||
})
|
||||
)
|
||||
)
|
||||
sets.push(
|
||||
...Object.keys(cisFemaleDoll).map((size) =>
|
||||
createSetData({
|
||||
name: `Imperial Cis Female Doll - ${size}%`,
|
||||
measies: cisFemaleDoll[size],
|
||||
imperial: true,
|
||||
})
|
||||
)
|
||||
)
|
||||
|
||||
// CIS Male Doll
|
||||
sets.push(
|
||||
...Object.keys(cisMaleDoll).map((size) =>
|
||||
createSetData({
|
||||
name: `Metric Cis Male Doll - ${size}%`,
|
||||
measies: cisMaleDoll[size],
|
||||
imperial: false,
|
||||
})
|
||||
)
|
||||
)
|
||||
sets.push(
|
||||
...Object.keys(cisMaleDoll).map((size) =>
|
||||
createSetData({
|
||||
name: `Imperial Cis Male Doll - ${size}%`,
|
||||
measies: cisMaleDoll[size],
|
||||
imperial: true,
|
||||
})
|
||||
)
|
||||
)
|
||||
|
||||
// CIS Female Giant
|
||||
sets.push(
|
||||
...Object.keys(cisFemaleGiant).map((size) =>
|
||||
createSetData({
|
||||
name: `Metric Cis Female Giant - Size ${size}%`,
|
||||
measies: cisFemaleGiant[size],
|
||||
imperial: false,
|
||||
})
|
||||
)
|
||||
)
|
||||
sets.push(
|
||||
...Object.keys(cisFemaleGiant).map((size) =>
|
||||
createSetData({
|
||||
name: `Imperial Cis Female Giant - Size ${size}%`,
|
||||
measies: cisFemaleGiant[size],
|
||||
imperial: true,
|
||||
})
|
||||
)
|
||||
)
|
||||
|
||||
// CIS Male Giant
|
||||
sets.push(
|
||||
...Object.keys(cisMaleGiant).map((size) =>
|
||||
createSetData({
|
||||
name: `Metric Cis Male Giant - Size ${size}%`,
|
||||
measies: cisMaleGiant[size],
|
||||
imperial: false,
|
||||
})
|
||||
)
|
||||
)
|
||||
sets.push(
|
||||
...Object.keys(cisMaleGiant).map((size) =>
|
||||
createSetData({
|
||||
name: `Imperial Cis Male Giant - Size ${size}%`,
|
||||
measies: cisMaleGiant[size],
|
||||
imperial: true,
|
||||
})
|
||||
)
|
||||
)
|
||||
|
||||
importSets(sets)
|
||||
|
||||
async function createSet(set) {
|
||||
try {
|
||||
record = await prisma.user.create({ data: set })
|
||||
} catch (err) {
|
||||
console.log(err)
|
||||
}
|
||||
}
|
||||
|
||||
async function importSets(sets) {
|
||||
for (const set of sets) {
|
||||
console.log(`Importing ${set.name}`)
|
||||
await createSet(set)
|
||||
}
|
||||
}
|
|
@ -27,6 +27,8 @@ const envToBool = (input = 'no') => {
|
|||
|
||||
// Construct config object
|
||||
const baseConfig = {
|
||||
// Environment
|
||||
env: process.env.NODE_ENV || 'development',
|
||||
// Feature flags
|
||||
use: {
|
||||
github: envToBool(process.env.BACKEND_ENABLE_GITHUB),
|
||||
|
@ -87,6 +89,7 @@ const baseConfig = {
|
|||
},
|
||||
tests: {
|
||||
domain: process.env.BACKEND_TEST_DOMAIN || 'freesewing.dev',
|
||||
production: envToBool(process.env.BACKEND_ALLOW_TESTS_IN_PRODUCTION),
|
||||
},
|
||||
website: {
|
||||
domain: process.env.BACKEND_WEBSITE_DOMAIN || 'freesewing.org',
|
||||
|
@ -202,6 +205,7 @@ const vars = {
|
|||
BACKEND_ENABLE_OAUTH_GITHUB: 'optional',
|
||||
BACKEND_ENABLE_OAUTH_GOOGLE: 'optional',
|
||||
BACKEND_ENABLE_TESTS: 'optional',
|
||||
BACKEND_ALLOW_TESTS_IN_PRODUCTION: 'optional',
|
||||
BACKEND_ENABLE_DUMP_CONFIG_AT_STARTUP: 'optional',
|
||||
}
|
||||
|
||||
|
@ -241,7 +245,7 @@ if (envToBool(process.env.BACKEND_ENABLE_OAUTH_GOOGLE)) {
|
|||
vars.BACKEND_OAUTH_GOOGLE_CLIENT_ID = 'required'
|
||||
vars.BACKEND_OAUTH_GOOGLE_CLIENT_SECRET = 'requiredSecret'
|
||||
}
|
||||
// Vars for unit tests
|
||||
// Vars for (unit) tests
|
||||
if (envToBool(process.env.BACKEND_ENABLE_TESTS)) {
|
||||
vars.BACKEND_TEST_DOMAIN = 'optional'
|
||||
vars.BACKEND_ENABLE_TESTS_EMAIL = 'optional'
|
||||
|
|
79
sites/backend/src/controllers/curated-sets.mjs
Normal file
79
sites/backend/src/controllers/curated-sets.mjs
Normal file
|
@ -0,0 +1,79 @@
|
|||
import { CuratedSetModel } from '../models/curated-set.mjs'
|
||||
import { SetModel } from '../models/set.mjs'
|
||||
|
||||
export function CuratedSetsController() {}
|
||||
|
||||
/*
|
||||
* Create a curated measurements set
|
||||
* See: https://freesewing.dev/reference/backend/api
|
||||
*/
|
||||
CuratedSetsController.prototype.create = async (req, res, tools) => {
|
||||
const CuratedSet = new CuratedSetModel(tools)
|
||||
await CuratedSet.guardedCreate(req)
|
||||
|
||||
return CuratedSet.sendResponse(res)
|
||||
}
|
||||
|
||||
/*
|
||||
* Read a curated measurements set
|
||||
* See: https://freesewing.dev/reference/backend/api
|
||||
*/
|
||||
CuratedSetsController.prototype.read = async (req, res, tools, format = false) => {
|
||||
const CuratedSet = new CuratedSetModel(tools)
|
||||
await CuratedSet.guardedRead(req, format)
|
||||
|
||||
return format === 'yaml' ? CuratedSet.sendYamlResponse(res) : CuratedSet.sendResponse(res)
|
||||
}
|
||||
|
||||
/*
|
||||
* Get a list of curated measurements sets
|
||||
* See: https://freesewing.dev/reference/backend/api
|
||||
*/
|
||||
CuratedSetsController.prototype.list = async (req, res, tools, format = false) => {
|
||||
const CuratedSet = new CuratedSetModel(tools)
|
||||
const curatedSets = await CuratedSet.allCuratedSets()
|
||||
|
||||
if (curatedSets) {
|
||||
if (!format) CuratedSet.setResponse(200, 'success', { curatedSets })
|
||||
else CuratedSet.setResponse(200, 'success', curatedSets, true)
|
||||
} else CuratedSet.setResponse(404, 'notFound')
|
||||
|
||||
return format === 'yaml' && curatedSets
|
||||
? CuratedSet.sendYamlResponse(res)
|
||||
: CuratedSet.sendResponse(res)
|
||||
}
|
||||
|
||||
/*
|
||||
* Update a curated measurements set
|
||||
* See: https://freesewing.dev/reference/backend/api
|
||||
*/
|
||||
CuratedSetsController.prototype.update = async (req, res, tools) => {
|
||||
const CuratedSet = new CuratedSetModel(tools)
|
||||
await CuratedSet.guardedUpdate(req)
|
||||
|
||||
return CuratedSet.sendResponse(res)
|
||||
}
|
||||
|
||||
/*
|
||||
* Remove a curated measurements set
|
||||
* See: https://freesewing.dev/reference/backend/api
|
||||
*/
|
||||
CuratedSetsController.prototype.delete = async (req, res, tools) => {
|
||||
const CuratedSet = new CuratedSetModel(tools)
|
||||
await CuratedSet.guardedDelete(req)
|
||||
|
||||
return Set.sendResponse(res)
|
||||
}
|
||||
|
||||
/*
|
||||
* Clone a curated measurements set
|
||||
* See: https://freesewing.dev/reference/backend/api
|
||||
*/
|
||||
CuratedSetsController.prototype.clone = async (req, res, tools) => {
|
||||
const CuratedSet = new CuratedSetModel(tools)
|
||||
const Set = new SetModel(tools)
|
||||
await CuratedSet.guardedClone(req, Set)
|
||||
|
||||
// Note: Sending the set back
|
||||
return Set.sendResponse(res)
|
||||
}
|
322
sites/backend/src/models/curated-set.mjs
Normal file
322
sites/backend/src/models/curated-set.mjs
Normal file
|
@ -0,0 +1,322 @@
|
|||
import { capitalize } from '../utils/index.mjs'
|
||||
import { log } from '../utils/log.mjs'
|
||||
import { setSetAvatar } from '../utils/sanity.mjs'
|
||||
import yaml from 'js-yaml'
|
||||
|
||||
export function CuratedSetModel(tools) {
|
||||
this.config = tools.config
|
||||
this.prisma = tools.prisma
|
||||
this.rbac = tools.rbac
|
||||
|
||||
return this
|
||||
}
|
||||
|
||||
CuratedSetModel.prototype.guardedCreate = async function ({ body, user }) {
|
||||
if (!this.rbac.curator(user)) return this.setResponse(403, 'insufficientAccessLevel')
|
||||
if (Object.keys(body).length < 1) return this.setResponse(400, 'postBodyMissing')
|
||||
if (!body.nameEn || typeof body.nameEn !== 'string') return this.setResponse(400, 'nameEnMissing')
|
||||
|
||||
// Prepare data
|
||||
const data = {}
|
||||
for (const lang of this.config.languages) {
|
||||
for (const field of ['name', 'notes']) {
|
||||
const key = field + capitalize(lang)
|
||||
if (body[key] && typeof body[key] === 'string') data[key] = body[key]
|
||||
}
|
||||
}
|
||||
if (body.measies) data.measies = this.sanitizeMeasurements(body.measies)
|
||||
else data.measies = {}
|
||||
// Set this one initially as we need the ID to create a custom img via Sanity
|
||||
data.img = this.config.avatars.set
|
||||
|
||||
// Create record
|
||||
await this.unguardedCreate(data)
|
||||
|
||||
// Update img? (now that we have the ID)
|
||||
const img =
|
||||
this.config.use.sanity &&
|
||||
typeof body.img === 'string' &&
|
||||
(!body.test || (body.test && this.config.use.tests?.sanity))
|
||||
? await setSetAvatar(this.record.id, body.img)
|
||||
: false
|
||||
|
||||
if (img) await this.unguardedUpdate({ img: img.url })
|
||||
else await this.read({ id: this.record.id })
|
||||
|
||||
return this.setResponse(201, 'created', { curatedSet: this.asCuratedSet() })
|
||||
}
|
||||
|
||||
CuratedSetModel.prototype.unguardedCreate = async function (data) {
|
||||
// FIXME: See https://github.com/prisma/prisma/issues/3786
|
||||
if (data.measies && typeof data.measies === 'object') data.measies = JSON.stringify(data.measies)
|
||||
try {
|
||||
this.record = await this.prisma.curatedSet.create({ data })
|
||||
} catch (err) {
|
||||
log.warn(err, 'Could not create set')
|
||||
return this.setResponse(500, 'createSetFailed')
|
||||
}
|
||||
|
||||
return this
|
||||
}
|
||||
|
||||
/*
|
||||
* Loads a measurements set from the database based on the where clause you pass it
|
||||
*
|
||||
* Stores result in this.record
|
||||
*/
|
||||
CuratedSetModel.prototype.read = async function (where) {
|
||||
try {
|
||||
this.record = await this.prisma.curatedSet.findUnique({ where })
|
||||
} catch (err) {
|
||||
log.warn({ err, where }, 'Could not read measurements set')
|
||||
}
|
||||
|
||||
// FIXME: Convert JSON to object. See https://github.com/prisma/prisma/issues/3786
|
||||
this.record.measies = JSON.parse(this.record.measies)
|
||||
|
||||
return this.curatedSetExists()
|
||||
}
|
||||
|
||||
/*
|
||||
* Loads a measurements set from the database based on the where clause you pass it
|
||||
* In addition prepares it for returning the set data
|
||||
*
|
||||
* Stores result in this.record
|
||||
*/
|
||||
CuratedSetModel.prototype.guardedRead = async function ({ params }, format = false) {
|
||||
await this.read({ id: parseInt(params.id) })
|
||||
|
||||
if (!format)
|
||||
return this.setResponse(200, false, {
|
||||
result: 'success',
|
||||
curatedSet: this.asCuratedSet(),
|
||||
})
|
||||
|
||||
return this.setResponse(200, false, this.asData(), true)
|
||||
}
|
||||
|
||||
/*
|
||||
* Returns a list of all curated sets
|
||||
*/
|
||||
CuratedSetModel.prototype.allCuratedSets = async function () {
|
||||
let curatedSets
|
||||
try {
|
||||
curatedSets = await this.prisma.curatedSet.findMany()
|
||||
} catch (err) {
|
||||
log.warn(`Failed to search curated sets: ${err}`)
|
||||
}
|
||||
const list = []
|
||||
for (const curatedSet of curatedSets) list.push(curatedSet)
|
||||
|
||||
return list
|
||||
}
|
||||
|
||||
/*
|
||||
* Clones a curated measurements set (into a regular set)
|
||||
* In addition prepares it for returning the set data
|
||||
*
|
||||
* Stores result in this.record
|
||||
*/
|
||||
CuratedSetModel.prototype.guardedClone = async function ({ params, user, body }, Set) {
|
||||
if (!this.rbac.writeSome(user)) return this.setResponse(403, 'insufficientAccessLevel')
|
||||
if (user.iss && user.status < 1) return this.setResponse(403, 'accountStatusLacking')
|
||||
if (!body.language || !this.config.languages.includes(body.language))
|
||||
return this.setResponse(403, 'languageMissing')
|
||||
|
||||
await this.read({ id: parseInt(params.id) })
|
||||
|
||||
// Clone curated set
|
||||
const data = {}
|
||||
const lang = capitalize(body.language.toLowerCase())
|
||||
data.name = this.record[`name${lang}`]
|
||||
data.notes = this.record[`notes${lang}`]
|
||||
data.measies = this.record.measies
|
||||
|
||||
await Set.guardedCreate({ params, user, body: data })
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
/*
|
||||
* Checks this.record and sets a boolean to indicate whether
|
||||
* the curated set exists or not
|
||||
*
|
||||
* Stores result in this.exists
|
||||
*/
|
||||
CuratedSetModel.prototype.curatedSetExists = function () {
|
||||
this.exists = this.record ? true : false
|
||||
|
||||
return this
|
||||
}
|
||||
|
||||
/*
|
||||
* Updates the set data - Used when we create the data ourselves
|
||||
* so we know it's safe
|
||||
*/
|
||||
CuratedSetModel.prototype.unguardedUpdate = async function (data) {
|
||||
// FIXME: Convert object to JSON. See https://github.com/prisma/prisma/issues/3786
|
||||
if (data.measies && typeof data.measies === 'object') data.measies = JSON.stringify(data.measies)
|
||||
|
||||
try {
|
||||
this.record = await this.prisma.curatedSet.update({
|
||||
where: { id: this.record.id },
|
||||
data,
|
||||
})
|
||||
// FIXME: Convert JSON to object. See https://github.com/prisma/prisma/issues/3786
|
||||
this.record.measies = JSON.parse(this.record.measies)
|
||||
} catch (err) {
|
||||
log.warn(err, 'Could not update set record')
|
||||
process.exit()
|
||||
return this.setResponse(500, 'updateSetFailed')
|
||||
}
|
||||
|
||||
return this.setResponse(200)
|
||||
}
|
||||
|
||||
/*
|
||||
* Updates the set data - Used when we pass through user-provided data
|
||||
* so we can't be certain it's safe
|
||||
*/
|
||||
CuratedSetModel.prototype.guardedUpdate = async function ({ params, body, user }) {
|
||||
if (!this.rbac.curator(user)) return this.setResponse(403, 'insufficientAccessLevel')
|
||||
if (user.iss && user.status < 1) return this.setResponse(403, 'accountStatusLacking')
|
||||
await this.read({ id: parseInt(params.id) })
|
||||
|
||||
// Prepare data
|
||||
const data = {}
|
||||
for (const lang of this.config.languages) {
|
||||
for (const field of ['name', 'notes']) {
|
||||
const key = field + capitalize(lang)
|
||||
if (body[key] && typeof body[key] === 'string') data[key] = body[key]
|
||||
}
|
||||
}
|
||||
// Measurements
|
||||
const measies = {}
|
||||
if (typeof body.measies === 'object') {
|
||||
const remove = []
|
||||
for (const [key, val] of Object.entries(body.measies)) {
|
||||
if (this.config.measies.includes(key)) {
|
||||
if (val === null) remove.push(key)
|
||||
else if (typeof val == 'number' && val > 0) measies[key] = val
|
||||
}
|
||||
}
|
||||
data.measies = { ...this.record.measies, ...measies }
|
||||
for (const key of remove) delete data.measies[key]
|
||||
}
|
||||
|
||||
// Image (img)
|
||||
if (typeof body.img === 'string') {
|
||||
const img = await setSetAvatar(params.id, body.img)
|
||||
data.img = img.url
|
||||
}
|
||||
|
||||
// Now update the record
|
||||
await this.unguardedUpdate(data)
|
||||
|
||||
return this.setResponse(200, false, { set: this.asCuratedSet() })
|
||||
}
|
||||
|
||||
/*
|
||||
* Removes the set - No questions asked
|
||||
*/
|
||||
CuratedSetModel.prototype.unguardedDelete = async function () {
|
||||
await this.prisma.curatedSet.delete({ where: { id: this.record.id } })
|
||||
this.record = null
|
||||
this.clear = null
|
||||
|
||||
return this.curatedSetExists()
|
||||
}
|
||||
|
||||
/*
|
||||
* Removes the set - Checks permissions
|
||||
*/
|
||||
CuratedSetModel.prototype.guardedDelete = async function ({ params, user }) {
|
||||
if (!this.rbac.curator(user)) return this.setResponse(403, 'insufficientAccessLevel')
|
||||
if (user.iss && user.status < 1) return this.setResponse(403, 'accountStatusLacking')
|
||||
|
||||
await this.read({ id: parseInt(params.id) })
|
||||
if (this.record.userId !== user.uid && user.level < 8) {
|
||||
return this.setResponse(403, 'insufficientAccessLevel')
|
||||
}
|
||||
|
||||
await this.unguardedDelete()
|
||||
|
||||
return this.setResponse(204, false)
|
||||
}
|
||||
|
||||
/*
|
||||
* Returns record data fit for public publishing
|
||||
*/
|
||||
CuratedSetModel.prototype.asCuratedSet = function () {
|
||||
return { ...this.record }
|
||||
}
|
||||
|
||||
/*
|
||||
* Returns record data fit for public publishing
|
||||
*/
|
||||
CuratedSetModel.prototype.asData = function () {
|
||||
const data = {
|
||||
author: 'FreeSewing.org',
|
||||
type: 'curatedMeasurementsSet',
|
||||
about: 'Contains measurements in mm as well as metadata',
|
||||
...this.asCuratedSet(),
|
||||
}
|
||||
data.measurements = data.measies
|
||||
delete data.measies
|
||||
|
||||
return data
|
||||
}
|
||||
|
||||
/*
|
||||
* Helper method to set the response code, result, and body
|
||||
*
|
||||
* Will be used by this.sendResponse()
|
||||
*/
|
||||
CuratedSetModel.prototype.setResponse = function (
|
||||
status = 200,
|
||||
error = false,
|
||||
data = {},
|
||||
rawData = false
|
||||
) {
|
||||
this.response = {
|
||||
status,
|
||||
body: rawData
|
||||
? data
|
||||
: {
|
||||
result: 'success',
|
||||
...data,
|
||||
},
|
||||
}
|
||||
if (status > 201) {
|
||||
this.response.body.error = error
|
||||
this.response.body.result = 'error'
|
||||
this.error = true
|
||||
} else this.error = false
|
||||
|
||||
return this.curatedSetExists()
|
||||
}
|
||||
|
||||
/*
|
||||
* Helper method to send response (as JSON)
|
||||
*/
|
||||
CuratedSetModel.prototype.sendResponse = async function (res) {
|
||||
return res.status(this.response.status).send(this.response.body)
|
||||
}
|
||||
|
||||
/*
|
||||
* Helper method to send response as YAML
|
||||
*/
|
||||
CuratedSetModel.prototype.sendYamlResponse = async function (res) {
|
||||
return res.status(this.response.status).type('yaml').send(yaml.dump(this.response.body))
|
||||
}
|
||||
|
||||
/* Helper method to parse user-supplied measurements */
|
||||
CuratedSetModel.prototype.sanitizeMeasurements = function (input) {
|
||||
const measies = {}
|
||||
if (typeof input !== 'object') return measies
|
||||
for (const [m, val] of Object.entries(input)) {
|
||||
if (this.config.measies.includes(m) && typeof val === 'number' && val > 0) measies[m] = val
|
||||
}
|
||||
|
||||
return measies
|
||||
}
|
|
@ -51,7 +51,7 @@ PatternModel.prototype.guardedCreate = async function ({ body, user }) {
|
|||
const img =
|
||||
this.config.use.sanity &&
|
||||
typeof body.img === 'string' &&
|
||||
(!body.unittest || (body.unittest && this.config.use.tests?.sanity))
|
||||
(!body.test || (body.test && this.config.use.tests?.sanity))
|
||||
? await setPatternAvatar(this.record.id, body.img)
|
||||
: false
|
||||
|
||||
|
|
|
@ -42,7 +42,7 @@ SetModel.prototype.guardedCreate = async function ({ body, user }) {
|
|||
const img =
|
||||
this.config.use.sanity &&
|
||||
typeof body.img === 'string' &&
|
||||
(!body.unittest || (body.unittest && this.config.use.tests?.sanity))
|
||||
(!body.test || (body.test && this.config.use.tests?.sanity))
|
||||
? await setSetAvatar(this.record.id, body.img)
|
||||
: false
|
||||
|
||||
|
@ -393,10 +393,10 @@ SetModel.prototype.sendYamlResponse = async function (res) {
|
|||
|
||||
/*
|
||||
* Update method to determine whether this request is
|
||||
* part of a unit test
|
||||
* part of a test
|
||||
*/
|
||||
//UserModel.prototype.isUnitTest = function (body) {
|
||||
// if (!body.unittest) return false
|
||||
//UserModel.prototype.isTest = function (body) {
|
||||
// if (!body.test) return false
|
||||
// if (!this.clear.email.split('@').pop() === this.config.tests.domain) return false
|
||||
// if (body.email && !body.email.split('@').pop() === this.config.tests.domain) return false
|
||||
//
|
||||
|
|
|
@ -150,6 +150,10 @@ UserModel.prototype.guardedCreate = async function ({ body }) {
|
|||
const ehash = hash(clean(body.email))
|
||||
const check = randomString()
|
||||
await this.read({ ehash })
|
||||
|
||||
// Check for unit tests only once
|
||||
const isTest = this.isTest(body)
|
||||
|
||||
if (this.exists) {
|
||||
/*
|
||||
* User already exists. However, if we return an error, then baddies can
|
||||
|
@ -183,20 +187,21 @@ UserModel.prototype.guardedCreate = async function ({ body }) {
|
|||
userId: this.record.id,
|
||||
})
|
||||
}
|
||||
// Always send email
|
||||
await this.mailer.send({
|
||||
template: type,
|
||||
language: body.language,
|
||||
to: this.clear.email,
|
||||
replacements: {
|
||||
actionUrl:
|
||||
type === 'signup-aed'
|
||||
? false // No actionUrl for disabled accounts
|
||||
: i18nUrl(body.language, `/confirm/${type}/${this.Confirmation.record.id}/${check}`),
|
||||
whyUrl: i18nUrl(body.language, `/docs/faq/email/why-${type}`),
|
||||
supportUrl: i18nUrl(body.language, `/patrons/join`),
|
||||
},
|
||||
})
|
||||
// Send email unless it's a test and we don't want to send test emails
|
||||
if (!isTest || this.config.tests.sendEmail)
|
||||
await this.mailer.send({
|
||||
template: type,
|
||||
language: body.language,
|
||||
to: this.clear.email,
|
||||
replacements: {
|
||||
actionUrl:
|
||||
type === 'signup-aed'
|
||||
? false // No actionUrl for disabled accounts
|
||||
: i18nUrl(body.language, `/confirm/${type}/${this.Confirmation.record.id}/${check}`),
|
||||
whyUrl: i18nUrl(body.language, `/docs/faq/email/why-${type}`),
|
||||
supportUrl: i18nUrl(body.language, `/patrons/join`),
|
||||
},
|
||||
})
|
||||
|
||||
// Now return as if everything is fine
|
||||
return this.setResponse(201, false, { email: this.clear.email })
|
||||
|
@ -225,6 +230,8 @@ UserModel.prototype.guardedCreate = async function ({ body }) {
|
|||
// Set this one initially as we need the ID to create a custom img via Sanity
|
||||
img: this.encrypt(this.config.avatars.user),
|
||||
}
|
||||
// During tests, users can set their own permission level so you can test admin stuff
|
||||
if (isTest && body.role) data.role = body.role
|
||||
this.record = await this.prisma.user.create({ data })
|
||||
} catch (err) {
|
||||
log.warn(err, 'Could not create user record')
|
||||
|
@ -256,7 +263,7 @@ UserModel.prototype.guardedCreate = async function ({ body }) {
|
|||
})
|
||||
|
||||
// Send signup email
|
||||
if (!this.isUnitTest(body) || this.config.tests.sendEmail)
|
||||
if (!this.isTest(body) || this.config.tests.sendEmail)
|
||||
await this.mailer.send({
|
||||
template: 'signup',
|
||||
language: this.language,
|
||||
|
@ -271,7 +278,7 @@ UserModel.prototype.guardedCreate = async function ({ body }) {
|
|||
},
|
||||
})
|
||||
|
||||
return this.isUnitTest(body)
|
||||
return this.isTest(body)
|
||||
? this.setResponse(201, false, {
|
||||
email: this.clear.email,
|
||||
confirmation: this.confirmation.record.id,
|
||||
|
@ -390,8 +397,8 @@ UserModel.prototype.sendSigninlink = async function (req) {
|
|||
},
|
||||
userId: this.record.id,
|
||||
})
|
||||
const isUnitTest = this.isUnitTest(req.body)
|
||||
if (!isUnitTest) {
|
||||
const isTest = this.isTest(req.body)
|
||||
if (!isTest) {
|
||||
// Send sign-in link email
|
||||
await this.mailer.send({
|
||||
template: 'signinlink',
|
||||
|
@ -522,7 +529,7 @@ UserModel.prototype.guardedUpdate = async function ({ body, user }) {
|
|||
// Now update the record
|
||||
await this.unguardedUpdate(this.cloak(data))
|
||||
|
||||
const isUnitTest = this.isUnitTest(body)
|
||||
const isTest = this.isTest(body)
|
||||
if (typeof body.email === 'string' && this.clear.email !== clean(body.email)) {
|
||||
// Email change (requires confirmation)
|
||||
const check = randomString()
|
||||
|
@ -538,7 +545,7 @@ UserModel.prototype.guardedUpdate = async function ({ body, user }) {
|
|||
},
|
||||
userId: this.record.id,
|
||||
})
|
||||
if (!isUnitTest || this.config.tests.sendEmail) {
|
||||
if (!isTest || this.config.tests.sendEmail) {
|
||||
// Send confirmation email
|
||||
await this.mailer.send({
|
||||
template: 'emailchange',
|
||||
|
@ -590,8 +597,7 @@ UserModel.prototype.guardedUpdate = async function ({ body, user }) {
|
|||
result: 'success',
|
||||
account: this.asAccount(),
|
||||
}
|
||||
if (isUnitTest && this.Confirmation.record?.id)
|
||||
returnData.confirmation = this.Confirmation.record.id
|
||||
if (isTest && this.Confirmation.record?.id) returnData.confirmation = this.Confirmation.record.id
|
||||
|
||||
return this.setResponse(200, false, returnData)
|
||||
}
|
||||
|
@ -755,11 +761,14 @@ UserModel.prototype.sendResponse = async function (res) {
|
|||
|
||||
/*
|
||||
* Update method to determine whether this request is
|
||||
* part of a unit test
|
||||
* part of a (unit) test
|
||||
*/
|
||||
UserModel.prototype.isUnitTest = function (body) {
|
||||
if (!body.unittest) return false
|
||||
if (!this.clear.email.split('@').pop() === this.config.tests.domain) return false
|
||||
UserModel.prototype.isTest = function (body) {
|
||||
// Disalowing tests in prodution is hard-coded to protect people from
|
||||
if (this.config.env === 'production' && !this.config.tests.production) return false
|
||||
if (!body.test) return false
|
||||
if (this.clear?.email && !this.clear.email.split('@').pop() === this.config.tests.domain)
|
||||
return false
|
||||
if (body.email && !body.email.split('@').pop() === this.config.tests.domain) return false
|
||||
|
||||
return true
|
||||
|
|
51
sites/backend/src/routes/curated-sets.mjs
Normal file
51
sites/backend/src/routes/curated-sets.mjs
Normal file
|
@ -0,0 +1,51 @@
|
|||
import { CuratedSetsController } from '../controllers/curated-sets.mjs'
|
||||
|
||||
const CuratedSets = new CuratedSetsController()
|
||||
const jwt = ['jwt', { session: false }]
|
||||
const bsc = ['basic', { session: false }]
|
||||
|
||||
export function curatedSetsRoutes(tools) {
|
||||
const { app, passport } = tools
|
||||
|
||||
// Read a curated measurements set (no need to authenticate for this)
|
||||
app.get('/curated-sets/:id.json', (req, res) => CuratedSets.read(req, res, tools, 'json'))
|
||||
app.get('/curated-sets/:id.yaml', (req, res) => CuratedSets.read(req, res, tools, 'yaml'))
|
||||
app.get('/curated-sets/:id', (req, res) => CuratedSets.read(req, res, tools))
|
||||
|
||||
// Get a list of all curated measurments sets (no need to authenticate for this)
|
||||
app.get('/curated-sets.json', (req, res) => CuratedSets.list(req, res, tools, 'json'))
|
||||
app.get('/curated-sets.yaml', (req, res) => CuratedSets.list(req, res, tools, 'yaml'))
|
||||
app.get('/curated-sets', (req, res) => CuratedSets.list(req, res, tools))
|
||||
|
||||
// Create a curated measurements set
|
||||
app.post('/curated-sets/jwt', passport.authenticate(...jwt), (req, res) =>
|
||||
CuratedSets.create(req, res, tools)
|
||||
)
|
||||
app.post('/curated-sets/key', passport.authenticate(...bsc), (req, res) =>
|
||||
CuratedSets.create(req, res, tools)
|
||||
)
|
||||
|
||||
// Clone a curated measurements set
|
||||
app.post('/curated-sets/:id/clone/jwt', passport.authenticate(...jwt), (req, res) =>
|
||||
CuratedSets.clone(req, res, tools)
|
||||
)
|
||||
app.post('/curated-sets/:id/clone/key', passport.authenticate(...bsc), (req, res) =>
|
||||
CuratedSets.clone(req, res, tools)
|
||||
)
|
||||
|
||||
// Update a curated measurements set
|
||||
app.patch('/curated-sets/:id/jwt', passport.authenticate(...jwt), (req, res) =>
|
||||
CuratedSets.update(req, res, tools)
|
||||
)
|
||||
app.patch('/curated-sets/:id/key', passport.authenticate(...bsc), (req, res) =>
|
||||
CuratedSets.update(req, res, tools)
|
||||
)
|
||||
|
||||
// Delete a curated measurements set
|
||||
app.delete('/curated-sets/:id/jwt', passport.authenticate(...jwt), (req, res) =>
|
||||
CuratedSets.delete(req, res, tools)
|
||||
)
|
||||
app.delete('/curated-sets/:id/key', passport.authenticate(...bsc), (req, res) =>
|
||||
CuratedSets.delete(req, res, tools)
|
||||
)
|
||||
}
|
|
@ -3,7 +3,7 @@ import { usersRoutes } from './users.mjs'
|
|||
import { setsRoutes } from './sets.mjs'
|
||||
import { patternsRoutes } from './patterns.mjs'
|
||||
import { confirmationsRoutes } from './confirmations.mjs'
|
||||
//import { curatedSetsRoutes } from './curated-sets.mjs'
|
||||
import { curatedSetsRoutes } from './curated-sets.mjs'
|
||||
|
||||
export const routes = {
|
||||
apikeysRoutes,
|
||||
|
@ -11,5 +11,5 @@ export const routes = {
|
|||
setsRoutes,
|
||||
patternsRoutes,
|
||||
confirmationsRoutes,
|
||||
//curatedSetsRoutes,
|
||||
curatedSetsRoutes,
|
||||
}
|
||||
|
|
|
@ -1,5 +1,10 @@
|
|||
import { website } from '../config.mjs'
|
||||
|
||||
/*
|
||||
* Capitalizes a string
|
||||
*/
|
||||
export const capitalize = (string) => string.charAt(0).toUpperCase() + string.slice(1)
|
||||
|
||||
/*
|
||||
* Cleans a string (typically email) for hashing
|
||||
*/
|
||||
|
|
|
@ -232,7 +232,7 @@ export const accountTests = async (chai, config, expect, store) => {
|
|||
)
|
||||
.send({
|
||||
email: `updating_${store.randomString()}@${store.config.tests.domain}`,
|
||||
unittest: true,
|
||||
test: true,
|
||||
})
|
||||
.end((err, res) => {
|
||||
expect(err === null).to.equal(true)
|
||||
|
@ -287,7 +287,7 @@ export const accountTests = async (chai, config, expect, store) => {
|
|||
)
|
||||
.send({
|
||||
email: store.account.email,
|
||||
unittest: true,
|
||||
test: true,
|
||||
})
|
||||
.end((err, res) => {
|
||||
expect(err === null).to.equal(true)
|
||||
|
|
309
sites/backend/tests/curated-set.mjs
Normal file
309
sites/backend/tests/curated-set.mjs
Normal file
|
@ -0,0 +1,309 @@
|
|||
import { cat } from './cat.mjs'
|
||||
import { capitalize } from '../src/utils/index.mjs'
|
||||
|
||||
export const curatedSetTests = async (chai, config, expect, store) => {
|
||||
const data = {
|
||||
jwt: {
|
||||
nameDe: 'Beispielmessungen A',
|
||||
nameEn: 'Example measurements A',
|
||||
nameEs: 'Medidas de ejemplo A',
|
||||
nameFr: 'Mesures exemple A',
|
||||
nameNl: 'Voorbeel maten A',
|
||||
notesDe: 'Das sind die Notizen A',
|
||||
notesEn: 'These are the notes A',
|
||||
notesEs: 'Estas son las notas A',
|
||||
notesFr: 'Ce sont les notes A',
|
||||
notesNl: 'Dit zijn de notities A',
|
||||
measies: {
|
||||
chest: 1000,
|
||||
neck: 420,
|
||||
},
|
||||
},
|
||||
key: {
|
||||
nameDe: 'Beispielmessungen B',
|
||||
nameEn: 'Example measurements B',
|
||||
nameEs: 'Medidas de ejemplo B',
|
||||
nameFr: 'Mesures exemple B',
|
||||
nameNl: 'Voorbeel maten B',
|
||||
notesDe: 'Das sind die Notizen B',
|
||||
notesEn: 'These are the notes B',
|
||||
notesEs: 'Estas son las notas B',
|
||||
notesFr: 'Ce sont les notes B',
|
||||
notesNl: 'Dit zijn de notities B',
|
||||
measies: {
|
||||
chest: 930,
|
||||
neck: 360,
|
||||
},
|
||||
img: cat,
|
||||
},
|
||||
}
|
||||
store.curatedSet = {
|
||||
jwt: {},
|
||||
key: {},
|
||||
}
|
||||
store.altset = {
|
||||
jwt: {},
|
||||
key: {},
|
||||
}
|
||||
|
||||
for (const auth of ['jwt', 'key']) {
|
||||
describe(`${store.icon('set', auth)} Curated set tests (${auth})`, () => {
|
||||
it(`${store.icon('set', auth)} Should create a new curated set (${auth})`, (done) => {
|
||||
chai
|
||||
.request(config.api)
|
||||
.post(`/curated-sets/${auth}`)
|
||||
.set(
|
||||
'Authorization',
|
||||
auth === 'jwt'
|
||||
? 'Bearer ' + store.account.token
|
||||
: 'Basic ' +
|
||||
new Buffer(`${store.account.apikey.key}:${store.account.apikey.secret}`).toString(
|
||||
'base64'
|
||||
)
|
||||
)
|
||||
.send(data[auth])
|
||||
.end((err, res) => {
|
||||
expect(err === null).to.equal(true)
|
||||
expect(res.status).to.equal(201)
|
||||
expect(res.body.result).to.equal(`success`)
|
||||
for (const [key, val] of Object.entries(data[auth])) {
|
||||
if (!['measies', 'img'].includes(key)) expect(res.body.curatedSet[key]).to.equal(val)
|
||||
}
|
||||
store.curatedSet[auth] = res.body.curatedSet
|
||||
done()
|
||||
})
|
||||
}).timeout(5000)
|
||||
|
||||
for (const field of ['name', 'notes']) {
|
||||
for (const lang of config.languages) {
|
||||
const langField = field + capitalize(lang)
|
||||
it(`${store.icon(
|
||||
'set',
|
||||
auth
|
||||
)} Should update the ${langField} field (${auth})`, (done) => {
|
||||
const data = {}
|
||||
const val = store.curatedSet[auth][langField] + '_updated'
|
||||
data[langField] = val
|
||||
chai
|
||||
.request(config.api)
|
||||
.patch(`/curated-sets/${store.curatedSet[auth].id}/${auth}`)
|
||||
.set(
|
||||
'Authorization',
|
||||
auth === 'jwt'
|
||||
? 'Bearer ' + store.account.token
|
||||
: 'Basic ' +
|
||||
new Buffer(
|
||||
`${store.account.apikey.key}:${store.account.apikey.secret}`
|
||||
).toString('base64')
|
||||
)
|
||||
.send(data)
|
||||
.end((err, res) => {
|
||||
expect(err === null).to.equal(true)
|
||||
expect(res.status).to.equal(200)
|
||||
expect(res.body.result).to.equal(`success`)
|
||||
expect(res.body.set[langField]).to.equal(val)
|
||||
done()
|
||||
})
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
for (const field of ['chest', 'neck', 'ankle']) {
|
||||
it(`${store.icon(
|
||||
'set',
|
||||
auth
|
||||
)} Should update the ${field} measurement (${auth})`, (done) => {
|
||||
const data = { measies: {} }
|
||||
const val = Math.ceil(Math.random() * 1000)
|
||||
data.measies[field] = val
|
||||
chai
|
||||
.request(config.api)
|
||||
.patch(`/curated-sets/${store.curatedSet[auth].id}/${auth}`)
|
||||
.set(
|
||||
'Authorization',
|
||||
auth === 'jwt'
|
||||
? 'Bearer ' + store.account.token
|
||||
: 'Basic ' +
|
||||
new Buffer(
|
||||
`${store.account.apikey.key}:${store.account.apikey.secret}`
|
||||
).toString('base64')
|
||||
)
|
||||
.send(data)
|
||||
.end((err, res) => {
|
||||
expect(err === null).to.equal(true)
|
||||
expect(res.status).to.equal(200)
|
||||
expect(res.body.result).to.equal(`success`)
|
||||
expect(res.body.set.measies[field]).to.equal(val)
|
||||
done()
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
it(`${store.icon(
|
||||
'set',
|
||||
auth
|
||||
)} Should not set an non-existing measurement (${auth})`, (done) => {
|
||||
chai
|
||||
.request(config.api)
|
||||
.patch(`/curated-sets/${store.curatedSet[auth].id}/${auth}`)
|
||||
.set(
|
||||
'Authorization',
|
||||
auth === 'jwt'
|
||||
? 'Bearer ' + store.account.token
|
||||
: 'Basic ' +
|
||||
new Buffer(`${store.account.apikey.key}:${store.account.apikey.secret}`).toString(
|
||||
'base64'
|
||||
)
|
||||
)
|
||||
.send({
|
||||
measies: {
|
||||
ankle: 320,
|
||||
potatoe: 12,
|
||||
},
|
||||
})
|
||||
.end((err, res) => {
|
||||
expect(err === null).to.equal(true)
|
||||
expect(res.status).to.equal(200)
|
||||
expect(res.body.result).to.equal(`success`)
|
||||
expect(res.body.set.measies.ankle).to.equal(320)
|
||||
expect(typeof res.body.set.measies.potatoe).to.equal('undefined')
|
||||
done()
|
||||
})
|
||||
})
|
||||
|
||||
it(`${store.icon('set', auth)} Should clear a measurement (${auth})`, (done) => {
|
||||
chai
|
||||
.request(config.api)
|
||||
.patch(`/curated-sets/${store.curatedSet[auth].id}/${auth}`)
|
||||
.set(
|
||||
'Authorization',
|
||||
auth === 'jwt'
|
||||
? 'Bearer ' + store.account.token
|
||||
: 'Basic ' +
|
||||
new Buffer(`${store.account.apikey.key}:${store.account.apikey.secret}`).toString(
|
||||
'base64'
|
||||
)
|
||||
)
|
||||
.send({
|
||||
measies: {
|
||||
chest: null,
|
||||
},
|
||||
})
|
||||
.end((err, res) => {
|
||||
expect(err === null).to.equal(true)
|
||||
expect(res.status).to.equal(200)
|
||||
expect(res.body.result).to.equal(`success`)
|
||||
expect(typeof res.body.set.measies.chest).to.equal('undefined')
|
||||
done()
|
||||
})
|
||||
})
|
||||
|
||||
it(`${store.icon('set', auth)} Should clone a set (${auth})`, (done) => {
|
||||
chai
|
||||
.request(config.api)
|
||||
.post(`/curated-sets/${store.curatedSet[auth].id}/clone/${auth}`)
|
||||
.set(
|
||||
'Authorization',
|
||||
auth === 'jwt'
|
||||
? 'Bearer ' + store.account.token
|
||||
: 'Basic ' +
|
||||
new Buffer(`${store.account.apikey.key}:${store.account.apikey.secret}`).toString(
|
||||
'base64'
|
||||
)
|
||||
)
|
||||
.send({ language: 'nl' })
|
||||
.end((err, res) => {
|
||||
expect(err === null).to.equal(true)
|
||||
expect(res.status).to.equal(201)
|
||||
expect(res.body.result).to.equal(`success`)
|
||||
expect(typeof res.body.error).to.equal(`undefined`)
|
||||
expect(typeof res.body.set.id).to.equal(`number`)
|
||||
expect(res.body.set.name).to.equal(store.curatedSet[auth].nameNl + '_updated')
|
||||
expect(res.body.set.notes).to.equal(store.curatedSet[auth].notesNl + '_updated')
|
||||
done()
|
||||
})
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
// Unauthenticated tests
|
||||
describe(`${store.icon('set')} Curated set tests (unauthenticated)`, () => {
|
||||
for (const auth of ['jwt', 'key']) {
|
||||
it(`${store.icon('set')} Should read a curated set created with ${auth}`, (done) => {
|
||||
chai
|
||||
.request(config.api)
|
||||
.get(`/curated-sets/${store.curatedSet[auth].id}`)
|
||||
.end((err, res) => {
|
||||
expect(err === null).to.equal(true)
|
||||
expect(res.status).to.equal(200)
|
||||
expect(res.body.result).to.equal(`success`)
|
||||
expect(typeof res.body.curatedSet.measies).to.equal('object')
|
||||
done()
|
||||
})
|
||||
})
|
||||
|
||||
it(`${store.icon('set')} Should read a curated set created with ${auth} as JSON`, (done) => {
|
||||
chai
|
||||
.request(config.api)
|
||||
.get(`/curated-sets/${store.curatedSet[auth].id}.json`)
|
||||
.end((err, res) => {
|
||||
expect(err === null).to.equal(true)
|
||||
expect(res.status).to.equal(200)
|
||||
expect(typeof res.body.measurements).to.equal('object')
|
||||
done()
|
||||
})
|
||||
})
|
||||
|
||||
it(`${store.icon('set')} Should read a curated set created with ${auth} as YAML`, (done) => {
|
||||
chai
|
||||
.request(config.api)
|
||||
.get(`/curated-sets/${store.curatedSet[auth].id}.yaml`)
|
||||
.end((err, res) => {
|
||||
expect(err === null).to.equal(true)
|
||||
expect(res.status).to.equal(200)
|
||||
expect(res.text).to.include('FreeSewing')
|
||||
done()
|
||||
})
|
||||
})
|
||||
|
||||
it(`${store.icon('set')} Should retrieve a list of curated sets`, (done) => {
|
||||
chai
|
||||
.request(config.api)
|
||||
.get(`/curated-sets`)
|
||||
.end((err, res) => {
|
||||
expect(err === null).to.equal(true)
|
||||
expect(res.status).to.equal(200)
|
||||
expect(res.body.result).to.equal('success')
|
||||
done()
|
||||
})
|
||||
})
|
||||
|
||||
it(`${store.icon('set')} Should retrieve a list of curated sets as JSON`, (done) => {
|
||||
chai
|
||||
.request(config.api)
|
||||
.get(`/curated-sets.json`)
|
||||
.end((err, res) => {
|
||||
expect(err === null).to.equal(true)
|
||||
expect(res.status).to.equal(200)
|
||||
done()
|
||||
})
|
||||
})
|
||||
|
||||
it(`${store.icon('set')} Should retrieve a list of curated sets as YAML`, (done) => {
|
||||
chai
|
||||
.request(config.api)
|
||||
.get(`/curated-sets.yaml`)
|
||||
.end((err, res) => {
|
||||
expect(err === null).to.equal(true)
|
||||
expect(res.status).to.equal(200)
|
||||
done()
|
||||
})
|
||||
})
|
||||
}
|
||||
})
|
||||
//console.log(`/curated-sets/${store.curatedSet[auth].id}/${auth}`)
|
||||
//console.log({body: res.body, status: res.status})
|
||||
|
||||
// TODO:
|
||||
// - Delete Curated set
|
||||
}
|
|
@ -3,16 +3,18 @@ import { mfaTests } from './mfa.mjs'
|
|||
import { accountTests } from './account.mjs'
|
||||
import { apikeyTests } from './apikey.mjs'
|
||||
import { setTests } from './set.mjs'
|
||||
import { curatedSetTests } from './curated-set.mjs'
|
||||
import { patternTests } from './pattern.mjs'
|
||||
import { setup } from './shared.mjs'
|
||||
|
||||
const runTests = async (...params) => {
|
||||
await userTests(...params)
|
||||
await mfaTests(...params)
|
||||
await apikeyTests(...params)
|
||||
await accountTests(...params)
|
||||
await setTests(...params)
|
||||
await patternTests(...params)
|
||||
//await userTests(...params)
|
||||
//await mfaTests(...params)
|
||||
//await apikeyTests(...params)
|
||||
//await accountTests(...params)
|
||||
//await setTests(...params)
|
||||
await curatedSetTests(...params)
|
||||
//await patternTests(...params)
|
||||
}
|
||||
|
||||
// Load initial data required for tests
|
||||
|
|
|
@ -10,7 +10,7 @@ export const setTests = async (chai, config, expect, store) => {
|
|||
neck: 420,
|
||||
},
|
||||
public: true,
|
||||
unittest: true,
|
||||
test: true,
|
||||
imperial: true,
|
||||
},
|
||||
key: {
|
||||
|
@ -22,7 +22,7 @@ export const setTests = async (chai, config, expect, store) => {
|
|||
},
|
||||
public: false,
|
||||
img: cat,
|
||||
unittest: true,
|
||||
test: true,
|
||||
imperial: false,
|
||||
},
|
||||
}
|
||||
|
@ -56,8 +56,7 @@ export const setTests = async (chai, config, expect, store) => {
|
|||
expect(res.status).to.equal(201)
|
||||
expect(res.body.result).to.equal(`success`)
|
||||
for (const [key, val] of Object.entries(data[auth])) {
|
||||
if (!['measies', 'img', 'unittest'].includes(key))
|
||||
expect(res.body.set[key]).to.equal(val)
|
||||
if (!['measies', 'img', 'test'].includes(key)) expect(res.body.set[key]).to.equal(val)
|
||||
}
|
||||
store.set[auth] = res.body.set
|
||||
done()
|
||||
|
|
|
@ -53,7 +53,8 @@ export const setup = async () => {
|
|||
result = await axios.post(`${store.config.api}/signup`, {
|
||||
email: store[acc].email,
|
||||
language: store[acc].language,
|
||||
unittest: true,
|
||||
test: true,
|
||||
role: 'curator',
|
||||
})
|
||||
} catch (err) {
|
||||
console.log('Failed at first setup request', err)
|
||||
|
@ -80,7 +81,7 @@ export const setup = async () => {
|
|||
`${store.config.api}/apikeys/jwt`,
|
||||
{
|
||||
name: 'Test API key',
|
||||
level: 4,
|
||||
level: 5,
|
||||
expiresIn: 60,
|
||||
},
|
||||
{
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue